Commit 757d2b5c authored by T.J. Corona's avatar T.J. Corona

Add merge operation to mesh session

parent b1382cbb
## Changes to the mesh session
### Merge Operation
There is now an operation that merges model entities of like dimension.
......@@ -5,7 +5,7 @@
<include href="smtk/operation/Operation.xml"/>
<AttDef Type="print mesh information" BaseType="operation" Label="Mesh - Print Information">
<AssociationsDef Name="mesh" LockType="Read"
NumberOfRequiredValues="1" Extensible="false" HoldReference="true">
NumberOfRequiredValues="1" Extensible="true" HoldReference="true">
<Accepts><Resource Name="smtk::mesh::Resource" Filter="meshset"/></Accepts>
</AssociationsDef>
<BriefDescription>
......
......@@ -6,8 +6,10 @@ set(meshSrcs
Topology.cxx
operators/CreateUniformGrid.cxx
operators/EulerCharacteristicRatio.cxx
operators/Import.cxx
operators/Export.cxx
operators/Import.cxx
operators/Merge.cxx
operators/Print.cxx
operators/Read.cxx
operators/Write.cxx
)
......@@ -20,8 +22,10 @@ set(meshHeaders
Topology.h
operators/CreateUniformGrid.h
operators/EulerCharacteristicRatio.h
operators/Import.h
operators/Export.h
operators/Import.h
operators/Merge.h
operators/Print.h
operators/Read.h
operators/Write.h
)
......@@ -47,8 +51,10 @@ smtk_install_library(smtkMeshSession)
# the header in their implementations.
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/CreateUniformGrid.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/EulerCharacteristicRatio.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/Import.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/Export.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/Import.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/Merge.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/Print.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/Read.sbt" meshOperationXML)
smtk_operation_xml("${CMAKE_CURRENT_SOURCE_DIR}/operators/Write.sbt" meshOperationXML)
......
......@@ -15,6 +15,8 @@
#include "smtk/session/mesh/operators/EulerCharacteristicRatio.h"
#include "smtk/session/mesh/operators/Export.h"
#include "smtk/session/mesh/operators/Import.h"
#include "smtk/session/mesh/operators/Merge.h"
#include "smtk/session/mesh/operators/Print.h"
#include "smtk/session/mesh/operators/Read.h"
#include "smtk/session/mesh/operators/Write.h"
......@@ -34,8 +36,8 @@ namespace mesh
namespace
{
typedef std::tuple<CreateUniformGrid, smtk::session::mesh::EulerCharacteristicRatio, Export, Import,
Read, Write>
typedef std::tuple<CreateUniformGrid, EulerCharacteristicRatio, Export, Import, Merge, Print, Read,
Write>
OperationList;
}
......
......@@ -65,14 +65,17 @@ public:
}
// add the unique id as a child of the model
m_root->m_children.push_back(id);
m_root->m_children.insert(id);
// construct an element for the mesh, insert it into the topology's map
// with its id as the key and, if requested, store its shell as a pair along
// with a pointer to its associated Element (for use in extracting bound
// elements).
Topology::Element* element =
&m_topology->m_elements.insert(std::make_pair(id, Topology::Element(singleMesh, m_dimension)))
&m_topology->m_elements
.insert(std::make_pair(id, Topology::Element(singleMesh, id, m_dimension)))
.first->second;
element->m_parents.insert(m_root->m_id);
if (m_shells)
{
smtk::mesh::MeshSet shell = singleMesh.extractShell();
......@@ -174,7 +177,9 @@ struct AddBoundElements
}
// Next, we remove the intersection from the contributing mesh sets
// and add the new id as a child of these sets.
// and add the new id as a child of these sets. We also record the
// sets' ids as parents of the new set.
smtk::common::UUIDArray parents;
for (auto&& shell : activeShells)
{
if (activeShells.size() > 1)
......@@ -184,15 +189,18 @@ struct AddBoundElements
m_topology->m_resource->removeMeshes(shell->first);
shell->first = tmp;
}
shell->second->m_children.push_back(id);
shell->second->m_children.insert(id);
parents.push_back(shell->second->m_id);
}
// finally, we insert it as an element into the topology. If
// necessary, we store its shell for the bound element calculation of
// lower dimension
Topology::Element* element =
&m_topology->m_elements.insert(std::make_pair(id, Topology::Element(m, m_dimension)))
&m_topology->m_elements
.insert(std::make_pair(id, Topology::Element(m, id, m_dimension)))
.first->second;
element->m_parents.insert(parents.begin(), parents.end());
if (m_shells)
{
m_shells->push_back(std::make_pair(m.extractShell(), element));
......@@ -222,7 +230,8 @@ Topology::Topology(
, m_modelId(modelId)
{
// Insert the mesh resource as the top-level element representing the model
Element* model = &(m_elements.insert(std::make_pair(modelId, Element(meshset))).first->second);
Element* model =
&(m_elements.insert(std::make_pair(modelId, Element(meshset, modelId))).first->second);
if (constructHierarchy)
{
......
......@@ -41,15 +41,18 @@ struct SMTKMESHSESSION_EXPORT Topology
struct Element
{
Element(const smtk::mesh::MeshSet& mesh, int dimension = -1)
Element(const smtk::mesh::MeshSet& mesh, const smtk::common::UUID& id, int dimension = -1)
: m_mesh(mesh)
, m_dimension(dimension)
, m_id(id)
{
}
smtk::mesh::MeshSet m_mesh;
int m_dimension;
smtk::common::UUIDArray m_children;
smtk::common::UUID m_id;
std::set<smtk::common::UUID> m_parents;
std::set<smtk::common::UUID> m_children;
};
smtk::mesh::ResourcePtr m_resource;
......
//=========================================================================
// 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/session/mesh/operators/Merge.h"
#include "smtk/session/mesh/Resource.h"
#include "smtk/session/mesh/Session.h"
#include "smtk/session/mesh/Topology.h"
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ComponentItem.h"
#include "smtk/common/CompilerInformation.h"
#include "smtk/mesh/core/Resource.h"
#include "smtk/mesh/utility/Metrics.h"
#include "smtk/model/Face.h"
#include "smtk/model/Model.h"
#include "smtk/resource/Component.h"
#include "smtk/resource/Resource.h"
#include "smtk/session/mesh/Merge_xml.h"
using namespace smtk::model;
using namespace smtk::common;
namespace smtk
{
namespace session
{
namespace mesh
{
bool Merge::ableToOperate()
{
if (!this->smtk::operation::XMLOperation::ableToOperate())
{
return false;
}
// Access the associated model entities
auto associations = this->parameters()->associations();
// Access the first input model entity
auto component = associations->valueAs<smtk::resource::Component>();
// Access the model resource associated with the first input model entity
smtk::session::mesh::Resource::Ptr resource =
std::static_pointer_cast<smtk::session::mesh::Resource>(component->resource());
// Access the model resource's associated topology
smtk::session::mesh::Topology* topology = resource->session()->topology(resource);
// Access the dimension of the model entity
int dimension = topology->m_elements.at(component->id()).m_dimension;
for (auto it = associations->begin(); it != associations->end(); ++it)
{
// All model entities must come from the same resource
if (resource->id() !=
std::static_pointer_cast<smtk::resource::Component>(*it)->resource()->id())
{
return false;
}
// Also, all model entities must have the same dimension
if (dimension != topology->m_elements.at(it->id()).m_dimension)
{
return false;
}
}
return true;
}
Merge::Result Merge::operateInternal()
{
// Access the associated model entities
auto associations = this->parameters()->associations();
// Access the model resource associated with all of the input model entities
smtk::session::mesh::Resource::Ptr resource =
std::static_pointer_cast<smtk::session::mesh::Resource>(
associations->valueAs<smtk::resource::Component>()->resource());
// Access the model resource's associated mesh resource
smtk::mesh::Resource::Ptr meshResource = resource->resource();
// Access the model resource's associated topology
smtk::session::mesh::Topology* topology = resource->session()->topology(resource);
Result result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
smtk::attribute::ComponentItem::Ptr created = result->findComponent("created");
smtk::attribute::ComponentItem::Ptr modified = result->findComponent("modified");
smtk::attribute::ComponentItem::Ptr expunged = result->findComponent("expunged");
// Create a new id for the merged entity.
smtk::common::UUID id = resource->unusedUUID();
smtk::mesh::HandleRange cells;
std::set<smtk::common::UUID> parents;
std::set<smtk::common::UUID> children;
int dimension = -1;
std::string name;
std::set<smtk::mesh::MeshSet> toRemove;
// For each model entity to be merged...
for (auto it = associations->begin(); it != associations->end(); ++it)
{
//...access its associated topology element.
auto elementIt = topology->m_elements.find(it->id());
Topology::Element& element = elementIt->second;
// If This is our first model entity...
if (dimension == -1)
{
//...assign the dimension and parent elements.
dimension = element.m_dimension;
parents.insert(element.m_parents.begin(), element.m_parents.end());
}
else
{
// Otherwise, assign parent elements as the intersection of the current
// parent list and the parents of the current topology element.
std::set<smtk::common::UUID> intersection;
std::set_intersection(parents.begin(), parents.end(), element.m_parents.begin(),
element.m_parents.end(), std::inserter(intersection, intersection.begin()));
parents = std::move(intersection);
}
// Aggregate the cells that comprise the element.
cells += element.m_mesh.cells().range();
// Aggregate the element's children.
children.insert(element.m_children.begin(), element.m_children.end());
// Access the entity ref associated wit hthe current model entity...
smtk::model::EntityRef eRef(resource, it->id());
//...and remove relations to higher- and lower-dimensional entities.
for (auto parentId = element.m_parents.begin(); parentId != element.m_parents.end(); ++parentId)
{
auto parentElementIt = topology->m_elements.find(*parentId);
Topology::Element& parentElement = parentElementIt->second;
parentElement.m_children.erase(it->id());
parentElement.m_children.insert(id);
smtk::model::EntityRef parentEntityRef(resource, *parentId);
parentEntityRef.elideRawRelation(eRef);
eRef.elideRawRelation(parentEntityRef);
modified->appendValue(parentEntityRef.component());
}
for (auto childId = element.m_children.begin(); childId != element.m_children.end(); ++childId)
{
auto childElementIt = topology->m_elements.find(*childId);
Topology::Element& childElement = childElementIt->second;
childElement.m_parents.erase(it->id());
childElement.m_parents.insert(id);
smtk::model::EntityRef childEntityRef(resource, *childId);
childEntityRef.elideRawRelation(eRef);
eRef.elideRawRelation(childEntityRef);
modified->appendValue(childEntityRef.component());
}
if (!name.empty())
{
name += " & ";
}
name += eRef.name();
// Add the model entity to the list of expunged components.
expunged->appendValue(std::static_pointer_cast<smtk::resource::Component>(*it));
// Remove the model entity from the model resource.
resource->erase(eRef);
// Remove the underlying meshset.
toRemove.insert(element.m_mesh);
// Finally, remove the topology element.
topology->m_elements.erase(elementIt);
}
smtk::model::EntityRef entityRef(resource, id);
entityRef.setName(name);
smtk::mesh::MeshSet mergedMesh =
meshResource->createMesh(smtk::mesh::CellSet(meshResource, cells));
// Associate the merged mesh with the merged model entity.
mergedMesh.setModelEntityId(id);
// Now that the new mesh is created, we can delete the old meshes without
// losing the cell and point information.
for (auto& mesh : toRemove)
{
meshResource->removeMeshes(mesh);
}
Topology::Element* element =
&topology->m_elements.insert(std::make_pair(id, Topology::Element(mergedMesh, id, dimension)))
.first->second;
// If there are no common parents for the merged element, assign the
// encompassing model as its parent.
if (parents.empty())
{
parents.insert(topology->m_modelId);
}
element->m_parents.insert(parents.begin(), parents.end());
element->m_children.insert(children.begin(), children.end());
// Declare the model as "dangling" so it will be transcribed.
resource->session()->declareDanglingEntity(entityRef);
for (auto& parentId : element->m_parents)
{
smtk::model::EntityRef parentEntityRef(resource, parentId);
resource->session()->declareDanglingEntity(parentEntityRef);
resource->session()->transcribe(parentEntityRef, smtk::model::SESSION_ENTITY_RELATIONS, false);
}
// If we don't call "transcribe" ourselves, it never gets called.
resource->session()->transcribe(entityRef, smtk::model::SESSION_EVERYTHING, false);
created->appendValue(entityRef.component());
return result;
}
const char* Merge::xmlDescription() const
{
return Merge_xml;
}
} // namespace mesh
} // namespace session
} // namespace smtk
//=========================================================================
// 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_session_mesh_Merge_h
#define __smtk_session_mesh_Merge_h
#include "smtk/session/mesh/Exports.h"
#include "smtk/operation/XMLOperation.h"
namespace smtk
{
namespace session
{
namespace mesh
{
/**\brief Merge entities from the same model and of like dimension into a single
entity.
*/
class SMTKMESHSESSION_EXPORT Merge : public smtk::operation::XMLOperation
{
public:
smtkTypeMacro(smtk::session::mesh::Merge);
smtkCreateMacro(Merge);
smtkSharedFromThisMacro(smtk::operation::Operation);
bool ableToOperate() override;
protected:
Result operateInternal() override;
virtual const char* xmlDescription() const override;
};
}
}
}
#endif
<?xml version="1.0" encoding="utf-8" ?>
<!-- Description of the "Merge" Operation -->
<SMTK_AttributeResource Version="3">
<Definitions>
<include href="smtk/operation/Operation.xml"/>
<AttDef Type="merge" Label="Model - Merge Entities" BaseType="operation">
<BriefDescription>
Merge entities from the same model and of like dimension into a single entity.
</BriefDescription>
<AssociationsDef Name="Entities" NumberOfRequiredValues="2" Extensible="true">
<Accepts><Resource Name="smtk::session::mesh::Resource" Filter="cell"/></Accepts>
</AssociationsDef>
</AttDef>
<!-- Result -->
<include href="smtk/operation/Result.xml"/>
<AttDef Type="result(merge)" BaseType="result"/>
</Definitions>
</SMTK_AttributeResource>
//=========================================================================
// 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/session/mesh/operators/Print.h"
#include "smtk/session/mesh/Resource.h"
#include "smtk/session/mesh/Session.h"
#include "smtk/session/mesh/Topology.h"
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ComponentItem.h"
#include "smtk/mesh/core/Resource.h"
#include "smtk/mesh/utility/Metrics.h"
#include "smtk/model/Face.h"
#include "smtk/model/Model.h"
#include "smtk/resource/Component.h"
#include "smtk/resource/Resource.h"
#include "smtk/session/mesh/Print_xml.h"
using namespace smtk::model;
using namespace smtk::common;
namespace smtk
{
namespace session
{
namespace mesh
{
Print::Result Print::operateInternal()
{
// Access the associated model entities
auto associations = this->parameters()->associations();
// Access the model resource associated with all of the input model entities
smtk::session::mesh::Resource::Ptr resource =
std::static_pointer_cast<smtk::session::mesh::Resource>(
associations->valueAs<smtk::resource::Component>()->resource());
// Access the model resource's associated mesh resource
smtk::mesh::Resource::Ptr meshResource = resource->resource();
// Access the model resource's associated topology
smtk::session::mesh::Topology* topology = resource->session()->topology(resource);
// For each model entity to be merged...
for (auto it = associations->begin(); it != associations->end(); ++it)
{
//...access its associated topology element.
auto elementIt = topology->m_elements.find(it->id());
Topology::Element& element = elementIt->second;
smtk::mesh::MeshSet meshset = element.m_mesh;
smtkInfoMacro(this->log(), "Model Entity <" << it->id() << ">\n"
<< " name: " << it->name()
<< "\n"
" # meshes: "
<< meshset.size() << "\n"
" # cells: "
<< meshset.cells().size() << "\n"
" # points: "
<< meshset.points().size() << "\n"
" # domains: "
<< meshset.domains().size() << "\n"
" # dirichlets: "
<< meshset.dirichlets().size() << "\n"
" # neumanns: "
<< meshset.neumanns().size());
}
return this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
;
}
const char* Print::xmlDescription() const
{
return Print_xml;
}
}
}
}
//=========================================================================
// 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_session_mesh_Print_h
#define __smtk_session_mesh_Print_h
#include "smtk/session/mesh/Exports.h"
#include "smtk/operation/XMLOperation.h"
namespace smtk
{
namespace session
{
namespace mesh
{
/**\brief Print the underlying data of a mesh session entity.
*/
class SMTKMESHSESSION_EXPORT Print : public smtk::operation::XMLOperation
{
public:
smtkTypeMacro(smtk::session::mesh::Print);
smtkCreateMacro(Print);
smtkSharedFromThisMacro(smtk::operation::Operation);
protected:
Result operateInternal() override;
virtual const char* xmlDescription() const override;
};
}
}
}
#endif
<?xml version="1.0" encoding="utf-8" ?>
<!-- Description of the "Print" Operation -->
<SMTK_AttributeResource Version="3">
<Definitions>
<include href="smtk/operation/Operation.xml"/>
<AttDef Type="print" Label="Model - Print Entity Information" BaseType="operation">
<BriefDescription>
Print the underlying data of a mesh session entity.
</BriefDescription>
<AssociationsDef Name="Entities" NumberOfRequiredValues="1" Extensible="true">
<Accepts><Resource Name="smtk::session::mesh::Resource" Filter="cell"/></Accepts>
</AssociationsDef>
</AttDef>
<!-- Result -->
<include href="smtk/operation/Result.xml"/>
<AttDef Type="result(print)" BaseType="result"/>
</Definitions>
</SMTK_AttributeResource>
......@@ -28,10 +28,12 @@ py::class_< smtk::session::mesh::Topology > pybind11_init_smtk_session_mesh_Topo
.def("resource", [](const smtk::session::mesh::Topology& topology){ return topology.m_resource; })
;
py::class_< smtk::session::mesh::Topology::Element >(instance, "Element")
.def(py::init<::smtk::mesh::MeshSet, int>())
.def(py::init<::smtk::mesh::MeshSet, smtk::common::UUID const &, int>())
.def(py::init<::smtk::session::mesh::Topology::Element const &>())
.def("deepcopy", (smtk::session::mesh::Topology::Element & (smtk::session::mesh::Topology::Element::*)(::smtk::session::mesh::Topology::Element const &)) &smtk::session::mesh::Topology::Element::operator=)
.def_readwrite("m_mesh", &smtk::session::mesh::Topology::Element::m_mesh)
.def_readwrite("m_dimension", &smtk::session::mesh::Topology::Element::m_dimension)
.def_readwrite("m_parents", &smtk::session::mesh::Topology::Element::m_parents)
.def_readwrite("m_children", &smtk::session::mesh::Topology::Element::m_children)
;
return instance;
......
......@@ -12,6 +12,7 @@
set (unit_tests
TestCreateUniformGridOp.cxx
TestMergeOp.cxx
)
set (unit_tests_which_require_data
......
//=========================================================================
// 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/attribute/Attribute.h"
#include "smtk/attribute/ComponentItem.h"
#include "smtk/attribute/DoubleItem.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/attribute/GroupItem.h"
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/attribute/StringItem.h"
#include "smtk/session/mesh/Registrar.h"
#include "smtk/session/mesh/Session.h"
#include "smtk/session/mesh/operators/CreateUniformGrid.h"
#include "smtk/session/mesh/operators/Merge.h"
#include "smtk/session/mesh/operators/Print.h"
#include "smtk/common/testing/cxx/helpers.h"
#include "smtk/model/CellEntity.h"
#include "smtk/model/EntityRef.h"
#include "smtk/model/Face.h"
#include "smtk/model/Group.h"
#include "smtk/model/Resource.h"
#include "smtk/resource/Manager.h"
#include "smtk/operation/Manager.h"
#ifdef SMTK_ENABLE_VTK_SUPPORT
#include "smtk/extension/vtk/source/vtkModelMultiBlockSource.h"
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkCommand.h"
#include "vtkCompositePolyDataMapper.h"
#include "vtkDataSetAttributes.h"
#include "vtkInteractorStyleSwitch.h"
#include "vtkNew.h"
#include "vtkPlane.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSmartPointer.h"
#include "vtkStringArray.h"
#include "vtkXMLMultiBlockDataWriter.h"
// This macro ensures the vtk io library is loaded into the executable
smtkComponentInitMacro(smtk_extension_vtk_io_mesh_MeshIOVTK)
#endif
#include "nlohmann/json.hpp"
namespace
{
void UniqueEntities(const smtk::model::EntityRef& root, std::set<smtk::model::EntityRef>& unique)
{
smtk::model::EntityRefArray children = (root.isModel()
? root.as<smtk::model::Model>().cellsAs<smtk::model::EntityRefArray>()
: (root.isCellEntity()
? root.as<smtk::model::CellEntity>().boundingCellsAs<smtk::model::EntityRefArray>()