Commit 9971a98c authored by David Thompson's avatar David Thompson
Browse files

Progress on force face creation.

Multiple construction methods.
Also, add smtkSuperclassMacro to polygon operators.
Add some auxiliary methods to help with transcription and edge creation
from within "force create face".
parent 1a00476a
......@@ -64,6 +64,9 @@ public:
template<typename T>
model::Edge createModelEdgeFromSegments(smtk::model::ManagerPtr mgr, T begin, T end);
template<typename T>
model::Edge createModelEdgeFromPoints(smtk::model::ManagerPtr mgr, T begin, T end, bool isFreeCell);
template<typename T>
std::set<Id> createModelEdgesFromPoints(T begin, T end);
......
......@@ -117,6 +117,101 @@ model::Edge pmodel::createModelEdgeFromSegments(model::ManagerPtr mgr, T begin,
return created;
}
/**\brief Create a model edge from ordered points with preconditions.
*
* + Points must be ordered head-to-tail.
* + Only the initial and final points may have model vertices associated
* with their locations.
* + If the initial and final points are not identical, then model vertices
* *must* be associated with their locations and those vertices must not
* have faces that overlap the edge. (This is verified before creation
* but only the immediate neighborhood of the endpoints is checked by
* examining face adjacencies at the location where the new edge would
* be inserted into the vertex connectivity.)
*
* If these preconditions do not hold, either an invalid (empty) edge will be
* returned or the model will become inconsistent.
*/
template<typename T>
model::Edge pmodel::createModelEdgeFromPoints(model::ManagerPtr mgr, T begin, T end, bool isFreeCell)
{
if (!mgr || begin == end)
return smtk::model::Edge();
Id vInit = this->pointId(*begin);
Id vFini = this->pointId((*(end - 1)));
vertex::Ptr vInitStorage = this->m_session->findStorage<vertex>(vInit);
vertex::Ptr vFiniStorage = this->m_session->findStorage<vertex>(vFini);
if (!vInitStorage ^ !vFiniStorage)
{ // one but not both of the points are model vertices. Error.
smtkErrorMacro(this->session()->log(),
"Zero or both endpoints must be model vertices to create edge.");
return smtk::model::Edge();
}
internal::vertex::incident_edges::iterator whereBegin;
internal::vertex::incident_edges::iterator whereEnd;
if (vInitStorage)
{ // Ensure edge can be inserted without splitting a face.
if (!vInitStorage->canInsertEdge(*begin, &whereBegin))
{
smtkErrorMacro(this->m_session->log(),
"Edge would overlap face in neighborhood of first vertex");
return smtk::model::Edge();
}
}
if (vFiniStorage)
{ // Ensure edge can be inserted without splitting a face.
if (!vFiniStorage->canInsertEdge(*(end - 1), &whereEnd))
{
smtkErrorMacro(this->m_session->log(),
"Edge would overlap face in neighborhood of last vertex");
return smtk::model::Edge();
}
}
// We can safely create the edge now
smtk::model::Edge created = mgr->addEdge();
internal::edge::Ptr storage = internal::edge::create();
storage->setParent(this);
storage->setId(created.entity());
this->m_session->addStorage(created.entity(), storage);
storage->m_points.clear();
storage->m_points.insert(storage->m_points.end(), begin, end);
smtk::model::Model parentModel(mgr, this->id());
// Insert edge at proper place in model vertex edge-lists
if (vInitStorage)
{
vInitStorage->insertEdgeAt(whereBegin, created.entity(), /* edge is outwards: */ true);
smtk::model::Vertex vert(mgr, vInit);
if (parentModel.isEmbedded(vert))
parentModel.unembedEntity(vert);
created.findOrAddRawRelation(vert);
}
if (vFiniStorage)
{
vFiniStorage->insertEdgeAt(whereEnd, created.entity(), /* edge is outwards: */ false);
smtk::model::Vertex vert(mgr, vFini);
if (parentModel.isEmbedded(vert))
parentModel.unembedEntity(vert);
created.findOrAddRawRelation(vert);
}
// Add tesselation to created edge using storage to lift point coordinates:
this->addEdgeTessellation(created, storage);
if (isFreeCell)
{
parentModel.embedEntity(created);
created.assignDefaultName(); // Do not move above parentModel.embedEntity() or name will suck.
}
return created;
}
template<typename T>
Point pmodel::projectPoint(T coordBegin, T coordEnd)
{
......
......@@ -26,6 +26,7 @@ public:
smtkTypeMacro(CreateEdge);
smtkCreateMacro(CreateEdge);
smtkSharedFromThisMacro(Operator);
smtkSuperclassMacro(Operator);
smtkDeclareModelOperator();
protected:
......
......@@ -47,6 +47,7 @@ public:
smtkTypeMacro(CreateFaces);
smtkCreateMacro(CreateFaces);
smtkSharedFromThisMacro(Operator);
smtkSuperclassMacro(Operator);
smtkDeclareModelOperator();
protected:
......
......@@ -36,6 +36,7 @@ public:
smtkTypeMacro(CreateModel);
smtkCreateMacro(CreateModel);
smtkSharedFromThisMacro(Operator);
smtkSuperclassMacro(Operator);
smtkDeclareModelOperator();
protected:
......
......@@ -25,6 +25,7 @@ public:
smtkTypeMacro(CreateVertices);
smtkCreateMacro(CreateVertices);
smtkSharedFromThisMacro(Operator);
smtkSuperclassMacro(Operator);
smtkDeclareModelOperator();
protected:
......
......@@ -12,6 +12,7 @@
#include "smtk/bridge/polygon/Session.h"
#include "smtk/bridge/polygon/internal/Config.h"
#include "smtk/bridge/polygon/internal/Model.h"
#include "smtk/bridge/polygon/internal/Model.txx"
#include "smtk/bridge/polygon/internal/Edge.h"
#include "smtk/bridge/polygon/internal/Util.h"
......@@ -49,19 +50,114 @@ namespace smtk {
namespace bridge {
namespace polygon {
typedef std::vector<std::pair<smtk::model::Edge,bool> > EdgesWithOrientation;
/// Ensure that we are provided the proper edge orientations in addition to the usual checks.
bool ForceCreateFace::ableToOperate()
{
// TODO: This should probably also verify that the "counts" item
// matches the number of points or edges provided:
int method = this->findInt("construction method")->discreteIndex();
smtk::attribute::IntItem::Ptr edgeDirItem = this->findInt("orientations");
int numOrient = edgeDirItem->numberOfValues();
return this->Superclass::ableToOperate() &&
(method == ForceCreateFace::POINTS ||
(method == ForceCreateFace::EDGES &&
(numOrient == -1 ||
numOrient == this->specification()->associations()->numberOfValues()
)
)
);
}
template<typename T, typename U, typename V>
void ForceCreateFace::pointsForLoop(
T& polypts,
int numPtsToUse,
U start,
U finish,
int numCoordsPerPoint,
V pmodel
)
{
// Map user point coordinates into model points.
polypts.reserve(polypts.size() + numPtsToUse);
U specEnd = start + numPtsToUse * numCoordsPerPoint;
U cit;
for (cit = start; cit != finish && cit != specEnd; cit += numCoordsPerPoint)
{
internal::Point pt = pmodel->projectPoint(cit, cit + numCoordsPerPoint);
if (!polypts.empty() && polypts.back() == pt)
{
continue;
}
polypts.push_back(pt);
}
}
template<typename T, typename U, typename V, typename W>
void ForceCreateFace::pointsForLoop(
T& polypts,
int numEdgesToUse,
U curEdge,
U edgesFinish,
V curEdgeDir,
V edgeDirFinish,
W& outerLoopEdges
)
{
smtk::attribute::ModelEntityItem::const_iterator edgesStop = curEdge + numEdgesToUse;
smtk::attribute::IntItem::value_type::const_iterator edgeDirStop = curEdgeDir + numEdgesToUse;
//model::Orientation lastOrient = model::UNKNOWN;
//model::Edge lastEdge;
bool firstInLoop = true;
for (;
curEdge != edgesFinish && curEdge != edgesStop &&
curEdgeDir != edgeDirFinish && curEdgeDir != edgeDirStop;
++curEdge, ++curEdgeDir)
{
// TODO: More sanity checking... verify prev-tail === head.
if (firstInLoop)
{
firstInLoop = false;
}
else
{ // Don't double-include endpoints.
polypts.erase(polypts.end() - 1);
}
internal::EdgePtr edgeRec= this->findStorage<internal::edge>(curEdge->entity());
if (!edgeRec)
{
std::cerr << "Skipping missing edge record " << curEdge->name() << "\n";
continue;
}
outerLoopEdges.push_back(std::make_pair(*curEdge, *curEdgeDir));
if (*curEdgeDir < 0)
{
polypts.insert(polypts.end(), edgeRec->pointsRBegin(), edgeRec->pointsREnd());
}
else
{
polypts.insert(polypts.end(), edgeRec->pointsBegin(), edgeRec->pointsEnd());
}
}
}
/// Create one or more polygonal faces without sanity checks.
smtk::model::OperatorResult ForceCreateFace::operateInternal()
{
int method = this->findInt("construction method")->discreteIndex();
smtk::attribute::DoubleItem::Ptr pointsItem = this->findDouble("points");
smtk::attribute::IntItem::Ptr coordinatesItem = this->findInt("coordinates");
smtk::attribute::IntItem::Ptr offsetsItem = this->findInt("offsets");
smtk::attribute::IntItem::Ptr countsItem = this->findInt("counts");
smtk::attribute::IntItem::Ptr edgeDirItem = this->findInt("orientations");
int numCoordsPerPt = coordinatesItem->value(0);
smtk::attribute::ModelEntityItem::Ptr modelItem = this->specification()->associations();
smtk::model::Model model;
bool ok = true;
Session* sess = this->polygonSession();
smtk::model::ManagerPtr mgr;
if (!sess || !(mgr = sess->manager()))
......@@ -70,7 +166,22 @@ smtk::model::OperatorResult ForceCreateFace::operateInternal()
return this->createResult(smtk::model::OPERATION_FAILED);
}
model = modelItem->value(0).as<smtk::model::Model>();
// Obtain the SMTK and polygon models we will work with:
switch (method)
{
case ForceCreateFace::POINTS:
model = modelItem->value(0).as<smtk::model::Model>();
break;
case ForceCreateFace::EDGES:
model = modelItem->value(0).as<smtk::model::Edge>().owningModel();
break;
default:
{
smtkErrorMacro(this->log(), "Unknown construction method " << method << ".");
return this->createResult(smtk::model::OPERATION_FAILED);
}
break;
}
internal::pmodel::Ptr pmodel = this->findStorage<internal::pmodel>(model.entity());
if (!pmodel)
{
......@@ -78,7 +189,10 @@ smtk::model::OperatorResult ForceCreateFace::operateInternal()
return this->createResult(smtk::model::OPERATION_FAILED);
}
if (pointsItem->numberOfValues() % numCoordsPerPt > 0)
// Sanity-check point coordinates array size:
if (
method == ForceCreateFace::POINTS &&
pointsItem->numberOfValues() % numCoordsPerPt > 0)
{
smtkErrorMacro(this->log(),
"Number of point-coordinates (" << pointsItem->numberOfValues() << ") " <<
......@@ -86,164 +200,159 @@ smtk::model::OperatorResult ForceCreateFace::operateInternal()
return this->createResult(smtk::model::OPERATION_FAILED);
}
// I. Map user point coordinates into model points.
std::vector<double>::const_iterator cit; // point-coordinate iterator
std::vector<internal::Point> polypts;
polypts.reserve(pointsItem->numberOfValues() / numCoordsPerPt);
for (cit = pointsItem->begin(); cit != pointsItem->end(); cit += numCoordsPerPt)
{
internal::Point pt = pmodel->projectPoint(cit, cit + numCoordsPerPt);
if (!polypts.empty() && polypts.back() == pt)
{
continue;
}
polypts.push_back(pt);
}
// I. While the counts array indicates we have faces to process:
smtk::attribute::IntItem::value_type::const_iterator countIt;
smtk::attribute::IntItem::value_type::const_iterator endCount;
// Initialize iterators over things we consume as we create faces:
smtk::attribute::DoubleItem::value_type::const_iterator coordIt = pointsItem->begin();
smtk::attribute::ModelEntityItem::const_iterator edgeIt = modelItem->begin();
smtk::attribute::IntItem::value_type::const_iterator edgeDirIt = edgeDirItem->begin();
// II. Invoke boost's create_polygon to get actual edges to create.
int oidx = 0;
std::size_t numOffsets = offsetsItem->numberOfValues();
std::size_t numPts = polypts.size();
int offset = offsetsItem->value(oidx);
if (offset == 0)
// Handle default value for "counts" item:
std::vector<int> tmpCount;
if (countsItem->numberOfValues() == 1 && countsItem->value(0) < 0)
{
++oidx;
offset = (oidx == numOffsets) ?
polypts.size() :
offsetsItem->value(oidx);
// If given default value, create a simple and valid "counts" array to iterator over:
tmpCount.push_back(
method == ForceCreateFace::POINTS ?
pointsItem->numberOfValues() / numCoordsPerPt :
modelItem->numberOfValues());
tmpCount.push_back(0);
countIt = tmpCount.begin();
endCount = tmpCount.end();
}
if (offset <= 3 || offset > static_cast<int>(polypts.size()))
else
{
smtkErrorMacro(this->log(), "Invalid offset (" << offset << ") or too few points (" << polypts.size() << ").");
return this->createResult(smtk::model::OPERATION_FAILED);
countIt = countsItem->begin();
endCount = countsItem->end();
}
poly::polygon_with_holes_data<internal::Coord> pface;
pface.set(polypts.begin(), polypts.begin() + offset);
if (offset < static_cast<int>(numPts))
// Begin loop over faces requested by "counts":
smtk::model::Faces created;
for (; countIt != endCount; )
{
EdgesWithOrientation outerLoopEdges;
// II. Queue ordered points for the outer loop
poly::polygon_with_holes_data<internal::Coord> pface;
{ // This block exists so that polypts can go out of scope early.
std::vector<internal::Point> polypts;
if (method == ForceCreateFace::POINTS)
{
this->pointsForLoop(polypts, *countIt, coordIt, pointsItem->end(), numCoordsPerPt, pmodel);
// Now create the matching SMTK edge
smtk::model::Edge modelEdge = pmodel->createModelEdgeFromPoints(mgr, polypts.begin(), polypts.end(), false);
outerLoopEdges.push_back(std::make_pair(modelEdge, true));
}
else
{
this->pointsForLoop(polypts, *countIt, edgeIt, modelItem->end(), edgeDirIt, edgeDirItem->end(), outerLoopEdges);
}
pface.set(polypts.begin(), polypts.end());
}
// III. Create a polygon set and add a polygon For each inner loop
++countIt;
std::vector<poly::polygon_data<internal::Coord> > holes;
std::vector<int> holeOffsets(offsetsItem->begin() + oidx, offsetsItem->end());
holeOffsets.push_back(pointsItem->numberOfValues() / numCoordsPerPt);
std::vector<int>::const_iterator oit; // point-coordinate iterator
for (oit = holeOffsets.begin(); oit != holeOffsets.end(); ++oit)
int numHoles = (countIt == endCount ? 0 : *countIt);
++countIt;
std::vector<EdgesWithOrientation> innerLoopsEdges;
for (int h = 0; h < numHoles; ++h, ++countIt)
{
if (*oit == offset)
continue;
// Create placeholder for SMTK model edge(s) bounding the current hole:
EdgesWithOrientation blank;
innerLoopsEdges.push_back(blank);
// Create or record edges of loop so we can add SMTK use-records below:
std::vector<internal::Point> polypts;
if (method == ForceCreateFace::POINTS)
{
this->pointsForLoop(polypts, *countIt, coordIt, pointsItem->end(), numCoordsPerPt, pmodel);
// Now create the matching SMTK edge
smtk::model::Edge modelEdge = pmodel->createModelEdgeFromPoints(mgr, polypts.begin(), polypts.end(), false);
innerLoopsEdges.back().push_back(std::make_pair(modelEdge, true));
}
else
{
this->pointsForLoop(polypts, *countIt, edgeIt, modelItem->end(), edgeDirIt, edgeDirItem->end(), innerLoopsEdges.back());
}
// Add to polygon_set_data
poly::polygon_data<internal::Coord> loop;
loop.set(polypts.begin() + offset, polypts.begin() + *oit);
loop.set(polypts.begin(), polypts.end());
holes.push_back(loop);
offset = *oit;
}
pface.set_holes(holes.begin(), holes.end());
}
// III. Create edges
smtk::model::Operator::Ptr ceo = sess->op("create edge");
if (!ceo)
{
smtkErrorMacro(this->log(), "Could not create model edge operator.");
return this->createResult(smtk::model::OPERATION_FAILED);
}
ceo->associateEntity(model);
ceo->findInt("construction method")->setDiscreteIndex(0); // construct from points, not vertices
ceo->findInt("coordinates")->setValue(0, numCoordsPerPt);
ceo->findInt("offsets")->setValues(offsetsItem->begin(), offsetsItem->end());
ceo->findDouble("points")->setValues(pointsItem->begin(), pointsItem->end());
smtk::model::OperatorResult edgeResult = ceo->operate();
if (edgeResult->findInt("outcome")->value(0) != smtk::model::OPERATION_SUCCEEDED)
{
smtkErrorMacro(this->log(), "Could not create model edge(s) of face.");
return this->createResult(smtk::model::OPERATION_FAILED);
}
smtk::attribute::ModelEntityItem::Ptr created = edgeResult->findModelEntity("created");
// IV. Transcribe face (uses, loops, face) to smtk
//
// Make up some UUIDs for the new entities.
std::vector<std::pair<smtk::model::Edge, bool> > orientedLoopEdges;
smtk::model::Edge outerEdge = created->value(0).as<smtk::model::Edge>();
orientedLoopEdges.push_back(std::make_pair(outerEdge, true));
smtk::common::UUID modelFaceId = mgr->unusedUUID();
smtk::common::UUID modelFaceUseId = mgr->unusedUUID();
smtk::common::UUID outerLoopId = mgr->unusedUUID();
if (!mgr->insertModelFaceWithOrientedOuterLoop(modelFaceId, modelFaceUseId, outerLoopId, orientedLoopEdges))
{
smtkErrorMacro(this->log(), "Could not create SMTK outer loop of face.");
return this->createResult(smtk::model::OPERATION_FAILED);
}
smtk::model::Face modelFace(mgr, modelFaceId);
model.removeCell(outerEdge);
model.addCell(modelFace);
modelFace.assignDefaultName();
// Now transcribe inner-loop edges:
int numInner = static_cast<int>(created->numberOfValues());
for (int inner = 1; inner < numInner; ++inner)
{
orientedLoopEdges.clear();
smtk::model::Edge innerEdge = created->value(inner).as<smtk::model::Edge>();
orientedLoopEdges.push_back(std::make_pair(innerEdge, true));
smtk::common::UUID innerLoopId = mgr->unusedUUID();
if (!mgr->insertModelFaceOrientedInnerLoop(innerLoopId, outerLoopId, orientedLoopEdges))
// IV. Transcribe face (uses, loops, face) to smtk
//
// Make up some UUIDs for the new entities.
smtk::common::UUID modelFaceId = mgr->unusedUUID();
smtk::common::UUID modelFaceUseId = mgr->unusedUUID();
smtk::common::UUID outerLoopId = mgr->unusedUUID();
// Transcribe the outer loop, face use, and face:
if (!mgr->insertModelFaceWithOrientedOuterLoop(modelFaceId, modelFaceUseId, outerLoopId, outerLoopEdges))
{
smtkErrorMacro(this->log(), "Could not create SMTK inner loop of face.");
smtkErrorMacro(this->log(), "Could not create SMTK outer loop of face.");
return this->createResult(smtk::model::OPERATION_FAILED);
}
model.removeCell(innerEdge);
}
smtk::model::Face modelFace(mgr, modelFaceId);
model.addCell(modelFace);
modelFace.assignDefaultName();
created.push_back(modelFace);
// V. Add tessellation
poly::polygon_set_data<internal::Coord> polys;
std::vector<poly::polygon_data<internal::Coord> > tess;
polys += pface;
get_trapezoids(tess, polys);
std::vector<poly::polygon_data<internal::Coord> >::const_iterator pit;
smtk::model::Tessellation blank;
smtk::model::UUIDsToTessellations::iterator smtkTess =
mgr->setTessellation(modelFaceId, blank);
double smtkPt[3];
for (pit = tess.begin(); pit != tess.end(); ++pit)
{
//std::cout << "Fan\n";
poly::polygon_data<internal::Coord>::iterator_type pcit;
pcit = poly::begin_points(*pit);
std::vector<int> triConn;
triConn.resize(4);
triConn[0] = smtk::model::TESS_TRIANGLE;
pmodel->liftPoint(*pcit, &smtkPt[0]);
triConn[1] = smtkTess->second.addCoords(&smtkPt[0]);
//std::cout << " " << triConn[1] << " " << smtkPt[0] << " " << smtkPt[1] << " " << smtkPt[2] << "\n";
++pcit;
pmodel->liftPoint(*pcit, &smtkPt[0]);
triConn[3] = smtkTess->second.addCoords(&smtkPt[0]);
++pcit;
//std::cout << " " << triConn[3] << " " << smtkPt[0] << " " << smtkPt[1] << " " << smtkPt[2] << "\n";
for (; pcit != poly::end_points(*pit); ++pcit)
// Now transcribe inner-loop edges:
std::size_t numInner = innerLoopsEdges.size();
for (std::size_t inner = 0; inner < numInner; ++inner)
{
smtk::common::UUID innerLoopId = mgr->unusedUUID();
if (!mgr->insertModelFaceOrientedInnerLoop(innerLoopId, outerLoopId, innerLoopsEdges[inner]))
{
smtkErrorMacro(this->log(), "Could not create SMTK inner loop of face.");
return this->createResult(smtk::model::OPERATION_FAILED);
}
}
// V. Add tessellation
poly::polygon_set_data<internal::Coord> polys;
std::vector<poly::polygon_data<internal::Coord> > tess;
polys += pface;
get_trapezoids(tess, polys);
std::vector<poly::polygon_data<internal::Coord> >::const_iterator pit;
smtk::model::Tessellation blank;
smtk::model::UUIDsToTessellations::iterator smtkTess =
mgr->setTessellation(modelFaceId, blank);
double smtkPt[3];
for (pit = tess.begin(); pit != tess.end(); ++pit)
{
triConn[2] = triConn[3];
//std::cout << "Fan\n";
poly::polygon_data<internal::Coord>::iterator_type pcit;
pcit = poly::begin_points(*pit);
std::vector<int> triConn;
triConn.resize(4);
triConn[0] = smtk::model::TESS_TRIANGLE;
pmodel->liftPoint(*pcit, &smtkPt[0]);
triConn[1] = smtkTess->second.addCoords(&smtkPt[0]);
//std::cout << " " << triConn[1] << " " << smtkPt[0] << " " << smtkPt[1] << " " << smtkPt[2] << "\n";
++pcit;
pmodel->liftPoint(*pcit, &smtkPt[0]);
triConn[3] = smtkTess->second.addCoords(&smtkPt[0]);
++pcit;
//std::cout << " " << triConn[3] << " " << smtkPt[0] << " " << smtkPt[1] << " " << smtkPt[2] << "\n";
smtkTess->second.insertNextCell(triConn);
for (; pcit != poly::end_points(*pit); ++pcit)
{
triConn[2] = triConn[3];
pmodel->liftPoint(*pcit, &smtkPt[0]);
triConn[3] = smtkTess->second.addCoords(&smtkPt[0]);
//std::cout << " " << triConn[3] << " " << smtkPt[0] << " " << smtkPt[1] << " " << smtkPt[2] << "\n";
smtkTess->second.insertNextCell(triConn);
}
//std::cout << "\n";
}
//std::cout << "\n";