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

Fixes, better attention to detail for member masks.

Some fixes and enhancements for the code processing
membership mask specifications.

This also improves the Exodus bridge to include the
true embedding dimension of the model (obtained from the
reader), both directly and in the dimension bits of
boundary and domain groups. This in turn causes
more precise descriptions of the groups and gets
the CMBv4 hydra template working so that only groups
of the proper dimensionality appear as available.

Finally, it improves the method by which the
qtAssociationWidget obtains its list of available
entities. Iterating over subphrases is not an efficient
(nor guaranteed accurate) method for obtaining all of
the entities available.
parent 0d73324a
......@@ -20,7 +20,9 @@
#include "vtkCellArray.h"
#include "vtkGeometryFilter.h"
#include "vtkHyperTreeGrid.h"
#include "vtkInformation.h"
#include "vtkInformationIntegerKey.h"
#include "vtkInformationIntegerVectorKey.h"
#include "vtkInformationStringKey.h"
#include "vtkPoints.h"
......@@ -142,29 +144,55 @@ BridgedInfoBits Bridge::transcribeInternal(
if (!obj)
return actual;
int dim = obj->GetInformation()->Get(vtkHyperTreeGrid::DIMENSION());
// Grab the parent entity early if possible... we need its dimension().
Cursor parentCursor;
EntityHandle parentHandle = handle.parent();
if (parentHandle.isValid())
{
parentCursor = this->toCursor(parentHandle);
if (!parentCursor.isValid())
{
// The handle is valid, so perhaps we were asked to
// transcribe a group before its parent model?
this->declareDanglingEntity(parentCursor, 0);
this->transcribe(parentCursor, requestedInfo, true);
}
dim = parentCursor.embeddingDimension();
}
smtk::model::Cursor mutableCursor(entity);
BitFlags entityDimBits;
if (!mutableCursor.isValid())
{
switch (handle.entityType)
{
case EXO_MODEL:
mutableCursor.manager()->insertModel(
mutableCursor.entity(), 3, 3);
mutableCursor.entity(), dim, dim);
break;
case EXO_BLOCK:
entityDimBits = Entity::dimensionToDimensionBits(dim);
mutableCursor.manager()->insertGroup(
mutableCursor.entity(), MODEL_DOMAIN,
mutableCursor.entity(), MODEL_DOMAIN | entityDimBits,
this->toBlockName(handle));
mutableCursor.as<GroupEntity>().setMembershipMask(VOLUME);
break;
case EXO_SIDE_SET:
entityDimBits = 0;
for (int i = 0; i < dim; ++i)
entityDimBits |= Entity::dimensionToDimensionBits(i);
mutableCursor.manager()->insertGroup(
mutableCursor.entity(), MODEL_BOUNDARY,
mutableCursor.entity(), MODEL_BOUNDARY | entityDimBits,
this->toBlockName(handle));
mutableCursor.as<GroupEntity>().setMembershipMask(CELL_ENTITY | entityDimBits);
break;
case EXO_NODE_SET:
mutableCursor.manager()->insertGroup(
mutableCursor.entity(), MODEL_BOUNDARY,
mutableCursor.entity(), MODEL_BOUNDARY | DIMENSION_0,
this->toBlockName(handle));
mutableCursor.as<GroupEntity>().setMembershipMask(VERTEX);
break;
default:
return actual;
......@@ -184,16 +212,9 @@ BridgedInfoBits Bridge::transcribeInternal(
if (requestedInfo & (smtk::model::BRIDGE_ENTITY_RELATIONS | smtk::model::BRIDGE_ARRANGEMENTS))
{
//this->addRelations(mutableCursor, rels, requestedInfo, -1);
EntityHandle parentHandle = handle.parent();
if (parentHandle.isValid())
if (parentCursor.isValid())
{
Cursor parentCursor = this->toCursor(parentHandle);
mutableCursor.findOrAddRawRelation(parentCursor);
if (!parentCursor.isValid())
{
this->declareDanglingEntity(parentCursor, 0);
this->transcribe(parentCursor, requestedInfo, true);
}
}
// Now add children.
std::vector<EntityHandle> children = this->childrenOf(handle);
......
......@@ -21,6 +21,8 @@
#include "smtk/model/ModelEntity.h"
#include "vtkExodusIIReader.h"
#include "vtkHyperTreeGrid.h"
#include "vtkInformation.h"
using namespace smtk::model;
using namespace smtk::common;
......@@ -65,6 +67,12 @@ smtk::model::OperatorResult ReadOperator::operateInternal()
modelOut->ShallowCopy(
vtkMultiBlockDataSet::SafeDownCast(
rdr->GetOutputDataObject(0)));
// Set the DIMENSION information key so that transcription
// can properly set dimension bits on the model and groups
// without access to the reader:
modelOut->GetInformation()->Set(
vtkHyperTreeGrid::DIMENSION(),
rdr->GetDimensionality());
Bridge* brdg = this->exodusBridge();
smtk::model::ModelEntity smtkModelOut =
brdg->addModel(modelOut);
......@@ -78,7 +86,7 @@ smtk::model::OperatorResult ReadOperator::operateInternal()
// The side and node sets now exist; go through
// and use the Exodus reader's private information
// to add property information.
// to correct the property information.
GroupEntities groups = smtkModelOut.groups();
for (GroupEntities::iterator git = groups.begin(); git != groups.end(); ++git)
{
......@@ -89,14 +97,17 @@ smtk::model::OperatorResult ReadOperator::operateInternal()
case EXO_BLOCK:
oid = rdr->GetObjectId(vtkExodusIIReader::ELEM_BLOCK, handle.entityId);
git->setStringProperty("exodus type", "element block");
git->setMembershipMask(DIMENSION_3 | MODEL_DOMAIN);
break;
case EXO_NODE_SET:
oid = rdr->GetObjectId(vtkExodusIIReader::NODE_SET, handle.entityId);
git->setStringProperty("exodus type", "node set");
git->setMembershipMask(DIMENSION_0 | MODEL_BOUNDARY);
break;
case EXO_SIDE_SET:
oid = rdr->GetObjectId(vtkExodusIIReader::SIDE_SET, handle.entityId);
git->setStringProperty("exodus type", "side set");
git->setMembershipMask(ANY_DIMENSION | MODEL_BOUNDARY);
break;
case EXO_MODEL:
default:
......
......@@ -88,7 +88,7 @@ std::vector<std::string> StringUtil::split(
for (start = 0; start != std::string::npos; )
{
match = s.find_first_of(sep, start);
std::string token = s.substr(start, match);
std::string token = s.substr(start, match - start);
if (trim)
StringUtil::trim(token);
if (!omitEmpty || !token.empty())
......
......@@ -49,6 +49,9 @@
namespace Ui { class qtAttributeAssociation; }
using namespace smtk::attribute;
using smtk::model::Cursor;
using smtk::model::Cursors;
using smtk::model::GroupEntity;
namespace detail
{
......@@ -289,11 +292,8 @@ void qtAssociationWidget::showEntityAssociation(
//add current-associated items to the current attribute
smtk::model::ManagerPtr modelManager = attDef->system()->refModelManager();
smtk::model::Cursors modelEnts;
smtk::model::Cursor::CursorsFromUUIDs(
modelEnts,
modelManager,
theAtt->associatedModelEntityIds());
smtk::model::Cursors modelEnts =
theAtt->associatedModelEntities<smtk::model::Cursors>();
typedef smtk::model::Cursors::const_iterator cit;
for (cit i =modelEnts.begin(); i != modelEnts.end(); ++i)
......@@ -304,52 +304,23 @@ void qtAssociationWidget::showEntityAssociation(
//now that we have add all the used model entities, we need to move onto
//all model entities that havent been matched
const smtk::model::BitFlags emask = smtk::model::MODEL_ENTITY;
smtk::model::Cursors cursors;
smtk::model::Cursor::CursorsFromUUIDs(
cursors,
modelManager,
modelManager->entitiesMatchingFlags(emask));
smtk::model::EntityListPhrasePtr entityList = smtk::model::EntityListPhrase::create();
entityList->setup(cursors);
//set the subphrase generator:
entityList->setDelegate(smtk::model::SimpleModelSubphrases::create());
//walk the tree getting all entities in the tree in a brute force manner
smtk::model::DescriptivePhrases subs = entityList->subphrases();
smtk::model::Cursors allEntities;
while(subs.size() > 0)
{
allEntities.insert(subs[subs.size()-1]->relatedEntity());
smtk::model::DescriptivePhrases nested_subs = subs[subs.size()-1]->subphrases();
subs.insert(subs.begin(),nested_subs.begin(),nested_subs.end());
subs.pop_back();
}
smtk::model::Cursors cursors = modelManager->entitiesMatchingFlagsAs<smtk::model::Cursors>(
attDef->associationMask(), false);
Cursors avail;
// subtract the set of already-associated entities.
std::set_difference(cursors.begin(), cursors.end(),
modelEnts.begin(), modelEnts.end(),
std::inserter(avail, avail.end()));
typedef smtk::model::Cursors::const_iterator me_it;
for(me_it i = allEntities.begin(); i != allEntities.end(); ++i)
smtk::model::Manager::Ptr tmpMgr = smtk::model::Manager::create();
GroupEntity tmpGrp = tmpMgr->addGroup();
tmpGrp.setMembershipMask(attDef->associationMask());
for(Cursors::iterator i = avail.begin(); i != avail.end(); ++i)
{
bool match = false;
if(attDef->associatesWithVertex() && i->isVertex())
{ match = true; }
if(attDef->associatesWithEdge() && i->isEdge())
{ match = true; }
if(attDef->associatesWithFace() && i->isFace())
{ match = true; }
if(attDef->associatesWithVolume() && i->isVolume())
{ match = true; }
if(attDef->associatesWithGroup() && i->isGroupEntity())
{ match = true; }
if(attDef->associatesWithModel() && i->isModelEntity())
{ match = true; }
//only add this item if we haven't seen it already
if(match && usedModelEnts.count(*i) == 0)
{
this->addModelAssociationListItem(this->Internals->AvailableList, *i);
}
if (tmpGrp.meetsMembershipConstraints(*i))
this->addModelAssociationListItem(this->Internals->AvailableList, *i);
}
this->Internals->CurrentList->blockSignals(false);
......
......@@ -113,6 +113,70 @@ int Cursor::dimensionBits() const
return 0;
}
/**\brief Set the dimensionality of this entity. WARNING: Intended for internal use only.
*
* This method is used by some bridges to change the dimensionality of a model
* after it has been transcribed. Do not call it.
*/
void Cursor::setDimensionBits(BitFlags dimBits)
{
if (this->m_manager && !this->m_entity.isNull())
{
Entity* entRec = this->m_manager->findEntity(this->m_entity);
if (entRec)
{
BitFlags old = entRec->entityFlags() & ~ANY_DIMENSION;
entRec->setEntityFlags(old | dimBits);
}
}
}
/**\brief Return the maximum number of coordinates required to parameterize this model's point locus.
*
* Unlike Cursor::dimension(), this will always return a non-negative number
* for valid cell, use, shell and model entities.
* It returns -1 for unpopulated groups with no members, instance entities, bridge sessions,
* and other entities with no dimension bits set.
*/
int Cursor::maxParametricDimension() const
{
int result = -1;
BitFlags dimbits = this->dimensionBits();
if (dimbits == 0) return result;
BitFlags onedim = DIMENSION_0;
while (1)
{
++result;
if (2 * onedim > dimbits)
return result;
onedim <<= 1;
}
return -1;
}
/**\brief Return the dimension of the space into which this entity is embedded.
*
* This is the number of actual coordinate values associated with points in
* the underlying space of the entity (i.e., the point locus that make it up).
* It should be equal to or greater than the maximum parametric dimension.
*
* All the entities in a model must have the same embedding dimension; it
* is a property stored with the model.
*
* By default, the embedding dimension is 3.
*/
int Cursor::embeddingDimension() const
{
ModelEntity owner = this->owningModel();
if (owner.isValid() && owner.hasIntegerProperty("embedding dimension"))
{
const IntegerList& prop(owner.integerProperty("embedding dimension"));
if (!prop.empty())
return prop[0];
}
return this->maxParametricDimension();
}
/// Return the bit vector describing the entity's type. \sa isVector, isEdge, ...
BitFlags Cursor::entityFlags() const
{
......
......@@ -92,9 +92,14 @@ public:
int dimension() const;
int dimensionBits() const;
void setDimensionBits(BitFlags dim);
BitFlags entityFlags() const;
std::string flagSummary(int form = 0) const;
int maxParametricDimension() const;
int embeddingDimension() const;
std::string name() const;
void setName(const std::string& n);
std::string assignDefaultName();
......
......@@ -224,6 +224,9 @@ std::string Entity::flagDimensionList(BitFlags flags, bool& plural)
std::string Entity::flagSummaryHelper(BitFlags flags, int form)
{
std::string result;
if (flags & GROUP_ENTITY)
flags &= (~ENTITY_MASK) | GROUP_ENTITY; // omit all other entity types for switch statement;
switch (flags & ENTITY_MASK)
{
case CELL_ENTITY:
......@@ -354,41 +357,57 @@ std::string Entity::flagSummary(BitFlags flags, int form)
{
std::string result = flagSummaryHelper(flags, form);
// Add some extra information about groups.
if ((flags & ENTITY_MASK) == GROUP_ENTITY)
if ((flags & GROUP_ENTITY) == GROUP_ENTITY)
{
bool dimSep = false;
if (flags & (ANY_ENTITY & ~GROUP_ENTITY))
result += " (";
if (flags & ANY_DIMENSION)
{
result += " (";
bool comma = false;
for (int i = 0; i <= 4; ++i)
{
int dim = 1 << i;
if (flags & dim)
{
if (comma)
{
if (dimSep)
result += ",";
}
result += cellNamesByDimensionSingular[i];
comma = true;
result += ('0' + i);
dimSep = true;
}
}
switch (flags & ENTITY_MASK)
if (dimSep) result += "-d";
}
bool entSep = false;
for (BitFlags entType = CELL_ENTITY; entType < ENTITY_MASK; entType <<= 1)
{
if ((entType & GROUP_ENTITY) && NO_SUBGROUPS)
continue;
if (entType & flags)
{
case CELL_ENTITY:
result += " cells)";
break;
case USE_ENTITY:
result += " uses)";
break;
case SHELL_ENTITY:
result += " shells)";
break;
default:
result += " entities)";
break;
if (entSep)
result += ", ";
else if (dimSep)
result += " ";
entSep = true;
switch (entType)
{
case CELL_ENTITY: result += "cells"; break;
case USE_ENTITY: result += "uses"; break;
case SHELL_ENTITY: result += "shells"; break;
case GROUP_ENTITY: result += "groups"; break;
case MODEL_ENTITY: result += "models"; break;
case INSTANCE_ENTITY: result += "instances"; break;
case BRIDGE_SESSION: result += "bridge sessions"; break;
default: break;
}
}
}
if (dimSep && !entSep)
result += " entities";
if (dimSep || entSep)
result += ")";
if (flags & COVER) result += " cover";
if (flags & PARTITION) result += " partition";
}
......@@ -712,5 +731,23 @@ BitFlags Entity::specifierStringToFlag(const std::string& spec)
return result;
}
/**\brief Given a dimension number (0, 1, 2, 3, 4), return the proper bitcode.
*
* This does bounds checking and will return 0 for out-of-bound dimensions.
*/
BitFlags Entity::dimensionToDimensionBits(int dim)
{
switch (dim)
{
case 0: return DIMENSION_0;
case 1: return DIMENSION_1;
case 2: return DIMENSION_2;
case 3: return DIMENSION_3;
case 4: return DIMENSION_4;
default: break;
}
return 0;
}
} // namespace model
} //namespace smtk
......@@ -72,6 +72,7 @@ public:
static std::string defaultNameFromCounters(BitFlags entityFlags, IntegerList& counters);
static std::string flagToSpecifierString(BitFlags flagsOrMask, bool textual = true);
static BitFlags specifierStringToFlag(const std::string& spec);
static BitFlags dimensionToDimensionBits(int dim);
protected:
BitFlags m_entityFlags;
......
......@@ -17,6 +17,8 @@
namespace smtk {
namespace model {
static const std::string memberMaskName("membership mask");
/**\brief Return the parent of this group.
*
* The group may be embedded in multiple containers but its first
......@@ -127,6 +129,43 @@ bool GroupEntity::meetsMembershipConstraints(
prospectiveMember, typeMask, mustBeHomogenous);
}
/**\brief Set constraints on what may be added as a member of the group.
*
* This is stored separately from the Entity record as an
* integer property named "membership mask".
*
* Note that the \a mask is not used directly; bits from
* the GroupEntity::entityFlags() related to membership constraints
* and dimensionality are bitwise-ORed with the mask.
*/
void GroupEntity::setMembershipMask(BitFlags mask)
{
this->setIntegerProperty(memberMaskName,
(this->entityFlags() & (ANY_DIMENSION | GROUP_CONSTRAINT_MASK)) |
mask);
}
/**\brief Return the mask constraining the types of entities that may be members.
*
* Note that because the group's entityFlags() also constrain members,
* this may not return the exact mask value passed to setMembershipMask();
* in particular, the group's dimension bits and group constraint bits
* are bitwise-ORed with the mask.
*/
BitFlags GroupEntity::membershipMask() const
{
BitFlags result =
(this->entityFlags() & (ANY_DIMENSION | GROUP_CONSTRAINT_MASK)) |
ENTITY_MASK;
if (this->hasIntegerProperty(memberMaskName))
{
const IntegerList& prop(this->integerProperty(memberMaskName));
if (!prop.empty())
result = static_cast<BitFlags>(prop[0]);
}
return result;
}
/**\brief A protected method used by the public version to handle recursive group tests.
*
* The \a typeMask flag is used to pass information about entities contained
......@@ -148,11 +187,11 @@ bool GroupEntity::meetsMembershipConstraintsInternal(
}
else
{
memberMask = groupFlags;
memberMask = this->membershipMask();
// First, if no entity types other than groups are specified, we assume that all
// entity types are allowed. We do not currently handle the case where you wish
// to have a group composed of only a hierarchy of groups with no other entities.
if ((groupFlags & GROUP_ENTITY) == GROUP_ENTITY) memberMask |= ENTITY_MASK;
if ((memberMask & ENTITY_MASK) == GROUP_ENTITY) memberMask |= ENTITY_MASK;
// By default, a group can contain other groups as long as their members meet the
// membership test.
if (groupFlags & NO_SUBGROUPS) memberMask &= ~GROUP_ENTITY;
......@@ -215,7 +254,7 @@ bool GroupEntity::meetsMembershipConstraintsInternal(
BitFlags memberFlags = prospectiveMember.entityFlags();
BitFlags memberTest = (memberFlags & memberMask);
if (!(memberTest & ANY_DIMENSION) && (memberMask & ANY_DIMENSION)) return false;
if (!(memberTest & ANY_ENTITY) && (memberMask & ANY_ENTITY)) return false;
if (!(memberTest & ENTITY_MASK) && (memberMask & ENTITY_MASK)) return false;
// If the prospectiveMember is a group, we must now also test its members.
// If it is not a group, maybe we should update the typemask.
......
......@@ -40,6 +40,9 @@ public:
virtual bool meetsMembershipConstraints(
const Cursor& prospectiveMember);
virtual void setMembershipMask(BitFlags mask);
BitFlags membershipMask() const;
protected:
friend class smtk::attribute::ModelEntityItemDefinition;
......
......@@ -18,6 +18,19 @@
namespace smtk {
namespace model {
/**\brief Set the number coordinate values used to specify each point in the locus of this model.
*
* This should meet or exceed the maxParametricDimension() of every entity in the model.
*
* WARNING: This is not intended to be changed during the lifetime of a model;
* this method exists for the convenience of bridge classes.
*/
void ModelEntity::setEmbeddingDimension(int dim)
{
this->setIntegerProperty("embedding dimension", dim);
}
/// Return the parent of this entity, which should be invalid or another ModelEntity.
Cursor ModelEntity::parent() const
{
return CursorArrangementOps::firstRelation<Cursor>(*this, EMBEDDED_IN);
......
......@@ -30,6 +30,8 @@ class SMTKCORE_EXPORT ModelEntity : public Cursor
public:
SMTK_CURSOR_CLASS(ModelEntity,Cursor,isModelEntity);
void setEmbeddingDimension(int dim);
Cursor parent() const;
CellEntities cells() const;
......
......@@ -37,17 +37,20 @@ static const char* correct =
"0x00000403 chain\n"
"0x00000406 loop\n"
"0x0000040c shell\n"
"0x00000801 group (vertex entities)\n"
"0x00000802 group (edge entities)\n"
"0x00000804 group (face entities)\n"
"0x00000808 group (volume entities)\n"
"0x00000809 group (vertex,volume entities)\n"
"0x02000804 domain group (face entities)\n"
"0x02000808 domain group (volume entities)\n"
"0x01000804 boundary group (face entities)\n"
"0x01000808 boundary group (volume entities)\n"
"0x00000801 group (0-d entities)\n"
"0x00000802 group (1-d entities)\n"
"0x00000804 group (2-d entities)\n"
"0x00000808 group (3-d entities)\n"
"0x00000809 group (0,3-d entities)\n"
"0x02000804 domain group (2-d entities)\n"
"0x02000808 domain group (3-d entities)\n"
"0x01000804 boundary group (2-d entities)\n"
"0x01000808 boundary group (3-d entities)\n"
"0x02000800 domain group\n"
"0x01000800 boundary group\n"
"0x02000900 domain group (cells)\n"
"0x01000902 boundary group (1-d cells)\n"
"0x00002e04 group (2-d uses, shells, instances)\n"
"0x00000101 vertices\n"
"0x00000102 edges\n"
"0x00000104 faces\n"
......@@ -60,17 +63,20 @@ static const char* correct =
"0x00000403 chains\n"
"0x00000406 loops\n"
"0x0000040c shells\n"
"0x00000801 groups (vertex entities)\n"
"0x00000802 groups (edge entities)\n"
"0x00000804 groups (face entities)\n"
"0x00000808 groups (volume entities)\n"
"0x00000809 groups (vertex,volume entities)\n"
"0x02000804 domain groups (face entities)\n"
"0x02000808 domain groups (volume entities)\n"
"0x01000804 boundary groups (face entities)\n"
"0x01000808 boundary groups (volume entities)\n"
"0x00000801 groups (0-d entities)\n"
"0x00000802 groups (1-d entities)\n"
"0x00000804 groups (2-d