mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1133 lines
36 KiB
C++
1133 lines
36 KiB
C++
/*=========================================================================
|
|
|
|
Program: ParaView
|
|
Module: $RCSfile$
|
|
|
|
Copyright (c) Kitware, Inc.
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 "vtkSMTransferFunctionProxy.h"
|
|
|
|
#include "vtkDoubleArray.h"
|
|
#include "vtkNew.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPVArrayInformation.h"
|
|
#include "vtkPVProminentValuesInformation.h"
|
|
#include "vtkPVXMLElement.h"
|
|
#include "vtkPVXMLParser.h"
|
|
#include "vtkScalarsToColors.h"
|
|
#include "vtkSMCoreUtilities.h"
|
|
#include "vtkSMNamedPropertyIterator.h"
|
|
#include "vtkSMPropertyHelper.h"
|
|
#include "vtkSMProxyManager.h"
|
|
#include "vtkSMPVRepresentationProxy.h"
|
|
#include "vtkSMScalarBarWidgetRepresentationProxy.h"
|
|
#include "vtkSMSettings.h"
|
|
#include "vtkSMStringVectorProperty.h"
|
|
#include "vtkSMTrace.h"
|
|
#include "vtkSMTransferFunctionManager.h"
|
|
#include "vtkSMTransferFunctionPresets.h"
|
|
#include "vtkStringList.h"
|
|
#include "vtkTuple.h"
|
|
#include "vtk_jsoncpp.h"
|
|
|
|
#include <algorithm>
|
|
#include <math.h>
|
|
#include <vector>
|
|
|
|
namespace
|
|
{
|
|
//----------------------------------------------------------------------------
|
|
class StrictWeakOrdering
|
|
{
|
|
public:
|
|
bool operator()(
|
|
const vtkTuple<double, 4>& x, const vtkTuple<double, 4>& y) const
|
|
{
|
|
return (x.GetData()[0] < y.GetData()[0]);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
inline vtkSMProperty* GetControlPointsProperty(vtkSMProxy* self)
|
|
{
|
|
vtkSMProperty* controlPointsProperty = self->GetProperty("RGBPoints");
|
|
if (!controlPointsProperty)
|
|
{
|
|
controlPointsProperty = self->GetProperty("Points");
|
|
}
|
|
|
|
if (!controlPointsProperty)
|
|
{
|
|
vtkGenericWarningMacro("'RGBPoints' or 'Points' property is required.");
|
|
return NULL;
|
|
}
|
|
|
|
vtkSMPropertyHelper cntrlPoints(controlPointsProperty);
|
|
unsigned int num_elements = cntrlPoints.GetNumberOfElements();
|
|
if (num_elements % 4 != 0)
|
|
{
|
|
vtkGenericWarningMacro("Property must have 4-tuples. Resizing.");
|
|
cntrlPoints.SetNumberOfElements((num_elements/4)*4);
|
|
}
|
|
|
|
return controlPointsProperty;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Normalize cntrlPoints so that the range goes from (0, 1). The cntrlPoints
|
|
// are assumed to be using log-space interpolation if "log_space" is true.
|
|
// The result is always in linear space irrespective of the original
|
|
// interpolation space.
|
|
// originalRange is filled with the original range of the cntrlPoints before
|
|
// rescaling.
|
|
bool vtkNormalize(std::vector<vtkTuple<double, 4> >& cntrlPoints, bool log_space,
|
|
vtkTuple<double, 2>* originalRange=NULL)
|
|
{
|
|
if (cntrlPoints.size() == 0)
|
|
{
|
|
// nothing to do, but not an error, so return true.
|
|
return true;
|
|
}
|
|
if (cntrlPoints.size() == 1)
|
|
{
|
|
if (originalRange)
|
|
{
|
|
(*originalRange)[0] = cntrlPoints[0][0];
|
|
(*originalRange)[1] = cntrlPoints[0][0];
|
|
}
|
|
|
|
// Only 1 control point in the property. We'll add 2 points, however.
|
|
cntrlPoints.resize(2);
|
|
cntrlPoints[1] = cntrlPoints[0];
|
|
cntrlPoints[0][0] = 0.0;
|
|
cntrlPoints[1][0] = 1.0;
|
|
return true;
|
|
}
|
|
|
|
// sort the points by x, just in case user didn't add them correctly.
|
|
std::sort(cntrlPoints.begin(), cntrlPoints.end(), StrictWeakOrdering());
|
|
|
|
const double old_range[2] = {cntrlPoints.front().GetData()[0],
|
|
cntrlPoints.back().GetData()[0]};
|
|
if (log_space && (old_range[0] <=0 || old_range[1] <= 0))
|
|
{
|
|
vtkGenericWarningMacro("Range not valid for log. Assuming control points "
|
|
"are not specified in log space.");
|
|
log_space = false;
|
|
}
|
|
if (originalRange)
|
|
{
|
|
(*originalRange)[0] = old_range[0];
|
|
(*originalRange)[1] = old_range[1];
|
|
}
|
|
|
|
// if in log_space, let's convert all the control point values to
|
|
// log.
|
|
if (log_space)
|
|
{
|
|
for (size_t cc = 0; cc < cntrlPoints.size(); ++cc)
|
|
{
|
|
cntrlPoints[cc][0] = log10(cntrlPoints[cc][0]);
|
|
}
|
|
}
|
|
|
|
// now simply normalize the cntrlPoints.
|
|
const double range[2] = {cntrlPoints.front()[0], cntrlPoints.back()[0]};
|
|
if (range[0] == 0.0 && range[1] == 1.0)
|
|
{
|
|
// nothing to do.
|
|
return true;
|
|
}
|
|
const double denominator = (range[1] - range[0]);
|
|
assert(denominator > 0);
|
|
for (size_t cc = 0; cc < cntrlPoints.size(); ++cc)
|
|
{
|
|
cntrlPoints[cc][0] = (cntrlPoints[cc][0] - range[0]) / denominator;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Rescale normalize control points to the given range. If "log_space" is
|
|
// true, the log interpolation is used between rangeMin and rangeMax. If that
|
|
// case too, rangeMin/rangeMax should be still specified as the original data
|
|
// values (and not log of the data values).
|
|
bool vtkRescaleNormalizedControlPoints(
|
|
std::vector<vtkTuple<double, 4> >& cntrlPoints, double rangeMin, double rangeMax, bool log_space)
|
|
{
|
|
assert(cntrlPoints.size() >= 2);
|
|
assert(cntrlPoints.front()[0] == 0.0 && cntrlPoints.back()[0] == 1.0);
|
|
assert(rangeMin < rangeMax);
|
|
if (log_space && (rangeMin <= 0.0 || rangeMax <= 0.0))
|
|
{
|
|
// ensure the range is valid for log space.
|
|
double range[2] = {rangeMin, rangeMax};
|
|
if (vtkSMCoreUtilities::AdjustRangeForLog(range))
|
|
{
|
|
// ranges not valid for log-space. Will convert them.
|
|
vtkGenericWarningMacro(
|
|
"Ranges not valid for log-space. "
|
|
"Changed the range to (" << range[0] <<", " << range[1] << ").");
|
|
}
|
|
rangeMin = range[0];
|
|
rangeMax = range[1];
|
|
}
|
|
|
|
double scale = (rangeMax - rangeMin);
|
|
if (log_space)
|
|
{
|
|
rangeMin = log10(rangeMin);
|
|
rangeMax = log10(rangeMax);
|
|
scale = (rangeMax - rangeMin);
|
|
}
|
|
assert(scale > 0);
|
|
for (size_t cc=0; cc < cntrlPoints.size(); ++cc)
|
|
{
|
|
double &x = cntrlPoints[cc][0];
|
|
x = x * scale + rangeMin;
|
|
if (log_space)
|
|
{
|
|
x = pow(10.0, x);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
vtkStandardNewMacro(vtkSMTransferFunctionProxy);
|
|
//----------------------------------------------------------------------------
|
|
vtkSMTransferFunctionProxy::vtkSMTransferFunctionProxy()
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkSMTransferFunctionProxy::~vtkSMTransferFunctionProxy()
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::RescaleTransferFunction(
|
|
vtkSMProxy* proxy, double rangeMin, double rangeMax, bool extend)
|
|
{
|
|
vtkSMTransferFunctionProxy* tfp =
|
|
vtkSMTransferFunctionProxy::SafeDownCast(proxy);
|
|
|
|
if (!tfp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return tfp->RescaleTransferFunction(rangeMin, rangeMax, extend);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::GetRange(double range[2])
|
|
{
|
|
range[0] = VTK_DOUBLE_MAX;
|
|
range[1] = VTK_DOUBLE_MIN;
|
|
|
|
vtkSMProperty* controlPointsProperty = GetControlPointsProperty(this);
|
|
if (!controlPointsProperty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSMPropertyHelper cntrlPoints(controlPointsProperty);
|
|
unsigned int num_elements = cntrlPoints.GetNumberOfElements();
|
|
if (num_elements < 4)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<vtkTuple<double, 4> > points;
|
|
points.resize(num_elements/4);
|
|
cntrlPoints.Get(points[0].GetData(), num_elements);
|
|
|
|
// sort the points by x, just in case user didn't add them correctly.
|
|
std::sort(points.begin(), points.end(), StrictWeakOrdering());
|
|
|
|
range[0] = points.front().GetData()[0];
|
|
range[1] = points.back().GetData()[0];
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::RescaleTransferFunction(
|
|
double rangeMin, double rangeMax, bool extend)
|
|
{
|
|
vtkSMProperty* controlPointsProperty = GetControlPointsProperty(this);
|
|
if (!controlPointsProperty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSMCoreUtilities::AdjustRange(rangeMin, rangeMax);
|
|
if (rangeMax < rangeMin)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSMPropertyHelper cntrlPoints(controlPointsProperty);
|
|
unsigned int num_elements = cntrlPoints.GetNumberOfElements();
|
|
if (num_elements == 0)
|
|
{
|
|
// nothing to do, but not an error, so return true.
|
|
return true;
|
|
}
|
|
|
|
if (vtkSMProperty* sriProp = this->GetProperty("ScalarRangeInitialized"))
|
|
{
|
|
// mark the range as initialized.
|
|
vtkSMPropertyHelper helper(sriProp);
|
|
bool rangeInitialized = helper.GetAsInt() != 0;
|
|
helper.Set(1);
|
|
if (!rangeInitialized)
|
|
{
|
|
// don't extend the LUT if the current data range is invalid.
|
|
extend = false;
|
|
}
|
|
}
|
|
|
|
bool log_space = (vtkSMPropertyHelper(this, "UseLogScale", true).GetAsInt() != 0);
|
|
|
|
// just in case the num_elements is not a perfect multiple of 4.
|
|
num_elements = 4*(num_elements / 4);
|
|
std::vector<vtkTuple<double, 4> > points;
|
|
points.resize(num_elements/4);
|
|
cntrlPoints.Get(points[0].GetData(), num_elements);
|
|
|
|
vtkTuple<double, 2> preNormalizationRange;
|
|
vtkNormalize(points, log_space, &preNormalizationRange);
|
|
if (extend)
|
|
{
|
|
rangeMin = std::min(rangeMin, preNormalizationRange[0]);
|
|
rangeMax = std::max(rangeMax, preNormalizationRange[1]);
|
|
}
|
|
if (preNormalizationRange[0] == rangeMin && preNormalizationRange[1] == rangeMax)
|
|
{
|
|
// current range is same as the new range. Nothing to do here.
|
|
return true;
|
|
}
|
|
|
|
vtkRescaleNormalizedControlPoints(points, rangeMin, rangeMax, log_space);
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("RescaleTransferFunction")
|
|
.arg(rangeMin)
|
|
.arg(rangeMax)
|
|
.arg("comment", "Rescale transfer function");
|
|
cntrlPoints.Set(points[0].GetData(), num_elements);
|
|
this->UpdateVTKObjects();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::ComputeDataRange(double range[2])
|
|
{
|
|
range[0] = VTK_DOUBLE_MAX;
|
|
range[1] = VTK_DOUBLE_MIN;
|
|
int component = -1;
|
|
if (vtkSMPropertyHelper(this, "VectorMode").GetAsInt() == vtkScalarsToColors::COMPONENT)
|
|
{
|
|
component = vtkSMPropertyHelper(this, "VectorComponent").GetAsInt();
|
|
}
|
|
|
|
for (unsigned int cc=0, max=this->GetNumberOfConsumers(); cc < max; ++cc)
|
|
{
|
|
vtkSMProxy* proxy = this->GetConsumerProxy(cc);
|
|
// consumers could be subproxy of something; so, we locate the true-parent
|
|
// proxy for a proxy.
|
|
proxy = proxy? proxy->GetTrueParentProxy() : NULL;
|
|
vtkSMPVRepresentationProxy* consumer = vtkSMPVRepresentationProxy::SafeDownCast(proxy);
|
|
if (consumer &&
|
|
// consumer is visible.
|
|
vtkSMPropertyHelper(consumer, "Visibility", true).GetAsInt() == 1 &&
|
|
// consumer is using scalar coloring.
|
|
consumer->GetUsingScalarColoring())
|
|
{
|
|
vtkPVArrayInformation* arrayInfo = consumer->GetArrayInformationForColorArray();
|
|
if (!arrayInfo || (component >= 0 && arrayInfo->GetNumberOfComponents() <= component))
|
|
{
|
|
// skip if no arrayInfo available of doesn't have enough components.
|
|
continue;
|
|
}
|
|
|
|
double cur_range[2];
|
|
arrayInfo->GetComponentRange(component, cur_range);
|
|
if (cur_range[0] <= cur_range[1])
|
|
{
|
|
range[0] = cur_range[0] < range[0]? cur_range[0] : range[0];
|
|
range[1] = cur_range[1] > range[1]? cur_range[1] : range[1];
|
|
}
|
|
}
|
|
}
|
|
return (range[0] <= range[1]);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::ComputeAvailableAnnotations(bool extend)
|
|
{
|
|
int component = -1;
|
|
if (vtkSMPropertyHelper(this, "VectorMode").GetAsInt() == vtkScalarsToColors::COMPONENT)
|
|
{
|
|
component = vtkSMPropertyHelper(this, "VectorComponent").GetAsInt();
|
|
}
|
|
|
|
vtkSMStringVectorProperty* allAnnotations =
|
|
vtkSMStringVectorProperty::SafeDownCast(this->GetProperty("Annotations"));
|
|
vtkSmartPointer<vtkStringList> activeAnnotations = vtkSmartPointer<vtkStringList>::New();
|
|
vtkSmartPointer<vtkDoubleArray> activeIndexedColors = vtkSmartPointer<vtkDoubleArray>::New();
|
|
vtkSMStringVectorProperty* activeAnnotatedValuesProperty =
|
|
vtkSMStringVectorProperty::SafeDownCast(this->GetProperty("ActiveAnnotatedValues"));
|
|
vtkSmartPointer<vtkStringList> activeAnnotatedValues =
|
|
vtkSmartPointer<vtkStringList>::New();
|
|
|
|
if (!allAnnotations || !activeAnnotatedValuesProperty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If we are extending, copy the active annotated values
|
|
if (extend)
|
|
{
|
|
activeAnnotatedValuesProperty->GetElements(activeAnnotatedValues);
|
|
}
|
|
|
|
for (unsigned int cc=0, max=this->GetNumberOfConsumers(); cc < max; ++cc)
|
|
{
|
|
vtkSMProxy* proxy = this->GetConsumerProxy(cc);
|
|
// consumers could be subproxy of something; so, we locate the true-parent
|
|
// proxy for a proxy.
|
|
proxy = proxy? proxy->GetTrueParentProxy() : NULL;
|
|
vtkSMPVRepresentationProxy* consumer = vtkSMPVRepresentationProxy::SafeDownCast(proxy);
|
|
if (consumer &&
|
|
// consumer is visible.
|
|
vtkSMPropertyHelper(consumer, "Visibility", true).GetAsInt() == 1 &&
|
|
// consumer is using scalar coloring.
|
|
consumer->GetUsingScalarColoring())
|
|
{
|
|
vtkPVProminentValuesInformation* prominentValues =
|
|
vtkSMPVRepresentationProxy::GetProminentValuesInformationForColorArray(consumer);
|
|
if (!prominentValues)
|
|
{
|
|
vtkWarningMacro(<< "No prominent values for consumer");
|
|
continue;
|
|
}
|
|
vtkSmartPointer<vtkAbstractArray> uniqueValues;
|
|
uniqueValues.TakeReference(
|
|
prominentValues->GetProminentComponentValues(component));
|
|
|
|
if (uniqueValues)
|
|
{
|
|
for (int idx = 0; idx < uniqueValues->GetNumberOfTuples(); ++idx)
|
|
{
|
|
// Look up index of color corresponding to the annotation
|
|
for (unsigned int j = 0; j < allAnnotations->GetNumberOfElements()/2; ++j)
|
|
{
|
|
vtkVariant annotatedValue(allAnnotations->GetElement(2*j + 0));
|
|
if (annotatedValue == uniqueValues->GetVariantValue(idx))
|
|
{
|
|
activeAnnotatedValues->AddUniqueString(allAnnotations->GetElement(2*j + 0));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
activeAnnotatedValuesProperty->SetElements(activeAnnotatedValues);
|
|
this->UpdateVTKObjects();
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::RescaleTransferFunctionToDataRange(bool extend)
|
|
{
|
|
double range[2] = {VTK_DOUBLE_MAX, VTK_DOUBLE_MIN};
|
|
if (this->ComputeDataRange(range))
|
|
{
|
|
return this->RescaleTransferFunction(range[0], range[1], extend);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::InvertTransferFunction(vtkSMProxy* proxy)
|
|
{
|
|
vtkSMTransferFunctionProxy* ctf =
|
|
vtkSMTransferFunctionProxy::SafeDownCast(proxy);
|
|
return ctf? ctf->InvertTransferFunction() : false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::InvertTransferFunction()
|
|
{
|
|
vtkSMProperty* controlPointsProperty = GetControlPointsProperty(this);
|
|
if (!controlPointsProperty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("InvertTransferFunction")
|
|
.arg("comment", "invert the transfer function");
|
|
|
|
vtkSMPropertyHelper cntrlPoints(controlPointsProperty);
|
|
unsigned int num_elements = cntrlPoints.GetNumberOfElements();
|
|
if (num_elements == 0 || num_elements == 4)
|
|
{
|
|
// nothing to do, but not an error, so return true.
|
|
return true;
|
|
}
|
|
|
|
// determine if the interpolation has to happen in log-space.
|
|
bool log_space =
|
|
(vtkSMPropertyHelper(this, "UseLogScale", true).GetAsInt() != 0);
|
|
|
|
std::vector<vtkTuple<double, 4> > points;
|
|
points.resize(num_elements/4);
|
|
cntrlPoints.Get(points[0].GetData(), num_elements);
|
|
|
|
vtkTuple<double, 2> range;
|
|
vtkNormalize(points, log_space, &range);
|
|
for (size_t cc=0; cc < points.size(); cc++)
|
|
{
|
|
double &x = points[cc].GetData()[0];
|
|
x = (1.0 - x);
|
|
}
|
|
// sort again to ensure that the property value is set as min->max.
|
|
std::sort(points.begin(), points.end(), StrictWeakOrdering());
|
|
vtkRescaleNormalizedControlPoints(points, range[0], range[1], log_space);
|
|
|
|
cntrlPoints.Set(points[0].GetData(), num_elements);
|
|
this->UpdateVTKObjects();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::MapControlPointsToLogSpace(
|
|
bool inverse/*=false*/)
|
|
{
|
|
vtkSMProperty* controlPointsProperty = GetControlPointsProperty(this);
|
|
if (!controlPointsProperty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg(inverse? "MapControlPointsToLinearSpace" : "MapControlPointsToLogSpace")
|
|
.arg("comment",
|
|
inverse? "convert from log to linear" : "convert to log space");
|
|
|
|
vtkSMPropertyHelper cntrlPoints(controlPointsProperty);
|
|
unsigned int num_elements = cntrlPoints.GetNumberOfElements();
|
|
if (num_elements == 0 || num_elements == 4)
|
|
{
|
|
// nothing to do, but not an error, so return true.
|
|
return true;
|
|
}
|
|
|
|
std::vector<vtkTuple<double, 4> > points;
|
|
points.resize(num_elements/4);
|
|
cntrlPoints.Get(points[0].GetData(), num_elements);
|
|
|
|
vtkTuple<double, 2> range;
|
|
vtkNormalize(points, inverse, &range); // if inverse==true, we're in log-space currently.
|
|
if (range[0] >= range[1])
|
|
{
|
|
vtkWarningMacro("Empty range! Cannot map control points.");
|
|
return false;
|
|
}
|
|
vtkRescaleNormalizedControlPoints(points, range[0], range[1], !inverse);
|
|
cntrlPoints.Set(points[0].GetData(), num_elements);
|
|
this->UpdateVTKObjects();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::ApplyColorMap(const char* text)
|
|
{
|
|
Json::Value json = this->ConvertLegacyColorMapXMLToJSON(text);
|
|
return this->ApplyPreset(json);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::ApplyColorMap(vtkPVXMLElement* xml)
|
|
{
|
|
Json::Value json = this->ConvertLegacyColorMapXMLToJSON(xml);
|
|
return this->ApplyPreset(json);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::ApplyPreset(const Json::Value& arg, bool rescale)
|
|
{
|
|
if (arg.isNull())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
SM_SCOPED_TRACE(CallMethod)
|
|
.arg(this)
|
|
.arg("ApplyPreset")
|
|
.arg(arg.get("Name", "-PresetName-").asString().c_str())
|
|
.arg(rescale)
|
|
.arg("comment", "Apply a preset using its name. "
|
|
"Note this may not work as expected when presets have duplicate names.");
|
|
|
|
bool usingIndexedColors = arg.isMember("IndexedColors");
|
|
|
|
double range[2];
|
|
bool valid_range = (rescale && !usingIndexedColors)? this->GetRange(range) : false;
|
|
|
|
// Fill up preset with defaults for missing values.
|
|
Json::Value preset(arg);
|
|
preset["IndexedLookup"] = usingIndexedColors? 1 : 0;
|
|
if (usingIndexedColors && rescale)
|
|
{
|
|
// if rescaling, for indexed colors, it means we need to preserve the
|
|
// current annotations.
|
|
preset.removeMember("Annotations");
|
|
}
|
|
|
|
if (rescale && valid_range)
|
|
{
|
|
assert(usingIndexedColors == false);
|
|
|
|
// Since rescaling gets tricky especially when log scaling is involved
|
|
// either in the preset or in the proxy we're loading the preset values on,
|
|
// we will just "rescale" the range in the preset itself.
|
|
Json::Value& pointsValue = this->GetProperty("RGBPoints")?
|
|
preset["RGBPoints"] : preset["Points"];
|
|
if (pointsValue.isNull() || !pointsValue.isArray() ||
|
|
(pointsValue.size() % 4) != 0 || pointsValue.size() == 0)
|
|
{
|
|
vtkErrorMacro("Preset may not be valid. Please validate the preset:\n"
|
|
<< arg.toStyledString().c_str());
|
|
return false;
|
|
}
|
|
|
|
std::vector<vtkTuple<double, 4> > cntrlPoints;
|
|
cntrlPoints.resize(pointsValue.size() / 4);
|
|
for (Json::ArrayIndex cc=0, max = pointsValue.size()/4; cc < max; ++cc)
|
|
{
|
|
cntrlPoints[cc][0] = pointsValue[4*cc].asDouble();
|
|
cntrlPoints[cc][1] = pointsValue[4*cc+1].asDouble();
|
|
cntrlPoints[cc][2] = pointsValue[4*cc+2].asDouble();
|
|
cntrlPoints[cc][3] = pointsValue[4*cc+3].asDouble();
|
|
}
|
|
|
|
bool presetIsLog = preset.get("UseLogScale", Json::Value(false)).asBool();
|
|
vtkNormalize(cntrlPoints, presetIsLog);
|
|
preset.removeMember("UseLogScale");
|
|
|
|
bool proxyIsLog = (vtkSMPropertyHelper(this, "UseLogScale", true).GetAsInt() == 1);
|
|
vtkRescaleNormalizedControlPoints(cntrlPoints, range[0], range[1], proxyIsLog);
|
|
|
|
pointsValue.resize(static_cast<Json::ArrayIndex>(cntrlPoints.size()*4));
|
|
for (size_t cc=0; cc < cntrlPoints.size(); cc++)
|
|
{
|
|
pointsValue[static_cast<Json::ArrayIndex>(4*cc)] = Json::Value(cntrlPoints[cc][0]);
|
|
pointsValue[static_cast<Json::ArrayIndex>(4*cc)+1] = Json::Value(cntrlPoints[cc][1]);
|
|
pointsValue[static_cast<Json::ArrayIndex>(4*cc)+2] = Json::Value(cntrlPoints[cc][2]);
|
|
pointsValue[static_cast<Json::ArrayIndex>(4*cc)+3] = Json::Value(cntrlPoints[cc][3]);
|
|
}
|
|
}
|
|
|
|
if (!preset.isMember("HSVWrap"))
|
|
{
|
|
preset["HSVWrap"] = 0;
|
|
}
|
|
if (vtkSMSettings::DeserializeFromJSON(this, preset))
|
|
{
|
|
this->UpdateVTKObjects();
|
|
return true;
|
|
}
|
|
vtkErrorMacro("Failed to load preset properly");
|
|
this->UpdateVTKObjects();
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::ApplyPreset(const char* presetname, bool rescale)
|
|
{
|
|
vtkNew<vtkSMTransferFunctionPresets> presets;
|
|
return this->ApplyPreset(presets->GetFirstPresetWithName(presetname), rescale);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Json::Value vtkSMTransferFunctionProxy::GetStateAsPreset()
|
|
{
|
|
vtkNew<vtkStringList> toSave;
|
|
if (this->GetProperty("RGBPoints"))
|
|
{
|
|
if (vtkSMPropertyHelper(this, "IndexedLookup", /*quiet=*/true).GetAsInt() == 0)
|
|
{
|
|
toSave->AddString("ColorSpace");
|
|
toSave->AddString("RGBPoints");
|
|
if (vtkSMPropertyHelper(this, "HSVWrap", true).GetAsInt() != 0)
|
|
{
|
|
toSave->AddString("HSVWrap");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
toSave->AddString("IndexedColors");
|
|
|
|
// Annotations are only saved with indexed colors.
|
|
if (vtkSMPropertyHelper(this, "Annotations", true).GetNumberOfElements() > 0)
|
|
{
|
|
toSave->AddString("Annotations");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
toSave->AddString("Points");
|
|
}
|
|
if (vtkSMPropertyHelper(this, "UseLogScale", true).GetAsInt() == 1)
|
|
{
|
|
// save log-space only if using log space.
|
|
toSave->AddString("UseLogScale");
|
|
}
|
|
|
|
vtkNew<vtkSMNamedPropertyIterator> iter;
|
|
iter->SetProxy(this);
|
|
iter->SetPropertyNames(toSave.GetPointer());
|
|
return vtkSMSettings::SerializeAsJSON(this, iter.GetPointer());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Json::Value vtkSMTransferFunctionProxy::GetStateAsPreset(vtkSMProxy* proxy)
|
|
{
|
|
vtkSMTransferFunctionProxy* self = vtkSMTransferFunctionProxy::SafeDownCast(proxy);
|
|
return self? self->GetStateAsPreset() : Json::Value();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::SaveColorMap(vtkPVXMLElement* xml)
|
|
{
|
|
if (!xml)
|
|
{
|
|
vtkWarningMacro("'xml' cannot be NULL");
|
|
return false;
|
|
}
|
|
|
|
xml->SetName("ColorMap");
|
|
|
|
bool indexedLookup = vtkSMPropertyHelper(this, "IndexedLookup").GetAsInt() != 0;
|
|
xml->AddAttribute("indexedLookup", indexedLookup ? "1" : "0");
|
|
|
|
if (!indexedLookup)
|
|
{
|
|
std::string space = vtkSMPropertyHelper(this, "ColorSpace").GetAsString();
|
|
if (space == "HSV" && vtkSMPropertyHelper(this, "HSVWrap").GetAsInt() == 1)
|
|
{
|
|
xml->AddAttribute("space", "Wrapped");
|
|
}
|
|
else
|
|
{
|
|
xml->AddAttribute("space", space.c_str());
|
|
}
|
|
}
|
|
|
|
// Add the control points.
|
|
vtkSMProperty* controlPointsProperty = indexedLookup?
|
|
this->GetProperty("IndexedColors") : GetControlPointsProperty(this);
|
|
if (!controlPointsProperty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vtkSMPropertyHelper cntrlPoints(controlPointsProperty);
|
|
unsigned int num_elements = cntrlPoints.GetNumberOfElements();
|
|
if (num_elements > 0)
|
|
{
|
|
if (indexedLookup)
|
|
{
|
|
// Save (r,g,b) tuples for categorical colors.
|
|
std::vector<vtkTuple<double, 3> > points;
|
|
points.resize(num_elements/3);
|
|
cntrlPoints.Get(points[0].GetData(), num_elements);
|
|
|
|
for (size_t cc=0; cc < points.size(); cc++)
|
|
{
|
|
vtkNew<vtkPVXMLElement> child;
|
|
child->SetName("Point");
|
|
child->AddAttribute("r", points[cc].GetData()[1]);
|
|
child->AddAttribute("g", points[cc].GetData()[2]);
|
|
child->AddAttribute("b", points[cc].GetData()[3]);
|
|
child->AddAttribute("o", "1");
|
|
xml->AddNestedElement(child.GetPointer());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Save (x, r, g, b) tuples for non-categorical colors.
|
|
std::vector<vtkTuple<double, 4> > points;
|
|
points.resize(num_elements/4);
|
|
cntrlPoints.Get(points[0].GetData(), num_elements);
|
|
|
|
// sort the points by x, just in case user didn't add them correctly.
|
|
std::sort(points.begin(), points.end(), StrictWeakOrdering());
|
|
|
|
for (size_t cc=0; cc < points.size(); cc++)
|
|
{
|
|
vtkNew<vtkPVXMLElement> child;
|
|
child->SetName("Point");
|
|
child->AddAttribute("x", points[cc].GetData()[0]);
|
|
child->AddAttribute("r", points[cc].GetData()[1]);
|
|
child->AddAttribute("g", points[cc].GetData()[2]);
|
|
child->AddAttribute("b", points[cc].GetData()[3]);
|
|
child->AddAttribute("o", "1");
|
|
xml->AddNestedElement(child.GetPointer());
|
|
}
|
|
}
|
|
}
|
|
|
|
// add NanColor.
|
|
vtkSMPropertyHelper nanProperty(this, "NanColor");
|
|
vtkNew<vtkPVXMLElement> nan;
|
|
nan->SetName("NaN");
|
|
nan->AddAttribute("r", nanProperty.GetAsDouble(0));
|
|
nan->AddAttribute("g", nanProperty.GetAsDouble(1));
|
|
nan->AddAttribute("b", nanProperty.GetAsDouble(2));
|
|
xml->AddNestedElement(nan.GetPointer());
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::IsScalarBarVisible(vtkSMProxy* view)
|
|
{
|
|
if (vtkSMProxy* sbProxy = this->FindScalarBarRepresentation(view))
|
|
{
|
|
if (vtkSMPropertyHelper(sbProxy, "Visibility").GetAsInt() == 1)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkSMProxy* vtkSMTransferFunctionProxy::FindScalarBarRepresentation(vtkSMProxy* view)
|
|
{
|
|
if (!view || !view->GetProperty("Representations"))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
vtkSMPropertyHelper reprHelper(view, "Representations");
|
|
for (unsigned int cc=0; cc < reprHelper.GetNumberOfElements(); cc++)
|
|
{
|
|
vtkSMProxy* current = reprHelper.GetAsProxy(cc);
|
|
if (current && current->GetXMLName() &&
|
|
strcmp(current->GetXMLName(), "ScalarBarWidgetRepresentation") == 0 &&
|
|
current->GetProperty("LookupTable"))
|
|
{
|
|
if (vtkSMPropertyHelper(current, "LookupTable").GetAsProxy() == this)
|
|
{
|
|
return current;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::UpdateScalarBarsComponentTitle(
|
|
vtkPVArrayInformation* info)
|
|
{
|
|
// find all scalar bars for this transfer function and update their titles.
|
|
for (unsigned int cc=0, max = this->GetNumberOfConsumers(); cc < max; ++cc)
|
|
{
|
|
vtkSMProxy* consumer = this->GetConsumerProxy(cc);
|
|
while (consumer && consumer->GetParentProxy())
|
|
{
|
|
consumer = consumer->GetParentProxy();
|
|
}
|
|
|
|
vtkSMScalarBarWidgetRepresentationProxy* sb =
|
|
vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast(consumer);
|
|
if (sb)
|
|
{
|
|
sb->UpdateComponentTitle(info);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMTransferFunctionProxy::ResetPropertiesToXMLDefaults(
|
|
bool preserve_range)
|
|
{
|
|
try
|
|
{
|
|
if (!preserve_range)
|
|
{
|
|
throw true;
|
|
}
|
|
|
|
vtkSMProperty* controlPointsProperty = GetControlPointsProperty(this);
|
|
if (!controlPointsProperty)
|
|
{
|
|
throw true;
|
|
}
|
|
|
|
vtkSMPropertyHelper cntrlPoints(controlPointsProperty);
|
|
unsigned int num_elements = cntrlPoints.GetNumberOfElements();
|
|
if (num_elements == 0 || num_elements == 4)
|
|
{
|
|
// nothing to do, but not an error, so return true.
|
|
throw true;
|
|
}
|
|
|
|
std::vector<vtkTuple<double, 4> > points;
|
|
points.resize(num_elements/4);
|
|
cntrlPoints.Get(points[0].GetData(), num_elements);
|
|
|
|
// sort the points by x, just in case user didn't add them correctly.
|
|
std::sort(points.begin(), points.end(), StrictWeakOrdering());
|
|
|
|
double range[2] = {points.front().GetData()[0],
|
|
points.back().GetData()[0]};
|
|
this->ResetPropertiesToXMLDefaults();
|
|
this->RescaleTransferFunction(range[0], range[1], false);
|
|
}
|
|
catch (bool val)
|
|
{
|
|
if (val)
|
|
{
|
|
this->ResetPropertiesToXMLDefaults();
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Json::Value vtkSMTransferFunctionProxy::ConvertLegacyColorMapXMLToJSON(vtkPVXMLElement* xml)
|
|
{
|
|
if (!xml || !xml->GetName() || strcmp(xml->GetName(), "ColorMap") != 0)
|
|
{
|
|
vtkGenericWarningMacro("'ColorMap' XML expected.");
|
|
return Json::Value();
|
|
}
|
|
|
|
Json::Value json(Json::objectValue);
|
|
|
|
bool indexedLookup =
|
|
(strcmp(xml->GetAttributeOrDefault("indexedLookup", "false"), "true") == 0);
|
|
if (!indexedLookup)
|
|
{
|
|
// load color-space for only for non-categorical color maps
|
|
std::string colorSpace = xml->GetAttributeOrDefault("space", "NoChange");
|
|
if (colorSpace == "Wrapped")
|
|
{
|
|
json["HSVWrap"] = 1;
|
|
json["ColorSpace"] = "HSV";
|
|
}
|
|
else if (colorSpace != "NoChange")
|
|
{
|
|
json["ColorSpace"] = colorSpace;
|
|
}
|
|
}
|
|
|
|
vtkPVXMLElement* nanElement = xml->FindNestedElementByName("NaN");
|
|
if (nanElement && nanElement->GetAttribute("r") &&
|
|
nanElement->GetAttribute("g") && nanElement->GetAttribute("b"))
|
|
{
|
|
double rgb[3];
|
|
nanElement->GetScalarAttribute("r", &rgb[0]);
|
|
nanElement->GetScalarAttribute("g", &rgb[1]);
|
|
nanElement->GetScalarAttribute("b", &rgb[2]);
|
|
|
|
Json::Value nancolor(Json::arrayValue);
|
|
nancolor[0] = rgb[0];
|
|
nancolor[1] = rgb[1];
|
|
nancolor[2] = rgb[2];
|
|
json["NanColor"] = nancolor;
|
|
}
|
|
|
|
// Read the control points from the XML.
|
|
std::vector<vtkTuple<double, 4> > new_points;
|
|
std::vector<vtkTuple<const char*, 2> > new_annotations;
|
|
|
|
for (unsigned int cc=0; cc < xml->GetNumberOfNestedElements(); cc++)
|
|
{
|
|
vtkPVXMLElement* pointElement = xml->GetNestedElement(cc);
|
|
double xrgb[4];
|
|
if (pointElement && pointElement->GetName() &&
|
|
strcmp(pointElement->GetName(), "Point") == 0 &&
|
|
pointElement->GetScalarAttribute("r", &xrgb[1]) &&
|
|
pointElement->GetScalarAttribute("g", &xrgb[2]) &&
|
|
pointElement->GetScalarAttribute("b", &xrgb[3]))
|
|
{
|
|
if (!indexedLookup &&
|
|
pointElement->GetScalarAttribute("x", &xrgb[0]))
|
|
{
|
|
// "x" attribute is only needed for non-categorical color maps.
|
|
new_points.push_back(vtkTuple<double, 4>(xrgb));
|
|
}
|
|
else if (indexedLookup)
|
|
{
|
|
// since "x" attribute is only needed for non-categorical color maps, we
|
|
// make up one. This will be ignored when setting the "IndexedColors"
|
|
// property.
|
|
xrgb[0] = cc;
|
|
new_points.push_back(vtkTuple<double, 4>(xrgb));
|
|
}
|
|
}
|
|
else if (pointElement && pointElement->GetName() &&
|
|
strcmp(pointElement->GetName(),"Annotation") &&
|
|
pointElement->GetAttribute("v") &&
|
|
pointElement->GetAttribute("t"))
|
|
{
|
|
const char* value[2] = {
|
|
pointElement->GetAttribute("v"),
|
|
pointElement->GetAttribute("t")};
|
|
new_annotations.push_back(vtkTuple<const char*, 2>(value));
|
|
}
|
|
}
|
|
|
|
if (new_annotations.size() > 0)
|
|
{
|
|
Json::Value annotations(Json::arrayValue);
|
|
for (int cc=0, max = static_cast<int>(new_annotations.size()); cc < max; cc++)
|
|
{
|
|
annotations[2*cc] = new_annotations[cc][0];
|
|
annotations[2*cc+1] = new_annotations[cc][1];
|
|
}
|
|
json["Annotations"] = annotations;
|
|
}
|
|
|
|
if (new_points.size() > 0 && indexedLookup)
|
|
{
|
|
Json::Value rgbColors(Json::arrayValue);
|
|
for (int cc=0, max = static_cast<int>(new_points.size()); cc < max; cc++)
|
|
{
|
|
rgbColors[3*cc] = new_points[cc].GetData()[1];
|
|
rgbColors[3*cc + 1] = new_points[cc].GetData()[2];
|
|
rgbColors[3*cc + 2] = new_points[cc].GetData()[3];
|
|
}
|
|
json["IndexedColors"] = rgbColors;
|
|
}
|
|
else if (new_points.size() > 0 && !indexedLookup)
|
|
{
|
|
// sort the points by x, just in case user didn't add them correctly.
|
|
std::sort(new_points.begin(), new_points.end(), StrictWeakOrdering());
|
|
|
|
Json::Value rgbColors(Json::arrayValue);
|
|
for (int cc=0, max = static_cast<int>(new_points.size()); cc < max; cc++)
|
|
{
|
|
rgbColors[4*cc] = new_points[cc].GetData()[0];
|
|
rgbColors[4*cc + 1] = new_points[cc].GetData()[1];
|
|
rgbColors[4*cc + 2] = new_points[cc].GetData()[2];
|
|
rgbColors[4*cc + 3] = new_points[cc].GetData()[3];
|
|
}
|
|
json["RGBPoints"] = rgbColors;
|
|
}
|
|
|
|
// add name.
|
|
json["Name"] = xml->GetAttribute("name");
|
|
if (const char* creator= xml->GetAttribute("creator"))
|
|
{
|
|
json["Creator"] = creator;
|
|
}
|
|
return json;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Json::Value vtkSMTransferFunctionProxy::ConvertLegacyColorMapXMLToJSON(const char* xmlcontents)
|
|
{
|
|
vtkNew<vtkPVXMLParser> parser;
|
|
if (!parser->Parse(xmlcontents))
|
|
{
|
|
return Json::Value();
|
|
}
|
|
return vtkSMTransferFunctionProxy::ConvertLegacyColorMapXMLToJSON(parser->GetRootElement());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Json::Value vtkSMTransferFunctionProxy::ConvertMultipleLegacyColorMapXMLToJSON(vtkPVXMLElement* xml)
|
|
{
|
|
if (!xml || !xml->GetName() || strcmp(xml->GetName(), "ColorMaps") != 0)
|
|
{
|
|
vtkGenericWarningMacro("'ColorMaps' XML expected.");
|
|
return Json::Value();
|
|
}
|
|
|
|
Json::Value json(Json::arrayValue);
|
|
for (unsigned int cc=0, max=xml->GetNumberOfNestedElements(); cc < max; ++cc)
|
|
{
|
|
vtkPVXMLElement* elem = xml->GetNestedElement(cc);
|
|
if (elem && elem->GetName() && strcmp(elem->GetName(), "ColorMap") == 0 && elem->GetAttribute("name"))
|
|
{
|
|
Json::Value cmap = vtkSMTransferFunctionProxy::ConvertLegacyColorMapXMLToJSON(elem);
|
|
if (!cmap.empty())
|
|
{
|
|
json.append(cmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Json::Value vtkSMTransferFunctionProxy::ConvertMultipleLegacyColorMapXMLToJSON(const char* xmlcontents)
|
|
{
|
|
vtkNew<vtkPVXMLParser> parser;
|
|
if (!parser->Parse(xmlcontents))
|
|
{
|
|
return Json::Value();
|
|
}
|
|
return vtkSMTransferFunctionProxy::ConvertMultipleLegacyColorMapXMLToJSON(parser->GetRootElement());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkSMTransferFunctionProxy::ConvertLegacyColorMapsToJSON(
|
|
const char* inxmlfile, const char* outjsonfile)
|
|
{
|
|
vtkNew<vtkPVXMLParser> parser;
|
|
parser->SetFileName(inxmlfile);
|
|
if (!parser->Parse())
|
|
{
|
|
vtkGenericWarningMacro("Failed to parse XML!");
|
|
return false;
|
|
}
|
|
|
|
Json::Value json = vtkSMTransferFunctionProxy::ConvertMultipleLegacyColorMapXMLToJSON(parser->GetRootElement());
|
|
if (json.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ofstream file;
|
|
file.open(outjsonfile);
|
|
if (file)
|
|
{
|
|
file << json.toStyledString().c_str();
|
|
file.close();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkSMTransferFunctionProxy::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
}
|