diff --git a/data/attribute/widgets/gallery-disk.sbt b/data/attribute/widgets/gallery-disk.sbt new file mode 100644 index 0000000000000000000000000000000000000000..8853ddbf0592a36def885d8e16378019608404d5 --- /dev/null +++ b/data/attribute/widgets/gallery-disk.sbt @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8" ?> +<SMTK_AttributeResource Version="3"> + <Definitions> + <AttDef Type="Example"> + <ItemDefinitions> + <Group Name="disk" NumberOfRequiredGroups="1"> + <ItemDefinitions> + <Double Name="center" NumberOfRequiredValues="3"/> + <Double Name="normal" NumberOfRequiredValues="3"/> + <Double Name="radius"/> + </ItemDefinitions> + </Group> + </ItemDefinitions> + </AttDef> + </Definitions> + <Attributes> + <Att Name="Disk Widget Example" Type="Example"> + <Items> + <Group Name="disk" NumberOfGroups="1"> + <GroupClusters> + <Cluster Ith="0"> + <Double Name="center"> + <Values> + <Val Ith="0">0.0</Val> + <Val Ith="1">0.0</Val> + <Val Ith="2">0.0</Val> + </Values> + </Double> + <Double Name="normal"> + <Values> + <Val Ith="0">0.0</Val> + <Val Ith="1">0.0</Val> + <Val Ith="2">1.0</Val> + </Values> + </Double> + <Double Name="radius"> + <Values> + <Val Ith="0">0.5</Val> + </Values> + </Double> + </Cluster> + </GroupClusters> + </Group> + </Items> + </Att> + </Attributes> + <Views> + <View + Type="Instanced" + Title="Example" + TopLevel="true" + FilterByAdvanceLevel="false" + FilterByCategoryMode="false" + > + <InstancedAttributes> + <Att Type="Example" Name="Disk Widget Example" + > + <ItemViews> + <View Item="disk" Type="Disk" Center="center" Normal="normal" Radius="radius" ShowControls="true"/> + </ItemViews> + </Att> + </InstancedAttributes> + </View> + </Views> +</SMTK_AttributeResource> diff --git a/doc/release/notes/geometry-attribute.rst b/doc/release/notes/geometry-attribute.rst new file mode 100644 index 0000000000000000000000000000000000000000..ff7f1c06bb33e08666e8f4b1839ff902073e2e14 --- /dev/null +++ b/doc/release/notes/geometry-attribute.rst @@ -0,0 +1,12 @@ +Geometry Subsystem +================== + +Attribute Resources with Renderable Geometry +-------------------------------------------- + +The ``smtk::extension::vtk::source::SourceFromAttribute`` class has been removed. +This class provided no renderable geometry for attribute resources +and interfered with other plugins that do provide renderable geometry. + +Previously, it served to force creation of a ParaView pipeline object +for attribute resources, but the need for this no longer exists. diff --git a/doc/release/notes/operation-handlers.rst b/doc/release/notes/operation-handlers.rst new file mode 100644 index 0000000000000000000000000000000000000000..758d14a9a6090a381a90b5033a6036fe1306ec27 --- /dev/null +++ b/doc/release/notes/operation-handlers.rst @@ -0,0 +1,32 @@ +Operation System +================ + +Handlers for specific operation instances +----------------------------------------- + +You may now add and remove handlers to an instance of an operation. +A handler is a function to be invoked upon the next completion of +one instance of an operation object (regardless of whether the result +indicates success or failure). + +Handlers are function objects with a signature similar to observers +(see :ref:`operation-observers`) +except that they are only called upon completion of an operation: +handlers may not cancel the operation and are not passed an ``EventType`` +(only the operation and its result). +Handlers, like observers, are invoked at a time when all the resource +locks required for the operation are held. + +Handlers are invoked on the thread in which the operation is run (unlike +observers, which are invoked on the main/GUI thread in Qt applications). + +Handlers are invoked only for the instance of the operation they are +added to; if you create multiple operations of the same type and add +a handler to one, only that instance will have the handler called. + +Handlers are invoked zero or one times at most. +Operation handlers are removed each time the operation is invoked; +you are responsible for adding handlers for each invocation. +It is acceptable for a handler to add itself to the operation which +invoked it (as the container of handlers is copied and cleared before +any handlers are invoked). diff --git a/doc/release/notes/resource-unit-system-typo.rst b/doc/release/notes/resource-unit-system-typo.rst new file mode 100644 index 0000000000000000000000000000000000000000..815a23e621908bafe6f67373d023803756f292bd --- /dev/null +++ b/doc/release/notes/resource-unit-system-typo.rst @@ -0,0 +1,21 @@ +Resource system +=============== + +Unit system API name change +--------------------------- + +The methods to set or get the system of units for a resource have +been renamed to match the terminology most engineers use: "unit" +is singular since there is one system holding all of the units. + ++ :smtk:`unitsSystem()<smtk::resource::Resource::unitsSystem()` becomes + :smtk:`unitSystem()<smtk::resource::Resource::unitSystem()>` ++ :smtk:`setUnitsSystem()<smtk::resource::Resource::setUnitsSystem()` becomes + :smtk:`setUnitSystem()<smtk::resource::Resource::setUnitSystem()` + +The attribute resource has additional methods that have changed: + ++ :smtk:`Definition::setItemDefinitionUnitsSystem()<smtk::attribute::Definition::setItemDefinitionUnitsSystem()` becomes + :smtk:`Definition::setItemDefinitionUnitSystem()<smtk::attribute::Definition::setItemDefinitionUnitSystem()` ++ :smtk:`ItemDefinition::setUnitsSystem()<smtk::attribute::Definition::setUnitsSystem()` becomes + :smtk:`ItemDefinition::setUnitSystem()<smtk::attribute::Definition::setUnitSystem()` diff --git a/doc/userguide/attribute/concepts.rst b/doc/userguide/attribute/concepts.rst index c96b65d1d3fd1222c8d4a0f64c6758ad6099b1db..ef2c82c638394e00760c0b381e09c94ecbcc070c 100644 --- a/doc/userguide/attribute/concepts.rst +++ b/doc/userguide/attribute/concepts.rst @@ -379,7 +379,7 @@ Related API * :smtk:`Definition::units` - method to return the units associated with the definition (either locally set or inherited from its derived definition) * :smtk:`Definition::localUnits` - method to return the local units explicitly associated with the definition * :smtk:`Definition::setLocalUnits` - method to set the units explicitly associated with the definition -* :smtk:`Definition::unitsSystem` - method to return the units system associated with the definition +* :smtk:`Definition::unitSystem` - method to return the units system associated with the definition Please see `unitAttributeUnits <https://gitlab.kitware.com/cmb/smtk/-/blob/master/smtk/attribute/testing/cxx/unitAttributeUnits.cxx>`_ for a simple example of using units with Attributes and Definitions. diff --git a/doc/userguide/operation/operators.rst b/doc/userguide/operation/operators.rst index 98a63f0ddd9ac08d69c6eff994716795e6604c06..3d6beb6259036c460d17d897c440a20c4668c935 100644 --- a/doc/userguide/operation/operators.rst +++ b/doc/userguide/operation/operators.rst @@ -60,3 +60,34 @@ means that one need not construct an instance of the operator's C++ class in ord to obtain information about it; instead, simply call :smtk:`operatorSystem() <smtk::model::Session::operatorSystem>` on the session and ask for all the definitions which inherit "operator". + +Handlers +-------- + +You may add and remove handlers to an instance of an operation. +A handler is a function to be invoked upon the next completion of +one instance of an operation object (regardless of whether the result +indicates success or failure). + +Handlers are function objects with a signature similar to observers +except that they are only called upon completion of an operation: +handlers may not cancel the operation and are not passed an ``EventType`` +(only the operation and its result). +Handlers, like observers, are invoked at a time when all the resource +locks required for the operation are held. + +Handlers are invoked on the thread in which the operation is run (unlike +observers, which are invoked on the main/GUI thread in Qt applications). + +Handlers are invoked only for the instance of the operation they are +added to; if you create multiple operations of the same type and add +a handler to one, only that instance will have the handler called. + +Handlers are invoked zero or one times at most. +Operation handlers are removed each time the operation is invoked; +you are responsible for adding handlers for each invocation. +If an operation is canceled by an observer, the handler is removed +from the operation and not invoked. +It is acceptable for a handler to add itself to the operation which +invoked it (as the container of handlers is copied and cleared before +any handlers are invoked). diff --git a/doc/userguide/operation/support.rst b/doc/userguide/operation/support.rst index 0cef96f93231c2e3c5fd186ce5cbe2ccfe6525e6..668bd75218315b6101e9346f94fe9ea598d06638 100644 --- a/doc/userguide/operation/support.rst +++ b/doc/userguide/operation/support.rst @@ -188,6 +188,8 @@ as may operations that only require read access to the same resource. However, operations that require write access to the same resource will be run sequentially. +.. _operation-observers: + Observing operations -------------------- diff --git a/doc/userguide/task/agents.rst b/doc/userguide/task/agents.rst index 0a97ef82677a895b8a4566d1c02ba1201a6fb660..7b5b7cb958f7eb1ed95250766feb252fbfa704fe 100644 --- a/doc/userguide/task/agents.rst +++ b/doc/userguide/task/agents.rst @@ -488,6 +488,10 @@ this agent: If an object is a resource, each specifier is a tuple holding a UUID and ``null``. If an object is a component, each specified is a tuple holding the UUID of the component's parent resource and the component's UUID. +* ``required-counts``: is a map from a role name to an array of 2 integers specifying + the minimum and maximum number of objects permitted in the given role. A ``-1`` for + the second array value indicates there is no maximum. If both numbers are ``-1``, + then no objects are allowed in the given role. Example """"""" diff --git a/smtk/attribute/Attribute.cxx b/smtk/attribute/Attribute.cxx index 0d9e37b01cfc4837864fc1801e0c21fcb8ba70f6..244c780cd84232ec18c66c83231feb28596730aa 100644 --- a/smtk/attribute/Attribute.cxx +++ b/smtk/attribute/Attribute.cxx @@ -1208,7 +1208,7 @@ bool Attribute::setLocalUnits(const std::string& newUnits) return false; } - const auto& unitSys = m_definition->unitsSystem(); + const auto& unitSys = m_definition->unitSystem(); // Can't determine if the units are compatible w/o units system if (!unitSys) { diff --git a/smtk/attribute/Definition.cxx b/smtk/attribute/Definition.cxx index 5a233cfd24285627e09c289d47443457c252486b..d533adef5be1a8e32d3d071b60e942a7caba4ac7 100644 --- a/smtk/attribute/Definition.cxx +++ b/smtk/attribute/Definition.cxx @@ -690,19 +690,19 @@ bool Definition::addItemDefinition(smtk::attribute::ItemDefinitionPtr cdef) std::size_t n = m_itemDefs.size(); m_itemDefs.push_back(cdef); m_itemDefPositions[cdef->name()] = static_cast<int>(n); - this->setItemDefinitionUnitsSystem(cdef); + this->setItemDefinitionUnitSystem(cdef); this->updateDerivedDefinitions(); return true; } -void Definition::setItemDefinitionUnitsSystem( +void Definition::setItemDefinitionUnitSystem( const smtk::attribute::ItemDefinitionPtr& itemDef) const { - const auto& defUnitsSystem = this->unitsSystem(); + const auto& defUnitSystem = this->unitSystem(); auto attRes = this->attributeResource(); - if (defUnitsSystem) + if (defUnitSystem) { - itemDef->setUnitsSystem(defUnitsSystem); + itemDef->setUnitSystem(defUnitSystem); } } @@ -914,15 +914,15 @@ void Definition::applyAdvanceLevels( } } -const std::shared_ptr<units::System>& Definition::unitsSystem() const +const std::shared_ptr<units::System>& Definition::unitSystem() const { - static std::shared_ptr<units::System> nullUnitsSystem; + static std::shared_ptr<units::System> nullUnitSystem; auto attRes = this->attributeResource(); if (attRes) { - return attRes->unitsSystem(); + return attRes->unitSystem(); } - return nullUnitsSystem; + return nullUnitSystem; } bool Definition::setLocalUnits(const std::string& newUnits, bool force) @@ -933,7 +933,7 @@ bool Definition::setLocalUnits(const std::string& newUnits, bool force) m_localUnits = newUnits; return true; } - const auto& unitSys = this->unitsSystem(); + const auto& unitSys = this->unitSystem(); if (!unitSys) { return false; // There is no unit system diff --git a/smtk/attribute/Definition.h b/smtk/attribute/Definition.h index 5881045f0dac3b3d64438e89ce0af560d34b10dc..e39efaaacb8104f81c7431dfc785b2dabb98a919 100644 --- a/smtk/attribute/Definition.h +++ b/smtk/attribute/Definition.h @@ -21,6 +21,7 @@ #include "smtk/attribute/Tag.h" #include "smtk/common/Categories.h" +#include "smtk/common/Deprecation.h" #include "smtk/model/EntityRef.h" //for EntityRef version of canBeAssociated #include "smtk/model/EntityTypeBits.h" // for BitFlags type @@ -359,7 +360,7 @@ public: { std::size_t n = m_itemDefs.size(); item = SharedTypes::RawPointerType::New(name); - this->setItemDefinitionUnitsSystem(item); + this->setItemDefinitionUnitSystem(item); m_itemDefs.push_back(item); m_itemDefPositions[name] = static_cast<int>(n); this->updateDerivedDefinitions(); @@ -481,7 +482,10 @@ public: bool setLocalUnits(const std::string& newUnits, bool force = false); /// \brief Gets the system of units used by this definition. - const std::shared_ptr<units::System>& unitsSystem() const; + const std::shared_ptr<units::System>& unitSystem() const; + SMTK_DEPRECATED_IN_NEXT("Use unitSystem() instead.") + const std::shared_ptr<units::System>& unitsSystem() const + { return this->unitSystem(); } protected: friend class smtk::attribute::Resource; @@ -510,7 +514,10 @@ protected: const unsigned int& readLevelFromParent, const unsigned int& writeLevelFromParent); - void setItemDefinitionUnitsSystem(const smtk::attribute::ItemDefinitionPtr& itemDef) const; + void setItemDefinitionUnitSystem(const smtk::attribute::ItemDefinitionPtr& itemDef) const; + SMTK_DEPRECATED_IN_NEXT("Use setItemDefinitionUnitSystem() instead.") + void setItemDefinitionUnitsSystem(const smtk::attribute::ItemDefinitionPtr& itemDef) const + { this->setItemDefinitionUnitSystem(itemDef); } smtk::attribute::WeakResourcePtr m_resource; smtk::common::UUID m_id; diff --git a/smtk/attribute/DoubleItem.cxx b/smtk/attribute/DoubleItem.cxx index 5e36badb0f9d7275232d7aca4cf22c3c58bdaf72..170dfc78ccfc9ac1380202c2d6aa2a201bb51db0 100644 --- a/smtk/attribute/DoubleItem.cxx +++ b/smtk/attribute/DoubleItem.cxx @@ -207,26 +207,26 @@ bool DoubleItem::setValue(std::size_t element, const double& val, const std::str const std::string& myUnitStr = this->supportedUnits(); if (myUnitStr != valUnitStr) { - auto unitsSystem = this->definition()->unitsSystem(); + auto unitSystem = this->definition()->unitSystem(); // Is there a units system specified? - if (!unitsSystem) + if (!unitSystem) { return false; // we can not convert units } bool status; - auto myUnits = unitsSystem->unit(myUnitStr, &status); + auto myUnits = unitSystem->unit(myUnitStr, &status); if (!status) { return false; // Could not find the base's units } - auto valUnits = unitsSystem->unit(valUnitStr, &status); + auto valUnits = unitSystem->unit(valUnitStr, &status); if (!status) { return false; // Could not find vals' units } units::Measurement m(val, valUnits); - auto newM = unitsSystem->convert(m, myUnits, &status); + auto newM = unitSystem->convert(m, myUnits, &status); if (!status) { return false; // could not convert @@ -268,7 +268,7 @@ bool DoubleItem::setValueFromString(std::size_t element, const std::string& val) return false; // badly formatted string } - auto unitsSystem = this->definition()->unitsSystem(); + auto unitSystem = this->definition()->unitSystem(); const std::string& myUnitStr = this->supportedUnits(); units::Unit myUnit; @@ -276,11 +276,11 @@ bool DoubleItem::setValueFromString(std::size_t element, const std::string& val) // item has known units. Note that we don't need to do conversion // if the value does not have units specified bool convert = false; - if (unitsSystem && (!(myUnitStr.empty() || valUnitsStr.empty()))) + if (unitSystem && (!(myUnitStr.empty() || valUnitsStr.empty()))) { // If we have a units System, let's see if the base units // are valid? - myUnit = unitsSystem->unit(myUnitStr, &convert); + myUnit = unitSystem->unit(myUnitStr, &convert); } double convertedVal; @@ -304,7 +304,7 @@ bool DoubleItem::setValueFromString(std::size_t element, const std::string& val) // We can convert units bool status; - auto valMeasure = unitsSystem->measurement(val, &status); + auto valMeasure = unitSystem->measurement(val, &status); if (!status) { // Could not parse the value @@ -312,7 +312,7 @@ bool DoubleItem::setValueFromString(std::size_t element, const std::string& val) } if (!valMeasure.m_units.dimensionless()) { - auto convertedMeasure = unitsSystem->convert(valMeasure, myUnit, &status); + auto convertedMeasure = unitSystem->convert(valMeasure, myUnit, &status); if (!status) { return false; @@ -519,9 +519,9 @@ double DoubleItem::value(std::size_t element, smtk::io::Logger& log) const return eval; // no conversion needed } // We need to convert to the units of the item - auto unitsSystem = this->definition()->unitsSystem(); + auto unitSystem = this->definition()->unitSystem(); // Is there a units system specified? - if (!unitsSystem) + if (!unitSystem) { smtkErrorMacro( log, @@ -531,7 +531,7 @@ double DoubleItem::value(std::size_t element, smtk::io::Logger& log) const return 0.0; } bool status; - auto myUnits = unitsSystem->unit(this->units(), &status); + auto myUnits = unitSystem->unit(this->units(), &status); if (!status) { smtkErrorMacro( @@ -541,7 +541,7 @@ double DoubleItem::value(std::size_t element, smtk::io::Logger& log) const << " are not supported for conversion."); return 0.0; } - auto exUnits = unitsSystem->unit(exStr, &status); + auto exUnits = unitSystem->unit(exStr, &status); if (!status) { smtkErrorMacro( @@ -553,7 +553,7 @@ double DoubleItem::value(std::size_t element, smtk::io::Logger& log) const } units::Measurement m(eval, exUnits); - auto newM = unitsSystem->convert(m, myUnits, &status); + auto newM = unitSystem->convert(m, myUnits, &status); if (!status) { smtkErrorMacro( diff --git a/smtk/attribute/DoubleItemDefinition.cxx b/smtk/attribute/DoubleItemDefinition.cxx index 6dbb2bacc6ffb0a083036b4eaf082cf2c77558c1..9964adc256140aa34887449ae71d0b4bfe2e9f48 100644 --- a/smtk/attribute/DoubleItemDefinition.cxx +++ b/smtk/attribute/DoubleItemDefinition.cxx @@ -124,21 +124,21 @@ bool DoubleItemDefinition::setDefaultValue( return false; } // do we have a unit system? - if (m_unitsSystem) + if (m_unitSystem) { // Are the units compatible? bool status; - auto defUnit = m_unitsSystem->unit(m_units, &status); + auto defUnit = m_unitSystem->unit(m_units, &status); if (!status) { return false; // Could not find the definition's units } - auto valUnit = m_unitsSystem->unit(units, &status); + auto valUnit = m_unitSystem->unit(units, &status); if (!status) { return false; // Could not find the default vals' units } - auto converter = m_unitsSystem->convert(valUnit, defUnit); + auto converter = m_unitSystem->convert(valUnit, defUnit); if (!converter) { return false; // Could not find a conversion between the units @@ -257,11 +257,11 @@ bool DoubleItemDefinition::reevaluateDefaults() std::string units; units::Unit defUnit; bool convert = false; - if (m_unitsSystem) + if (m_unitSystem) { // If we have an units System, lets see if the definition's units // are valid? - defUnit = m_unitsSystem->unit(m_units, &convert); + defUnit = m_unitSystem->unit(m_units, &convert); } // Can we not do conversion? @@ -302,7 +302,7 @@ bool DoubleItemDefinition::reevaluateDefaults() double convertedVal; for (std::size_t i = 0; i < m_defaultValuesAsStrings.size(); i++) { - auto valMeasure = m_unitsSystem->measurement(m_defaultValuesAsStrings[i], &status); + auto valMeasure = m_unitSystem->measurement(m_defaultValuesAsStrings[i], &status); if (!status) { // Could not parse the value @@ -310,7 +310,7 @@ bool DoubleItemDefinition::reevaluateDefaults() } if (!valMeasure.m_units.dimensionless()) { - auto convertedMeasure = m_unitsSystem->convert(valMeasure, defUnit, &status); + auto convertedMeasure = m_unitSystem->convert(valMeasure, defUnit, &status); if (!status) { return false; diff --git a/smtk/attribute/GroupItemDefinition.cxx b/smtk/attribute/GroupItemDefinition.cxx index 61e05b590b081319a4a374761b3e03822520e933..eda96072f60da84765df3937dfca81345199a5eb 100644 --- a/smtk/attribute/GroupItemDefinition.cxx +++ b/smtk/attribute/GroupItemDefinition.cxx @@ -57,7 +57,7 @@ bool GroupItemDefinition::addItemDefinition(smtk::attribute::ItemDefinitionPtr c { cdef->setIsOptional(true); } - cdef->setUnitsSystem(m_unitsSystem); + cdef->setUnitSystem(m_unitSystem); return true; } @@ -238,12 +238,12 @@ bool GroupItemDefinition::removeItemDefinition(ItemDefinitionPtr itemDef) return true; } -void GroupItemDefinition::setUnitsSystem(const shared_ptr<units::System>& unitsSystem) +void GroupItemDefinition::setUnitSystem(const shared_ptr<units::System>& unitSystem) { - m_unitsSystem = unitsSystem; + m_unitSystem = unitSystem; for (const auto& item : m_itemDefs) { - item->setUnitsSystem(m_unitsSystem); + item->setUnitSystem(m_unitSystem); } } diff --git a/smtk/attribute/GroupItemDefinition.h b/smtk/attribute/GroupItemDefinition.h index 47ca4c1f392016a2011567be73c0343e860bee80..53a0fee95d189795ecc7544bb285d3a875bb3e60 100644 --- a/smtk/attribute/GroupItemDefinition.h +++ b/smtk/attribute/GroupItemDefinition.h @@ -65,8 +65,8 @@ public: { item->setIsOptional(true); } - // We need to get a pointer to the base Item class to set the unitsSystem - static_cast<ItemDefinition*>(item.get())->setUnitsSystem(m_unitsSystem); + // We need to get a pointer to the base Item class to set the unitSystem + static_cast<ItemDefinition*>(item.get())->setUnitSystem(m_unitSystem); m_itemDefs.push_back(item); m_itemDefPositions[inName] = static_cast<int>(n); } @@ -159,7 +159,10 @@ protected: void applyAdvanceLevels( const unsigned int& readLevelFromParent, const unsigned int& writeLevelFromParent) override; - void setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override; + void setUnitSystem(const shared_ptr<units::System>& unitSystem) override; + SMTK_DEPRECATED_IN_NEXT("Use setUnitSystem() instead.") + void setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override + { this->setUnitSystem(unitsSystem); } std::vector<smtk::attribute::ItemDefinitionPtr> m_itemDefs; std::map<std::string, int> m_itemDefPositions; std::vector<std::string> m_labels; diff --git a/smtk/attribute/ItemDefinition.cxx b/smtk/attribute/ItemDefinition.cxx index 5923c35eeb641010bdfe8ca53c540c86c67feba5..f7874a611ca61966a1048170772c9855de979d73 100644 --- a/smtk/attribute/ItemDefinition.cxx +++ b/smtk/attribute/ItemDefinition.cxx @@ -157,7 +157,7 @@ bool ItemDefinition::removeTag(const std::string& name) return false; } -void ItemDefinition::setUnitsSystem(const shared_ptr<units::System>& unitsSystem) +void ItemDefinition::setUnitSystem(const shared_ptr<units::System>& unitSystem) { - m_unitsSystem = unitsSystem; + m_unitSystem = unitSystem; } diff --git a/smtk/attribute/ItemDefinition.h b/smtk/attribute/ItemDefinition.h index 1ac7f9eb00525a2858d219eb86fea4aab8ae3dbe..9b2a45e31ef99c4766055f6f12a6b0c14eacf468 100644 --- a/smtk/attribute/ItemDefinition.h +++ b/smtk/attribute/ItemDefinition.h @@ -23,6 +23,7 @@ #include "smtk/attribute/Item.h" // For Item Types. #include "smtk/attribute/Tag.h" #include "smtk/common/Categories.h" +#include "smtk/common/Deprecation.h" #include <queue> #include <set> @@ -167,8 +168,10 @@ public: bool removeTag(const std::string& name); ///@} - ///\brief Return the unitsSystem of the Definition - const shared_ptr<units::System>& unitsSystem() const { return m_unitsSystem; } + ///\brief Return the unitSystem of the Definition + const shared_ptr<units::System>& unitSystem() const { return m_unitSystem; } + SMTK_DEPRECATED_IN_NEXT("Use unitSystem() instead.") + const shared_ptr<units::System>& unitsSystem() const { return m_unitSystem; } virtual smtk::attribute::ItemPtr buildItem(Attribute* owningAttribute, int itemPosition) const = 0; @@ -189,10 +192,13 @@ protected: const unsigned int& readLevelFromParent, const unsigned int& writeLevelFromParent); - ///\brief Set the unitsSystem of the Definition + ///\brief Set the unitSystem of the Definition /// /// Note that this should be done before units are specified in the Definition - virtual void setUnitsSystem(const shared_ptr<units::System>& unitsSystem); + virtual void setUnitSystem(const shared_ptr<units::System>& unitSystem); + SMTK_DEPRECATED_IN_NEXT("Use setUnitSystem() instead.") + virtual void setUnitsSystem(const shared_ptr<units::System>& unitsSystem) + { this->setUnitSystem(unitsSystem); } int m_version; bool m_isOptional; @@ -207,7 +213,7 @@ protected: unsigned int m_advanceLevel[2]; attribute::Tags m_tags; smtk::common::Categories::CombinationMode m_combinationMode; - std::shared_ptr<units::System> m_unitsSystem; + std::shared_ptr<units::System> m_unitSystem; private: // constant value that should never be changed diff --git a/smtk/attribute/ReferenceItemDefinition.cxx b/smtk/attribute/ReferenceItemDefinition.cxx index 75005792726beacf6f6fd53d384f0b1bb52dd47e..7abd002d1b604bd1992ff61791d7f02bb1a04cea 100644 --- a/smtk/attribute/ReferenceItemDefinition.cxx +++ b/smtk/attribute/ReferenceItemDefinition.cxx @@ -611,13 +611,13 @@ std::size_t ReferenceItemDefinition::testConditionals(PersistentObjectPtr& objec return s_invalidIndex; } -void ReferenceItemDefinition::setUnitsSystem(const shared_ptr<units::System>& unitsSystem) +void ReferenceItemDefinition::setUnitSystem(const shared_ptr<units::System>& unitSystem) { - m_unitsSystem = unitsSystem; + m_unitSystem = unitSystem; for (const auto& item : m_itemDefs) { - item.second->setUnitsSystem(m_unitsSystem); + item.second->setUnitSystem(m_unitSystem); } } } // namespace attribute diff --git a/smtk/attribute/ReferenceItemDefinition.h b/smtk/attribute/ReferenceItemDefinition.h index 1acd7c629650b9e2c785e800bc2651b939af3445..b792cb2a6e493f8ce2505c5118c0c32480de28cd 100644 --- a/smtk/attribute/ReferenceItemDefinition.h +++ b/smtk/attribute/ReferenceItemDefinition.h @@ -265,7 +265,10 @@ protected: const smtk::common::Categories::Stack& inheritedFromParent, smtk::common::Categories& inheritedToParent) override; - void setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override; + void setUnitSystem(const shared_ptr<units::System>& unitSystem) override; + SMTK_DEPRECATED_IN_NEXT("Use setUnitSystem() instead.") + void setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override + { this->setUnitSystem(unitsSystem); } /// Debug method that returns a description concerning the validity of the component std::string componentValidityCheck(const smtk::resource::Component* comp) const; diff --git a/smtk/attribute/Resource.cxx b/smtk/attribute/Resource.cxx index afab3d5e37ecb0525265c5fe400b621a8f1deb75..850b2d108c63dc41a46c710084dfcdd35aec5009 100644 --- a/smtk/attribute/Resource.cxx +++ b/smtk/attribute/Resource.cxx @@ -54,7 +54,7 @@ Resource::Resource(const smtk::common::UUID& myID, smtk::resource::ManagerPtr ma : smtk::resource::DerivedFrom<Resource, smtk::geometry::Resource>(myID, manager) { queries().registerQueries<QueryList>(); - m_unitsSystem = units::System:: + m_unitSystem = units::System:: createWithDefaults(); // Create a unit system with some prefixes, dimensions, and units. // Do not display resource views in the attribute panel automatically. @@ -66,7 +66,7 @@ Resource::Resource(smtk::resource::ManagerPtr manager) : smtk::resource::DerivedFrom<Resource, smtk::geometry::Resource>(manager) { queries().registerQueries<QueryList>(); - m_unitsSystem = units::System:: + m_unitSystem = units::System:: createWithDefaults(); // Create a unit system with some prefixes, dimensions, and units. // Do not display resource views in the attribute panel automatically. @@ -84,20 +84,20 @@ Resource::~Resource() } } -bool Resource::setUnitsSystem(const shared_ptr<units::System>& unitsSystem) +bool Resource::setUnitSystem(const shared_ptr<units::System>& unitSystem) { - if (m_unitsSystem == unitsSystem) + if (m_unitSystem == unitSystem) { return true; } // Since changing units systems can invalidate Item Definitions and Items // we only can change systems when there are no definitions or if there was not // currently an units system specified. - if (m_unitsSystem && !m_definitions.empty()) + if (m_unitSystem && !m_definitions.empty()) { return false; } - m_unitsSystem = unitsSystem; + m_unitSystem = unitSystem; return true; } diff --git a/smtk/attribute/Resource.h b/smtk/attribute/Resource.h index cae76c0d69d325c155099bec06c3307547731f0b..7d2f20d745c3c525f7cd8ce1761725e060291080 100644 --- a/smtk/attribute/Resource.h +++ b/smtk/attribute/Resource.h @@ -106,7 +106,10 @@ public: ~Resource() override; - bool setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override; + bool setUnitSystem(const shared_ptr<units::System>& unitSystem) override; + SMTK_DEPRECATED_IN_NEXT("Use setUnitSystem instead.") + bool setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override + { return this->setUnitSystem(unitsSystem); } smtk::attribute::DefinitionPtr createDefinition( const std::string& typeName, @@ -443,7 +446,7 @@ public: /// /// If \a options has copyTemplateData() set to true, then this resource's /// Definition instances will be copied to the output resources. - /// In addition, unitsSystem and Analysis information is copied. + /// In addition, unitSystem and Analysis information is copied. std::shared_ptr<smtk::resource::Resource> clone( smtk::resource::CopyOptions& options) const override; diff --git a/smtk/attribute/ValueItem.cxx b/smtk/attribute/ValueItem.cxx index cfcb55df33d29ef94a9ad675fbed2a14170dd05c..d30160239afbf3480862b966a944eb6d9bf039ab 100644 --- a/smtk/attribute/ValueItem.cxx +++ b/smtk/attribute/ValueItem.cxx @@ -340,7 +340,7 @@ bool ValueItem::isAcceptable(const smtk::attribute::AttributePtr& exp) const return false; } - const auto& unitSys = def->unitsSystem(); + const auto& unitSys = def->unitSystem(); // Can't determine if the units are compatible w/o units system if (!unitSys) { @@ -976,11 +976,11 @@ std::string ValueItem::supportedUnits() const { bool status = false; auto munits = this->units(); - auto unitsSystem = m_definition->unitsSystem(); + auto unitSystem = m_definition->unitSystem(); - if (!(munits.empty() || (unitsSystem == nullptr))) + if (!(munits.empty() || (unitSystem == nullptr))) { - auto myUnit = unitsSystem->unit(munits, &status); + auto myUnit = unitSystem->unit(munits, &status); } return (status ? munits : std::string()); } diff --git a/smtk/attribute/ValueItemDefinition.cxx b/smtk/attribute/ValueItemDefinition.cxx index 5eacf6f6d97fa47feeaae6b60baf80d0964a4166..8d9772eb9e9fb208fd6830d1c8f1f8c12844d98d 100644 --- a/smtk/attribute/ValueItemDefinition.cxx +++ b/smtk/attribute/ValueItemDefinition.cxx @@ -521,7 +521,7 @@ bool ValueItemDefinition::addItemDefinition(smtk::attribute::ItemDefinitionPtr c return false; } m_itemDefs[cdef->name()] = cdef; - cdef->setUnitsSystem(m_unitsSystem); + cdef->setUnitSystem(m_unitSystem); return true; } @@ -575,13 +575,13 @@ std::vector<std::string> ValueItemDefinition::relevantEnums( return result; } -void ValueItemDefinition::setUnitsSystem(const shared_ptr<units::System>& unitsSystem) +void ValueItemDefinition::setUnitSystem(const shared_ptr<units::System>& unitSystem) { - m_unitsSystem = unitsSystem; + m_unitSystem = unitSystem; for (const auto& item : m_itemDefs) { - item.second->setUnitsSystem(m_unitsSystem); + item.second->setUnitSystem(m_unitSystem); } } @@ -605,10 +605,10 @@ bool ValueItemDefinition::isDiscreteIndexValid(int index) const bool ValueItemDefinition::hasSupportedUnits() const { - if (!(m_units.empty() || (m_unitsSystem == nullptr))) + if (!(m_units.empty() || (m_unitSystem == nullptr))) { bool status; - auto defUnit = m_unitsSystem->unit(m_units, &status); + auto defUnit = m_unitSystem->unit(m_units, &status); return status; } return false; @@ -617,9 +617,9 @@ bool ValueItemDefinition::hasSupportedUnits() const std::string ValueItemDefinition::supportedUnits() const { bool status = false; - if (!(m_units.empty() || (m_unitsSystem == nullptr))) + if (!(m_units.empty() || (m_unitSystem == nullptr))) { - auto defUnit = m_unitsSystem->unit(m_units, &status); + auto defUnit = m_unitSystem->unit(m_units, &status); } return (status ? m_units : std::string()); } diff --git a/smtk/attribute/ValueItemDefinition.h b/smtk/attribute/ValueItemDefinition.h index 07223e3b2d8352dce32266fb2e364f8d360a29dd..b858bd774f04567da98cfb0037b34a904a1c3a78 100644 --- a/smtk/attribute/ValueItemDefinition.h +++ b/smtk/attribute/ValueItemDefinition.h @@ -248,8 +248,8 @@ public: } item = SharedTypes::RawPointerType::New(idName); m_itemDefs[item->name()] = item; - // We need to get a pointer to the base Item class to set the unitsSystem - static_cast<ItemDefinition*>(item.get())->setUnitsSystem(m_unitsSystem); + // We need to get a pointer to the base Item class to set the unitSystem + static_cast<ItemDefinition*>(item.get())->setUnitSystem(m_unitSystem); return item; } @@ -290,7 +290,10 @@ protected: const unsigned int& readLevelFromParent, const unsigned int& writeLevelFromParent) override; - void setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override; + void setUnitSystem(const shared_ptr<units::System>& unitSystem) override; + SMTK_DEPRECATED_IN_NEXT("Use setUnitSystem() instead.") + void setUnitsSystem(const shared_ptr<units::System>& unitsSystem) override + { this->setUnitSystem(unitsSystem); } virtual void updateDiscreteValue() = 0; bool m_hasDefault; diff --git a/smtk/attribute/operators/Write.cxx b/smtk/attribute/operators/Write.cxx index 1e554a7960fae0e981ab79584634275cc5a179e2..56b560df06dc8c1056f91ead25f768b53b242bde 100644 --- a/smtk/attribute/operators/Write.cxx +++ b/smtk/attribute/operators/Write.cxx @@ -52,7 +52,10 @@ Write::Result Write::operateInternal() } { - std::ofstream file(resource->location()); + // Ensure parent directory exists. It may not if we are part of a project + // that has organized us into an unrealized subdirectory. + auto location = resource->absoluteLocation(/*create directories*/true); + std::ofstream file(location); if (!file.good()) { smtkErrorMacro(log(), "Unable to open \"" << resource->location() << "\" for writing."); @@ -82,6 +85,17 @@ void Write::markModifiedResources(Write::Result& /*unused*/) } } +void Write::generateSummary(Result& res) +{ + if (smtk::operation::outcome(res) != Outcome::SUCCEEDED) + { + this->Superclass::generateSummary(res); + } + auto resourceItem = this->parameters()->associations(); + auto resource = std::dynamic_pointer_cast<smtk::resource::Resource>(resourceItem->value()); + smtkInfoMacro(this->log(), "Wrote \"" << resource->location() << "\"."); +} + bool write( const smtk::resource::ResourcePtr& resource, const std::shared_ptr<smtk::common::Managers>& managers) diff --git a/smtk/attribute/operators/Write.h b/smtk/attribute/operators/Write.h index 30ccd4b85077477f2a45e4e5c7a6b80dd0b66cfa..1925dee44c56c6ae6958e11983f6589978259503 100644 --- a/smtk/attribute/operators/Write.h +++ b/smtk/attribute/operators/Write.h @@ -31,6 +31,7 @@ protected: Result operateInternal() override; const char* xmlDescription() const override; void markModifiedResources(Result&) override; + void generateSummary(Result&) override; }; SMTKCORE_EXPORT bool write( diff --git a/smtk/attribute/pybind11/PybindAssociate.h b/smtk/attribute/pybind11/PybindAssociate.h index d87c54fc7fa0aecd387e5029a3effd7c6d30e2bf..5ce9cfa50cb5199bc36c15f710b44da428c9d9f6 100644 --- a/smtk/attribute/pybind11/PybindAssociate.h +++ b/smtk/attribute/pybind11/PybindAssociate.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::attribute::Associate, smtk::operation::XMLOperati PySharedPtrClass< smtk::attribute::Associate, smtk::operation::XMLOperation > instance(m, "Associate"); instance .def(py::init<>()) - .def(py::init<::smtk::attribute::Associate const &>()) - .def("deepcopy", (smtk::attribute::Associate & (smtk::attribute::Associate::*)(::smtk::attribute::Associate const &)) &smtk::attribute::Associate::operator=) .def_static("create", (std::shared_ptr<smtk::attribute::Associate> (*)()) &smtk::attribute::Associate::create) .def_static("create", (std::shared_ptr<smtk::attribute::Associate> (*)(::std::shared_ptr<smtk::attribute::Associate> &)) &smtk::attribute::Associate::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::attribute::Associate> (smtk::attribute::Associate::*)() const) &smtk::attribute::Associate::shared_from_this) diff --git a/smtk/attribute/pybind11/PybindDissociate.h b/smtk/attribute/pybind11/PybindDissociate.h index a9d928807c96091bb6eb18d93a8d78caf50e4fd8..d587cc83ea71ab9a41211e8e0ec2cbc7d48d3b17 100644 --- a/smtk/attribute/pybind11/PybindDissociate.h +++ b/smtk/attribute/pybind11/PybindDissociate.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::attribute::Dissociate, smtk::operation::XMLOperat PySharedPtrClass< smtk::attribute::Dissociate, smtk::operation::XMLOperation > instance(m, "Dissociate"); instance .def(py::init<>()) - .def(py::init<::smtk::attribute::Dissociate const &>()) - .def("deepcopy", (smtk::attribute::Dissociate & (smtk::attribute::Dissociate::*)(::smtk::attribute::Dissociate const &)) &smtk::attribute::Dissociate::operator=) .def_static("create", (std::shared_ptr<smtk::attribute::Dissociate> (*)()) &smtk::attribute::Dissociate::create) .def_static("create", (std::shared_ptr<smtk::attribute::Dissociate> (*)(::std::shared_ptr<smtk::attribute::Dissociate> &)) &smtk::attribute::Dissociate::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::attribute::Dissociate> (smtk::attribute::Dissociate::*)() const) &smtk::attribute::Dissociate::shared_from_this) diff --git a/smtk/attribute/pybind11/PybindExport.h b/smtk/attribute/pybind11/PybindExport.h index a20bb348f9c9f434aef9320abd5a5e3651123286..49431b4de754232737f4929aa66549747677668d 100644 --- a/smtk/attribute/pybind11/PybindExport.h +++ b/smtk/attribute/pybind11/PybindExport.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::attribute::Export, smtk::operation::XMLOperation PySharedPtrClass< smtk::attribute::Export, smtk::operation::XMLOperation > instance(m, "Export"); instance .def(py::init<>()) - .def(py::init<::smtk::attribute::Export const &>()) - .def("deepcopy", (smtk::attribute::Export & (smtk::attribute::Export::*)(::smtk::attribute::Export const &)) &smtk::attribute::Export::operator=) .def_static("create", (std::shared_ptr<smtk::attribute::Export> (*)()) &smtk::attribute::Export::create) .def_static("create", (std::shared_ptr<smtk::attribute::Export> (*)(::std::shared_ptr<smtk::attribute::Export> &)) &smtk::attribute::Export::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::attribute::Export> (smtk::attribute::Export::*)() const) &smtk::attribute::Export::shared_from_this) diff --git a/smtk/attribute/pybind11/PybindImport.h b/smtk/attribute/pybind11/PybindImport.h index 2c6d16ad18d71b202e08791226ea80fa1e96d82c..9b4d38eb7fb48eb66d67cc4bc3e228959586568e 100644 --- a/smtk/attribute/pybind11/PybindImport.h +++ b/smtk/attribute/pybind11/PybindImport.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::attribute::Import, smtk::operation::XMLOperation PySharedPtrClass< smtk::attribute::Import, smtk::operation::XMLOperation > instance(m, "Import"); instance .def(py::init<>()) - .def(py::init<::smtk::attribute::Import const &>()) - .def("deepcopy", (smtk::attribute::Import & (smtk::attribute::Import::*)(::smtk::attribute::Import const &)) &smtk::attribute::Import::operator=) .def_static("create", (std::shared_ptr<smtk::attribute::Import> (*)()) &smtk::attribute::Import::create) .def_static("create", (std::shared_ptr<smtk::attribute::Import> (*)(::std::shared_ptr<smtk::attribute::Import> &)) &smtk::attribute::Import::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::attribute::Import> (smtk::attribute::Import::*)() const) &smtk::attribute::Import::shared_from_this) diff --git a/smtk/attribute/pybind11/PybindRead.h b/smtk/attribute/pybind11/PybindRead.h index e72a26a73a777edc0bc6eb6e43ad328e8149d39e..45c1b3ba30f4b16c5dba1bbb1d8033b1e9784558 100644 --- a/smtk/attribute/pybind11/PybindRead.h +++ b/smtk/attribute/pybind11/PybindRead.h @@ -25,8 +25,6 @@ inline PySharedPtrClass< smtk::attribute::Read, smtk::operation::XMLOperation > PySharedPtrClass< smtk::attribute::Read, smtk::operation::XMLOperation > instance(m, "Read"); instance .def(py::init<>()) - .def(py::init<::smtk::attribute::Read const &>()) - .def("deepcopy", (smtk::attribute::Read & (smtk::attribute::Read::*)(::smtk::attribute::Read const &)) &smtk::attribute::Read::operator=) .def_static("create", (std::shared_ptr<smtk::attribute::Read> (*)()) &smtk::attribute::Read::create) .def_static("create", (std::shared_ptr<smtk::attribute::Read> (*)(::std::shared_ptr<smtk::attribute::Read> &)) &smtk::attribute::Read::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::attribute::Read> (smtk::attribute::Read::*)() const) &smtk::attribute::Read::shared_from_this) diff --git a/smtk/attribute/pybind11/PybindReferenceItem.h b/smtk/attribute/pybind11/PybindReferenceItem.h index 9d04c5fba079ab97d134772b869ce6d47c2610e6..ae4adfc792a5cf28cec1c32bc5ceb51fb1b169e8 100644 --- a/smtk/attribute/pybind11/PybindReferenceItem.h +++ b/smtk/attribute/pybind11/PybindReferenceItem.h @@ -57,6 +57,21 @@ inline PySharedPtrClass< smtk::attribute::ReferenceItem, smtk::attribute::Item > .def("unset", &smtk::attribute::ReferenceItem::unset, py::arg("i") = 0) .def("valueAsString", (std::string (smtk::attribute::ReferenceItem::*)() const) &smtk::attribute::ReferenceItem::valueAsString) .def("valueAsString", (std::string (smtk::attribute::ReferenceItem::*)(::size_t) const) &smtk::attribute::ReferenceItem::valueAsString, py::arg("i")) + .def("setValues", [&](smtk::attribute::ReferenceItem* self, const std::vector<smtk::resource::PersistentObject::Ptr>& values) + { + return self->setValues(values.begin(), values.end()); + }, py::arg("values")) + .def("values", [&](smtk::attribute::ReferenceItem* self) -> std::vector<smtk::resource::PersistentObject::Ptr> + { + std::vector<smtk::resource::PersistentObject::Ptr> values; + std::size_t nn = self->numberOfValues(); + values.reserve(nn); + for (std::size_t ii = 0; ii < nn; ++ii) + { + values.push_back(self->isSet(ii) ? self->value(ii) : smtk::resource::PersistentObject::Ptr()); + } + return values; + }) ; return instance; } diff --git a/smtk/attribute/pybind11/PybindWrite.h b/smtk/attribute/pybind11/PybindWrite.h index b7a3b083475770cb482cf28116142b9c0edc4282..22cd6bd27aaaeb96545fdb7d87654b2d24bd887f 100644 --- a/smtk/attribute/pybind11/PybindWrite.h +++ b/smtk/attribute/pybind11/PybindWrite.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::attribute::Write, smtk::operation::XMLOperation > PySharedPtrClass< smtk::attribute::Write, smtk::operation::XMLOperation > instance(m, "Write"); instance .def(py::init<>()) - .def(py::init<::smtk::attribute::Write const &>()) - .def("deepcopy", (smtk::attribute::Write & (smtk::attribute::Write::*)(::smtk::attribute::Write const &)) &smtk::attribute::Write::operator=) .def_static("create", (std::shared_ptr<smtk::attribute::Write> (*)()) &smtk::attribute::Write::create) .def_static("create", (std::shared_ptr<smtk::attribute::Write> (*)(::std::shared_ptr<smtk::attribute::Write> &)) &smtk::attribute::Write::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::attribute::Write> (smtk::attribute::Write::*)() const) &smtk::attribute::Write::shared_from_this) diff --git a/smtk/common/CMakeLists.txt b/smtk/common/CMakeLists.txt index 2c5489637c96efcf2e43762ee3f8ac2727255aaf..132b5029b941f38a0fbd3a62fa288f8ffc004d70 100644 --- a/smtk/common/CMakeLists.txt +++ b/smtk/common/CMakeLists.txt @@ -17,6 +17,7 @@ set(commonSrcs TimeZone.cxx timezonespec.cxx TypeContainer.cxx + URL.cxx UUID.cxx UUIDGenerator.cxx VersionNumber.cxx @@ -63,6 +64,7 @@ set(commonHeaders TypeName.h TypeContainer.h TypeTraits.h + URL.h UUID.h UUIDGenerator.h VersionNumber.h diff --git a/smtk/common/Paths.cxx b/smtk/common/Paths.cxx index f3eeae0c17cd93a0da531ebd6debb186a01f6e7d..a33386c789d2d4610e8fe2c5c2e74d67428aa1ff 100644 --- a/smtk/common/Paths.cxx +++ b/smtk/common/Paths.cxx @@ -265,6 +265,11 @@ std::string Paths::uniquePath() return boost::filesystem::unique_path().string(); } +std::string Paths::uniquePath(const std::string& modelPath) +{ + return boost::filesystem::unique_path(modelPath).string(); +} + /**\brief Return the best guess at the directory containing the current process's executable. */ std::string Paths::executableDirectory() diff --git a/smtk/common/Paths.h b/smtk/common/Paths.h index 2b6a77d688a3aeead20ecba08553b276e4f787d2..7a4c21f156ff8b5bb80ce88dabfbd4b96de956b7 100644 --- a/smtk/common/Paths.h +++ b/smtk/common/Paths.h @@ -64,6 +64,8 @@ public: static std::string replaceFilename(const std::string& path, const std::string& newFilename); static std::string tempDirectory(); static std::string uniquePath(); + /// The \a modelPath must have percent (%) signs that will be replaced with random characters. + static std::string uniquePath(const std::string& modelPath); std::string executableDirectory(); std::string toplevelDirectory(); diff --git a/smtk/common/URL.cxx b/smtk/common/URL.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5841685132d2f35e9fb1b7471fa315a147d4c839 --- /dev/null +++ b/smtk/common/URL.cxx @@ -0,0 +1,172 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/common/URL.h" + +#include "tao/pegtl.hpp" +#include "tao/pegtl/contrib/uri.hpp" + +namespace pegtl = tao::pegtl; + +namespace smtk +{ +namespace common +{ +namespace uri +{ + +template< bool (smtk::common::URL::*Field)(smtk::string::Token) > +struct bind +{ + template< typename Input > + static void apply( const Input& in, smtk::common::URL& url ) + { + (url.*Field)(smtk::string::Token(in.string())); + } +}; + +// clang-format off +template< typename Rule > struct action {}; + +template<> struct action< pegtl::uri::scheme > : bind< &URL::setScheme > {}; +template<> struct action< pegtl::uri::authority > : bind< &URL::setAuthority > {}; +// template<> struct action< pegtl::uri::host > : bind< &URL::setHost > {}; +// template<> struct action< pegtl::uri::port > : bind< &URL::setPort > {}; +template<> struct action< pegtl::uri::path_noscheme > : bind< &URL::setPath > {}; +template<> struct action< pegtl::uri::path_rootless > : bind< &URL::setPath > {}; +template<> struct action< pegtl::uri::path_absolute > : bind< &URL::setPath > {}; +template<> struct action< pegtl::uri::path_abempty > : bind< &URL::setPath > {}; +template<> struct action< pegtl::uri::query > : bind< &URL::setQuery > {}; +template<> struct action< pegtl::uri::fragment > : bind< &URL::setFragment > {}; +// clang-format on + +} // anonymous namespace + +URL::URL(const std::string& txt) +{ + using grammar = pegtl::must< pegtl::uri::URI >; + pegtl::memory_input<> input( txt, "smtk::common::URL" ); + pegtl::parse< grammar, uri::action >( input, *this ); +} + +URL::URL(Token scheme, Token authority, Token path, Token query, Token fragment) + : m_scheme(scheme) + , m_authority(authority) + , m_path(path) + , m_query(query) + , m_fragment(fragment) +{ +} + +bool URL::valid() const +{ + // The URL "/" is not valid. If you want the root of the local filesystem, use "file:///". + return m_path.valid() || m_scheme.valid(); +} + +bool URL::setScheme(Token scheme) +{ + if (scheme == m_scheme) { return false; } + m_scheme = scheme; + return true; +} + +bool URL::setAuthority(Token authority) +{ + if (authority == m_authority) { return false; } + m_authority = authority; + return true; +} + +bool URL::setPath(Token path) +{ + if (path == m_path) { return false; } + m_path = path; + return true; +} + +bool URL::setQuery(Token query) +{ + if (query == m_query) { return false; } + m_query = query; + return true; +} + +bool URL::setFragment(Token fragment) +{ + if (fragment == m_fragment) { return false; } + m_fragment = fragment; + return true; +} + +bool URL::operator!=(URL const& other) const +{ + return m_scheme != other.m_scheme || + m_authority != other.m_authority || + m_path != other.m_path || + m_query != other.m_query || + m_fragment != other.m_fragment; +} + +bool URL::operator==(URL const& other) const +{ + return m_scheme == other.m_scheme && + m_authority == other.m_authority && + m_path == other.m_path && + m_query == other.m_query && + m_fragment == other.m_fragment; +} + +bool URL::operator<(URL const& other) const +{ + return + m_scheme < other.m_scheme || ( + m_scheme == other.m_scheme && ( + m_authority < other.m_authority || ( + m_authority == other.m_authority && ( + m_path < other.m_path || ( + m_path == other.m_path && ( + m_query < other.m_query || ( + m_query == other.m_query && + m_fragment < other.m_fragment + ) + ) + ) + ) + ) + ) + ); +} + +URL::operator bool() const +{ + return this->valid(); +} + +URL::operator std::string() const +{ + std::string result; + if (m_scheme.valid()) { result += m_scheme.data() + ":"; } + if (m_authority.valid()) { result += "//" + m_authority.data(); } + // result += "/"; + if (m_path.valid()) { result += m_path.data(); } + if (m_query.valid()) { result += "?" + m_query.data(); } + if (m_fragment.valid()) { result += "#" + m_fragment.data(); } + return result; +} + +std::ostream& operator<<(std::ostream& stream, const URL& url) +{ + std::string data = (std::string)url; + stream << data; + return stream; +} + +} // namespace common +} // namespace smtk diff --git a/smtk/common/URL.h b/smtk/common/URL.h new file mode 100644 index 0000000000000000000000000000000000000000..5151eaf2cc9ddafbce0fbebacffbc638d14eddf0 --- /dev/null +++ b/smtk/common/URL.h @@ -0,0 +1,74 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef smtk_common_URL_h +#define smtk_common_URL_h + +#include "smtk/string/Token.h" + +namespace smtk +{ +namespace common +{ + +/** An RFC3986-compliant Uniform Resource Locator (URL). + * + * This class holds URL data as a set of string tokens. + * If given a single string, that string is parsed and + * decomposed into tokens for querying data. + */ +class SMTKCORE_EXPORT URL +{ +public: + using Token = smtk::string::Token; + + URL() = default; + URL(const URL& other) = default; + URL(const std::string& txt); + URL(Token scheme, Token authority, Token path, Token query = Token(), Token fragment = Token()); + + bool valid() const; + bool operator!=(URL const& other) const; + bool operator==(URL const& other) const; + bool operator<(URL const& other) const; + + URL& operator=(URL const& other) = default; + + operator bool() const; + explicit operator std::string() const; + + Token scheme() const { return m_scheme; } + bool setScheme(Token scheme); + + Token authority() const { return m_authority; } + bool setAuthority(Token authority); + + Token path() const { return m_path; } + bool setPath(Token path); + + Token query() const { return m_query; } + bool setQuery(Token query); + + Token fragment() const { return m_fragment; } + bool setFragment(Token fragment); + +protected: + Token m_scheme; + Token m_authority; + Token m_path; + Token m_query; + Token m_fragment; +}; + +SMTKCORE_EXPORT std::ostream& operator<<(std::ostream& stream, const URL& url); + +} // namespace common +} // namespace smtk + +#endif // smtk_common_URL_h diff --git a/smtk/common/pybind11/PybindCommon.cxx b/smtk/common/pybind11/PybindCommon.cxx index a26b1e806da68d7fff48612dfdd5d6a9cfbbaca7..8f8ffb9150814d96243d98ef495b0dd1b28a90d7 100644 --- a/smtk/common/pybind11/PybindCommon.cxx +++ b/smtk/common/pybind11/PybindCommon.cxx @@ -42,6 +42,7 @@ using PySharedPtrClass = py::class_<T, std::shared_ptr<T>, Args...>; #include "PybindRangeDetector.h" #include "PybindStringUtil.h" #include "PybindTimeZone.h" +#include "PybindURL.h" #include "PybindUUID.h" #include "PybindUUIDGenerator.h" #include "PybindUnionFind.h" @@ -73,6 +74,7 @@ PYBIND11_MODULE(_smtkPybindCommon, common) #endif py::class_< smtk::common::StringUtil > smtk_common_StringUtil = pybind11_init_smtk_common_StringUtil(common); py::class_< smtk::common::TimeZone > smtk_common_TimeZone = pybind11_init_smtk_common_TimeZone(common); + py::class_< smtk::common::URL > smtk_common_URL = pybind11_init_smtk_common_URL(common); py::class_< smtk::common::UUID > smtk_common_UUID = pybind11_init_smtk_common_UUID(common); py::class_< smtk::common::UUIDGenerator > smtk_common_UUIDGenerator = pybind11_init_smtk_common_UUIDGenerator(common); py::class_< smtk::common::Version > smtk_common_Version = pybind11_init_smtk_common_Version(common); diff --git a/smtk/common/pybind11/PybindURL.h b/smtk/common/pybind11/PybindURL.h new file mode 100644 index 0000000000000000000000000000000000000000..48e93c88ed24fe337fa8f350cab8e69454686cac --- /dev/null +++ b/smtk/common/pybind11/PybindURL.h @@ -0,0 +1,58 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= + +#ifndef pybind_smtk_common_URL_h +#define pybind_smtk_common_URL_h + +#include <pybind11/pybind11.h> + +#include "smtk/common/URL.h" + +namespace py = pybind11; + +inline py::class_< smtk::common::URL > pybind11_init_smtk_common_URL(py::module &m) +{ + py::class_< smtk::common::URL > instance(m, "URL"); + instance + .def(py::init<>()) + .def(py::init<::smtk::common::URL const &>()) + .def(py::init<::std::string const &>()) + .def("__eq__", [](smtk::common::URL* url, smtk::common::URL* other) + { return *url == *other; }, py::arg("other")) + .def("__lt__", [](smtk::common::URL* url, smtk::common::URL* other) + { return *url < *other; }, py::arg("other")) + .def("__hash__", [](smtk::common::URL* url) { return smtk::string::Token((std::string)*url).id(); }) + .def("__str__", [](smtk::common::URL* url) { return (std::string)*url; }) + .def("__repr__", [](smtk::common::URL* url) { return "URL('" + (std::string)*url + "')"; }) + .def("deepcopy", + (smtk::common::URL & (smtk::common::URL::*)(::smtk::common::URL const &)) + &smtk::common::URL::operator=) + .def("valid", &smtk::common::URL::valid) + .def("to_string", [](const smtk::common::URL& url) + { + auto result = (std::string)url; + return result; + } + ) + .def("scheme", (smtk::string::Token (smtk::common::URL::*)() const)&smtk::common::URL::scheme) + .def("authority", (smtk::string::Token (smtk::common::URL::*)() const)&smtk::common::URL::authority) + .def("path", (smtk::string::Token (smtk::common::URL::*)() const)&smtk::common::URL::path) + .def("query", (smtk::string::Token (smtk::common::URL::*)() const)&smtk::common::URL::query) + .def("fragment", (smtk::string::Token (smtk::common::URL::*)() const)&smtk::common::URL::fragment) + .def("setScheme", [](smtk::common::URL* url, const std::string& txt) { return url->setScheme(txt); }, py::arg("scheme")) + .def("setAuthority", [](smtk::common::URL* url, const std::string& txt) { return url->setAuthority(txt); }, py::arg("authority")) + .def("setPath", [](smtk::common::URL* url, const std::string& txt) { return url->setPath(txt); }, py::arg("path")) + .def("setQuery", [](smtk::common::URL* url, const std::string& txt) { return url->setQuery(txt); }, py::arg("query")) + .def("setFragment", [](smtk::common::URL* url, const std::string& txt) { return url->setFragment(txt); }, py::arg("fragment")) + ; + return instance; +} + +#endif diff --git a/smtk/common/pybind11/PybindUUID.h b/smtk/common/pybind11/PybindUUID.h index a3c0c968b71f2d7696e5aaf4ff01fc5483ec67e3..36bff5a284248746d40104070d74b4cc15cfb8ff 100644 --- a/smtk/common/pybind11/PybindUUID.h +++ b/smtk/common/pybind11/PybindUUID.h @@ -31,6 +31,8 @@ inline py::class_< smtk::common::UUID > pybind11_init_smtk_common_UUID(py::modul .def("__ne__", (bool (smtk::common::UUID::*)(::smtk::common::UUID const &) const) &smtk::common::UUID::operator!=) .def("__eq__", (bool (smtk::common::UUID::*)(::smtk::common::UUID const &) const) &smtk::common::UUID::operator==) .def("__lt__", (bool (smtk::common::UUID::*)(::smtk::common::UUID const &) const) &smtk::common::UUID::operator<) + .def("__hash__", [](const smtk::common::UUID& uid) { return uid.hash(); }) + .def("__repr__", [](const smtk::common::UUID& uid) { return "UUID('" + uid.toString() + "')"; }) .def("deepcopy", (smtk::common::UUID & (smtk::common::UUID::*)(::smtk::common::UUID const &)) &smtk::common::UUID::operator=) .def_static("random", &smtk::common::UUID::random) .def_static("null", &smtk::common::UUID::null) diff --git a/smtk/common/testing/python/CMakeLists.txt b/smtk/common/testing/python/CMakeLists.txt index 65a8ab094dccb0b4c2caf2c0ce7c85f2febfd10a..8b08400cd0de7f19001e2d10f933389590c17398 100644 --- a/smtk/common/testing/python/CMakeLists.txt +++ b/smtk/common/testing/python/CMakeLists.txt @@ -1,6 +1,7 @@ set(smtkCommonPythonTests uuidGenerator datetimezonepairtest + testURL ) if (ParaView_VERSION VERSION_GREATER_EQUAL "5.10.0") diff --git a/smtk/common/testing/python/testURL.py b/smtk/common/testing/python/testURL.py new file mode 100644 index 0000000000000000000000000000000000000000..fef0e47f20adb76e409bc680e1f54225a7e2709f --- /dev/null +++ b/smtk/common/testing/python/testURL.py @@ -0,0 +1,55 @@ +# ============================================================================= +# +# Copyright (c) Kitware, Inc. +# All rights reserved. +# See LICENSE.txt 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. +# +# ============================================================================= +import os +import sys +import unittest +import smtk +import smtk.string +import smtk.common +from smtk import common +import smtk.testing +import uuid + + +class TestURL(unittest.TestCase): + + def test(self): + blank = smtk.common.URL() + self.assertFalse(blank.valid(), 'Default-constructed URL should be invalid.') + urls = ('https://kitware.com/foo/bar', \ + 'file:///foo/bar', \ + 'file://baz/foo/bar', \ + 'http://xyzzy@baz/foo/bar?bleb', + 'http://xyzzy@baz/foo/bar?bleb#flim') + allParsed = [] + for txt in urls: + parsed = smtk.common.URL(txt) + print('"', parsed.scheme().data(), \ + '", "', parsed.authority().data(), \ + '", "', parsed.path().data(), \ + '", "', parsed.query().data(), \ + '", "', parsed.fragment().data(), \ + '"') + print(parsed.to_string(), txt == parsed.to_string()) + self.assertEqual(parsed.to_string(), txt, 'Round-tripped URL was not identical.') + allParsed.append(parsed) + self.assertEqual(len(allParsed), len(urls), 'Expected to parse every URL.') + self.assertEqual(allParsed, [smtk.common.URL(x) for x in urls], \ + 'Same test in should produce identical URLs out.') + s1 = set(allParsed) + s2 = set([smtk.common.URL(x) for x in urls]) + print(s1) + self.assertEqual(s1, s2, 'Hashing should produce identical sets of URLs.') + +if __name__ == '__main__': + smtk.testing.process_arguments() + unittest.main() diff --git a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx index ad3460849f7a76cbe892d03948e18ec7160be234..3385fb853e4b7642e36b1a110904721fecdce7c0 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx +++ b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx @@ -503,7 +503,8 @@ bool pqSMTKAttributePanel::displayResourceInternal( { // Only use the top level view if the resource is not // managed by a project - if (rsrc && (rsrc->parentResource() == nullptr)) + auto* project = dynamic_cast<smtk::project::Project*>(rsrc->parentResource()); + if (rsrc && (!project || (project && project->taskManager().taskInstances().topLevelTasks().empty()))) { theView = rsrc->findTopLevelView(); } diff --git a/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.cxx index 12d042d3ae263ad2aba1ca0fee02de2cc6e79d94..857f359ed1720ed831abad8eb73632fb84a9f4e3 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.cxx +++ b/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.cxx @@ -211,3 +211,21 @@ bool pqSMTKDiagramPanel::configure(const nlohmann::json& data) } return false; } + +void pqSMTKDiagramPanel::focusPanel() +{ + // If we are owned by a dock widget, ensure the dock is shown. + if (auto* dock = qobject_cast<QDockWidget*>(this->parent())) + { + auto* action = dock->toggleViewAction(); + if (!action->isChecked()) + { + action->trigger(); + } + } + // Raise our parent widget. + if (auto* parent = qobject_cast<QWidget*>(this->parent())) + { + parent->raise(); + } +} diff --git a/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.h b/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.h index 3f3f9115cdc6d46965e9bb6135f70cb06a9e6157..6c3e0759a622c0a4fa2db7e2679205d0737cf31f 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.h +++ b/smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.h @@ -63,6 +63,10 @@ public: Q_SIGNALS: void titleChanged(QString title); +public Q_SLOTS: + /// Bring this panel into focus (showing and raising it as required). + virtual void focusPanel(); + protected Q_SLOTS: virtual void resourceManagerAdded(pqSMTKWrapper* mgr, pqServer* server); virtual void resourceManagerRemoved(pqSMTKWrapper* mgr, pqServer* server); diff --git a/smtk/extension/paraview/project/CMakeLists.txt b/smtk/extension/paraview/project/CMakeLists.txt index 3da2593762f0dfe017173987181da5be103921dc..bf00a7d8d58233f071715574fff38f2a656e751d 100644 --- a/smtk/extension/paraview/project/CMakeLists.txt +++ b/smtk/extension/paraview/project/CMakeLists.txt @@ -4,7 +4,9 @@ set(classes pqSMTKProjectMenu pqSMTKProjectPanel pqSMTKTaskResourceVisibility + pqTaskControlView Registrar + Utility ) smtk_encode_file("${CMAKE_CURRENT_SOURCE_DIR}/ProjectPanelConfiguration.json" diff --git a/smtk/extension/paraview/project/Registrar.cxx b/smtk/extension/paraview/project/Registrar.cxx index f8e72dfca414f5f1fc5030d1f4e9f3953e900605..edb97a507f0de10279dc2b5e5f7a82dbac75ada2 100644 --- a/smtk/extension/paraview/project/Registrar.cxx +++ b/smtk/extension/paraview/project/Registrar.cxx @@ -12,6 +12,7 @@ #include "smtk/extension/paraview/project/Registrar.h" #include "smtk/extension/paraview/project/pqSMTKProjectBrowser.h" +#include "smtk/extension/paraview/project/pqTaskControlView.h" namespace smtk { @@ -21,6 +22,12 @@ namespace paraview { namespace project { + +using ViewWidgetList = std::tuple< + pqSMTKProjectBrowser, + pqTaskControlView +>; + void Registrar::registerTo(const smtk::project::Manager::Ptr& projectManager) { projectManager->registerProject("basic"); @@ -34,13 +41,14 @@ void Registrar::unregisterFrom(const smtk::project::Manager::Ptr& projectManager void Registrar::registerTo(const smtk::view::Manager::Ptr& viewManager) { (void)viewManager; - viewManager->viewWidgetFactory().registerType<pqSMTKProjectBrowser>(); + viewManager->viewWidgetFactory().registerTypes<ViewWidgetList>(); + viewManager->viewWidgetFactory().addAlias<pqTaskControlView>("TaskControl"); } void Registrar::unregisterFrom(const smtk::view::Manager::Ptr& viewManager) { (void)viewManager; - viewManager->viewWidgetFactory().unregisterType<pqSMTKProjectBrowser>(); + viewManager->viewWidgetFactory().unregisterTypes<ViewWidgetList>(); } } // namespace project } // namespace paraview diff --git a/smtk/extension/paraview/project/Utility.cxx b/smtk/extension/paraview/project/Utility.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a06c2fb12103592dc64940a0a09653d1d7f3115b --- /dev/null +++ b/smtk/extension/paraview/project/Utility.cxx @@ -0,0 +1,66 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/paraview/project/Utility.h" + +#include "smtk/extension/paraview/appcomponents/pqSMTKBehavior.h" +#include "smtk/extension/paraview/appcomponents/pqSMTKResource.h" + +#include "smtk/task/Manager.h" + +#include "pqDataRepresentation.h" + +namespace smtk +{ +namespace paraview +{ + +RepresentationObjectMap representationsOfObjects( + const smtk::task::Manager::ResourceObjectMap& resourcesToObjects) +{ + // Filter representations by the \a spec. + RepresentationObjectMap representations; + + auto* behavior = pqSMTKBehavior::instance(); + for (const auto& entry : resourcesToObjects) + { + smtk::resource::Resource* resource = entry.first; + if (!resource) { continue; } + + QPointer<pqSMTKResource> pvrsrc = behavior->getPVResource(resource->shared_from_this()); + if (!pvrsrc) + { + continue; + } + + for (const auto& view : pvrsrc->getViews()) + { + for (const auto& representation : pvrsrc->getRepresentations(view)) + { + for (const auto& object : entry.second) + { + representations[representation].insert(object); + } + } + } + } + + return representations; +} + +RepresentationObjectMap relevantRepresentations( + const nlohmann::json& spec, smtk::task::Manager* taskMgr, smtk::task::Task* task) +{ + auto objectMap = taskMgr->workflowObjects(spec, task); + auto representations = representationsOfObjects(objectMap); + return representations; +} + +} // namespace paraview +} // namespace smtk diff --git a/smtk/extension/paraview/project/Utility.h b/smtk/extension/paraview/project/Utility.h new file mode 100644 index 0000000000000000000000000000000000000000..7e87a6e740104746e5c14bb63dab2aae26925bdf --- /dev/null +++ b/smtk/extension/paraview/project/Utility.h @@ -0,0 +1,52 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef smtk_extension_paraview_project_Utility_h +#define smtk_extension_paraview_project_Utility_h + +#include "smtk/extension/paraview/project/smtkPQProjectExtModule.h" + +#include "smtk/project/Observer.h" +#include "smtk/task/Active.h" +#include "smtk/task/Manager.h" +#include "smtk/task/Task.h" + +#include "smtk/PublicPointerDefs.h" + +class pqDataRepresentation; + +namespace smtk +{ +namespace paraview +{ + +using RepresentationObjectMap = + std::map<pqDataRepresentation*, std::unordered_set<smtk::resource::PersistentObject*>>; + +/// Given a map from resources to persistent objects, return a map from +/// representations tied to the resource keys to persisten object values. +/// +/// This is intended for use by user interface code that needs to adjust the visibility of +/// components and/or resources. +RepresentationObjectMap representationsOfObjects( + const smtk::task::Manager::ResourceObjectMap& resourcesToObjects); + +/// Return a list of representations that are relevant to \a spec and \a task. +/// +/// These representations will correspond to a resources that match +/// a directive above because (a) they are part of the project owning the +/// task manager or (b) they are on input ports of the task referenced by +/// the \a spec. +RepresentationObjectMap relevantRepresentations( + const nlohmann::json& spec, smtk::task::Manager* taskMgr, smtk::task::Task* task); + +} // namespace paraview +} // namespace smtk + +#endif // smtk_extension_paraview_project_Utility_h diff --git a/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.cxx b/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.cxx index 303981911b2fe129e06174fb5291feb762d46e2e..e21b3850ee409ee6feb346bbbf43eb86a511ca29 100644 --- a/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.cxx +++ b/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.cxx @@ -36,93 +36,16 @@ #include <QTimer> +// Change "#undef" to "#define" to print messages as this behavior +// responds to changes in tasks with 3-d view style. +#undef SMTK_DBG_3D_STYLE + using namespace smtk::string::literals; namespace { -pqSMTKTaskResourceVisibility* gInstance = nullptr; - -bool resourceMatch( - const std::string& filter, - smtk::resource::PersistentObject* object, - smtk::resource::Resource*& rsrc) -{ - if (auto* resource = dynamic_cast<smtk::resource::Resource*>(object)) - { - rsrc = resource; - if (filter == "*" || rsrc->matchesType(filter)) - { - return true; - } - } - else if (auto* comp = dynamic_cast<smtk::resource::Component*>(object)) - { - if ((rsrc = comp->parentResource())) - { - if (filter == "*" || rsrc->matchesType(filter)) - { - return true; - } - } - } - return false; -} -bool componentMatch( - const std::string& filter, - smtk::resource::PersistentObject* object, - smtk::resource::Resource* rsrc) -{ - // An empty filter string indicates only resources are allowed. - if (filter.empty()) - { - return (object == rsrc); - } - // "*" allows any component: - else if (filter == "*") - { - // Assume that if object is not a pointer to the parent resource, - // it must be a component: - return (object != rsrc); - } - // We have a non-trivial filter string; we must have a component. - if (auto* comp = dynamic_cast<smtk::resource::Component*>(object)) - { - auto query = rsrc->queryOperation(filter); - return query ? query(*comp) : false; - } - return false; -} - -bool filterObject(const nlohmann::json::array_t& filter, smtk::resource::PersistentObject* object) -{ - for (const auto& filterTuple : filter) - { - try - { - if (filterTuple.is_array() && filterTuple.size() == 2) - { - smtk::resource::Resource* rsrc{ nullptr }; - if (resourceMatch(filterTuple[0].get<std::string>(), object, rsrc)) - { - auto compSpec = filterTuple[1].is_null() ? "" : filterTuple[1].get<std::string>(); - if (componentMatch(compSpec, object, rsrc)) - { - return true; - } - } - } - } - catch (nlohmann::json::exception& e) - { - smtkErrorMacro( - smtk::io::Logger::instance(), - "Cannot process filter \"" << filterTuple.dump() << "\";" << e.what()); - continue; - } - } - return false; -} +pqSMTKTaskResourceVisibility* gInstance = nullptr; } // anonymous namespace @@ -299,7 +222,6 @@ void pqSMTKTaskResourceVisibility::handleTaskEvent( { m_currentTask->observers().erase(m_currentTaskObserver); this->processTaskEvent(m_currentTask, "deactivated"_token); - std::cout << "Process \"deactivated\" event for \"" << m_currentTask->name() << "\".\n"; // self->displayResource(nullptr); } m_currentTask = nextTask; @@ -316,7 +238,7 @@ void pqSMTKTaskResourceVisibility::handleTaskEvent( auto* pqCore = pqApplicationCore::instance(); if (pqCore) { - if (auto* panel = dynamic_cast<pqSMTKDiagramPanel*>(pqCore->manager("smtk task diagram"))) + if (auto* panel = dynamic_cast<pqSMTKDiagramPanel*>(pqCore->manager("smtk task panel"))) { for (const auto& generatorEntry : panel->diagram()->generators()) { @@ -331,8 +253,10 @@ void pqSMTKTaskResourceVisibility::handleTaskEvent( } } } +#ifdef SMTK_DBG_3D_STYLE std::cout << "Process \"activated\" event for \"" << (m_currentTask ? m_currentTask->name() : "(default)") << "\".\n"; +#endif this->processTaskEvent(m_currentTask, "activated"_token); } @@ -396,7 +320,7 @@ void pqSMTKTaskResourceVisibility::applyColorBy( << spec.dump(2) << "\n"); return; } - auto representations = this->relevantRepresentations(spec); + auto representations = smtk::paraview::relevantRepresentations(spec, m_currentTaskManager, m_currentTask); auto mode(spec.at("mode").get<smtk::string::Token>()); bool needsRender = false; for (const auto& entry : representations) @@ -461,7 +385,7 @@ void pqSMTKTaskResourceVisibility::applyShowObjects( << spec.dump(2) << "\n"); return; } - auto representations = this->relevantRepresentations(spec); + auto representations = smtk::paraview::relevantRepresentations(spec, m_currentTaskManager, m_currentTask); for (const auto& entry : representations) { auto* representation = entry.first; @@ -479,171 +403,3 @@ void pqSMTKTaskResourceVisibility::applyShowObjects( pqActiveObjects::instance().activeView()->render(); } } - -std::map<pqRepresentation*, std::unordered_set<smtk::resource::PersistentObject*>> -pqSMTKTaskResourceVisibility::relevantRepresentations(const nlohmann::json& spec) -{ - // Filter representations by the \a spec. - std::map<pqRepresentation*, std::unordered_set<smtk::resource::PersistentObject*>> - representations; - - nlohmann::json source; - nlohmann::json filter; - if (spec.contains("source")) - { - source = spec.at("source"); - if (!source.is_object() || !source.contains("type")) - { - smtkErrorMacro( - smtk::io::Logger::instance(), - "Spec source must be a dictionary with 'type' key, " - "got \"" - << source.dump() << "\""); - return representations; - } - } - else - { - source = { { "type", "project resources" } }; - } - - if (spec.contains("filter")) - { - filter = spec.at("filter"); - if (!filter.is_array()) - { - smtkErrorMacro( - smtk::io::Logger::instance(), - "Spec filter must be an array, got \"" << filter.dump() << "\"."); - return representations; - } - } - else - { - // Accept any resource or component: - filter = nlohmann::json::array_t({ { "*", "*" }, { "*", nullptr } }); - } - - auto* behavior = pqSMTKBehavior::instance(); - auto sourceType = source["type"].get<smtk::string::Token>(); - std::unordered_set<smtk::resource::PersistentObject*> objects; - switch (sourceType.id()) - { - default: - smtkErrorMacro( - smtk::io::Logger::instance(), "Unknown source type \"" << sourceType.data() << "\"."); - // fall through - case "project resources"_hash: - { - if (!m_currentTaskManager) - { - return representations; - } - - auto* project = dynamic_cast<smtk::project::Project*>(m_currentTaskManager->resource()); - if (!project) - { - return representations; - } - - for (const auto& resource : project->resources()) - { - if (filterObject(filter, resource.get())) - { - objects.insert(resource.get()); - } - } - } - break; - case "active task port"_hash: - { - if (!m_currentTask || !source.contains("port")) - { - smtkErrorMacro(smtk::io::Logger::instance(), "No active task or no \"port\" specified."); - return representations; - } - - // We are asked to find objects on a port of the (now) active task. - // Determine whether we are filtering on role or not. - smtk::string::Token sourceRole; - if (source.contains("role")) - { - sourceRole = source.at("role").get<smtk::string::Token>(); - } - // Find the correct port: - const auto& taskPortMap = m_currentTask->ports(); - auto it = taskPortMap.find(source["port"]); - if (it == taskPortMap.end()) - { - return representations; - } - // Fetch data from the port. We only understand ObjectsInRoles at this point: - auto portData = it->second->parent()->portData(it->second); - if (portData) - { - if (auto objectsInRoles = std::dynamic_pointer_cast<smtk::task::ObjectsInRoles>(portData)) - { - for (const auto& entry : objectsInRoles->data()) - { - if (sourceRole.valid() && entry.first != sourceRole) - { // Skip objects in unrequested roles. - continue; - } - // Filter objects as requested. - for (const auto& object : entry.second) - { - if (filterObject(filter, object)) - { - objects.insert(object); - } - } - } - } - else - { - smtkErrorMacro( - smtk::io::Logger::instance(), - "Unhandled port data type \"" << portData->typeName() << "\"."); - } - } - } - break; - } - - for (const auto& object : objects) - { - smtk::resource::Resource* resource{ nullptr }; - QPointer<pqSMTKResource> pvrsrc; - if ((resource = dynamic_cast<smtk::resource::Resource*>(object))) - { - pvrsrc = behavior->getPVResource(resource->shared_from_this()); - } - else if (auto* comp = dynamic_cast<smtk::resource::Component*>(object)) - { - resource = comp->resource().get(); - pvrsrc = behavior->getPVResource(comp->resource()); - } - if (!pvrsrc || !this->isResourceRelevant(resource->shared_from_this(), filter)) - { - continue; - } - - for (const auto& view : pvrsrc->getViews()) - { - for (const auto& representation : pvrsrc->getRepresentations(view)) - { - representations[representation].insert(object); - } - } - } - - return representations; -} - -bool pqSMTKTaskResourceVisibility::isResourceRelevant( - const std::shared_ptr<smtk::resource::Resource>& resource, - const nlohmann::json& filter) -{ - smtk::resource::Resource* rsrc = resource.get(); - return filterObject(filter, rsrc); -} diff --git a/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.h b/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.h index 11ebb9456b055ee6a96ca81fba7a7726c0b1a803..cae7e9a81f5954f486e42f6baf212580f957bcc0 100644 --- a/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.h +++ b/smtk/extension/paraview/project/pqSMTKTaskResourceVisibility.h @@ -10,7 +10,7 @@ #ifndef smtk_extension_paraview_appcomponents_pqSMTKTaskResourceVisibility_h #define smtk_extension_paraview_appcomponents_pqSMTKTaskResourceVisibility_h -#include "smtk/extension/paraview/project/smtkPQProjectExtModule.h" +#include "smtk/extension/paraview/project/Utility.h" #include "smtk/project/Observer.h" #include "smtk/task/Active.h" @@ -55,10 +55,10 @@ class pqServer; * + "port" (with the name of a port on the active task) if the type is "active task port". * + "roles" (with the name of a role for data on the port) if the type is "active task port". * - * The "filter" must be an dictionary holding: + * The "filter" must be an array holding: * - * + "resources", an optional array of accepted resource type-names or "*". If not present, "*" is assumed. - * + "components", an optional array of accepted component-filters or "*". If not present, only + * + dictionaries with a "resource" and "component" key or + * + arrays holding two strings (a resource and component filter, in that order). * * When a task is deactivated and no new task is activated at the same time, * (1) if the task-path is empty (i.e., the top-level tasks are showing), then the default @@ -102,7 +102,8 @@ class pqServer; * + hide markup resources present on the "input" port of the active task (without toggling * per-component visibility) when a task with the "example" style is deactivated; and * + show both resources and components (toggling as needed) on the active task's "output" port - * when any task with the "example" style is deactivated. + * when any task with the "example" style is deactivated. (This way, as long as the task is + * active, its input port data is visible; when deactivated, its output port data is visible.) */ class SMTKPQPROJECTEXT_EXPORT pqSMTKTaskResourceVisibility : public QObject { @@ -136,20 +137,6 @@ protected: // NOLINT(readability-redundant-access-specifiers) smtk::task::Task* task, smtk::string::Token event); - /// Return a list of representations that are relevant to \a spec. - /// - /// These representations will correspond to a resources that match - /// a directive above because (a) they are part of the project owning the - /// task manager or (b) they are on input ports of the task referenced by - /// the \a spec. - std::map<pqRepresentation*, std::unordered_set<smtk::resource::PersistentObject*>> - relevantRepresentations(const nlohmann::json& spec); - - /// Is the given resource relevant to the \a spec? - bool isResourceRelevant( - const std::shared_ptr<smtk::resource::Resource>& resource, - const nlohmann::json& filter); - std::map<smtk::project::ManagerPtr, smtk::project::Observers::Key> m_projectManagerObservers; smtk::task::Task* m_currentTask{ nullptr }; smtk::task::Manager* m_currentTaskManager{ nullptr }; diff --git a/smtk/extension/paraview/project/pqTaskControlView.cxx b/smtk/extension/paraview/project/pqTaskControlView.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3a61e41468a0a7bf5a9d028ddbad14d698616104 --- /dev/null +++ b/smtk/extension/paraview/project/pqTaskControlView.cxx @@ -0,0 +1,690 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/paraview/project/pqTaskControlView.h" + +#include "smtk/extension/paraview/appcomponents/pqSMTKDiagramPanel.h" +#include "smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.h" +#include "smtk/extension/paraview/appcomponents/pqSMTKResource.h" +#include "smtk/extension/paraview/project/Utility.h" + +#include "smtk/extension/qt/qtUIManager.h" + +#include "smtk/view/Configuration.h" +#include "smtk/view/Manager.h" + +#include "smtk/operation/Manager.h" +#include "smtk/project/Manager.h" +#include "smtk/task/Manager.h" +#include "smtk/task/Active.h" + +#include "smtk/common/Managers.h" + +#include "smtk/io/Logger.h" + +#include "pqApplicationCore.h" +#include "pqSMAdaptor.h" +#include "pqView.h" + +#include "vtkSMProperty.h" +#include "vtkSMPropertyHelper.h" +#include "vtkSMProxy.h" + +#include <QFile> +#include <QSlider> +#include <QFont> +#include <QFormLayout> +#include <QFrame> +#include <QHBoxLayout> +#include <QLabel> +#include <QScrollArea> +#include <QSize> +#include <QTabWidget> +#include <QPushButton> +#include <QVBoxLayout> +#include <QTimer> +#include <QVariant> + +using namespace smtk::paraview; +using namespace smtk::string::literals; + +namespace smtk +{ +namespace extension +{ +namespace // anonymous +{ + +bool representationObjectMapContainsResource(const RepresentationObjectMap::value_type& entry) +{ + auto pvDRep = dynamic_cast<pqDataRepresentation*>(entry.first); + auto pvRsrc = pvDRep ? dynamic_cast<pqSMTKResource*>(pvDRep->getInput()) : nullptr; + if (!pvRsrc) { return false; } + auto it = entry.second.find(pvRsrc->getResource().get()); + return (it != entry.second.end() || entry.second.find(nullptr) != entry.second.end()); +} + +} // anonymous namespace + +/// Private storage for a task-control views. +class pqTaskControlView::Internal +{ +public: + Internal(pqTaskControlView* self) + : m_view(self) + {} + + /// Add a widget for a \a childSpec. + /// + /// If \a layout is non-null and appropriate for the \a childSpec, + /// the current \a layout and \a widget are used. Otherwise, a new + /// layout is created. + bool addChildItem( + const smtk::view::Configuration::Component& childSpec, + QLayout*& layout, + QWidget*& parent, + int& childNum, + smtk::task::Task* task); + + /// Called when the active task changes. + void activeTaskChange(smtk::task::Task* prev, smtk::task::Task* next) + { + (void)prev; + (void)next; + m_view->updateWithActiveTask(next); + } + + void addTaskDescription( + QLayout* layout, smtk::task::Task* task, const smtk::view::Configuration::Component& spec) + { + (void)spec; // Currently no options to look up. + auto* description = new QLabel(); + smtk::task::Task::InformationOptions opt; + description->setObjectName("taskDescription"); + description->setText(QString::fromStdString(task ? task->information(opt) : "")); + description->setWordWrap(true); // Allow line breaks so panel is not forced to be crazy wide. + layout->addWidget(description); + ++this->numChildren; + } + + void addTaskStatus( + QFormLayout* formLayout, smtk::task::Task* task, const smtk::view::Configuration::Component& spec) + { + auto* label = new QLabel(); + label->setObjectName("taskName"); + auto txt = QString::fromStdString(task ? task->name() : ""); + if (spec.attributeAsBool("ReturnToDiagram")) + { + txt += QString::fromStdString("<a href=\"#returnToDiagram\">⤴</a>"); + } + label->setText(txt); + label->setOpenExternalLinks(false); + QObject::connect(label, &QLabel::linkActivated, m_view, &pqTaskControlView::returnToDiagram); + auto* ctrl = new QPushButton(); + ctrl->setObjectName("taskCompleter"); + ctrl->setText( + task ? (task->isCompleted() ? "Completed" : "Complete") : "Complete"); + ctrl->setCheckable(true); + ctrl->setChecked(task ? task->isCompleted() : false); + ctrl->setEnabled(task ? task->state() >= smtk::task::State::Completable : false); + QObject::connect(ctrl, &QAbstractButton::toggled, m_view, &pqTaskControlView::updateTaskCompletion); + if (task) + { + m_taskStateObserver = task->observers().insert( + [ctrl, this](smtk::task::Task& t, smtk::task::State prev, smtk::task::State next) + { + (void)prev; + ctrl->setChecked(t.isCompleted()); + ctrl->setEnabled(next >= smtk::task::State::Completable); + } + ); + } + formLayout->addRow(label, ctrl); + ++this->numChildren; + } + + void addRepresentationControl( + QFormLayout* formLayout, + smtk::task::Task* task, + const smtk::view::Configuration::Component& spec) + { + (void)task; + auto* field = new QHBoxLayout; + std::string name; + if (!spec.attribute("Name", name)) + { + smtkWarningMacro(smtk::io::Logger::instance(), "RepresentationControl without name."); + name = "Unknown"; + } + std::string label; + if (!spec.attribute("Label", label)) + { + smtkWarningMacro(smtk::io::Logger::instance(), "RepresentationControl without label."); + label = name; + } + for (const auto& controlSpec : spec.children()) + { + if (controlSpec.name() == "Control") + { + if (controlSpec.attributeAsString("Type") == "Visibility") + { + auto* ctrl = new QPushButton; + ctrl->setObjectName("visibility"); + ctrl->setText("Visibility"); + ctrl->setCheckable(true); + ctrl->setToolTip("Toggle visibility"); + bool initiallyVisible{ true }; + controlSpec.attributeAsBool("InitiallyVisible", initiallyVisible); + ctrl->setChecked(initiallyVisible); + m_representationSpecs[name] = spec; + QObject::connect( + ctrl, &QPushButton::toggled, [this, name](bool toggled) { m_view->toggleVisibility(name, toggled); }); + field->addWidget(ctrl); + // Now force state to match button upon view construction. + m_view->toggleVisibility(name, initiallyVisible); + } + else if (controlSpec.attributeAsString("Type") == "Opacity") + { + auto* ctrl = new QSlider; + ctrl->setObjectName("opacity"); + ctrl->setMinimum(0); + ctrl->setMaximum(255); + ctrl->setPageStep(16); + ctrl->setOrientation(Qt::Horizontal); + int initialValue{ 255 }; + controlSpec.attributeAsInt("InitialValue", initialValue); + ctrl->setValue(initialValue); + ctrl->setToolTip("Adjust opacity"); + m_representationSpecs[name] = spec; + QObject::connect( + ctrl, &QSlider::valueChanged, [this, name](int value) { m_view->updateOpacity(name, value / 255.0); }); + field->addWidget(ctrl); + // Now force state to match slider upon view construction. + m_view->updateOpacity(name, initialValue / 255.); + } + } + } + formLayout->addRow(QString::fromStdString(label), field); + ++this->numChildren; + } + + void addOperationControl( + QLayout* layout, + smtk::task::Task* task, + const smtk::view::Configuration::Component& spec) + { + (void)layout; + (void)task; + (void)spec; + std::string opType; + if (!spec.attribute("Type", opType) || opType.empty()) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "Operation type must be specified."); + return; + } + std::string opLabel; + spec.attribute("Label", opLabel); + if (opLabel.empty()) + { + opLabel = "Run " + opType; + } + auto taskMgr = task->manager(); + auto mgrs = taskMgr->managers(); + auto opMgr = mgrs ? mgrs->get<smtk::operation::Manager::Ptr>() : nullptr; + auto op = opMgr ? opMgr->create(opType) : nullptr; + if (!op) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "Could not create \"" << opType << "\" from mgr " << opMgr << "."); + return; + } + auto opButton = new QPushButton; + opButton->setText(QString::fromStdString(opLabel)); + QObject::connect(opButton, &QAbstractButton::clicked, [task, opMgr, op, spec](bool clicked) + { + (void)clicked; + for (const auto& paramSpec : spec.children()) + { + if (paramSpec.name() == "Association") + { + if (!op->parameters()->associations()) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "No associations can be specified for operation."); + continue; + } + op->parameters()->associations()->reset(); + auto data = task->manager()->workflowObjects(paramSpec, task); + for (const auto& entry : data) + { + for (const auto& obj : entry.second) + { + if (obj) + { + op->parameters()->associations()->appendValue(obj->shared_from_this()); + } + else + { + op->parameters()->associations()->appendValue(entry.first->shared_from_this()); + } + } + } + } + else if (paramSpec.name() == "Reference") + { + std::string refPath; + smtk::attribute::ReferenceItem::Ptr refItem; + if (!paramSpec.attribute("Path", refPath) || !(refItem = op->parameters()->itemAtPathAs<smtk::attribute::ReferenceItem>(refPath))) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "No reference item at path '" << refPath << "'."); + continue; + } + refItem->reset(); + auto data = task->manager()->workflowObjects(paramSpec, task); + for (const auto& entry : data) + { + for (const auto& obj : entry.second) + { + if (obj) + { + refItem->appendValue(obj->shared_from_this()); + } + else + { + refItem->appendValue(entry.first->shared_from_this()); + } + } + } + } + else + { + smtkErrorMacro(smtk::io::Logger::instance(), + "Unhandled element '" << paramSpec.name() << "'."); + } + } + opMgr->launchers()(op); + }); + + if (auto* formLayout = dynamic_cast<QFormLayout*>(layout)) + { + formLayout->addRow(QString::fromStdString(opLabel), opButton); + } + else + { + layout->addWidget(opButton); + } + ++this->numChildren; + } + + void addFormGroup( + QFormLayout* formLayout, + smtk::task::Task* task, + const smtk::view::Configuration::Component& spec, + QWidget*& parent, + int& childNum) + { + // Create an internal HBoxLayout and add children (presumed + // to be non-form controls) to that. Then add the HBox plus + // the form-title to \a formLayout. + QLayout* hbox = new QHBoxLayout; + for (const auto& formChild : spec.children()) + { + this->addChildItem(formChild, hbox, parent, childNum, task); + } + std::string formLabel; + if (!spec.attribute("Title", formLabel)) + { + formLabel = " "; + } + formLayout->addRow(QString::fromStdString(formLabel), hbox); + } + + pqTaskControlView* m_view{ nullptr }; + smtk::task::Manager* m_currentTaskManager{ nullptr }; + smtk::task::Task* m_currentTask{ nullptr }; + smtk::project::Observers::Key m_projectObserverKey; + smtk::task::Active::Observers::Key m_activeTaskObserverKey; + std::unordered_map<smtk::string::Token, smtk::view::Configuration::Component> m_representationSpecs; + int numChildren{ 0 }; + smtk::task::Task::Observers::Key m_taskStateObserver; +}; + +bool pqTaskControlView::Internal::addChildItem( + const smtk::view::Configuration::Component& childSpec, + QLayout*& layout, + QWidget*& parent, + int& childNum, + smtk::task::Task* task) +{ + bool needLayout = !layout; + smtk::string::Token childType = childSpec.name(); + static std::unordered_set<smtk::string::Token> formItems{ + "ActiveTaskStatus"_token, "RepresentationControl"_token, "FormGroup"_token + }; + + bool isFormItem = (formItems.find(childType) != formItems.end()); + auto* formLayout = dynamic_cast<QFormLayout*>(layout); + needLayout |= (isFormItem && !formLayout) || (!isFormItem && formLayout); + if (needLayout) + { + parent = new QWidget(); + std::ostringstream pname; + pname << "TaskControlWidget" << childNum++; + parent->setObjectName(pname.str().c_str()); + if (isFormItem) + { + formLayout = new QFormLayout(parent); + layout = formLayout; + } + else + { + layout = new QVBoxLayout(parent); + } + m_view->widget()->layout()->addWidget(parent); + } + switch (childType.id()) + { + case "ActiveTaskDescription"_hash: + this->addTaskDescription(layout, task, childSpec); + break; + case "ActiveTaskStatus"_hash: + this->addTaskStatus(formLayout, task, childSpec); + break; + case "RepresentationControl"_hash: + this->addRepresentationControl(formLayout, task, childSpec); + break; + case "OperationControl"_hash: + this->addOperationControl(layout, task, childSpec); + break; + case "FormGroup"_hash: + this->addFormGroup(formLayout, task, childSpec, parent, childNum); + break; + default: + return false; + break; + } + return true; +} + +qtBaseView* pqTaskControlView::createViewWidget(const smtk::view::Information& info) +{ + if (!qtBaseView::validateInformation(info)) + { + return nullptr; // \a info is not suitable for this View + } + auto* view = new pqTaskControlView(info); + view->buildUI(); + return view; +} + +pqTaskControlView::pqTaskControlView(const smtk::view::Information& info) + : qtBaseView(info) +{ + m_p = new Internal(this); + // auto viewConfig = this->configuration(); + // if (viewConfig) + // { + // } +} + +pqTaskControlView::~pqTaskControlView() +{ + delete m_p; +} + +bool pqTaskControlView::isEmpty() const +{ + return m_p->numChildren == 0; +} + +bool pqTaskControlView::isValid() const +{ + // This view is always valid for now. + return true; +} + +void pqTaskControlView::updateUI() +{ + auto projMgr = this->uiManager()->managers().get<smtk::project::Manager::Ptr>(); + auto project = projMgr ? *projMgr->projects().begin() : nullptr; + auto* taskMgr = project ? &project->taskManager() : nullptr; + auto* activeTask = taskMgr ? taskMgr->active().task() : nullptr; + this->updateWithActiveTask(activeTask); +} + +void pqTaskControlView::showAdvanceLevelOverlay(bool show) +{ + this->qtBaseView::showAdvanceLevelOverlay(show); +} + +void pqTaskControlView::onShowCategory() +{ + this->Superclass::onShowCategory(); +} + +void pqTaskControlView::returnToDiagram() +{ + auto* pqCore = pqApplicationCore::instance(); + if (!pqCore) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "Cannot return to diagram panel as there is no application core."); + return; + } + auto* panel = dynamic_cast<pqSMTKDiagramPanel*>(pqCore->manager("smtk task panel")); + if (!panel) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "Cannot return to diagram panel as there is no diagram panel."); + return; + } + panel->focusPanel(); +} + +void pqTaskControlView::updateTaskCompletion(bool completed) +{ + auto projMgr = this->uiManager()->managers().get<smtk::project::Manager::Ptr>(); + auto project = projMgr ? *projMgr->projects().begin() : nullptr; + auto* taskMgr = project ? &project->taskManager() : nullptr; + if (taskMgr) + { + auto* task = taskMgr->active().task(); + if (task && task->state() >= smtk::task::State::Completable) + { + QTimer::singleShot(0, [this, task, completed]() + { + if (task->markCompleted(completed) && completed) + { + this->returnToDiagram(); + } + } + ); + } + } +} + +void pqTaskControlView::toggleVisibility(const std::string& name, bool show) +{ + if (m_p->m_currentTaskManager) + { + auto objMap = m_p->m_currentTaskManager->workflowObjects(m_p->m_representationSpecs[name], m_p->m_currentTask); + auto repMap = smtk::paraview::representationsOfObjects(objMap); + for (const auto& entry : repMap) + { + bool needRender = false; + // Toggle the whole representation's visibility if either + // (1) entry.second contains a null pointer (indicating the resource itself + // is supposed to have its visibility changed) or + // (2) \a show is set to true (indicating that some components that were + // hidden should now be shown – which cannot happen without the + // resource-representation visibility being set to true). + if (show || representationObjectMapContainsResource(entry)) + { + if (entry.first->isVisible() != show) + { + entry.first->setVisible(show); + needRender = true; + } + } + auto pvDRep = dynamic_cast<pqSMTKResourceRepresentation*>(entry.first); + if (!pvDRep) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "Could not downcast representation " << entry.first << " to an SMTK resource representation."); + continue; + } + // Now, tell the representation to toggle individual component visibilities. + for (const auto& obj : entry.second) + { + if (!obj) { continue; } + if (auto comp = dynamic_cast<smtk::resource::Component*>(obj)->shared_from_this()) + { + needRender |= pvDRep->setVisibility(comp, show); + } + } + if (needRender) + { + entry.first->getView()->render(); + } + } + } +} + +void pqTaskControlView::updateOpacity(const std::string& name, double opacity) +{ + if (m_p->m_currentTaskManager) + { + auto objMap = m_p->m_currentTaskManager->workflowObjects(m_p->m_representationSpecs[name], m_p->m_currentTask); + auto repMap = smtk::paraview::representationsOfObjects(objMap); + for (const auto& entry : repMap) + { + bool needRender = false; + // Toggle the whole representation's visibility if either + // (1) entry.second contains a null pointer (indicating the resource itself + // is supposed to have its visibility changed) or + // (2) \a show is set to true (indicating that some components that were + // hidden should now be shown – which cannot happen without the + // resource-representation visibility being set to true). + if (representationObjectMapContainsResource(entry)) + { + pqSMAdaptor::setElementProperty(entry.first->getProxy()->GetProperty("Opacity"), opacity); + entry.first->getProxy()->UpdateVTKObjects(); + needRender = true; + } + // TODO: Handle per-block opacity settings for components. + // Iterate over entry.second, find component inside the + // pqSMTKRepresentation and set per-block opacity. + if (needRender) + { + entry.first->getView()->render(); + } + } + } +} + +void pqTaskControlView::buildUI() +{ + this->createWidget(); + + QVBoxLayout* parentlayout = static_cast<QVBoxLayout*>(this->parentWidget()->layout()); + if (!parentlayout) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "No parent layout for task control view."); + return; + } + + if (!this->isTopLevel()) + { + parentlayout->setAlignment(Qt::AlignTop); + parentlayout->addWidget(this->Widget); + return; + } + // XXX TODO. +} + +void pqTaskControlView::createWidget() +{ + smtk::view::ConfigurationPtr view = this->configuration(); + if (!view) + { + return; + } + // If we have a pre-existing widget, destroy it as we are about to recreate it. + QVBoxLayout* parentlayout = static_cast<QVBoxLayout*>(this->parentWidget()->layout()); + if (this->Widget) + { + if (parentlayout) + { + parentlayout->removeWidget(this->Widget); + } + delete this->Widget; + } + + this->Widget = new QFrame(this->parentWidget()); + this->Widget->setObjectName(view->name().c_str()); + + // Create the layout for the frame + QVBoxLayout* layout = new QVBoxLayout(this->Widget); + layout->setMargin(0); + this->Widget->setLayout(layout); + + // Start observing changes in the active task: + auto projMgr = this->uiManager()->managers().get<smtk::project::Manager::Ptr>(); + auto project = projMgr ? *projMgr->projects().begin() : nullptr; + auto* taskMgr = project ? &project->taskManager() : nullptr; + if (taskMgr) + { + m_p->m_activeTaskObserverKey = taskMgr->active().observers().insert( + [&](smtk::task::Task* prev, smtk::task::Task* next) { m_p->activeTaskChange(prev, next); }, + 0, false, "TaskControlView"); + } + else + { + m_p->m_activeTaskObserverKey.release(); + } + + this->updateUI(); +} + +void pqTaskControlView::updateWithActiveTask(smtk::task::Task* task) +{ + m_p->m_currentTaskManager = task ? task->manager() : nullptr; + m_p->m_currentTask = task; + + smtk::view::ConfigurationPtr view = this->configuration(); + if (!view) + { + return; + } + // Remove all previous children (if any). + if (this->widget()) + { + for (auto* child : this->widget()->findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly)) + { + delete child; + } + } + + // Now process all of this view's entries + QLayout* activeLayout = nullptr; + QWidget* activeWidget = nullptr; + int childNum = 0; + for (const auto& child : view->details().children()) + { + m_p->addChildItem(child, activeLayout, activeWidget, childNum, task); + } +} + + +} // namespace extension +} // namespace smtk diff --git a/smtk/extension/paraview/project/pqTaskControlView.h b/smtk/extension/paraview/project/pqTaskControlView.h new file mode 100644 index 0000000000000000000000000000000000000000..64463f7e1d5a852bffc6e85123998610d64fe19b --- /dev/null +++ b/smtk/extension/paraview/project/pqTaskControlView.h @@ -0,0 +1,71 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef smtk_extension_pqTaskControlView_h +#define smtk_extension_pqTaskControlView_h + +#include "smtk/extension/paraview/project/smtkPQProjectExtModule.h" // For export. +#include "smtk/extension/qt/qtBaseView.h" +#include "smtk/view/Configuration.h" // For API. + +namespace smtk +{ +namespace extension +{ + +/**\brief A view for task-level controls. + * + * Views of this type may contain any number of widgets for manipulating + * the currently-active task's completion status, port data, etc. + * The view may also be configured to show diagnostic/summary information + * about the active task. + */ +class SMTKPQPROJECTEXT_EXPORT pqTaskControlView : public qtBaseView +{ + Q_OBJECT + +public: + smtkTypenameMacro(smtk::extension::pqTaskControlView); + smtkSuperclassMacro(smtk::extension::qtBaseView); + + static qtBaseView* createViewWidget(const smtk::view::Information& info); + pqTaskControlView(const smtk::view::Information& info); + ~pqTaskControlView() override; + + /// Returns true if the view does not contain any information to display. + bool isEmpty() const override; + + /// Returns false when users must use the view to adjust the workflow. + /// + /// Currently this always returns true, even when the active task is incomplete. + bool isValid() const override; + +public Q_SLOTS: + void updateUI() override; + void showAdvanceLevelOverlay(bool show) override; + void onShowCategory() override; + void returnToDiagram(); + void updateTaskCompletion(bool completed); + void toggleVisibility(const std::string& name, bool show); + void updateOpacity(const std::string& name, double opacity); + +protected: + void buildUI() override; + void createWidget() override; + void updateWithActiveTask(smtk::task::Task* task); + +private: + class Internal; + Internal* m_p; +}; + +} // namespace extension +} // namespace smtk + +#endif diff --git a/smtk/extension/paraview/server/smconfig.xml b/smtk/extension/paraview/server/smconfig.xml index 7a15c29fec533065c4db844701a4ac3bf0c01137..597a266e5a2169ba8f5f5ba44a76c61b3bbe461f 100644 --- a/smtk/extension/paraview/server/smconfig.xml +++ b/smtk/extension/paraview/server/smconfig.xml @@ -1171,6 +1171,63 @@ <BooleanDomain name="bool"/> </IntVectorProperty> </Proxy> + + <Proxy + class="vtkDiskRepresentation" + name="DiskRepresentation"> + <IntVectorProperty + animateable="1" + command="SetVisibility" + default_values="0" + name="Visibility" + number_of_elements="1"> + <BooleanDomain name="bool" /> + </IntVectorProperty> + <DoubleVectorProperty + argument_is_array="1" + command="SetCenterPoint" + default_values="0 0 0" + information_property="CenterPointInfo" + name="CenterPoint" + number_of_elements="3"> + <DoubleRangeDomain name="range" /> + </DoubleVectorProperty> + <DoubleVectorProperty + command="GetCenterPoint" + information_only="1" + name="CenterPointInfo"> + <SimpleDoubleInformationHelper /> + </DoubleVectorProperty> + <DoubleVectorProperty + argument_is_array="1" + command="SetNormal" + default_values="0 0 0" + information_property="NormalInfo" + name="Normal" + number_of_elements="3"> + <DoubleRangeDomain name="range" /> + </DoubleVectorProperty> + <DoubleVectorProperty + command="GetNormalVector" + information_only="1" + name="NormalInfo"> + <SimpleDoubleInformationHelper /> + </DoubleVectorProperty> + <DoubleVectorProperty + command="SetRadius" + default_values="1" + information_property="RadiusInfo" + name="Radius" + number_of_elements="1"> + <DoubleRangeDomain name="range" /> + </DoubleVectorProperty> + <DoubleVectorProperty + command="GetRadius" + information_only="1" + name="RadiusInfo"> + <SimpleDoubleInformationHelper /> + </DoubleVectorProperty> + </Proxy> </ProxyGroup> <!-- ====================================================================== --> @@ -1183,6 +1240,13 @@ name="ConeWidget"> </Proxy> + <Proxy + base_proxygroup="3d_widgets" + base_proxyname="WidgetBase" + class="vtkDiskWidget" + name="DiskWidget"> + </Proxy> + </ProxyGroup> <!-- ====================================================================== --> @@ -1228,6 +1292,38 @@ </PropertyGroup> </Proxy> + <Proxy + name="ImplicitDisk" + class="vtkImplicitDisk" + > + <DoubleVectorProperty + name="CenterPoint" + command="SetCenterPoint" + default_values="0 0 0" + number_of_elements="3"> + <DoubleRangeDomain name="range"/> + </DoubleVectorProperty> + <DoubleVectorProperty + name="Radius" + command="SetRadius" + default_values="0.5" + number_of_elements="1"> + <DoubleRangeDomain name="range" min="0.0"/> + </DoubleVectorProperty> + <DoubleVectorProperty + name="Normal" + command="SetNormal" + default_values="0 0 1" + number_of_elements="3"> + <DoubleRangeDomain name="range"/> + </DoubleVectorProperty> + <PropertyGroup label="Disk Parameters" panel_widget="InteractiveDisk"> + <Property function="CenterPoint" name="CenterPoint" /> + <Property function="Radius" name="Radius" /> + <Property function="Normal" name="Normal" /> + </PropertyGroup> + </Proxy> + </ProxyGroup> <!-- ====================================================================== --> @@ -1975,6 +2071,41 @@ </SubProxy> </NewWidgetRepresentationProxy> + <!-- Disk widget --> + <NewWidgetRepresentationProxy + class="vtk3DWidgetRepresentation" + name="DiskWidgetRepresentation"> + <IntVectorProperty + command="SetEnabled" + default_values="0" + name="Enabled" + number_of_elements="1"> + <BooleanDomain name="bool" /> + <Documentation>Enable/Disable widget interaction.</Documentation> + </IntVectorProperty> + <SubProxy> + <Proxy + name="Prop" + proxygroup="3d_widget_representations" + proxyname="DiskRepresentation"></Proxy> + <ExposedProperties> + <Property name="Visibility" /> + <Property name="CenterPointInfo" /> + <Property name="CenterPoint" /> + <Property name="NormalInfo" /> + <Property name="Normal" /> + <Property name="RadiusInfo" /> + <Property name="Radius" /> + </ExposedProperties> + </SubProxy> + <SubProxy> + <Proxy + name="Widget" + proxygroup="3d_widgets" + proxyname="DiskWidget"></Proxy> + </SubProxy> + </NewWidgetRepresentationProxy> + </ProxyGroup> <!-- Expose user preferences --> diff --git a/smtk/extension/paraview/widgets/CMakeLists.txt b/smtk/extension/paraview/widgets/CMakeLists.txt index d1bfb909d651f1de2726da1a0de808f636e35275..24a19f19c232caa165f5261a9d67f4bdbc81cefa 100644 --- a/smtk/extension/paraview/widgets/CMakeLists.txt +++ b/smtk/extension/paraview/widgets/CMakeLists.txt @@ -8,11 +8,13 @@ set(headers set(classes Registrar pqConePropertyWidget + pqDiskPropertyWidget pqPointPropertyWidget pqSMTKAttributeItemWidget pqSMTKBoxItemWidget pqSMTKTransformWidget pqSMTKConeItemWidget + pqSMTKDiskItemWidget pqSMTKInfiniteCylinderItemWidget pqSMTKLineItemWidget pqSMTKPointItemWidget @@ -23,6 +25,7 @@ set(classes set(ui_files resources/pqConePropertyWidget.ui + resources/pqDiskPropertyWidget.ui resources/pqPointPropertyWidget.ui ) diff --git a/smtk/extension/paraview/widgets/plugin/pqSMTKWidgetsAutoStart.cxx b/smtk/extension/paraview/widgets/plugin/pqSMTKWidgetsAutoStart.cxx index 07621f35703be7325d9b5fa35ef8f8c7c51bf722..60f2158b9a00db31a60951b8e12aef137540e92f 100644 --- a/smtk/extension/paraview/widgets/plugin/pqSMTKWidgetsAutoStart.cxx +++ b/smtk/extension/paraview/widgets/plugin/pqSMTKWidgetsAutoStart.cxx @@ -13,6 +13,7 @@ #include "smtk/extension/paraview/widgets/pqSMTKBoxItemWidget.h" #include "smtk/extension/paraview/widgets/pqSMTKConeItemWidget.h" +#include "smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.h" #include "smtk/extension/paraview/widgets/pqSMTKCoordinateFrameItemWidget.h" #include "smtk/extension/paraview/widgets/pqSMTKInfiniteCylinderItemWidget.h" #include "smtk/extension/paraview/widgets/pqSMTKLineItemWidget.h" @@ -46,24 +47,21 @@ void pqSMTKWidgetsAutoStart::startup() */ // Register qtItem widget subclasses implemented using ParaView 3-D widgets: + // clang-format off qtSMTKUtilities::registerItemConstructor("Box", pqSMTKBoxItemWidget::createBoxItemWidget); qtSMTKUtilities::registerItemConstructor("Cone", pqSMTKConeItemWidget::createConeItemWidget); - qtSMTKUtilities::registerItemConstructor( - "Cylinder", pqSMTKConeItemWidget::createCylinderItemWidget); - qtSMTKUtilities::registerItemConstructor( - "CoordinateFrame", pqSMTKCoordinateFrameItemWidget::createCoordinateFrameItemWidget); - qtSMTKUtilities::registerItemConstructor( - "InfiniteCylinder", pqSMTKInfiniteCylinderItemWidget::createCylinderItemWidget); + qtSMTKUtilities::registerItemConstructor("Cylinder", pqSMTKConeItemWidget::createCylinderItemWidget); + qtSMTKUtilities::registerItemConstructor("CoordinateFrame", pqSMTKCoordinateFrameItemWidget::createCoordinateFrameItemWidget); + qtSMTKUtilities::registerItemConstructor("Disk", pqSMTKDiskItemWidget::createDiskItemWidget); + qtSMTKUtilities::registerItemConstructor("InfiniteCylinder", pqSMTKInfiniteCylinderItemWidget::createCylinderItemWidget); qtSMTKUtilities::registerItemConstructor("Line", pqSMTKLineItemWidget::createLineItemWidget); qtSMTKUtilities::registerItemConstructor("Plane", pqSMTKPlaneItemWidget::createPlaneItemWidget); qtSMTKUtilities::registerItemConstructor("Point", pqSMTKPointItemWidget::createPointItemWidget); qtSMTKUtilities::registerItemConstructor("Slice", pqSMTKSliceItemWidget::createSliceItemWidget); - qtSMTKUtilities::registerItemConstructor( - "Sphere", pqSMTKSphereItemWidget::createSphereItemWidget); - qtSMTKUtilities::registerItemConstructor( - "Spline", pqSMTKSplineItemWidget::createSplineItemWidget); - qtSMTKUtilities::registerItemConstructor( - "Transform", pqSMTKTransformWidget::createTransformWidget); + qtSMTKUtilities::registerItemConstructor("Sphere", pqSMTKSphereItemWidget::createSphereItemWidget); + qtSMTKUtilities::registerItemConstructor("Spline", pqSMTKSplineItemWidget::createSplineItemWidget); + qtSMTKUtilities::registerItemConstructor("Transform", pqSMTKTransformWidget::createTransformWidget); + // clang-format on } void pqSMTKWidgetsAutoStart::shutdown() diff --git a/smtk/extension/paraview/widgets/pqDiskPropertyWidget.cxx b/smtk/extension/paraview/widgets/pqDiskPropertyWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0b7e9b0e7925f9c9e86cda50f8f6e7843f59b199 --- /dev/null +++ b/smtk/extension/paraview/widgets/pqDiskPropertyWidget.cxx @@ -0,0 +1,134 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/paraview/widgets/pqDiskPropertyWidget.h" +#include "smtk/extension/paraview/widgets/pqPointPickingVisibilityHelper.h" +#include "smtk/extension/paraview/widgets/ui_pqDiskPropertyWidget.h" + +#include "pqCoreUtilities.h" +#include "pqPointPickingHelper.h" +#include "pqView.h" + +#include "vtkSMNewWidgetRepresentationProxy.h" +#include "vtkSMProperty.h" +#include "vtkSMPropertyGroup.h" +#include "vtkSMPropertyHelper.h" + +#include "vtkCommand.h" +#include "vtkMath.h" +#include "vtkVector.h" +#include "vtkVectorOperators.h" + +#include <QCheckBox> + +class pqDiskPropertyWidget::Internals +{ +public: + Internals() = default; + + Ui::DiskPropertyWidget Ui; +}; + +pqDiskPropertyWidget::pqDiskPropertyWidget( + vtkSMProxy* smproxy, + vtkSMPropertyGroup* smgroup, + QWidget* parentObj) + : Superclass("representations", "DiskWidgetRepresentation", smproxy, smgroup, parentObj) + , m_p(new pqDiskPropertyWidget::Internals()) +{ + Ui::DiskPropertyWidget& ui = m_p->Ui; + ui.setupUi(this); + + // link show3DWidget checkbox + QObject::connect(ui.show3DWidget, &QCheckBox::toggled, this, &pqDiskPropertyWidget::setWidgetVisible); + QObject::connect(this, &pqDiskPropertyWidget::widgetVisibilityToggled, ui.show3DWidget, &QCheckBox::setChecked); + this->setWidgetVisible(ui.show3DWidget->isChecked()); + +#ifdef Q_OS_MAC + ui.pickLabel->setText(ui.pickLabel->text().replace("Ctrl", "Cmd")); +#endif + + if (vtkSMProperty* ctr = smgroup->GetProperty("CenterPoint")) + { + ui.labelCenter->setText(tr(ctr->GetXMLLabel())); + this->addPropertyLink(ui.centerX, "text2", SIGNAL(textChangedAndEditingFinished()), ctr, 0); + this->addPropertyLink(ui.centerY, "text2", SIGNAL(textChangedAndEditingFinished()), ctr, 1); + this->addPropertyLink(ui.centerZ, "text2", SIGNAL(textChangedAndEditingFinished()), ctr, 2); + ui.labelCenter->setText(ctr->GetXMLLabel()); + } + else + { + qCritical("Missing required property for function 'CenterPoint'."); + } + + if (vtkSMProperty* nrm = smgroup->GetProperty("Normal")) + { + ui.labelNormal->setText(tr(nrm->GetXMLLabel())); + this->addPropertyLink(ui.normalX, "text2", SIGNAL(textChangedAndEditingFinished()), nrm, 0); + this->addPropertyLink(ui.normalY, "text2", SIGNAL(textChangedAndEditingFinished()), nrm, 1); + this->addPropertyLink(ui.normalZ, "text2", SIGNAL(textChangedAndEditingFinished()), nrm, 2); + ui.labelNormal->setText(nrm->GetXMLLabel()); + } + else + { + qCritical("Missing required property for function 'Normal'."); + } + + if (vtkSMProperty* rad = smgroup->GetProperty("Radius")) + { + ui.labelRadius->setText(tr(rad->GetXMLLabel())); + this->addPropertyLink(ui.radius, "text2", SIGNAL(textChangedAndEditingFinished()), rad, 0); + } + else + { + qCritical("Missing required property for function 'Radius'."); + } + + pqPointPickingHelper* pickHelper = new pqPointPickingHelper(QKeySequence(tr("P")), false, this); + QObject::connect(this, &pqDiskPropertyWidget::viewChanged, pickHelper, &pqPointPickingHelper::setView); + QObject::connect(pickHelper, &pqPointPickingHelper::pick, this, &pqDiskPropertyWidget::pick); + pqPointPickingVisibilityHelper<pqPointPickingHelper>{ *this, *pickHelper }; + + pqPointPickingHelper* pickHelper2 = + new pqPointPickingHelper(QKeySequence(tr("Ctrl+P")), true, this); + QObject::connect(this, &pqDiskPropertyWidget::viewChanged, pickHelper2, &pqPointPickingHelper::setView); + QObject::connect(pickHelper2, &pqPointPickingHelper::pick, this, &pqDiskPropertyWidget::pick); + pqPointPickingVisibilityHelper<pqPointPickingHelper>{ *this, *pickHelper2 }; + + pqCoreUtilities::connect( + this->widgetProxy(), vtkCommand::PropertyModifiedEvent, this, SLOT(updateInformationLabels())); + this->updateInformationLabels(); +} + +pqDiskPropertyWidget::~pqDiskPropertyWidget() = default; + +void pqDiskPropertyWidget::pick(double wx, double wy, double wz) +{ + double position[3] = { wx, wy, wz }; + vtkSMNewWidgetRepresentationProxy* wdgProxy = this->widgetProxy(); + vtkSMPropertyHelper(wdgProxy, "CenterPoint").Set(position, 3); + wdgProxy->UpdateVTKObjects(); + Q_EMIT this->changeAvailable(); + this->render(); +} + +void pqDiskPropertyWidget::updateInformationLabels() +{ + // Ui::DiskPropertyWidget& ui = m_p->Ui; + + vtkVector3d ctr, nrm; + vtkSMProxy* wproxy = this->widgetProxy(); + vtkSMPropertyHelper(wproxy, "CenterPoint").Get(ctr.GetData(), 3); + vtkSMPropertyHelper(wproxy, "Normal").Get(nrm.GetData(), 3); +} + +void pqDiskPropertyWidget::placeWidget() +{ + // Nothing to do? +} diff --git a/smtk/extension/paraview/widgets/pqDiskPropertyWidget.h b/smtk/extension/paraview/widgets/pqDiskPropertyWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..1b0c3b0ec3fb06380b0d1f92bf46017dbf8a3f92 --- /dev/null +++ b/smtk/extension/paraview/widgets/pqDiskPropertyWidget.h @@ -0,0 +1,40 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef smtk_extension_paraview_widgets_pqDiskPropertyWidget_h +#define smtk_extension_paraview_widgets_pqDiskPropertyWidget_h + +#include "smtk/extension/paraview/widgets/pqSMTKInteractivePropertyWidget.h" +#include "smtk/extension/paraview/widgets/smtkPQWidgetsExtModule.h" + +class SMTKPQWIDGETSEXT_EXPORT pqDiskPropertyWidget : public pqSMTKInteractivePropertyWidget +{ + Q_OBJECT + using Superclass = pqSMTKInteractivePropertyWidget; + +public: + pqDiskPropertyWidget(vtkSMProxy* proxy, vtkSMPropertyGroup* smgroup, QWidget* parent = nullptr); + ~pqDiskPropertyWidget() override; + +public Q_SLOTS: + void pick(double, double, double); + +protected Q_SLOTS: + void updateInformationLabels(); + void placeWidget() override; + +protected: + class Internals; + Internals* m_p; + +private: + Q_DISABLE_COPY(pqDiskPropertyWidget); +}; + +#endif // smtk_extension_paraview_widgets_pqDiskPropertyWidget_h diff --git a/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f004ab44124c1c7424a664e99f848068f5c5e195 --- /dev/null +++ b/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.cxx @@ -0,0 +1,230 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.h" +#include "smtk/extension/paraview/widgets/pqDiskPropertyWidget.h" +#include "smtk/extension/paraview/widgets/pqSMTKAttributeItemWidgetP.h" + +#include "smtk/attribute/DoubleItem.h" +#include "smtk/attribute/GroupItem.h" + +#include "smtk/io/Logger.h" + +#include "pqActiveObjects.h" +#include "pqApplicationCore.h" +#include "pqDataRepresentation.h" +#include "pqImplicitPlanePropertyWidget.h" +#include "pqObjectBuilder.h" +#include "pqPipelineSource.h" +#include "pqServer.h" +#include "pqSpherePropertyWidget.h" +#include "vtkMath.h" +#include "vtkPVXMLElement.h" +#include "vtkSMNewWidgetRepresentationProxy.h" +#include "vtkSMProperty.h" +#include "vtkSMPropertyGroup.h" +#include "vtkSMPropertyHelper.h" +#include "vtkSMProxy.h" +#include "vtkVector.h" +#include "vtkVectorOperators.h" + +using qtItem = smtk::extension::qtItem; +using qtAttributeItemInfo = smtk::extension::qtAttributeItemInfo; + +pqSMTKDiskItemWidget::pqSMTKDiskItemWidget( + const smtk::extension::qtAttributeItemInfo& info, + Qt::Orientation orient) + : pqSMTKAttributeItemWidget(info, orient) +{ + this->createWidget(); +} + +pqSMTKDiskItemWidget::~pqSMTKDiskItemWidget() = default; + +qtItem* pqSMTKDiskItemWidget::createDiskItemWidget(const qtAttributeItemInfo& info) +{ + return new pqSMTKDiskItemWidget(info); +} + +bool pqSMTKDiskItemWidget::createProxyAndWidget( + vtkSMProxy*& proxy, + pqInteractivePropertyWidget*& widget) +{ + ItemBindings binding; + std::vector<smtk::attribute::DoubleItemPtr> items; + bool haveItems = this->fetchDiskItems(binding, items); + if (!haveItems || binding == ItemBindings::Invalid) + { + smtkErrorMacro(smtk::io::Logger::instance(), "Could not find items for widget."); + return false; + } + + // I. Create the ParaView widget and a proxy for its representation. + pqApplicationCore* paraViewApp = pqApplicationCore::instance(); + pqServer* server = paraViewApp->getActiveServer(); + pqObjectBuilder* builder = paraViewApp->getObjectBuilder(); + + proxy = builder->createProxy("implicit_functions", "ImplicitDisk", server, ""); + if (!proxy) + { + return false; + } + auto* diskWidget = new pqDiskPropertyWidget(proxy, proxy->GetPropertyGroup(0)); + widget = diskWidget; + + // II. Initialize the properties. + m_p->m_pvwidget = widget; + this->updateWidgetFromItem(); + auto* widgetProxy = widget->widgetProxy(); + widgetProxy->UpdateVTKObjects(); + // vtkSMPropertyHelper(widgetProxy, "RotationEnabled").Set(false); + + return widget != nullptr; +} + +bool pqSMTKDiskItemWidget::updateItemFromWidgetInternal() +{ + vtkSMNewWidgetRepresentationProxy* widget = m_p->m_pvwidget->widgetProxy(); + std::vector<smtk::attribute::DoubleItemPtr> items; + ItemBindings binding; + if (!this->fetchDiskItems(binding, items)) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Item widget has an update but the item(s) do not exist or are not sized properly."); + return false; + } + + // Values held by widget + vtkVector3d ctr; + vtkVector3d nrm; + double rad; + vtkSMPropertyHelper ctrHelper(widget, "CenterPoint"); + vtkSMPropertyHelper nrmHelper(widget, "Normal"); + vtkSMPropertyHelper radHelper(widget, "Radius"); + ctrHelper.Get(ctr.GetData(), 3); + nrmHelper.Get(nrm.GetData(), 3); + radHelper.Get(&rad, 1); + bool didChange = false; + + // Current values held in items: + vtkVector3d curPt0; + vtkVector3d curPt1; + double curRad; + + // Translate widget values to item values and fetch current item values: + curPt0 = vtkVector3d(&(*items[0]->begin())); + curPt1 = vtkVector3d(&(*items[1]->begin())); + curRad = *items[2]->begin(); + switch (binding) + { + case ItemBindings::DiskPointsRadii: + if (curPt0 != ctr || curPt1 != nrm || curRad != rad) + { + didChange = true; + items[0]->setValues(ctr.GetData(), ctr.GetData() + 3); + items[1]->setValues(nrm.GetData(), nrm.GetData() + 3); + items[2]->setValue(rad); + } + break; + case ItemBindings::Invalid: + default: + smtkErrorMacro(smtk::io::Logger::instance(), "Unable to determine item binding."); + break; + } + + return didChange; +} + +bool pqSMTKDiskItemWidget::updateWidgetFromItemInternal() +{ + vtkSMNewWidgetRepresentationProxy* widget = m_p->m_pvwidget->widgetProxy(); + std::vector<smtk::attribute::DoubleItemPtr> items; + ItemBindings binding; + if (!this->fetchDiskItems(binding, items)) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Item signaled an update but the item(s) do not exist or are not sized properly."); + return false; + } + + // Unlike updateItemFromWidget, we don't care if we cause ParaView an unnecessary update; + // we might cause an extra render but we won't accidentally mark a resource as modified. + // Since there's no need to compare new values to old, this is simpler than updateItemFromWidget: + vtkVector3d ctr(&(*items[0]->begin())); + vtkVector3d nrm(&(*items[1]->begin())); + double radius = items[2]->value(0); + vtkSMPropertyHelper(widget, "CenterPoint").Set(ctr.GetData(), 3); + vtkSMPropertyHelper(widget, "Normal").Set(nrm.GetData(), 3); + vtkSMPropertyHelper(widget, "Radius").Set(&radius, 1); + switch (binding) + { + case ItemBindings::DiskPointsRadii: + break; + case ItemBindings::Invalid: + default: + { + smtkErrorMacro(smtk::io::Logger::instance(), "Unhandled item binding."); + } + break; + } + return true; // TODO: determine whether values were changed to avoid unnecessary renders. +} + +bool pqSMTKDiskItemWidget::fetchDiskItems( + ItemBindings& binding, + std::vector<smtk::attribute::DoubleItemPtr>& items) +{ + items.clear(); + + // Check to see if item is a group containing items of double-vector items. + auto groupItem = m_itemInfo.itemAs<smtk::attribute::GroupItem>(); + if (!groupItem || groupItem->numberOfGroups() < 1 || groupItem->numberOfItemsPerGroup() < 3) + { + smtkErrorMacro( + smtk::io::Logger::instance(), "Expected a group item with 1 group of 3 or more items."); + return false; + } + + // Find items in the group based on names in the configuration info: + // ctr, nrm, rad + std::string ctrItemName; + std::string nrmItemName; + std::string radItemName; + if (!m_itemInfo.component().attribute("Center", ctrItemName)) + { + ctrItemName = "Center"; + } + if (!m_itemInfo.component().attribute("Normal", nrmItemName)) + { + nrmItemName = "Normal"; + } + if (!m_itemInfo.component().attribute("Radius", radItemName)) + { + radItemName = "Radius"; + } + auto ctrItem = groupItem->findAs<smtk::attribute::DoubleItem>(ctrItemName); + auto nrmItem = groupItem->findAs<smtk::attribute::DoubleItem>(nrmItemName); + auto radItem = groupItem->findAs<smtk::attribute::DoubleItem>(radItemName); + + if ( + ctrItem && ctrItem->numberOfValues() == 3 && nrmItem && nrmItem->numberOfValues() == 3 && + radItem && radItem->numberOfValues() == 1) + { + items.push_back(ctrItem); + items.push_back(nrmItem); + items.push_back(radItem); + binding = ItemBindings::DiskPointsRadii; + return true; + } + + binding = ItemBindings::Invalid; + return false; +} diff --git a/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.h b/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..e78312bafc145a7968ebcd631d14dc662f306199 --- /dev/null +++ b/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.h @@ -0,0 +1,74 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef smtk_extension_paraview_widgets_pqSMTKDiskItemWidget_h +#define smtk_extension_paraview_widgets_pqSMTKDiskItemWidget_h + +#include "smtk/extension/paraview/widgets/pqSMTKAttributeItemWidget.h" + +/**\brief Display an interactive disk widget. + * + * The widget currently accepts a center point, normal, and radius + * that defines 2-d disk embedded in 3-d. + * + * In the future, other item types (such as a GroupItem holding + * children specifying 3 points on the circumference of a disk; + * or a center point and point on the circumferece; or a pair of + * diametrically opposed points and a normal) may be supported. + * + * Currently, there is no support to initialize the placement to + * a given set of bounds; if you use this widget as part of the + * user interface to an operation, implement a configure() method + * on the operation to contextually place the widget based on + * associations. + */ +class SMTKPQWIDGETSEXT_EXPORT pqSMTKDiskItemWidget : public pqSMTKAttributeItemWidget +{ + Q_OBJECT +public: + pqSMTKDiskItemWidget( + const smtk::extension::qtAttributeItemInfo& info, + Qt::Orientation orient = Qt::Horizontal); + ~pqSMTKDiskItemWidget() override; + + /// Create an instance of the widget that allows users to define a disk. + static qtItem* createDiskItemWidget(const qtAttributeItemInfo& info); + + bool createProxyAndWidget(vtkSMProxy*& proxy, pqInteractivePropertyWidget*& widget) override; + +protected Q_SLOTS: + /// Retrieve property values from ParaView proxy and store them in the attribute's Item. + bool updateItemFromWidgetInternal() override; + /// Retrieve property values from the attribute's Item and update the ParaView proxy. + bool updateWidgetFromItemInternal() override; + +protected: + /// Describe how an attribute's items specify a disk or cylinder. + enum class ItemBindings + { + /// 2 items with 3 values, 1 items with 1 value (cx, cy, cz, nx, ny, nz, rr). + DiskPointsRadii, + /// No consistent set of items detected. + Invalid + }; + /**\brief Starting with the widget's assigned item (which must currently + * be a GroupItem), determine and return bound items. + * + * The named item must be a Group holding items as called out by + * one of the valid ItemBindings enumerants. + * The items inside the Group must currently be Double items. + * + * If errors (such as a lack of matching item names or an + * unexpected number of values per item) are encountered, + * this method returns false. + */ + bool fetchDiskItems(ItemBindings& binding, std::vector<smtk::attribute::DoubleItemPtr>& items); +}; + +#endif // smtk_extension_paraview_widgets_pqSMTKDiskItemWidget_h diff --git a/smtk/extension/paraview/widgets/resources/pqDiskPropertyWidget.ui b/smtk/extension/paraview/widgets/resources/pqDiskPropertyWidget.ui new file mode 100644 index 0000000000000000000000000000000000000000..3aeb2a730bb8977a6dd48c1479204a7ba56f335a --- /dev/null +++ b/smtk/extension/paraview/widgets/resources/pqDiskPropertyWidget.ui @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>DiskPropertyWidget</class> + <widget class="QWidget" name="DiskPropertyWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>341</width> + <height>155</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>2</number> + </property> + <item row="5" column="1"> + <widget class="pqDoubleLineEdit" name="normalX"/> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="labelNormal"> + <property name="text"> + <string>Normal</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="7" column="0" colspan="4"> + <widget class="QLabel" name="pickLabel"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Note: Use 'P' to move the center to the pointer or 'Ctrl+P' to snap to the closest mesh point. </string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0" colspan="3"> + <widget class="QCheckBox" name="show3DWidget"> + <property name="text"> + <string>Show disk</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="labelRadius"> + <property name="text"> + <string>Radius</string> + </property> + </widget> + </item> + <item row="4" column="3"> + <widget class="pqDoubleLineEdit" name="centerZ"/> + </item> + <item row="5" column="3"> + <widget class="pqDoubleLineEdit" name="normalZ"/> + </item> + <item row="4" column="1"> + <widget class="pqDoubleLineEdit" name="centerX"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="labelCenter"> + <property name="text"> + <string>Center</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="pqDoubleLineEdit" name="radius"/> + </item> + <item row="5" column="2"> + <widget class="pqDoubleLineEdit" name="normalY"/> + </item> + <item row="4" column="2"> + <widget class="pqDoubleLineEdit" name="centerY"/> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>pqDoubleLineEdit</class> + <extends>QLineEdit</extends> + <header>pqDoubleLineEdit.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>show3DWidget</tabstop> + <tabstop>centerX</tabstop> + <tabstop>centerY</tabstop> + <tabstop>centerZ</tabstop> + <tabstop>normalX</tabstop> + <tabstop>normalY</tabstop> + <tabstop>normalZ</tabstop> + <tabstop>radius</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/smtk/extension/qt/diagram/qtBaseTaskNode.cxx b/smtk/extension/qt/diagram/qtBaseTaskNode.cxx index 22a79b75101862395175d3b4e4de19aa2390723e..7fdbb840f88b8d9e5b6bb01c928b111049ce112b 100644 --- a/smtk/extension/qt/diagram/qtBaseTaskNode.cxx +++ b/smtk/extension/qt/diagram/qtBaseTaskNode.cxx @@ -91,7 +91,7 @@ void qtBaseTaskNode::updateTaskState(smtk::task::State prev, smtk::task::State n (void)active; // Update the tool tip with diagnostic information smtk::task::Task::InformationOptions opt; - opt.m_includeTitle = false; + opt.m_includeTitle = true; this->setToolTip(QString::fromStdString(m_task->information(opt))); } @@ -101,7 +101,7 @@ void qtBaseTaskNode::dataUpdated() // Update the tool tip with diagnostic information smtk::task::Task::InformationOptions opt; - opt.m_includeTitle = false; + opt.m_includeTitle = true; this->setToolTip(QString::fromStdString(m_task->information(opt))); } diff --git a/smtk/extension/qt/qtAttribute.cxx b/smtk/extension/qt/qtAttribute.cxx index 17fc3c95f5cf58c454a083666ffcaa62f80310a5..d5ec6b31cb715f6e50f791db3e89726267b7458e 100644 --- a/smtk/extension/qt/qtAttribute.cxx +++ b/smtk/extension/qt/qtAttribute.cxx @@ -286,7 +286,7 @@ void qtAttribute::createBasicLayout(bool includeAssociations) QString baseUnits = (att->definition()->units() == "*") ? att->units().c_str() : att->definition()->units().c_str(); auto* unitsWidget = - new qtUnitsLineEdit(baseUnits, att->definition()->unitsSystem(), uiManager, unitFrame); + new qtUnitsLineEdit(baseUnits, att->definition()->unitSystem(), uiManager, unitFrame); std::string unitsWidgetName = att->name() + "UnitsLineWidget"; unitsWidget->setObjectName(unitsWidgetName.c_str()); unitsLayout->addWidget(unitsWidget); diff --git a/smtk/extension/qt/qtDoubleUnitsLineEdit.cxx b/smtk/extension/qt/qtDoubleUnitsLineEdit.cxx index 346e28ed1c69e134a76a899dd896a1b337819e2a..e2180bf1b2e0c4b0acde749005ee1c58d2590fdc 100644 --- a/smtk/extension/qt/qtDoubleUnitsLineEdit.cxx +++ b/smtk/extension/qt/qtDoubleUnitsLineEdit.cxx @@ -167,7 +167,7 @@ qtDoubleUnitsLineEdit* qtDoubleUnitsLineEdit::checkAndCreate( } // Get units system - auto unitSystem = dItem->definition()->unitsSystem(); + auto unitSystem = dItem->definition()->unitSystem(); if (unitSystem == nullptr) { return nullptr; @@ -302,7 +302,7 @@ void qtDoubleUnitsLineEdit::onTextEdited() auto dItem = m_inputsItem->itemAs<DoubleItem>(); auto dUnits = dItem->units(); - auto unitSystem = dItem->definition()->unitsSystem(); + auto unitSystem = dItem->definition()->unitSystem(); // Parsing the Item's unit string bool parsedOK = false; diff --git a/smtk/extension/qt/qtInputsItem.cxx b/smtk/extension/qt/qtInputsItem.cxx index 7b70552d52cbbe44aa1b81fa0755ac1ad74210ae..8589a48f5e32a4043a86187ddf9520ea579a3e0a 100644 --- a/smtk/extension/qt/qtInputsItem.cxx +++ b/smtk/extension/qt/qtInputsItem.cxx @@ -744,9 +744,9 @@ void qtInputsItem::showExpressionResultWidgets( auto itemUnits = item->units(); if (!itemUnits.empty()) { - auto unitsSystem = item->definition()->unitsSystem(); + auto unitSystem = item->definition()->unitSystem(); bool parsed = false; - unitsSystem->unit(itemUnits, &parsed); + unitSystem->unit(itemUnits, &parsed); if (parsed) { displayText = QString("%1 %2").arg(text).arg(itemUnits.c_str()); @@ -981,11 +981,11 @@ QFrame* qtInputsItem::createLabelFrame( if (addUnitsLabel && !vitemDef->isDiscrete()) { // Check if units are "valid" - const auto& unitsSystem = vitemDef->unitsSystem(); - if (unitsSystem) + const auto& unitSystem = vitemDef->unitSystem(); + if (unitSystem) { bool unitsParsed = false; - units::Unit defUnit = unitsSystem->unit(valUnits, &unitsParsed); + units::Unit defUnit = unitSystem->unit(valUnits, &unitsParsed); addUnitsLabel = addUnitsLabel && (!unitsParsed); } } diff --git a/smtk/extension/qt/qtWorkletModel.cxx b/smtk/extension/qt/qtWorkletModel.cxx index e89a43bf7d514cbf7635e2df39c1d559ea50ab0d..245ee18081c78e4593e9ed2def51aa9cd594dd6a 100644 --- a/smtk/extension/qt/qtWorkletModel.cxx +++ b/smtk/extension/qt/qtWorkletModel.cxx @@ -283,12 +283,18 @@ void qtWorkletModel::workletUpdate( // components. So use a set to assemble all of the uniquely created worklets and then // insert them all at the same time. std::set<smtk::task::Worklet::Ptr> workletsToBeInserted; + std::set<smtk::task::Worklet::Ptr> allWorklets(m_worklets.begin(), m_worklets.end()); smtk::resource::Component::Visitor visitor = - [this, workletTypeName, &workletsToBeInserted](const smtk::resource::Component::Ptr& comp) { + [this, workletTypeName, &allWorklets, &workletsToBeInserted]( + const smtk::resource::Component::Ptr& comp) { if (comp->matchesType(workletTypeName)) { auto worklet = std::dynamic_pointer_cast<smtk::task::Worklet>(comp); - m_worklets.push_back(worklet); + if (allWorklets.find(worklet) == allWorklets.end()) + { + m_worklets.push_back(worklet); + allWorklets.insert(worklet); + } // Lets see if the worklet should be presented? if (m_parentTask) { @@ -324,7 +330,11 @@ void qtWorkletModel::workletUpdate( { if (auto worklet = std::dynamic_pointer_cast<smtk::task::Worklet>(comp)) { - m_worklets.push_back(worklet); + if (allWorklets.find(worklet) == allWorklets.end()) + { + m_worklets.push_back(worklet); + allWorklets.insert(worklet); + } // Lets see if the worklet should be presented? if (m_parentTask) { diff --git a/smtk/extension/vtk/markup/Geometry.cxx b/smtk/extension/vtk/markup/Geometry.cxx index fb0700fe050b4f4e1b6e33f629a48a1f32f39562..8239b19679f2e0c2ba62b32011c8202a128d2cfa 100644 --- a/smtk/extension/vtk/markup/Geometry.cxx +++ b/smtk/extension/vtk/markup/Geometry.cxx @@ -61,6 +61,16 @@ void Geometry::queryGeometry(const smtk::resource::PersistentObject::Ptr& obj, C entry.m_geometry = nullptr; + // Check component-wide blanking + if (auto* spatial = dynamic_cast<smtk::markup::SpatialData*>(component.get())) + { + if (spatial->isBlanked()) + { + entry.m_generation = Invalid; + return; + } + } + if (auto* image = dynamic_cast<smtk::markup::ImageData*>(component.get())) { auto shape = image->shapeData(); diff --git a/smtk/extension/vtk/source/CMakeLists.txt b/smtk/extension/vtk/source/CMakeLists.txt index 1f7d2a9221a401198d2f7d5bc762ffbe5b79d09a..040407f99ffc23339241f18d38db2353e69bca81 100644 --- a/smtk/extension/vtk/source/CMakeLists.txt +++ b/smtk/extension/vtk/source/CMakeLists.txt @@ -7,7 +7,9 @@ set(classes vtkAttributeMultiBlockSource vtkCmbLayeredConeSource vtkConeFrustum + vtkDisk vtkImplicitConeFrustum + vtkImplicitDisk vtkModelMultiBlockSource vtkModelView vtkResourceMultiBlockSource) diff --git a/smtk/extension/vtk/source/vtkDisk.cxx b/smtk/extension/vtk/source/vtkDisk.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c8663d85b9f314e1494d468456e09109ff077507 --- /dev/null +++ b/smtk/extension/vtk/source/vtkDisk.cxx @@ -0,0 +1,185 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/vtk/source/vtkDisk.h" + +#include "vtkCellArray.h" +#include "vtkDoubleArray.h" +#include "vtkFloatArray.h" +#include "vtkIdTypeArray.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkNew.h" +#include "vtkObjectFactory.h" +#include "vtkPointData.h" +#include "vtkPolyData.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkTransform.h" +#include "vtkVector.h" +#include "vtkVectorOperators.h" + +#include <cmath> + +vtkStandardNewMacro(vtkDisk); + +vtkDisk::vtkDisk(int res) + : Resolution(res <= 3 ? 3 : res) +{ + this->SetNumberOfInputPorts(0); + this->SetNumberOfOutputPorts(NumberOfOutputs); +} + +vtkDisk::~vtkDisk() = default; + +void vtkDisk::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + + os << indent << "CenterPoint: (" << this->CenterPoint[0] << ", " << this->CenterPoint[1] << ", " + << this->CenterPoint[2] << ")\n"; + os << indent << "Radius: " << this->Radius << "\n"; + os << indent << "Normal: (" << this->Normal[0] << ", " << this->Normal[1] << ", " + << this->Normal[2] << ")\n"; + os << indent << "Resolution: " << this->Resolution << "\n"; + os << indent << "Output Points Precision: " << this->OutputPointsPrecision << "\n"; +} + +int vtkDisk::RequestData( + vtkInformation* /*request*/, + vtkInformationVector** /*inputVector*/, + vtkInformationVector* outputVector) +{ + // vtkInformation* outInfo = outputVector->GetInformationObject(0); + // clang-format off + vtkPolyData* diskFace = vtkPolyData::GetData(outputVector, static_cast<int>(OutputPorts::DiskFace)); + vtkPolyData* normEdge = vtkPolyData::GetData(outputVector, static_cast<int>(OutputPorts::DiskNormal)); + vtkPolyData* diskEdge = vtkPolyData::GetData(outputVector, static_cast<int>(OutputPorts::DiskEdge)); + vtkPolyData* ctrVert = vtkPolyData::GetData(outputVector, static_cast<int>(OutputPorts::CenterVertex)); + if (!diskFace || !normEdge || !diskEdge || !ctrVert) + { + vtkErrorMacro("No output provided."); + return 0; + } + diskFace->Initialize(); + normEdge->Initialize(); + diskEdge->Initialize(); + ctrVert->Initialize(); + + vtkVector3d p0(this->CenterPoint); + vtkVector3d nn(this->Normal); + + vtkDebugMacro("DiskSource Executing"); + + // Get axes xx and yy in the plane normal to the disk axis + vtkVector3d axis = nn.Normalized(); + vtkVector3d p1 = p0 + axis * this->Radius; + vtkVector3d px(1., 0., 0.); + vtkVector3d py(0., 1., 0.); + vtkVector3d yy = axis.Cross(px); + if (yy.Norm() < 1e-10) + { + yy = axis.Cross(py); + } + yy.Normalize(); + vtkVector3d xx = yy.Cross(axis).Normalized(); + + // I. Compute the point coordinates. + vtkNew<vtkPoints> facePts; + facePts->SetDataType( + this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION ? VTK_DOUBLE : VTK_FLOAT); + facePts->SetNumberOfPoints(1 + this->Resolution); + facePts->SetPoint(this->Resolution, p0.GetData()); + + vtkNew<vtkPoints> normPts; + normPts->SetDataType( + this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION ? VTK_DOUBLE : VTK_FLOAT); + normPts->Allocate(2); + normPts->InsertNextPoint(p0.GetData()); + normPts->InsertNextPoint(p1.GetData()); + + vtkNew<vtkPoints> edgePts; + edgePts->SetDataType( + this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION ? VTK_DOUBLE : VTK_FLOAT); + edgePts->Allocate(this->Resolution); + + vtkNew<vtkPoints> centerPoint; + centerPoint->SetDataType( + this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION ? VTK_DOUBLE : VTK_FLOAT); + centerPoint->Allocate(1); + centerPoint->InsertNextPoint(p0.GetData()); + + vtkNew<vtkDoubleArray> faceNrm; // point normals + faceNrm->SetNumberOfComponents(3); + faceNrm->SetName("normals"); + faceNrm->SetNumberOfTuples(facePts->GetNumberOfPoints()); + faceNrm->FillComponent(0, nn[0]); + faceNrm->FillComponent(1, nn[1]); + faceNrm->FillComponent(2, nn[2]); + + double angle = 2.0 * vtkMath::Pi() / this->Resolution; + + // Disk face and edge points + for (int ii = 0; ii < this->Resolution; ++ii) + { + double theta = angle * ii; + vtkVector3d pt = p0 + this->Radius * (xx * cos(theta) + yy * sin(theta)); + facePts->SetPoint(ii, pt.GetData()); + edgePts->InsertNextPoint(pt.GetData()); + } + + // II. Prepare face connectivity + vtkIdType numConnEdge = 2 + this->Resolution; + vtkIdType numConnFace = (3 + 1) * this->Resolution; + + vtkNew<vtkCellArray> facePolys; + vtkNew<vtkCellArray> edgeLines; + vtkNew<vtkCellArray> normLines; + vtkNew<vtkCellArray> centerVertex; + vtkNew<vtkIdTypeArray> faceConn; + vtkNew<vtkIdTypeArray> edgeConn; + vtkNew<vtkIdTypeArray> normConn; + faceConn->Allocate(numConnFace); + edgeConn->Allocate(numConnEdge); + normConn->SetNumberOfTuples(3); + normLines->Allocate(3); + vtkIdType zero = 0; + centerVertex->InsertNextCell(1, &zero); + + // Disk face connectivity (triangles). + // The last point is the center of the disk. + edgeConn->InsertNextValue(this->Resolution); + for (int ii = 0; ii < this->Resolution; ++ii) + { + edgeConn->InsertNextValue(ii); + faceConn->InsertNextValue(3); + faceConn->InsertNextValue(this->Resolution); + faceConn->InsertNextValue(ii); + faceConn->InsertNextValue((ii + 1) % this->Resolution); + } + normConn->SetValue(0, 2); + normConn->SetValue(1, 0); + normConn->SetValue(2, 1); + + facePolys->SetCells(this->Resolution, faceConn); + edgeLines->SetCells(1, edgeConn); + normLines->SetCells(1, normConn); + + diskFace->SetPoints(facePts); + diskFace->SetPolys(facePolys); + diskFace->GetPointData()->SetNormals(faceNrm); + normEdge->SetPoints(normPts); + normEdge->SetLines(normLines); + diskEdge->SetPoints(edgePts); + diskEdge->SetLines(edgeLines); + ctrVert->SetPoints(centerPoint); + ctrVert->SetVerts(centerVertex); + + return 1; +} diff --git a/smtk/extension/vtk/source/vtkDisk.h b/smtk/extension/vtk/source/vtkDisk.h new file mode 100644 index 0000000000000000000000000000000000000000..38b1859c94cbc4fe299f5708c9abd538da570455 --- /dev/null +++ b/smtk/extension/vtk/source/vtkDisk.h @@ -0,0 +1,121 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef vtkDisk_h +#define vtkDisk_h + +#include "smtk/extension/vtk/source/vtkImplicitDisk.h" // For ivar +#include "vtkPolyDataAlgorithm.h" + +#include "vtkCell.h" // Needed for VTK_CELL_SIZE + +/** + * @class vtkDisk + * @brief Generate a polygonal approximation to a disk + * + * vtkDisk creates a disk with a given center, normal, and radius. + * By default, point 1 lies at the origin with radius 0.5 + * and the normal is (0,0,1). + * The resolution specifies the number of points around the circle; + * it must be at least 3 and defaults to 32. + * + * Note that unlike many other source filters, this one is *not* + * intended for use as an input to a glyph filter or glyph mapper. + * Instead, its purpose is to provide a high-quality visual representation + * of a planar disk. + * That means it uses more points and cells than is strictly necessary + * but is able to provide a better visual quality as a result. + */ +class VTKSMTKSOURCEEXT_EXPORT vtkDisk : public vtkPolyDataAlgorithm +{ +public: + vtkTypeMacro(vtkDisk, vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent) override; + + vtkDisk(const vtkDisk&) = delete; + vtkDisk& operator=(const vtkDisk&) = delete; + + /// An enum indexing data present at each output. + enum OutputPorts + { + DiskFace = 0, //!< Triangulation of the disk face. + DiskNormal, //!< Line from the center point along the normal vector (length of disk radius). + DiskEdge, //!< Polyline of the disk boundary. + CenterVertex, //!< A single vertex at the center of the bottom face. + NumberOfOutputs + }; + + /** + * Construct with default parameters. + */ + static vtkDisk* New(); + + //@{ + /** + * Set/get the radius at the bottom of the cone. + * + * It must be non-negative. + */ + vtkSetClampMacro(Radius, double, 0.0, VTK_DOUBLE_MAX); + vtkGetMacro(Radius, double); + //@} + + //@{ + /** + * Set/get the bottom point of the cone. + * The default is 0,0,0. + */ + vtkSetVector3Macro(CenterPoint, double); + vtkGetVectorMacro(CenterPoint, double, 3); + //@} + + //@{ + /** + * Set/get the normal to the disk's plane. + * The default is 0,0,1. + */ + vtkSetVector3Macro(Normal, double); + vtkGetVectorMacro(Normal, double, 3); + //@} + + //@{ + /** + * Set/get the number of facets used to represent the conical side-face. + * + * This defaults to 32 and has a minimum of 3. + */ + vtkSetClampMacro(Resolution, int, 3, VTK_CELL_SIZE); + vtkGetMacro(Resolution, int); + //@} + + //@{ + /** + * Set/get the desired precision for the output points. + * vtkAlgorithm::SINGLE_PRECISION - Output single-precision floating point. + * vtkAlgorithm::DOUBLE_PRECISION - Output double-precision floating point. + */ + vtkSetMacro(OutputPointsPrecision, int); + vtkGetMacro(OutputPointsPrecision, int); + //@} + +protected: + vtkDisk(int res = 32); + ~vtkDisk() override; + + // int RequestInformation(vtkInformation* , vtkInformationVector** , vtkInformationVector* ) override; + int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override; + + double CenterPoint[3]{ 0, 0, 0 }; + double Radius{ 0.5 }; + double Normal[3]{ 0, 0, 1 }; + int Resolution; + int OutputPointsPrecision{ SINGLE_PRECISION }; +}; + +#endif diff --git a/smtk/extension/vtk/source/vtkImplicitDisk.cxx b/smtk/extension/vtk/source/vtkImplicitDisk.cxx new file mode 100644 index 0000000000000000000000000000000000000000..225d5b09715786609f8510bcd243b505ed2435d3 --- /dev/null +++ b/smtk/extension/vtk/source/vtkImplicitDisk.cxx @@ -0,0 +1,112 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/vtk/source/vtkImplicitDisk.h" + +#include "vtkCone.h" +#include "vtkMath.h" +#include "vtkObjectFactory.h" +#include "vtkPlane.h" +#include "vtkTransform.h" +#include "vtkVectorOperators.h" + +#include <cmath> + +vtkStandardNewMacro(vtkImplicitDisk); + +vtkImplicitDisk::vtkImplicitDisk() + : CenterPoint{ 0, 0, 0 } + , Normal{ 0, 0, 1 } +{ +} + +vtkImplicitDisk::~vtkImplicitDisk() = default; + +void vtkImplicitDisk::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + + os << indent << "CenterPoint: (" << this->CenterPoint << ")\n"; + os << indent << "Radius: " << this->Radius << "\n"; + os << indent << "Normal: (" << this->Normal << ")\n"; +} + +double vtkImplicitDisk::EvaluateFunction(double xx[3]) +{ + vtkVector3d pp(xx); + vtkVector3d delta = this->CenterPoint - pp; + double halfPlane = delta.Dot(this->Normal); + delta = delta - halfPlane * this->Normal; + double outOfDisk = delta.Norm() - this->Radius; + double outOfPlane = halfPlane < 0. ? -halfPlane : halfPlane; + // outOfPlane is >= 0. + // outOfDisk is < 0. if inside, > 0 if outside. + // The signed distance is a combination of these two distances, with its + // sign modulated by the sign of outOfDisk. + double signedDist = std::copysign(std::sqrt(outOfDisk * outOfDisk + outOfPlane * outOfPlane), outOfDisk); + return signedDist; +} + +void vtkImplicitDisk::EvaluateGradient(double xx[3], double gg[3]) +{ + vtkVector3d pp(xx); + vtkVector3d delta = this->CenterPoint - pp; + double halfPlane = delta.Dot(this->Normal); + delta = delta - halfPlane * this->Normal; + double outOfDisk = delta.Norm() - this->Radius; + // outOfPlane is (0,0,0) on the plane with normal this->Normal passing + // through this->CenterPoint and unit length pointing away from this + // plane everywhere else in space. + vtkVector3d outOfPlane; + if (halfPlane > 0.) + { + outOfPlane = -this->Normal * halfPlane; + } + else + { + outOfPlane = this->Normal * halfPlane; + } + // inPlane = (0,0,0) at CenterPoint, unit length pointing away from disk center elswhere. + vtkVector3d inPlane = delta.Normalized() * (outOfDisk < 0 ? 0. : -outOfDisk); + vtkVector3d gradient = inPlane + outOfPlane; + gg[0] = gradient[0]; + gg[1] = gradient[1]; + gg[2] = gradient[2]; +} + +bool vtkImplicitDisk::SetRadius(double radius) +{ + if (radius <= 0.0) + { + return false; + } + this->Radius = radius; + return true; +} + +bool vtkImplicitDisk::SetCenterPoint(const vtkVector3d& pt) +{ + if (pt == this->CenterPoint) + { + return false; + } + this->CenterPoint = pt; + return true; +} + +bool vtkImplicitDisk::SetNormal(const vtkVector3d& normal) +{ + auto nn = normal.Normalized(); + if (nn == this->Normal) + { + return false; + } + this->Normal = nn; + return true; +} diff --git a/smtk/extension/vtk/source/vtkImplicitDisk.h b/smtk/extension/vtk/source/vtkImplicitDisk.h new file mode 100644 index 0000000000000000000000000000000000000000..eafccf9b8b2be606de5bf3d12fe2af1b9fcb319e --- /dev/null +++ b/smtk/extension/vtk/source/vtkImplicitDisk.h @@ -0,0 +1,121 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef vtkImplicitDisk_h +#define vtkImplicitDisk_h + +#include "smtk/extension/vtk/source/vtkSMTKSourceExtModule.h" // For export macro +#include "vtkImplicitFunction.h" +#include "vtkNew.h" +#include "vtkVector.h" + +class vtkCone; +class vtkPlane; +class vtkTransform; + +/** + * @class vtkImplicitDisk + * @brief Generate an implicit function whose 0-isocontour + * is a planar disk. + * + * vtkImplicitDisk creates a signed distance function for a + * disk whose center lies at a given point with the specified + * normal vector and radius. + * + * By default, point 1 (the center) lies at the origin with radius 0.5 + * and with a z-normal (0, 0, 1). + */ +class VTKSMTKSOURCEEXT_EXPORT vtkImplicitDisk : public vtkImplicitFunction +{ +public: + vtkTypeMacro(vtkImplicitDisk, vtkImplicitFunction); + void PrintSelf(ostream& os, vtkIndent indent) override; + + vtkImplicitDisk(const vtkImplicitDisk&) = delete; + vtkImplicitDisk& operator=(const vtkImplicitDisk&) = delete; + + ///@{ + /** + * Evaluate our implicit function. + */ + using vtkImplicitFunction::EvaluateFunction; + double EvaluateFunction(double xx[3]) override; + ///@} + + /** + * Evaluate gradient of boolean combination. + */ + void EvaluateGradient(double xx[3], double gg[3]) override; + + /** + * Construct with default parameters. + */ + static vtkImplicitDisk* New(); + + //@{ + /** + * Set/get the radius at the bottom of the cone. + * + * It must be non-negative. + */ + virtual bool SetRadius(double radius); + vtkGetMacro(Radius, double); + //@} + + //@{ + /** + * Set/get the bottom point of the cone. + * The default is 0,0,0. + */ + virtual bool SetCenterPoint(const vtkVector3d& pt); + virtual bool SetCenterPoint(double x, double y, double z) + { + return this->SetCenterPoint(vtkVector3d(x, y, z)); + } + vtkGetMacro(CenterPoint, vtkVector3d); + //@} + + //@{ + /** + * Set/get the top point of the cone. + * The default is 0,0,0. + */ + virtual bool SetNormal(const vtkVector3d& pt); + virtual bool SetNormal(double x, double y, double z) + { + return this->SetNormal(vtkVector3d(x, y, z)); + } + virtual bool SetNormal(double* xyz) VTK_SIZEHINT(3) + { + return this->SetNormal(vtkVector3d(xyz[0], xyz[1], xyz[2])); + } + vtkGetMacro(Normal, vtkVector3d); + //@} + +protected: + vtkImplicitDisk(); + ~vtkImplicitDisk() override; + + /// Update the internal implicits and mark this object as modified. + /// + /// This is invoked by SetCenterPoint, SetRadius, SetNormal. + void UpdateImplicit(); + + // vtkNew<vtkCone> InfiniteCone; + // vtkNew<vtkTransform> ConeTransform; + + vtkVector3d CenterPoint; + double Radius{ 0.5 }; + // vtkNew<vtkPlane> BottomPlane; + + vtkVector3d Normal; + // vtkNew<vtkPlane> TopPlane; +}; + +#endif diff --git a/smtk/extension/vtk/widgets/CMakeLists.txt b/smtk/extension/vtk/widgets/CMakeLists.txt index 77ad19215891cba1392c637f48b8bdf8331007d1..1eaa17d5ffe6ecc03c989647c0d55140cb613d22 100644 --- a/smtk/extension/vtk/widgets/CMakeLists.txt +++ b/smtk/extension/vtk/widgets/CMakeLists.txt @@ -1,6 +1,8 @@ set(classes vtkConeRepresentation vtkConeWidget + vtkDiskRepresentation + vtkDiskWidget vtkSBFunctionParser vtkSMTKArcRepresentation) diff --git a/smtk/extension/vtk/widgets/vtkDiskRepresentation.cxx b/smtk/extension/vtk/widgets/vtkDiskRepresentation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..daae7177e40485468b132f5bd6e8e74c8dfbc6d4 --- /dev/null +++ b/smtk/extension/vtk/widgets/vtkDiskRepresentation.cxx @@ -0,0 +1,1030 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/vtk/widgets/vtkDiskRepresentation.h" +#include "smtk/extension/vtk/source/vtkDisk.h" +#include "smtk/extension/vtk/source/vtkImplicitDisk.h" + +#include "vtkActor.h" +#include "vtkAssemblyNode.h" +#include "vtkAssemblyPath.h" +#include "vtkBox.h" +#include "vtkCallbackCommand.h" +#include "vtkCamera.h" +#include "vtkCellArray.h" +#include "vtkCellPicker.h" +#include "vtkCommand.h" +#include "vtkDiskSource.h" +#include "vtkDoubleArray.h" +#include "vtkGlyph3DMapper.h" +#include "vtkHardwarePicker.h" +#include "vtkImageData.h" +#include "vtkInteractorObserver.h" +#include "vtkLineSource.h" +#include "vtkLookupTable.h" +#include "vtkMath.h" +#include "vtkObjectFactory.h" +#include "vtkOutlineFilter.h" +#include "vtkPickingManager.h" +#include "vtkPlane.h" +#include "vtkPointData.h" +#include "vtkPoints.h" +#include "vtkPolyData.h" +#include "vtkPolyDataMapper.h" +#include "vtkProperty.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" +#include "vtkSmartPointer.h" +#include "vtkSphereSource.h" +#include "vtkTransform.h" +#include "vtkTubeFilter.h" +#include "vtkVectorOperators.h" +#include "vtkWindow.h" + +#include <algorithm> +#include <cfloat> //for FLT_EPSILON + +vtkStandardNewMacro(vtkDiskRepresentation); + +vtkDiskRepresentation::vtkDiskRepresentation() +{ + this->HandleSize = 7.5; + this->Disk->SetResolution(128); + + // Set up the initial properties + this->CreateDefaultProperties(); + + for (int ii = 0; ii < NumberOfElements; ++ii) + { + this->Elements[ii].Actor->SetMapper(this->Elements[ii].Mapper); + if (ii <= DiskFace) + { + this->Elements[ii].Actor->SetProperty(this->DiskProperty); + } + else if (ii <= DiskEdge) + { + this->Elements[ii].Actor->SetProperty(this->EdgeProperty); + } + else + { + this->Elements[ii].Actor->SetProperty(this->HandleProperty); + } + } + + // Set up the pipelines for the visual elements + this->AxisTuber->SetInputConnection(this->Disk->GetOutputPort(vtkDisk::OutputPorts::DiskNormal)); + this->AxisTuber->SetNumberOfSides(12); + this->Elements[DiskNormal].Mapper->SetInputConnection(this->AxisTuber->GetOutputPort()); + this->Elements[DiskNormal].Actor->SetMapper(this->Elements[DiskNormal].Mapper); + + this->Elements[DiskFace].Mapper->SetInputConnection( + this->Disk->GetOutputPort(vtkDisk::OutputPorts::DiskFace)); + + this->Elements[DiskEdge].Mapper->SetInputConnection( + this->Disk->GetOutputPort(vtkDisk::OutputPorts::DiskEdge)); + + // Create the endpoint geometry source + this->Sphere->SetThetaResolution(16); + this->Sphere->SetPhiResolution(8); + + this->CenterVertexMapper->SetSourceConnection(this->Sphere->GetOutputPort()); + this->CenterVertexMapper->SetInputConnection( + this->Disk->GetOutputPort(vtkDisk::OutputPorts::CenterVertex)); + this->Elements[CenterVertex].Actor->SetMapper(this->CenterVertexMapper); + + // Define the point coordinates + double bounds[6]; + bounds[0] = -0.5; + bounds[1] = 0.5; + bounds[2] = -0.5; + bounds[3] = 0.5; + bounds[4] = -0.5; + bounds[5] = 0.5; + + // Initial creation of the widget, serves to initialize it + this->PlaceWidget(bounds); + + //Manage the picking stuff + this->Picker->SetTolerance(0.005); + for (int ii = DiskNormal; ii < NumberOfElements; ++ii) + { + this->Picker->AddPickList(this->Elements[ii].Actor); + } + this->Picker->PickFromListOn(); + + this->FacePicker->SetTolerance(0.005); + for (int ii = DiskFace; ii <= DiskFace; ++ii) + { + this->FacePicker->AddPickList(this->Elements[ii].Actor); + } + this->FacePicker->PickFromListOn(); + + this->RepresentationState = vtkDiskRepresentation::Outside; +} + +vtkDiskRepresentation::~vtkDiskRepresentation() = default; + +bool vtkDiskRepresentation::SetCenter(double x, double y, double z) +{ + return this->SetCenter(vtkVector3d(x, y, z)); +} + +bool vtkDiskRepresentation::SetCenter(const vtkVector3d& pt) +{ + vtkVector3d p0(this->Disk->GetCenterPoint()); + + vtkVector3d temp(pt); // Because vtkSetVectorMacro is not const correct. + if (p0 == pt) + { + // If pt is already the existing value, do nothing. + return false; + } + + this->Disk->SetCenterPoint(temp.GetData()); + this->Modified(); + return true; +} + +vtkVector3d vtkDiskRepresentation::GetCenter() const +{ + return vtkVector3d(this->Disk->GetCenterPoint()); +} + +double* vtkDiskRepresentation::GetCenterPoint() +{ + return this->Disk->GetCenterPoint(); +} + +bool vtkDiskRepresentation::SetNormal(double x, double y, double z) +{ + return this->SetNormal(vtkVector3d(x, y, z)); +} + +bool vtkDiskRepresentation::SetNormal(const vtkVector3d& nm) +{ + auto* norm = this->Disk->GetNormal(); + vtkVector3d dnorm(norm[0], norm[1], norm[2]); + if (dnorm == nm) + { + return false; + } + this->Disk->SetNormal(nm.GetData()); + return true; +} + +vtkVector3d vtkDiskRepresentation::GetNormal() const +{ + double* nm = this->Disk->GetNormal(); + return vtkVector3d(nm[0], nm[1], nm[2]); +} + +double* vtkDiskRepresentation::GetNormalVector() +{ + return this->Disk->GetNormal(); +} + +bool vtkDiskRepresentation::SetRadius(double r) +{ + double prev = this->Disk->GetRadius(); + if (prev == r) + { + return false; + } + this->Disk->SetRadius(r); + this->Modified(); + return true; +} + +double vtkDiskRepresentation::GetRadius() const +{ + return this->Disk->GetRadius(); +} + +int vtkDiskRepresentation::ComputeInteractionState(int X, int Y, int /*modify*/) +{ + // See if anything has been selected + vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->Picker); + + // The second picker may need to be called. This is done because the disk face + // may obstruct things that can be picked; thus the disk face is the selection + // of last resort. This allows users to rotate the normal vector even from behind + // the disk + if (path == nullptr) + { + this->FacePicker->Pick(X, Y, 0., this->Renderer); + path = this->FacePicker->GetPath(); + } + + if (path == nullptr) // Nothing picked + { + this->SetRepresentationState(vtkDiskRepresentation::Outside); + this->InteractionState = vtkDiskRepresentation::Outside; + return this->InteractionState; + } + + // Something picked, continue + this->ValidPick = 1; + + // Depending on the interaction state (set by the widget) we modify + // this state based on what is picked. + if (this->InteractionState == vtkDiskRepresentation::Moving) + { + vtkProp* prop = path->GetFirstNode()->GetViewProp(); + if (prop == this->Elements[DiskNormal].Actor) + { + this->InteractionState = vtkDiskRepresentation::RotatingNormal; + this->SetRepresentationState(vtkDiskRepresentation::RotatingNormal); + } + else if (prop == this->Elements[DiskEdge].Actor) + { + this->InteractionState = vtkDiskRepresentation::AdjustingRadius; + this->SetRepresentationState(vtkDiskRepresentation::AdjustingRadius); + } + else if (prop == this->Elements[CenterVertex].Actor) + { + this->InteractionState = vtkDiskRepresentation::MovingCenterVertex; + this->SetRepresentationState(vtkDiskRepresentation::MovingCenterVertex); + } + // Better to push the face along the normal than translate in view plane. + // else if (prop == this->Elements[DiskFace].Actor) + // { + // this->InteractionState = vtkDiskRepresentation::MovingWhole; + // this->SetRepresentationState(vtkDiskRepresentation::MovingWhole); + // } + else if (prop == this->Elements[DiskFace].Actor) + { + this->InteractionState = vtkDiskRepresentation::PushingDiskFace; + this->SetRepresentationState(vtkDiskRepresentation::PushingDiskFace); + } + else + { + this->InteractionState = vtkDiskRepresentation::Outside; + this->SetRepresentationState(vtkDiskRepresentation::Outside); + } + } + else + { + this->InteractionState = vtkDiskRepresentation::Outside; + } + + return this->InteractionState; +} + +void vtkDiskRepresentation::SetRepresentationState(int state) +{ + if (this->RepresentationState == state) + { + return; + } + + // Clamp the state + state = + (state < vtkDiskRepresentation::Outside + ? vtkDiskRepresentation::Outside + : (state > vtkDiskRepresentation::RotatingNormal ? vtkDiskRepresentation::RotatingNormal : state)); + + this->RepresentationState = state; + this->Modified(); + +#if 0 + // For debugging, it is handy to see state changes: + std::cout + << " State " + << vtkDiskRepresentation::InteractionStateToString(this->RepresentationState) + << "\n"; +#endif + + this->HighlightElement(NumberOfElements, 0); // Turn everything off + if (state == vtkDiskRepresentation::RotatingNormal) + { + this->HighlightAxis(1); + } + else if (state == vtkDiskRepresentation::PushingDiskFace) + { + this->HighlightDisk(1); + } + else if (state == vtkDiskRepresentation::AdjustingRadius) + { + this->HighlightCurve(1); + } + else if (state == vtkDiskRepresentation::MovingWhole) + { + this->HighlightCurve(1); + this->HighlightDisk(1); + this->HighlightAxis(1); + this->HighlightHandle(1); + } + else + { + this->HighlightAxis(0); + this->HighlightDisk(0); + // this->HighlightOutline(0); + } +} + +void vtkDiskRepresentation::StartWidgetInteraction(double e[2]) +{ + this->StartEventPosition[0] = e[0]; + this->StartEventPosition[1] = e[1]; + this->StartEventPosition[2] = 0.0; + + this->LastEventPosition[0] = e[0]; + this->LastEventPosition[1] = e[1]; + this->LastEventPosition[2] = 0.0; +} + +void vtkDiskRepresentation::WidgetInteraction(double e[2]) +{ + // Do different things depending on state + // Calculations everybody does + double focalPoint[4], pickPoint[4], prevPickPoint[4]; + double z, vpn[3]; + + vtkCamera* camera = this->Renderer->GetActiveCamera(); + if (!camera) + { + return; + } + + // Compute the two points defining the motion vector + double pos[3]; + this->Picker->GetPickPosition(pos); + vtkInteractorObserver::ComputeWorldToDisplay(this->Renderer, pos[0], pos[1], pos[2], focalPoint); + z = focalPoint[2]; + vtkInteractorObserver::ComputeDisplayToWorld( + this->Renderer, this->LastEventPosition[0], this->LastEventPosition[1], z, prevPickPoint); + vtkInteractorObserver::ComputeDisplayToWorld(this->Renderer, e[0], e[1], z, pickPoint); + + // Process the motion + if (this->InteractionState == vtkDiskRepresentation::AdjustingRadius) + { + this->AdjustRadius(e[0], e[1], prevPickPoint, pickPoint); + } + else if (this->InteractionState == vtkDiskRepresentation::MovingCenterVertex) + { + this->TranslateCenterInPlane(prevPickPoint, pickPoint); + } + else if (this->InteractionState == vtkDiskRepresentation::MovingWhole) + { + this->TranslateCenter(prevPickPoint, pickPoint); + } + else if (this->InteractionState == vtkDiskRepresentation::PushingDiskFace) + { + this->PushFace(prevPickPoint, pickPoint); + } + else if (this->InteractionState == vtkDiskRepresentation::RotatingNormal) + { + camera->GetViewPlaneNormal(vpn); + this->Rotate(e[0], e[1], prevPickPoint, pickPoint, vpn); + } + + this->LastEventPosition[0] = e[0]; + this->LastEventPosition[1] = e[1]; + this->LastEventPosition[2] = 0.0; +} + +void vtkDiskRepresentation::EndWidgetInteraction(double /*newEventPos*/[2]) +{ + this->SetRepresentationState(vtkDiskRepresentation::Outside); +} + +double* vtkDiskRepresentation::GetBounds() +{ + this->BuildRepresentation(); + this->BoundingBox->SetBounds(this->Elements[DiskFace].Actor->GetBounds()); + for (int ii = DiskFace; ii < NumberOfElements; ++ii) + { + this->BoundingBox->AddBounds(this->Elements[ii].Actor->GetBounds()); + } + + return this->BoundingBox->GetBounds(); +} + +void vtkDiskRepresentation::GetActors(vtkPropCollection* pc) +{ + for (int ii = 0; ii < NumberOfElements; ++ii) + { + this->Elements[ii].Actor->GetActors(pc); + } +} + +void vtkDiskRepresentation::ReleaseGraphicsResources(vtkWindow* w) +{ + for (int ii = 0; ii < NumberOfElements; ++ii) + { + this->Elements[ii].Actor->ReleaseGraphicsResources(w); + } +} + +int vtkDiskRepresentation::RenderOpaqueGeometry(vtkViewport* v) +{ + int count = 0; + this->BuildRepresentation(); + for (int ii = DiskNormal; ii < NumberOfElements; ++ii) + { + count += this->Elements[ii].Actor->RenderOpaqueGeometry(v); + } + + if (this->DrawDisk) + { + for (int ii = DiskFace; ii < DiskNormal; ++ii) + { + count += this->Elements[ii].Actor->RenderOpaqueGeometry(v); + } + } + + return count; +} + +int vtkDiskRepresentation::RenderTranslucentPolygonalGeometry(vtkViewport* v) +{ + int count = 0; + this->BuildRepresentation(); + for (int ii = DiskNormal; ii < NumberOfElements; ++ii) + { + count += this->Elements[ii].Actor->RenderTranslucentPolygonalGeometry(v); + } + + if (this->DrawDisk) + { + for (int ii = DiskFace; ii < DiskNormal; ++ii) + { + count += this->Elements[ii].Actor->RenderTranslucentPolygonalGeometry(v); + } + } + + return count; +} + +vtkTypeBool vtkDiskRepresentation::HasTranslucentPolygonalGeometry() +{ + int result = 0; + for (int ii = DiskNormal; ii < NumberOfElements; ++ii) + { + result |= this->Elements[ii].Actor->HasTranslucentPolygonalGeometry(); + } + + if (this->DrawDisk) + { + for (int ii = DiskFace; ii < DiskNormal; ++ii) + { + result |= this->Elements[ii].Actor->HasTranslucentPolygonalGeometry(); + } + } + + return result; +} + +void vtkDiskRepresentation::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + os << indent << "Resolution: " << this->Resolution << "\n"; + + if (this->HandleProperty) + { + os << indent << "Handle Property: " << this->HandleProperty << "\n"; + } + else + { + os << indent << "Handle Property: (none)\n"; + } + if (this->SelectedHandleProperty) + { + os << indent << "Selected Handle Property: " << this->SelectedHandleProperty << "\n"; + } + else + { + os << indent << "Selected Handle Property: (none)\n"; + } + + if (this->DiskProperty) + { + os << indent << "Disk Property: " << this->DiskProperty << "\n"; + } + else + { + os << indent << "Disk Property: (none)\n"; + } + if (this->SelectedDiskProperty) + { + os << indent << "Selected Disk Property: " << this->SelectedDiskProperty << "\n"; + } + else + { + os << indent << "Selected Disk Property: (none)\n"; + } + + if (this->EdgeProperty) + { + os << indent << "Edge Property: " << this->EdgeProperty << "\n"; + } + else + { + os << indent << "Edge Property: (none)\n"; + } + + os << indent << "Along X Axis: " << (this->AlongXAxis ? "On" : "Off") << "\n"; + os << indent << "Along Y Axis: " << (this->AlongYAxis ? "On" : "Off") << "\n"; + os << indent << "ALong Z Axis: " << (this->AlongZAxis ? "On" : "Off") << "\n"; + + os << indent << "Tubing: " << (this->Tubing ? "On" : "Off") << "\n"; + os << indent << "Draw Disk: " << (this->DrawDisk ? "On" : "Off") << "\n"; + os << indent << "Bump Distance: " << this->BumpDistance << "\n"; + + os << indent << "Representation State: " + << vtkDiskRepresentation::InteractionStateToString(this->RepresentationState) << "\n"; + + // this->InteractionState is printed in superclass + // this is commented to avoid PrintSelf errors +} + +void vtkDiskRepresentation::HighlightElement(ElementType elem, int highlight) +{ + switch (elem) + { + case DiskFace: + this->HighlightDisk(highlight); + break; + case DiskNormal: + this->HighlightAxis(highlight); + break; + case DiskEdge: + this->HighlightCurve(highlight); + break; + case CenterVertex: + this->HighlightHandle(highlight); + break; + case NumberOfElements: + // Set everything to the given highlight state. + this->HighlightAxis(highlight); + this->HighlightDisk(highlight); + this->HighlightCurve(highlight); + this->HighlightHandle(highlight); + break; + } +} + +void vtkDiskRepresentation::HighlightDisk(int highlight) +{ + if (highlight) + { + this->Elements[DiskFace].Actor->SetProperty(this->SelectedDiskProperty); + } + else + { + this->Elements[DiskFace].Actor->SetProperty(this->DiskProperty); + } +} + +void vtkDiskRepresentation::HighlightAxis(int highlight) +{ + if (highlight) + { + this->Elements[DiskNormal].Actor->SetProperty(this->SelectedEdgeProperty); + } + else + { + this->Elements[DiskNormal].Actor->SetProperty(this->EdgeProperty); + } +} + +void vtkDiskRepresentation::HighlightCurve(int highlight) +{ + ElementType elem = DiskEdge; + if (highlight) + { + this->Elements[elem].Actor->SetProperty(this->SelectedEdgeProperty); + } + else + { + this->Elements[elem].Actor->SetProperty(this->EdgeProperty); + } +} + +void vtkDiskRepresentation::HighlightHandle(int highlight) +{ + ElementType elem = CenterVertex; + if (highlight) + { + this->Elements[elem].Actor->SetProperty(this->SelectedHandleProperty); + } + else + { + this->Elements[elem].Actor->SetProperty(this->HandleProperty); + } +} + +void vtkDiskRepresentation::Rotate(double X, double Y, double* p1, double* p2, double* vpn) +{ + double v[3]; //vector of motion + double axis[3]; //axis of rotation + double theta; //rotation angle + + // mouse motion vector in world space + v[0] = p2[0] - p1[0]; + v[1] = p2[1] - p1[1]; + v[2] = p2[2] - p1[2]; + + vtkVector3d cp0(this->Disk->GetCenterPoint()); + vtkVector3d cp1(this->Disk->GetNormal()); + vtkVector3d center = cp0; + + // Create axis of rotation and angle of rotation + vtkMath::Cross(vpn, v, axis); + if (vtkMath::Normalize(axis) == 0.0) + { + return; + } + int* size = this->Renderer->GetSize(); + double l2 = (X - this->LastEventPosition[0]) * (X - this->LastEventPosition[0]) + + (Y - this->LastEventPosition[1]) * (Y - this->LastEventPosition[1]); + theta = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1])); + + // Manipulate the transform to reflect the rotation + this->Transform->Identity(); + this->Transform->Translate(center[0], center[1], center[2]); + this->Transform->RotateWXYZ(theta, axis); + this->Transform->Translate(-center[0], -center[1], -center[2]); + + // cp0 is the center of rotation, it will not move. + // this->Transform->TransformPoint(cp0.GetData(), cp0.GetData()); + // this->Transform->TransformPoint(cp1.GetData(), cp1.GetData()); + this->Transform->TransformVector(cp1.GetData(), cp1.GetData()); + + // this->Disk->SetCenterPoint(cp0.GetData()); + // this->Disk->SetTopPoint(cp1.GetData()); + this->Disk->SetNormal(cp1.GetData()); +} + +void vtkDiskRepresentation::PushFace(double* p1, double* p2) +{ + //Get the motion vector + vtkVector3d v; + v[0] = p2[0] - p1[0]; + v[1] = p2[1] - p1[1]; + v[2] = p2[2] - p1[2]; + + vtkVector3d cp0(this->Disk->GetCenterPoint()); + vtkVector3d cp1(this->Disk->GetNormal()); + vtkVector3d axis = cp1; + axis.Normalize(); + double bump = v.Dot(axis); + + this->Disk->SetCenterPoint((cp0 + bump * axis).GetData()); +} + +// Loop through all points and translate them +void vtkDiskRepresentation::AdjustRadius(double /*X*/, double Y, double* p1, double* p2) +{ + if (Y == this->LastEventPosition[1]) + { + return; + } + + double dr; + double radius = this->Disk->GetRadius(); + double v[3]; //vector of motion + v[0] = p2[0] - p1[0]; + v[1] = p2[1] - p1[1]; + v[2] = p2[2] - p1[2]; + double l = sqrt(vtkMath::Dot(v, v)); + + dr = l / 4; + if (Y < this->LastEventPosition[1]) + { + dr *= -1.0; + } + + double nextRadius = radius + dr; + + if (nextRadius < 1e-30) + { + nextRadius = 1e-30; + } + else if (nextRadius < 0.) + { + nextRadius = -nextRadius; + } + this->Disk->SetRadius(nextRadius); + this->BuildRepresentation(); +} + +// Loop through all points and translate them +void vtkDiskRepresentation::TranslateCenter(double* p1, double* p2) +{ + //Get the motion vector + vtkVector3d v; + v[0] = p2[0] - p1[0]; + v[1] = p2[1] - p1[1]; + v[2] = p2[2] - p1[2]; + + vtkVector3d cp0(this->Disk->GetCenterPoint()); + + this->Disk->SetCenterPoint((cp0 + v).GetData()); + this->BuildRepresentation(); +} + +// Translate the center point within the plane of the disk. +void vtkDiskRepresentation::TranslateCenterInPlane(double* p1, double* p2) +{ + // Get the motion vector + vtkVector3d v; + v[0] = p2[0] - p1[0]; + v[1] = p2[1] - p1[1]; + v[2] = p2[2] - p1[2]; + + vtkVector3d cp(this->Disk->GetCenterPoint()); + vtkVector3d norm(this->Disk->GetNormal()); + + v = v - v.Dot(norm) * norm; + + this->Disk->SetCenterPoint((cp + v).GetData()); + this->BuildRepresentation(); +} + + +// Loop through all points and translate them +void vtkDiskRepresentation::TranslateHandle(double* p1, double* p2) +{ + //Get the motion vector + vtkVector3d v; + v[0] = p2[0] - p1[0]; + v[1] = p2[1] - p1[1]; + v[2] = p2[2] - p1[2]; + + vtkVector3d handle(this->Disk->GetCenterPoint()); + + this->Disk->SetCenterPoint((handle + v).GetData()); + this->BuildRepresentation(); +} + +void vtkDiskRepresentation::SizeHandles() +{ + double radius = + this->vtkWidgetRepresentation::SizeHandlesInPixels(1.5, this->Sphere->GetCenter()); + + this->Sphere->SetRadius(radius); + this->AxisTuber->SetRadius(0.25 * radius); +} + +void vtkDiskRepresentation::CreateDefaultProperties() +{ + this->HandleProperty->SetColor(1., 1., 1.); + + this->EdgeProperty->SetColor(1., 1., 1.); + this->EdgeProperty->SetLineWidth(3); + + this->DiskProperty->SetColor(1., 1., 1.); + this->DiskProperty->SetOpacity(0.5); + + this->SelectedHandleProperty->SetColor(0.0, 1.0, 0.); + this->SelectedHandleProperty->SetAmbient(1.0); + + this->SelectedEdgeProperty->SetColor(0., 1.0, 0.0); + this->SelectedEdgeProperty->SetLineWidth(3); + + this->SelectedDiskProperty->SetColor(0., 1., 0.); + this->SelectedDiskProperty->SetOpacity(0.5); +} + +void vtkDiskRepresentation::PlaceWidget(double bds[6]) +{ + vtkVector3d lo(bds[0], bds[2], bds[4]); + vtkVector3d hi(bds[1], bds[3], bds[5]); + vtkVector3d md = 0.5 * (lo + hi); + + this->InitialLength = (hi - lo).Norm(); + + if (this->AlongYAxis) + { + this->Disk->SetCenterPoint(md[0], lo[1], md[2]); + double radius = hi[2] - md[2] > hi[0] - md[0] ? hi[0] - md[0] : hi[2] - md[2]; + this->Disk->SetRadius(radius); + } + else if (this->AlongZAxis) + { + this->Disk->SetCenterPoint(md[0], md[1], lo[2]); + double radius = hi[0] - md[0] > hi[1] - md[1] ? hi[1] - md[1] : hi[0] - md[0]; + this->Disk->SetRadius(radius); + } + else //default or x-normal + { + this->Disk->SetCenterPoint(lo[0], md[1], md[2]); + double radius = hi[2] - md[2] > hi[1] - md[1] ? hi[1] - md[1] : hi[2] - md[2]; + this->Disk->SetRadius(radius); + } + + this->ValidPick = 1; // since we have positioned the widget successfully + this->BuildRepresentation(); +} + +void vtkDiskRepresentation::SetDrawDisk(vtkTypeBool drawCyl) +{ + if (drawCyl == this->DrawDisk) + { + return; + } + + this->Modified(); + this->DrawDisk = drawCyl; + this->BuildRepresentation(); +} + +void vtkDiskRepresentation::SetAlongXAxis(vtkTypeBool var) +{ + if (this->AlongXAxis != var) + { + this->AlongXAxis = var; + this->Modified(); + } + if (var) + { + this->AlongYAxisOff(); + this->AlongZAxisOff(); + } +} + +void vtkDiskRepresentation::SetAlongYAxis(vtkTypeBool var) +{ + if (this->AlongYAxis != var) + { + this->AlongYAxis = var; + this->Modified(); + } + if (var) + { + this->AlongXAxisOff(); + this->AlongZAxisOff(); + } +} + +void vtkDiskRepresentation::SetAlongZAxis(vtkTypeBool var) +{ + if (this->AlongZAxis != var) + { + this->AlongZAxis = var; + this->Modified(); + } + if (var) + { + this->AlongXAxisOff(); + this->AlongYAxisOff(); + } +} + +void vtkDiskRepresentation::GetDisk(vtkImplicitDisk* disk) +{ + if (disk == nullptr) + { + return; + } + + disk->SetCenterPoint(this->GetCenter()); + disk->SetRadius(this->Disk->GetRadius()); + disk->SetNormal(this->Disk->GetNormal()); +} + +void vtkDiskRepresentation::UpdatePlacement() +{ + this->BuildRepresentation(); +} + +void vtkDiskRepresentation::BumpDisk(int dir, double factor) +{ + // Compute the distance + double d = this->InitialLength * this->BumpDistance * factor; + + // Push the cylinder + this->PushDisk((dir > 0 ? d : -d)); +} + +void vtkDiskRepresentation::PushDisk(double d) +{ + vtkCamera* camera = this->Renderer->GetActiveCamera(); + if (!camera) + { + return; + } + vtkVector3d vpn; + vtkVector3d p0(this->Disk->GetCenterPoint()); + vtkVector3d p1(this->Disk->GetNormal()); + camera->GetViewPlaneNormal(vpn.GetData()); + + p0 = p0 + d * vpn; + + this->Disk->SetCenterPoint(p0.GetData()); + this->BuildRepresentation(); +} + +bool vtkDiskRepresentation::PickNormal(int X, int Y, bool snapToMeshPoint) +{ + this->HardwarePicker->SetSnapToMeshPoint(snapToMeshPoint); + vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HardwarePicker); + if (path == nullptr) // actors of renderer were not touched + { + if (this->PickCameraFocalInfo) + { + double normal[3]; + this->HardwarePicker->GetPickNormal(normal); + this->SetNormal(normal); + this->BuildRepresentation(); + } + return this->PickCameraFocalInfo; + } + else // actors of renderer were touched + { + double normal[3]; + this->HardwarePicker->GetPickNormal(normal); + if (!std::isnan(normal[0]) || !std::isnan(normal[1]) || !std::isnan(normal[2])) + { + this->SetNormal(normal); + this->BuildRepresentation(); + return true; + } + else + { + return false; + } + } +} + +std::string vtkDiskRepresentation::InteractionStateToString(int state) +{ + switch (state) + { + case Outside: + return "Outside"; + break; + case Moving: + return "Moving"; + break; + case PushingDiskFace: + return "PushingDiskFace"; + break; + case AdjustingRadius: + return "AdjustingRadius"; + break; + case MovingCenterVertex: + return "MovingCenterVertex"; + break; + case MovingWhole: + return "MovingWhole"; + break; + case RotatingNormal: + return "RotatingNormal"; + break; + default: + break; + } + return "Invalid"; +} + +void vtkDiskRepresentation::BuildRepresentation() +{ + if (!this->Renderer || !this->Renderer->GetRenderWindow()) + { + return; + } + + vtkInformation* info = this->GetPropertyKeys(); + for (int ii = 0; ii < NumberOfElements; ++ii) + { + this->Elements[ii].Actor->SetPropertyKeys(info); + } + + if ( + this->GetMTime() > this->BuildTime || this->Disk->GetMTime() > this->BuildTime || + this->Renderer->GetRenderWindow()->GetMTime() > this->BuildTime) + { + // Control the look of the edges + if (this->Tubing) + { + this->Elements[DiskNormal].Mapper->SetInputConnection(this->AxisTuber->GetOutputPort()); + } + else + { + this->Elements[DiskNormal].Mapper->SetInputConnection( + this->Disk->GetOutputPort(vtkDisk::OutputPorts::DiskNormal)); + } + + this->SizeHandles(); + this->BuildTime.Modified(); + } +} + +void vtkDiskRepresentation::RegisterPickers() +{ + vtkPickingManager* pm = this->GetPickingManager(); + if (!pm) + { + return; + } + pm->AddPicker(this->Picker, this); +} diff --git a/smtk/extension/vtk/widgets/vtkDiskRepresentation.h b/smtk/extension/vtk/widgets/vtkDiskRepresentation.h new file mode 100644 index 0000000000000000000000000000000000000000..b9a10f798e3027535db1ddb7d7ff8e6dddde300e --- /dev/null +++ b/smtk/extension/vtk/widgets/vtkDiskRepresentation.h @@ -0,0 +1,383 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef vtkDiskRepresentation_h +#define vtkDiskRepresentation_h + +#include "smtk/extension/vtk/widgets/vtkSMTKWidgetsExtModule.h" // For export macro +#include "vtkNew.h" +#include "vtkVector.h" +#include "vtkWidgetRepresentation.h" + +#include <array> + +class vtkActor; +class vtkPolyDataMapper; +class vtkHardwarePicker; +class vtkCellPicker; +class vtkDisk; +class vtkGlyph3DMapper; +class vtkImplicitDisk; +class vtkSphereSource; +class vtkTubeFilter; +class vtkProperty; +class vtkImageData; +class vtkOutlineFilter; +class vtkFeatureEdges; +class vtkPolyData; +class vtkPolyDataAlgorithm; +class vtkTransform; +class vtkBox; +class vtkLookupTable; + +#define VTK_MAX_DISK_RESOLUTION 2048 + +/** + * @class vtkDiskRepresentation + * @brief defining the representation for a planar disk. + * + * This class is a concrete representation for the + * vtkDiskWidget. It represents a finite disk + * defined by a center point, a normal vector, and a radius. + * This disk representation can be manipulated by using the + * vtkDiskWidget. + * + * @sa + * vtkDiskWidget +*/ +class VTKSMTKWIDGETSEXT_EXPORT vtkDiskRepresentation : public vtkWidgetRepresentation +{ +public: + /** + * Instantiate the class. + */ + static vtkDiskRepresentation* New(); + + //@{ + /** + * Standard methods for the class. + */ + vtkTypeMacro(vtkDiskRepresentation, vtkWidgetRepresentation); + void PrintSelf(ostream& os, vtkIndent indent) override; + //@} + + vtkDiskRepresentation(const vtkDiskRepresentation&) = delete; + vtkDiskRepresentation& operator=(const vtkDiskRepresentation&) = delete; + + //@{ + /** + * Set/get the center of the disk. + */ + bool SetCenter(double x, double y, double z); + bool SetCenter(const vtkVector3d& pt); + vtkVector3d GetCenter() const; + void SetCenterPoint(double* x) VTK_SIZEHINT(3) { this->SetCenter(x[0], x[1], x[2]); } + double* GetCenterPoint() VTK_SIZEHINT(3); + //@} + + //@{ + /** + * Set/get the normal of the disk. + */ + bool SetNormal(double x, double y, double z); + bool SetNormal(const vtkVector3d& nm); + vtkVector3d GetNormal() const; + void SetNormal(double* x) VTK_SIZEHINT(3) { this->SetNormal(x[0], x[1], x[2]); } + double* GetNormalVector() VTK_SIZEHINT(3); + //@} + + //@{ + /** + * Set/get the radius of the disk. + * + * Negative values are generally a bad idea but not prohibited at this point. + * They will result in bad surface normals, though. + */ + bool SetRadius(double r); + double GetRadius() const; + //@} + + //@{ + /** + * Force the disk widget's normal to be aligned with one of the x-y-z axes. + * If one axis is set on, the other two will be set off. + * Remember that when the state changes, a ModifiedEvent is invoked. + * This can be used to snap the disk to the axes if it is originally + * not aligned. + */ + void SetAlongXAxis(vtkTypeBool); + vtkGetMacro(AlongXAxis, vtkTypeBool); + vtkBooleanMacro(AlongXAxis, vtkTypeBool); + void SetAlongYAxis(vtkTypeBool); + vtkGetMacro(AlongYAxis, vtkTypeBool); + vtkBooleanMacro(AlongYAxis, vtkTypeBool); + void SetAlongZAxis(vtkTypeBool); + vtkGetMacro(AlongZAxis, vtkTypeBool); + vtkBooleanMacro(AlongZAxis, vtkTypeBool); + //@} + + //@{ + /** + * Enable/disable the drawing of the disk. In some cases the disk + * interferes with the object that it is operating on (e.g., the + * disk interferes with the cut surface it produces resulting in + * z-buffer artifacts.) By default it is off. + */ + void SetDrawDisk(vtkTypeBool drawCyl); + vtkGetMacro(DrawDisk, vtkTypeBool); + vtkBooleanMacro(DrawDisk, vtkTypeBool); + //@} + + //@{ + /** + * Set/Get the resolution of the disk. This is the number of + * triangles used to approximate the circular face and line + * segments used to approximate the circular edge. + * A vtkDisk is used under the hood. + */ + vtkSetClampMacro(Resolution, int, 3, VTK_MAX_DISK_RESOLUTION); + vtkGetMacro(Resolution, int); + //@} + + //@{ + /** + * Turn on/off tubing of the wire outline of the cylinder + * intersection (against the bounding box). The tube thickens the + * line by wrapping with a vtkTubeFilter. + */ + vtkSetMacro(Tubing, vtkTypeBool); + vtkGetMacro(Tubing, vtkTypeBool); + vtkBooleanMacro(Tubing, vtkTypeBool); + //@} + + /** + * Get the implicit function for the disk. The user must provide an instance + * vtkImplicitDisk; upon return, it will be set to the difference between + * an infinite disk and the two cutting planes. + * The returned implicit can be used by a variety of filters + * to perform clipping, cutting, and selection of data. + */ + void GetDisk(vtkImplicitDisk* disk); + + /** + * Satisfies the superclass API. This will change the state of the widget + * to match changes that have been made to the underlying PolyDataSource. + */ + void UpdatePlacement(); + + //@{ + /** + * Get the properties used to render the center point + * when selected or not. + */ + vtkGetObjectMacro(HandleProperty, vtkProperty); + vtkGetObjectMacro(SelectedHandleProperty, vtkProperty); + //@} + + //@{ + /** + * Get the disk-face properties. The properties of the disk when selected + * and unselected can be manipulated. + */ + vtkGetObjectMacro(DiskProperty, vtkProperty); + vtkGetObjectMacro(SelectedDiskProperty, vtkProperty); + //@} + + //@{ + /** + * Get the property of the boundary edge and normal line. (This property also + * applies to the edges when tubed.) + */ + vtkGetObjectMacro(EdgeProperty, vtkProperty); + //@} + + //@{ + /** + * Methods to interface with the vtkDiskWidget. + */ + int ComputeInteractionState(int X, int Y, int modify = 0) override; + void PlaceWidget(double bounds[6]) override; + void BuildRepresentation() override; + void StartWidgetInteraction(double eventPos[2]) override; + void WidgetInteraction(double newEventPos[2]) override; + void EndWidgetInteraction(double newEventPos[2]) override; + //@} + + //@{ + /** + * Methods supporting the rendering process. + */ + double* GetBounds() override; + void GetActors(vtkPropCollection* pc) override; + void ReleaseGraphicsResources(vtkWindow*) override; + int RenderOpaqueGeometry(vtkViewport*) override; + int RenderTranslucentPolygonalGeometry(vtkViewport*) override; + vtkTypeBool HasTranslucentPolygonalGeometry() override; + //@} + + //@{ + /** + * Specify a translation distance used by the BumpDisk() method. Note that the + * distance is normalized; it is the fraction of the length of the bounding + * box of the wire outline. + */ + vtkSetClampMacro(BumpDistance, double, 0.000001, 1); + vtkGetMacro(BumpDistance, double); + //@} + + /** + * Translate the disk in the direction of the view vector by the + * specified BumpDistance. The dir parameter controls which + * direction the pushing occurs, either in the same direction as the + * view vector, or when negative, in the opposite direction. The factor + * controls what percentage of the bump is used. + */ + void BumpDisk(int dir, double factor); + + /** + * Push the disk the distance specified along the view + * vector. Positive values are in the direction of the view vector; + * negative values are in the opposite direction. The distance value + * is expressed in world coordinates. + */ + void PushDisk(double distance); + + /**\brief Set the disk normal to either the surface normal below the pointer + * or the opposite of the camera view vector (pointing back toward the camera). + */ + bool PickNormal(int X, int Y, bool snapToMeshPoint); + + // Manage the state of the widget + enum _InteractionState + { + Outside = 0, + Moving, + PushingDiskFace, + AdjustingRadius, + MovingCenterVertex, + MovingWhole, + RotatingNormal + }; + + static std::string InteractionStateToString(int); + + //@{ + /** + * The interaction state may be set from a widget (e.g., + * vtkImplicitCylinderWidget) or other object. This controls how the + * interaction with the widget proceeds. Normally this method is used as + * part of a handshaking process with the widget: First + * ComputeInteractionState() is invoked that returns a state based on + * geometric considerations (i.e., cursor near a widget feature), then + * based on events, the widget may modify this further. + */ + vtkSetClampMacro(InteractionState, int, Outside, RotatingNormal); + //@} + + //@{ + /** + * Sets the visual appearance of the representation based on the + * state it is in. This state is usually the same as InteractionState. + */ + virtual void SetRepresentationState(int); + vtkGetMacro(RepresentationState, int); + //@} + + /* + * Register internal Pickers within PickingManager + */ + void RegisterPickers() override; + +protected: + vtkDiskRepresentation(); + ~vtkDiskRepresentation() override; + + /// Visual elements of the representation. + enum ElementType + { + DiskFace = 0, + DiskNormal, + DiskEdge, + CenterVertex, + NumberOfElements + }; + + void HighlightElement(ElementType elem, int highlight); + + void HighlightDisk(int highlight); + void HighlightAxis(int highlight); + void HighlightCurve(int highlight); + void HighlightHandle(int highlight); + + // Methods to manipulate the disk + void Rotate(double X, double Y, double* p1, double* p2, double* vpn); + void TranslateDisk(double* p1, double* p2); + void PushFace(double* p1, double* p2); + void AdjustRadius(double X, double Y, double* p1, double* p2); + void TranslateCenter(double* p1, double* p2); + void TranslateCenterInPlane(double* p1, double* p2); + void TranslateHandle(double* p1, double* p2); + void SizeHandles(); + + void CreateDefaultProperties(); + + struct Element + { + vtkNew<vtkActor> Actor; + vtkNew<vtkPolyDataMapper> Mapper; + }; + + // Actors and mappers for all visual elements of the representation. + std::array<Element, NumberOfElements> Elements; + + int RepresentationState; + // Keep track of event positions + double LastEventPosition[3]; + // Controlling the push operation + double BumpDistance{ 0.01 }; + // Controlling ivars + vtkTypeBool AlongXAxis{ 0 }; + vtkTypeBool AlongYAxis{ 0 }; + vtkTypeBool AlongZAxis{ 0 }; + // The actual disk which is being manipulated + vtkNew<vtkDisk> Disk; + // The facet resolution for rendering purposes. + int Resolution{ 128 }; + vtkTypeBool DrawDisk{ 1 }; + vtkNew<vtkTubeFilter> AxisTuber; // Used to style edges. + vtkTypeBool Tubing{ 1 }; //control whether tubing is on + + // Source of center-point handle geometry + vtkNew<vtkSphereSource> Sphere; + + // Do the picking + vtkNew<vtkCellPicker> Picker; + vtkNew<vtkCellPicker> FacePicker; + vtkNew<vtkHardwarePicker> HardwarePicker; // For picking surface normals. + vtkTypeBool PickCameraFocalInfo{ true }; // Allow picking the camera projection direction. + + // Properties used to control the appearance of selected objects and + // the manipulator in general. + vtkNew<vtkProperty> HandleProperty; + vtkNew<vtkProperty> SelectedHandleProperty; + vtkNew<vtkProperty> DiskProperty; + vtkNew<vtkProperty> SelectedDiskProperty; + vtkNew<vtkProperty> EdgeProperty; + vtkNew<vtkProperty> SelectedEdgeProperty; + + // Support GetBounds() method + vtkNew<vtkBox> BoundingBox; + + // Overrides for the point-handle polydata mappers + vtkNew<vtkGlyph3DMapper> CenterVertexMapper; + + vtkNew<vtkTransform> Transform; +}; + +#endif diff --git a/smtk/extension/vtk/widgets/vtkDiskWidget.cxx b/smtk/extension/vtk/widgets/vtkDiskWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..da8c53c711aefe67cf9dac6a37bcd8a89ccbdd2e --- /dev/null +++ b/smtk/extension/vtk/widgets/vtkDiskWidget.cxx @@ -0,0 +1,407 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/extension/vtk/widgets/vtkDiskWidget.h" +#include "smtk/extension/vtk/widgets/vtkDiskRepresentation.h" + +#include "vtkCallbackCommand.h" +#include "vtkCamera.h" +#include "vtkCommand.h" +#include "vtkEvent.h" +#include "vtkObjectFactory.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" +#include "vtkStdString.h" +#include "vtkWidgetCallbackMapper.h" +#include "vtkWidgetEvent.h" +#include "vtkWidgetEventTranslator.h" + +vtkStandardNewMacro(vtkDiskWidget); + +//---------------------------------------------------------------------------- +vtkDiskWidget::vtkDiskWidget() +{ + this->WidgetState = vtkDiskWidget::Start; + + // Define widget events + this->CallbackMapper->SetCallbackMethod( + vtkCommand::LeftButtonPressEvent, vtkWidgetEvent::Select, this, vtkDiskWidget::SelectAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::LeftButtonReleaseEvent, + vtkWidgetEvent::EndSelect, + this, + vtkDiskWidget::EndSelectAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::MiddleButtonPressEvent, + vtkWidgetEvent::Translate, + this, + vtkDiskWidget::TranslateAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::MiddleButtonReleaseEvent, + vtkWidgetEvent::EndTranslate, + this, + vtkDiskWidget::EndSelectAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::RightButtonPressEvent, vtkWidgetEvent::Scale, this, vtkDiskWidget::ScaleAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::RightButtonReleaseEvent, + vtkWidgetEvent::EndScale, + this, + vtkDiskWidget::EndSelectAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::MouseMoveEvent, vtkWidgetEvent::Move, this, vtkDiskWidget::MoveAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::KeyPressEvent, + vtkEvent::AnyModifier, + 30, + 1, + "Up", + vtkWidgetEvent::Up, + this, + vtkDiskWidget::MoveDiskAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::KeyPressEvent, + vtkEvent::AnyModifier, + 28, + 1, + "Right", + vtkWidgetEvent::Up, + this, + vtkDiskWidget::MoveDiskAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::KeyPressEvent, + vtkEvent::AnyModifier, + 31, + 1, + "Down", + vtkWidgetEvent::Down, + this, + vtkDiskWidget::MoveDiskAction); + this->CallbackMapper->SetCallbackMethod( + vtkCommand::KeyPressEvent, + vtkEvent::AnyModifier, + 29, + 1, + "Left", + vtkWidgetEvent::Down, + this, + vtkDiskWidget::MoveDiskAction); + this->CallbackMapper->SetCallbackMethod(vtkCommand::KeyPressEvent, vtkEvent::AnyModifier, 'n', 1, + "n", vtkWidgetEvent::PickNormal, this, vtkDiskWidget::PickNormalAction); + this->CallbackMapper->SetCallbackMethod(vtkCommand::KeyPressEvent, vtkEvent::AnyModifier, 'N', 1, + "N", vtkWidgetEvent::PickNormal, this, vtkDiskWidget::PickNormalAction); + this->CallbackMapper->SetCallbackMethod(vtkCommand::KeyPressEvent, vtkEvent::AnyModifier, 14, 1, + "n", vtkWidgetEvent::PickNormal, this, vtkDiskWidget::PickNormalAction); +} + +//---------------------------------------------------------------------------- +vtkDiskWidget::~vtkDiskWidget() = default; + +//---------------------------------------------------------------------- +void vtkDiskWidget::SelectAction(vtkAbstractWidget* w) +{ + vtkDiskWidget* self = reinterpret_cast<vtkDiskWidget*>(w); + + // Get the event position + int X = self->Interactor->GetEventPosition()[0]; + int Y = self->Interactor->GetEventPosition()[1]; + + // We want to update the radius, normal, and center as appropriate + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep) + ->SetInteractionState(vtkDiskRepresentation::Moving); + int interactionState = self->WidgetRep->ComputeInteractionState(X, Y); + self->UpdateCursorShape(interactionState); + + if (self->WidgetRep->GetInteractionState() == vtkDiskRepresentation::Outside) + { + return; + } + + if (self->Interactor->GetControlKey() && interactionState == vtkDiskRepresentation::MovingWhole) + { + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep) + ->SetInteractionState(vtkDiskRepresentation::MovingWhole); + } + + // We are definitely selected + self->GrabFocus(self->EventCallbackCommand); + double eventPos[2]; + eventPos[0] = static_cast<double>(X); + eventPos[1] = static_cast<double>(Y); + self->WidgetState = vtkDiskWidget::Active; + self->WidgetRep->StartWidgetInteraction(eventPos); + + self->EventCallbackCommand->SetAbortFlag(1); + self->StartInteraction(); + self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); + self->Render(); +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::TranslateAction(vtkAbstractWidget* w) +{ + vtkDiskWidget* self = reinterpret_cast<vtkDiskWidget*>(w); + + // Get the event position + int X = self->Interactor->GetEventPosition()[0]; + int Y = self->Interactor->GetEventPosition()[1]; + + // We want to compute an orthogonal vector to the pane that has been selected + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep) + ->SetInteractionState(vtkDiskRepresentation::Moving); + int interactionState = self->WidgetRep->ComputeInteractionState(X, Y); + self->UpdateCursorShape(interactionState); + + if (self->WidgetRep->GetInteractionState() == vtkDiskRepresentation::Outside) + { + return; + } + + // We are definitely selected + self->GrabFocus(self->EventCallbackCommand); + double eventPos[2]; + eventPos[0] = static_cast<double>(X); + eventPos[1] = static_cast<double>(Y); + self->WidgetState = vtkDiskWidget::Active; + self->WidgetRep->StartWidgetInteraction(eventPos); + + self->EventCallbackCommand->SetAbortFlag(1); + self->StartInteraction(); + self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); + self->Render(); +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::ScaleAction(vtkAbstractWidget* w) +{ + vtkDiskWidget* self = reinterpret_cast<vtkDiskWidget*>(w); + + // Get the event position + int X = self->Interactor->GetEventPosition()[0]; + int Y = self->Interactor->GetEventPosition()[1]; + + // We want to compute an orthogonal vector to the pane that has been selected + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep) + ->SetInteractionState(vtkDiskRepresentation::AdjustingRadius); + int interactionState = self->WidgetRep->ComputeInteractionState(X, Y); + self->UpdateCursorShape(interactionState); + + if (self->WidgetRep->GetInteractionState() == vtkDiskRepresentation::Outside) + { + return; + } + + // We are definitely selected + self->GrabFocus(self->EventCallbackCommand); + double eventPos[2]; + eventPos[0] = static_cast<double>(X); + eventPos[1] = static_cast<double>(Y); + self->WidgetState = vtkDiskWidget::Active; + self->WidgetRep->StartWidgetInteraction(eventPos); + + self->EventCallbackCommand->SetAbortFlag(1); + self->StartInteraction(); + self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); + self->Render(); +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::MoveAction(vtkAbstractWidget* w) +{ + vtkDiskWidget* self = reinterpret_cast<vtkDiskWidget*>(w); + + // So as to change the cursor shape when the mouse is poised over + // the widget. Unfortunately, this results in a few extra picks + // due to the cell picker. However given that its picking simple geometry + // like the handles/arrows, this should be very quick + int X = self->Interactor->GetEventPosition()[0]; + int Y = self->Interactor->GetEventPosition()[1]; + int changed = 0; + + if (self->ManagesCursor && self->WidgetState != vtkDiskWidget::Active) + { + int oldInteractionState = + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep)->GetInteractionState(); + + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep) + ->SetInteractionState(vtkDiskRepresentation::Moving); + int state = self->WidgetRep->ComputeInteractionState(X, Y); + changed = self->UpdateCursorShape(state); + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep) + ->SetInteractionState(oldInteractionState); + changed = (changed || state != oldInteractionState) ? 1 : 0; + } + + // See whether we're active + if (self->WidgetState == vtkDiskWidget::Start) + { + if (changed && self->ManagesCursor) + { + self->Render(); + } + return; + } + + // Okay, adjust the representation + double e[2]; + e[0] = static_cast<double>(X); + e[1] = static_cast<double>(Y); + self->WidgetRep->WidgetInteraction(e); + + // moving something + self->EventCallbackCommand->SetAbortFlag(1); + self->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + self->Render(); +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::EndSelectAction(vtkAbstractWidget* w) +{ + vtkDiskWidget* self = reinterpret_cast<vtkDiskWidget*>(w); + + if ( + self->WidgetState != vtkDiskWidget::Active || + self->WidgetRep->GetInteractionState() == vtkDiskRepresentation::Outside) + { + return; + } + + // Return state to not selected + double e[2]; + self->WidgetRep->EndWidgetInteraction(e); + self->WidgetState = vtkDiskWidget::Start; + self->ReleaseFocus(); + + // Update cursor if managed + self->UpdateCursorShape( + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep)->GetRepresentationState()); + + self->EventCallbackCommand->SetAbortFlag(1); + self->EndInteraction(); + self->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr); + self->Render(); +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::MoveDiskAction(vtkAbstractWidget* w) +{ + vtkDiskWidget* self = reinterpret_cast<vtkDiskWidget*>(w); + + reinterpret_cast<vtkDiskRepresentation*>(self->WidgetRep) + ->SetInteractionState(vtkDiskRepresentation::Moving); + + int X = self->Interactor->GetEventPosition()[0]; + int Y = self->Interactor->GetEventPosition()[1]; + self->WidgetRep->ComputeInteractionState(X, Y); + + // The cursor must be over part of the widget for these key presses to work + if (self->WidgetRep->GetInteractionState() == vtkDiskRepresentation::Outside) + { + return; + } + + // Invoke all of the events associated with moving the cylinder + self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); + + // Move the cylinder + double factor = (self->Interactor->GetControlKey() ? 0.5 : 1.0); + if ( + vtkStdString(self->Interactor->GetKeySym()) == vtkStdString("Down") || + vtkStdString(self->Interactor->GetKeySym()) == vtkStdString("Left")) + { + self->GetDiskRepresentation()->BumpDisk(-1, factor); + } + else + { + self->GetDiskRepresentation()->BumpDisk(1, factor); + } + self->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + + self->EventCallbackCommand->SetAbortFlag(1); + self->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr); + self->Render(); +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::PickNormalAction(vtkAbstractWidget* w) +{ + vtkDiskWidget* self = reinterpret_cast<vtkDiskWidget*>(w); + + int X = self->Interactor->GetEventPosition()[0]; + int Y = self->Interactor->GetEventPosition()[1]; + + // Invoke all the events associated with moving the plane + self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); + bool newNormalPicked = self->GetDiskRepresentation()->PickNormal( + X, Y, self->Interactor->GetControlKey() == 1); + self->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + self->EventCallbackCommand->SetAbortFlag(1); + self->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr); + if (newNormalPicked) + { + self->Render(); + } +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::SetEnabled(int enabling) +{ + if (this->Enabled == enabling) + { + return; + } + + Superclass::SetEnabled(enabling); +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::CreateDefaultRepresentation() +{ + if (!this->WidgetRep) + { + this->WidgetRep = vtkDiskRepresentation::New(); + } +} + +//---------------------------------------------------------------------- +void vtkDiskWidget::SetRepresentation(vtkDiskRepresentation* rep) +{ + this->Superclass::SetWidgetRepresentation(reinterpret_cast<vtkWidgetRepresentation*>(rep)); +} + +//---------------------------------------------------------------------- +int vtkDiskWidget::UpdateCursorShape(int state) +{ + // So as to change the cursor shape when the mouse is poised over + // the widget. + if (this->ManagesCursor) + { + if (state == vtkDiskRepresentation::Outside) + { + return this->RequestCursorShape(VTK_CURSOR_DEFAULT); + } + else if (state == vtkDiskRepresentation::AdjustingRadius) + { + return this->RequestCursorShape(VTK_CURSOR_SIZEALL); + } + else + { + return this->RequestCursorShape(VTK_CURSOR_HAND); + } + } + + return 0; +} + +//---------------------------------------------------------------------------- +void vtkDiskWidget::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); +} diff --git a/smtk/extension/vtk/widgets/vtkDiskWidget.h b/smtk/extension/vtk/widgets/vtkDiskWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..b1a6c915189588562f42897b0b46e36eb989948e --- /dev/null +++ b/smtk/extension/vtk/widgets/vtkDiskWidget.h @@ -0,0 +1,162 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef vtkDiskWidget_h +#define vtkDiskWidget_h + +#include "smtk/extension/vtk/widgets/vtkSMTKWidgetsExtModule.h" // For export macro +#include "vtkAbstractWidget.h" + +class vtkDiskRepresentation; + +/** + * @class vtkDiskWidget + * @brief 3D widget for manipulating an infinite cylinder + * + * This 3D widget defines a planar, circular disk that can be + * interactively placed in a scene. The widget is assumed to consist + * of four parts: 1) a disk contained in a 2) bounding box, with a + * 3) normal vector, which is rooted at a 4) center point in the bounding + * box. (The representation paired with this widget determines the + * actual geometry of the widget.) + * + * To use this widget, you generally pair it with a vtkDiskRepresentation + * (or a subclass). Various options are available for controlling how the + * representation appears, and how the widget functions. + * + * @par Event Bindings: + * By default, the widget responds to the following VTK events (i.e., it + * watches the vtkRenderWindowInteractor for these events): + * <pre> + * If the normal vector is selected: + * LeftButtonPressEvent - select normal + * LeftButtonReleaseEvent - release (end select) normal + * MouseMoveEvent - orient the normal vector + * If the center point (handle) is selected: + * LeftButtonPressEvent - select handle (if on slider) + * LeftButtonReleaseEvent - release handle (if selected) + * MouseMoveEvent - move the center point (constrained to plane or on the + * axis if CTRL key is pressed) + * If the disk is selected: + * LeftButtonPressEvent - select disk + * LeftButtonReleaseEvent - release disk + * MouseMoveEvent - increase/decrease disk radius + * If the outline is selected: + * LeftButtonPressEvent - select outline + * LeftButtonReleaseEvent - release outline + * MouseMoveEvent - move the outline + * If the keypress characters are used + * 'Down/Left' Move disk away from viewer + * 'Up/Right' Move disk towards viewer + * In all the cases, independent of what is picked, the widget responds to the + * following VTK events: + * MiddleButtonPressEvent - move the disk + * MiddleButtonReleaseEvent - release the disk + * RightButtonPressEvent - scale the widget's representation + * RightButtonReleaseEvent - stop scaling the widget + * MouseMoveEvent - scale (if right button) or move (if middle button) the widget + * </pre> + * + * @par Event Bindings: + * Note that the event bindings described above can be changed using this + * class's vtkWidgetEventTranslator. This class translates VTK events + * into the vtkDiskWidget's widget events: + * <pre> + * vtkWidgetEvent::Select -- some part of the widget has been selected + * vtkWidgetEvent::EndSelect -- the selection process has completed + * vtkWidgetEvent::Move -- a request for widget motion has been invoked + * vtkWidgetEvent::Up and vtkWidgetEvent::Down -- MoveConeAction + * </pre> + * + * @par Event Bindings: + * In turn, when these widget events are processed, the vtkDiskWidget + * invokes the following VTK events on itself (which observers can listen for): + * <pre> + * vtkCommand::StartInteractionEvent (on vtkWidgetEvent::Select) + * vtkCommand::EndInteractionEvent (on vtkWidgetEvent::EndSelect) + * vtkCommand::InteractionEvent (on vtkWidgetEvent::Move) + * </pre> + * + * + * @sa + * vtk3DWidget vtkImplicitPlaneWidget +*/ +class VTKSMTKWIDGETSEXT_EXPORT vtkDiskWidget : public vtkAbstractWidget +{ +public: + /** + * Instantiate the object. + */ + static vtkDiskWidget* New(); + + //@{ + /** + * Standard vtkObject methods + */ + vtkTypeMacro(vtkDiskWidget, vtkAbstractWidget); + void PrintSelf(ostream& os, vtkIndent indent) override; + //@} + + vtkDiskWidget(const vtkDiskWidget&) = delete; + vtkDiskWidget& operator=(const vtkDiskWidget&) = delete; + + /** + * Specify an instance of vtkWidgetRepresentation used to represent this + * widget in the scene. Note that the representation is a subclass of vtkProp + * so it can be added to the renderer independent of the widget. + */ + void SetRepresentation(vtkDiskRepresentation* rep); + + /// Control widget interactivity, allowing users to interact with the camera or other widgets. + /// + /// The camera is unobserved when the widget is disabled. + void SetEnabled(int enabling) override; + + /** + * Return the representation as a vtkDiskRepresentation. + */ + vtkDiskRepresentation* GetDiskRepresentation() + { + return reinterpret_cast<vtkDiskRepresentation*>(this->WidgetRep); + } + + /** + * Create the default widget representation if one is not set. + */ + void CreateDefaultRepresentation() override; + +protected: + vtkDiskWidget(); + ~vtkDiskWidget() override; + + // Manage the state of the widget + int WidgetState; + enum _WidgetState + { + Start = 0, + Active + }; + + // These methods handle events + static void SelectAction(vtkAbstractWidget*); + static void TranslateAction(vtkAbstractWidget*); + static void ScaleAction(vtkAbstractWidget*); + static void EndSelectAction(vtkAbstractWidget*); + static void MoveAction(vtkAbstractWidget*); + static void MoveDiskAction(vtkAbstractWidget*); + static void PickNormalAction(vtkAbstractWidget*); + + /** + * Update the cursor shape based on the interaction state. Returns 1 + * if the cursor shape requested is different from the existing one. + */ + int UpdateCursorShape(int interactionState); +}; + +#endif diff --git a/smtk/markup/CMakeLists.txt b/smtk/markup/CMakeLists.txt index 8e0a735ea79de50c9f01fd25ab3d75ea76bd74be..d3ee3570c03583df31f921b58d1930e19a1c3a84 100644 --- a/smtk/markup/CMakeLists.txt +++ b/smtk/markup/CMakeLists.txt @@ -10,6 +10,7 @@ set(headers testing/cxx/helpers.h ) set(operations + ChangeUnits Create CreateArc CreateAnalyticShape diff --git a/smtk/markup/ImageData.h b/smtk/markup/ImageData.h index 96469de7fa8265d58f6c3d5038b5d41c96471e72..4e5e9413e959e2ccae0b47244bb767670ba4d01b 100644 --- a/smtk/markup/ImageData.h +++ b/smtk/markup/ImageData.h @@ -79,7 +79,15 @@ public: /// Do not call this method outside of an operation and be aware /// that it _may_ modify m_pointIds and m_cellIds as well. bool setShapeData(vtkSmartPointer<vtkImageData> image, Superclass::ShapeOptions& options); + /// Return the geometric content for this node as a vtkImageData. + /// + /// This method is not inherited and provides output in the node's native format. vtkSmartPointer<vtkImageData> shapeData() const { return m_image; } + /// Return the geometric content for this node. + /// + /// This method is inherited from DiscreteGeometry and does not make any assumptions + /// about the type of data returned. + vtkSmartPointer<vtkDataObject> shape() const override { return m_image; } /// Assign this node's state from \a source. bool assign(const smtk::graph::Component::ConstPtr& source, smtk::resource::CopyOptions& options) diff --git a/smtk/markup/Registrar.cxx b/smtk/markup/Registrar.cxx index 47c90a2724e3f8f6f2c7c7b89dd246912f9396db..dd642236275248ba1a17548f52c19982e31e514c 100644 --- a/smtk/markup/Registrar.cxx +++ b/smtk/markup/Registrar.cxx @@ -11,6 +11,7 @@ //============================================================================= #include "smtk/markup/Registrar.h" +#include "smtk/markup/operators/ChangeUnits.h" #include "smtk/markup/operators/Create.h" #include "smtk/markup/operators/CreateAnalyticShape.h" #include "smtk/markup/operators/CreateArc.h" @@ -48,6 +49,7 @@ namespace markup namespace { using OperationList = std::tuple< + ChangeUnits, Create, CreateArc, CreateAnalyticShape, diff --git a/smtk/markup/Resource.cxx b/smtk/markup/Resource.cxx index 336a3122265b8f6c67147eac083ed5ae34411693..f912ab8809ed0dd1647e6b358d5f21a9401809d0 100644 --- a/smtk/markup/Resource.cxx +++ b/smtk/markup/Resource.cxx @@ -23,6 +23,8 @@ #include "smtk/common/Paths.h" #include "smtk/common/StringUtil.h" +#include "units/System.h" + namespace smtk { namespace markup @@ -137,6 +139,29 @@ std::function<bool(const smtk::resource::Component&)> Resource::queryOperation( return smtk::resource::filter::Filter<smtk::graph::filter::Grammar>(query); } +bool Resource::setLengthUnit(const std::string& unit) +{ + auto unitSys = this->unitSystem(); + if (unit == m_lengthUnit || !unitSys) + { + return false; + } + bool ok = true; + auto uu = unitSys->unit(unit, &ok); + if (!ok) + { + // Unit must be a valid unit. + return false; + } + if (uu.dimension() != unitSys->dimension("L")) + { + // Units must be length. + return false; + } + m_lengthUnit = unit; + return true; +} + void Resource::initialize() { using namespace smtk::string::literals; // for ""_token diff --git a/smtk/markup/Resource.h b/smtk/markup/Resource.h index f65cbd153fd01dd0a852e42812dfbd3cbde4f6d1..6e16182dd1ddb63394124dac5f6e9977da86af45 100644 --- a/smtk/markup/Resource.h +++ b/smtk/markup/Resource.h @@ -98,6 +98,21 @@ public: */ static DomainFactory& domainFactory() { return s_domainFactory; } + /**\brief Set/get the default units of length for geometric data in this resource. + * + * When the default is empty (which it is by default), no units are provided. + * Otherwise, all geometric data is assumed to be in these units. + * + * You should not modify the length unit while geometric data exists inside + * the resource. + * + * If there is no valid unit system or if \a unit is invalid + * (i.e., not a valid unit or with dimensions other than length), + * setLengthUnit() will fail. + */ + std::string lengthUnit() const { return m_lengthUnit; } + bool setLengthUnit(const std::string& unit); + protected: friend class Component; @@ -111,6 +126,8 @@ protected: DomainMap m_domains; static DomainFactory s_domainFactory; + + std::string m_lengthUnit; }; template<typename Modifier> diff --git a/smtk/markup/SpatialData.cxx b/smtk/markup/SpatialData.cxx index 2b2cf3e3f58e962b760f6cf1d8602e63d493e400..267690259a221426fb4e8d1cf55a24a845c0f76b 100644 --- a/smtk/markup/SpatialData.cxx +++ b/smtk/markup/SpatialData.cxx @@ -47,5 +47,39 @@ AssignedIds* SpatialData::domainExtent(smtk::string::Token domainName) const return nullptr; } +bool SpatialData::isBlanked() const +{ + const auto& boolProps = this->properties().get<bool>(); + if (!boolProps.contains("_blanked")) + { + return false; + } + return boolProps.at("_blanked"); +} + +bool SpatialData::setBlanking(bool shouldBlank) +{ + if (!this->properties().contains<bool>("_blanked")) + { + // Already not blanked? Do nothing. + if (!shouldBlank) { return false; } + this->properties().emplace<bool>("_blanked", true); + return true; + } + // Already blanked? Do nothing + if (this->properties().at<bool>("_blanked") == shouldBlank) + { + return false; + } + if (shouldBlank) + { + this->properties().emplace<bool>("_blanked", true); + return true; + } + this->properties().erase<bool>("_blanked"); + return true; +} + + } // namespace markup } // namespace smtk diff --git a/smtk/markup/SpatialData.h b/smtk/markup/SpatialData.h index b4b62b7986ed4abb560ba22300d5adc7238d123a..ffe15a02fa102ea128bdd4fc4cd7339a339700df 100644 --- a/smtk/markup/SpatialData.h +++ b/smtk/markup/SpatialData.h @@ -69,6 +69,21 @@ public: virtual AssignedIds* domainExtent(Domain* domain) const; virtual AssignedIds* domainExtent(smtk::string::Token domainName) const; + /// Return whether or not this node has its geometry blanked (i.e., not rendered). + bool isBlanked() const; + + /// Set whether or not this node has its geometry blanked. + /// + /// Blanking a node's geometry is usually performed by an operation that wishes to + /// keep the source geometry around but also permanently transform it in some reversible + /// or editable way. While the markup session also allows transform properties + /// attached to nodes to affect rendering, this can cause issues for downstream filters + /// that need access to the transformed geometry (and do not wish to perform the transform + /// themselves). + /// + /// This method returns true when the node's blanking state was modified. + bool setBlanking(bool shouldBlank); + protected: }; diff --git a/smtk/markup/Traits.h b/smtk/markup/Traits.h index f6af4bfee157e57e84674284c2459e5af2bdcac8..1edda988703a00327ac678c8f1513f5daee1fb96 100644 --- a/smtk/markup/Traits.h +++ b/smtk/markup/Traits.h @@ -147,6 +147,7 @@ struct SMTKMARKUP_EXPORT BoundariesToShapes static constexpr graph::OwnershipSemantics semantics = graph::OwnershipSemantics::ToNodeOwnsFromNode; }; + } // namespace arcs // Forward-declare domain types diff --git a/smtk/markup/UnstructuredData.h b/smtk/markup/UnstructuredData.h index 5b3c60c86d35ddef120868abcee2b18ce21dc23e..e3594bef8e87bf6e77a5a81b7ec93ecb805e4875 100644 --- a/smtk/markup/UnstructuredData.h +++ b/smtk/markup/UnstructuredData.h @@ -75,7 +75,16 @@ public: /// Assign the \a mesh data as this object's shape and update Field children to match. bool setShapeData(vtkSmartPointer<vtkDataObject> mesh, ShapeOptions& options); + /// Return the geometric content for this node as a vtkDataObject. + /// + /// Because unstructured data may be modeled with vtkPolyData, vtkUnstructuredGrid, + /// or vtkCellGrid data, there is no better type to return. vtkSmartPointer<vtkDataObject> shapeData() const; + /// Return the geometric content for this node. + /// + /// This method is inherited from DiscreteGeometry and does not make any assumptions + /// about the type of data returned. + vtkSmartPointer<vtkDataObject> shape() const override { return m_mesh; } const AssignedIds& pointIds() const { return *m_pointIds; } const AssignedIds& cellIds() const { return *m_cellIds; } diff --git a/smtk/markup/json/jsonResource.cxx b/smtk/markup/json/jsonResource.cxx index d219b27319b973d8ba3e2609108e651f72269158..ad2dea5b93edae956f69607875818c2aad68c860 100644 --- a/smtk/markup/json/jsonResource.cxx +++ b/smtk/markup/json/jsonResource.cxx @@ -217,6 +217,12 @@ void to_json(nlohmann::json& j, const smtk::markup::Resource::Ptr& resource) j["domains"] = domainData; } + // Record length unit (if one exists). + if (!resource->lengthUnit().empty()) + { + j["length-unit"] = resource->lengthUnit(); + } + // Record string-token hashes. // Some nodes may use string tokens, so we must serialize that map if it exists. auto& tokenManager = smtk::string::Token::manager(); @@ -280,6 +286,13 @@ void from_json(const nlohmann::json& jj, smtk::markup::Resource::Ptr& resource) } } + // Deserialize length unit. + auto jLengthUnit = jj.find("length-unit"); + if (jLengthUnit != jj.end()) + { + resource->setLengthUnit(jLengthUnit->get<std::string>()); + } + // Deserialize domains auto jDomains = jj.find("domains"); if (jDomains != jj.end()) diff --git a/smtk/markup/operators/ChangeUnits.cxx b/smtk/markup/operators/ChangeUnits.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e85ca37212465e4e1d85291382877e2aa44bbe72 --- /dev/null +++ b/smtk/markup/operators/ChangeUnits.cxx @@ -0,0 +1,158 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/markup/operators/ChangeUnits.h" + +#include "smtk/markup/DiscreteGeometry.h" + +#include "smtk/operation/MarkGeometry.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ReferenceItem.h" +#include "smtk/attribute/StringItem.h" + +#include "vtkImageData.h" +#include "vtkPoints.h" +#include "vtkPointSet.h" +#include "vtkSMPTools.h" +#include "vtkVector.h" + +#include "units/Converter.h" +#include "units/System.h" +#include "units/Unit.h" + +#include "smtk/markup/operators/ChangeUnits_xml.h" + +namespace smtk +{ +namespace markup +{ + +bool ChangeUnits::ableToOperate() +{ + if (!this->Superclass::ableToOperate()) + { + return false; + } + + std::string srcLengthUnitStr = this->parameters()->findString("source units")->value(); + + auto assocs = this->parameters()->associations(); + bool ok = assocs->numberOfValues() > 0; + for (const auto& assoc : *assocs) + { + auto* rsrc = dynamic_cast<smtk::markup::Resource*>(assoc->parentResource()); + auto usys = rsrc ? rsrc->unitSystem() : nullptr; + // Every object associated must live in a resource with a unit system and default length unit + // that can be converted to the length unit for this operation. + if (!usys || rsrc->lengthUnit().empty()) + { + ok = false; + break; + } + + auto srcUnit = usys->unit(srcLengthUnitStr, &ok); + if (!ok) + { + break; + } + auto dstUnit = usys->unit(rsrc->lengthUnit(), &ok); + if (!ok) + { + break; + } + auto cvt = usys->convert(srcUnit, dstUnit); + if (!cvt) + { + ok = false; + break; + } + } + return ok; +} + +ChangeUnits::Result ChangeUnits::operateInternal() +{ + std::string srcLengthUnitStr = this->parameters()->findString("source units")->value(); + auto assocs = this->parameters()->associations(); + auto result = this->createResult(ChangeUnits::Outcome::SUCCEEDED); + auto mod = result->findComponent("modified"); + mod->setNumberOfValues(assocs->numberOfValues()); + for (const auto& assoc : *assocs) + { + auto* rsrc = dynamic_cast<smtk::markup::Resource*>(assoc->parentResource()); + auto usys = rsrc ? rsrc->unitSystem() : nullptr; + if (!usys) { continue; } + bool ok; + auto srcUnit = usys->unit(srcLengthUnitStr, &ok); + if (!ok) { continue; } + auto dstUnit = usys->unit(rsrc->lengthUnit(), &ok); + if (!ok) { continue; } + auto cvt = usys->convert(srcUnit, dstUnit); + auto spatialData = std::dynamic_pointer_cast<smtk::markup::DiscreteGeometry>(assoc); + auto mesh = spatialData->shape(); + if (!cvt || !mesh) + { + ok = false; + break; + } + if (auto* image = vtkImageData::SafeDownCast(mesh)) + { + vtkVector3d xx; + vtkVector3d yy; + image->GetOrigin(xx.GetData()); + image->GetSpacing(yy.GetData()); + for (int ii = 0; ii < 3; ++ii) + { + xx[ii] = cvt->transform(xx[ii]); + yy[ii] = cvt->transform(yy[ii]); + } + image->SetOrigin(xx.GetData()); + image->SetSpacing(yy.GetData()); + mesh->Modified(); + smtk::operation::MarkGeometry().markModified(assoc); + } + else if (auto* pset = vtkPointSet::SafeDownCast(mesh)) + { + auto* pts = pset->GetPoints(); + vtkSMPTools::For(0, pset->GetNumberOfPoints(), [&](vtkIdType begin, vtkIdType end) + { + vtkVector3d xx; + for (vtkIdType pp = begin; pp < end; ++pp) + { + pts->GetPoint(pp, xx.GetData()); + for (int ii = 0; ii < 3; ++ii) + { + xx[ii] = cvt->transform(xx[ii]); + } + pts->SetPoint(pp, xx.GetData()); + } + } + ); + mesh->Modified(); + smtk::operation::MarkGeometry().markModified(spatialData); + } + else + { + smtkErrorMacro(this->log(), + "Unhandled shape type '" << mesh->GetClassName() << "' for " + "component '" << assoc->name() << "' (" << assoc->typeName() << ")."); + } + } + return result; +} + +const char* ChangeUnits::xmlDescription() const +{ + return ChangeUnits_xml; +} + +} // namespace markup +} // namespace smtk diff --git a/smtk/markup/operators/ChangeUnits.h b/smtk/markup/operators/ChangeUnits.h new file mode 100644 index 0000000000000000000000000000000000000000..ff909c9b76b03251e311084cd22349e4ec849457 --- /dev/null +++ b/smtk/markup/operators/ChangeUnits.h @@ -0,0 +1,54 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef smtk_markup_ChangeUnits_h +#define smtk_markup_ChangeUnits_h + +#include "smtk/markup/Resource.h" + +#include "smtk/operation/XMLOperation.h" + +namespace smtk +{ +namespace markup +{ + +/**\brief Scale geometric data of associated objects from one length unit to another. + * + * If the destination unit is empty (the default) and the resource has a + * unit system with an active context (that specifies default units for + * each dimension), then the default length unit will be the destination. + * + * If the input association is spatial data, then the input nodes are + * blanked and output versions are created with the transformed geometry. + * + * If the input association is a markup resource, this changes the + * default length unit for the resource (and rescales all of its spatial + * data components if the default length unit was different but valid; + * no rescaling is performed if there was no prior default length unit). + */ +class SMTKMARKUP_EXPORT ChangeUnits : public smtk::operation::XMLOperation +{ +public: + smtkTypeMacro(smtk::markup::ChangeUnits); + smtkCreateMacro(ChangeUnits); + smtkSharedFromThisMacro(smtk::operation::Operation); + smtkSuperclassMacro(smtk::operation::XMLOperation); + + bool ableToOperate() override; + +protected: + Result operateInternal() override; + const char* xmlDescription() const override; +}; + +} // namespace markup +} // namespace smtk + +#endif // smtk_markup_ChangeUnits_h diff --git a/smtk/markup/operators/ChangeUnits.sbt b/smtk/markup/operators/ChangeUnits.sbt new file mode 100644 index 0000000000000000000000000000000000000000..20be19b824b7a0987dc90aaf05741ca95ff2945b --- /dev/null +++ b/smtk/markup/operators/ChangeUnits.sbt @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Description of the markup resource's "create" Operation --> +<SMTK_AttributeResource Version="3"> + <Definitions> + <include href="smtk/operation/Operation.xml"/> + <AttDef Type="change units" Label="change units" BaseType="operation"> + <AssociationsDef LockType="Write"> + <Accepts> + <!-- Accept any spatial markup component (though only discrete handled now) --> + <Resource Name="smtk::markup::Resource" Filter="smtk::markup::SpatialData"/> + </Accepts> + </AssociationsDef> + <ItemDefinitions> + <String Name="source units" Label="source units"> + <BriefDescription> + The length units the geometry is specified in; the geometry + </BriefDescription> + <DetailedDescription> + The length units the geometry is specified in; the geometry + will be converted from this length unit to the resource's default length units. + Examples include "mm", "meter", "inch", "foot", "yard", or even "furlong". + </DetailedDescription> + </String> + </ItemDefinitions> + </AttDef> + <!-- Result --> + <include href="smtk/operation/Result.xml"/> + <AttDef Type="result(set name)" BaseType="result"/> + </Definitions> +</SMTK_AttributeResource> diff --git a/smtk/markup/operators/Import.cxx b/smtk/markup/operators/Import.cxx index 5770b9cbe4711c93e082860f584a99cec4330bb2..744a23f71dab7c41bca71b99b4a50176abb8ed83 100644 --- a/smtk/markup/operators/Import.cxx +++ b/smtk/markup/operators/Import.cxx @@ -18,6 +18,8 @@ #include "smtk/extension/vtk/io/ImportAsVTKData.h" +#include "smtk/view/Selection.h" + #include "smtk/attribute/Attribute.h" #include "smtk/attribute/ComponentItem.h" #include "smtk/attribute/FileItem.h" @@ -29,6 +31,7 @@ #include "smtk/attribute/StringItem.h" #include "smtk/operation/MarkGeometry.h" +#include "smtk/operation/Hints.h" #include "smtk/resource/Manager.h" @@ -414,6 +417,11 @@ Import::Result Import::operateInternal() { m_result->findInt("outcome")->setValue(0, static_cast<int>(Import::Outcome::SUCCEEDED)); m_result->findResource("resourcesCreated")->appendValue(resource); + + auto created = m_result->findComponent("created"); + std::set<smtk::resource::Component::Ptr> importedComponents; + importedComponents.insert(created->begin(), created->end()); + smtk::operation::addSelectionHint(m_result, importedComponents, smtk::view::SelectionAction::UNFILTERED_REPLACE); } return m_result; } diff --git a/smtk/markup/operators/Import.sbt b/smtk/markup/operators/Import.sbt index d22f03234dd194dad43294b0c8e9ce0c7ff89267..06855f1bbd7703aea84a31bf8fb48c9dcfadf358 100644 --- a/smtk/markup/operators/Import.sbt +++ b/smtk/markup/operators/Import.sbt @@ -24,6 +24,7 @@ </AttDef> <!-- Result --> <include href="smtk/operation/Result.xml"/> + <include href="smtk/operation/Hints.xml"/> <AttDef Type="result(import)" BaseType="result"> <ItemDefinitions> <Void Name="allow camera reset" Optional="true" IsEnabledByDefault="true" AdvanceLevel="11"/> diff --git a/smtk/markup/operators/Write.cxx b/smtk/markup/operators/Write.cxx index 2921f59e243f2e353a4723462afeb98a2871dbad..2bdd7ffe2fa8d946b1215071875a041a8c424662 100644 --- a/smtk/markup/operators/Write.cxx +++ b/smtk/markup/operators/Write.cxx @@ -203,6 +203,17 @@ void Write::markModifiedResources(Write::Result& /*unused*/) } } +void Write::generateSummary(Result& res) +{ + if (smtk::operation::outcome(res) != Outcome::SUCCEEDED) + { + this->Superclass::generateSummary(res); + } + auto resourceItem = this->parameters()->associations(); + auto resource = std::dynamic_pointer_cast<smtk::resource::Resource>(resourceItem->value()); + smtkInfoMacro(this->log(), "Wrote \"" << resource->location() << "\"."); +} + bool Write::writeData( const Component* dataNode, const std::string& filename, diff --git a/smtk/markup/operators/Write.h b/smtk/markup/operators/Write.h index 0f8dd40cf1e183216cfa3994072ac85a460837f6..41e4d35cb2d63a927018f0b704c08c2ef6171499 100644 --- a/smtk/markup/operators/Write.h +++ b/smtk/markup/operators/Write.h @@ -38,6 +38,7 @@ protected: Result operateInternal() override; const char* xmlDescription() const override; void markModifiedResources(Result&) override; + void generateSummary(Result&) override; bool writeData(const Component* dataNode, const std::string& filename, smtk::string::Token mimeType); diff --git a/smtk/markup/pybind11/CMakeLists.txt b/smtk/markup/pybind11/CMakeLists.txt index 245c4015a5d7f1d7d41855a832164270b16de3fe..8bd8126cc7f839019aa9f637368c5f8fdc337cb9 100644 --- a/smtk/markup/pybind11/CMakeLists.txt +++ b/smtk/markup/pybind11/CMakeLists.txt @@ -9,7 +9,13 @@ pybind11_add_module(${library_name} target_include_directories(${library_name} PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}> ) -target_link_libraries(${library_name} LINK_PUBLIC smtkCore smtkMarkup) +target_link_libraries(${library_name} + LINK_PUBLIC + smtkMarkup + smtkCore + VTK::CommonCore + VTK::WrappingPythonCore +) set_target_properties(${library_name} PROPERTIES CXX_VISIBILITY_PRESET hidden diff --git a/smtk/markup/pybind11/PybindMarkup.cxx b/smtk/markup/pybind11/PybindMarkup.cxx index 6869fad85eec9c8892026319bb64e518479d1fb9..29ef5e1ecc710a970aa3d1229e1ef1f1de056a30 100644 --- a/smtk/markup/pybind11/PybindMarkup.cxx +++ b/smtk/markup/pybind11/PybindMarkup.cxx @@ -23,8 +23,10 @@ using PySharedPtrClass = py::class_<T, std::shared_ptr<T>, Args...>; #include "PybindResource.h" #include "PybindComponent.h" +#include "PybindSpatialData.h" #include "PybindDomain.h" #include "PybindDomainMap.h" +#include "PybindUnstructuredData.h" #include "smtk/resource/Manager.h" @@ -44,4 +46,6 @@ PYBIND11_MODULE(_smtkPybindMarkup, markup) auto smtk_markup_Component = pybind11_init_smtk_markup_Component(markup); auto smtk_markup_Domain = pybind11_init_smtk_markup_Domain(markup); auto smtk_markup_DomainMap = pybind11_init_smtk_markup_DomainMap(markup); + auto smtk_markup_SpatialData = pybind11_init_smtk_markup_SpatialData(markup); + auto smtk_markup_UnstructuredData = pybind11_init_smtk_markup_UnstructuredData(markup); } diff --git a/smtk/markup/pybind11/PybindResource.h b/smtk/markup/pybind11/PybindResource.h index 9e1a119986cce19b300c343315cda126bee34598..3ee7ded19a84088feabf113532d9bc065f974c7b 100644 --- a/smtk/markup/pybind11/PybindResource.h +++ b/smtk/markup/pybind11/PybindResource.h @@ -27,6 +27,8 @@ inline PySharedPtrClass< smtk::markup::Resource> pybind11_init_smtk_markup_Resou instance .def_static("create", []() { return smtk::markup::Resource::create(); }) .def("domains", [](smtk::markup::Resource& rr) { return rr.domains(); }) + .def("lengthUnit", &smtk::markup::Resource::lengthUnit) + .def("setLengthUnit", &smtk::markup::Resource::setLengthUnit, py::arg("lengthUnit")) ; return instance; } diff --git a/smtk/markup/pybind11/PybindSpatialData.h b/smtk/markup/pybind11/PybindSpatialData.h new file mode 100644 index 0000000000000000000000000000000000000000..049eac0c5d0d48e17581a75a90acbb7f98fcb663 --- /dev/null +++ b/smtk/markup/pybind11/PybindSpatialData.h @@ -0,0 +1,35 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= + +#ifndef pybind_smtk_markup_SpatialData_h +#define pybind_smtk_markup_SpatialData_h + +#include <pybind11/pybind11.h> + +#include "smtk/markup/SpatialData.h" + +#include "smtk/common/UUID.h" +#include "smtk/common/pybind11/PybindUUIDTypeCaster.h" + +namespace py = pybind11; + +inline PySharedPtrClass< smtk::markup::SpatialData> pybind11_init_smtk_markup_SpatialData(py::module &m) +{ + PySharedPtrClass< smtk::markup::SpatialData, smtk::markup::Component> instance(m, "SpatialData"); + instance + .def("setBlanking", &smtk::markup::SpatialData::setBlanking, py::arg("shouldBlank")) + .def("isBlanked", &smtk::markup::SpatialData::isBlanked) + .def_static("CastTo", [](const std::shared_ptr<smtk::resource::PersistentObject>& obj) + { return std::dynamic_pointer_cast<smtk::markup::SpatialData>(obj); }) + ; + return instance; +} + +#endif diff --git a/smtk/markup/pybind11/PybindUnstructuredData.h b/smtk/markup/pybind11/PybindUnstructuredData.h new file mode 100644 index 0000000000000000000000000000000000000000..0541d8de087392506a1d031811e5e4fff7fa6795 --- /dev/null +++ b/smtk/markup/pybind11/PybindUnstructuredData.h @@ -0,0 +1,36 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= + +#ifndef pybind_smtk_markup_UnstructuredData_h +#define pybind_smtk_markup_UnstructuredData_h + +#include <pybind11/pybind11.h> + +#include "smtk/markup/UnstructuredData.h" + +#include "smtk/extension/vtk/pybind11/PybindVTKTypeCaster.h" + +#include "smtk/common/UUID.h" +#include "smtk/common/pybind11/PybindUUIDTypeCaster.h" + +namespace py = pybind11; + +inline PySharedPtrClass< smtk::markup::UnstructuredData> pybind11_init_smtk_markup_UnstructuredData(py::module &m) +{ + PySharedPtrClass< smtk::markup::UnstructuredData, smtk::markup::SpatialData> instance(m, "UnstructuredData"); + instance + .def("shape", &smtk::markup::UnstructuredData::shape) + .def_static("CastTo", [](const std::shared_ptr<smtk::resource::PersistentObject>& obj) + { return std::dynamic_pointer_cast<smtk::markup::UnstructuredData>(obj); }) + ; + return instance; +} + +#endif diff --git a/smtk/markup/testing/python/markupResource.py b/smtk/markup/testing/python/markupResource.py index e9c42e67addc835e890f0b354f53233cb5e02eb9..7a944e774eb5dabc638a2ad5233356b983e6d9dd 100644 --- a/smtk/markup/testing/python/markupResource.py +++ b/smtk/markup/testing/python/markupResource.py @@ -19,6 +19,25 @@ print('Node Types\n ' + '\n '.join([xx.data() for xx in resource.nodeTypes()])) print('Arc Types\n ' + '\n '.join([xx.data() for xx in resource.arcTypes()])) +expectedNodeset = set([ + 'URL', 'UnstructuredData', 'Comment', 'Plane', 'Cone', 'Label', 'DiscreteGeometry', + 'Ontology', 'Field', 'AnalyticShape', 'SideSet', 'Box', 'OntologyIdentifier', + 'Subset', 'NodeSet', 'Landmark', 'SpatialData', 'ImageData', 'Component', 'Sphere', + 'Feature', 'Group']) +fqnsNodes = {smtk.string.Token('smtk::markup::' + xx) for xx in expectedNodeset} +if fqnsNodes != set(resource.nodeTypes()): + print('Error: unexpected nodeset.') + raise RuntimeError('Unexpected nodeset') + +expectedArcset = set([ + 'URLsToData', 'ReferencesToPrimaries', 'LabelsToSubjects', 'OntologyIdentifiersToSubtypes', + 'URLsToImportedData', 'OntologyIdentifiersToIndividuals', 'FieldsToShapes', + 'OntologyToIdentifiers', 'GroupsToMembers', 'BoundariesToShapes']) +fqnsArcs = { smtk.string.Token('smtk::markup::arcs::' + xx) for xx in expectedArcset } +if fqnsArcs != set(resource.arcTypes()): + print('Error: unexpected arcset.') + raise RuntimeError('Unexpected arcset') + # Create one node of every type nodes = [resource.createNodeOfType(nodeTypeName) for nodeTypeName in resource.nodeTypes()] @@ -48,5 +67,15 @@ if not didConnect: resource.dump('') -print(resource.domains()) -print(resource.domains().keys()) +# print(resource.domains()) +print('Domains\n ' + '\n '.join([xx.data() for xx in resource.domains().keys()])) + +print('Test blanking') +nodeToBlank = resource.createNodeOfType(smtk.string.Token('smtk::markup::UnstructuredData')) +bbefore = nodeToBlank.isBlanked() +print(' Is blanked initially?', nodeToBlank.isBlanked()) +nodeToBlank.setBlanking(True) +print(' Is blanked finally?', nodeToBlank.isBlanked()) +bafter = nodeToBlank.isBlanked() +if bbefore or not bafter: + raise ('Blanking incorrect') diff --git a/smtk/mesh/pybind11/PybindDeleteMesh.h b/smtk/mesh/pybind11/PybindDeleteMesh.h index be400746205fc61c3e6ecc639cae85af8f394f8a..8623beca0b4f3d3db81b85196a7026685bbb0c13 100644 --- a/smtk/mesh/pybind11/PybindDeleteMesh.h +++ b/smtk/mesh/pybind11/PybindDeleteMesh.h @@ -23,7 +23,6 @@ inline PySharedPtrClass< smtk::mesh::DeleteMesh, smtk::operation::XMLOperation > { PySharedPtrClass< smtk::mesh::DeleteMesh, smtk::operation::XMLOperation > instance(m, "DeleteMesh"); instance - .def("deepcopy", (smtk::mesh::DeleteMesh & (smtk::mesh::DeleteMesh::*)(::smtk::mesh::DeleteMesh const &)) &smtk::mesh::DeleteMesh::operator=) .def("ableToOperate", &smtk::mesh::DeleteMesh::ableToOperate) .def_static("create", (std::shared_ptr<smtk::mesh::DeleteMesh> (*)()) &smtk::mesh::DeleteMesh::create) .def_static("create", (std::shared_ptr<smtk::mesh::DeleteMesh> (*)(::std::shared_ptr<smtk::mesh::DeleteMesh> &)) &smtk::mesh::DeleteMesh::create, py::arg("ref")) diff --git a/smtk/mesh/pybind11/PybindElevateMesh.h b/smtk/mesh/pybind11/PybindElevateMesh.h index df4662f7215ee315b1ba5b50848097a149a2926f..e8548545d0a26d666fbdbbd74f3b03d94cc38dce 100644 --- a/smtk/mesh/pybind11/PybindElevateMesh.h +++ b/smtk/mesh/pybind11/PybindElevateMesh.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::mesh::ElevateMesh, smtk::operation::XMLOperation PySharedPtrClass< smtk::mesh::ElevateMesh, smtk::operation::XMLOperation > instance(m, "ElevateMesh"); instance .def(py::init<>()) - .def(py::init<::smtk::mesh::ElevateMesh const &>()) - .def("deepcopy", (smtk::mesh::ElevateMesh & (smtk::mesh::ElevateMesh::*)(::smtk::mesh::ElevateMesh const &)) &smtk::mesh::ElevateMesh::operator=) .def("ableToOperate", &smtk::mesh::ElevateMesh::ableToOperate) .def_static("create", (std::shared_ptr<smtk::mesh::ElevateMesh> (*)()) &smtk::mesh::ElevateMesh::create) .def_static("create", (std::shared_ptr<smtk::mesh::ElevateMesh> (*)(::std::shared_ptr<smtk::mesh::ElevateMesh> &)) &smtk::mesh::ElevateMesh::create, py::arg("ref")) diff --git a/smtk/mesh/pybind11/PybindExport.h b/smtk/mesh/pybind11/PybindExport.h index 11a2a8e8b2080daf513bdd78bf4ec6b6dbc9f93e..c9d7cde222829cef88d2dce6fdbfe40c5a8e7246 100644 --- a/smtk/mesh/pybind11/PybindExport.h +++ b/smtk/mesh/pybind11/PybindExport.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::mesh::Export, smtk::operation::XMLOperation > pyb PySharedPtrClass< smtk::mesh::Export, smtk::operation::XMLOperation > instance(m, "Export"); instance .def(py::init<>()) - .def(py::init<::smtk::mesh::Export const &>()) - .def("deepcopy", (smtk::mesh::Export & (smtk::mesh::Export::*)(::smtk::mesh::Export const &)) &smtk::mesh::Export::operator=) .def("ableToOperate", &smtk::mesh::Export::ableToOperate) .def_static("create", (std::shared_ptr<smtk::mesh::Export> (*)()) &smtk::mesh::Export::create) .def_static("create", (std::shared_ptr<smtk::mesh::Export> (*)(::std::shared_ptr<smtk::mesh::Export> &)) &smtk::mesh::Export::create, py::arg("ref")) diff --git a/smtk/mesh/pybind11/PybindImport.h b/smtk/mesh/pybind11/PybindImport.h index 2e54d72de5d7e7280716065aeb5e9c6c89c9fbf5..2fe8a437ac2224aa72a0d635e8ac2799f16a158c 100644 --- a/smtk/mesh/pybind11/PybindImport.h +++ b/smtk/mesh/pybind11/PybindImport.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::mesh::Import, smtk::operation::XMLOperation > pyb PySharedPtrClass< smtk::mesh::Import, smtk::operation::XMLOperation > instance(m, "Import"); instance .def(py::init<>()) - .def(py::init<::smtk::mesh::Import const &>()) - .def("deepcopy", (smtk::mesh::Import & (smtk::mesh::Import::*)(::smtk::mesh::Import const &)) &smtk::mesh::Import::operator=) .def("ableToOperate", &smtk::mesh::Import::ableToOperate) .def_static("create", (std::shared_ptr<smtk::mesh::Import> (*)()) &smtk::mesh::Import::create) .def_static("create", (std::shared_ptr<smtk::mesh::Import> (*)(::std::shared_ptr<smtk::mesh::Import> &)) &smtk::mesh::Import::create, py::arg("ref")) diff --git a/smtk/mesh/pybind11/PybindInterpolateOntoMesh.h b/smtk/mesh/pybind11/PybindInterpolateOntoMesh.h index c00203f0cf0fca5180165002c007eed065832cd3..e6374ac9b978365897c69c63dbb28b0f6e7aa247 100644 --- a/smtk/mesh/pybind11/PybindInterpolateOntoMesh.h +++ b/smtk/mesh/pybind11/PybindInterpolateOntoMesh.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::mesh::InterpolateOntoMesh, smtk::operation::XMLOp PySharedPtrClass< smtk::mesh::InterpolateOntoMesh, smtk::operation::XMLOperation > instance(m, "InterpolateOntoMesh"); instance .def(py::init<>()) - .def(py::init<::smtk::mesh::InterpolateOntoMesh const &>()) - .def("deepcopy", (smtk::mesh::InterpolateOntoMesh & (smtk::mesh::InterpolateOntoMesh::*)(::smtk::mesh::InterpolateOntoMesh const &)) &smtk::mesh::InterpolateOntoMesh::operator=) .def("ableToOperate", &smtk::mesh::InterpolateOntoMesh::ableToOperate) .def_static("create", (std::shared_ptr<smtk::mesh::InterpolateOntoMesh> (*)()) &smtk::mesh::InterpolateOntoMesh::create) .def_static("create", (std::shared_ptr<smtk::mesh::InterpolateOntoMesh> (*)(::std::shared_ptr<smtk::mesh::InterpolateOntoMesh> &)) &smtk::mesh::InterpolateOntoMesh::create, py::arg("ref")) diff --git a/smtk/mesh/pybind11/PybindRead.h b/smtk/mesh/pybind11/PybindRead.h index cd402f3f164c40d091ab919f93b820bfc39ea683..55054d38d367f7cb8c15e94af513d76a25a7031e 100644 --- a/smtk/mesh/pybind11/PybindRead.h +++ b/smtk/mesh/pybind11/PybindRead.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::mesh::Read, smtk::operation::XMLOperation > pybin PySharedPtrClass< smtk::mesh::Read, smtk::operation::XMLOperation > instance(m, "Read"); instance .def(py::init<>()) - .def(py::init<::smtk::mesh::Read const &>()) - .def("deepcopy", (smtk::mesh::Read & (smtk::mesh::Read::*)(::smtk::mesh::Read const &)) &smtk::mesh::Read::operator=) .def("ableToOperate", &smtk::mesh::Read::ableToOperate) .def_static("create", (std::shared_ptr<smtk::mesh::Read> (*)()) &smtk::mesh::Read::create) .def_static("create", (std::shared_ptr<smtk::mesh::Read> (*)(::std::shared_ptr<smtk::mesh::Read> &)) &smtk::mesh::Read::create, py::arg("ref")) diff --git a/smtk/mesh/pybind11/PybindWrite.h b/smtk/mesh/pybind11/PybindWrite.h index 83c6259021bb602afa9c75970aeedd7a064bade2..cfde610355e3b0e49dc334772c0783d5df00b102 100644 --- a/smtk/mesh/pybind11/PybindWrite.h +++ b/smtk/mesh/pybind11/PybindWrite.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::mesh::Write, smtk::operation::XMLOperation > pybi PySharedPtrClass< smtk::mesh::Write, smtk::operation::XMLOperation > instance(m, "Write"); instance .def(py::init<>()) - .def(py::init<::smtk::mesh::Write const &>()) - .def("deepcopy", (smtk::mesh::Write & (smtk::mesh::Write::*)(::smtk::mesh::Write const &)) &smtk::mesh::Write::operator=) .def("ableToOperate", &smtk::mesh::Write::ableToOperate) .def_static("create", (std::shared_ptr<smtk::mesh::Write> (*)()) &smtk::mesh::Write::create) .def_static("create", (std::shared_ptr<smtk::mesh::Write> (*)(::std::shared_ptr<smtk::mesh::Write> &)) &smtk::mesh::Write::create, py::arg("ref")) diff --git a/smtk/model/pybind11/PybindAddAuxiliaryGeometry.h b/smtk/model/pybind11/PybindAddAuxiliaryGeometry.h index 35c0d26f8bb208140a9d050fa3c566f844955022..2c7519b3f8bad68a426dff5d48c14d950e46a156 100644 --- a/smtk/model/pybind11/PybindAddAuxiliaryGeometry.h +++ b/smtk/model/pybind11/PybindAddAuxiliaryGeometry.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::model::AddAuxiliaryGeometry, smtk::operation::XML PySharedPtrClass< smtk::model::AddAuxiliaryGeometry, smtk::operation::XMLOperation > instance(m, "AddAuxiliaryGeometry"); instance .def(py::init<>()) - .def(py::init<::smtk::model::AddAuxiliaryGeometry const &>()) - .def("deepcopy", (smtk::model::AddAuxiliaryGeometry & (smtk::model::AddAuxiliaryGeometry::*)(::smtk::model::AddAuxiliaryGeometry const &)) &smtk::model::AddAuxiliaryGeometry::operator=) .def_static("create", (std::shared_ptr<smtk::model::AddAuxiliaryGeometry> (*)()) &smtk::model::AddAuxiliaryGeometry::create) .def_static("create", (std::shared_ptr<smtk::model::AddAuxiliaryGeometry> (*)(::std::shared_ptr<smtk::model::AddAuxiliaryGeometry> &)) &smtk::model::AddAuxiliaryGeometry::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::model::AddAuxiliaryGeometry> (smtk::model::AddAuxiliaryGeometry::*)() const) &smtk::model::AddAuxiliaryGeometry::shared_from_this) diff --git a/smtk/model/pybind11/PybindCloseModel.h b/smtk/model/pybind11/PybindCloseModel.h index 7167f61c1b8e45bdedcda4c864265776e1231643..5fe80ad9194da63678985e524b9edc7f5c18ebc7 100644 --- a/smtk/model/pybind11/PybindCloseModel.h +++ b/smtk/model/pybind11/PybindCloseModel.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::model::CloseModel, smtk::operation::XMLOperation PySharedPtrClass< smtk::model::CloseModel, smtk::operation::XMLOperation > instance(m, "CloseModel"); instance .def(py::init<>()) - .def(py::init<::smtk::model::CloseModel const &>()) - .def("deepcopy", (smtk::model::CloseModel & (smtk::model::CloseModel::*)(::smtk::model::CloseModel const &)) &smtk::model::CloseModel::operator=) .def("ableToOperate", &smtk::model::CloseModel::ableToOperate) .def_static("create", (std::shared_ptr<smtk::model::CloseModel> (*)()) &smtk::model::CloseModel::create) .def_static("create", (std::shared_ptr<smtk::model::CloseModel> (*)(::std::shared_ptr<smtk::model::CloseModel> &)) &smtk::model::CloseModel::create, py::arg("ref")) diff --git a/smtk/model/pybind11/PybindCreateInstances.h b/smtk/model/pybind11/PybindCreateInstances.h index 9a5a13291108281a31a3212dff801227d0379eb7..decbb5e9ab0f30b9d999308acd12c7bfe1426ad4 100644 --- a/smtk/model/pybind11/PybindCreateInstances.h +++ b/smtk/model/pybind11/PybindCreateInstances.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::model::CreateInstances, smtk::operation::XMLOpera PySharedPtrClass< smtk::model::CreateInstances, smtk::operation::XMLOperation > instance(m, "CreateInstances"); instance .def(py::init<>()) - .def(py::init<::smtk::model::CreateInstances const &>()) - .def("deepcopy", (smtk::model::CreateInstances & (smtk::model::CreateInstances::*)(::smtk::model::CreateInstances const &)) &smtk::model::CreateInstances::operator=) .def_static("create", (std::shared_ptr<smtk::model::CreateInstances> (*)()) &smtk::model::CreateInstances::create) .def_static("create", (std::shared_ptr<smtk::model::CreateInstances> (*)(::std::shared_ptr<smtk::model::CreateInstances> &)) &smtk::model::CreateInstances::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::model::CreateInstances> (smtk::model::CreateInstances::*)() const) &smtk::model::CreateInstances::shared_from_this) diff --git a/smtk/model/pybind11/PybindDelete.h b/smtk/model/pybind11/PybindDelete.h index 21922018b34b6f3dfebb123173b6f39919c8173f..620aef924a7cab21760ebc2e06bdc04f3dd63ced 100644 --- a/smtk/model/pybind11/PybindDelete.h +++ b/smtk/model/pybind11/PybindDelete.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::model::Delete, smtk::operation::XMLOperation > py PySharedPtrClass< smtk::model::Delete, smtk::operation::XMLOperation > instance(m, "Delete"); instance .def(py::init<>()) - .def(py::init<::smtk::model::Delete const &>()) - .def("deepcopy", (smtk::model::Delete & (smtk::model::Delete::*)(::smtk::model::Delete const &)) &smtk::model::Delete::operator=) .def_static("create", (std::shared_ptr<smtk::model::Delete> (*)()) &smtk::model::Delete::create) .def_static("create", (std::shared_ptr<smtk::model::Delete> (*)(::std::shared_ptr<smtk::model::Delete> &)) &smtk::model::Delete::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::model::Delete> (smtk::model::Delete::*)() const) &smtk::model::Delete::shared_from_this) diff --git a/smtk/model/pybind11/PybindExportModelJSON.h b/smtk/model/pybind11/PybindExportModelJSON.h index 87fafcd78d307dad6f5ea10e96f5be8a43fd9a72..72a08fc51e07fbcb76af4fe9fe8fa626341fc958 100644 --- a/smtk/model/pybind11/PybindExportModelJSON.h +++ b/smtk/model/pybind11/PybindExportModelJSON.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::model::ExportModelJSON, smtk::operation::XMLOpera PySharedPtrClass< smtk::model::ExportModelJSON, smtk::operation::XMLOperation > instance(m, "ExportModelJSON"); instance .def(py::init<>()) - .def(py::init<::smtk::model::ExportModelJSON const &>()) - .def("deepcopy", (smtk::model::ExportModelJSON & (smtk::model::ExportModelJSON::*)(::smtk::model::ExportModelJSON const &)) &smtk::model::ExportModelJSON::operator=) .def_static("create", (std::shared_ptr<smtk::model::ExportModelJSON> (*)()) &smtk::model::ExportModelJSON::create) .def_static("create", (std::shared_ptr<smtk::model::ExportModelJSON> (*)(::std::shared_ptr<smtk::model::ExportModelJSON> &)) &smtk::model::ExportModelJSON::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::model::ExportModelJSON> (smtk::model::ExportModelJSON::*)() const) &smtk::model::ExportModelJSON::shared_from_this) diff --git a/smtk/operation/Operation.cxx b/smtk/operation/Operation.cxx index 0f4d6dcb7c071a6ba2f3190afd25667ba0807737..01c3f2c585a0d933c97f48378967c3e0af8b5ab8 100644 --- a/smtk/operation/Operation.cxx +++ b/smtk/operation/Operation.cxx @@ -139,6 +139,13 @@ Operation::Result Operation::operate() Operation::Result Operation::operate(const BaseKey& key) { + std::multimap<Priority, Handler> instanceHandlers; + { + std::lock_guard<std::mutex> guard(m_handlerLock); + instanceHandlers = m_handlers; + m_handlers.clear(); + } + // Gather all requested resources and their lock types. const auto resourcesAndLockTypes = this->identifyLocksRequired(); @@ -373,6 +380,14 @@ Operation::Result Operation::operate(const BaseKey& key) } // Execute post-operation observation + // Always call handlers, but based on the \a key, observers may be skipped. + // Handlers will always be invoked before other observers, but in priority order. + // In the future, we may attempt to interleave the handler and observer calls. + for (const auto& entry : instanceHandlers) + { + entry.second(*this, result); + } + instanceHandlers.clear(); if (key.m_observerOption == ObserverOption::InvokeObservers && observePostOperation && manager) { manager->observers()(*this, EventType::DID_OPERATE, result); @@ -529,6 +544,34 @@ Operation::Result Operation::createResult(Outcome outcome) return result; } +void Operation::addHandler(Handler handler, Priority priority) +{ + std::lock_guard<std::mutex> guard(m_handlerLock); + m_handlers.emplace(priority, handler); +} + +bool Operation::removeHandler(Handler handler, Priority priority) +{ + std::lock_guard<std::mutex> guard(m_handlerLock); + for (auto it = m_handlers.lower_bound(priority); it != m_handlers.upper_bound(priority); ++it) + { + if (it->second.target<Handler>() == handler.target<Handler>()) + { + m_handlers.erase(it); + return true; + } + } + return false; +} + +bool Operation::clearHandlers() +{ + std::lock_guard<std::mutex> guard(m_handlerLock); + bool didRemove = !m_handlers.empty(); + m_handlers.clear(); + return didRemove; +} + void Operation::markModifiedResources(Operation::Result& result) { // Gather all requested resources and their lock types. diff --git a/smtk/operation/Operation.h b/smtk/operation/Operation.h index 35df8ee43762e1639b3c798b3f56485744450d5d..4489886249e910ed237af8cd340350f5d93015b1 100644 --- a/smtk/operation/Operation.h +++ b/smtk/operation/Operation.h @@ -18,6 +18,7 @@ #include <functional> #include <map> +#include <mutex> #include <string> #include <typeindex> #include <utility> @@ -40,6 +41,7 @@ class Helper; class ImportPythonOperation; class Manager; class Operation; +class PythonRunChild; using Handler = std::function<void(Operation&, const std::shared_ptr<smtk::attribute::Attribute>&)>; @@ -66,17 +68,20 @@ class SMTKCORE_EXPORT Operation : smtkEnableSharedPtr(Operation) public: smtkTypeMacroBase(smtk::operation::Operation); - // A hash value uniquely representing the operation. + /// A priority for Handlers. + using Priority = int; + + /// A hash value uniquely representing the operation. typedef std::size_t Index; - // An attribute describing the operation's input. + /// An attribute describing the operation's input. typedef std::shared_ptr<smtk::attribute::Attribute> Parameters; - // An attribute containing the operation's result. + /// An attribute containing the operation's result. typedef std::shared_ptr<smtk::attribute::Attribute> Result; - // An attribute resource containing the operation's execution definition - // result definition. + /// An attribute resource containing the operation's execution definition + /// result definition. typedef std::shared_ptr<smtk::attribute::Resource> Specification; typedef std::shared_ptr<smtk::attribute::Definition> Definition; @@ -185,6 +190,33 @@ public: /// attribute is distinguished by its derivation from the "result" attribute. Result createResult(Outcome); + /// Pass a \a handler to invoke after the operation's completion (successful or not). + /// The \a handler is called only on this instance of the operation (as opposed to + /// observers of the operation manager, which are invoked for every operation). + /// + /// The \a priority is used to schedule the \a handler relative to other observers. + /// + /// Handlers are cleared each time the operation is run, so if you run the same + /// instance of an operation multiple times, you must add the handler before + /// launching the operation each time. + /// + /// Note that you are expected to avoid launching the same instance of an operation + /// multiple times without ensuring that the operation is not already queued. + /// Otherwise, handlers may be added or removed while a previous instance is running + /// – including removal after the first instance of the operation has completed but + /// before successive instances – making invocation of handlers intermittent after the + /// first operation. + /// + /// A handler may add itself or other handlers back to the operation instance + /// as the operation will copy the handler container before invoking handlers. + void addHandler(Handler handler, Priority priority); + + /// Remove a \a handler from the operation. + bool removeHandler(Handler handler, Priority priority); + + /// Clear all handlers from the operation. + bool clearHandlers(); + /// Operations that are managed have a non-null pointer to their manager. ManagerPtr manager() const { return m_manager.lock(); } @@ -228,6 +260,7 @@ public: }; protected: + friend class PythonRunChild; Operation(); /// Identify resources to lock, and whether to lock them for reading or writing. @@ -329,6 +362,8 @@ private: Definition m_resultDefinition; std::vector<std::weak_ptr<smtk::attribute::Attribute>> m_results; ResourceAccessMap m_lockedResources; + std::mutex m_handlerLock; + std::multimap<Priority, Handler> m_handlers; }; /**\brief Return the outcome of an operation given its \a result object. diff --git a/smtk/operation/operators/WriteResource.cxx b/smtk/operation/operators/WriteResource.cxx index 2d38d771903ffde1e4d6616a6743ae3bfb6bd780..7e4c6fe3c589e8490aa970d3a89bb24ea6bfdc2d 100644 --- a/smtk/operation/operators/WriteResource.cxx +++ b/smtk/operation/operators/WriteResource.cxx @@ -213,8 +213,7 @@ void WriteResource::generateSummary(WriteResource::Result& res) } else if (outcome == static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED)) { - msg << ": wrote \"" << resource->location() << "\""; - smtkInfoMacro(this->log(), msg.str()); + // Do nothing. Each child operation should provide a summary. } else { diff --git a/smtk/operation/pybind11/PybindOperation.h b/smtk/operation/pybind11/PybindOperation.h index b0dddeafa818bdd229e0edaadc2627c3ddac4e75..5e1775d1e8b1ec6c4bd886a8bc8149b1c6d79eb6 100644 --- a/smtk/operation/pybind11/PybindOperation.h +++ b/smtk/operation/pybind11/PybindOperation.h @@ -22,6 +22,43 @@ #include "smtk/io/Logger.h" +namespace smtk +{ +namespace operation +{ + +/// A helper class that is a friend of smtk::operation::Operation so it +/// can construct a Key to run "child" (nested) operations. +class PythonRunChild +{ +public: + using Result = smtk::operation::Operation::Result; + using ObserverOption = smtk::operation::Operation::ObserverOption; + using LockOption = smtk::operation::Operation::LockOption; + using ParametersOption = smtk::operation::Operation::ParametersOption; + + PythonRunChild(smtk::operation::Operation* self) + : m_self(self) + { + } + + Result run( + const smtk::operation::Operation::Ptr& childOp, + ObserverOption observerOption, + LockOption lockOption, + ParametersOption paramsOption) + { + auto key = m_self->childKey(observerOption, lockOption, paramsOption); + return childOp->operate(key); + } + +protected: + smtk::operation::Operation* m_self{ nullptr }; +}; + +} // namespace operation +} // namespace smtk + namespace py = pybind11; inline PySharedPtrClass< smtk::operation::Operation, smtk::operation::PyOperation > pybind11_init_smtk_operation_Operation(py::module &m) @@ -46,7 +83,6 @@ inline PySharedPtrClass< smtk::operation::Operation, smtk::operation::PyOperatio instance .def(py::init<>()) - .def("deepcopy", (smtk::operation::Operation & (smtk::operation::Operation::*)(::smtk::operation::Operation const &)) &smtk::operation::Operation::operator=) .def_static("create", &smtk::operation::PyOperation::create) .def("typeName", &smtk::operation::Operation::typeName) .def("index", &smtk::operation::Operation::index) @@ -101,7 +137,23 @@ inline PySharedPtrClass< smtk::operation::Operation, smtk::operation::PyOperatio .def("createResult", &smtk::operation::Operation::createResult, py::arg("arg0")) .def("manager", &smtk::operation::Operation::manager) .def("managers", &smtk::operation::Operation::managers) + .def("addHandler", &smtk::operation::Operation::addHandler, py::arg("handler"), py::arg("priority")) + .def("removeHandler", &smtk::operation::Operation::removeHandler, py::arg("handler"), py::arg("priority")) + .def("clearHandlers", &smtk::operation::Operation::clearHandlers) .def("restoreTrace", (bool (smtk::operation::Operation::*)(::std::string const &)) &smtk::operation::Operation::restoreTrace) + .def("runChildOp", [](smtk::operation::Operation* self, const smtk::operation::Operation::Ptr& childOp, + smtk::operation::Operation::ObserverOption observerOption, + smtk::operation::Operation::LockOption lockOption, + smtk::operation::Operation::ParametersOption paramsOption) + { + smtk::operation::PythonRunChild runner(self); + auto result = runner.run(childOp, observerOption, lockOption, paramsOption); + return result; + }, + py::arg("childOp"), + py::arg("observerOption") = smtk::operation::Operation::ObserverOption::SkipObservers, + py::arg("lockOption") = smtk::operation::Operation::LockOption::ParentLocksOnly, + py::arg("paramsOption") = smtk::operation::Operation::ParametersOption::SkipValidation) ; py::enum_<smtk::operation::Operation::Outcome>(instance, "Outcome") .value("UNABLE_TO_OPERATE", smtk::operation::Operation::Outcome::UNABLE_TO_OPERATE) diff --git a/smtk/operation/pybind11/PybindReadResource.h b/smtk/operation/pybind11/PybindReadResource.h index 22e57381016f83e841bad7100147667b484b804e..1ec92d4cf290a6a4d4331e89230fdf5a1d58a932 100644 --- a/smtk/operation/pybind11/PybindReadResource.h +++ b/smtk/operation/pybind11/PybindReadResource.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::operation::ReadResource, smtk::operation::XMLOper { PySharedPtrClass< smtk::operation::ReadResource, smtk::operation::XMLOperation > instance(m, "ReadResource"); instance - .def(py::init<::smtk::operation::ReadResource const &>()) - .def("deepcopy", (smtk::operation::ReadResource & (smtk::operation::ReadResource::*)(::smtk::operation::ReadResource const &)) &smtk::operation::ReadResource::operator=) .def_static("create", (std::shared_ptr<smtk::operation::ReadResource> (*)()) &smtk::operation::ReadResource::create) .def_static("create", (std::shared_ptr<smtk::operation::ReadResource> (*)(::std::shared_ptr<smtk::operation::ReadResource> &)) &smtk::operation::ReadResource::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::operation::ReadResource> (smtk::operation::ReadResource::*)() const) &smtk::operation::ReadResource::shared_from_this) diff --git a/smtk/operation/pybind11/PybindRemoveResource.h b/smtk/operation/pybind11/PybindRemoveResource.h index edb6de9498bac7fd369563f185d3c7f023134439..13ef374a6f521e0c5ab320507c205389d4ebb61f 100644 --- a/smtk/operation/pybind11/PybindRemoveResource.h +++ b/smtk/operation/pybind11/PybindRemoveResource.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::operation::RemoveResource, smtk::operation::XMLOp { PySharedPtrClass< smtk::operation::RemoveResource, smtk::operation::XMLOperation > instance(m, "RemoveResource"); instance - .def(py::init<::smtk::operation::RemoveResource const &>()) - .def("deepcopy", (smtk::operation::RemoveResource & (smtk::operation::RemoveResource::*)(::smtk::operation::RemoveResource const &)) &smtk::operation::RemoveResource::operator=) .def_static("create", (std::shared_ptr<smtk::operation::RemoveResource> (*)()) &smtk::operation::RemoveResource::create) .def_static("create", (std::shared_ptr<smtk::operation::RemoveResource> (*)(::std::shared_ptr<smtk::operation::RemoveResource> &)) &smtk::operation::RemoveResource::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::operation::RemoveResource> (smtk::operation::RemoveResource::*)() const) &smtk::operation::RemoveResource::shared_from_this) diff --git a/smtk/operation/pybind11/PybindSetProperty.h b/smtk/operation/pybind11/PybindSetProperty.h index 2e0d964431dd996fb3f4bdafe8ef76eb793ad58b..9aeec35f7a4f8b9357cba56bb2fca77010f42368 100644 --- a/smtk/operation/pybind11/PybindSetProperty.h +++ b/smtk/operation/pybind11/PybindSetProperty.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::operation::SetProperty, smtk::operation::XMLOpera PySharedPtrClass< smtk::operation::SetProperty, smtk::operation::XMLOperation > instance(m, "SetProperty"); instance .def(py::init<>()) - .def(py::init<::smtk::operation::SetProperty const &>()) - .def("deepcopy", (smtk::operation::SetProperty & (smtk::operation::SetProperty::*)(::smtk::operation::SetProperty const &)) &smtk::operation::SetProperty::operator=) .def_static("create", (std::shared_ptr<smtk::operation::SetProperty> (*)()) &smtk::operation::SetProperty::create) .def_static("create", (std::shared_ptr<smtk::operation::SetProperty> (*)(::std::shared_ptr<smtk::operation::SetProperty> &)) &smtk::operation::SetProperty::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::operation::SetProperty> (smtk::operation::SetProperty::*)() const) &smtk::operation::SetProperty::shared_from_this) diff --git a/smtk/operation/pybind11/PybindWriteResource.h b/smtk/operation/pybind11/PybindWriteResource.h index f54bbefa6b22d4f592b53ec0e36777b26de3db1e..c679383b9429544180a13b2701cbcb8a4f9b88ff 100644 --- a/smtk/operation/pybind11/PybindWriteResource.h +++ b/smtk/operation/pybind11/PybindWriteResource.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::operation::WriteResource, smtk::operation::XMLOpe { PySharedPtrClass< smtk::operation::WriteResource, smtk::operation::XMLOperation > instance(m, "WriteResource"); instance - .def(py::init<::smtk::operation::WriteResource const &>()) - .def("deepcopy", (smtk::operation::WriteResource & (smtk::operation::WriteResource::*)(::smtk::operation::WriteResource const &)) &smtk::operation::WriteResource::operator=) .def_static("create", (std::shared_ptr<smtk::operation::WriteResource> (*)()) &smtk::operation::WriteResource::create) .def_static("create", (std::shared_ptr<smtk::operation::WriteResource> (*)(::std::shared_ptr<smtk::operation::WriteResource> &)) &smtk::operation::WriteResource::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::operation::WriteResource> (smtk::operation::WriteResource::*)() const) &smtk::operation::WriteResource::shared_from_this) diff --git a/smtk/operation/pybind11/PybindXMLOperation.h b/smtk/operation/pybind11/PybindXMLOperation.h index 5bad73ffdc45fd2d4f26a3a2491c8eccaaf8f1de..dce73b0f001301564500eab904d352e5bd5e943a 100644 --- a/smtk/operation/pybind11/PybindXMLOperation.h +++ b/smtk/operation/pybind11/PybindXMLOperation.h @@ -23,7 +23,6 @@ inline PySharedPtrClass< smtk::operation::XMLOperation, smtk::operation::Operati { PySharedPtrClass< smtk::operation::XMLOperation, smtk::operation::Operation > instance(m, "XMLOperation"); instance - .def("deepcopy", (smtk::operation::XMLOperation & (smtk::operation::XMLOperation::*)(::smtk::operation::XMLOperation const &)) &smtk::operation::XMLOperation::operator=) .def("shared_from_this", (std::shared_ptr<const smtk::operation::XMLOperation> (smtk::operation::XMLOperation::*)() const) &smtk::operation::XMLOperation::shared_from_this) .def("shared_from_this", (std::shared_ptr<smtk::operation::XMLOperation> (smtk::operation::XMLOperation::*)()) &smtk::operation::XMLOperation::shared_from_this) ; diff --git a/smtk/operation/testing/python/CMakeLists.txt b/smtk/operation/testing/python/CMakeLists.txt index 9859534ed86424707621c97f2287698e405ded8f..218eec617485a937b647c82ff4979ac33d1ee988 100644 --- a/smtk/operation/testing/python/CMakeLists.txt +++ b/smtk/operation/testing/python/CMakeLists.txt @@ -6,6 +6,12 @@ set(smtkOperationPythonTests set(smtkOperationPythonDataTests ) +if (SMTK_ENABLE_POLYGON_SESSION) + list(APPEND smtkOperationPythonDataTests + testOperationHandler + ) +endif() + if(SMTK_ENABLE_PARAVIEW_SUPPORT) list(APPEND smtkOperationPythonDataTests testOperationTracing diff --git a/smtk/operation/testing/python/testOperationHandler.py b/smtk/operation/testing/python/testOperationHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..8d6fb2558c922cc6c7cecb8a1fc3095e311a98ea --- /dev/null +++ b/smtk/operation/testing/python/testOperationHandler.py @@ -0,0 +1,111 @@ +# ============================================================================= +# +# Copyright (c) Kitware, Inc. +# All rights reserved. +# See LICENSE.txt 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. +# +# ============================================================================= +import smtk +import smtk.attribute +import smtk.operation +import smtk.session.polygon +import smtk.testing + +invokedCount = 0 +message = 'test' +addSelf = False + +def handler(op, result): + global message, invokedCount + invokedCount += 1 + print('%s: Handler invoked (%d)' % (message, invokedCount)) + if addSelf: + op.addHandler(handler, 0) + +class TestOperationHandler(smtk.testing.TestCase): + + def testSingleHandler(self): + import os + global message, invokedCount + self.mgr = smtk.model.Resource.create() + fpath = [smtk.testing.DATA_DIR, 'model', + '2d', 'smtk', 'epic-trex-drummer.smtk'] + op = smtk.session.polygon.Read.create() + op.parameters().find('filename').setValue(os.path.join(*fpath)) + print + op.addHandler(handler, 0) + op.addHandler(handler, 1) + op.removeHandler(handler, 1) + invokedCount = 0 + message = 'testSingleHandler' + res = op.operate() + if res.find('outcome').value(0) != int(smtk.operation.Operation.SUCCEEDED): + raise RuntimeError + self.assertEqual(invokedCount, 1, 'Expected to be called exactly once.') + + def testMultipleHandlers(self): + import os + global message, invokedCount + self.mgr = smtk.model.Resource.create() + fpath = [smtk.testing.DATA_DIR, 'model', + '2d', 'smtk', 'epic-trex-drummer.smtk'] + op = smtk.session.polygon.Read.create() + op.parameters().find('filename').setValue(os.path.join(*fpath)) + op.addHandler(handler, 0) + op.addHandler(handler, 1) + invokedCount = 0 + message = 'testMultipleHandlers' + res = op.operate() + if res.find('outcome').value(0) != int(smtk.operation.Operation.SUCCEEDED): + raise RuntimeError + self.assertEqual(invokedCount, 2, 'Expected to be called exactly twice.') + + def testMultipleTimes(self): + import os + global message, invokedCount, addSelf + self.mgr = smtk.model.Resource.create() + fpath = [smtk.testing.DATA_DIR, 'model', + '2d', 'smtk', 'epic-trex-drummer.smtk'] + op = smtk.session.polygon.Read.create() + op.parameters().find('filename').setValue(os.path.join(*fpath)) + op.addHandler(handler, 0) + op.addHandler(handler, 1) + addSelf = True + invokedCount = 0 + message = 'testMultipleTimes (first time)' + res = op.operate() + if res.find('outcome').value(0) != int(smtk.operation.Operation.SUCCEEDED): + raise RuntimeError + self.assertEqual(invokedCount, 2, 'Expected to be called exactly twice.') + + # Invoke the operation a second time. + invokedCount = 0 + message = 'testMultipleTimes (second time)' + # Remove one of the handlers that got re-added since "addSelf" was true. + op.removeHandler(handler, 0) + addSelf = False + res = op.operate() + if res.find('outcome').value(0) != int(smtk.operation.Operation.SUCCEEDED): + raise RuntimeError + self.assertEqual(invokedCount, 1, 'Expected to be called after first operation.') + + # Invoke the operation a third time (with no re-add of handler). + invokedCount = 0 + message = 'testMultipleTimes (third time)' + addSelf = False + res = op.operate() + if res.find('outcome').value(0) != int(smtk.operation.Operation.SUCCEEDED): + raise RuntimeError + self.assertEqual(invokedCount, 0, 'Expected to be called no more after the first operation.') + + # Test that no handlers remain on the operation + didRemove = op.clearHandlers() + self.assertFalse(didRemove, 'Expected no handlers remaining.') + +if __name__ == '__main__': + smtk.testing.process_arguments() + smtk.testing.main() diff --git a/smtk/project/operators/Write.cxx b/smtk/project/operators/Write.cxx index 960e67a5c83b43c8fd81e979f1e585bd3924864f..af50c76532a508dd15aae888ab0b013664b519b6 100644 --- a/smtk/project/operators/Write.cxx +++ b/smtk/project/operators/Write.cxx @@ -173,6 +173,17 @@ Write::Result Write::operateInternal() return result; } +void Write::generateSummary(Operation::Result& res) +{ + if (smtk::operation::outcome(res) != Outcome::SUCCEEDED) + { + this->Superclass::generateSummary(res); + } + auto resourceItem = this->parameters()->associations(); + auto resource = std::dynamic_pointer_cast<smtk::resource::Resource>(resourceItem->value()); + smtkInfoMacro(this->log(), "Wrote \"" << resource->location() << "\"."); +} + const char* Write::xmlDescription() const { return Write_xml; diff --git a/smtk/project/operators/Write.h b/smtk/project/operators/Write.h index 5a5c85f51f885db648cf64d7d26735237217cb89..e2c97f457b83d1d215b13b7cff30b46ad9a25976 100644 --- a/smtk/project/operators/Write.h +++ b/smtk/project/operators/Write.h @@ -34,6 +34,7 @@ public: protected: Result operateInternal() override; + void generateSummary(Operation::Result& res) override; const char* xmlDescription() const override; }; diff --git a/smtk/project/pybind11/PybindOperation.h b/smtk/project/pybind11/PybindOperation.h index 306815c304ca624a9320e82234b8c0e647584a64..88667341566773b6d60c6007a3f27534b3590649 100644 --- a/smtk/project/pybind11/PybindOperation.h +++ b/smtk/project/pybind11/PybindOperation.h @@ -26,7 +26,6 @@ inline PySharedPtrClass< smtk::project::Operation, smtk::operation::XMLOperation PySharedPtrClass< smtk::project::Operation, smtk::operation::XMLOperation > instance(m, "Operation"); instance - .def("deepcopy", (smtk::project::Operation & (smtk::project::Operation::*)(::smtk::project::Operation const &)) &smtk::project::Operation::operator=) .def("typeName", &smtk::project::Operation::typeName) .def("shared_from_this", (std::shared_ptr<smtk::project::Operation> (smtk::project::Operation::*)()) &smtk::project::Operation::shared_from_this) .def("shared_from_this", (std::shared_ptr<const smtk::project::Operation> (smtk::project::Operation::*)() const) &smtk::project::Operation::shared_from_this) diff --git a/smtk/resource/Artifact.cxx b/smtk/resource/Artifact.cxx new file mode 100644 index 0000000000000000000000000000000000000000..bee50b7b6ecb795f6b2b06fc921d752f1e86eda7 --- /dev/null +++ b/smtk/resource/Artifact.cxx @@ -0,0 +1,60 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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 "smtk/resource/Artifact.h" + +namespace smtk +{ +namespace resource +{ + +bool Artifact::setLocation(URL location) +{ + if (m_location == location) + { + return false; + } + m_location = location; + return true; +} + +bool Artifact::setChecksum(smtk::string::Token algorithm, const std::vector<std::uint8_t>& value) +{ + if (m_checksumAlgorithm == algorithm && m_checksumData == value) + { + return false; + } + m_checksumAlgorithm = algorithm; + m_checksumData = value; + return true; +} + +bool Artifact::setTimestamp(smtk::string::Token format, const std::vector<std::uint8_t>& value) +{ + if (m_timestampFormat == format && m_timestampData == value) + { + return false; + } + m_timestampFormat = format; + m_timestampData = value; + return true; +} + +bool Artifact::setExtant(bool isExtant) +{ + if (m_extant == isExtant) + { + return false; + } + m_extant = isExtant; + return true; +} + +} // namespace resource +} // namespace smtk diff --git a/smtk/resource/Artifact.h b/smtk/resource/Artifact.h new file mode 100644 index 0000000000000000000000000000000000000000..4fa7bbac7f47f00368d81835e141e3afe2b03deb --- /dev/null +++ b/smtk/resource/Artifact.h @@ -0,0 +1,121 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= +#ifndef smtk_resource_Artifact_h +#define smtk_resource_Artifact_h + +#include "smtk/resource/Component.h" +#include "smtk/common/URL.h" // For ivar and API. + +namespace smtk +{ +namespace resource +{ + +/**\brief An artifact is a component that represents external data. + * + * Artifacts are typically used to represent files that hold opaque + * data relevant to a simulation. Examples include historical input + * decks, job logs, job results files, meshes of simulation domains. + * + * Artifacts may be stored inside or outside of a resource (i.e., they + * may be shared among multiple resources or they may be considered + * owned by a single resource). + * + * Artifacts may be small or large. + * + * What characterizes artifacts is that they reference data external + * to a resource. The data is referenced via an smtk::common::URL. + * URLs may be absolute (and must be if the data is stored external to + * the resource) or relative (and should be if the data is stored internal + * to the resource). Any relative URL is considered relative to its parent + * resource's location. + * + * Artifacts may also be characterized by a checksum and a timestamp. + * This allows resources to determine whether an artifact has been + * modified since the last time it was accessed. + * + * Note that this class is abstract; if you wish your resource to store + * instances of Artifact, you will need to subclass it to provide + * + */ +class SMTKCORE_EXPORT Artifact : public PersistentObject +{ +public: + using URL = smtk::common::URL; + + smtkTypeMacro(smtk::resource::Artifact); + smtkSuperclassMacro(smtk::resource::Component); + smtkSharedFromThisMacro(smtk::resource::PersistentObject); + + ~Artifact() override = default; + + // const smtk::common::UUID& id() const override { return m_id; } + // void setId(const smtk::common::UUID& uid) override { m_id = uid; } + + /// Return the location of the artifact's data. + const URL& location() const { return m_location; } + + /// Set the location of the artifact's data. + bool setLocation(URL location); + + /// Indicate whether the artifact has a checksum provided by returning + /// the checksum algorithm. + /// + /// If the returned token is invalid, no checksum is available. + smtk::string::Token hasChecksum() const { return m_checksumAlgorithm; } + + /// Return the artifact's checksum (if it has one). + std::vector<std::uint8_t> checksumData() const { return m_checksumData; } + + /// Set the artifact's current checksum. + virtual bool setChecksum(smtk::string::Token algorithm, const std::vector<std::uint8_t>& value); + + /// Indicate whether the artifact has a timestamp provided by returning + /// the timestamp format. + /// + /// If the returned token is invalid, no timestamp is available. + smtk::string::Token hasTimestamp() const { return m_timestampFormat; } + + /// Return the artifact's timestamp (if it has one). + std::vector<std::uint8_t> timestampData() const { return m_timestampData; } + + /// Set the artifact's current timestamp. + virtual bool setTimestamp(smtk::string::Token format, const std::vector<std::uint8_t>& value); + + /// Return true if the artifact is still extant and false if expired. + /// + /// If an application determines an artifact is no longer accessible, + /// it may call setExtant(). By default, artifacts are extant upon + /// creation. + bool extant() const { return m_extant; } + + /// Set whether an artifact exists at its location or not. + /// + /// Artifacts may not be extant initially (for example a log file may not + /// exist before a simulation job has commenced) or finally (for example a + /// log file may be removed after a certain time) or at other times in + /// the lifecycle of the artifact. + bool setExtant(bool isExtant); + +protected: + Artifact(); + + smtk::common::UUID m_id; + URL m_location; + smtk::string::Token m_checksumAlgorithm; + std::vector<std::uint8_t> m_checksumData; + smtk::string::Token m_timestampFormat; + std::vector<std::uint8_t> m_timestampData; + bool m_extant{ true }; +}; +} // namespace resource +} // namespace smtk + +#endif // smtk_resource_Artifact_h diff --git a/smtk/resource/CMakeLists.txt b/smtk/resource/CMakeLists.txt index ea3e64e7568747fc8d34f7ae0a116f1edc581a26..82e0a32ed9ff6ac0844a958d78e1b7a71214a4d8 100644 --- a/smtk/resource/CMakeLists.txt +++ b/smtk/resource/CMakeLists.txt @@ -1,5 +1,6 @@ # set up sources to build set(resourceSrcs + Artifact.cxx Component.cxx ComponentLinks.cxx CopyOptions.cxx @@ -26,6 +27,7 @@ set(resourceSrcs ) set(resourceHeaders + Artifact.h Component.h ComponentLinks.h Container.h diff --git a/smtk/resource/Resource.cxx b/smtk/resource/Resource.cxx index de90b2788ab405d51498c8f875620ba2e961af88..9523d7f866df462d211f0676c28787f51738262c 100644 --- a/smtk/resource/Resource.cxx +++ b/smtk/resource/Resource.cxx @@ -200,6 +200,39 @@ bool Resource::setLocation(const std::string& myLocation) return false; } +std::string Resource::absoluteLocation(bool createDir) const +{ + auto url = this->location(); + if (!smtk::common::Paths::isRelative(url)) + { + if (createDir) + { + auto containingDir = smtk::common::Paths::directory(url); + smtk::common::Paths::createDirectory(containingDir); + } + return url; + } + const auto* parent = this->parentResource(); + if (parent && parent != this) + { + auto parentUrl = parent->absoluteLocation(); + // The parent URL contains the filename of the parent resource. Strip that: + auto parentDir = smtk::common::Paths::directory(parentUrl); + url = parentDir + "/" + url; + } + else + { + auto workDir = smtk::common::Paths::currentDirectory(); + url = workDir + "/" + url; + } + if (createDir) + { + auto containingDir = smtk::common::Paths::directory(url); + smtk::common::Paths::createDirectory(containingDir); + } + return url; +} + std::string Resource::name() const { if (m_name.empty()) @@ -225,9 +258,9 @@ void Resource::setClean(bool state) m_clean = state; } -bool Resource::setUnitsSystem(const shared_ptr<units::System>& unitsSystem) +bool Resource::setUnitSystem(const shared_ptr<units::System>& unitSystem) { - m_unitsSystem = unitsSystem; + m_unitSystem = unitSystem; return true; } @@ -285,15 +318,15 @@ void Resource::copyUnitSystem( // Do not set a unit system. break; case CopyOptions::CopyType::Shallow: - this->setUnitsSystem(rsrc->unitsSystem()); + this->setUnitSystem(rsrc->unitSystem()); break; case CopyOptions::CopyType::Deep: { - if (auto unitSys = rsrc->unitsSystem()) + if (auto unitSys = rsrc->unitSystem()) { nlohmann::json spec = unitSys; shared_ptr<units::System> unitCopy = units::System::createFromSpec(spec.dump()); - this->setUnitsSystem(unitCopy); + this->setUnitSystem(unitCopy); } } break; diff --git a/smtk/resource/Resource.h b/smtk/resource/Resource.h index c7d1c9706ba33a9abab6c96fc87b19eeada70475..39c40e0897f3eda74a556ab72e8a50537f60073f 100644 --- a/smtk/resource/Resource.h +++ b/smtk/resource/Resource.h @@ -13,6 +13,7 @@ #include "smtk/CoreExports.h" +#include "smtk/common/Deprecation.h" #include "smtk/common/UUID.h" #include "smtk/resource/Component.h" @@ -170,6 +171,28 @@ public: /// This may change when a user chooses to "Save As…" a different filename. const std::string& location() const { return m_location; } virtual bool setLocation(const std::string& location); + + /// As a convenience, fetch the absolute path to a resource from its location. + /// + /// For URLs with a protocol (e.g., "https://foo.com/resource.smtk") or local + /// paths with a leading slash (e.g., "/foo/resource.smtk"), this is exactly + /// the resource's location() string. + /// + /// However, if a resource has a relative path, we must look to see whether + /// it has a parent resource; if so, this method is recursively called on + /// the parent to find its absolute path so that the relative path of the + /// child resource can be determined. + /// + /// When a resource (the immediate one or any parent) has a relative location + /// but no parent, then the current working directory is used to determine + /// its absolute path. + /// + /// As an added convenience, you may pass "true" as a second argument to this + /// method to have it create the containing directory of the resource's + /// location for you. This is for use by Write operations. + /// + /// \sa smtk::common::Paths::isRelative, smtk::common::Paths::currentDirectory + std::string absoluteLocation(bool createDir = false) const; ///@} ///@name Naming @@ -331,9 +354,15 @@ public: /// unit systems. /// \brief Sets the system of units used by this resource. - virtual bool setUnitsSystem(const shared_ptr<units::System>& unitsSystem); + virtual bool setUnitSystem(const shared_ptr<units::System>& unitSystem); + SMTK_DEPRECATED_IN_NEXT("Use setUnitSystem() instead.") + virtual bool setUnitsSystem(const shared_ptr<units::System>& unitsSystem) + { return this->setUnitSystem(unitsSystem); } + /// \brief Gets the system of units used by this resource. - const shared_ptr<units::System>& unitsSystem() const { return m_unitsSystem; } + const shared_ptr<units::System>& unitSystem() const { return m_unitSystem; } + SMTK_DEPRECATED_IN_NEXT("Use setUnitSystem() instead.") + const shared_ptr<units::System>& unitsSystem() const { return m_unitSystem; } ///@} ///@name Resource Templates @@ -489,7 +518,7 @@ protected: void copyLinks(const std::shared_ptr<const Resource>& rsrc, const CopyOptions& options); WeakManagerPtr m_manager; - std::shared_ptr<units::System> m_unitsSystem; + std::shared_ptr<units::System> m_unitSystem; private: /// Instances of this internal class are passed to resource::Manager to @@ -594,6 +623,7 @@ SMTKCORE_NO_EXPORT QueryType& queryForObject(const PersistentObject& object) } throw query::BadTypeError(smtk::common::typeName<QueryType>()); } + } // namespace resource } // namespace smtk diff --git a/smtk/resource/pybind11/PybindResource.h b/smtk/resource/pybind11/PybindResource.h index 8812f707d60393b979750acf8c8f0a6bec19cffe..9ff47c70bc970e05bd204a2347f11699b46c1215 100644 --- a/smtk/resource/pybind11/PybindResource.h +++ b/smtk/resource/pybind11/PybindResource.h @@ -23,6 +23,11 @@ #include "smtk/resource/pybind11/PyResource.h" +// For unit conversion +#include "units/Measurement.h" +#include "units/System.h" +#include "units/Unit.h" + namespace py = pybind11; inline PySharedPtrClass< smtk::resource::Resource, smtk::resource::PyResource, smtk::resource::PersistentObject > pybind11_init_smtk_resource_Resource(py::module &m) @@ -150,6 +155,37 @@ inline PySharedPtrClass< smtk::resource::Resource, smtk::resource::PyResource, s .def("setMarkedForRemoval", &smtk::resource::Resource::setMarkedForRemoval, py::arg("val")) .def("setName", &smtk::resource::Resource::setName, py::arg("name")) .def("typeName", &smtk::resource::Resource::typeName) + .def("createDefaultUnitSystem", [](smtk::resource::Resource* rsrc) + { + auto sys = rsrc->unitSystem(); + if (!sys) + { + sys = units::System::createWithDefaults(); + rsrc->setUnitSystem(sys); + return true; + } + return false; + } + ) + .def("unitConversion", [](smtk::resource::Resource* rsrc, const std::string& measurement, const std::string& unit) -> double + { + double result = std::numeric_limits<double>::quiet_NaN(); + auto sys = rsrc->unitSystem(); + if (!sys) + { + return result; + } + bool didConvert; + auto valueIn = sys->measurement(measurement, &didConvert); + if (!didConvert) { return result; } + auto unitOut = sys->unit(unit, &didConvert); + if (!didConvert) { return result; } + auto valueOut = sys->convert(valueIn, unitOut, &didConvert); + if (!didConvert) { return result; } + result = valueOut.m_value; + return result; + }, py::arg("valueWithUnitsIn"), py::arg("unitsOut") + ) .def("visit", &smtk::resource::Resource::visit, py::arg("v")) .def_static("visuallyLinkedRole", &smtk::resource::Resource::visuallyLinkedRole) .def_readonly_static("VisuallyLinkedRole", &smtk::resource::Resource::VisuallyLinkedRole) diff --git a/smtk/resource/testing/python/CMakeLists.txt b/smtk/resource/testing/python/CMakeLists.txt index a3cff4a0a21bc78aeb776dac67e9f7f7645fd3ec..1a22079cb26041b23222c29d45a4e4de1137c865 100644 --- a/smtk/resource/testing/python/CMakeLists.txt +++ b/smtk/resource/testing/python/CMakeLists.txt @@ -1,5 +1,6 @@ set(smtkResourcePythonTests testResource + testResourceUnitSystem ) # Additional tests that require SMTK_DATA_DIR diff --git a/smtk/resource/testing/python/testResourceUnitSystem.py b/smtk/resource/testing/python/testResourceUnitSystem.py new file mode 100644 index 0000000000000000000000000000000000000000..e4e4ee0222c93f898d21ec8d43a940a7aea9a7e2 --- /dev/null +++ b/smtk/resource/testing/python/testResourceUnitSystem.py @@ -0,0 +1,34 @@ +# ============================================================================= +# +# Copyright (c) Kitware, Inc. +# All rights reserved. +# See LICENSE.txt 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. +# +# ============================================================================= + +import smtk +import smtk.common +import smtk.resource +import smtk.model + +import math + +# Create a resource; by default it has no unit system. +rsrc = smtk.model.Resource.create() +# Test that conversion fails with no unit system. +result = rsrc.unitConversion("1 ft", "m") +if not math.isnan(result): + raise RuntimeError('Unit conversion with no unit system should fail.') + +rsrc.createDefaultUnitSystem() +result = rsrc.unitConversion("1 ft", "m") +if math.fabs(result - 0.3048) > 1e-7: + raise RuntimeError('Unit conversion failed.') + +result = rsrc.unitConversion("1 ft", "second") +if not math.isnan(result): + raise RuntimeError('Unit conversion between incompatible units should fail.') diff --git a/smtk/session/mesh/pybind11/PybindCreateUniformGrid.h b/smtk/session/mesh/pybind11/PybindCreateUniformGrid.h index 8d307ef0eed2850d7c507b8ec5b90b8e3e2bef76..22da73495a7d38a44e8341ba3c77a721532c0021 100644 --- a/smtk/session/mesh/pybind11/PybindCreateUniformGrid.h +++ b/smtk/session/mesh/pybind11/PybindCreateUniformGrid.h @@ -23,9 +23,7 @@ inline PySharedPtrClass< smtk::session::mesh::CreateUniformGrid, smtk::operation { PySharedPtrClass< smtk::session::mesh::CreateUniformGrid, smtk::operation::XMLOperation > instance(m, "CreateUniformGrid"); instance - .def(py::init<::smtk::session::mesh::CreateUniformGrid const &>()) .def(py::init<>()) - .def("deepcopy", (smtk::session::mesh::CreateUniformGrid & (smtk::session::mesh::CreateUniformGrid::*)(::smtk::session::mesh::CreateUniformGrid const &)) &smtk::session::mesh::CreateUniformGrid::operator=) .def_static("create", (std::shared_ptr<smtk::session::mesh::CreateUniformGrid> (*)()) &smtk::session::mesh::CreateUniformGrid::create) .def_static("create", (std::shared_ptr<smtk::session::mesh::CreateUniformGrid> (*)(::std::shared_ptr<smtk::session::mesh::CreateUniformGrid> &)) &smtk::session::mesh::CreateUniformGrid::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<smtk::session::mesh::CreateUniformGrid> (smtk::session::mesh::CreateUniformGrid::*)()) &smtk::session::mesh::CreateUniformGrid::shared_from_this) diff --git a/smtk/session/mesh/pybind11/PybindEulerCharacteristicRatio.h b/smtk/session/mesh/pybind11/PybindEulerCharacteristicRatio.h index f138013f3a82ead4dec0eb9017b65ce5986b7072..c50a531de1fba9f407058b85211c580df3922153 100644 --- a/smtk/session/mesh/pybind11/PybindEulerCharacteristicRatio.h +++ b/smtk/session/mesh/pybind11/PybindEulerCharacteristicRatio.h @@ -23,9 +23,7 @@ inline PySharedPtrClass< smtk::session::mesh::EulerCharacteristicRatio, smtk::op { PySharedPtrClass< smtk::session::mesh::EulerCharacteristicRatio, smtk::operation::XMLOperation > instance(m, "EulerCharacteristicRatio"); instance - .def(py::init<::smtk::session::mesh::EulerCharacteristicRatio const &>()) .def(py::init<>()) - .def("deepcopy", (smtk::session::mesh::EulerCharacteristicRatio & (smtk::session::mesh::EulerCharacteristicRatio::*)(::smtk::session::mesh::EulerCharacteristicRatio const &)) &smtk::session::mesh::EulerCharacteristicRatio::operator=) .def_static("create", (std::shared_ptr<smtk::session::mesh::EulerCharacteristicRatio> (*)()) &smtk::session::mesh::EulerCharacteristicRatio::create) .def_static("create", (std::shared_ptr<smtk::session::mesh::EulerCharacteristicRatio> (*)(::std::shared_ptr<smtk::session::mesh::EulerCharacteristicRatio> &)) &smtk::session::mesh::EulerCharacteristicRatio::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<smtk::session::mesh::EulerCharacteristicRatio> (smtk::session::mesh::EulerCharacteristicRatio::*)()) &smtk::session::mesh::EulerCharacteristicRatio::shared_from_this) diff --git a/smtk/session/mesh/pybind11/PybindExport.h b/smtk/session/mesh/pybind11/PybindExport.h index 631c8e6aeb370f61a54eafb80c73361d99d7b526..a842082da32157bd52370221bb5397e9697b23e9 100644 --- a/smtk/session/mesh/pybind11/PybindExport.h +++ b/smtk/session/mesh/pybind11/PybindExport.h @@ -23,9 +23,7 @@ inline PySharedPtrClass< smtk::session::mesh::Export, smtk::operation::XMLOperat { PySharedPtrClass< smtk::session::mesh::Export, smtk::operation::XMLOperation > instance(m, "Export"); instance - .def(py::init<::smtk::session::mesh::Export const &>()) .def(py::init<>()) - .def("deepcopy", (smtk::session::mesh::Export & (smtk::session::mesh::Export::*)(::smtk::session::mesh::Export const &)) &smtk::session::mesh::Export::operator=) .def_static("create", (std::shared_ptr<smtk::session::mesh::Export> (*)()) &smtk::session::mesh::Export::create) .def_static("create", (std::shared_ptr<smtk::session::mesh::Export> (*)(::std::shared_ptr<smtk::session::mesh::Export> &)) &smtk::session::mesh::Export::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<smtk::session::mesh::Export> (smtk::session::mesh::Export::*)()) &smtk::session::mesh::Export::shared_from_this) diff --git a/smtk/session/mesh/pybind11/PybindImport.h b/smtk/session/mesh/pybind11/PybindImport.h index 8c48f8bfaf4e4408419fd2ac27b2fc10f9a74654..7c737a9a5491803b028234f6cf0163a0900324c3 100644 --- a/smtk/session/mesh/pybind11/PybindImport.h +++ b/smtk/session/mesh/pybind11/PybindImport.h @@ -23,9 +23,7 @@ inline PySharedPtrClass< smtk::session::mesh::Import, smtk::operation::XMLOperat { PySharedPtrClass< smtk::session::mesh::Import, smtk::operation::XMLOperation > instance(m, "Import"); instance - .def(py::init<::smtk::session::mesh::Import const &>()) .def(py::init<>()) - .def("deepcopy", (smtk::session::mesh::Import & (smtk::session::mesh::Import::*)(::smtk::session::mesh::Import const &)) &smtk::session::mesh::Import::operator=) .def_static("create", (std::shared_ptr<smtk::session::mesh::Import> (*)()) &smtk::session::mesh::Import::create) .def_static("create", (std::shared_ptr<smtk::session::mesh::Import> (*)(::std::shared_ptr<smtk::session::mesh::Import> &)) &smtk::session::mesh::Import::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<smtk::session::mesh::Import> (smtk::session::mesh::Import::*)()) &smtk::session::mesh::Import::shared_from_this) diff --git a/smtk/session/mesh/pybind11/PybindRead.h b/smtk/session/mesh/pybind11/PybindRead.h index c05001a0488d059f6f6cc8f2a222e2c4a1a637b9..6746242067299c343f49fa44ab689b36e5e87f25 100644 --- a/smtk/session/mesh/pybind11/PybindRead.h +++ b/smtk/session/mesh/pybind11/PybindRead.h @@ -25,9 +25,7 @@ inline PySharedPtrClass< smtk::session::mesh::Read, smtk::operation::XMLOperatio { PySharedPtrClass< smtk::session::mesh::Read, smtk::operation::XMLOperation > instance(m, "Read"); instance - .def(py::init<::smtk::session::mesh::Read const &>()) .def(py::init<>()) - .def("deepcopy", (smtk::session::mesh::Read & (smtk::session::mesh::Read::*)(::smtk::session::mesh::Read const &)) &smtk::session::mesh::Read::operator=) .def_static("create", (std::shared_ptr<smtk::session::mesh::Read> (*)()) &smtk::session::mesh::Read::create) .def_static("create", (std::shared_ptr<smtk::session::mesh::Read> (*)(::std::shared_ptr<smtk::session::mesh::Read> &)) &smtk::session::mesh::Read::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<smtk::session::mesh::Read> (smtk::session::mesh::Read::*)()) &smtk::session::mesh::Read::shared_from_this) diff --git a/smtk/session/mesh/pybind11/PybindWrite.h b/smtk/session/mesh/pybind11/PybindWrite.h index ae21f08115d1bcbadf19aeb337ee8dea341aa034..98ffebe4a3b66197001bddb89d9fe9d3de5dd71d 100644 --- a/smtk/session/mesh/pybind11/PybindWrite.h +++ b/smtk/session/mesh/pybind11/PybindWrite.h @@ -25,9 +25,7 @@ inline PySharedPtrClass< smtk::session::mesh::Write, smtk::operation::XMLOperati { PySharedPtrClass< smtk::session::mesh::Write, smtk::operation::XMLOperation > instance(m, "Write"); instance - .def(py::init<::smtk::session::mesh::Write const &>()) .def(py::init<>()) - .def("deepcopy", (smtk::session::mesh::Write & (smtk::session::mesh::Write::*)(::smtk::session::mesh::Write const &)) &smtk::session::mesh::Write::operator=) .def_static("create", (std::shared_ptr<smtk::session::mesh::Write> (*)()) &smtk::session::mesh::Write::create) .def_static("create", (std::shared_ptr<smtk::session::mesh::Write> (*)(::std::shared_ptr<smtk::session::mesh::Write> &)) &smtk::session::mesh::Write::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<smtk::session::mesh::Write> (smtk::session::mesh::Write::*)()) &smtk::session::mesh::Write::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindCreateEdge.h b/smtk/session/polygon/pybind11/PybindCreateEdge.h index 4e727a80518eca549054bb72271bf24a609e0688..4fd8edcb5d73cdb815273f32b9d167c027e154b5 100644 --- a/smtk/session/polygon/pybind11/PybindCreateEdge.h +++ b/smtk/session/polygon/pybind11/PybindCreateEdge.h @@ -21,7 +21,6 @@ inline PySharedPtrClass< smtk::session::polygon::CreateEdge > pybind11_init_smtk { PySharedPtrClass< smtk::session::polygon::CreateEdge > instance(m, "CreateEdge", parent); instance - .def(py::init<::smtk::session::polygon::CreateEdge const &>()) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateEdge> (*)()) &smtk::session::polygon::CreateEdge::create) ; return instance; diff --git a/smtk/session/polygon/pybind11/PybindCreateEdgeFromPoints.h b/smtk/session/polygon/pybind11/PybindCreateEdgeFromPoints.h index 1935aeaceb9ea872513c43da37b011ea823878a4..8a33f4885042ecb257a597a5398b1a2a0fc43ed8 100644 --- a/smtk/session/polygon/pybind11/PybindCreateEdgeFromPoints.h +++ b/smtk/session/polygon/pybind11/PybindCreateEdgeFromPoints.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::CreateEdgeFromPoints > pybind11 { PySharedPtrClass< smtk::session::polygon::CreateEdgeFromPoints > instance(m, "CreateEdgeFromPoints", parent); instance - .def(py::init<::smtk::session::polygon::CreateEdgeFromPoints const &>()) - .def("deepcopy", (smtk::session::polygon::CreateEdgeFromPoints & (smtk::session::polygon::CreateEdgeFromPoints::*)(::smtk::session::polygon::CreateEdgeFromPoints const &)) &smtk::session::polygon::CreateEdgeFromPoints::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateEdgeFromPoints> (*)()) &smtk::session::polygon::CreateEdgeFromPoints::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateEdgeFromPoints> (*)(::std::shared_ptr<smtk::session::polygon::CreateEdgeFromPoints> &)) &smtk::session::polygon::CreateEdgeFromPoints::create, py::arg("ref")) .def("process", &smtk::session::polygon::CreateEdgeFromPoints::process, py::arg("pnts"), py::arg("numCoordsPerPoint"), py::arg("parentModel")) diff --git a/smtk/session/polygon/pybind11/PybindCreateEdgeFromVertices.h b/smtk/session/polygon/pybind11/PybindCreateEdgeFromVertices.h index 5f293fe1ff466522018c2a0dfe2207ee687410a5..3059e7a68bb86898a71f411eb015ac4d0cf670ba 100644 --- a/smtk/session/polygon/pybind11/PybindCreateEdgeFromVertices.h +++ b/smtk/session/polygon/pybind11/PybindCreateEdgeFromVertices.h @@ -21,8 +21,6 @@ inline PySharedPtrClass< smtk::session::polygon::CreateEdgeFromVertices > pybind { PySharedPtrClass< smtk::session::polygon::CreateEdgeFromVertices > instance(m, "CreateEdgeFromVertices", parent); instance - .def(py::init<::smtk::session::polygon::CreateEdgeFromVertices const &>()) - .def("deepcopy", (smtk::session::polygon::CreateEdgeFromVertices & (smtk::session::polygon::CreateEdgeFromVertices::*)(::smtk::session::polygon::CreateEdgeFromVertices const &)) &smtk::session::polygon::CreateEdgeFromVertices::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateEdgeFromVertices> (*)()) &smtk::session::polygon::CreateEdgeFromVertices::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateEdgeFromVertices> (*)(::std::shared_ptr<smtk::session::polygon::CreateEdgeFromVertices> &)) &smtk::session::polygon::CreateEdgeFromVertices::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::polygon::CreateEdgeFromVertices> (smtk::session::polygon::CreateEdgeFromVertices::*)() const) &smtk::session::polygon::CreateEdgeFromVertices::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindCreateFaces.h b/smtk/session/polygon/pybind11/PybindCreateFaces.h index 0232a87849f887b67ab5b155169dc87af0dbf742..83e608559fbdb5bafa957c9a958e20591f836b73 100644 --- a/smtk/session/polygon/pybind11/PybindCreateFaces.h +++ b/smtk/session/polygon/pybind11/PybindCreateFaces.h @@ -34,8 +34,6 @@ inline PySharedPtrClass< smtk::session::polygon::CreateFaces > pybind11_init_smt { PySharedPtrClass< smtk::session::polygon::CreateFaces > instance(m, "CreateFaces", parent); instance - .def(py::init<::smtk::session::polygon::CreateFaces const &>()) - .def("deepcopy", (smtk::session::polygon::CreateFaces & (smtk::session::polygon::CreateFaces::*)(::smtk::session::polygon::CreateFaces const &)) &smtk::session::polygon::CreateFaces::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateFaces> (*)()) &smtk::session::polygon::CreateFaces::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateFaces> (*)(::std::shared_ptr<smtk::session::polygon::CreateFaces> &)) &smtk::session::polygon::CreateFaces::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::polygon::CreateFaces> (smtk::session::polygon::CreateFaces::*)() const) &smtk::session::polygon::CreateFaces::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindCreateModel.h b/smtk/session/polygon/pybind11/PybindCreateModel.h index c13f00b325222890a721e8a84204e7eebe258565..d61613a838633c066ca2a0b54d143de02d55a027 100644 --- a/smtk/session/polygon/pybind11/PybindCreateModel.h +++ b/smtk/session/polygon/pybind11/PybindCreateModel.h @@ -21,7 +21,6 @@ inline PySharedPtrClass< smtk::session::polygon::CreateModel > pybind11_init_smt { PySharedPtrClass< smtk::session::polygon::CreateModel > instance(m, "CreateModel", parent); instance - .def(py::init<::smtk::session::polygon::CreateModel const &>()) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateModel> (*)()) &smtk::session::polygon::CreateModel::create) ; return instance; diff --git a/smtk/session/polygon/pybind11/PybindCreateVertices.h b/smtk/session/polygon/pybind11/PybindCreateVertices.h index fe29abbe64c25a96908e4b3382c55e16c96dfe10..545cda15cb2b5956376a0e0c3c03cb27f0ceb104 100644 --- a/smtk/session/polygon/pybind11/PybindCreateVertices.h +++ b/smtk/session/polygon/pybind11/PybindCreateVertices.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::CreateVertices> pybind11_init_s { PySharedPtrClass< smtk::session::polygon::CreateVertices > instance(m, "CreateVertices", parent); instance - .def(py::init<::smtk::session::polygon::CreateVertices const &>()) - .def("deepcopy", (smtk::session::polygon::CreateVertices & (smtk::session::polygon::CreateVertices::*)(::smtk::session::polygon::CreateVertices const &)) &smtk::session::polygon::CreateVertices::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateVertices> (*)()) &smtk::session::polygon::CreateVertices::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::CreateVertices> (*)(::std::shared_ptr<smtk::session::polygon::CreateVertices> &)) &smtk::session::polygon::CreateVertices::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::polygon::CreateVertices> (smtk::session::polygon::CreateVertices::*)() const) &smtk::session::polygon::CreateVertices::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindDelete.h b/smtk/session/polygon/pybind11/PybindDelete.h index 68c61196d272781d697cc50b199669443e33288c..c897b10b9c5beb072a48d404b22cc9760908447b 100644 --- a/smtk/session/polygon/pybind11/PybindDelete.h +++ b/smtk/session/polygon/pybind11/PybindDelete.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::Delete> pybind11_init_smtk_sess { PySharedPtrClass< smtk::session::polygon::Delete > instance(m, "Delete", parent); instance - .def(py::init<::smtk::session::polygon::Delete const &>()) - .def("deepcopy", (smtk::session::polygon::Delete & (smtk::session::polygon::Delete::*)(::smtk::session::polygon::Delete const &)) &smtk::session::polygon::Delete::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::Delete> (*)()) &smtk::session::polygon::Delete::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::Delete> (*)(::std::shared_ptr<smtk::session::polygon::Delete> &)) &smtk::session::polygon::Delete::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::polygon::Delete> (smtk::session::polygon::Delete::*)() const) &smtk::session::polygon::Delete::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindDemoteVertex.h b/smtk/session/polygon/pybind11/PybindDemoteVertex.h index 820101da1e4a4b9108d54fe5b39785f3fd357d6a..471408450d09216b912ca90ef477e60d0bb8f52c 100644 --- a/smtk/session/polygon/pybind11/PybindDemoteVertex.h +++ b/smtk/session/polygon/pybind11/PybindDemoteVertex.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::DemoteVertex > pybind11_init_sm { PySharedPtrClass< smtk::session::polygon::DemoteVertex > instance(m, "DemoteVertex", parent); instance - .def(py::init<::smtk::session::polygon::DemoteVertex const &>()) - .def("deepcopy", (smtk::session::polygon::DemoteVertex & (smtk::session::polygon::DemoteVertex::*)(::smtk::session::polygon::DemoteVertex const &)) &smtk::session::polygon::DemoteVertex::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::DemoteVertex> (*)()) &smtk::session::polygon::DemoteVertex::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::DemoteVertex> (*)(::std::shared_ptr<smtk::session::polygon::DemoteVertex> &)) &smtk::session::polygon::DemoteVertex::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::polygon::DemoteVertex> (smtk::session::polygon::DemoteVertex::*)() const) &smtk::session::polygon::DemoteVertex::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindExtractContours.h b/smtk/session/polygon/pybind11/PybindExtractContours.h index eec43fff9ead594429d9b0eb985b139ef4de1e83..46049721cec4e404e3880cb7b92d40c5391d4d1a 100644 --- a/smtk/session/polygon/pybind11/PybindExtractContours.h +++ b/smtk/session/polygon/pybind11/PybindExtractContours.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::session::polygon::ExtractContours > pybind11_init PySharedPtrClass< smtk::session::polygon::ExtractContours > instance(m, "ExtractContours", parent); instance .def(py::init<>()) - .def(py::init<::smtk::session::polygon::ExtractContours const &>()) - .def("deepcopy", (smtk::session::polygon::ExtractContours & (smtk::session::polygon::ExtractContours::*)(::smtk::session::polygon::ExtractContours const &)) &smtk::session::polygon::ExtractContours::operator=) .def("ableToOperate", &smtk::session::polygon::ExtractContours::ableToOperate) .def_static("create", (std::shared_ptr<smtk::session::polygon::ExtractContours> (*)()) &smtk::session::polygon::ExtractContours::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::ExtractContours> (*)(::std::shared_ptr<smtk::session::polygon::ExtractContours> &)) &smtk::session::polygon::ExtractContours::create, py::arg("ref")) diff --git a/smtk/session/polygon/pybind11/PybindForceCreateFace.h b/smtk/session/polygon/pybind11/PybindForceCreateFace.h index b380c71098c2a6cb707ade248e21c9c9b78a5598..c625a29112ab8de10d5eba6a7de2d7674403278b 100644 --- a/smtk/session/polygon/pybind11/PybindForceCreateFace.h +++ b/smtk/session/polygon/pybind11/PybindForceCreateFace.h @@ -24,8 +24,6 @@ inline PySharedPtrClass< smtk::session::polygon::ForceCreateFace > pybind11_init PySharedPtrClass< smtk::session::polygon::ForceCreateFace > instance(m, "ForceCreateFace", parent); instance .def(py::init<>()) - .def(py::init<::smtk::session::polygon::ForceCreateFace const &>()) - .def("deepcopy", (smtk::session::polygon::ForceCreateFace & (smtk::session::polygon::ForceCreateFace::*)(::smtk::session::polygon::ForceCreateFace const &)) &smtk::session::polygon::ForceCreateFace::operator=) .def("ableToOperate", &smtk::session::polygon::ForceCreateFace::ableToOperate) .def_static("create", (std::shared_ptr<smtk::session::polygon::ForceCreateFace> (*)()) &smtk::session::polygon::ForceCreateFace::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::ForceCreateFace> (*)(::std::shared_ptr<smtk::session::polygon::ForceCreateFace> &)) &smtk::session::polygon::ForceCreateFace::create, py::arg("ref")) diff --git a/smtk/session/polygon/pybind11/PybindImport.h b/smtk/session/polygon/pybind11/PybindImport.h index 2b943c00d47dfb96457c533da97715bfe1ef7ca8..5b59ab9bae03665a30d5078c06b38d695cb5a2c8 100644 --- a/smtk/session/polygon/pybind11/PybindImport.h +++ b/smtk/session/polygon/pybind11/PybindImport.h @@ -21,8 +21,6 @@ inline PySharedPtrClass< smtk::session::polygon::Import > pybind11_init_smtk_ses { PySharedPtrClass< smtk::session::polygon::Import > instance(m, "Import", parent); instance - .def(py::init<::smtk::session::polygon::Import const &>()) - .def("deepcopy", (smtk::session::polygon::Import & (smtk::session::polygon::Import::*)(::smtk::session::polygon::Import const &)) &smtk::session::polygon::Import::operator=) .def("ableToOperate", &smtk::session::polygon::Import::ableToOperate) .def_static("create", (std::shared_ptr<smtk::session::polygon::Import> (*)()) &smtk::session::polygon::Import::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::Import> (*)(::std::shared_ptr<smtk::session::polygon::Import> &)) &smtk::session::polygon::Import::create, py::arg("ref")) diff --git a/smtk/session/polygon/pybind11/PybindImportPPG.h b/smtk/session/polygon/pybind11/PybindImportPPG.h index 4ff34a66529c417bf1f0c981c5eb24435f598d9c..524e0b9f950b7f0450f1e85e6ec9debafb29e73f 100644 --- a/smtk/session/polygon/pybind11/PybindImportPPG.h +++ b/smtk/session/polygon/pybind11/PybindImportPPG.h @@ -21,8 +21,6 @@ inline PySharedPtrClass< smtk::session::polygon::ImportPPG > pybind11_init_smtk_ { PySharedPtrClass< smtk::session::polygon::ImportPPG > instance(m, "ImportPPG", parent); instance - .def(py::init<::smtk::session::polygon::ImportPPG const &>()) - .def("deepcopy", (smtk::session::polygon::ImportPPG & (smtk::session::polygon::ImportPPG::*)(::smtk::session::polygon::ImportPPG const &)) &smtk::session::polygon::ImportPPG::operator=) .def("ableToOperate", &smtk::session::polygon::ImportPPG::ableToOperate) .def_static("create", (std::shared_ptr<smtk::session::polygon::ImportPPG> (*)()) &smtk::session::polygon::ImportPPG::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::ImportPPG> (*)(::std::shared_ptr<smtk::session::polygon::ImportPPG> &)) &smtk::session::polygon::ImportPPG::create, py::arg("ref")) diff --git a/smtk/session/polygon/pybind11/PybindLegacyRead.h b/smtk/session/polygon/pybind11/PybindLegacyRead.h index 9850b93a68138b1a5ffb5fa02cfc364beaeec81a..e4f346bc084b2648c22acdb634ac44be7a6bd5c2 100644 --- a/smtk/session/polygon/pybind11/PybindLegacyRead.h +++ b/smtk/session/polygon/pybind11/PybindLegacyRead.h @@ -21,8 +21,6 @@ inline PySharedPtrClass< smtk::session::polygon::LegacyRead > pybind11_init_smtk { PySharedPtrClass< smtk::session::polygon::LegacyRead > instance(m, "LegacyRead", parent); instance - .def(py::init<::smtk::session::polygon::LegacyRead const &>()) - .def("deepcopy", (smtk::session::polygon::LegacyRead & (smtk::session::polygon::LegacyRead::*)(::smtk::session::polygon::LegacyRead const &)) &smtk::session::polygon::LegacyRead::operator=) .def("ableToOperate", &smtk::session::polygon::LegacyRead::ableToOperate) .def_static("create", (std::shared_ptr<smtk::session::polygon::LegacyRead> (*)()) &smtk::session::polygon::LegacyRead::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::LegacyRead> (*)(::std::shared_ptr<smtk::session::polygon::LegacyRead> &)) &smtk::session::polygon::LegacyRead::create, py::arg("ref")) diff --git a/smtk/session/polygon/pybind11/PybindOperation.h b/smtk/session/polygon/pybind11/PybindOperation.h index a5063a4587b273d6d09a462775bcc79656592c64..4d4e62bd71484915920ee26e660970b48584116c 100644 --- a/smtk/session/polygon/pybind11/PybindOperation.h +++ b/smtk/session/polygon/pybind11/PybindOperation.h @@ -22,9 +22,6 @@ namespace py = pybind11; inline PySharedPtrClass< smtk::session::polygon::Operation, smtk::operation::XMLOperation > pybind11_init_smtk_session_polygon_Operation(py::module &m) { PySharedPtrClass< smtk::session::polygon::Operation, smtk::operation::XMLOperation > instance(m, "Operation"); - instance - .def("deepcopy", (smtk::session::polygon::Operation & (smtk::session::polygon::Operation::*)(::smtk::session::polygon::Operation const &)) &smtk::session::polygon::Operation::operator=) - ; return instance; } diff --git a/smtk/session/polygon/pybind11/PybindRead.h b/smtk/session/polygon/pybind11/PybindRead.h index cb7de145dcf7391cd364e569004f1c2a3d46cf4e..054a02c8ede7543e35ce83e7dbb270be8df73ab4 100644 --- a/smtk/session/polygon/pybind11/PybindRead.h +++ b/smtk/session/polygon/pybind11/PybindRead.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::Read > pybind11_init_smtk_sessi { PySharedPtrClass< smtk::session::polygon::Read > instance(m, "Read", parent); instance - .def(py::init<::smtk::session::polygon::Read const &>()) - .def("deepcopy", (smtk::session::polygon::Read & (smtk::session::polygon::Read::*)(::smtk::session::polygon::Read const &)) &smtk::session::polygon::Read::operator=) .def("ableToOperate", &smtk::session::polygon::Read::ableToOperate) .def_static("create", (std::shared_ptr<smtk::session::polygon::Read> (*)()) &smtk::session::polygon::Read::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::Read> (*)(::std::shared_ptr<smtk::session::polygon::Read> &)) &smtk::session::polygon::Read::create, py::arg("ref")) diff --git a/smtk/session/polygon/pybind11/PybindSplitEdge.h b/smtk/session/polygon/pybind11/PybindSplitEdge.h index 1e6527c1ba2b1547c2a9278a390f7ad9ac287a36..7064e366adef651f4b19dba1ec235e7baaf8365e 100644 --- a/smtk/session/polygon/pybind11/PybindSplitEdge.h +++ b/smtk/session/polygon/pybind11/PybindSplitEdge.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::SplitEdge > pybind11_init_smtk_ { PySharedPtrClass< smtk::session::polygon::SplitEdge > instance(m, "SplitEdge", parent); instance - .def(py::init<::smtk::session::polygon::SplitEdge const &>()) - .def("deepcopy", (smtk::session::polygon::SplitEdge & (smtk::session::polygon::SplitEdge::*)(::smtk::session::polygon::SplitEdge const &)) &smtk::session::polygon::SplitEdge::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::SplitEdge> (*)()) &smtk::session::polygon::SplitEdge::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::SplitEdge> (*)(::std::shared_ptr<smtk::session::polygon::SplitEdge> &)) &smtk::session::polygon::SplitEdge::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::polygon::SplitEdge> (smtk::session::polygon::SplitEdge::*)() const) &smtk::session::polygon::SplitEdge::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindTweakEdge.h b/smtk/session/polygon/pybind11/PybindTweakEdge.h index ec1467160f25c566440de6e4769b0b7e18f5d3dc..ce89ef45324ddda2f7a6caa4e01d1fa4a3b57b43 100644 --- a/smtk/session/polygon/pybind11/PybindTweakEdge.h +++ b/smtk/session/polygon/pybind11/PybindTweakEdge.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::TweakEdge > pybind11_init_smtk_ { PySharedPtrClass< smtk::session::polygon::TweakEdge > instance(m, "TweakEdge", parent); instance - .def(py::init<::smtk::session::polygon::TweakEdge const &>()) - .def("deepcopy", (smtk::session::polygon::TweakEdge & (smtk::session::polygon::TweakEdge::*)(::smtk::session::polygon::TweakEdge const &)) &smtk::session::polygon::TweakEdge::operator=) .def_static("create", (std::shared_ptr<smtk::session::polygon::TweakEdge> (*)()) &smtk::session::polygon::TweakEdge::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::TweakEdge> (*)(::std::shared_ptr<smtk::session::polygon::TweakEdge> &)) &smtk::session::polygon::TweakEdge::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::polygon::TweakEdge> (smtk::session::polygon::TweakEdge::*)() const) &smtk::session::polygon::TweakEdge::shared_from_this) diff --git a/smtk/session/polygon/pybind11/PybindWrite.h b/smtk/session/polygon/pybind11/PybindWrite.h index e7bec1512a622767b3ed75bfb352b984c5b5709f..a709786dccaae65b3f91c502545de9fd53b67abf 100644 --- a/smtk/session/polygon/pybind11/PybindWrite.h +++ b/smtk/session/polygon/pybind11/PybindWrite.h @@ -23,8 +23,6 @@ inline PySharedPtrClass< smtk::session::polygon::Write > pybind11_init_smtk_sess { PySharedPtrClass< smtk::session::polygon::Write > instance(m, "Write", parent); instance - .def(py::init<::smtk::session::polygon::Write const &>()) - .def("deepcopy", (smtk::session::polygon::Write & (smtk::session::polygon::Write::*)(::smtk::session::polygon::Write const &)) &smtk::session::polygon::Write::operator=) .def("ableToOperate", &smtk::session::polygon::Write::ableToOperate) .def_static("create", (std::shared_ptr<smtk::session::polygon::Write> (*)()) &smtk::session::polygon::Write::create) .def_static("create", (std::shared_ptr<smtk::session::polygon::Write> (*)(::std::shared_ptr<smtk::session::polygon::Write> &)) &smtk::session::polygon::Write::create, py::arg("ref")) diff --git a/smtk/session/vtk/pybind11/PybindExport.h b/smtk/session/vtk/pybind11/PybindExport.h index d33d0104decf552a9e237448aeb438a313f4c325..df02089f653944cdc79f5cb9242f2b11a3f838ba 100644 --- a/smtk/session/vtk/pybind11/PybindExport.h +++ b/smtk/session/vtk/pybind11/PybindExport.h @@ -22,7 +22,6 @@ inline PySharedPtrClass< smtk::session::vtk::Export > pybind11_init_smtk_session PySharedPtrClass< smtk::session::vtk::Export > instance(m, "Export", parent); instance .def(py::init<>()) - .def("deepcopy", (smtk::session::vtk::Export & (smtk::session::vtk::Export::*)(::smtk::session::vtk::Export const &)) &smtk::session::vtk::Export::operator=) .def_static("create", (std::shared_ptr<smtk::session::vtk::Export> (*)()) &smtk::session::vtk::Export::create) .def_static("create", (std::shared_ptr<smtk::session::vtk::Export> (*)(::std::shared_ptr<smtk::session::vtk::Export> &)) &smtk::session::vtk::Export::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::vtk::Export> (smtk::session::vtk::Export::*)() const) &smtk::session::vtk::Export::shared_from_this) diff --git a/smtk/session/vtk/pybind11/PybindImport.h b/smtk/session/vtk/pybind11/PybindImport.h index f63fa6a205b8b22cf8c54190270396a78fb93da3..34a5121a14d722cc0534b7fcbfb0fc2d01032367 100644 --- a/smtk/session/vtk/pybind11/PybindImport.h +++ b/smtk/session/vtk/pybind11/PybindImport.h @@ -22,7 +22,6 @@ inline PySharedPtrClass< smtk::session::vtk::Import > pybind11_init_smtk_session PySharedPtrClass< smtk::session::vtk::Import > instance(m, "Import", parent); instance .def(py::init<>()) - .def("deepcopy", (smtk::session::vtk::Import & (smtk::session::vtk::Import::*)(::smtk::session::vtk::Import const &)) &smtk::session::vtk::Import::operator=) .def_static("create", (std::shared_ptr<smtk::session::vtk::Import> (*)()) &smtk::session::vtk::Import::create) .def_static("create", (std::shared_ptr<smtk::session::vtk::Import> (*)(::std::shared_ptr<smtk::session::vtk::Import> &)) &smtk::session::vtk::Import::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::vtk::Import> (smtk::session::vtk::Import::*)() const) &smtk::session::vtk::Import::shared_from_this) diff --git a/smtk/session/vtk/pybind11/PybindLegacyRead.h b/smtk/session/vtk/pybind11/PybindLegacyRead.h index 4e4b24dbb3c2ef3d1fb22c9307d2695e4a53e1a0..366965344144a321c6bb65935e5f5888efe7ae3f 100644 --- a/smtk/session/vtk/pybind11/PybindLegacyRead.h +++ b/smtk/session/vtk/pybind11/PybindLegacyRead.h @@ -22,7 +22,6 @@ inline PySharedPtrClass< smtk::session::vtk::LegacyRead > pybind11_init_smtk_ses PySharedPtrClass< smtk::session::vtk::LegacyRead > instance(m, "LegacyRead", parent); instance .def(py::init<>()) - .def("deepcopy", (smtk::session::vtk::LegacyRead & (smtk::session::vtk::LegacyRead::*)(::smtk::session::vtk::LegacyRead const &)) &smtk::session::vtk::LegacyRead::operator=) .def_static("create", (std::shared_ptr<smtk::session::vtk::LegacyRead> (*)()) &smtk::session::vtk::LegacyRead::create) .def_static("create", (std::shared_ptr<smtk::session::vtk::LegacyRead> (*)(::std::shared_ptr<smtk::session::vtk::LegacyRead> &)) &smtk::session::vtk::LegacyRead::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::vtk::LegacyRead> (smtk::session::vtk::LegacyRead::*)() const) &smtk::session::vtk::LegacyRead::shared_from_this) diff --git a/smtk/session/vtk/pybind11/PybindOperation.h b/smtk/session/vtk/pybind11/PybindOperation.h index 239ebda656124e58b9e3e8c8a9236e9897b25c0e..b929d0058d9685303e0a4e4a781be7d155fe288a 100644 --- a/smtk/session/vtk/pybind11/PybindOperation.h +++ b/smtk/session/vtk/pybind11/PybindOperation.h @@ -22,9 +22,6 @@ namespace py = pybind11; inline PySharedPtrClass< smtk::session::vtk::Operation, smtk::operation::XMLOperation > pybind11_init_smtk_session_vtk_Operation(py::module &m) { PySharedPtrClass< smtk::session::vtk::Operation, smtk::operation::XMLOperation > instance(m, "Operation"); - instance - .def("deepcopy", (smtk::session::vtk::Operation & (smtk::session::vtk::Operation::*)(::smtk::session::vtk::Operation const &)) &smtk::session::vtk::Operation::operator=) - ; return instance; } diff --git a/smtk/session/vtk/pybind11/PybindRead.h b/smtk/session/vtk/pybind11/PybindRead.h index d4f77557f82ee400a85a6e9b9c3473aaca2023e3..cf82ecd3b8cd40047d535ebcfd9f7c2a57722ada 100644 --- a/smtk/session/vtk/pybind11/PybindRead.h +++ b/smtk/session/vtk/pybind11/PybindRead.h @@ -24,7 +24,6 @@ inline PySharedPtrClass< smtk::session::vtk::Read > pybind11_init_smtk_session_v PySharedPtrClass< smtk::session::vtk::Read > instance(m, "Read", parent); instance .def(py::init<>()) - .def("deepcopy", (smtk::session::vtk::Read & (smtk::session::vtk::Read::*)(::smtk::session::vtk::Read const &)) &smtk::session::vtk::Read::operator=) .def_static("create", (std::shared_ptr<smtk::session::vtk::Read> (*)()) &smtk::session::vtk::Read::create) .def_static("create", (std::shared_ptr<smtk::session::vtk::Read> (*)(::std::shared_ptr<smtk::session::vtk::Read> &)) &smtk::session::vtk::Read::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::vtk::Read> (smtk::session::vtk::Read::*)() const) &smtk::session::vtk::Read::shared_from_this) diff --git a/smtk/session/vtk/pybind11/PybindWrite.h b/smtk/session/vtk/pybind11/PybindWrite.h index 88543d10ab124af27047c65530171e96d47c1040..aeefca5ef5d045fb9ab9e1c0faa18dd57a05d93a 100644 --- a/smtk/session/vtk/pybind11/PybindWrite.h +++ b/smtk/session/vtk/pybind11/PybindWrite.h @@ -24,7 +24,6 @@ inline PySharedPtrClass< smtk::session::vtk::Write > pybind11_init_smtk_session_ PySharedPtrClass< smtk::session::vtk::Write > instance(m, "Write", parent); instance .def(py::init<>()) - .def("deepcopy", (smtk::session::vtk::Write & (smtk::session::vtk::Write::*)(::smtk::session::vtk::Write const &)) &smtk::session::vtk::Write::operator=) .def_static("create", (std::shared_ptr<smtk::session::vtk::Write> (*)()) &smtk::session::vtk::Write::create) .def_static("create", (std::shared_ptr<smtk::session::vtk::Write> (*)(::std::shared_ptr<smtk::session::vtk::Write> &)) &smtk::session::vtk::Write::create, py::arg("ref")) .def("shared_from_this", (std::shared_ptr<const smtk::session::vtk::Write> (smtk::session::vtk::Write::*)() const) &smtk::session::vtk::Write::shared_from_this) diff --git a/smtk/string/pybind11/PybindToken.h b/smtk/string/pybind11/PybindToken.h index 0dad97c200507fddbbd3e7e5799c368ae40f6160..c973046491f407411cc5518a8dd9796fd23eab01 100644 --- a/smtk/string/pybind11/PybindToken.h +++ b/smtk/string/pybind11/PybindToken.h @@ -22,13 +22,25 @@ inline py::class_<smtk::string::Token> pybind11_init_smtk_string_Token(py::modul py::class_<smtk::string::Token> instance(m, "Token"); instance .def_static("manager", &smtk::string::Token::manager) + .def(py::init<>()) .def(py::init<const std::string &>()) .def(py::init<smtk::string::Hash>()) .def("data", &smtk::string::Token::data) .def("id", &smtk::string::Token::id) + .def("valid", &smtk::string::Token::valid) + .def("__str__", [](const smtk::string::Token& token) + { return token.data(); }) .def("__repr__", [](const smtk::string::Token& token) { - return "<smtk.string.Token '" + token.data() + "'>"; + if (token.valid()) + { + if (token.hasData()) + { + return "Token('" + token.data() + "')"; + } + return "Token(" + std::to_string(token.id()) + ")"; + } + return std::string("Token()"); }) .def("__hash__", [](const smtk::string::Token& self) { @@ -38,6 +50,10 @@ inline py::class_<smtk::string::Token> pybind11_init_smtk_string_Token(py::modul { return self == other; }) + .def("__lt__", [](const smtk::string::Token& self, const smtk::string::Token& other) + { + return self < other; + }) ; return instance; } diff --git a/smtk/string/testing/CMakeLists.txt b/smtk/string/testing/CMakeLists.txt index 2941d367a3cf5e1cbcdf5137f12b35096cd03df9..83305da3a2914ab6d4dec88c168fc7037bd7553e 100644 --- a/smtk/string/testing/CMakeLists.txt +++ b/smtk/string/testing/CMakeLists.txt @@ -1,5 +1,5 @@ add_subdirectory(cxx) -# if(SMTK_ENABLE_PYTHON_WRAPPING) -# add_subdirectory(python) -# endif() +if(SMTK_ENABLE_PYTHON_WRAPPING) + add_subdirectory(python) +endif() diff --git a/smtk/string/testing/python/CMakeLists.txt b/smtk/string/testing/python/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0525061eb4dee28bc77d38990738a7f6f58cf308 --- /dev/null +++ b/smtk/string/testing/python/CMakeLists.txt @@ -0,0 +1,7 @@ +set(smtkStringPythonTests + testStringToken +) + +foreach (test ${smtkStringPythonTests}) + smtk_add_test_python(${test}Py ${test}.py --src-dir=${smtk_SOURCE_DIR}) +endforeach() diff --git a/smtk/string/testing/python/testStringToken.py b/smtk/string/testing/python/testStringToken.py new file mode 100644 index 0000000000000000000000000000000000000000..05cb7bea7ad61eeaad2e083ff90ae7d7fddae896 --- /dev/null +++ b/smtk/string/testing/python/testStringToken.py @@ -0,0 +1,45 @@ +# ============================================================================= +# +# Copyright (c) Kitware, Inc. +# All rights reserved. +# See LICENSE.txt 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. +# +# ============================================================================= +import os +import sys +import unittest +import smtk +import smtk.string +import smtk.common +from smtk import common +import smtk.testing + + +class TestStringToken(unittest.TestCase): + + def test(self): + blank = smtk.string.Token() + print(blank) + self.assertFalse(blank.valid(), 'Default-constructed Token should be invalid.') + texts = ('a', 'b', 'c', 'aa') + allParsed = [] + for txt in texts: + parsed = smtk.string.Token(txt) + print(parsed) + self.assertEqual(str(parsed), txt, 'Round-tripped Token was not identical.') + allParsed.append(parsed) + self.assertEqual(len(allParsed), len(texts), 'Expected to parse every Token.') + self.assertEqual(allParsed, [smtk.string.Token(x) for x in texts], \ + 'Same text in should produce identical Tokens out.') + s1 = set(allParsed) + s2 = set([smtk.string.Token(x) for x in texts]) + print(s1) + self.assertEqual(s1, s2, 'Hashing should produce identical sets of Tokens.') + +if __name__ == '__main__': + smtk.testing.process_arguments() + unittest.main() diff --git a/smtk/task/Manager.cxx b/smtk/task/Manager.cxx index 8d973e38c5204e38fcb45db916a4d1c7f0bfbe65..8333f0746fc5d482ef86b8733dfa157fc393b4cc 100644 --- a/smtk/task/Manager.cxx +++ b/smtk/task/Manager.cxx @@ -9,6 +9,9 @@ //========================================================================= #include "smtk/task/Manager.h" +#include "smtk/task/ObjectsInRoles.h" + +#include "smtk/project/Project.h" #include "smtk/operation/Operation.h" @@ -20,10 +23,124 @@ #include "smtk/io/Logger.h" +using namespace smtk::string::literals; + namespace smtk { namespace task { +namespace // anonymous +{ + +bool resourceMatch( + const std::string& filter, + smtk::resource::PersistentObject* object, + smtk::resource::Resource*& rsrc) +{ + if (auto* resource = dynamic_cast<smtk::resource::Resource*>(object)) + { + rsrc = resource; + if (filter == "*" || rsrc->matchesType(filter)) + { + return true; + } + } + else if (auto* comp = dynamic_cast<smtk::resource::Component*>(object)) + { + if ((rsrc = comp->parentResource())) + { + if (filter == "*" || rsrc->matchesType(filter)) + { + return true; + } + } + } + return false; +} + +bool componentMatch( + const std::string& filter, + smtk::resource::PersistentObject* object, + smtk::resource::Resource* rsrc) +{ + // An empty filter string indicates only resources are allowed. + if (filter.empty()) + { + return (object == rsrc); + } + // "*" allows any component: + else if (filter == "*") + { + // Assume that if object is not a pointer to the parent resource, + // it must be a component: + return (object != rsrc); + } + // We have a non-trivial filter string; we must have a component. + if (auto* comp = dynamic_cast<smtk::resource::Component*>(object)) + { + auto query = rsrc->queryOperation(filter); + return query ? query(*comp) : false; + } + return false; +} + +bool filterObject(const nlohmann::json::array_t& filter, smtk::resource::PersistentObject* object) +{ + for (const auto& filterTuple : filter) + { + try + { + if (filterTuple.is_array() && filterTuple.size() == 2) + { + smtk::resource::Resource* rsrc{ nullptr }; + if (resourceMatch(filterTuple[0].get<std::string>(), object, rsrc)) + { + auto compSpec = filterTuple[1].is_null() ? "" : filterTuple[1].get<std::string>(); + if (componentMatch(compSpec, object, rsrc)) + { + return true; + } + } + } + } + catch (nlohmann::json::exception& e) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Cannot process filter \"" << filterTuple.dump() << "\";" << e.what()); + continue; + } + } + return false; +} + +nlohmann::json::array_t filtersForSpec(const smtk::view::Configuration::Component& spec) +{ + nlohmann::json::array_t filters; + for (const auto& filter : spec.children()) + { + if (filter.name() != "Filter") + { + smtkWarningMacro(smtk::io::Logger::instance(), "Unhandled child \"" << filter.name() << "\"."); + continue; + } + std::string rsrcMatch; + if (!filter.attribute("Resource", rsrcMatch)) + { + smtkWarningMacro(smtk::io::Logger::instance(), "No resource in \"" << filter.name() << "\". Skipping."); + continue; + } + std::string compMatch; + filter.attribute("Component", compMatch); + nlohmann::json::array_t fspec{rsrcMatch}; + //NOLINTNEXTLINE(modernize-use-emplace) + if (!compMatch.empty()) { fspec.push_back(compMatch); } else { fspec.push_back(std::nullptr_t()); } + filters.emplace_back(fspec); + } + return filters; +} + +} // anonymous namespace constexpr const char* const Manager::type_name; @@ -149,6 +266,232 @@ smtk::resource::Resource* Manager::resource() const return m_parent; } +Manager::ResourceObjectMap Manager::workflowObjects( + const nlohmann::json& spec, + Task* task) +{ + // Filter objects by the \a spec. + ResourceObjectMap objectMap; + + nlohmann::json source; + nlohmann::json filter; + if (spec.contains("source")) + { + source = spec.at("source"); + if (!source.is_object() || !source.contains("type")) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Spec source must be a dictionary with 'type' key, " + "got \"" + << source.dump() << "\""); + return objectMap; + } + } + else + { + source = { { "type", "project resources" } }; + } + + if (spec.contains("filter")) + { + filter = spec.at("filter"); + if (!filter.is_array()) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Spec filter must be an array, got \"" << filter.dump() << "\"."); + return objectMap; + } + } + else + { + // Accept any resource or component: + filter = nlohmann::json::array_t({ { "*", "*" }, { "*", nullptr } }); + } + + auto sourceType = source["type"].get<smtk::string::Token>(); + std::unordered_set<smtk::resource::PersistentObject*> objects; + switch (sourceType.id()) + { + default: + smtkErrorMacro( + smtk::io::Logger::instance(), "Unknown source type \"" << sourceType.data() << "\"."); + // fall through + case "project resources"_hash: + { + auto* project = dynamic_cast<smtk::project::Project*>(this->resource()); + if (!project) + { + return objectMap; + } + + for (const auto& resource : project->resources()) + { + if (filterObject(filter, resource.get())) + { + objects.insert(resource.get()); + } + } + } + break; + case "active task port"_hash: + { + auto* currentTask = task ? task : this->active().task(); + if (!currentTask || !source.contains("port")) + { + smtkErrorMacro(smtk::io::Logger::instance(), "No active task or no \"port\" specified."); + return objectMap; + } + + // We are asked to find objects on a port of the (now) active task. + // Determine whether we are filtering on role or not. + smtk::string::Token sourceRole; + if (source.contains("role")) + { + sourceRole = source.at("role").get<smtk::string::Token>(); + } + // Find the correct port: + const auto& taskPortMap = currentTask->ports(); + auto it = taskPortMap.find(source["port"]); + if (it == taskPortMap.end()) + { + return objectMap; + } + // Fetch data from the port. We only understand ObjectsInRoles at this point: + auto portData = it->second->parent()->portData(it->second); + if (portData) + { + if (auto objectsInRoles = std::dynamic_pointer_cast<smtk::task::ObjectsInRoles>(portData)) + { + for (const auto& entry : objectsInRoles->data()) + { + if (sourceRole.valid() && entry.first != sourceRole) + { // Skip objects in unrequested roles. + continue; + } + // Filter objects as requested. + for (const auto& object : entry.second) + { + if (filterObject(filter, object)) + { + objects.insert(object); + } + } + } + } + else + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Unhandled port data type \"" << portData->typeName() << "\"."); + } + } + } + break; + } + + for (const auto& object : objects) + { + smtk::resource::Resource* resource =dynamic_cast<smtk::resource::Resource*>(object); + if (auto* comp = dynamic_cast<smtk::resource::Component*>(object)) + { + resource = comp->parentResource(); + } + if (!resource) + { + continue; + } + objectMap[resource].insert(object == resource ? nullptr : object); + } + + return objectMap; +} + +Manager::ResourceObjectMap Manager::workflowObjects( + const smtk::view::Configuration::Component& spec, Task* task) +{ + // To avoid dueling implementations, we'll convert \a spec into JSON and pass + // it to the variant above. + nlohmann::json jsonSpec; + nlohmann::json::array_t controls; + for (const auto& entry : spec.children()) + { + smtk::string::Token tagName = entry.name(); + switch (tagName.id()) + { + case "ActiveTaskPort"_hash: + { + auto portName = entry.attributeAsString("Port"); + auto roleName = entry.attributeAsString("Role"); + if (portName.empty()) + { + smtkErrorMacro(smtk::io::Logger::instance(), "Missing a port name."); + continue; + } + nlohmann::json sourceSpec{{ "type", "active task port" }, {"port", portName}}; + if (!roleName.empty()) + { + sourceSpec["role"] = roleName; + } + auto filters = filtersForSpec(entry); + if (!filters.empty()) + { + jsonSpec["filter"] = filters; + } + jsonSpec["source"] = sourceSpec; + } + break; + case "ProjectResources"_hash: + { + nlohmann::json sourceSpec{{ "type", "project resources" }}; + auto filters = filtersForSpec(entry); + if (!filters.empty()) + { + jsonSpec["filter"] = filters; + } + jsonSpec["source"] = sourceSpec; + } + break; + case "Control"_hash: + { + auto controlType = entry.attributeAsString("Type"); + if (controlType.empty()) + { + smtkErrorMacro(smtk::io::Logger::instance(), + "Control tag must provide a Type attribute."); + continue; + } + controls.emplace_back(controlType); + } + break; + default: + { + smtkWarningMacro(smtk::io::Logger::instance(), + "Unhandled specification tag <" << tagName.data() << ">. Skipping."); + } + } + } + if (!controls.empty()) + { + jsonSpec["controls"] = controls; + } + auto objMap = this->workflowObjects(jsonSpec, task); + return objMap; +} + +bool Manager::isResourceRelevant( + const std::shared_ptr<smtk::resource::Resource>& resource, + const nlohmann::json& filter) +{ + return filterObject(filter, resource.get()); +} + +bool Manager::isResourceRelevant(smtk::resource::Resource* resource, const nlohmann::json& filter) +{ + return filterObject(filter, resource); +} + int Manager::handleOperation( const smtk::operation::Operation& op, smtk::operation::EventType event, diff --git a/smtk/task/Manager.h b/smtk/task/Manager.h index 9dd682795e2cd0fc54b445f5e1fd8ed3a8b1a607..33fdb9a0ce6fc41b95c498436d2a177d4bef01c8 100644 --- a/smtk/task/Manager.h +++ b/smtk/task/Manager.h @@ -30,6 +30,8 @@ #include "smtk/task/Task.h" #include "smtk/task/adaptor/Instances.h" +#include "smtk/view/Configuration.h" + #include "nlohmann/json.hpp" #include <array> @@ -82,6 +84,13 @@ public: Manager(const Manager&) = delete; void operator=(const Manager&) = delete; + /// A map of objects grouped by their parent resource. + /// + /// If a resource itself is intended for selection, one + /// entry of its target set of objects will be the null pointer. + using ResourceObjectMap = + std::map<smtk::resource::Resource*, std::unordered_set<smtk::resource::PersistentObject*>>; + /// Managed instances of Task objects (and a registry of Task classes). using TaskInstances = smtk::task::Instances; @@ -132,6 +141,33 @@ public: /// If this manager is owned by a resource (typically a project), return it. smtk::resource::Resource* resource() const; + /// Given a filter \a spec, return a set of objects grouped by their parent resources. + /// + /// The objects are drawn – according to the \a spec – from either ports of the active + /// task or resources owned by the parent of this task manager (assuming it is a project). + /// If a resource itself is intended to be part of the result, one of the object pointers + /// in that resource's target set will be the null pointer. + /// + /// The variant that accepts a JSON \a spec is for use inside task-manager style. + /// The variant that accepts a Configuration::Component \a spec is for use inside + /// view-configuration XML. + /// The two forms of \a spec are similar but not identical in order to improve + /// readability and reduce maintenance overhead. + ResourceObjectMap workflowObjects(const nlohmann::json& spec, Task* task = nullptr); + ResourceObjectMap workflowObjects( + const smtk::view::Configuration::Component& spec, Task* task = nullptr); + + /// Return true if the \a resource matches the filter \a spec provided. + bool isResourceRelevant( + const std::shared_ptr<smtk::resource::Resource>& resource, const nlohmann::json& spec); + bool isResourceRelevant( + smtk::resource::Resource* resource, const nlohmann::json& spec); + bool isResourceRelevant( + const std::shared_ptr<smtk::resource::Resource>& resource, + const smtk::view::Configuration::Component& spec); + bool isResourceRelevant( + smtk::resource::Resource* resource, const smtk::view::Configuration::Component& spec); + /// Return a gallery of Task Worklets Gallery& gallery() { return m_gallery; } const Gallery& gallery() const { return m_gallery; } diff --git a/smtk/task/TrivialProducerAgent.cxx b/smtk/task/TrivialProducerAgent.cxx index 8cba737887eeeecdce191b22186914f648ee5ab1..a242df90cce0e7c854bec6ae25d499e4e53d35aa 100644 --- a/smtk/task/TrivialProducerAgent.cxx +++ b/smtk/task/TrivialProducerAgent.cxx @@ -28,7 +28,7 @@ TrivialProducerAgent::TrivialProducerAgent(Task* owningTask) State TrivialProducerAgent::state() const { - return State::Completable; + return m_internalState; } void TrivialProducerAgent::configure(const Configuration& config) @@ -54,8 +54,8 @@ void TrivialProducerAgent::configure(const Configuration& config) for (const auto& objectSpec : entry.value()) { auto* obj = helper.objectFromJSONSpec(objectSpec, "port"); - std::cout << "Port \"" << m_name.data() << "\" add obj " << obj << " role " << entry.key() - << "\n"; + // std::cout << "Port \"" << m_name.data() << "\" add obj " << obj << " role " + // << entry.key() << "\n"; if (obj) { addedData |= m_data->addObject(obj, role); @@ -65,6 +65,29 @@ void TrivialProducerAgent::configure(const Configuration& config) } #endif } + it = config.find("required-counts"); + if (it != config.end()) + { + m_requiredObjectCounts = it->get<std::map<smtk::string::Token, std::pair<int, int>>>(); + } + else + { + m_requiredObjectCounts.clear(); + } + it = config.find("internal-state"); + if (it != config.end()) + { + bool valid = false; + m_internalState = stateEnum(it->get<std::string>(), &valid); + if (!valid) + { + m_internalState = State::Completable; + } + } + else + { + m_internalState = State::Completable; + } it = config.find("output-port"); if (it != config.end()) { @@ -83,6 +106,7 @@ void TrivialProducerAgent::configure(const Configuration& config) TrivialProducerAgent::Configuration TrivialProducerAgent::configuration() const { auto config = this->Superclass::configuration(); + config["internal-state"] = stateName(m_internalState); if (m_outputPort) { config["output-port"] = m_outputPort->name(); @@ -95,9 +119,27 @@ TrivialProducerAgent::Configuration TrivialProducerAgent::configuration() const { config["data"] = m_data; } + if (!m_requiredObjectCounts.empty()) + { + config["required-counts"] = m_requiredObjectCounts; + } return config; } +std::string TrivialProducerAgent::troubleshoot() const +{ + std::ostringstream msg; + switch (m_internalState) + { + default: + break; + case smtk::task::State::Incomplete: + msg << R"html(<li>Missing objects in roles.</li>)html"; + break; + } + return msg.str(); +} + std::shared_ptr<PortData> TrivialProducerAgent::portData(const Port* port) const { if (port == m_outputPort) @@ -123,7 +165,13 @@ bool TrivialProducerAgent::addObjectInRole( { if (trivialProducer->name() == agentName) { - return trivialProducer->m_data->addObject(object, role); + State prev = trivialProducer->state(); + bool didAdd = trivialProducer->m_data->addObject(object, role); + if (didAdd) + { + trivialProducer->parent()->updateAgentState(trivialProducer, prev, trivialProducer->computeInternalState()); + } + return didAdd; } } } @@ -146,7 +194,13 @@ bool TrivialProducerAgent::addObjectInRole( { if (trivialProducer->outputPort() == port) { - return trivialProducer->m_data->addObject(object, role); + State prev = trivialProducer->state(); + bool didAdd = trivialProducer->m_data->addObject(object, role); + if (didAdd) + { + trivialProducer->parent()->updateAgentState(trivialProducer, prev, trivialProducer->computeInternalState()); + } + return didAdd; } } } @@ -170,8 +224,10 @@ bool TrivialProducerAgent::removeObjectFromRole( { if (trivialProducer->name() == agentName) { + State prev = trivialProducer->state(); if (trivialProducer->m_data->removeObject(object, role)) { + trivialProducer->parent()->updateAgentState(trivialProducer, prev, trivialProducer->computeInternalState()); return true; } } @@ -197,8 +253,10 @@ bool TrivialProducerAgent::removeObjectFromRole( { if (trivialProducer->outputPort() == port) { + State prev = trivialProducer->state(); if (trivialProducer->m_data->removeObject(object, role)) { + trivialProducer->parent()->updateAgentState(trivialProducer, prev, trivialProducer->computeInternalState()); return true; } } @@ -220,7 +278,12 @@ bool TrivialProducerAgent::resetData(Task* task, const std::string& agentName) { if (trivialProducer->name() == agentName) { + State prev = trivialProducer->state(); didChange |= trivialProducer->m_data->clear(); + if (didChange) + { + trivialProducer->parent()->updateAgentState(trivialProducer, prev, trivialProducer->computeInternalState()); + } } } } @@ -238,11 +301,50 @@ bool TrivialProducerAgent::resetData(Task* task, Port* port) { if (auto* trivialProducer = dynamic_cast<TrivialProducerAgent*>(agent)) { + State prev = trivialProducer->state(); didChange |= trivialProducer->m_data->clear(); + if (didChange) + { + trivialProducer->parent()->updateAgentState(trivialProducer, prev, trivialProducer->computeInternalState()); + } } } return didChange; } +State TrivialProducerAgent::computeInternalState() +{ + State result = State::Completable; + for (const auto& entry : m_requiredObjectCounts) + { + auto it = m_data->data().find(entry.first); + if (it == m_data->data().end()) + { + result = State::Incomplete; + break; + } + auto numObj = static_cast<int>(it->second.size()); + if (numObj >= entry.second.first && numObj <= entry.second.second) + { + // We are in range, skip following checks. + continue; + } + if (entry.second.second < 0 && numObj >= entry.second.first) + { + // We are allowed to have any number as long as we exceed the minimum. + continue; + } + if (entry.second.first < 0 && entry.second.second < 0 && numObj == 0) + { + // We are required to have 0 objects in this role. + continue; + } + result = State::Incomplete; + break; + } + m_internalState = result; + return result; +} + } // namespace task } // namespace smtk diff --git a/smtk/task/TrivialProducerAgent.h b/smtk/task/TrivialProducerAgent.h index 53677880219b6daadd78854116b91bb34bb99550..776659a10396a17e0b8db9cac2ed1296512a1570 100644 --- a/smtk/task/TrivialProducerAgent.h +++ b/smtk/task/TrivialProducerAgent.h @@ -25,6 +25,9 @@ class ObjectsInRoles; /// assign objects to a task. The downstream or child tasks of this /// agent's task will then be configured with the objects in the roles /// as configured. +/// +/// Unless the task's configuration includes a minimum/maximum count +/// of objects per role, the task will always be completable. class SMTKCORE_EXPORT TrivialProducerAgent : public Agent { public: @@ -38,7 +41,10 @@ public: ///\brief Return the current state of the agent. /// - /// This agent will always be completable, even if no resources are assigned. + /// By default, this agent will always be completable, even if no resources are assigned. + /// However, if the agent's configuration contains minimum/maximum counts + /// for objects by role, the state will only be completable when the number of objects + /// in each specified role is in the allowed range. State state() const override; ///\brief Configure the agent based on a provided JSON configuration. @@ -47,6 +53,9 @@ public: ///\brief Produce a JSON configuration object for the current task state. Configuration configuration() const override; + ///\brief Provide feedback to users on how to make this agent completable. + std::string troubleshoot() const override; + ///\brief Return the port data from the agent. std::shared_ptr<PortData> portData(const Port* port) const override; @@ -94,7 +103,11 @@ public: static bool resetData(Task* task, Port* port); protected: + virtual State computeInternalState(); + + State m_internalState{ State::Completable }; std::shared_ptr<ObjectsInRoles> m_data; + std::map<smtk::string::Token, std::pair<int, int>> m_requiredObjectCounts; Port* m_outputPort{ nullptr }; }; diff --git a/smtk/task/pybind11/PybindTask.cxx b/smtk/task/pybind11/PybindTask.cxx index 0abf159f540cfc9a8e3672a674181946763bb8b1..3e0944bf7590c88d91c3c2935e8f494668c5fe94 100644 --- a/smtk/task/pybind11/PybindTask.cxx +++ b/smtk/task/pybind11/PybindTask.cxx @@ -35,6 +35,7 @@ using namespace nlohmann; #include "PybindState.h" #include "PybindSubmitOperationAgent.h" #include "PybindTask.h" +#include "PybindTrivialProducerAgent.h" #include "PybindWorklet.h" #include "PybindInstances.h" @@ -58,6 +59,7 @@ PYBIND11_MODULE(_smtkPybindTask, m) auto smtk_task_FillOutAttributesAgent = pybind11_init_smtk_task_FillOutAttributesAgent(m); auto smtk_task_SubmitOperationAgent = pybind11_init_smtk_task_SubmitOperationAgent(m); auto smtk_task_GatherObjectsAgent = pybind11_init_smtk_task_GatherObjectsAgent(m); + auto smtk_task_TrivialProducerAgent = pybind11_init_smtk_task_TrivialProducerAgent(m); auto smtk_task_Task = pybind11_init_smtk_task_Task(m); pybind11_init_smtk_task_State(m); pybind11_init_smtk_task_stateEnum(m); diff --git a/smtk/task/pybind11/PybindTask.h b/smtk/task/pybind11/PybindTask.h index 303a2fac91be4e7375652576aa9a34fca6e72f84..5330f3cea182ed592f021c232fc434d8c8876778 100644 --- a/smtk/task/pybind11/PybindTask.h +++ b/smtk/task/pybind11/PybindTask.h @@ -34,6 +34,8 @@ inline PySharedPtrClass< smtk::task::Task, smtk::resource::Component > pybind11_ .def("setName", &smtk::task::Task::setName, py::arg("name")) .def("title", &smtk::task::Task::name) .def("setTitle", &smtk::task::Task::setName, py::arg("title")) + .def("ports", &smtk::task::Task::ports) + .def("portData", &smtk::task::Task::portData, py::arg("port")) .def("state", &smtk::task::Task::state) .def("agents", &smtk::task::Task::agents, py::return_value_policy::reference_internal) .def("children", &smtk::task::Task::children, py::return_value_policy::reference_internal) diff --git a/smtk/task/pybind11/PybindTrivialProducerAgent.h b/smtk/task/pybind11/PybindTrivialProducerAgent.h new file mode 100644 index 0000000000000000000000000000000000000000..0ec4527f82d5caa9d6ec498a53579da5cc182d4e --- /dev/null +++ b/smtk/task/pybind11/PybindTrivialProducerAgent.h @@ -0,0 +1,79 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt 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. +//========================================================================= + +#ifndef pybind_smtk_task_TrivialProducerAgent_h +#define pybind_smtk_task_TrivialProducerAgent_h + +#include <pybind11/pybind11.h> + +#include "smtk/task/TrivialProducerAgent.h" + +#include "smtk/task/Port.h" + +namespace py = pybind11; + +inline py::class_< smtk::task::TrivialProducerAgent > pybind11_init_smtk_task_TrivialProducerAgent(py::module &m) +{ + py::class_< smtk::task::TrivialProducerAgent, smtk::task::Agent > instance(m, "TrivialProducerAgent"); + instance + .def_static("addObjectInRole", []( + smtk::task::Task* task, const std::string& agentName, const std::string& role, const smtk::resource::PersistentObject::Ptr& object) + { + return smtk::task::TrivialProducerAgent::addObjectInRole(task, agentName, role, object.get()); + }, + py::arg("task"), py::arg("agentName"), py::arg("role"), py::arg("object")) + .def_static("addObjectInRole", []( + smtk::task::Task* task, smtk::task::Port* port, const std::string& role, const smtk::resource::PersistentObject::Ptr& object) + { + return smtk::task::TrivialProducerAgent::addObjectInRole(task, port, role, object.get()); + }, + py::arg("task"), py::arg("port"), py::arg("role"), py::arg("object")) + .def_static("removeObjectFromRole", []( + smtk::task::Task* task, const std::string& agentName, const std::string& role, const smtk::resource::PersistentObject::Ptr& object) + { + return smtk::task::TrivialProducerAgent::removeObjectFromRole(task, agentName, role, object.get()); + }, + py::arg("task"), py::arg("agentName"), py::arg("role"), py::arg("object")) + .def_static("removeObjectFromRole", []( + smtk::task::Task* task, smtk::task::Port* port, const std::string& role, const smtk::resource::PersistentObject::Ptr& object) + { + return smtk::task::TrivialProducerAgent::removeObjectFromRole(task, port, role, object.get()); + }, + py::arg("task"), py::arg("port"), py::arg("role"), py::arg("object")) + .def_static("resetData", [](smtk::task::Task* task, const std::string& agentName) + { + return smtk::task::TrivialProducerAgent::resetData(task, agentName); + }, py::arg("task"), py::arg("agentName")) + .def_static("resetData", [](smtk::task::Task* task, smtk::task::Port* port) + { + return smtk::task::TrivialProducerAgent::resetData(task, port); + }, py::arg("task"), py::arg("port")) + .def("typeToken", &smtk::task::TrivialProducerAgent::typeToken) + .def("classHierarchy", &smtk::task::TrivialProducerAgent::classHierarchy) + .def("matchesType", &smtk::task::TrivialProducerAgent::matchesType, py::arg("candidate")) + .def("generationsFromBase", &smtk::task::TrivialProducerAgent::generationsFromBase, py::arg("base")) + .def("state", &smtk::task::TrivialProducerAgent::state) + .def("configure", [](smtk::task::TrivialProducerAgent& self, const std::string& jsonConfig) + { + auto config = nlohmann::json::parse(jsonConfig); + self.configure(config); + }) + .def("configuration", &smtk::task::TrivialProducerAgent::configuration) + .def("name", &smtk::task::TrivialProducerAgent::name) + .def("portData", [](smtk::task::TrivialProducerAgent& self, smtk::task::Port::Ptr port) + { + return self.portData(port.get()); + }, py::arg("port")) + .def("parent", &smtk::task::TrivialProducerAgent::parent, py::return_value_policy::reference_internal) + ; + return instance; +} + +#endif diff --git a/smtk/view/Configuration.h b/smtk/view/Configuration.h index 3eaac22f721251dc815ab3a45092ed83b81a04bf..fa9b94370a587e3a978326fd66bfc9231893642a 100644 --- a/smtk/view/Configuration.h +++ b/smtk/view/Configuration.h @@ -63,7 +63,7 @@ public: /// is t or true, false if attribute is f or false and not set otherwise /// set value to the attribute's values. Else it returns false bool attributeAsBool(const std::string& attname, bool& value) const; - /// Returns true if the component has an attribute called name and if it's value is + /// Returns true if the component has an attribute called name and if its value is /// either t or true (ignoring case). Else it returns false. bool attributeAsBool(const std::string& attname) const;