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

Progress on regions and loops.

Disconnected regions and loops are correctly detected.
Disjoint regions are not merged together; that requires
additional work.
parent 37651591
......@@ -310,17 +310,14 @@ struct SweeplinePosition
struct EdgeFragmentComparator
{
FragmentArray* m_sweptFragments;
SweeplinePosition* m_sweepPosition;
EdgeFragmentComparator(FragmentArray& frag, SweeplinePosition& startPoint)
: m_sweptFragments(&frag), m_sweepPosition(&startPoint)
: m_sweptFragments(&frag)
{
}
EdgeFragmentComparator(const EdgeFragmentComparator& other)
:
m_sweptFragments(other.m_sweptFragments),
m_sweepPosition(other.m_sweepPosition)
: m_sweptFragments(other.m_sweptFragments)
{
}
......@@ -329,17 +326,6 @@ struct EdgeFragmentComparator
{
return this->m_sweptFragments;
}
/**\brief Return the current sweep line location, which moves from left to right.
*
* Fragments are ordered bottom-to-top along the vertical line passing through this point.
* It is possible for multiple sweep points to line on the same vertical line;
* points directly beneath the sweepline position are considered
* to be to the "left" of the current point.
*/
internal::Point position() const
{
return this->m_sweepPosition->m_position;
}
/**\brief Return true when line a lies to the left of and/or below line b for the current sweepLocation.
*
* When the sweep position is at an intersection point of 2 or more lines, we order
......@@ -357,6 +343,11 @@ struct EdgeFragmentComparator
if (a >= fsize)
return false;
// Now we know a is valid (or we would have returned false).
// A segment is never less than itself.
if (b == a)
return false;
// Valid segments are always below invalid ones:
if (b >= fsize)
return true;
......@@ -365,56 +356,44 @@ struct EdgeFragmentComparator
EdgeFragment& lineA((*this->fragments())[a]);
EdgeFragment& lineB((*this->fragments())[b]);
// I. Compare their y-intercepts with the sweepline
internal::Point p = this->position();
internal::HighPrecisionCoord ya =
lineA.m_lo.y() * (lineA.m_hi.x() - p.x()) +
lineA.m_hi.y() * (p.x() - lineA.m_lo.x());
internal::Coord denomA = lineA.m_hi.x() - lineA.m_lo.x();
internal::HighPrecisionCoord yb =
lineB.m_lo.y() * (lineB.m_hi.x() - p.x()) +
lineB.m_hi.y() * (p.x() - lineB.m_lo.x());
internal::Coord denomB = lineB.m_hi.x() - lineB.m_lo.x();
// I. Compare y coordinates of fragment lo() points.
// Since active fragments do not cross (or have their hi() coordinates
// altered to an intersection point) comparing low coordinates is
// valid as long as fragments are removed once their hi() neighborhood
// has been processed.
if (lineA.lo().y() < lineB.lo().y())
return true;
else if (lineA.lo().y() > lineB.lo().y())
return false;
if (denomA == 0) // Fragment a is vertical, so y_a is technically the sweep position.
{
if (denomB == 0) // Both fragments are vertical and intersect our vertical sweep line
{
return (
// Order by the "lower" coordinates
(lineA.m_lo.x() < lineB.m_lo.x() || // FIXME: Not necessary since lines are vertical?
(lineA.m_lo.x() == lineB.m_lo.x() && // FIXME: Not necessary since lines are vertical?
(lineA.m_lo.y() < lineB.m_lo.y() ||
(lineA.m_lo.y() == lineB.m_lo.y() &&
// Order so that "lower" segment is the one that will *exit* the active fragments first
// (i.e., by smaller m_hi.y()) and then by edge Id and segment Id so that no segments are identical
(lineA.m_hi.y() < lineB.m_hi.y() ||
(lineA.m_hi.y() == lineB.m_hi.y() &&
(lineA.m_edge.entity() < lineB.m_edge.entity() ||
(lineA.m_edge.entity() == lineB.m_edge.entity() &&
(lineA.m_segment < lineB.m_segment ||
(lineA.m_segment == lineB.m_segment &&
(lineA.m_sense < lineB.m_sense)))))))))))) ? true : false;
}
else
{ // A is vertical, B is not. A < B for all lines that could possibly be active.
return true;
}
}
else if (denomB == 0)
{ // A is not vertical, B is vertical. A > B for all lines that could possibly be active.
// II. Compare slopes of fragments when they have identical lo().y() coords.
// Multiple fragments can start at the same point but shouldn't have the
// same slope. Note that because fragments always go from low to high
// x coordinates, we do not have to worry about the direction of the
// inequality changing because of negative delta_x values in the slope.
// (delta_x >= 0 for all fragments).
internal::HighPrecisionCoord dxA = lineA.hi().x() - lineA.lo().x();
internal::HighPrecisionCoord dxB = lineB.hi().x() - lineB.lo().x();
internal::HighPrecisionCoord slopeDiff =
dxB * (lineA.hi().y() - lineA.lo().y()) -
dxA * (lineB.hi().y() - lineB.lo().y());
if (slopeDiff < 0)
return true;
else if (slopeDiff > 0)
return false;
}
else
{
return (ya * denomB < yb * denomA) ? true : false;
}
// We are here because of a problem. No two line segments should be
// collinear. If they are, one corresponding fragment should be discarded
// before being added to m_fragments. So, we should complain but not die.
std::cerr
<< "Error: trying to insert coincident, collinear fragments " << a << " and " << b << " into active fragment tree.";
return a < b;
}
};
/// The sweepline Interval Tree (IT), of active edge segments, is a set of offsets into the array of fragments.
typedef std::set<FragmentId, EdgeFragmentComparator> ActiveSegmentTree;
typedef std::set<FragmentId, EdgeFragmentComparator> ActiveFragmentTree;
/// The set of all regions is a UnionFind (UF) data structure.
typedef smtk::common::UnionFind<int> RegionIdSet;
......@@ -480,7 +459,7 @@ internal::HighPrecisionCoord cross2d(const internal::Coord oa[2], const internal
*/
struct Neighborhood
{
Neighborhood(SweeplinePosition& x, FragmentArray& fragments, SweepEventSet& eventQueue, ActiveSegmentTree& active)
Neighborhood(SweeplinePosition& x, FragmentArray& fragments, SweepEventSet& eventQueue, ActiveFragmentTree& active)
: m_point(&x), m_fragments(&fragments), m_eventQueue(&eventQueue), m_activeEdges(&active)
{
}
......@@ -488,10 +467,11 @@ struct Neighborhood
SweeplinePosition* m_point;
FragmentArray* m_fragments;
SweepEventSet* m_eventQueue;
ActiveSegmentTree* m_activeEdges;
ActiveFragmentTree* m_activeEdges;
RegionIdSet m_regionIds;
RegionDefinitions m_regions;
std::vector<FragmentId> m_fragmentsToQueue;
std::set<FragmentId> m_fragmentsToDeactivate;
std::list<FragmentId> m_ring; // offsets into m_fragments that order a neighborhood CCW
/**\brief Return the orientation of a fragment relative to the neighborhood.
......@@ -503,7 +483,7 @@ struct Neighborhood
*/
bool isFragmentOutgoing(const EdgeFragment& frag)
{
return frag.lo() <= this->m_point->position();
return frag.lo() == this->m_point->position();
}
/// The space between \a ringA and \a ringB is not interrupted; mark coedges of A/B as same region.
......@@ -518,25 +498,6 @@ struct Neighborhood
bool isOutA = this->isFragmentOutgoing(fragA); // true when m_point is coincident with fragA.lower
bool isOutB = this->isFragmentOutgoing(fragB);
// XXX FIXME URHERE TODO!!!
// Merging fragments should always be done according to the following logic:
// - all incomplete fragment chains point from low-to-high (x_min to x_max, then y_min to y_max)
// - completed fragment chains must be oriented to match the region (CCW for outer loops, CW for inner loops);
// that means: fragA is outgoing, fragB is incoming. In terms of vertices, the order must be B-x-A.
// - when linking chains ending at fragA and fragB:
// - if either fragment is starting (i.e., isOutA or isOutB), then that fragment must be the new chain
// end and the chain must go from low to high.
// - if !isOutA && !isOutB (both fragments ending), then the chain should be oriented from B to A.
// - the chain direction is independent of the region-merge winner?
// TODO. Fix so that no regionId is ever < 0.
// Then this code becomes moot and we just:
// - insert fragment A to end of m_regions[find(fragA.regionIds[0])] if fragA's coedge 0 points "backwards"
// - insert fragment B to end of m_regions[find(fragB.regionIds[1])] if fragB's coedge 1 points "backwards"
// - winner = mergeSet(fragA.regionIds[0], fragB.regionIds[1])
// - pop loser (!= winner) from m_regions and hold onto it.
// - append/prepend m_regions[loser] to m_regions[winner] (append if winner = fragA, prepend if winner = fragB)
// - if m_regions[winner] is closed, output region and add it as an inner loop to the region containing the neighborhood (if any).
RegionIdSet::value_type idA = this->m_regionIds.find(fragA.ccwRegion(isOutA));
RegionIdSet::value_type idB = this->m_regionIds.find(fragB.cwRegion(isOutB));
RegionIdSet::value_type winner;
......@@ -689,15 +650,24 @@ struct Neighborhood
this->m_fragmentsToQueue.push_back(fragId);
}
void removeActiveEdge(FragmentId fragId, EdgeFragment& frag)
void removeActiveEdge(FragmentId fragId)
{
this->m_fragmentsToDeactivate.insert(fragId);
}
/**\brief Process the neighborhood of one or more event endpoints.
*
* When this method is called, m_ring contains a CCW-ordered list
* of fragments incident to the sweepline position.
*/
void processQueue()
{
std::cout << "Neighborhood::processQueue()\n";
// If we have any incident edges (and it is highly suspicious if we don't)
// then loop over adjacent pairs assigning/merging region IDs.
// I. Merge regions associated with neighboring fragments.
// This also marks one co-edge of the pair with the "next"
// fragment in the loop bounding a region.
if (!this->m_ring.empty())
{ // Note that ringA == ringB is valid (both sides of fragment are the same regionId).
std::list<FragmentId>::iterator ringA = this->m_ring.end();
......@@ -712,6 +682,9 @@ struct Neighborhood
this->assignAndMergeRegions(ringA, ringB);
}
}
#if 0
// Debug printout
std::list<FragmentId>::iterator rit;
for (rit = this->m_ring.begin(); rit != this->m_ring.end(); ++rit)
{
......@@ -722,6 +695,10 @@ struct Neighborhood
<< " " << frag.m_edge.name() << ", seg " << frag.m_segment
<< "\n";
}
#endif // 0
// II. We are done processing the ring; if any incident edges are outgoing,
// add their SEGMENT_END events to the event queue.
std::vector<FragmentId>::iterator it;
for (it = this->m_fragmentsToQueue.begin(); it != this->m_fragmentsToQueue.end(); ++it)
{
......@@ -732,6 +709,15 @@ struct Neighborhood
}
this->m_fragmentsToQueue.clear();
this->m_ring.clear();
// III. Remove active edges going out of scope after the neighborhood
// has been visited.
while (!this->m_fragmentsToDeactivate.empty())
{
std::set<FragmentId>::iterator fragIt = this->m_fragmentsToDeactivate.begin();
this->m_activeEdges->erase(*fragIt);
this->m_fragmentsToDeactivate.erase(fragIt);
}
}
/**\brief Advance the sweepline to the next event's point.
......@@ -744,9 +730,6 @@ struct Neighborhood
{
if (this->m_point->m_position != pt)
{
// Advance the sweepline the absolute minimum expressable amount.
// This makes it possible to insert fragments that start at this->m_point->m_position:
this->m_point->m_position.y(this->m_point->m_position.y()+1);
// Now it is safe to add edges which crossed at the previous sweepline point.
this->processQueue();
......@@ -817,13 +800,6 @@ static void AddLoopsForEdge(
}
#endif // 0
template<typename T>
void ConditionalErase(T& container, typename T::iterator item, bool shouldErase)
{
if (shouldErase)
container.erase(item);
}
void DumpEventQueue(const char* msg, SweepEventSet& eventQueue)
{
std::cout << ">>>>> " << msg << "\n";
......@@ -992,24 +968,22 @@ smtk::model::OperatorResult CreateFaces::operateInternal()
internal::Point startPoint = eventQueue.begin()->point();
startPoint.x(startPoint.x() - 1);
SweeplinePosition sweepPosn(startPoint);
ActiveSegmentTree activeEdges(
ActiveFragmentTree activeEdges(
EdgeFragmentComparator(fragments, sweepPosn)); // (IT)
Neighborhood neighborhood(sweepPosn, fragments, eventQueue, activeEdges); // N(x)
// Set the initial sweepline to before the beginning of the queue.
bool shouldErase;
for (
event = eventQueue.begin();
(event = eventQueue.begin()) != eventQueue.end();
ConditionalErase(eventQueue, event, shouldErase))
)
{
shouldErase = true;
neighborhood.advanceSweeplineTo(event->point()); // XXX URHERE
neighborhood.advanceSweeplineTo(event->point());
event = eventQueue.begin(); // Advancing the sweepline may have changed the eventQueue.
/*
std::cout
<< "Event " << event->type() << " posn " << event->point().x() << " " << event->point().y()
;
/*
if (event->type() == SweepEvent::SEGMENT_START)
{
std::cout << " edge " << event->m_edge.entity().toString() << " seg " << event->m_indx << "\n";
......@@ -1040,6 +1014,15 @@ smtk::model::OperatorResult CreateFaces::operateInternal()
this->processSegmentCross(*event, fragments, sweepPosn, activeEdges, edgesToInsertAfterAdvance, neighborhood);
break;
}
// The event is processed; remove it.
eventQueue.erase(event);
// Now if the queue is empty, advance the sweepline just past the current point in order
// to process the neighborhood (which may add events to the queue).
if (eventQueue.empty())
{
neighborhood.processQueue();
}
}
// Dump report of regions discovered and chains bounding regions.
......@@ -1072,7 +1055,7 @@ void CreateFaces::processSegmentStart(
const SweepEvent& event,
FragmentArray& fragments,
SweeplinePosition& sweepPosn,
ActiveSegmentTree& activeEdges,
ActiveFragmentTree& activeEdges,
SweepEventArray& edgesToInsertAfterAdvance,
Neighborhood& n)
{
......@@ -1125,14 +1108,14 @@ void CreateFaces::processSegmentEnd(
// Find neighbors of fragment in activeEdges
// Remove fragment from activeEdges
// If neighbors intersect, add crossing to event queue
n.removeActiveEdge(fragId, frag);
n.removeActiveEdge(fragId);
}
void CreateFaces::processSegmentCross(
const SweepEvent& event,
FragmentArray& fragments,
SweeplinePosition& sweepPosn,
ActiveSegmentTree& activeEdges,
ActiveFragmentTree& activeEdges,
SweepEventArray& edgesToInsertAfterAdvance,
Neighborhood& n)
{
......
......@@ -32,7 +32,7 @@ struct SweeplinePosition;
typedef size_t FragmentId;
typedef std::set<SweepEvent> SweepEventSet;
typedef std::vector<SweepEvent> SweepEventArray;
typedef std::set<FragmentId, EdgeFragmentComparator> ActiveSegmentTree;
typedef std::set<FragmentId, EdgeFragmentComparator> ActiveFragmentTree;
typedef std::vector<EdgeFragment> FragmentArray;
typedef int RegionId;
typedef smtk::common::UnionFind<RegionId> RegionIdSet;
......@@ -60,7 +60,7 @@ protected:
const SweepEvent& event,
FragmentArray& fragments,
SweeplinePosition& sweepPosn,
ActiveSegmentTree& activeEdges,
ActiveFragmentTree& activeEdges,
SweepEventArray& edgesToInsertAfterAdvance,
Neighborhood& n);
void processSegmentEnd(
......@@ -71,7 +71,7 @@ protected:
const SweepEvent& event,
FragmentArray& fragments,
SweeplinePosition& sweepPosn,
ActiveSegmentTree& activeEdges,
ActiveFragmentTree& activeEdges,
SweepEventArray& edgesToInsertAfterAdvance,
Neighborhood& n);
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment