/*========================================================================= 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 #include #include namespace { //---------------------------------------------------------------------------- class StrictWeakOrdering { public: bool operator()( const vtkTuple& x, const vtkTuple& 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 >& cntrlPoints, bool log_space, vtkTuple* 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 >& 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 > 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 > points; points.resize(num_elements/4); cntrlPoints.Get(points[0].GetData(), num_elements); vtkTuple 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 activeAnnotations = vtkSmartPointer::New(); vtkSmartPointer activeIndexedColors = vtkSmartPointer::New(); vtkSMStringVectorProperty* activeAnnotatedValuesProperty = vtkSMStringVectorProperty::SafeDownCast(this->GetProperty("ActiveAnnotatedValues")); vtkSmartPointer activeAnnotatedValues = vtkSmartPointer::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 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 > points; points.resize(num_elements/4); cntrlPoints.Get(points[0].GetData(), num_elements); vtkTuple 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 > points; points.resize(num_elements/4); cntrlPoints.Get(points[0].GetData(), num_elements); vtkTuple 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 > 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(cntrlPoints.size()*4)); for (size_t cc=0; cc < cntrlPoints.size(); cc++) { pointsValue[static_cast(4*cc)] = Json::Value(cntrlPoints[cc][0]); pointsValue[static_cast(4*cc)+1] = Json::Value(cntrlPoints[cc][1]); pointsValue[static_cast(4*cc)+2] = Json::Value(cntrlPoints[cc][2]); pointsValue[static_cast(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 presets; return this->ApplyPreset(presets->GetFirstPresetWithName(presetname), rescale); } //---------------------------------------------------------------------------- Json::Value vtkSMTransferFunctionProxy::GetStateAsPreset() { vtkNew 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 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 > points; points.resize(num_elements/3); cntrlPoints.Get(points[0].GetData(), num_elements); for (size_t cc=0; cc < points.size(); cc++) { vtkNew 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 > 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 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 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 > 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 > new_points; std::vector > 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(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(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(value)); } } if (new_annotations.size() > 0) { Json::Value annotations(Json::arrayValue); for (int cc=0, max = static_cast(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(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(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 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 parser; if (!parser->Parse(xmlcontents)) { return Json::Value(); } return vtkSMTransferFunctionProxy::ConvertMultipleLegacyColorMapXMLToJSON(parser->GetRootElement()); } //---------------------------------------------------------------------------- bool vtkSMTransferFunctionProxy::ConvertLegacyColorMapsToJSON( const char* inxmlfile, const char* outjsonfile) { vtkNew 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); }