...
 
Commits (10)
## Operation system changes
### Configuration
Operations now have a method named `configure()`.
This method may be invoked by a user interface when
the operation's associations are changed, item values are
changed, or new attributes are created (in the operation's
resource) in order for the operation to edit itself
for consistency and to provide context-sensitive default
values for items.
An example is provided in the oscillator-session's EditSource
operation that uses changes to associations to compute a default
center and radius for the source.
......@@ -335,7 +335,7 @@ SMTKCORE_EXPORT void from_json(const json& j, smtk::attribute::ResourcePtr& res)
std::vector<smtk::attribute::DefinitionPtr> defs;
for (auto strIter = excObj.begin(); strIter != excObj.end(); strIter++)
{
auto def = res->findDefinition(strIter->get<std::string>());
def = res->findDefinition(strIter->get<std::string>());
if (def)
{
defs.push_back(def);
......@@ -385,7 +385,7 @@ SMTKCORE_EXPORT void from_json(const json& j, smtk::attribute::ResourcePtr& res)
auto preDefs = preObj.at("Prerequisite");
for (auto strIter = preDefs.begin(); strIter != preDefs.end(); strIter++)
{
auto def = res->findDefinition(strIter->get<std::string>());
def = res->findDefinition(strIter->get<std::string>());
if (def)
{
tdef->addPrerequisite(def);
......
......@@ -376,12 +376,25 @@ void pqSMTKOperationPanel::operationListDoubleClicked(QListWidgetItem* item)
auto seln = m_availableOperations->selection();
const auto& smap = seln->currentSelection();
auto params = opInstance->parameters();
bool anyAssociations = false;
for (auto entry : smap)
{
if ((entry.second & 0x01) ==
0x01) // FIXME: properly select entities from the map based on a specific bit flag
{
params->associate(entry.first);
anyAssociations = true;
}
}
if (anyAssociations)
{
if (opInstance->configure(nullptr, params->associations()))
{
auto baseView = m_attrUIMgr ? m_attrUIMgr->topView() : nullptr;
if (baseView)
{
baseView->attributeChanged(opInstance->parameters());
}
}
}
if (this->editOperation(opInstance))
......
......@@ -57,21 +57,32 @@
namespace
{
void ColorBlockAsEntity(vtkCompositePolyDataMapper2* mapper, vtkDataObject* block,
const std::string& uuid, const smtk::resource::ResourcePtr& res)
void SetAttributeBlockColorToEntity(vtkCompositeDataDisplayAttributes* atts, vtkDataObject* block,
const smtk::common::UUID& uuid, const smtk::resource::ResourcePtr& res)
{
using namespace smtk::model;
auto modelResource = std::static_pointer_cast<Resource>(res);
EntityRef entity(modelResource, smtk::common::UUID(uuid));
EntityRef entity(modelResource, uuid);
FloatList color = entity.color();
color = color[3] < 0 ? FloatList({ 1., 1., 1., 1. }) : color;
// FloatList is a typedef for std::vector<double>, so it is safe to
// pass the raw pointer to its data.
auto atts = mapper->GetCompositeDataDisplayAttributes();
atts->SetBlockColor(block, color.data());
}
void ColorBlockAsEntity(vtkGlyph3DMapper* mapper, vtkDataObject* block,
const smtk::common::UUID& uuid, const smtk::resource::ResourcePtr& res)
{
SetAttributeBlockColorToEntity(mapper->GetBlockAttributes(), block, uuid, res);
}
void ColorBlockAsEntity(vtkCompositePolyDataMapper2* mapper, vtkDataObject* block,
const smtk::common::UUID& uuid, const smtk::resource::ResourcePtr& res)
{
SetAttributeBlockColorToEntity(mapper->GetCompositeDataDisplayAttributes(), block, uuid, res);
}
void AddRenderables(
vtkMultiBlockDataSet* data, vtkSMTKModelRepresentation::RenderableDataMap& renderables)
{
......@@ -788,23 +799,16 @@ void vtkSMTKModelRepresentation::UpdateSelection(
}
vtkDataObject* vtkSMTKModelRepresentation::FindNode(
vtkMultiBlockDataSet* data, const std::string& uuid)
vtkMultiBlockDataSet* data, const smtk::common::UUID& uuid)
{
const int numBlocks = data->GetNumberOfBlocks();
for (int index = 0; index < numBlocks; index++)
{
auto currentBlock = data->GetBlock(index);
if (data->HasMetaData(index))
auto currentId = vtkModelMultiBlockSource::GetDataObjectUUID(data->GetMetaData(index));
if (currentId == uuid)
{
auto currentId = data->GetMetaData(index)->Get(vtkModelMultiBlockSource::ENTITYID());
if (currentId)
{
const std::string currentIdStr = currentId;
if (currentIdStr.compare(uuid) == 0)
{
return currentBlock;
}
}
return currentBlock;
}
auto childBlock = vtkMultiBlockDataSet::SafeDownCast(currentBlock);
......@@ -1246,13 +1250,16 @@ void vtkSMTKModelRepresentation::ColorByEntity(vtkMultiBlockDataSet* data)
while (!it->IsDoneWithTraversal())
{
auto dataObj = it->GetCurrentDataObject();
if (data->HasMetaData(it))
auto uuid = vtkModelMultiBlockSource::GetDataObjectUUID(data->GetMetaData(it));
if (uuid)
{
auto uuid = data->GetMetaData(it)->Get(vtkModelMultiBlockSource::ENTITYID());
if (uuid)
auto ent = std::dynamic_pointer_cast<smtk::model::Entity>(this->Resource->find(uuid));
if (ent && ent->isInstance())
{
ColorBlockAsEntity(this->GlyphMapper, dataObj, uuid, this->Resource);
}
else
{
// FIXME? Check whether UUID corresponds to an instance or not.
// Instances should use the GlyphMapper rather than the EntityMapper.
ColorBlockAsEntity(this->EntityMapper, dataObj, uuid, this->Resource);
}
}
......
......@@ -319,7 +319,7 @@ protected:
vtkMultiBlockDataSet* modelData, vtkMultiBlockDataSet* instanceData);
void UpdateSelection(
vtkMultiBlockDataSet* data, vtkCompositeDataDisplayAttributes* blockAttr, vtkActor* actor);
vtkDataObject* FindNode(vtkMultiBlockDataSet* data, const std::string& uuid);
vtkDataObject* FindNode(vtkMultiBlockDataSet* data, const smtk::common::UUID& uuid);
/**
* Clear the current selection stored in the mapper's
......
......@@ -13,9 +13,13 @@
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ValueItem.h"
#include "smtk/attribute/ValueItemDefinition.h"
#include "smtk/attribute/operators/Signal.h"
#include "smtk/model/EntityRef.h"
#include "smtk/operation/Manager.h"
#include "smtk/operation/Observer.h"
#include "smtk/io/Logger.h"
#include "pqActiveObjects.h"
......@@ -170,6 +174,18 @@ pqSMTKAttributeItemWidget::pqSMTKAttributeItemWidget(
m_p->m_overrideWhen = pqSMTKAttributeItemWidget::OverrideWhenConvert(ow);
m_p->m_fallbackStrategy = pqSMTKAttributeItemWidget::FallbackStrategyConvert(fs);
m_p->m_geometrySource = pqSMTKAttributeItemWidget::GeometrySourceConvert(gs);
m_p->m_opObserver = info.baseView()->uiManager()->operationManager()->observers().insert(
[&](std::shared_ptr<smtk::operation::Operation> op, smtk::operation::EventType event,
smtk::operation::Operation::Result res)
{
if (event == smtk::operation::EventType::DID_OPERATE &&
std::dynamic_pointer_cast<smtk::attribute::Signal>(op))
{
this->updateWidgetFromItem();
}
return 0;
}
);
}
pqSMTKAttributeItemWidget::pqSMTKAttributeItemWidget(smtk::attribute::ItemPtr itm, QWidget* p,
......@@ -177,12 +193,30 @@ pqSMTKAttributeItemWidget::pqSMTKAttributeItemWidget(smtk::attribute::ItemPtr it
: qtItem(smtk::extension::AttributeItemInfo(itm, smtk::view::View::Component(), p, bview))
{
m_p = new Internal(itm, this->widget(), bview, orient);
m_p->m_opObserver = bview->uiManager()->operationManager()->observers().insert(
[&](std::shared_ptr<smtk::operation::Operation> op, smtk::operation::EventType event,
smtk::operation::Operation::Result res)
{
if (event == smtk::operation::EventType::DID_OPERATE &&
std::dynamic_pointer_cast<smtk::attribute::Signal>(op))
{
this->updateWidgetFromItem();
}
return 0;
}
);
m_isLeafItem = true;
this->createWidget();
}
pqSMTKAttributeItemWidget::~pqSMTKAttributeItemWidget()
{
auto ui = this->uiManager();
auto operationManager = ui ? ui->operationManager() : nullptr;
if (operationManager && m_p->m_opObserver >= 0)
{
operationManager->observers().erase(m_p->m_opObserver);
}
delete this->m_p;
this->m_p = NULL;
}
......
......@@ -87,6 +87,7 @@ public:
public slots:
virtual void updateItemFromWidget() = 0;
virtual void updateWidgetFromItem() { }
/**\brief Change whether the item is enabled (and thus the widget active).
*
......
......@@ -39,6 +39,7 @@ public:
, m_overrideWhen(OverrideWhen::Unset)
, m_geometrySource(GeometrySource::BestGuess)
, m_fallbackStrategy(FallbackStrategy::Hide)
, m_opObserver(-1)
{
(void)itm;
(void)p;
......@@ -58,4 +59,6 @@ public:
// state of children
QMap<QWidget*, QPair<QLayout*, QWidget*> > m_children;
int m_opObserver;
};
......@@ -122,6 +122,32 @@ void pqSMTKSphereItemWidget::updateItemFromWidget()
}
}
/// Retrieve property values from ParaView proxy and store them in the attribute's Item.
void pqSMTKSphereItemWidget::updateWidgetFromItem()
{
smtk::attribute::DoubleItemPtr centerItem;
smtk::attribute::DoubleItemPtr radiusItem;
if (!fetchCenterAndRadiusItems(centerItem, radiusItem))
{
return;
}
vtkSMNewWidgetRepresentationProxy* widget = m_p->m_pvwidget->widgetProxy();
// pqImplicitPlanePropertyWidget* pw = dynamic_cast<pqImplicitPlanePropertyWidget*>(m_p->m_pvwidget);
vtkSMPropertyHelper centerHelper(widget, "Center");
vtkSMPropertyHelper radiusHelper(widget, "Radius");
bool didChange = false;
for (int i = 0; i < 3; ++i)
{
double cv = centerItem->value(i);
didChange |= (centerHelper.GetAsDouble(i) != cv);
centerHelper.Set(i, cv);
}
double rv = radiusItem->value(0);
didChange |= (radiusHelper.GetAsDouble(0) != rv);
radiusHelper.Set(rv);
}
bool pqSMTKSphereItemWidget::fetchCenterAndRadiusItems(
smtk::attribute::DoubleItemPtr& centerItem, smtk::attribute::DoubleItemPtr& radiusItem)
{
......
......@@ -32,6 +32,7 @@ public:
static qtItem* createSphereItemWidget(const AttributeItemInfo& info);
bool createProxyAndWidget(vtkSMProxy*& proxy, pqInteractivePropertyWidget*& widget) override;
void updateItemFromWidget() override;
void updateWidgetFromItem() override;
protected:
/**\brief Starting with the widget's assigned item (which must
......
......@@ -273,6 +273,7 @@ void qtAttribute::onItemModified()
static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED);
}
}
#ifndef NDEBUG
if (!didNotify)
{
static bool once = true;
......@@ -280,9 +281,13 @@ void qtAttribute::onItemModified()
{
once = false;
smtkWarningMacro(smtk::io::Logger::instance(),
"Could not notify resource observers that resource state changed.");
"Could not notify resource observers that resource state changed. "
"This is not necessarily an error if the operation is unmanaged.");
}
}
#else
(void) didNotify;
#endif // NDEBUG
}
emit this->itemModified(iobject);
emit this->modified();
......
......@@ -30,7 +30,11 @@ using namespace smtk::extension;
class qtInstancedViewInternals
{
public:
qtInstancedViewInternals() { m_isEmpty = true; }
qtInstancedViewInternals()
: m_isEmpty(true)
{
}
//QScrollArea *ScrollArea;
QList<QPointer<qtAttribute> > AttInstances;
bool m_isEmpty;
......@@ -176,6 +180,7 @@ void qtInstancedView::updateAttributeData()
this->Internals->m_isEmpty = false;
}
QObject::connect(attInstance, SIGNAL(modified()), this, SIGNAL(modified()));
QObject::connect(attInstance, SIGNAL(itemModified(qtItem*)), this, SIGNAL(itemModified(qtItem*)));
}
}
}
......
......@@ -46,6 +46,7 @@ public slots:
signals:
// emitted when an attribute is modified
void modified();
void itemModified(qtItem*);
protected:
void updateAttributeData() override;
......
......@@ -139,6 +139,7 @@ void qtOperationView::createWidget()
this->Internals->m_instancedView.reset(iview);
QObject::connect(iview, SIGNAL(modified()), this, SLOT(onModifiedParameters()));
QObject::connect(iview, SIGNAL(itemModified(qtItem*)), this, SLOT(onModifiedParameter(qtItem*)));
this->Internals->m_applyButton = new QPushButton("Apply", this->Widget);
this->Internals->m_applyButton->setObjectName("OpViewApplyButton");
......@@ -171,6 +172,21 @@ void qtOperationView::onModifiedParameters()
}
}
void qtOperationView::onModifiedParameter(qtItem* uiItem)
{
auto op = this->operation();
if (op && uiItem)
{
// Signal the operation that an item's value has been changed in the UI.
// The attribute pointer is null; that is reserved for when the UI creates
// or destroys an attribute instance.
if (op->configure(nullptr, uiItem->item()))
{
this->attributeChanged(op->parameters());
}
}
}
void qtOperationView::showAdvanceLevelOverlay(bool show)
{
this->Internals->m_instancedView->showAdvanceLevelOverlay(show);
......
......@@ -69,6 +69,7 @@ public slots:
void requestModelEntityAssociation() override;
void onShowCategory() override { this->updateAttributeData(); }
virtual void onModifiedParameters();
virtual void onModifiedParameter(qtItem* item);
virtual void onOperate();
signals:
......
......@@ -5,6 +5,7 @@ set(smtkMeshPythonDataTests
extractTessellation
cellField
meshMetrics
pointField
simple
iterateMesh
)
......
#=============================================================================
#
# 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 smtk
import smtk.common
import smtk.io
import smtk.mesh
import smtk.testing
import sys
def test_create_point_field():
# Load the mesh file
mesh_path = os.path.join(smtk.testing.DATA_DIR, 'mesh', '2d/twoMeshes.h5m')
c = smtk.mesh.Resource.create()
smtk.io.importMesh(mesh_path, c)
if not c.isValid():
raise RuntimeError("Failed to read valid mesh")
mesh = c.meshes()
field = [i for i in range(c.meshes().points().size())]
mesh.createPointField('point field', 1, field)
write_path = ''
write_path = os.path.join(smtk.testing.TEMP_DIR,
str(smtk.common.UUID.random()) + ".h5m")
smtk.io.exportMesh(write_path, c)
return write_path
def test_read_point_field(mesh_path):
# Load the mesh file
c = smtk.mesh.Resource.create()
smtk.io.importMesh(mesh_path, c)
if not c.isValid():
raise RuntimeError("Failed to read back valid mesh")
mesh = c.meshes()
pointfields = mesh.pointFields()
if not pointfields:
raise RuntimeError("No point fields associated with the mesh")
pointfield = next(iter(pointfields))
data = pointfield.get()
for i in range(pointfield.size()):
if i != data[i]:
raise RuntimeError(
"point field was not correctly saved and retrieved")
os.remove(mesh_path)
if __name__ == '__main__':
smtk.testing.process_arguments()
resource_url = test_create_point_field()
test_read_point_field(resource_url)
......@@ -88,8 +88,17 @@ Operation::Specification Operation::specification()
return m_specification;
}
bool Operation::configure(
const smtk::attribute::AttributePtr&,
const smtk::attribute::ItemPtr&)
{
// Do nothing. Subclasses might want to do something, though.
return false;
}
bool Operation::ableToOperate()
{
this->configure();
return this->parameters()->isValid();
}
......
......@@ -84,6 +84,30 @@ public:
// resolve to the same index).
virtual Index index() const { return std::type_index(typeid(*this)).hash_code(); }
/// Update the operation's specification and operations to be consistent.
///
/// This does nothing by default but subclasses may override this method to
/// update default values (say, based on the current set of associations).
/// This method will be called at the beginning of the default implementation
/// of ableToOperate and should be called by user interfaces when the associations
/// (and potentially other attributes/items in the specification) are modified.
///
/// By default, the attribute and item passed are null.
/// When an attribute is created or removed it is passed.
/// When an item's value(s) are directly edited by a user, then
/// a pointer to it is passed.
/// Only one value (the item or the attribute) should be non-null
/// for a given invocation of configure().
/// Both may be null (for instance, when an operation is being asked to
/// initialize its parameters based on the global application state rather
/// than a particular user input).
///
/// This method should return true if any changes were made to the operation's
/// specification and false otherwise.
virtual bool configure(
const smtk::attribute::AttributePtr& changedAttribute = smtk::attribute::AttributePtr(),
const smtk::attribute::ItemPtr& changedItem = smtk::attribute::ItemPtr());
// Check if the operation's attribute resource is valid. Derived operations
// may implement more task-specific checks to ensure that the operation is in
// a valid state.
......
......@@ -34,12 +34,17 @@ class DerivedFrom : public Parent
public:
typedef Parent ParentResource;
/// A static index for this resource type.
///
/// NOTE: because we are using CRTP, it is possible to make this value static
/// and redefined for each resource type, regardless of inheritance.
static const Resource::Index type_index;
/// given a resource index, return whether or not this resource is or is
/// derived from the resource described by the index.
virtual bool isOfType(const Resource::Index& index) const override
{
return std::type_index(typeid(Self)).hash_code() == index ? true
: ParentResource::isOfType(index);
return DerivedFrom<Self, Parent>::type_index == index ? true : ParentResource::isOfType(index);
}
/// given a resource's unique name, return whether or not this resource is or
......@@ -57,6 +62,10 @@ protected:
{
}
};
template <typename Self, typename Parent>
const Resource::Index DerivedFrom<Self, Parent>::type_index =
std::type_index(typeid(Self)).hash_code();
}
}
......
......@@ -184,5 +184,13 @@ int TestResourceManager(int, char** const)
smtkTest(resourceASet.size() == 3,
"Resource manager should have three resources of type ResourceA registered.");
// Test fetching resources by exact index; this will only
// return instances that are of the given class not including subclasses.
auto indexA = ResourceA::type_index;
auto resourcesByIndex =
resourceManager->resources().get<smtk::resource::IndexTag>().equal_range(indexA);
auto count = std::distance(resourcesByIndex.first, resourcesByIndex.second);
smtkTest(count == 2, "Fetched " << count << " instead of 2 resources by type-index failed.");
return 0;
}
......@@ -66,6 +66,11 @@ bool Resource::resetDomainTessellation(smtk::model::Volume& domain)
{
tess->addLine(edgeEndpoints[ee][0], edgeEndpoints[ee][1]);
}
double bbox[6] = {
origin[0], origin[0] + size[0],
origin[1], origin[1] + size[1],
origin[2], origin[2] + size[2] };
domain.setBoundingBox(bbox);
return true;
}
}
......
......@@ -21,17 +21,15 @@
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/attribute/StringItem.h"
#include "smtk/attribute/operators/Signal.h"
#include "smtk/common/UUID.h"
#include "smtk/operation/Manager.h"
#include "smtk/model/AuxiliaryGeometry.h"
#include "smtk/model/Model.h"
#include "smtk/model/Tessellation.h"
#include "smtk/mesh/core/CellTraits.h"
#include "smtk/mesh/core/CellTypes.h"
#include "smtk/mesh/utility/Create.h"
#include "smtk/common/UUID.h"
#include <ctime> // for std::time_t
......@@ -42,6 +40,69 @@ namespace session
namespace oscillator
{
bool EditSource::configure(
const smtk::attribute::AttributePtr&,
const smtk::attribute::ItemPtr& changedItem)
{
auto params = this->parameters();
auto assocs = params->associations();
if (!changedItem || changedItem != assocs || assocs->numberOfValues() != 1)
{
return false;
}
auto ent = assocs->valueAs<smtk::model::Entity>(0);
if (!ent)
{
return false;
}
// Copy their values to local fields
smtk::model::FloatList center{ 0., 0., 0. };
smtk::model::FloatList radius{ 0. };
auto source = ent->referenceAs<smtk::model::AuxiliaryGeometry>();
if (source.isValid() &&
source.hasStringProperty("oscillator_type") &&
source.stringProperty("oscillator_type")[0] == "source")
{
center = source.floatProperty("center");
radius = source.floatProperty("radius");
}
else
{
auto model = ent->referenceAs<smtk::model::Model>();
if (!model.isValid())
{
return false;
}
auto bbox = model.boundingBox();
double minDist = -1.0;
for (int ii = 0; ii < 3; ++ii)
{
center[ii] = 0.5 * (bbox[2 * ii + 0] + bbox[2 * ii + 1]);
double delta = bbox[2 * ii + 1] - bbox[2 * ii + 0];
if (delta > 0.0 && (delta > minDist || minDist < 0.0))
{
minDist = delta;
}
}
radius[0] = minDist / 4;
}
smtk::attribute::DoubleItemPtr centerItem =
this->parameters()->findGroup("location")->findAs<smtk::attribute::DoubleItem>("center");
smtk::attribute::DoubleItemPtr radiusItem =
this->parameters()->findGroup("location")->findAs<smtk::attribute::DoubleItem>("radius");
for (int ii = 0; ii < 3; ii++)
{
centerItem->setValue(ii, center[ii]);
}
radiusItem->setValue(0, radius[0]);
return true;
}
EditSource::Result EditSource::operateInternal()
{
// Access the origin, size and discretization parameters
......
......@@ -30,6 +30,17 @@ public:
smtkCreateMacro(EditSource);
smtkSharedFromThisMacro(smtk::operation::Operation);
/**\brief Change item parameters to match associated source (if any).
*
* When \a changedItem is the ReferenceItem that serves as the
* operation's associations and it contains a single "source" (i.e.,
* a 2-d auxiliary geometry representing an oscillator source),
* update the position and radius of the operation to match.
*/
bool configure(
const smtk::attribute::AttributePtr& changedAttribute,
const smtk::attribute::ItemPtr& changedItem) override;
protected:
Result operateInternal() override;
virtual const char* xmlDescription() const override;
......