Commit ca6c7e75 authored by David Thompson's avatar David Thompson
Browse files

Progress on cursor subclass implementation.

This patch includes many changes to the cursor subclasses
to improve their utility in constructing and traversing models.

+ More methods allowing model traversal are implemented (or fixed)
  and tested as part of the `unitCursor` test.

+ The methods of `Storage` that return cursor subclasses are now
  properly wrapped in Python. However, many cursor methods that
  return arrays of cursors (e.g., Vertex::edges()) still cause
  segfaults as shiboken does not properly handle value/pointer
  conversion.

+ Add a VolumeUse cursor class.
  This is for the convenience of automating adaptors to other modelers.

+ The `createTet` test helper now properly models
  vertex-uses, edge-uses, face-uses, volume-uses,
  chains, loops, and shells and is
  used to test cursor-subclass methods.

+ It also adds a compile-time option to make the Storage
  class configurable between map and sparse_hash_map.
  The new (advanced) CMake option is named `SMTK_HASH_STORAGE`.
  When true, the `sparse_hash_map` is used for the maps between

  + UUID and Entity records; and
  + UUID and Arrangement records

  in addition to property names and values.

  This makes debugging issues with invalidated iterators simpler
  since `std::map` does not invalidate iterators upon insertion
  while `google::sparse_hash_map` does.

+ The browseModel demo now allows more in-depth inspection
  of the model but has some issues. (No exploration below face-uses.)
