mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1101 lines
33 KiB
C++
1101 lines
33 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkMatplotlibMathTextUtilities.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 "vtkPython.h" // must be the first thing that's included.
|
|
#include "vtkPythonCompatibility.h"
|
|
#include "vtkMatplotlibMathTextUtilities.h"
|
|
|
|
#include "vtkCommand.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkImageReslice.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkNew.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPath.h"
|
|
#include "vtkPoints.h"
|
|
#include "vtkPythonInterpreter.h"
|
|
#include "vtkStdString.h"
|
|
#include "vtkSmartPyObject.h"
|
|
#include "vtkTextProperty.h"
|
|
#include "vtkTransform.h"
|
|
|
|
#include <vtksys/SystemTools.hxx>
|
|
|
|
#include <vector>
|
|
|
|
// We need to define Py_ssize_t for older python API version
|
|
#if PYTHON_API_VERSION < 1013
|
|
// Taken from pyport.h
|
|
# ifdef HAVE_SSIZE_T
|
|
typedef ssize_t Py_ssize_t;
|
|
# elif SIZEOF_VOID_P == SIZEOF_SIZE_T
|
|
typedef Py_intptr_t Py_ssize_t;
|
|
# else
|
|
# error "Python needs a typedef for Py_ssize_t."
|
|
# endif
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkMatplotlibMathTextUtilities::Availability
|
|
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable =
|
|
vtkMatplotlibMathTextUtilities::NOT_TESTED;
|
|
|
|
// A macro that is used in New() to print warnings if VTK_MATPLOTLIB_DEBUG
|
|
// is defined in the environment. Use vtkGenericWarningMacro to allow this to
|
|
// work in release mode builds.
|
|
#define vtkMplStartUpDebugMacro(x) if(debug){vtkGenericWarningMacro(x);}
|
|
|
|
vtkObjectFactoryNewMacro(vtkMatplotlibMathTextUtilities)
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkMatplotlibMathTextUtilities::Availability
|
|
vtkMatplotlibMathTextUtilities::CheckMPLAvailability()
|
|
{
|
|
if (vtkMatplotlibMathTextUtilities::MPLMathTextAvailable != NOT_TESTED)
|
|
{
|
|
// Already tested. Nothing to do now.
|
|
return vtkMatplotlibMathTextUtilities::MPLMathTextAvailable;
|
|
}
|
|
|
|
// Enable startup debugging output. This will be set to true when
|
|
// VTK_MATPLOTLIB_DEBUG is defined in the process environment.
|
|
bool debug = (vtksys::SystemTools::GetEnv("VTK_MATPLOTLIB_DEBUG") != NULL);
|
|
|
|
// Initialize the python interpretor if needed
|
|
vtkMplStartUpDebugMacro("Initializing Python, if not already.");
|
|
vtkPythonInterpreter::Initialize();
|
|
vtkMplStartUpDebugMacro("Attempting to import matplotlib.");
|
|
if (PyErr_Occurred() || !PyImport_ImportModule("matplotlib") || PyErr_Occurred())
|
|
{
|
|
// FIXME: Check if we need this. Wouldn't pipe-ing the stdout/stderr make
|
|
// this unnecessary?
|
|
|
|
// Fetch the exception info. Note that value and traceback may still be
|
|
// NULL after the call to PyErr_Fetch().
|
|
PyObject *type = NULL;
|
|
PyObject *value = NULL;
|
|
PyObject *traceback = NULL;
|
|
PyErr_Fetch(&type, &value, &traceback);
|
|
vtkSmartPyObject typeStr(PyObject_Str(type));
|
|
vtkSmartPyObject valueStr(PyObject_Str(value));
|
|
vtkSmartPyObject tracebackStr(PyObject_Str(traceback));
|
|
vtkMplStartUpDebugMacro(
|
|
"Error during matplotlib import:\n"
|
|
<< "\nStack:\n"
|
|
<< (tracebackStr.GetPointer() == NULL
|
|
? "(none)"
|
|
: const_cast<char*>(
|
|
PyString_AsString(tracebackStr.GetPointer())))
|
|
<< "\nValue:\n"
|
|
<< (valueStr.GetPointer() == NULL
|
|
? "(none)"
|
|
: const_cast<char*>(
|
|
PyString_AsString(valueStr.GetPointer())))
|
|
<< "\nType:\n"
|
|
<< (typeStr.GetPointer() == NULL
|
|
? "(none)"
|
|
: const_cast<char*>(
|
|
PyString_AsString(typeStr.GetPointer()))));
|
|
PyErr_Clear();
|
|
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable = UNAVAILABLE;
|
|
}
|
|
else
|
|
{
|
|
vtkMplStartUpDebugMacro("Successfully imported matplotlib.");
|
|
vtkMatplotlibMathTextUtilities::MPLMathTextAvailable = AVAILABLE;
|
|
}
|
|
|
|
return vtkMatplotlibMathTextUtilities::MPLMathTextAvailable;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::IsAvailable()
|
|
{
|
|
return this->CheckMPLAvailability() == AVAILABLE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkMatplotlibMathTextUtilities::vtkMatplotlibMathTextUtilities()
|
|
: Superclass(), MaskParser(NULL), PathParser(NULL), FontPropertiesClass(NULL),
|
|
ScaleToPowerOfTwo(true)
|
|
{
|
|
this->Interpreter = vtkPythonInterpreter::New();
|
|
this->Interpreter->AddObserver(vtkCommand::ExitEvent,
|
|
this, &vtkMatplotlibMathTextUtilities::CleanupPythonObjects);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkMatplotlibMathTextUtilities::~vtkMatplotlibMathTextUtilities()
|
|
{
|
|
this->CleanupPythonObjects();
|
|
this->Interpreter->Delete();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkMatplotlibMathTextUtilities::CleanupPythonObjects()
|
|
{
|
|
Py_XDECREF(this->MaskParser);
|
|
Py_XDECREF(this->PathParser);
|
|
Py_XDECREF(this->FontPropertiesClass);
|
|
|
|
this->MaskParser = NULL;
|
|
this->PathParser = NULL;
|
|
this->FontPropertiesClass = NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::InitializeMaskParser()
|
|
{
|
|
// ensure that Python is initialized.
|
|
vtkPythonInterpreter::Initialize();
|
|
|
|
vtkSmartPyObject mplMathTextLib(PyImport_ImportModule("matplotlib.mathtext"));
|
|
if (this->CheckForError(mplMathTextLib.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPyObject mathTextParserClass(
|
|
PyObject_GetAttrString(mplMathTextLib.GetPointer(), "MathTextParser"));
|
|
if (this->CheckForError(mathTextParserClass.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this->MaskParser =
|
|
PyObject_CallFunction(mathTextParserClass.GetPointer(),
|
|
const_cast<char*>("s"), "bitmap");
|
|
if (this->CheckForError(this->MaskParser))
|
|
{
|
|
Py_CLEAR(this->MaskParser);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::InitializePathParser()
|
|
{
|
|
// ensure that Python is initialized.
|
|
vtkPythonInterpreter::Initialize();
|
|
|
|
vtkSmartPyObject mplTextPathLib(PyImport_ImportModule("matplotlib.textpath"));
|
|
if (this->CheckForError(mplTextPathLib.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPyObject textToPathClass(
|
|
PyObject_GetAttrString(mplTextPathLib.GetPointer(), "TextToPath"));
|
|
if (this->CheckForError(textToPathClass.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this->PathParser = PyObject_CallFunction(textToPathClass.GetPointer(), NULL);
|
|
if (this->CheckForError(this->PathParser))
|
|
{
|
|
Py_CLEAR(this->PathParser);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::InitializeFontPropertiesClass()
|
|
{
|
|
// ensure that Python is initialized.
|
|
vtkPythonInterpreter::Initialize();
|
|
|
|
vtkSmartPyObject mplFontManagerLib(
|
|
PyImport_ImportModule("matplotlib.font_manager"));
|
|
if (this->CheckForError(mplFontManagerLib.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this->FontPropertiesClass = PyObject_GetAttrString(
|
|
mplFontManagerLib.GetPointer(), "FontProperties");
|
|
if (this->CheckForError(this->FontPropertiesClass))
|
|
{
|
|
Py_CLEAR(this->FontPropertiesClass);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::CheckForError()
|
|
{
|
|
PyObject *exception = PyErr_Occurred();
|
|
if (exception)
|
|
{
|
|
if (this->Debug)
|
|
{
|
|
// Fetch the exception info. Note that value and traceback may still be
|
|
// NULL after the call to PyErr_Fetch().
|
|
PyObject *type = NULL;
|
|
PyObject *value = NULL;
|
|
PyObject *traceback = NULL;
|
|
PyErr_Fetch(&type, &value, &traceback);
|
|
vtkSmartPyObject typeStr(PyObject_Str(type));
|
|
vtkSmartPyObject valueStr(PyObject_Str(value));
|
|
vtkSmartPyObject tracebackStr(PyObject_Str(traceback));
|
|
vtkWarningMacro(<< "Python exception raised:\n"
|
|
<< "\nStack:\n"
|
|
<< (tracebackStr.GetPointer() == NULL
|
|
? "(none)"
|
|
: const_cast<char*>(
|
|
PyString_AsString(tracebackStr.GetPointer())))
|
|
<< "\nValue:\n"
|
|
<< (valueStr.GetPointer() == NULL
|
|
? "(none)"
|
|
: const_cast<char*>(
|
|
PyString_AsString(valueStr.GetPointer())))
|
|
<< "\nType:\n"
|
|
<< (typeStr.GetPointer() == NULL
|
|
? "(none)"
|
|
: const_cast<char*>(
|
|
PyString_AsString(typeStr.GetPointer()))));
|
|
}
|
|
PyErr_Clear();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::CheckForError(PyObject *object)
|
|
{
|
|
// Print any exceptions
|
|
bool result = this->CheckForError();
|
|
|
|
if (object == NULL)
|
|
{
|
|
vtkDebugMacro(<< "Object is NULL!");
|
|
return true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
PyObject *
|
|
vtkMatplotlibMathTextUtilities::GetFontProperties(vtkTextProperty *tprop)
|
|
{
|
|
if (!this->IsAvailable())
|
|
{
|
|
vtkErrorMacro(<<"Matplotlib rendering is unavailable.");
|
|
return NULL;
|
|
}
|
|
|
|
if (!this->FontPropertiesClass)
|
|
{
|
|
if (!this->InitializeFontPropertiesClass())
|
|
{
|
|
vtkErrorMacro(<<"FontPropertiesClass is not initialized!");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
char tpropFamily[16];
|
|
char tpropStyle[16];
|
|
char tpropVariant[16] = "normal";
|
|
char tpropWeight[16];
|
|
long tpropFontSize;
|
|
|
|
switch (tprop->GetFontFamily())
|
|
{
|
|
default:
|
|
case VTK_ARIAL:
|
|
strcpy(tpropFamily, "sans-serif");
|
|
break;
|
|
case VTK_COURIER:
|
|
strcpy(tpropFamily, "monospace");
|
|
break;
|
|
case VTK_TIMES:
|
|
strcpy(tpropFamily, "serif");
|
|
break;
|
|
}
|
|
|
|
if (tprop->GetItalic())
|
|
{
|
|
strcpy(tpropStyle, "italic");
|
|
}
|
|
else
|
|
{
|
|
strcpy(tpropStyle, "normal");
|
|
}
|
|
|
|
if (tprop->GetBold())
|
|
{
|
|
strcpy(tpropWeight, "bold");
|
|
}
|
|
else
|
|
{
|
|
strcpy(tpropWeight, "normal");
|
|
}
|
|
|
|
tpropFontSize = tprop->GetFontSize();
|
|
|
|
return PyObject_CallFunction(this->FontPropertiesClass,
|
|
const_cast<char*>("sssssi"), tpropFamily,
|
|
tpropStyle, tpropVariant, tpropStyle,
|
|
tpropWeight, tpropFontSize);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkMatplotlibMathTextUtilities::GetJustifiedBBox(int rows, int cols,
|
|
vtkTextProperty *tprop,
|
|
int bbox[])
|
|
{
|
|
bbox[0] = 0;
|
|
bbox[1] = cols - 1;
|
|
bbox[2] = 0;
|
|
bbox[3] = rows - 1;
|
|
|
|
int justifyOffset[2];
|
|
switch (tprop->GetJustification())
|
|
{
|
|
default:
|
|
case VTK_TEXT_LEFT:
|
|
justifyOffset[0] = 0;
|
|
break;
|
|
case VTK_TEXT_CENTERED:
|
|
justifyOffset[0] = bbox[1] / 2;
|
|
break;
|
|
case VTK_TEXT_RIGHT:
|
|
justifyOffset[0] = bbox[1];
|
|
break;
|
|
}
|
|
switch (tprop->GetVerticalJustification())
|
|
{
|
|
default:
|
|
case VTK_TEXT_BOTTOM:
|
|
justifyOffset[1] = 0;
|
|
break;
|
|
case VTK_TEXT_CENTERED:
|
|
justifyOffset[1] = bbox[3] / 2;
|
|
break;
|
|
case VTK_TEXT_TOP:
|
|
justifyOffset[1] = bbox[3];
|
|
break;
|
|
}
|
|
|
|
bbox[0] -= justifyOffset[0];
|
|
bbox[1] -= justifyOffset[0];
|
|
bbox[2] -= justifyOffset[1];
|
|
bbox[3] -= justifyOffset[1];
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkMatplotlibMathTextUtilities::RotateCorners(double angleDeg,
|
|
double corners[4][2],
|
|
double bbox[4])
|
|
{
|
|
double angleRad = vtkMath::RadiansFromDegrees(angleDeg);
|
|
double c = cos(angleRad);
|
|
double s = sin(angleRad);
|
|
// Rotate corners
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
int newpt[2];
|
|
newpt[0] = c * corners[i][0] - s * corners[i][1];
|
|
newpt[1] = s * corners[i][0] + c * corners[i][1];
|
|
corners[i][0] = newpt[0];
|
|
corners[i][1] = newpt[1];
|
|
}
|
|
// Find new bounds
|
|
bbox[0] = VTK_INT_MAX;
|
|
bbox[1] = VTK_INT_MIN;
|
|
bbox[2] = VTK_INT_MAX;
|
|
bbox[3] = VTK_INT_MIN;
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
if (corners[i][0] < bbox[0])
|
|
{
|
|
bbox[0] = corners[i][0];
|
|
}
|
|
if (corners[i][0] > bbox[1])
|
|
{
|
|
bbox[1] = corners[i][0];
|
|
}
|
|
if (corners[i][1] < bbox[2])
|
|
{
|
|
bbox[2] = corners[i][1];
|
|
}
|
|
if (corners[i][1] > bbox[3])
|
|
{
|
|
bbox[3] = corners[i][1];
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This is more or less ported from vtkFreeTypeTools.
|
|
bool vtkMatplotlibMathTextUtilities::PrepareImageData(vtkImageData *data,
|
|
int textBbox[4])
|
|
{
|
|
// Calculate the bbox's dimensions
|
|
int textDims[2];
|
|
textDims[0] = (textBbox[1] - textBbox[0] + 1);
|
|
textDims[1] = (textBbox[3] - textBbox[2] + 1);
|
|
|
|
// Calculate the size the image needs to be.
|
|
int targetDims[3];
|
|
targetDims[0] = textDims[0];
|
|
targetDims[1] = textDims[1];
|
|
targetDims[2] = 1;
|
|
// Scale to the next highest power of 2 if required.
|
|
if (this->ScaleToPowerOfTwo)
|
|
{
|
|
targetDims[0] = vtkMath::NearestPowerOfTwo(targetDims[0]);
|
|
targetDims[1] = vtkMath::NearestPowerOfTwo(targetDims[1]);
|
|
}
|
|
|
|
// Calculate the target extent of the image.
|
|
int targetExtent[6];
|
|
targetExtent[0] = textBbox[0];
|
|
targetExtent[1] = textBbox[0] + targetDims[0] - 1;
|
|
targetExtent[2] = textBbox[2];
|
|
targetExtent[3] = textBbox[2] + targetDims[1] - 1;
|
|
targetExtent[4] = 0;
|
|
targetExtent[5] = 0;
|
|
|
|
// Get the actual image extents and increments
|
|
int imageExtent[6];
|
|
double imageSpacing[3];
|
|
data->GetExtent(imageExtent);
|
|
data->GetSpacing(imageSpacing);
|
|
|
|
// Do we need to reallocate the image memory?
|
|
if (data->GetScalarType() != VTK_UNSIGNED_CHAR ||
|
|
data->GetNumberOfScalarComponents() != 4 ||
|
|
imageExtent[0] != targetExtent[0] ||
|
|
imageExtent[1] != targetExtent[1] ||
|
|
imageExtent[2] != targetExtent[2] ||
|
|
imageExtent[3] != targetExtent[3] ||
|
|
imageExtent[4] != targetExtent[4] ||
|
|
imageExtent[5] != targetExtent[5] ||
|
|
fabs(imageSpacing[0] - 1.0) > 1e-10 ||
|
|
fabs(imageSpacing[1] - 1.0) > 1e-10 ||
|
|
fabs(imageSpacing[2] - 1.0) > 1e-10 )
|
|
{
|
|
data->SetSpacing(1.0, 1.0, 1.0);
|
|
data->SetExtent(targetExtent);
|
|
data->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
|
|
}
|
|
|
|
// Clear the image
|
|
memset(data->GetScalarPointer(), 0,
|
|
(data->GetNumberOfPoints() * data->GetNumberOfScalarComponents()));
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::GetBoundingBox(
|
|
vtkTextProperty *tprop, const char *str, int dpi, int bbox[4])
|
|
{
|
|
vtkTextRenderer::Metrics metrics;
|
|
if (!this->GetMetrics(tprop, str, dpi, metrics))
|
|
{
|
|
return false;
|
|
}
|
|
std::copy(metrics.BoundingBox.GetData(), metrics.BoundingBox.GetData() + 4,
|
|
bbox);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::GetMetrics(
|
|
vtkTextProperty *tprop, const char *str, int dpi,
|
|
vtkTextRenderer::Metrics &metrics)
|
|
{
|
|
if (!this->IsAvailable())
|
|
{
|
|
vtkErrorMacro(<<"Matplotlib rendering is unavailable.");
|
|
return false;
|
|
}
|
|
|
|
if (!this->MaskParser)
|
|
{
|
|
if (!this->InitializeMaskParser())
|
|
{
|
|
vtkErrorMacro(<<"MaskParser is not initialized!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
vtkDebugMacro(<<"Calculating metrics for '" << str << "'");
|
|
|
|
long int rows = 0;
|
|
long int cols = 0;
|
|
|
|
vtkSmartPyObject resultTuple(PyObject_CallMethod(this->MaskParser,
|
|
const_cast<char*>("to_mask"),
|
|
const_cast<char*>("sii"),
|
|
const_cast<char*>(str),
|
|
tprop->GetFontSize(),
|
|
dpi));
|
|
if (this->CheckForError(resultTuple.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// numpyArray is a borrowed reference, no smart wrapper needed:
|
|
PyObject *numpyArray = PyTuple_GetItem(resultTuple.GetPointer(), 0);
|
|
if (this->CheckForError(numpyArray))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPyObject dimTuple(PyObject_GetAttrString(numpyArray,
|
|
const_cast<char*>("shape")));
|
|
if (this->CheckForError(dimTuple.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PyArg_ParseTuple(dimTuple.GetPointer(), "ii", &rows, &cols);
|
|
if (this->CheckForError())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int extent[4];
|
|
this->GetJustifiedBBox(rows, cols, tprop, extent);
|
|
|
|
// Determine the dimensions of the rotated image
|
|
double angleDeg = tprop->GetOrientation();
|
|
// Corners of original image
|
|
double corners[4][2] = {
|
|
{static_cast<double>(extent[0]), static_cast<double>(extent[2])}, // TL
|
|
{static_cast<double>(extent[1]), static_cast<double>(extent[2])}, // TR
|
|
{static_cast<double>(extent[0]), static_cast<double>(extent[3])}, // BL
|
|
{static_cast<double>(extent[1]), static_cast<double>(extent[3])} }; // BR
|
|
|
|
double bboxd[4];
|
|
this->RotateCorners(angleDeg, corners, bboxd);
|
|
|
|
metrics.BoundingBox[0] = vtkMath::Floor(bboxd[0]);
|
|
metrics.BoundingBox[1] = vtkMath::Ceil(bboxd[1]);
|
|
metrics.BoundingBox[2] = vtkMath::Floor(bboxd[2]);
|
|
metrics.BoundingBox[3] = vtkMath::Ceil(bboxd[3]);
|
|
|
|
metrics.TopLeft[0] = vtkMath::Round(corners[0][0]);
|
|
metrics.TopLeft[1] = vtkMath::Round(corners[0][1]);
|
|
metrics.TopRight[0] = vtkMath::Round(corners[1][0]);
|
|
metrics.TopRight[1] = vtkMath::Round(corners[1][1]);
|
|
metrics.BottomLeft[0] = vtkMath::Round(corners[2][0]);
|
|
metrics.BottomLeft[1] = vtkMath::Round(corners[2][1]);
|
|
metrics.BottomRight[0] = vtkMath::Round(corners[3][0]);
|
|
metrics.BottomRight[1] = vtkMath::Round(corners[3][1]);
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::RenderString(const char *str,
|
|
vtkImageData *image,
|
|
vtkTextProperty *tprop,
|
|
int dpi,
|
|
int textDims[2])
|
|
{
|
|
if (!this->IsAvailable())
|
|
{
|
|
vtkErrorMacro(<<"Matplotlib rendering is unavailable.");
|
|
return false;
|
|
}
|
|
|
|
if (!this->MaskParser)
|
|
{
|
|
if (!this->InitializeMaskParser())
|
|
{
|
|
vtkErrorMacro(<<"MaskParser is not initialized!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
vtkDebugMacro(<<"Converting '" << str << "' into MathText image...");
|
|
|
|
long int rows = 0;
|
|
long int cols = 0;
|
|
long int ind = 0;
|
|
|
|
double *fgColor = tprop->GetColor();
|
|
unsigned char fgR = static_cast<unsigned char>(fgColor[0] * 255);
|
|
unsigned char fgG = static_cast<unsigned char>(fgColor[1] * 255);
|
|
unsigned char fgB = static_cast<unsigned char>(fgColor[2] * 255);
|
|
double fgA = tprop->GetOpacity();
|
|
|
|
double *bgColor = tprop->GetBackgroundColor();
|
|
unsigned char bgR = static_cast<unsigned char>(bgColor[0] * 255);
|
|
unsigned char bgG = static_cast<unsigned char>(bgColor[1] * 255);
|
|
unsigned char bgB = static_cast<unsigned char>(bgColor[2] * 255);
|
|
double bgA = tprop->GetBackgroundOpacity();
|
|
bool hasBackground = (static_cast<unsigned char>(bgA * 255) != 0);
|
|
|
|
vtkSmartPyObject resultTuple(PyObject_CallMethod(this->MaskParser,
|
|
const_cast<char*>("to_mask"),
|
|
const_cast<char*>("sii"),
|
|
const_cast<char*>(str),
|
|
tprop->GetFontSize(), dpi));
|
|
if (this->CheckForError(resultTuple.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// numpyArray is a borrowed reference, no smart wrapper needed:
|
|
PyObject *numpyArray = PyTuple_GetItem(resultTuple.GetPointer(), 0);
|
|
if (this->CheckForError(numpyArray))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPyObject flatArray(PyObject_CallMethod(numpyArray,
|
|
const_cast<char*>("flatten"),
|
|
const_cast<char*>("")));
|
|
if (this->CheckForError(flatArray.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPyObject list(PyObject_CallMethod(flatArray.GetPointer(),
|
|
const_cast<char*>("tolist"),
|
|
const_cast<char*>("")));
|
|
if (this->CheckForError(list.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPyObject dimTuple(PyObject_GetAttrString(numpyArray,
|
|
const_cast<char*>("shape")));
|
|
if (this->CheckForError(dimTuple.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PyArg_ParseTuple(dimTuple.GetPointer(), "ii", &rows, &cols);
|
|
if (this->CheckForError())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//numPixels = PyObject_Length(list.GetPointer());
|
|
if (this->CheckForError())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (textDims)
|
|
{
|
|
textDims[0] = cols;
|
|
textDims[1] = rows;
|
|
}
|
|
|
|
// Create justified bounding box.
|
|
int bbox[4];
|
|
this->GetJustifiedBBox(rows, cols, tprop, bbox);
|
|
|
|
this->PrepareImageData(image, bbox);
|
|
|
|
for (long int row = bbox[3]; row >= bbox[2]; --row)
|
|
{
|
|
for (long int col = bbox[0]; col <= bbox[1]; ++col)
|
|
{
|
|
// item is borrowed, no need for a smart wrapper
|
|
PyObject *item = PyList_GetItem(list.GetPointer(), ind++);
|
|
if (this->CheckForError(item))
|
|
{
|
|
return false;
|
|
}
|
|
const unsigned char val = static_cast<unsigned char>(PyInt_AsLong(item));
|
|
if (this->CheckForError())
|
|
{
|
|
return false;
|
|
}
|
|
unsigned char *ptr =
|
|
static_cast<unsigned char*>(image->GetScalarPointer(col, row, 0));
|
|
|
|
if (hasBackground)
|
|
{
|
|
const float fg_blend = fgA * (val / 255.f);
|
|
const float bg_blend = 1.f - fg_blend;
|
|
|
|
ptr[0] = static_cast<unsigned char>(bg_blend * bgR + fg_blend * fgR);
|
|
ptr[1] = static_cast<unsigned char>(bg_blend * bgG + fg_blend * fgG);
|
|
ptr[2] = static_cast<unsigned char>(bg_blend * bgB + fg_blend * fgB);
|
|
ptr[3] = static_cast<unsigned char>(255 * (fg_blend + bgA * bg_blend));
|
|
}
|
|
else
|
|
{
|
|
ptr[0] = fgR;
|
|
ptr[1] = fgG;
|
|
ptr[2] = fgB;
|
|
ptr[3] = static_cast<unsigned char>(val * fgA);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark the image data as modified, as it is possible that only
|
|
// vtkImageData::Get*Pointer methods will be called, which do not update the
|
|
// MTime.
|
|
image->Modified();
|
|
|
|
// Determine the dimensions of the rotated image
|
|
double angleDeg = tprop->GetOrientation();
|
|
// Save some time if no rotation needed
|
|
if (fabs(angleDeg) < 0.01)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Corners of original image
|
|
double corners[4][2] = { {static_cast<double>(bbox[0]),
|
|
static_cast<double>(bbox[2])},
|
|
{static_cast<double>(bbox[1]),
|
|
static_cast<double>(bbox[2])},
|
|
{static_cast<double>(bbox[0]),
|
|
static_cast<double>(bbox[3])},
|
|
{static_cast<double>(bbox[1]),
|
|
static_cast<double>(bbox[3])} };
|
|
double bboxd[4];
|
|
|
|
// Rotate the corners of the image and determine the bounding box
|
|
this->RotateCorners(angleDeg, corners, bboxd);
|
|
|
|
// Also rotate the text dimensions.
|
|
if (textDims)
|
|
{
|
|
double text_bbox[4];
|
|
corners[0][0] = static_cast<double>(0);
|
|
corners[0][1] = static_cast<double>(0);
|
|
corners[1][0] = static_cast<double>(0);
|
|
corners[1][1] = static_cast<double>(textDims[1]);
|
|
corners[2][0] = static_cast<double>(textDims[0]);
|
|
corners[2][1] = static_cast<double>(textDims[1]);
|
|
corners[3][0] = static_cast<double>(textDims[0]) ;
|
|
corners[3][1] = static_cast<double>(0);
|
|
this->RotateCorners(angleDeg, corners, text_bbox);
|
|
textDims[0] = std::ceil(text_bbox[1] - text_bbox[0]);
|
|
textDims[1] = std::ceil(text_bbox[3] - text_bbox[2]);
|
|
}
|
|
|
|
bbox[0] = vtkMath::Floor(bboxd[0]);
|
|
bbox[1] = vtkMath::Ceil(bboxd[1]);
|
|
bbox[2] = vtkMath::Floor(bboxd[2]);
|
|
bbox[3] = vtkMath::Ceil(bboxd[3]);
|
|
|
|
// Rotate the temporary image into the returned image:
|
|
vtkNew<vtkTransform> rotation;
|
|
rotation->RotateWXYZ(-angleDeg, 0, 0, 1);
|
|
// Dummy image with the output dimensions
|
|
vtkNew<vtkImageData> dummyImage;
|
|
this->PrepareImageData(dummyImage.GetPointer(), bbox);
|
|
vtkNew<vtkImageReslice> rotator;
|
|
rotator->SetInputData(image);
|
|
rotator->SetInformationInput(dummyImage.GetPointer());
|
|
rotator->SetResliceTransform(rotation.GetPointer());
|
|
rotator->SetInterpolationModeToLinear();
|
|
rotator->Update();
|
|
image->ShallowCopy(rotator->GetOutput());
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkMatplotlibMathTextUtilities::StringToPath(const char *str,
|
|
vtkPath *path,
|
|
vtkTextProperty *tprop,
|
|
int dpi)
|
|
{
|
|
if (!this->IsAvailable())
|
|
{
|
|
vtkErrorMacro(<<"Matplotlib rendering is unavailable.");
|
|
return false;
|
|
}
|
|
|
|
if (!this->PathParser)
|
|
{
|
|
if (!this->InitializePathParser())
|
|
{
|
|
vtkErrorMacro(<<"PathParser is not initialized!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
vtkDebugMacro(<<"Converting '" << str << "' into a vtkPath...");
|
|
|
|
// Matplotlib path codes:
|
|
const int pathStop = 0;
|
|
const int pathMoveTo = 1;
|
|
const int pathLineTo = 2;
|
|
const int pathCurve3 = 3;
|
|
const int pathCurve4 = 4;
|
|
const int pathClosePoly = 0x4f;
|
|
|
|
// List sizes:
|
|
Py_ssize_t numCodes;
|
|
Py_ssize_t numVerts;
|
|
|
|
// Temp vars:
|
|
float origin[2] = {0.0, 0.0};
|
|
float vert[2];
|
|
float delta[2] = {0.0, 0.0};
|
|
int code;
|
|
bool hasOrigin = false;
|
|
|
|
// Bounding box for all control points, used for justification
|
|
float cbox[4] = {VTK_FLOAT_MAX, VTK_FLOAT_MAX, VTK_FLOAT_MIN, VTK_FLOAT_MIN};
|
|
|
|
// The path is always generated using a 100pt font @72 dpi. Use this factor to
|
|
// recover the font.
|
|
const float fontScale = (tprop->GetFontSize() / 100.f) * (dpi / 72.f) ;
|
|
|
|
path->Reset();
|
|
|
|
// Create the font property
|
|
vtkSmartPyObject pyFontProp(this->GetFontProperties(tprop));
|
|
if (this->CheckForError(pyFontProp.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPyObject pyResultTuple(
|
|
PyObject_CallMethod(this->PathParser,
|
|
const_cast<char*>("get_text_path"),
|
|
const_cast<char*>("Osi"),
|
|
pyFontProp.GetPointer(),// prop
|
|
const_cast<char*>(str), // texstring
|
|
1, // boolean, ismath
|
|
0)); // boolean, usetex
|
|
if (this->CheckForError(pyResultTuple.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// pyVerts and pyCodes are borrowed references -- no need for smart wrappers
|
|
PyObject *pyVerts = PyTuple_GetItem(pyResultTuple.GetPointer(), 0);
|
|
PyObject *pyCodes = PyTuple_GetItem(pyResultTuple.GetPointer(), 1);
|
|
if (this->CheckForError(pyVerts) ||
|
|
this->CheckForError(pyCodes))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Both verts and codes are lists?
|
|
if (!PySequence_Check(pyVerts) || !PySequence_Check(pyCodes))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
numVerts = PySequence_Size(pyVerts);
|
|
numCodes = PySequence_Size(pyCodes);
|
|
if (numVerts != numCodes)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
path->Allocate(numVerts);
|
|
|
|
for (Py_ssize_t i = 0; i < numVerts; ++i)
|
|
{
|
|
vtkSmartPyObject pyVert(PySequence_GetItem(pyVerts, i));
|
|
vtkSmartPyObject pyCode(PySequence_GetItem(pyCodes, i));
|
|
if (this->CheckForError(pyVert.GetPointer()) ||
|
|
this->CheckForError(pyCode.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// pyVert is sometimes a numpy array, sometimes it's a tuple.
|
|
// Initialize the following objects in the following conditional, then
|
|
// convert to smart pointers afterwards.
|
|
PyObject *pyVertXObj = NULL;
|
|
PyObject *pyVertYObj = NULL;
|
|
if (pyVert->ob_type == &PyTuple_Type)
|
|
{
|
|
pyVertXObj = PyTuple_GetItem(pyVert.GetPointer(), 0);
|
|
pyVertYObj = PyTuple_GetItem(pyVert.GetPointer(), 1);
|
|
// Increase reference count -- the other branch returns a new reference,
|
|
// this keeps cleanup consistent
|
|
if (pyVertXObj)
|
|
{
|
|
Py_INCREF(pyVertXObj);
|
|
}
|
|
if (pyVertYObj)
|
|
{
|
|
Py_INCREF(pyVertYObj);
|
|
}
|
|
}
|
|
else // Assume numpy array. Convert to list and extract elements.
|
|
{
|
|
vtkSmartPyObject pyVertList(PyObject_CallMethod(pyVert.GetPointer(),
|
|
const_cast<char*>("tolist"),
|
|
NULL));
|
|
if (this->CheckForError(pyVertList.GetPointer()) ||
|
|
PySequence_Size(pyVertList.GetPointer()) < 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pyVertXObj = PySequence_GetItem(pyVertList.GetPointer(), 0);
|
|
pyVertYObj = PySequence_GetItem(pyVertList.GetPointer(), 1);
|
|
}
|
|
|
|
vtkSmartPyObject pyVertX(pyVertXObj);
|
|
vtkSmartPyObject pyVertY(pyVertYObj);
|
|
if (this->CheckForError(pyVertX.GetPointer()) ||
|
|
this->CheckForError(pyVertY.GetPointer()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vert[0] = PyFloat_AsDouble(pyVertX.GetPointer()) * fontScale;
|
|
vert[1] = PyFloat_AsDouble(pyVertY.GetPointer()) * fontScale;
|
|
if (this->CheckForError())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (vert[0] < cbox[0])
|
|
{
|
|
cbox[0] = vert[0];
|
|
}
|
|
if (vert[1] < cbox[1])
|
|
{
|
|
cbox[1] = vert[1];
|
|
}
|
|
if (vert[0] > cbox[2])
|
|
{
|
|
cbox[2] = vert[0];
|
|
}
|
|
if (vert[1] > cbox[3])
|
|
{
|
|
cbox[3] = vert[1];
|
|
}
|
|
|
|
code = PyInt_AsLong(pyCode.GetPointer());
|
|
if (this->CheckForError())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (code)
|
|
{
|
|
case pathStop:
|
|
hasOrigin = false;
|
|
break;
|
|
case pathMoveTo:
|
|
path->InsertNextPoint(vert[0], vert[1], 0, vtkPath::MOVE_TO);
|
|
origin[0] = vert[0];
|
|
origin[1] = vert[1];
|
|
hasOrigin = true;
|
|
break;
|
|
case pathLineTo:
|
|
path->InsertNextPoint(vert[0], vert[1], 0, vtkPath::LINE_TO);
|
|
break;
|
|
case pathCurve3:
|
|
path->InsertNextPoint(vert[0], vert[1], 0, vtkPath::CONIC_CURVE);
|
|
break;
|
|
case pathCurve4:
|
|
path->InsertNextPoint(vert[0], vert[1], 0, vtkPath::CUBIC_CURVE);
|
|
break;
|
|
case pathClosePoly:
|
|
if (hasOrigin)
|
|
path->InsertNextPoint(origin[0], origin[1], 0, vtkPath::LINE_TO);
|
|
hasOrigin = false;
|
|
break;
|
|
default:
|
|
vtkWarningMacro(<<"Unrecognized code: " << code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Apply justification:
|
|
switch (tprop->GetJustification())
|
|
{
|
|
default:
|
|
case VTK_TEXT_LEFT:
|
|
delta[0] = -cbox[0];
|
|
break;
|
|
case VTK_TEXT_CENTERED:
|
|
delta[0] = -(cbox[2] - cbox[0]) * 0.5;
|
|
break;
|
|
case VTK_TEXT_RIGHT:
|
|
delta[0] = -cbox[2];
|
|
break;
|
|
}
|
|
switch (tprop->GetVerticalJustification())
|
|
{
|
|
default:
|
|
case VTK_TEXT_BOTTOM:
|
|
delta[1] = -cbox[1];
|
|
break;
|
|
case VTK_TEXT_CENTERED:
|
|
delta[1] = -(cbox[3] - cbox[1]) * 0.5;
|
|
break;
|
|
case VTK_TEXT_TOP:
|
|
delta[1] = -cbox[3];
|
|
}
|
|
|
|
const double theta = vtkMath::RadiansFromDegrees(tprop->GetOrientation());
|
|
const double sinTheta = sin(theta);
|
|
const double cosTheta = cos(theta);
|
|
vtkPoints *points = path->GetPoints();
|
|
double point[3];
|
|
double newPoint[3];
|
|
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i)
|
|
{
|
|
points->GetPoint(i, point);
|
|
point[0] += delta[0];
|
|
point[1] += delta[1];
|
|
newPoint[0] = (point[0] * cosTheta) - (point[1] * sinTheta);
|
|
newPoint[1] = (point[0] * sinTheta) + (point[1] * cosTheta);
|
|
newPoint[2] = point[2];
|
|
points->SetPoint(i, newPoint);
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkMatplotlibMathTextUtilities::PrintSelf(ostream &os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
|
|
os << indent << "MPLMathTextAvailable: ";
|
|
switch (this->MPLMathTextAvailable)
|
|
{
|
|
case vtkMatplotlibMathTextUtilities::NOT_TESTED:
|
|
os << "Not tested\n";
|
|
break;
|
|
case vtkMatplotlibMathTextUtilities::AVAILABLE:
|
|
os << "Available\n";
|
|
break;
|
|
default:
|
|
case vtkMatplotlibMathTextUtilities::UNAVAILABLE:
|
|
os << "Unavailable\n";
|
|
break;
|
|
}
|
|
|
|
os << indent << "MaskParser: " << this->MaskParser << endl;
|
|
os << indent << "PathParser: " << this->PathParser << endl;
|
|
os << indent << "FontPropertiesClass: " << this->FontPropertiesClass << endl;
|
|
}
|