...
 
Commits (5)
......@@ -32,6 +32,7 @@ else()
find_package(Boost @SMTK_MINIMUM_BOOST_VERSION@
COMPONENTS @required_boost_components@ REQUIRED)
find_package(nlohmann_json REQUIRED)
find_pacjage(pegtl REQUIRED)
find_package(MOAB REQUIRED)
set(SMTK_ENABLE_QT_SUPPORT @SMTK_ENABLE_QT_SUPPORT@)
......
......@@ -316,6 +316,11 @@ endif ()
################################################################################
find_package(nlohmann_json REQUIRED)
################################################################################
# PEGTL Related Settings
################################################################################
find_package(pegtl REQUIRED)
################################################################################
# Moab Related Settings
################################################################################
......
## Model system changes
### Resource and Entity API
+ The model resource's `queryOperation()` method is now implemented in
Entity.cxx and includes the ability to filter entities by their type
bits (previously available) and their property values (new functionality).
### Operations
+ The "assign colors" operation now provides a way to set opacity independently
of or in tandem with colors. The user interface takes advantage of this to
provide an opacity slider. Since all model-entity colors have been stored as
a 4-component RGBA vector, the model representation now properly sets block
opacities.
......@@ -82,6 +82,7 @@ set(smtkCore_public_link_libraries
cJSON
${moab_libs}
nlohmann_json
taocpp::pegtl
)
set(smtkCore_private_link_libraries
......
This diff is collapsed.
......@@ -49,6 +49,7 @@ class SMTKCORE_EXPORT Entity : public smtk::resource::Component
public:
using UUID = smtk::common::UUID;
using QueryFunctor = std::function<bool(const smtk::resource::ConstComponentPtr&)>;
//using ResourcePtr = smtk::resource::ResourcePtr;
smtkTypeMacro(Entity);
......@@ -116,6 +117,8 @@ public:
static BitFlags dimensionToDimensionBits(int dim);
static int dimensionBitsToDimension(BitFlags dimBits);
static QueryFunctor filterStringToQueryFunctor(const std::string& spec);
int arrange(ArrangementKind, const Arrangement& arr, int index = -1);
int unarrange(ArrangementKind, int index, bool removeIfLast = false);
bool clearArrangements();
......
......@@ -1021,70 +1021,12 @@ smtk::resource::ComponentPtr Resource::find(const smtk::common::UUID& uid) const
return std::dynamic_pointer_cast<smtk::resource::Component>(this->findEntity(uid));
}
namespace
{
/// Given an entity and a mask, determine if the entity is accepted by the mask.
bool IsValueValid(const smtk::resource::ConstComponentPtr& comp, smtk::model::BitFlags mask)
{
auto modelEnt = dynamic_pointer_cast<const smtk::model::Entity>(comp);
if (modelEnt)
{
smtk::model::EntityRef c = modelEnt->referenceAs<smtk::model::EntityRef>();
if (!mask)
{
return false; // Nothing can possibly match.
}
if (mask == smtk::model::ANY_ENTITY)
{
return true; // Fast-track the trivial case.
}
smtk::model::BitFlags itemType = c.entityFlags();
// The m_membershipMask must match the entity type, the dimension, and (if the
// item is a group) group constraint flags separately;
// In other words, we require the entity type, the dimension, and the
// group constraints to be acceptable independently.
if (((mask & smtk::model::ENTITY_MASK) && !(itemType & mask & smtk::model::ENTITY_MASK) &&
(itemType & smtk::model::ENTITY_MASK) != smtk::model::GROUP_ENTITY) ||
((mask & smtk::model::ANY_DIMENSION) && !(itemType & mask & smtk::model::ANY_DIMENSION)) ||
((itemType & smtk::model::GROUP_ENTITY) && (mask & smtk::model::GROUP_CONSTRAINT_MASK) &&
!(itemType & mask & smtk::model::GROUP_CONSTRAINT_MASK)))
return false;
if (itemType != mask && itemType & smtk::model::GROUP_ENTITY &&
// if the mask is only defined as "group", don't have to check further for members
mask != smtk::model::GROUP_ENTITY)
{
// If the the membershipMask is the same as itemType, we don't need to check, else
// if the item is a group: recursively check that its members
// all match the criteria. Also, if the HOMOGENOUS_GROUP bit is set,
// require all entries to have the same entity type flag as the first.
smtk::model::BitFlags typeMask = mask;
bool mustBeHomogenous = (typeMask & smtk::model::HOMOGENOUS_GROUP) ? true : false;
if (!(typeMask & smtk::model::NO_SUBGROUPS) && !(typeMask & smtk::model::GROUP_ENTITY))
{
typeMask |= smtk::model::GROUP_ENTITY; // if groups aren't banned, allow them.
}
if (!c.as<model::Group>().meetsMembershipConstraints(c, typeMask, mustBeHomogenous))
{
return false;
}
}
return true;
}
return false;
}
}
/// Given a query string, return a functor that determines if a component is
/// accepted by the query.
std::function<bool(const ConstComponentPtr&)> Resource::queryOperation(
const std::string& queryString) const
{
smtk::model::BitFlags bitflags = queryString.empty()
? smtk::model::ANY_ENTITY
: smtk::model::Entity::specifierStringToFlag(queryString);
return std::bind(IsValueValid, std::placeholders::_1, bitflags);
return smtk::model::Entity::filterStringToQueryFunctor(queryString);
}
// visit all components in the resource.
......
......@@ -48,10 +48,6 @@ add_executable(unitArrangement unitArrangement.cxx)
target_link_libraries(unitArrangement smtkCore)
add_test(NAME unitArrangement COMMAND unitArrangement)
add_executable(unitEntity unitEntity.cxx)
target_link_libraries(unitEntity smtkCore smtkCoreModelTesting)
add_test(NAME unitEntity COMMAND unitEntity)
add_executable(unitExportMeshOperation unitExportMeshOperation.cxx)
target_compile_definitions(unitExportMeshOperation PRIVATE "SMTK_SCRATCH_DIR=\"${CMAKE_BINARY_DIR}/Testing/Temporary\"")
target_link_libraries(unitExportMeshOperation smtkCore smtkCoreModelTesting
......@@ -69,3 +65,16 @@ if (SMTK_DATA_DIR)
COMMAND $<TARGET_FILE:unitExportMeshOperation>
"${SMTK_DATA_DIR}/model/2d/smtk/test2D.json")
endif()
if (SMTK_ENABLE_POLYGON_SESSION)
list(APPEND unit_tests_which_require_data unitEntity.cxx)
endif ()
set(external_libs ${Boost_LIBRARIES})
smtk_unit_tests(
LABEL "Model"
# SOURCES ${unit_tests}
SOURCES_REQUIRE_DATA ${unit_tests_which_require_data}
LIBRARIES smtkCore smtkPolygonSession smtkCoreModelTesting ${external_libs}
)
......@@ -13,8 +13,17 @@
#include "smtk/model/IntegerData.h"
#include "smtk/model/testing/cxx/helpers.h"
#include "smtk/operation/Registrar.h"
#include "smtk/operation/operators/ReadResource.h"
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/FileItem.h"
#include "smtk/common/testing/cxx/helpers.h"
#include "smtk/session/polygon/Registrar.h"
#include "smtk/session/polygon/Resource.h"
#include <iostream>
#include <sstream>
......@@ -24,6 +33,13 @@ using namespace smtk::common;
using namespace smtk::model;
using namespace smtk::model::testing;
namespace
{
std::string dataRoot = SMTK_DATA_DIR;
std::string writeRoot = SMTK_SCRATCH_DIR;
std::string filename("/model/2d/smtk/epic-trex-drummer.smtk");
}
static const char* correct = "0x00000101 vertex\n"
"0x00000102 edge\n"
"0x00000104 face\n"
......@@ -420,8 +436,108 @@ int TestEntityIOSpecs()
return 0;
}
int main()
int TestEntityQueryFunctor()
{
std::cout << "\nTesting Entity::filterStringToQueryFunctor()\n\n";
// I. Load in a test model
smtk::resource::Manager::Ptr rsrcMgr = smtk::resource::Manager::create();
{
smtk::session::polygon::Registrar::registerTo(rsrcMgr);
}
smtk::operation::Manager::Ptr operMgr = smtk::operation::Manager::create();
{
smtk::operation::Registrar::registerTo(operMgr);
smtk::session::polygon::Registrar::registerTo(operMgr);
}
// Register the resource manager to the operation manager (newly created
// resources will be automatically registered to the resource manager).
operMgr->registerResourceManager(rsrcMgr);
std::string readFilePath = dataRoot + filename;
auto rdr = operMgr->create<smtk::operation::ReadResource>();
rdr->parameters()->findFile("filename")->setValue(readFilePath);
rdr->operate();
smtk::resource::ResourcePtr rsrc = nullptr;
std::for_each(rsrcMgr->resources().begin(), rsrcMgr->resources().end(),
[&rsrc](const smtk::resource::ResourcePtr& rr)
{
if (rr && !rsrc)
{
rsrc = rr;
}
});
smtkTest(!!rsrc, "Unable to load resource \"" + readFilePath + "\"");
// II. Try various filters with and without limiting clauses.
// Note that the whitespace here is purposefully included
// to verify that the parser will accept it.
// clang-format off
Entity::QueryFunctor qf;
qf = Entity::filterStringToQueryFunctor("model|2[ integer { 'counter' = ( 0 , 0 ) } ]");
qf = Entity::filterStringToQueryFunctor("edge [ floating-point { 'pressure' = 101.0e3 } ]");
qf = Entity::filterStringToQueryFunctor("any[ floating-point { 'color' = ( 0, 0, 0, -1 ) } ]");
// Find all groups named "drum" (test exact string-property name+value matches).
qf = Entity::filterStringToQueryFunctor("group [ string { /n.me/ = \t( /dr.m/ ) } ]");
// Find all models with the exact cell_counters integer property (test integer property).
auto q2 = Entity::filterStringToQueryFunctor("model[integer{ 'cell_counters' =( 45, 71 , 25 ,0 , 0, 0) }]");
// Find anything with a string name property regardless of value (test name-only matching).
// Note this also tests regular expressions containing square brackets inside the regex...
auto q3 = Entity::filterStringToQueryFunctor("any[ string { /n.[mM]e/ } ]");
// Again test name-only matching, but for integer properties.
auto q4 = Entity::filterStringToQueryFunctor("loop[integer]");
// Test exact floating-point property name+value matches.
auto q5 = Entity::filterStringToQueryFunctor("face[floating-point{'color'=( 1 , 0.666667\t, 0 , 1)}]");
// Test exact integer property name+value matches with scalar value (not vector tuple).
auto q6 = Entity::filterStringToQueryFunctor("any[integer{'visible'=1}]");
// Test exact integer property name+value matches.
auto q7 = Entity::filterStringToQueryFunctor("any[integer{'visible'}]");
// clang-format on
// III. Evaluate each functor on the model data
int qfCount = 0;
int q2Count = 0;
int q3Count = 0;
int q4Count = 0;
int q5Count = 0;
int q6Count = 0;
int q7Count = 0;
smtk::resource::Component::Visitor visitor =
[&](const smtk::resource::ComponentPtr& comp)
{
if (qf(comp)) { ++qfCount; }
if (q2(comp)) { ++q2Count; }
if (q3(comp)) { ++q3Count; }
if (q4(comp)) { ++q4Count; }
if (q5(comp)) { ++q5Count; }
if (q6(comp)) { ++q6Count; }
if (q7(comp)) { ++q7Count; }
};
rsrc->visit(visitor);
std::cout << " " << qfCount << " groups named 'drum'.\n";
std::cout << " " << q2Count << " models with the proper cell_counters int-vector.\n";
std::cout << " " << q3Count << " entities with string names.\n";
std::cout << " " << q4Count << " loops with any integer properties.\n";
std::cout << " " << q5Count << " faces colored orange.\n";
std::cout << " " << q6Count << " visible entities.\n";
std::cout << " " << q7Count << " visible+invisible entities.\n";
smtkTest(qfCount == 1, "Expected to find 1 group.");
smtkTest(q2Count == 1, "Expected to find 1 model.");
smtkTest(q3Count == 165, "Expected to find 165 named entities.");
smtkTest(q4Count == 23, "Expected to find 23 loops with integer properties.");
smtkTest(q5Count == 6, "Expected to find 6 of 16 faces colored orange.");
smtkTest(q6Count == 51, "Expected to find 51 of 67 visible entities.");
smtkTest(q7Count == 67, "Expected to find 67 entities with visibility.");
return 0;
}
int unitEntity(int argc, char* argv[])
{
(void)argc;
(void)argv;
int status = 0;
try
{
......@@ -434,5 +550,7 @@ int main()
status |= TestEntitySummary();
status |= TestEntityQueryFunctor();
return status ? 1 : 0;
}