parent 650727e2
#ifndef __smtk_options_h
#define __smtk_options_h
// Compile-time options chosen for this build of SMTK.
// Was SMTK built with Qt? If true, QtSMTK library will exist.
#cmakedefine SMTK_BUILD_QT
// Was SMTK built with VTK? If true, vtkSMTK library will exist.
#cmakedefine SMTK_BUILD_VTK
// Was SMTK built with CGM? If true, cgm-convert will exist.
#cmakedefine SMTK_BUILD_CGM
// Should sparse_hash_map be used (instead of std::map) for primary storage?
#cmakedefine SMTK_HASH_STORAGE
#endif // __smtk_options_h
......@@ -31,7 +31,8 @@ option(SMTK_BUILD_CGM "Build CGM component" OFF)
option(SMTK_ENABLE_TESTING "Enable Testing" ON)
option(SMTK_BUILD_PYTHON_WRAPPINGS "Build Python Wrappings using Shiboken" OFF)
option(SMTK_USE_SYSTEM_SPARSEHASH "Use the system-installed sparsehash?" OFF)
mark_as_advanced(SMTK_USE_SYSTEM_SPARSEHASH)
option(SMTK_HASH_STORAGE "Use sparsehash library for primary storage?" OFF)
mark_as_advanced(SMTK_USE_SYSTEM_SPARSEHASH SMTK_HASH_STORAGE)
################################################################################
# Testing Related Settings
......@@ -150,6 +151,18 @@ install (FILES ${PROJECT_BINARY_DIR}/smtk/HashFunctor.h
DESTINATION include/smtk)
################################################################################
# Save compile-time options for use by other packages
################################################################################
configure_file(
${PROJECT_SOURCE_DIR}/CMake/options.h.in
${PROJECT_BINARY_DIR}/smtk/options.h
@ONLY)
install (FILES ${PROJECT_BINARY_DIR}/smtk/options.h
DESTINATION include/smtk)
################################################################################
# Install Related Settings
################################################################################
......
......@@ -211,7 +211,11 @@ void AddArrangementsToBody(
// one of its uses has the same sense as our SenseEntity.
// If not, then we need to create a new SMTK use entity.
smtk::util::UUID smtkFaceUseId =
storage->findOrCreateCellUseOfSense(smtkFaceId, se->get_sense());
storage->findOrCreateCellUseOfSenseAndOrientation(
smtkFaceId, se->get_sense(),
se->get_sense() == CUBIT_REVERSED ?
smtk::model::NEGATIVE :
smtk::model::POSITIVE);
shellRelations[j] = smtkFaceUseId;
/*
cout
......
......@@ -15,7 +15,7 @@ namespace smtk {
* describing the relationship (e.g., HAS_USE becomes "HasUse").
* Finally, the parameters are called out as part of the method name
* where appropriate to prevent misinterpretation when calling (e.g.,
* "CellHasUseWithIndexAndSense" is used to note the order of the relation
* "CellHasUseWithIndexSenseAndOrientation" is used to note the order of the relation
* index and sense in the function call -- this does not necessarily reflect
* their order in the arrangement).
*/
......@@ -23,14 +23,17 @@ namespace smtk {
/**\brief Construct an arrangement record to add to a cell, indicating a use of that cell.
*
* The \a relationIdx is the offset in the Entity::relations() array of the USE_ENTITY.
* The \a sense is an arbitrary integer, but for edges and faces (not vertices),
* the values of the CellUseSenses enum should be used.
* The \a sense is an arbitrary integer, but for faces should be either 0 (when \a orient
* is NEGATIVE) or 1 (when \a orient is POSITIVE).
* the values of the enum should be used.
*/
Arrangement Arrangement::CellHasUseWithIndexAndSense(int relationIdx, int sense)
Arrangement Arrangement::CellHasUseWithIndexSenseAndOrientation(
int relationIdx, int sense, Orientation orient)
{
Arrangement result;
result.details().push_back(relationIdx);
result.details().push_back(sense);
result.details().push_back(orient);
return result;
}
......@@ -39,15 +42,27 @@ Arrangement Arrangement::CellHasUseWithIndexAndSense(int relationIdx, int sense)
* The \a relationIdx is the offset in the Entity::relations() array of the CELL_ENTITY.
*
* When the parent entity is a topological entity and the cell is dimension d,
* the parent entity must be of dimension **greater** than d and the cell must
* the parent entity must be of dimension greater than or equal to d and the cell must
* be completely interior to its parent.
* (Example: you may embed a point or edge in a face, but not a face within a face.)
* (Example: you may embed a point, edge, or face in a face, but not a volume within a face.)
*/
Arrangement Arrangement::CellEmbeddedInEntityWithIndex(int relationIdx)
{
Arrangement result;
result.details().push_back(relationIdx);
return result;
return Arrangement::SimpleIndex(relationIdx);
}
/**\brief Construct an arrangement record to add to a cell, indicating that it contains an entity.
*
* The \a relationIdx is the offset in the Entity::relations() array of the CELL_ENTITY.
*
* When the entity to be included is a topological entity and the cell is dimension d,
* the included entity must be of dimension than or equal to d and must be
* completely geometrically interior to the cell.
* (Example: a face may include a point or edge or face, but not a volume.)
*/
Arrangement Arrangement::CellIncludesEntityWithIndex(int relationIdx)
{
return Arrangement::SimpleIndex(relationIdx);
}
/**\brief Construct an arrangement record to add to a cell, indicating a boundary shell.
......@@ -60,9 +75,7 @@ Arrangement Arrangement::CellEmbeddedInEntityWithIndex(int relationIdx)
*/
Arrangement Arrangement::CellHasShellWithIndex(int relationIdx)
{
Arrangement result;
result.details().push_back(relationIdx);
return result;
return Arrangement::SimpleIndex(relationIdx);
}
/**\brief Construct an arrangement record to add to a cell-use, indicating its parent cell.
......@@ -79,6 +92,26 @@ Arrangement Arrangement::UseHasCellWithIndexAndSense(int relationIdx, int sense)
return result;
}
/**\brief Construct an arrangement record to add to a cell-use, indicating its parent shell.
*
* The \a relationIdx is the offset in the Entity::relations() array of a single parent SHELL_ENTITY.
* The *parent* shell must span a dimension higher than the cell-use.
* A cell-use may also have an EMBEDDED_IN arrangement to indicate child shell(s).
*/
Arrangement Arrangement::UseHasShellWithIndex(int relationIdx)
{
return Arrangement::SimpleIndex(relationIdx);
}
/**\brief Construct an arrangement record to add to a cell-use, indicating a child shell.
*
* The \a relationIdx is the offset in the Entity::relations() array of the SHELL_ENTITY.
*/
Arrangement Arrangement::UseOrShellIncludesShellWithIndex(int relationIdx)
{
return Arrangement::SimpleIndex(relationIdx);
}
/**\brief Construct an arrangement to add to a shell, indicating its parent cell.
*
* This relationship indicates that the shell forms part of the boundary of its parent cell.
......@@ -86,9 +119,7 @@ Arrangement Arrangement::UseHasCellWithIndexAndSense(int relationIdx, int sense)
*/
Arrangement Arrangement::ShellHasCellWithIndex(int relationIdx)
{
Arrangement result;
result.details().push_back(relationIdx);
return result;
return Arrangement::SimpleIndex(relationIdx);
}
/**\brief Construct an arrangement to add to a shell, indicating the uses that compose it.
*
......@@ -99,12 +130,16 @@ Arrangement Arrangement::ShellHasCellWithIndex(int relationIdx)
Arrangement Arrangement::ShellHasUseWithIndexRange(int relationBegin, int relationEnd)
{
Arrangement result;
for (int i = relationBegin; i < relationEnd; ++i)
{
result.details().push_back(i);
}
result.details().push_back(relationBegin);
result.details().push_back(relationEnd);
return result;
}
/// Create a record for a shell indicating the entity it is embedded in.
Arrangement Arrangement::ShellEmbeddedInUseOrShellWithIndex(int relationIdx)
{
return Arrangement::SimpleIndex(relationIdx);
}
///@}
/** @name Methods to interpret arrangements.
......@@ -114,38 +149,42 @@ Arrangement Arrangement::ShellHasUseWithIndexRange(int relationBegin, int relati
* If a vector is not sized properly, these methods will return false.
*/
///@{
bool Arrangement::IndexAndSenseFromCellHasUse(int& relationIdx, int& sense)
/**\brief Obtain the index (\a relationIdx), \a sense, and
* orientation (\a orient) from a cell's HAS_USE arrangement.
*/
bool Arrangement::IndexSenseAndOrientationFromCellHasUse(
int& relationIdx, int& sense, Orientation& orient) const
{
if (this->m_details.size() != 2)
if (this->m_details.size() != 3)
{
return false;
}
relationIdx = this->m_details[0];
sense = this->m_details[1];
orient = static_cast<Orientation>(this->m_details[2]);
return true;
}
bool Arrangement::IndexFromCellEmbeddedInEntity(int& relationIdx)
/// Obtain the index of an included entity from a cell EMBEDDED_IN arrangement.
bool Arrangement::IndexFromCellEmbeddedInEntity(int& relationIdx) const
{
if (this->m_details.size() != 1)
{
return false;
}
relationIdx = this->m_details[0];
return true;
return this->IndexFromSimple(relationIdx);
}
bool Arrangement::IndexFromCellHasShell(int& relationIdx)
/// Obtain the index of an included entity from a cell INCLUDES arrangement.
bool Arrangement::IndexFromCellIncludesEntity(int& relationIdx) const
{
if (this->m_details.size() != 1)
{
return false;
}
relationIdx = this->m_details[0];
return true;
return this->IndexFromSimple(relationIdx);
}
/// Obtain the index of a shell entity from a cell's HAS_SHELL arrangement.
bool Arrangement::IndexFromCellHasShell(int& relationIdx) const
{
return this->IndexFromSimple(relationIdx);
}
bool Arrangement::IndexAndSenseFromUseHasCell(int& relationIdx, int& sense)
/// Obtain the index and sense of a cell entity from a cell-use's HAS_CELL arrangement.
bool Arrangement::IndexAndSenseFromUseHasCell(int& relationIdx, int& sense) const
{
if (this->m_details.size() != 2)
{
......@@ -156,24 +195,56 @@ bool Arrangement::IndexAndSenseFromUseHasCell(int& relationIdx, int& sense)
return true;
}
bool Arrangement::IndexFromShellHasCell(int& relationIdx)
/// Obtain the index of a shell containing this cell-use.
bool Arrangement::IndexFromUseHasShell(int& relationIdx) const
{
if (this->m_details.size() != 1)
return this->IndexFromSimple(relationIdx);
}
/// Obtain the index of a child shell included in this use or shell.
bool Arrangement::IndexFromUseOrShellIncludesShell(int& relationIdx) const
{
return this->IndexFromSimple(relationIdx);
}
bool Arrangement::IndexFromShellHasCell(int& relationIdx) const
{
return this->IndexFromSimple(relationIdx);
}
bool Arrangement::IndexRangeFromShellHasUse(int& relationBegin, int& relationEnd) const
{
if (this->m_details.size() != 2)
{
return false;
}
relationIdx = this->m_details[0];
relationBegin = this->m_details[0];
relationEnd = this->m_details[1];
return true;
}
bool Arrangement::IndexRangeFromShellHasUse(int& relationBegin, int& relationEnd)
/// Obtain the index of the shell or cell-use in which this shell is embedded.
bool Arrangement::IndexFromShellEmbeddedInUseOrShell(int& relationIdx) const
{
if (this->m_details.size() != 2)
return this->IndexFromSimple(relationIdx);
}
/// Create an arrangement holding the index of a single entity ID (a simple arrangement).
Arrangement Arrangement::SimpleIndex(int relationIdx)
{
Arrangement result;
result.details().push_back(relationIdx);
return result;
}
/// Return the index of a related entity from an arrangement holding only this single index.
bool Arrangement::IndexFromSimple(int& relationIdx) const
{
if (this->m_details.size() != 1)
{
return false;
}
relationBegin = this->m_details[0];
relationEnd = this->m_details[1];
relationIdx = this->m_details[0];
return true;
}
///@}
......
......@@ -5,7 +5,10 @@
#include "smtk/model/BRepModel.h"
#include "smtk/util/UUID.h"
#include "sparsehash/sparse_hash_map"
#include "smtk/options.h" // for SMTK_HASH_STORAGE
#ifdef SMTK_HASH_STORAGE
# include "sparsehash/sparse_hash_map"
#endif // SMTK_HASH_STORAGE
#include <map>
#include <vector>
......@@ -35,19 +38,30 @@ public:
{ return this->m_details; }
///@}
static Arrangement CellHasUseWithIndexAndSense(int relationIdx, int sense);
static Arrangement CellHasUseWithIndexSenseAndOrientation(int relationIdx, int sense, Orientation o);
static Arrangement CellEmbeddedInEntityWithIndex(int relationIdx);
static Arrangement CellIncludesEntityWithIndex(int relationIdx);
static Arrangement CellHasShellWithIndex(int relationIdx);
static Arrangement UseHasCellWithIndexAndSense(int relationIdx, int sense);
static Arrangement UseHasShellWithIndex(int relationIdx);
static Arrangement UseOrShellIncludesShellWithIndex(int relationIdx);
static Arrangement ShellHasCellWithIndex(int relationIdx);
static Arrangement ShellHasUseWithIndexRange(int relationBegin, int relationEnd);
static Arrangement ShellEmbeddedInUseOrShellWithIndex(int relationIdx);
bool IndexAndSenseFromCellHasUse(int& relationIdx, int& sense);
bool IndexFromCellEmbeddedInEntity(int& relationIdx);
bool IndexFromCellHasShell(int& relationIdx);
bool IndexAndSenseFromUseHasCell(int& relationIdx, int& sense);
bool IndexFromShellHasCell(int& relationIdx);
bool IndexRangeFromShellHasUse(int& relationBegin, int& relationEnd);
bool IndexSenseAndOrientationFromCellHasUse(int& relationIdx, int& sense, Orientation& orient) const;
bool IndexFromCellEmbeddedInEntity(int& relationIdx) const;
bool IndexFromCellIncludesEntity(int& relationIdx) const;
bool IndexFromCellHasShell(int& relationIdx) const;
bool IndexAndSenseFromUseHasCell(int& relationIdx, int& sense) const;
bool IndexFromUseHasShell(int& relationIdx) const;
bool IndexFromUseOrShellIncludesShell(int& relationIdx) const;
bool IndexFromShellHasCell(int& relationIdx) const;
bool IndexRangeFromShellHasUse(int& relationBegin, int& relationEnd) const;
bool IndexFromShellEmbeddedInUseOrShell(int& relationIdx) const;
static Arrangement SimpleIndex(int relationIdx);
bool IndexFromSimple(int& relationIdx) const;
protected:
std::vector<int> m_details; // Kind-dependent specification of the arrangement.
......@@ -57,10 +71,17 @@ protected:
typedef std::vector<Arrangement> Arrangements;
/// A map holding Arrangements of different ArrangementKinds.
typedef std::map<ArrangementKind,Arrangements> KindsToArrangements;
#ifdef SMTK_HASH_STORAGE
/// Each Storage entity's UUID is mapped to a vector of Arrangment instances.
typedef google::sparse_hash_map<smtk::util::UUID,KindsToArrangements> UUIDsToArrangements;
/// An iterator referencing a (UUID,KindsToArrangements)-tuple.
typedef google::sparse_hash_map<smtk::util::UUID,KindsToArrangements>::iterator UUIDWithArrangementDictionary;
#else
/// Each Storage entity's UUID is mapped to a vector of Arrangment instances.
typedef std::map<smtk::util::UUID,KindsToArrangements> UUIDsToArrangements;
/// An iterator referencing a (UUID,KindsToArrangements)-tuple.
typedef std::map<smtk::util::UUID,KindsToArrangements>::iterator UUIDWithArrangementDictionary;
#endif // SMTK_HASH_STORAGE
/// An iterator referencing an (ArrangementKind,Arrangements)-tuple.
typedef std::map<ArrangementKind,Arrangements>::iterator ArrangementKindWithArrangements;
......
......@@ -4,12 +4,13 @@ namespace smtk {
namespace model {
const char* ArrangementKindName[KINDS_OF_ARRANGEMENTS + 1] = {
"inclusion", // INCLUSION
"cell", // CELL
"shell", // SHELL
"use", // USE
"sense", // SENSE
"embedding", // EMBEDDING
"inclusion", // INCLUDES
"cell", // HAS_CELL
"shell", // HAS_SHELL
"use", // HAS_USE
"embedding", // EMBEDDED_IN
"subset", // SUBSET_OF
"superset", // SUPERSET_OF
"invalid" // KINDS_OF_ARRANGEMENTS
};
......@@ -18,8 +19,9 @@ const char* ArrangementKindAbbr[KINDS_OF_ARRANGEMENTS + 1] = {
"c", // CELL
"s", // SHELL
"u", // ORIENTATION
"n", // SENSE
"e", // EMBEDDING
"b", // SUBSET_OF
"r", // SUPERSET_OF
"?" // KINDS_OF_ARRANGEMENTS
};
......
......@@ -8,26 +8,42 @@
namespace smtk {
namespace model {
/**\brief Constants that describe a cell-use's sense relative to its parent cell.
/**\brief Constants that describe a cell-use's orientation relative to its parent cell.
*
* These only have semantic meaning for edges and faces.
* Vertices may have any number of senses depending on the number of
* regions they border.
*
* Note that SMTK makes a distinction between the *orientation* of a cell-use
* and the *sense* of a cell-use.
* The orientation indicates whether the surface normal or curve direction
* opposes or matches the cell's.
* The *sense* is used to indicate the context in which the use occurs
* (e.g., an edge may participate in multiple face loops, and will have
* a separate edge-use for each loop).
*
* Also, note that unlike some solid modelers, SMTK composes k-shells out
* of cell-uses. This means that each k-shell is used *exactly* once
* because it may not have a NEGATIVE orientation relative to its cell-uses.
* If it did, it would be composed with oppositely-oriented cell-uses.
*/
enum CellUseSenses {
NEGATIVE_SENSE = 0,
POSITIIVE_SENSE = 1,
enum Orientation {
NEGATIVE = -1, //!< The entity is oriented opposite to the underlying geometry
POSITIVE = +1, //!< The entity is codirectional with the underlying geometry
UNDEFINED = 0 //!< The relative orientation is unknown or unknowable.
};
// === WARNING === If you change this enum, also update string names in Arrangement.cxx!
/// Specification of how a cell's relations are arranged.
enum ArrangementKind {
// Enums specific to cell, use, and shell relationships:
INCLUDES, //!< How another cell is contained in the interior of this cell.
HAS_CELL, //!< How a use or shell is related to its cell.
HAS_SHELL, //!< How this cell is bounded by cells of lower dimension or how a use participates in a shell.
HAS_USE, //!< How this cell's shells are combined into a single orientation for use by bordant cells.
SENSE, //!< How this cell participates in the boundary of a higher-dimensional cell.
EMBEDDED_IN, //!< How this cell is embedded in the interior of a cell of higher dimension
// Enums specific to group relationships:
SUBSET_OF, //!< This entity is a subset of the related entity.
SUPERSET_OF, //!< This entity is a superset of the related entity.
//
KINDS_OF_ARRANGEMENTS //!< The number of different kinds of arrangement relationships enumerated here.
};
......
......@@ -998,8 +998,17 @@ void BRepModel::prepareForEntity(std::pair<smtk::util::UUID,Entity>& entry)
if (store && !store->hasArrangementsOfKindForEntity(entry.first, HAS_USE))
{
// Create arrangements to hold face-uses:
store->arrangeEntity(entry.first, HAS_USE, Arrangement::CellHasUseWithIndexAndSense(-1, -1)); // Negative
store->arrangeEntity(entry.first, HAS_USE, Arrangement::CellHasUseWithIndexAndSense(-1, +1)); // Positive
store->arrangeEntity(entry.first, HAS_USE, Arrangement::CellHasUseWithIndexSenseAndOrientation(-1, 0, NEGATIVE));
store->arrangeEntity(entry.first, HAS_USE, Arrangement::CellHasUseWithIndexSenseAndOrientation(-1, 1, POSITIVE));
}
}
else if (entry.second.entityFlags() & USE_ENTITY)
{
Storage* store = dynamic_cast<Storage*>(this);
if (store && !store->hasArrangementsOfKindForEntity(entry.first, HAS_SHELL))
{
// Create arrangement to hold parent shell (or, for 3-uses, a parent volume cell):
store->arrangeEntity(entry.first, HAS_SHELL, Arrangement::UseHasShellWithIndex(-1));
}
}
else if ((entry.second.entityFlags() & MODEL_ENTITY) == MODEL_ENTITY)
......
......@@ -13,12 +13,21 @@
#include "smtk/model/StringData.h"
#include "smtk/model/IntegerData.h"
#include "sparsehash/sparse_hash_map"
#include "smtk/options.h" // for SMTK_HASH_STORAGE
#ifdef SMTK_HASH_STORAGE
# include "sparsehash/sparse_hash_map"
#else
# include <map>
#endif
namespace smtk {
namespace model {
#ifdef SMTK_HASH_STORAGE
typedef google::sparse_hash_map<smtk::util::UUID,Entity> UUIDsToEntities;
#else
typedef std::map<smtk::util::UUID,Entity> UUIDsToEntities;
#endif
typedef UUIDsToEntities::iterator UUIDWithEntity;
/// Primitive storage types for model properties
......@@ -41,7 +50,7 @@ enum PropertyType
class SMTKCORE_EXPORT BRepModel : smtkEnableSharedPtr(BRepModel)
{
public:
typedef google::sparse_hash_map<smtk::util::UUID,Entity> storage_type;
typedef UUIDsToEntities storage_type;
typedef storage_type::iterator iter_type;
smtkTypeMacro(BRepModel);
......
......@@ -7,6 +7,7 @@ set(modelSrcs
CellEntity.cxx
Chain.cxx
Cursor.cxx
CursorArrangementOps.cxx
DescriptivePhrase.cxx
Edge.cxx
EdgeUse.cxx
......@@ -36,6 +37,7 @@ set(modelSrcs
Vertex.cxx
VertexUse.cxx
Volume.cxx
VolumeUse.cxx
)
set(modelHeaders
......@@ -80,6 +82,7 @@ set(modelHeaders
Vertex.h
VertexUse.h
Volume.h
VolumeUse.h
)
#install the headers
......
......@@ -2,22 +2,22 @@
#include "smtk/model/Arrangement.h"
#include "smtk/model/CursorArrangementOps.h"
#include "smtk/model/ModelEntity.h"
#include "smtk/model/Storage.h"
namespace smtk {
namespace model {
/**\brief Report all of the "use" records associated with the cell.
/**\brief Return the model owning this cell (or an invalid cursor if the cell is free).
*
* The uses can be used to discover higher-dimensional cells that
* this cell borders.
* Each sense of a cell has its own use.
*/
UseEntities CellEntity::uses() const
ModelEntity CellEntity::model() const
{
UseEntities result;
CursorArrangementOps::appendAllRelations(*this, HAS_USE, result);
return result;
StoragePtr storage = this->storage();
return ModelEntity(
storage,
storage->modelOwningEntity(this->entity()));
}
/**\brief Report the toplevel shell records associated with the cell.
......@@ -25,12 +25,27 @@ UseEntities CellEntity::uses() const
* The uses can be used to discover lower-dimensional cells that
* form the boundary of this cell.
*/
ShellEntities CellEntity::shells() const
ShellEntities CellEntity::shellEntities() const
{
ShellEntities result;
CursorArrangementOps::appendAllRelations(*this, HAS_SHELL, result);
return result;
}
/*! \fn CellEntity::inclusions() const
* \brief Return the list of all entities embedded in this cell.
*
* Note that the inverse of this relation is provided by
* Cursor::embeddedIn().
*/
/*! \fn CellEntity::uses() const
* \brief Report all of the "use" records associated with the cell.
*
* The uses can be used to discover higher-dimensional cells that
* this cell borders.
* Each sense of a cell has its own use.
*/
} // namespace model
} // namespace smtk
......@@ -4,10 +4,13 @@
#include "smtk/model/Cursor.h"
#include "smtk/model/UseEntity.h" // For UseEntities
#include "smtk/model/ShellEntity.h" // For ShellEntities
#include "smtk/model/CursorArrangementOps.h" // For appendAllRelations
namespace smtk {
namespace model {
class ModelEntity;
/**\brief A cursor subclass with methods specific to cell entities.
*
*/
......@@ -16,10 +19,29 @@ class SMTKCORE_EXPORT CellEntity : public Cursor
public:
SMTK_CURSOR_CLASS(CellEntity,Cursor,isCellEntity);
UseEntities uses() const;
ShellEntities shells() const;
ModelEntity model() const;
ShellEntities shellEntities() const;