mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
985 lines
30 KiB
C++
985 lines
30 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkGL2PSUtilities.cxx
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
|
|
#include "vtkGL2PSUtilities.h"
|
|
|
|
#include "vtkIntArray.h"
|
|
#include "vtkFloatArray.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkMatrix4x4.h"
|
|
#include "vtkNew.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkOpenGLGL2PSHelper.h"
|
|
#include "vtkPath.h"
|
|
#include "vtkRenderWindow.h"
|
|
#include "vtkTextProperty.h"
|
|
#include "vtkTextRenderer.h"
|
|
|
|
#include "vtk_gl2ps.h"
|
|
|
|
#include <cassert>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
vtkStandardNewMacro(vtkGL2PSUtilities)
|
|
|
|
// Initialize static members
|
|
vtkRenderWindow *vtkGL2PSUtilities::RenderWindow = NULL;
|
|
bool vtkGL2PSUtilities::TextAsPath = false;
|
|
float vtkGL2PSUtilities::PointSizeFactor = 5.f / 7.f;
|
|
float vtkGL2PSUtilities::LineWidthFactor = 5.f / 7.f;
|
|
|
|
void vtkGL2PSUtilities::DrawString(const char *str,
|
|
vtkTextProperty *tprop, double pos[3],
|
|
double backgroundDepth)
|
|
{
|
|
if (!str)
|
|
{
|
|
return;
|
|
}
|
|
|
|
vtkTextRenderer *tren(vtkTextRenderer::GetInstance());
|
|
if (tren == NULL)
|
|
{
|
|
vtkNew<vtkGL2PSUtilities> dummy;
|
|
vtkErrorWithObjectMacro(dummy.GetPointer(),
|
|
<<"vtkTextRenderer unavailable.");
|
|
return;
|
|
}
|
|
|
|
int dpi = vtkGL2PSUtilities::RenderWindow->GetDPI();
|
|
|
|
// Draw the background if needed:
|
|
if (tprop->GetBackgroundOpacity() > 0.)
|
|
{
|
|
vtkTextRenderer::Metrics metrics;
|
|
if (tren->GetMetrics(tprop, str, metrics, dpi))
|
|
{
|
|
double bgPos[3] = { pos[0], pos[1], backgroundDepth };
|
|
vtkGL2PSUtilities::ProjectPoint(bgPos);
|
|
|
|
double bgVerts[12];
|
|
bgVerts[0] = bgPos[0] + static_cast<double>(metrics.TopLeft[0]);
|
|
bgVerts[1] = bgPos[1] + static_cast<double>(metrics.TopLeft[1]);
|
|
bgVerts[2] = bgPos[2];
|
|
bgVerts[3] = bgPos[0] + static_cast<double>(metrics.BottomLeft[0]);
|
|
bgVerts[4] = bgPos[1] + static_cast<double>(metrics.BottomLeft[1]);
|
|
bgVerts[5] = bgPos[2];
|
|
bgVerts[6] = bgPos[0] + static_cast<double>(metrics.BottomRight[0]);
|
|
bgVerts[7] = bgPos[1] + static_cast<double>(metrics.BottomRight[1]);
|
|
bgVerts[8] = bgPos[2];
|
|
bgVerts[9] = bgPos[0] + static_cast<double>(metrics.TopRight[0]);
|
|
bgVerts[10] = bgPos[1] + static_cast<double>(metrics.TopRight[1]);
|
|
bgVerts[11] = bgPos[2];
|
|
|
|
vtkGL2PSUtilities::UnprojectPoints(bgVerts, 4);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glColor4d(tprop->GetBackgroundColor()[0],
|
|
tprop->GetBackgroundColor()[1],
|
|
tprop->GetBackgroundColor()[2],
|
|
tprop->GetBackgroundOpacity());
|
|
glVertexPointer(3, GL_DOUBLE, 0, bgVerts);
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
}
|
|
}
|
|
|
|
bool isMath = tren->DetectBackend(str) == vtkTextRenderer::MathText;
|
|
if (!isMath && !vtkGL2PSUtilities::TextAsPath)
|
|
{
|
|
const char *fontname = vtkGL2PSUtilities::TextPropertyToPSFontName(tprop);
|
|
|
|
GLint align = static_cast<GLint>(
|
|
vtkGL2PSUtilities::TextPropertyToGL2PSAlignment(tprop));
|
|
|
|
GLfloat angle = static_cast<GLfloat>(tprop->GetOrientation());
|
|
|
|
// GL2PS assumes 72 DPI, so we'll have to adjust the font size:
|
|
GLint fontSize = static_cast<GLint>(tprop->GetFontSize() * (dpi / 72.));
|
|
|
|
GL2PSrgba rgba;
|
|
double rgbad[3];
|
|
tprop->GetColor(rgbad[0], rgbad[1], rgbad[2]);
|
|
rgba[0] = static_cast<GLfloat>(rgbad[0]);
|
|
rgba[1] = static_cast<GLfloat>(rgbad[1]);
|
|
rgba[2] = static_cast<GLfloat>(rgbad[2]);
|
|
rgba[3] = tprop->GetOpacity();
|
|
|
|
glRasterPos3dv(pos);
|
|
gl2psTextOptColor(str, fontname, fontSize, align, angle, rgba);
|
|
}
|
|
else
|
|
{
|
|
// Render the string to a path and then draw it to GL2PS:
|
|
vtkNew<vtkPath> path;
|
|
tren->StringToPath(tprop, str, path.GetPointer(), dpi);
|
|
// Get color
|
|
double rgbd[3];
|
|
tprop->GetColor(rgbd[0], rgbd[1], rgbd[2]);
|
|
unsigned char rgba[4] = {
|
|
static_cast<unsigned char>(rgbd[0]*255),
|
|
static_cast<unsigned char>(rgbd[1]*255),
|
|
static_cast<unsigned char>(rgbd[2]*255),
|
|
static_cast<unsigned char>(tprop->GetOpacity()*255)};
|
|
|
|
double devicePos[3] = {pos[0], pos[1], pos[2]};
|
|
vtkGL2PSUtilities::ProjectPoint(devicePos);
|
|
|
|
vtkGL2PSUtilities::DrawPath(path.GetPointer(), pos, devicePos, rgba, NULL,
|
|
0.0, -1.f, (std::string("Pathified string: ")
|
|
+ str).c_str());
|
|
}
|
|
}
|
|
|
|
const char *vtkGL2PSUtilities::TextPropertyToPSFontName(vtkTextProperty *tprop)
|
|
{
|
|
bool bold = tprop->GetBold() != 0;
|
|
bool italic = tprop->GetItalic() != 0;
|
|
|
|
switch (tprop->GetFontFamily())
|
|
{
|
|
case VTK_ARIAL:
|
|
{
|
|
if (!bold && !italic)
|
|
{
|
|
return "Helvetica";
|
|
}
|
|
else if (bold && italic)
|
|
{
|
|
return "Helvetica-BoldItalic";
|
|
}
|
|
else if (bold)
|
|
{
|
|
return "Helvetica-Bold";
|
|
}
|
|
else if (italic)
|
|
{
|
|
return "Helvetica-Italic";
|
|
}
|
|
}
|
|
break;
|
|
case VTK_TIMES:
|
|
{
|
|
if (!bold && !italic)
|
|
{
|
|
return "Times-Roman";
|
|
}
|
|
else if (bold && italic)
|
|
{
|
|
return "Times-BoldOblique";
|
|
}
|
|
else if (bold)
|
|
{
|
|
return "Times-Bold";
|
|
}
|
|
else if (italic)
|
|
{
|
|
return "Times-Oblique";
|
|
}
|
|
}
|
|
break;
|
|
case VTK_COURIER:
|
|
{
|
|
if (!bold && !italic)
|
|
{
|
|
return "Courier";
|
|
}
|
|
else if (bold && italic)
|
|
{
|
|
return "Courier-BoldOblique";
|
|
}
|
|
else if (bold)
|
|
{
|
|
return "Courier-Bold";
|
|
}
|
|
else if (italic)
|
|
{
|
|
return "Courier-Oblique";
|
|
}
|
|
}
|
|
break;
|
|
case VTK_UNKNOWN_FONT:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Helvetica";
|
|
}
|
|
|
|
int vtkGL2PSUtilities::TextPropertyToGL2PSAlignment(vtkTextProperty *tprop)
|
|
{
|
|
switch (tprop->GetJustification())
|
|
{
|
|
case VTK_TEXT_LEFT:
|
|
switch (tprop->GetVerticalJustification())
|
|
{
|
|
case VTK_TEXT_TOP:
|
|
{
|
|
return GL2PS_TEXT_TL;
|
|
}
|
|
case VTK_TEXT_CENTERED:
|
|
{
|
|
return GL2PS_TEXT_CL;
|
|
}
|
|
case VTK_TEXT_BOTTOM:
|
|
{
|
|
return GL2PS_TEXT_BL;
|
|
}
|
|
}
|
|
break;
|
|
case VTK_TEXT_CENTERED:
|
|
switch (tprop->GetVerticalJustification())
|
|
{
|
|
case VTK_TEXT_TOP:
|
|
{
|
|
return GL2PS_TEXT_T;
|
|
}
|
|
case VTK_TEXT_CENTERED:
|
|
{
|
|
return GL2PS_TEXT_C;
|
|
}
|
|
case VTK_TEXT_BOTTOM:
|
|
{
|
|
return GL2PS_TEXT_B;
|
|
}
|
|
}
|
|
break;
|
|
case VTK_TEXT_RIGHT:
|
|
switch (tprop->GetVerticalJustification())
|
|
{
|
|
case VTK_TEXT_TOP:
|
|
{
|
|
return GL2PS_TEXT_TR;
|
|
}
|
|
case VTK_TEXT_CENTERED:
|
|
{
|
|
return GL2PS_TEXT_CR;
|
|
}
|
|
case VTK_TEXT_BOTTOM:
|
|
{
|
|
return GL2PS_TEXT_BR;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GL2PS_TEXT_BL;
|
|
}
|
|
|
|
void vtkGL2PSUtilities::Draw3DPath(vtkPath *path, vtkMatrix4x4 *actorMatrix,
|
|
double rasterPos[3],
|
|
unsigned char actorColor[4],
|
|
const char *label)
|
|
{
|
|
double translation[2] = {0.0, 0.0};
|
|
vtkNew<vtkPath> projPath;
|
|
projPath->DeepCopy(path);
|
|
vtkGL2PSUtilities::ProjectPoints(projPath->GetPoints(), actorMatrix);
|
|
vtkGL2PSUtilities::DrawPath(projPath.GetPointer(), rasterPos, translation,
|
|
actorColor, NULL, 0.0, -1.f, label);
|
|
}
|
|
|
|
void vtkGL2PSUtilities::DrawPath(vtkPath *path, double rasterPos[3],
|
|
double windowPos[2], unsigned char rgba[4],
|
|
double scale[2], double rotateAngle,
|
|
float strokeWidth, const char *label)
|
|
{
|
|
// Replace newlines in label -- these will throw off the comments.
|
|
|
|
std::string l(label ? label : "");
|
|
size_t idx = 0;
|
|
while ((idx = l.find('\n', idx)) != std::string::npos)
|
|
{
|
|
l.replace(idx, 1, "\\n");
|
|
}
|
|
|
|
switch (gl2psGetFileFormat())
|
|
{
|
|
case GL2PS_PS:
|
|
case GL2PS_EPS:
|
|
vtkGL2PSUtilities::DrawPathPS(path, rasterPos, windowPos, rgba, scale,
|
|
rotateAngle, strokeWidth, l.c_str());
|
|
break;
|
|
case GL2PS_SVG:
|
|
vtkGL2PSUtilities::DrawPathSVG(path, rasterPos, windowPos, rgba, scale,
|
|
rotateAngle, strokeWidth, l.c_str());
|
|
break;
|
|
case GL2PS_PDF:
|
|
vtkGL2PSUtilities::DrawPathPDF(path, rasterPos, windowPos, rgba, scale,
|
|
rotateAngle, strokeWidth, l.c_str());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void vtkGL2PSUtilities::StartExport()
|
|
{
|
|
// This is nasty -- the tokens are used in the feedback buffer to tell GL2PS
|
|
// about stippling or when the linewidth/pointsize changes. These are the
|
|
// values defined in gl2ps.c as of v1.3.8. If these values change (doubtful)
|
|
// we'll need to detect the gl2ps version and set the values per version.
|
|
//
|
|
// We set these in the helper class to fake the GL2PS functions that inject
|
|
// the tokens into the feedback buffer to avoid making vtkRenderingOpenGL
|
|
// depend on gl2ps.
|
|
vtkOpenGLGL2PSHelper::StippleBeginToken = 5.f; // GL2PS_BEGIN_STIPPLE_TOKEN
|
|
vtkOpenGLGL2PSHelper::StippleEndToken = 6.f; // GL2PS_END_STIPPLE_TOKEN
|
|
vtkOpenGLGL2PSHelper::PointSizeToken = 7.f; // GL2PS_POINT_SIZE_TOKEN
|
|
vtkOpenGLGL2PSHelper::LineWidthToken = 8.f; // GL2PS_LINE_WIDTH_TOKEN
|
|
|
|
// These are used to scale the points and lines:
|
|
vtkOpenGLGL2PSHelper::PointSizeFactor = vtkGL2PSUtilities::PointSizeFactor;
|
|
vtkOpenGLGL2PSHelper::LineWidthFactor = vtkGL2PSUtilities::LineWidthFactor;
|
|
|
|
// Enable the code paths that interact with the feedback buffer:
|
|
vtkOpenGLGL2PSHelper::InGL2PSRender = true;
|
|
}
|
|
|
|
void vtkGL2PSUtilities::FinishExport()
|
|
{
|
|
vtkOpenGLGL2PSHelper::InGL2PSRender = false;
|
|
}
|
|
|
|
void vtkGL2PSUtilities::DrawPathPS(vtkPath *path, double rasterPos[3],
|
|
double windowPos[2], unsigned char rgba[4],
|
|
double scale[2], double rotateAngle,
|
|
float strokeWidth, const char *label)
|
|
{
|
|
vtkFloatArray *points =
|
|
vtkFloatArray::SafeDownCast(path->GetPoints()->GetData());
|
|
vtkIntArray *codes = path->GetCodes();
|
|
|
|
if (points->GetNumberOfTuples() != codes->GetNumberOfTuples())
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::stringstream out;
|
|
out.setf(std::ios_base::left | std::ios_base::fixed);
|
|
const int floatPrec = 2;
|
|
const int floatWidth = 6;
|
|
out.precision(floatPrec);
|
|
out.width(floatWidth);
|
|
|
|
float curveto[3][2];
|
|
float curX = 0;
|
|
float curY = 0;
|
|
|
|
float *pt = points->GetPointer(0);
|
|
int *code = codes->GetPointer(0);
|
|
|
|
// These are only used in an assertion, ifdef silences warning on non-debug
|
|
// builds
|
|
#ifndef NDEBUG
|
|
float *ptBegin = pt;
|
|
int *codeBegin = code;
|
|
#endif
|
|
int *codeEnd = code + codes->GetNumberOfTuples();
|
|
if (label != NULL && label[0] != '\0')
|
|
{
|
|
out << "% " << label << endl;
|
|
}
|
|
out << "gsave" << endl;
|
|
out << "initmatrix" << endl;
|
|
out << windowPos[0] << " " << windowPos[1] << " translate" << endl;
|
|
if (scale)
|
|
{
|
|
out << scale[0] << " " << scale[1] << " scale" << endl;
|
|
}
|
|
out << rotateAngle << " rotate" << endl;
|
|
out << "newpath" << endl;
|
|
while (code < codeEnd)
|
|
{
|
|
assert(pt - ptBegin == (code - codeBegin) * 3);
|
|
|
|
switch (static_cast<vtkPath::ControlPointType>(*code))
|
|
{
|
|
case vtkPath::MOVE_TO:
|
|
curX = *pt;
|
|
curY = *(++pt);
|
|
++pt; // Flush z
|
|
out << curX << " " << curY << " moveto\n";
|
|
break;
|
|
case vtkPath::LINE_TO:
|
|
curX = *pt;
|
|
curY = *(++pt);
|
|
++pt; // Flush z
|
|
out << curX << " " << curY << " lineto\n";
|
|
break;
|
|
case vtkPath::CONIC_CURVE:
|
|
// Postscript doesn't support conic curves -- elevate order to cubic:
|
|
{
|
|
// Next point should be a CONIC_CURVE as well
|
|
code += 1;
|
|
const float &conicX = *pt;
|
|
const float &conicY = *(++pt);
|
|
++pt; // Flush z
|
|
const float &nextX = *(++pt);
|
|
const float &nextY = *(++pt);
|
|
++pt; // Flush z
|
|
|
|
// Perform degree elevation:
|
|
curveto[0][0] = (1.0/3.0)*curX + (2.0/3.0)*conicX;
|
|
curveto[0][1] = (1.0/3.0)*curY + (2.0/3.0)*conicY;
|
|
curveto[1][0] = (2.0/3.0)*conicX + (1.0/3.0)*nextX;
|
|
curveto[1][1] = (2.0/3.0)*conicY + (1.0/3.0)*nextY;
|
|
curveto[2][0] = curX = nextX;
|
|
curveto[2][1] = curY = nextY;
|
|
out << curveto[0][0] << " " << curveto[0][1] << endl
|
|
<< curveto[1][0] << " " << curveto[1][1] << endl
|
|
<< curveto[2][0] << " " << curveto[2][1] << " curveto" << endl;
|
|
}
|
|
break;
|
|
case vtkPath::CUBIC_CURVE:
|
|
{
|
|
// Next two points should be CUBIC_CURVEs as well
|
|
code += 2;
|
|
|
|
curveto[0][0] = *pt;
|
|
curveto[0][1] = *(++pt);
|
|
++pt;
|
|
curveto[1][0] = *(++pt);
|
|
curveto[1][1] = *(++pt);
|
|
++pt;
|
|
curveto[2][0] = curX = *(++pt);
|
|
curveto[2][1] = curY = *(++pt);
|
|
++pt;
|
|
out << curveto[0][0] << " " << curveto[0][1] << endl
|
|
<< curveto[1][0] << " " << curveto[1][1] << endl
|
|
<< curveto[2][0] << " " << curveto[2][1] << " curveto" << endl;
|
|
}
|
|
break;
|
|
default:
|
|
out << "% Unrecognized control code: " << *code << endl;
|
|
pt +=2;
|
|
break;
|
|
}
|
|
|
|
++code;
|
|
++pt;
|
|
}
|
|
|
|
out << static_cast<float>(rgba[0])/255.f << " " <<
|
|
static_cast<float>(rgba[1])/255.f << " " <<
|
|
static_cast<float>(rgba[2])/255.f << " setrgbcolor" << endl;
|
|
|
|
if (strokeWidth > 1e-5)
|
|
{
|
|
out << strokeWidth << " setlinewidth\nstroke" << endl;
|
|
}
|
|
else
|
|
{
|
|
out << "fill" << endl;
|
|
}
|
|
out << "grestore" << endl;
|
|
|
|
glRasterPos3dv(rasterPos);
|
|
gl2psSpecial(gl2psGetFileFormat(), out.str().c_str());
|
|
}
|
|
|
|
void vtkGL2PSUtilities::DrawPathPDF(vtkPath *path, double rasterPos[3],
|
|
double windowPos[2], unsigned char rgba[4],
|
|
double scale[2], double rotateAngle,
|
|
float strokeWidth,
|
|
const char *)
|
|
{
|
|
vtkFloatArray *points =
|
|
vtkFloatArray::SafeDownCast(path->GetPoints()->GetData());
|
|
vtkIntArray *codes = path->GetCodes();
|
|
|
|
if (points->GetNumberOfTuples() != codes->GetNumberOfTuples())
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::stringstream out;
|
|
out.setf(std::ios_base::left | std::ios_base::fixed);
|
|
const int floatPrec = 2;
|
|
const int floatWidth = 6;
|
|
out.precision(floatPrec);
|
|
out.width(floatWidth);
|
|
|
|
float curveto[3][2];
|
|
float curX = 0;
|
|
float curY = 0;
|
|
|
|
float *pt = points->GetPointer(0);
|
|
int *code = codes->GetPointer(0);
|
|
|
|
// These are only used in an assertion, ifdef silences warning on non-debug
|
|
// builds
|
|
#ifndef NDEBUG
|
|
float *ptBegin = pt;
|
|
int *codeBegin = code;
|
|
#endif
|
|
int *codeEnd = code + codes->GetNumberOfTuples();
|
|
|
|
// Push state. PDF doesn't let you reset the CTM, so the hope is that it is
|
|
// identity when this block starts...
|
|
out << "q" << endl;
|
|
// color
|
|
out << static_cast<float>(rgba[0])/255.f << " " <<
|
|
static_cast<float>(rgba[1])/255.f << " " <<
|
|
static_cast<float>(rgba[2])/255.f <<
|
|
(strokeWidth > 1e-5 ? " RG" : " rg") << endl;
|
|
// opacity
|
|
out << static_cast<float>(rgba[3])/255.f
|
|
<< (strokeWidth > 1e-5 ? " CA" : " ca") << endl;
|
|
// translate
|
|
out << 1.f << " " << 0.f << " " << 0.f << " " << 1.f << " "
|
|
<< windowPos[0] << " " << windowPos[1] << " cm" << endl;
|
|
// rotate
|
|
float sT = sin(vtkMath::RadiansFromDegrees(rotateAngle));
|
|
float cT = cos(vtkMath::RadiansFromDegrees(rotateAngle));
|
|
out << cT << " " << sT << " " << -sT << " " << cT << " " << 0.f << " " << 0.f
|
|
<< " cm" << endl;
|
|
// scale
|
|
if (scale)
|
|
{
|
|
out << scale[0] << " " << 0.f << " " << 0.f << " " << scale[1] << " " << 0.f
|
|
<< " " << 0.f << " cm" << endl;
|
|
}
|
|
|
|
while (code < codeEnd)
|
|
{
|
|
assert(pt - ptBegin == (code - codeBegin) * 3);
|
|
|
|
switch (static_cast<vtkPath::ControlPointType>(*code))
|
|
{
|
|
case vtkPath::MOVE_TO:
|
|
curX = *pt;
|
|
curY = *(++pt);
|
|
++pt; // Flush z
|
|
out << curX << " " << curY << " m\n";
|
|
break;
|
|
case vtkPath::LINE_TO:
|
|
curX = *pt;
|
|
curY = *(++pt);
|
|
++pt; // Flush z
|
|
out << curX << " " << curY << " l\n";
|
|
break;
|
|
case vtkPath::CONIC_CURVE:
|
|
// Postscript doesn't support conic curves -- elevate order to cubic:
|
|
{
|
|
// Next point should be a CONIC_CURVE as well
|
|
code += 1;
|
|
const float &conicX = *pt;
|
|
const float &conicY = *(++pt);
|
|
++pt; // Flush z
|
|
const float &nextX = *(++pt);
|
|
const float &nextY = *(++pt);
|
|
++pt; // Flush z
|
|
|
|
// Perform degree elevation:
|
|
curveto[0][0] = (1.0/3.0)*curX + (2.0/3.0)*conicX;
|
|
curveto[0][1] = (1.0/3.0)*curY + (2.0/3.0)*conicY;
|
|
curveto[1][0] = (2.0/3.0)*conicX + (1.0/3.0)*nextX;
|
|
curveto[1][1] = (2.0/3.0)*conicY + (1.0/3.0)*nextY;
|
|
curveto[2][0] = curX = nextX;
|
|
curveto[2][1] = curY = nextY;
|
|
out << curveto[0][0] << " " << curveto[0][1] << endl
|
|
<< curveto[1][0] << " " << curveto[1][1] << endl
|
|
<< curveto[2][0] << " " << curveto[2][1] << " c" << endl;
|
|
}
|
|
break;
|
|
case vtkPath::CUBIC_CURVE:
|
|
{
|
|
// Next two points should be CUBIC_CURVEs as well
|
|
code += 2;
|
|
|
|
curveto[0][0] = *pt;
|
|
curveto[0][1] = *(++pt);
|
|
++pt;
|
|
curveto[1][0] = *(++pt);
|
|
curveto[1][1] = *(++pt);
|
|
++pt;
|
|
curveto[2][0] = curX = *(++pt);
|
|
curveto[2][1] = curY = *(++pt);
|
|
++pt;
|
|
out << curveto[0][0] << " " << curveto[0][1] << endl
|
|
<< curveto[1][0] << " " << curveto[1][1] << endl
|
|
<< curveto[2][0] << " " << curveto[2][1] << " c" << endl;
|
|
}
|
|
break;
|
|
default:
|
|
out << "% Unrecognized control code: " << *code << endl;
|
|
pt +=2;
|
|
break;
|
|
}
|
|
|
|
++code;
|
|
++pt;
|
|
}
|
|
|
|
out << "h ";
|
|
if (strokeWidth > 1e-5)
|
|
{
|
|
out << strokeWidth << " w S" << endl;
|
|
}
|
|
else
|
|
{
|
|
out<< "f" << endl;
|
|
}
|
|
out << "Q" << endl; // Pop state
|
|
|
|
glRasterPos3dv(rasterPos);
|
|
gl2psSpecial(gl2psGetFileFormat(), out.str().c_str());
|
|
}
|
|
|
|
void vtkGL2PSUtilities::DrawPathSVG(vtkPath *path, double rasterPos[3],
|
|
double windowPos[2], unsigned char rgba[4],
|
|
double scale[2], double rotateAngle,
|
|
float strokeWidth, const char *label)
|
|
{
|
|
vtkFloatArray *points =
|
|
vtkFloatArray::SafeDownCast(path->GetPoints()->GetData());
|
|
vtkIntArray *codes = path->GetCodes();
|
|
|
|
if (points->GetNumberOfTuples() != codes->GetNumberOfTuples())
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::stringstream out;
|
|
out.setf(std::ios_base::left | std::ios_base::fixed);
|
|
|
|
const int floatPrec = 2;
|
|
const int floatWidth = 6;
|
|
out.precision(floatPrec);
|
|
out.width(floatWidth);
|
|
|
|
float curveto[3][2];
|
|
float curX = 0;
|
|
float curY = 0;
|
|
|
|
float *pt = points->GetPointer(0);
|
|
int *code = codes->GetPointer(0);
|
|
|
|
// Get the size of the render window -- needed to calculate the SVG position.
|
|
if (!vtkGL2PSUtilities::RenderWindow)
|
|
{
|
|
vtkNew<vtkGL2PSUtilities> dummy;
|
|
vtkErrorWithObjectMacro(dummy.GetPointer(), << "No render window set!");
|
|
return;
|
|
}
|
|
double windowHeight =
|
|
static_cast<double>(vtkGL2PSUtilities::RenderWindow->GetSize()[1]);
|
|
|
|
// These are only used in an assertion, ifdef silences warning on non-debug
|
|
// builds
|
|
#ifndef NDEBUG
|
|
float *ptBegin = pt;
|
|
int *codeBegin = code;
|
|
#endif
|
|
|
|
if (label != NULL && label[0] != '\0')
|
|
{
|
|
out << "<!-- " << label << " -->" << endl;
|
|
}
|
|
|
|
int *codeEnd = code + codes->GetNumberOfTuples();
|
|
out << "<g transform=\"" << endl
|
|
<< " translate(" << windowPos[0] << " "
|
|
<< windowHeight - windowPos[1] << ")" << endl;
|
|
if (scale)
|
|
{
|
|
out << " scale(" << scale[0] << " " << -scale[1] << ")" << endl;
|
|
}
|
|
else
|
|
{
|
|
out << " scale(1.0 -1.0)" << endl;
|
|
}
|
|
out << " rotate(" << rotateAngle << ")\"" << endl;
|
|
if (strokeWidth > 1e-5)
|
|
{
|
|
out << " fill=\"none\"" << endl
|
|
<< " stroke-width=\"" << strokeWidth << "\"" << endl
|
|
<< " stroke=\"rgb(" << std::setprecision(0)
|
|
<< static_cast<int>(rgba[0]) << ","
|
|
<< static_cast<int>(rgba[1]) << ","
|
|
<< static_cast<int>(rgba[2])
|
|
<< std::setprecision(floatPrec) << ")\"" << endl;
|
|
}
|
|
else
|
|
{
|
|
out << " stroke=\"none\"" << endl
|
|
<< " fill=\"rgb(" << std::setprecision(0)
|
|
<< static_cast<int>(rgba[0]) << ","
|
|
<< static_cast<int>(rgba[1]) << ","
|
|
<< static_cast<int>(rgba[2])
|
|
<< std::setprecision(floatPrec) << ")\"" << endl;
|
|
}
|
|
out << " opacity=\"" << static_cast<float>(rgba[3])/255.f << "\"\n"
|
|
<< ">" << endl
|
|
<< " <path d=\"" << std::right << endl;
|
|
|
|
while (code < codeEnd)
|
|
{
|
|
assert(pt - ptBegin == (code - codeBegin) * 3);
|
|
|
|
switch (static_cast<vtkPath::ControlPointType>(*code))
|
|
{
|
|
case vtkPath::MOVE_TO:
|
|
curX = *pt;
|
|
curY = *(++pt);
|
|
++pt; // Flush z
|
|
out << " M " << curX << " " << curY << endl;
|
|
break;
|
|
case vtkPath::LINE_TO:
|
|
curX = *pt;
|
|
curY = *(++pt);
|
|
++pt; // Flush z
|
|
out << " L " << curX << " " << curY << endl;
|
|
break;
|
|
case vtkPath::CONIC_CURVE:
|
|
{
|
|
// Next point should be a CONIC_CURVE as well
|
|
code += 1;
|
|
const float &conicX = *pt;
|
|
const float &conicY = *(++pt);
|
|
++pt; // Flush z
|
|
const float &nextX = *(++pt);
|
|
const float &nextY = *(++pt);
|
|
++pt; // Flush z
|
|
|
|
// Perform degree elevation:
|
|
curveto[0][0] = conicX;
|
|
curveto[0][1] = conicY;
|
|
curveto[1][0] = curX = nextX;
|
|
curveto[1][1] = curY = nextY;
|
|
out << " Q " << curveto[0][0] << " " << curveto[0][1] << endl
|
|
<< " " << curveto[1][0] << " " << curveto[1][1] << endl;
|
|
}
|
|
break;
|
|
case vtkPath::CUBIC_CURVE:
|
|
{
|
|
// Next two points should be CUBIC_CURVEs as well
|
|
code += 2;
|
|
|
|
curveto[0][0] = *pt;
|
|
curveto[0][1] = *(++pt);
|
|
++pt;
|
|
curveto[1][0] = *(++pt);
|
|
curveto[1][1] = *(++pt);
|
|
++pt;
|
|
curveto[2][0] = curX = *(++pt);
|
|
curveto[2][1] = curY = *(++pt);
|
|
++pt;
|
|
out << " C " << curveto[0][0] << " " << curveto[0][1] << endl
|
|
<< " " << curveto[1][0] << " " << curveto[1][1] << endl
|
|
<< " " << curveto[2][0] << " " << curveto[2][1] << endl;
|
|
}
|
|
break;
|
|
default:
|
|
out << "<!-- Unrecognized control code: " << *code << " -->" << endl;
|
|
pt +=2;
|
|
break;
|
|
}
|
|
|
|
++code;
|
|
++pt;
|
|
}
|
|
|
|
out << " \" />" << endl
|
|
<< "</g>" << endl;
|
|
|
|
glRasterPos3dv(rasterPos);
|
|
gl2psSpecial(gl2psGetFileFormat(), out.str().c_str());
|
|
}
|
|
|
|
|
|
inline void vtkGL2PSUtilities::ProjectPoint(double point[3],
|
|
vtkMatrix4x4 *actorMatrix)
|
|
{
|
|
// Build transformation matrix
|
|
double glMatrix[16];
|
|
glGetDoublev(GL_PROJECTION_MATRIX, glMatrix);
|
|
vtkNew<vtkMatrix4x4> projectionMatrix;
|
|
projectionMatrix->DeepCopy(glMatrix);
|
|
projectionMatrix->Transpose();
|
|
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, glMatrix);
|
|
vtkNew<vtkMatrix4x4> modelviewMatrix;
|
|
modelviewMatrix->DeepCopy(glMatrix);
|
|
modelviewMatrix->Transpose();
|
|
|
|
vtkNew<vtkMatrix4x4> transformMatrix;
|
|
vtkMatrix4x4::Multiply4x4(projectionMatrix.GetPointer(),
|
|
modelviewMatrix.GetPointer(),
|
|
transformMatrix.GetPointer());
|
|
if (actorMatrix)
|
|
{
|
|
vtkMatrix4x4::Multiply4x4(transformMatrix.GetPointer(),
|
|
actorMatrix,
|
|
transformMatrix.GetPointer());
|
|
}
|
|
|
|
double viewport[4];
|
|
glGetDoublev(GL_VIEWPORT, viewport);
|
|
double depthRange[2];
|
|
glGetDoublev(GL_DEPTH_RANGE, depthRange);
|
|
|
|
const double halfWidth = viewport[2] * 0.5;
|
|
const double halfHeight = viewport[3] * 0.5;
|
|
const double zFactor1 = (depthRange[1] - depthRange[0]) * 0.5;
|
|
const double zFactor2 = (depthRange[1] + depthRange[0]) * 0.5;
|
|
|
|
double tmp[4] = {point[0], point[1], point[2], 1.0};
|
|
vtkGL2PSUtilities::ProjectPoint(tmp, transformMatrix.GetPointer(), viewport,
|
|
halfWidth, halfHeight, zFactor1, zFactor2);
|
|
point[0] = tmp[0];
|
|
point[1] = tmp[1];
|
|
point[2] = tmp[2];
|
|
}
|
|
|
|
inline void vtkGL2PSUtilities::ProjectPoint(double point[4],
|
|
vtkMatrix4x4 *transformMatrix,
|
|
double viewportOrigin[],
|
|
double halfWidth, double halfHeight,
|
|
double zfact1, double zfact2)
|
|
{
|
|
// Convert world to clip coordinates:
|
|
// <out point> = [projection] [modelview] [actor matrix] <in point>
|
|
transformMatrix->MultiplyPoint(point, point);
|
|
// Clip to NDC
|
|
const double invW = 1.0 / point[3];
|
|
point[0] *= invW;
|
|
point[1] *= invW;
|
|
point[2] *= invW;
|
|
// NDC to device:
|
|
point[0] = point[0] * halfWidth + viewportOrigin[0] + halfWidth;
|
|
point[1] = point[1] * halfHeight + viewportOrigin[1] + halfHeight;
|
|
point[2] = point[2] * zfact1 + zfact2;
|
|
}
|
|
|
|
inline void vtkGL2PSUtilities::ProjectPoints(vtkPoints *points,
|
|
vtkMatrix4x4 *actorMatrix)
|
|
{
|
|
// Build transformation matrix
|
|
double glMatrix[16];
|
|
glGetDoublev(GL_PROJECTION_MATRIX, glMatrix);
|
|
vtkNew<vtkMatrix4x4> projectionMatrix;
|
|
projectionMatrix->DeepCopy(glMatrix);
|
|
projectionMatrix->Transpose();
|
|
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, glMatrix);
|
|
vtkNew<vtkMatrix4x4> modelviewMatrix;
|
|
modelviewMatrix->DeepCopy(glMatrix);
|
|
modelviewMatrix->Transpose();
|
|
|
|
vtkNew<vtkMatrix4x4> transformMatrix;
|
|
vtkMatrix4x4::Multiply4x4(projectionMatrix.GetPointer(),
|
|
modelviewMatrix.GetPointer(),
|
|
transformMatrix.GetPointer());
|
|
if (actorMatrix)
|
|
{
|
|
vtkMatrix4x4::Multiply4x4(transformMatrix.GetPointer(),
|
|
actorMatrix,
|
|
transformMatrix.GetPointer());
|
|
}
|
|
|
|
double viewport[4];
|
|
glGetDoublev(GL_VIEWPORT, viewport);
|
|
double depthRange[2];
|
|
glGetDoublev(GL_DEPTH_RANGE, depthRange);
|
|
|
|
const double halfWidth = viewport[2] * 0.5;
|
|
const double halfHeight = viewport[3] * 0.5;
|
|
const double zFactor1 = (depthRange[1] - depthRange[0]) * 0.5;
|
|
const double zFactor2 = (depthRange[1] + depthRange[0]) * 0.5;
|
|
|
|
double point[4];
|
|
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i)
|
|
{
|
|
points->GetPoint(i, point);
|
|
point[3] = 1.0;
|
|
vtkGL2PSUtilities::ProjectPoint(point, transformMatrix.GetPointer(),
|
|
viewport, halfWidth, halfHeight, zFactor1,
|
|
zFactor2);
|
|
points->SetPoint(i, point);
|
|
}
|
|
}
|
|
|
|
void vtkGL2PSUtilities::UnprojectPoint(double point[4],
|
|
vtkMatrix4x4 *invTransformMatrix,
|
|
double viewportOrigin[],
|
|
double halfWidth, double halfHeight,
|
|
double zfact1, double zfact2)
|
|
{
|
|
point[0] = (point[0] - viewportOrigin[0] - halfWidth) / halfWidth;
|
|
point[1] = (point[1] - viewportOrigin[1] - halfHeight) / halfHeight;
|
|
point[2] = (point[2] - zfact2) / zfact1;
|
|
|
|
point[0] *= point[3];
|
|
point[1] *= point[3];
|
|
point[2] *= point[3];
|
|
|
|
invTransformMatrix->MultiplyPoint(point, point);
|
|
}
|
|
|
|
void vtkGL2PSUtilities::UnprojectPoints(double *points3D, vtkIdType numPoints,
|
|
vtkMatrix4x4 *actorMatrix)
|
|
{
|
|
// Build transformation matrix
|
|
double glMatrix[16];
|
|
glGetDoublev(GL_PROJECTION_MATRIX, glMatrix);
|
|
vtkNew<vtkMatrix4x4> projectionMatrix;
|
|
projectionMatrix->DeepCopy(glMatrix);
|
|
projectionMatrix->Transpose();
|
|
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, glMatrix);
|
|
vtkNew<vtkMatrix4x4> modelviewMatrix;
|
|
modelviewMatrix->DeepCopy(glMatrix);
|
|
modelviewMatrix->Transpose();
|
|
|
|
vtkNew<vtkMatrix4x4> transformMatrix;
|
|
vtkMatrix4x4::Multiply4x4(projectionMatrix.GetPointer(),
|
|
modelviewMatrix.GetPointer(),
|
|
transformMatrix.GetPointer());
|
|
if (actorMatrix)
|
|
{
|
|
vtkMatrix4x4::Multiply4x4(transformMatrix.GetPointer(),
|
|
actorMatrix,
|
|
transformMatrix.GetPointer());
|
|
}
|
|
|
|
transformMatrix->Invert();
|
|
|
|
double viewport[4];
|
|
glGetDoublev(GL_VIEWPORT, viewport);
|
|
double depthRange[2];
|
|
glGetDoublev(GL_DEPTH_RANGE, depthRange);
|
|
|
|
const double halfWidth = viewport[2] * 0.5;
|
|
const double halfHeight = viewport[3] * 0.5;
|
|
const double zFactor1 = (depthRange[1] - depthRange[0]) * 0.5;
|
|
const double zFactor2 = (depthRange[1] + depthRange[0]) * 0.5;
|
|
|
|
double point[4];
|
|
for (vtkIdType i = 0; i < numPoints; ++i)
|
|
{
|
|
std::copy(points3D + (i * 3), points3D + ((i + 1) * 3), point);
|
|
point[3] = 1.0;
|
|
vtkGL2PSUtilities::UnprojectPoint(point, transformMatrix.GetPointer(),
|
|
viewport, halfWidth, halfHeight, zFactor1,
|
|
zFactor2);
|
|
std::copy(point, point + 3, points3D + (i * 3));
|
|
}
|
|
}
|