You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4574 lines
124 KiB
4574 lines
124 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
*
|
|
* Path Self Intersection Remover Class.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Classes and functions used to remove self intersections in paths.
|
|
* Given a path, it produces one or more polygons which can be used to
|
|
* draw a widened path that is safe to use alternate fill mode with.
|
|
*
|
|
* Notes:
|
|
*
|
|
* Modified from Office code frOm John Bowler (at least that is what
|
|
* ericvan told me). Apparently owned by some 'KasiaK', but no idea
|
|
* who that is. (He is apparently retired)
|
|
* CAUTION: Not thoroughly tested yet for arbitrary paths.
|
|
*
|
|
* API:
|
|
* Init(EstimatedNumPts);
|
|
* AddPolygon(pathPts, numPts);
|
|
* RemoveSelfIntersects();
|
|
* GetNewPoints(newPts, polyCounts, numPolys, numTotalPts);
|
|
*
|
|
* Created:
|
|
*
|
|
* 06/06/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
// Return values for IntersectEdge
|
|
#define DONOT_INTERS 0
|
|
#define COMMON_POINT 1
|
|
#define INTERSECT 2
|
|
#define COLINEAR 3
|
|
|
|
// Used to produce the IEEE floating-point representation of infinity.
|
|
// Note that a few compile warnings have to be turned off to stop
|
|
// warnings about constant arithmetic overflow.
|
|
|
|
#pragma warning (disable : 4056 4756)
|
|
#define FP_INF (FLT_MAX+FLT_MAX)
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Insert a new item into a sorted dynamic array, keeping it sorted.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* newItem - new item to be inserted
|
|
* compareFunc - comparison function to be used for insertion.
|
|
* userData - User-specified data for use by the compare func.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
template <class T>
|
|
GpStatus
|
|
DynSortArray<T>::InsertSorted(
|
|
T &newItem,
|
|
DynSortArrayCompareFunc compareFunc,
|
|
VOID *userData
|
|
)
|
|
{
|
|
// insert item into sorted position of list.
|
|
T *cur;
|
|
INT pos = 0;
|
|
GpStatus status;
|
|
|
|
cur = GetDataBuffer();
|
|
|
|
{
|
|
INT sgn = 1;
|
|
unsigned iMin = 0;
|
|
unsigned iMid = 0;
|
|
unsigned iEnd = GetCount();
|
|
while (iMin != iEnd)
|
|
{
|
|
iMid = iMin + (iEnd-iMin)/2;
|
|
//Assert(iMid != iMac);
|
|
sgn = compareFunc(
|
|
(PathSelfIntersectRemover*)userData,
|
|
&GetDataBuffer()[iMid],
|
|
&newItem
|
|
);
|
|
if (sgn == 0)
|
|
{
|
|
// newItem already in sorted list, return index.
|
|
return Ok;
|
|
}
|
|
if (sgn < 0) // x(iMid) < x(p)
|
|
iMin = iMid+1;
|
|
else
|
|
iEnd = iMid;
|
|
}
|
|
|
|
pos = iMin;
|
|
}
|
|
|
|
status = InsertAt(pos,newItem);
|
|
if (status != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
cur = &GetDataBuffer()[pos];
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/****************************************************************************\
|
|
Private helper functions
|
|
\****************************************************************************/
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Return the sign of an INT
|
|
*
|
|
* Arguments:
|
|
*
|
|
* newItem - new item to be inserted
|
|
* compareFunc - comparison function to be used for insertion.
|
|
* userData - User-specified data for use by the compare func.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* 1 if greater than zero
|
|
* 0 if equal to zero
|
|
* -1 if less than zero
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
inline INT SignReal(const REAL i) { return (i > 0) - (i < 0); }
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Insert an edge into a linked list. This assumes the edge is an
|
|
* orphaned edge (not connected in a current list). Use DeleteEdgeFromList
|
|
* to orphan an edge if it's already in a list.
|
|
*
|
|
* This uses a double indirection pointer to track the address of the
|
|
* Next pointer that points to the current element rather than actually
|
|
* tracking the current element. This simplifies the code significantly.
|
|
*
|
|
* Created:
|
|
*
|
|
* 12/23/2000 asecchia
|
|
*
|
|
\**************************************************************************/
|
|
|
|
void PathSelfIntersectRemover::InsertEdgeIntoList(
|
|
INT *pListHead,
|
|
INT index,
|
|
DynSortArrayCompareFunc compare
|
|
)
|
|
{
|
|
ASSERT(EdgeList[index].Next == LIST_END);
|
|
ASSERT(index >= 0);
|
|
ASSERT(pListHead);
|
|
|
|
INT *pIndex = pListHead;
|
|
Edge *newEdge = &EdgeList[index];
|
|
|
|
// Calculate the YCur for this edge.
|
|
|
|
newEdge->YCur = PathPts[newEdge->SortBegin].Y;
|
|
newEdge->SortBegin = newEdge->Begin;
|
|
newEdge->SortEnd = newEdge->End;
|
|
newEdge->Normalize();
|
|
|
|
|
|
while(*pIndex != LIST_END)
|
|
{
|
|
if(compare(this, &EdgeList[*pIndex], newEdge) != -1)
|
|
{
|
|
// if we find the right spot, exit the search loop.
|
|
|
|
break;
|
|
}
|
|
|
|
// keep looking...
|
|
|
|
pIndex = &EdgeList[*pIndex].Next;
|
|
}
|
|
|
|
// Do the insertion
|
|
|
|
newEdge->Next = *pIndex;
|
|
*pIndex = index;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Delete an edge from a linked list.
|
|
* This uses a double indirection pointer to track the address of the
|
|
* Next pointer that points to the current element rather than actually
|
|
* tracking the current element. This simplifies the code significantly.
|
|
*
|
|
* Returns true if it found and deleted the edge, false otherwise.
|
|
*
|
|
* Created:
|
|
*
|
|
* 12/23/2000 asecchia
|
|
*
|
|
\**************************************************************************/
|
|
|
|
bool PathSelfIntersectRemover::DeleteEdgeFromList(
|
|
INT *pListHead,
|
|
INT index
|
|
)
|
|
{
|
|
ASSERT(index >= 0);
|
|
ASSERT(pListHead);
|
|
|
|
INT *pIndex = pListHead;
|
|
|
|
while(*pIndex != LIST_END)
|
|
{
|
|
if(*pIndex == index)
|
|
{
|
|
// found it.
|
|
*pIndex = EdgeList[index].Next; // point past the deleted item.
|
|
EdgeList[index].Next = LIST_END; // disconnect the deleted item.
|
|
return true;
|
|
}
|
|
|
|
// keep looking...
|
|
|
|
pIndex = &EdgeList[*pIndex].Next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Insert edges into the active edge list.
|
|
* This function takes a sorted block of edges from the pInactiveIndex list
|
|
* and inserts them sorted into the pActiveIndex list in linear time.
|
|
* The block from the pInactiveIndex list is known to be contiguous.
|
|
*
|
|
* Actually the source list and destination list are not sorted with the
|
|
* same sorting comparison function and therefore we can't optimize based
|
|
* on the known sort order of the destination. This is inefficient. Making
|
|
* them use the same sort order and fixing the active edge traversal code
|
|
* to compute the winding numbers would probably work better - it would
|
|
* allow us an O(n) merge sort here.
|
|
*
|
|
* Created:
|
|
*
|
|
* 03/25/2000 andrewgo
|
|
* 12/17/2000 asecchia - copied from aarasterizer.cpp and modified for the
|
|
* PathSelfIntersectRemover. When we have the time
|
|
* we should really merge these two pieces of code.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
void PathSelfIntersectRemover::InsertNewEdges(
|
|
INT *pActiveIndex, // IN/OUT
|
|
INT *pInactiveIndex, // IN/OUT
|
|
REAL xCurrent,
|
|
DynSortArrayCompareFunc compare
|
|
)
|
|
{
|
|
ASSERT(pInactiveIndex);
|
|
|
|
while(
|
|
(*pInactiveIndex != LIST_END) &&
|
|
((PathPts[EdgeList[*pInactiveIndex].SortBegin].X < xCurrent) ||
|
|
(CloseReal(xCurrent, PathPts[EdgeList[*pInactiveIndex].SortBegin].X))
|
|
))
|
|
{
|
|
// this is an edge we should move.
|
|
|
|
INT index = *pInactiveIndex;
|
|
|
|
// delete this element from the inactive list.
|
|
// this updates pInactiveIndex for the next iteration of the loop.
|
|
|
|
*pInactiveIndex = EdgeList[*pInactiveIndex].Next;
|
|
EdgeList[index].Next = LIST_END;
|
|
|
|
// Insert into the active list from the current active position.
|
|
|
|
InsertEdgeIntoList(pActiveIndex, index, compare);
|
|
|
|
// Update the active list pointer.
|
|
|
|
// Can't do this currently - our source and dest have different
|
|
// sort order. Were we sure that our source and destination were
|
|
// sorted with the same comparison function, we could remember the
|
|
// current position here and continue where we left off next time
|
|
// round - this would change the complexity from O(n^2) to O(n).
|
|
// Currently this is not critical path because it's used on the
|
|
// active edge list (small relative to the inactive list).
|
|
|
|
//pActiveIndex = &EdgeList[index].Next;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Normalize the edge - ie. update SortBegin and SortEnd.
|
|
*
|
|
* All lines have sorted begin and end. Begin.X is always X <= than End.X.
|
|
* If they are equal, Begin.Y <= End.Y
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None
|
|
*
|
|
* Return Value:
|
|
*
|
|
* None
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID Edge::Normalize()
|
|
{
|
|
|
|
if (Parent->PathPts[Begin].X < Parent->PathPts[End].X)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((Parent->PathPts[Begin].X == Parent->PathPts[End].X) &&
|
|
(Parent->PathPts[Begin].Y <= Parent->PathPts[End].Y))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// swap the points;
|
|
SortBegin = End;
|
|
SortEnd = Begin;
|
|
|
|
return;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Return TRUE if the real numbers are close. This uses the parent's
|
|
* comparison criteria.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] val1, val2 - REAL numbers to be compared for closeness
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE or FALSE
|
|
*
|
|
* Created:
|
|
*
|
|
* 9/11/2000 peterost
|
|
*
|
|
\**************************************************************************/
|
|
|
|
inline BOOL Edge::CloseReal(const REAL val1, const REAL val2)
|
|
{
|
|
return Parent->CloseReal(val1, val2);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Return TRUE if the edge is vertical.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE or FALSE
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL Edge::IsVertical()
|
|
{
|
|
return (CloseReal(Parent->PathPts[Begin].X,
|
|
Parent->PathPts[End].X));
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Mark the edge as outside
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID Edge::MarkOutside()
|
|
{
|
|
PointListNode *ptNode = NULL;
|
|
ptNode = &Parent->PtList[Begin];
|
|
ptNode->Inside = FALSE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Initialize PathSelfIntersectRemover with for the given number of path points. The
|
|
* number of points doesn't have to be exact, it just initializes arrays
|
|
* to avoid reallocation later.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* numPts - number of points that will be added to the path.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus.
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus PathSelfIntersectRemover::Init(INT numPts)
|
|
{
|
|
BOOL failed=FALSE;
|
|
GpStatus status;
|
|
|
|
// !!!: Decide what the initial number of elements should be.
|
|
// In general, we usually will have one extra intersection
|
|
// per vertex in the inset path. - KasiaK
|
|
|
|
// Initialize array with points
|
|
failed |= PathPts.ReserveSpace(numPts+1) != Ok;
|
|
|
|
// Initialize array with order information
|
|
failed |= PtList.ReserveSpace(2*numPts) != Ok;
|
|
|
|
failed |= EdgeList.ReserveSpace(2*numPts) != Ok;
|
|
|
|
ActiveEdgeList = LIST_END;
|
|
InactiveEdgeList = LIST_END;
|
|
|
|
if (failed)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Add a single polygon to the PathSelfIntersectRemover class.
|
|
* You cannot AddPolygon() after calling RemoveSelfIntersects().
|
|
*
|
|
* Arguments:
|
|
*
|
|
* ptrPts - points to add.
|
|
* numPtsToAdd - number of points to add.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus.
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
PathSelfIntersectRemover::AddPolygon(
|
|
const GpPointF *ptrPts,
|
|
INT numPtsToAdd
|
|
)
|
|
{
|
|
// Cannot add points after fixing path.
|
|
|
|
ASSERT(CanAddPts);
|
|
ASSERT(ptrPts != NULL);
|
|
|
|
if (numPtsToAdd < 2)
|
|
{
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus status;
|
|
|
|
// Make sure there is enough room in the arrays:
|
|
|
|
status = PathPts.ReserveSpace(numPtsToAdd+1);
|
|
|
|
if (status != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
INT oldNumPts = NumPts;
|
|
if (InsertPoints(ptrPts, numPtsToAdd) != Ok ||
|
|
InsertEdges(oldNumPts, NumPts-oldNumPts-1) != Ok)
|
|
{
|
|
return GenericError;
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Insert points information to relevant arrays.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pts - points to add to the class.
|
|
* numPts - number of points we want to add.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus.
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
PathSelfIntersectRemover::InsertPoints(
|
|
const GpPointF *pts,
|
|
INT numPts
|
|
)
|
|
{
|
|
INT FirstIndex = NumPts;
|
|
PointListNode ptNode;
|
|
GpPointF pt(0,0);
|
|
|
|
GpStatus status;
|
|
|
|
// We don't want to add 0-length edges
|
|
// Also, we don't want to add very thin spikes, for example
|
|
// when pts[n] == pts[n+2] (or almost the same. We will just
|
|
// skip pts[n+1] and pts[n+2].
|
|
pt = pts[0];
|
|
if ((status = PathPts.Add(pt)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
NumPts++;
|
|
for (INT i = 1; i < numPts; i++)
|
|
{
|
|
if (!IsClosePointF(pts[i], pts[i-1]))
|
|
{
|
|
if ((status = PathPts.Add(pts[i])) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
NumPts++;
|
|
}
|
|
}
|
|
|
|
// Add the first point to close the path
|
|
if (!IsClosePointF(pts[0], pts[numPts-1]))
|
|
{
|
|
pt = pts[0];
|
|
if ((status = PathPts.Add(pt)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
NumPts++;
|
|
}
|
|
|
|
// If all the points were equal we hit this point with NumPts set to 1
|
|
// which is a degenerate polygon.
|
|
// Make sure we handle this correctly.
|
|
|
|
if(NumPts < 2)
|
|
{
|
|
ONCE(WARNING(("Degenerate polygon in InsertPoints")));
|
|
PathPts.SetCount(0);
|
|
NumPts = 0;
|
|
return Ok;
|
|
}
|
|
|
|
// Initialize the linked list;
|
|
|
|
// If this is not the first set of points to be added, update
|
|
// the next ptr in the last element of the existing list
|
|
|
|
|
|
if (FirstIndex != 0 && PtList.GetCount() > 0)
|
|
{
|
|
PtList.Last().Next = FirstIndex;
|
|
}
|
|
|
|
// index 0:
|
|
ptNode.Prev = FirstIndex-1; // -1 means NULL;
|
|
if (NumPts == FirstIndex+1) // only one point is being added
|
|
{
|
|
ptNode.Next = -1;
|
|
ptNode.Dup = -1; // if we added one point, there is no dup
|
|
}
|
|
else
|
|
{
|
|
ptNode.Next = FirstIndex+1;
|
|
ptNode.Dup = NumPts-1; // the first point is same as closing point
|
|
}
|
|
ptNode.Inside = TRUE;
|
|
ptNode.Used = FALSE;
|
|
if ((status = PtList.Add(ptNode)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
//indecies 1..NumPts-1
|
|
INT ptIndex;
|
|
for (ptIndex = FirstIndex+1; ptIndex < NumPts-1; ptIndex++)
|
|
{
|
|
ptNode.Prev = ptIndex-1; // -1 means NULL;
|
|
ptNode.Next = ptIndex+1;
|
|
ptNode.Dup = -1;
|
|
ptNode.Inside = TRUE;
|
|
ptNode.Used = FALSE;
|
|
if ((status = PtList.Add(ptNode)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//index NumPts
|
|
ptNode.Prev = ptIndex-1;
|
|
ptNode.Next = -1; // -1 means NULL;
|
|
ptNode.Dup = FirstIndex; // the first point is same as closing point
|
|
ptNode.Inside = TRUE;
|
|
ptNode.Used = FALSE;
|
|
if ((status = PtList.Add(ptNode)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Perform a simple partition sort on a list indexing into the vector list.
|
|
* The result is a sorted index array keyed on the vertex array Y1 coordinate.
|
|
* The index array is sorted in place and in ascending order.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* v is the vertex list.
|
|
* F, L - First and Last pointer in the index array.
|
|
*
|
|
* Created:
|
|
*
|
|
* 09/16/2000 asecchia
|
|
*
|
|
\**************************************************************************/
|
|
|
|
void PathSelfIntersectRemover::QuickSortEdges(
|
|
Edge *F,
|
|
Edge *L
|
|
)
|
|
{
|
|
if(F < L)
|
|
{
|
|
// Find the median position.
|
|
|
|
Edge median = *(F + (L-F)/2);
|
|
|
|
Edge *i = F;
|
|
Edge *j = L;
|
|
|
|
while(i<j)
|
|
{
|
|
// seek for elements in the wrong partition.
|
|
|
|
// compare edges:
|
|
while(CompareLine(this, i, &median) == -1) { i++; }
|
|
while(CompareLine(this, j, &median) == 1) { j--; }
|
|
|
|
if(i>=j) { break; }
|
|
|
|
// Swap.
|
|
|
|
Edge temp = *i;
|
|
*i = *j;
|
|
*j = temp;
|
|
|
|
// tie breaker - handle the case where *i == *j == *median, but
|
|
// i != j. Only possible with multiple copies of the same entry.
|
|
|
|
if(CompareLine(this, i, j) == 0) { i++; }
|
|
}
|
|
|
|
// Call recursively for the two sub-partitions. The partitions don't
|
|
// include position i because it is correctly positioned.
|
|
|
|
QuickSortEdges(F, i-1);
|
|
QuickSortEdges(i+1, L);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Insert numEdges edges joining points stored in array PathPts. First point
|
|
* has index firstIndex. There must be numEdges+1 points to create numEdges
|
|
* edges.
|
|
*
|
|
* NOTE: NumEdges CAN be negative! The function will then just return.
|
|
* This can potentially happen when called from AddPolygon().
|
|
*
|
|
* Arguments:
|
|
*
|
|
* firstIndex - index of first point in PathPts array.
|
|
* numEdges - number of edges to add.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus.
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
* 10/19/2000 asecchia rewrote it to support quicksort instead of insert sort.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus PathSelfIntersectRemover::InsertEdges(INT firstIndex, INT numEdges)
|
|
{
|
|
// Handle the empty polygon up front.
|
|
|
|
if(numEdges == 0)
|
|
{
|
|
return Ok;
|
|
}
|
|
|
|
// Alloc space for all the edges up front.
|
|
|
|
Edge *edges = EdgeList.AddMultiple(numEdges);
|
|
if (edges == NULL)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
// Create an edge with it's parent pointer.
|
|
|
|
Edge newEdge(this);
|
|
|
|
for (INT i = 0; i < numEdges; i++)
|
|
{
|
|
newEdge.Begin = i+firstIndex;
|
|
newEdge.End = i+1+firstIndex;
|
|
newEdge.SortBegin = i+firstIndex;
|
|
newEdge.SortEnd = i+1+firstIndex;
|
|
|
|
newEdge.Normalize();
|
|
|
|
newEdge.YCur = 0; // make debugging easier
|
|
|
|
newEdge.OrigBegin = newEdge.SortBegin;
|
|
newEdge.OrigEnd = newEdge.SortEnd;
|
|
|
|
//Edge insertion
|
|
|
|
edges[i] = newEdge;
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Check if two lines intersect.
|
|
* NOTE: Lines also intersect if an end point of one line is anywhere in the
|
|
* middle (between the end points) of the other line.
|
|
*
|
|
* This algorithm was stolen from the NT path code with some modifications
|
|
* based on an algorithm presented in Graphics Gems III.
|
|
* We can try to speed it up by comparing bounding boxes of the two lines,
|
|
* however, according to GG III, this may or may not speed up the
|
|
* calculations.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* ptrEdge1, ptrEdge2 - edges to intersect.
|
|
* [OUT] intersectPt - the intersection point if lines INTERSECT
|
|
* or have a COMMON_POINT,
|
|
*
|
|
* Return Value:
|
|
*
|
|
* DONOT_INTERS - lines don't intersect
|
|
* COMMON_POINT - they share a common end point
|
|
* INTERSECT - they intersect
|
|
* COLINEAR - they arecolinear.
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
PathSelfIntersectRemover::IntersectEdge(
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2,
|
|
GpPointF *intersectPt
|
|
)
|
|
{
|
|
GpPointF *pfpvBegin1; // Start point of first line segment
|
|
GpPointF *pfpvEnd1; // End point of the first line segment
|
|
GpPointF *pfpvBegin2; // Start point of second line segment
|
|
GpPointF *pfpvEnd2; // End point of the second line segment
|
|
|
|
GpPointF fpvVec1(0,0); // Direction of first line segment
|
|
GpPointF fpvVec2(0,0); // Direction of second line segment
|
|
GpPointF fpvVec3(0,0);
|
|
|
|
// Get the actual coordinates of the points.
|
|
|
|
pfpvBegin1 = &PathPts[ptrEdge1->Begin];
|
|
pfpvEnd1 = &PathPts[ptrEdge1->End];
|
|
fpvVec1 = SubtractPoint(*pfpvEnd1,*pfpvBegin1);
|
|
|
|
// Nothing intersects with an empty line.
|
|
|
|
if( REALABS(fpvVec1.X) < REAL_EPSILON &&
|
|
REALABS(fpvVec1.Y) < REAL_EPSILON )
|
|
{
|
|
return(DONOT_INTERS);
|
|
}
|
|
|
|
pfpvBegin2 = &PathPts[ptrEdge2->Begin];
|
|
pfpvEnd2 = &PathPts[ptrEdge2->End];
|
|
fpvVec2 = SubtractPoint(*pfpvEnd2,*pfpvBegin2);
|
|
|
|
// Nothing intersects with an empty line.
|
|
|
|
if( REALABS(fpvVec2.X) < REAL_EPSILON &&
|
|
REALABS(fpvVec2.Y) < REAL_EPSILON )
|
|
{
|
|
return(DONOT_INTERS);
|
|
}
|
|
|
|
fpvVec3 = SubtractPoint(*pfpvBegin2,*pfpvBegin1);
|
|
|
|
//
|
|
// A -direction 1
|
|
// D -direction 2
|
|
// C -vec3 ->
|
|
|
|
// The intersection is computed by:
|
|
//
|
|
// intersect = pptBegin1 + lambda * A
|
|
// intersect = pptBegin2 + beta * D
|
|
//
|
|
// Cx(-Dy) + Cy(Dx)
|
|
// lambda = ------------------------------
|
|
// (Ax)(-Dy) + (Ay)(Dx)
|
|
//
|
|
// Cx(Dy) + Cy(-Dx)
|
|
// beta = ---------------------
|
|
// (Ax)(Dy) + (-Ay)(Dx)
|
|
//
|
|
REAL efTerm1;
|
|
REAL efTerm2;
|
|
REAL efNum1;
|
|
REAL efDenom;
|
|
REAL efColinX;
|
|
REAL efColinY;
|
|
REAL efTemp;
|
|
|
|
// Cx (-Dy)
|
|
|
|
efNum1 = fpvVec3.X;
|
|
efColinX = efNum1;
|
|
efTerm2 = -fpvVec2.Y;
|
|
efNum1 *= efTerm2;
|
|
|
|
// Cy (Dx)
|
|
|
|
efTerm1 = fpvVec3.Y;
|
|
efColinY = efTerm1;
|
|
efTerm2 = fpvVec2.X;
|
|
efTerm1 *= efTerm2;
|
|
efNum1 += efTerm1;
|
|
|
|
// (Ax)(-Dy)
|
|
|
|
efDenom = fpvVec1.X;
|
|
efTerm2 = -fpvVec2.Y;
|
|
efDenom *= efTerm2;
|
|
|
|
// (Ay)(Dx)
|
|
|
|
efTerm1 = fpvVec1.Y;
|
|
efTerm2 = fpvVec2.X;
|
|
efTerm1 *= efTerm2;
|
|
efDenom += efTerm1;
|
|
|
|
if (CloseReal(efDenom, 0))
|
|
//if (efDenom == 0)
|
|
{
|
|
// they are colinear, but are they on the same line?
|
|
efTemp = -fpvVec1.Y;
|
|
efColinX *= efTemp;
|
|
efTemp = fpvVec1.X;
|
|
efColinY *= efTemp;
|
|
efColinX += efColinY;
|
|
// if (efColinX == 0)
|
|
if (CloseReal(efColinX, 0))
|
|
{
|
|
return(COLINEAR);
|
|
}
|
|
else
|
|
{
|
|
return(DONOT_INTERS);
|
|
}
|
|
}
|
|
|
|
// Check if they share a common end point
|
|
|
|
if (ptrEdge2->End == ptrEdge1->Begin ||
|
|
ptrEdge2->End == ptrEdge1->End ||
|
|
ptrEdge2->Begin == ptrEdge1->Begin ||
|
|
ptrEdge2->Begin == ptrEdge1->End)
|
|
{
|
|
return COMMON_POINT;
|
|
}
|
|
|
|
if (ClosePt(*pfpvBegin1, *pfpvBegin2))
|
|
{
|
|
UpdateDups(ptrEdge1->Begin, ptrEdge2->Begin);
|
|
return COMMON_POINT;
|
|
}
|
|
if (ClosePt(*pfpvBegin1, *pfpvEnd2))
|
|
{
|
|
UpdateDups(ptrEdge1->Begin, ptrEdge2->End);
|
|
return COMMON_POINT;
|
|
}
|
|
|
|
if (ClosePt(*pfpvEnd1, *pfpvEnd2))
|
|
{
|
|
UpdateDups(ptrEdge1->End, ptrEdge2->End);
|
|
return COMMON_POINT;
|
|
}
|
|
|
|
if (ClosePt(*pfpvEnd1, *pfpvBegin2))
|
|
{
|
|
UpdateDups(ptrEdge1->End, ptrEdge2->Begin);
|
|
return COMMON_POINT;
|
|
}
|
|
|
|
// lambda
|
|
efNum1 /= efDenom;
|
|
|
|
if (efNum1 < 0.0)
|
|
{
|
|
return(DONOT_INTERS);
|
|
}
|
|
else if (efNum1 > 1.0)
|
|
{
|
|
return (DONOT_INTERS);
|
|
}
|
|
else
|
|
{
|
|
REAL efNum2;
|
|
REAL efBetaPart1;
|
|
REAL efBetaPart2;
|
|
|
|
// find beta
|
|
efNum2 = -fpvVec3.X;
|
|
efBetaPart2 = fpvVec1.Y;
|
|
efNum2 *= efBetaPart2;
|
|
efBetaPart1 = -fpvVec3.Y;
|
|
efBetaPart2 = -fpvVec1.X;
|
|
efBetaPart1 *= efBetaPart2;
|
|
efNum2 += efBetaPart1;
|
|
efNum2 /= efDenom;
|
|
if (efNum2 < 0.0)
|
|
{
|
|
return(DONOT_INTERS);
|
|
}
|
|
else if (efNum2 > 1.0)
|
|
{
|
|
return (DONOT_INTERS);
|
|
}
|
|
}
|
|
|
|
// pptBegin1 + lambda * A
|
|
|
|
// Following should be nice to the REAL unit - multiply-add instructions
|
|
// can be used.
|
|
efTerm1 = fpvVec1.X * efNum1 + pfpvBegin1->X;
|
|
efTerm2 = fpvVec1.Y * efNum1 + pfpvBegin1->Y;
|
|
|
|
intersectPt->X = efTerm1;
|
|
intersectPt->Y = efTerm2;
|
|
|
|
// Because of errors, we may still end up with the intersection
|
|
// point as a common point:
|
|
if (IsCommonPt(ptrEdge1, ptrEdge2, intersectPt))
|
|
{
|
|
return COMMON_POINT;
|
|
}
|
|
|
|
return(INTERSECT);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Find all self intersections of the paths and break up the edges.
|
|
*
|
|
* This is considered 'Phase 1' of the algorithm.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* FALSE if out of memory.
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::FindIntersects()
|
|
{
|
|
// Initialize the array of active edges.
|
|
// Take the first edge from the edge array InactiveEdgeList and add it to
|
|
// the active edgs. Add all other edges, which start at the same x.
|
|
|
|
INT numRemoved = 0;
|
|
|
|
if (EdgeList.GetCount() <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
XCur = PathPts[EdgeList[InactiveEdgeList].SortBegin].X;
|
|
|
|
// "move" all edges starting at XCur to the active edge array
|
|
// PathPts in the active edge array will be sorted by y for the
|
|
// given XCur.
|
|
|
|
AddActiveForX(&InactiveEdgeList);
|
|
|
|
while (TRUE)
|
|
{
|
|
// Find all intersections among the current edges
|
|
|
|
if (!FindIntersectsForX())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// if there are no edges left, we have found all intersections
|
|
// we can stop even if active edges array is not empty, or maybe
|
|
// it must be empty then? TODO: Kasiak
|
|
|
|
if (InactiveEdgeList == LIST_END)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!ClosestActive(InactiveEdgeList))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Remove all edges which end before (or on) XCur
|
|
|
|
ClearActiveListInclusiveX();
|
|
|
|
AddActiveForX(&InactiveEdgeList);
|
|
}
|
|
|
|
// remove everything else from the ActiveEdgeList
|
|
|
|
XCur = FP_INF;
|
|
ClearActiveListInclusiveX();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* IsTIntersection returns TRUE if the intersection point intersectPt is
|
|
* the same as an end point of one of the edges ptrEdge1 and ptrEdge2. If
|
|
* it is, pfFirst will be TRUE if the first edge needs to be broken (intersectPt
|
|
* is an end point of ptrEdge2), FALSE if the second one needs to be broken
|
|
* up. intersectIndex contains the index of the end point which is the same
|
|
* as the intersection point.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* ptrEdge1,
|
|
* ptrEdge2 - the two edges to check for T-intersections
|
|
* intersectPt - the intersection point previously found for the edges.
|
|
* [OUT] splitFirst - TRUE if the first edge needs to be split. FALSE if
|
|
* the second edge.
|
|
* [OUT] intersectIndex - index of endpoint which is the same as intersectPt if it
|
|
* is a T-intersection.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE or FALSE
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::IsTIntersection(
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2,
|
|
GpPointF *intersectPt,
|
|
BOOL *splitFirst,
|
|
INT *intersectIndex
|
|
)
|
|
{
|
|
GpPointF &begin1 = PathPts[ptrEdge1->SortBegin];
|
|
GpPointF &begin2 = PathPts[ptrEdge2->SortBegin];
|
|
GpPointF &end1 = PathPts[ptrEdge1->SortEnd];
|
|
GpPointF &end2 = PathPts[ptrEdge2->SortEnd];
|
|
|
|
if (ClosePt(end1, *intersectPt))
|
|
{
|
|
// only ptrEdge2 needs to be broken up
|
|
*splitFirst = FALSE;
|
|
*intersectIndex = ptrEdge1->SortEnd;
|
|
return TRUE;
|
|
}
|
|
else if(ClosePt(end2, *intersectPt))
|
|
{
|
|
// only ptrEdge1
|
|
*splitFirst = TRUE;
|
|
*intersectIndex = ptrEdge2->SortEnd;
|
|
return TRUE;
|
|
}
|
|
else if(ClosePt(begin1, *intersectPt))
|
|
{
|
|
// only ptrEdge2
|
|
*splitFirst = FALSE;
|
|
*intersectIndex = ptrEdge1->SortBegin;
|
|
return TRUE;
|
|
}
|
|
else if(ClosePt(begin2, *intersectPt))
|
|
{
|
|
// only ptrEdge1
|
|
*splitFirst = TRUE;
|
|
*intersectIndex = ptrEdge2->SortBegin;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Returns TRUE if the two lines ptrEdge1 and ptrEdge2 share a common
|
|
* end point. If they do, pptInter will contain this point.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL
|
|
PathSelfIntersectRemover::IsCommonPt(
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2,
|
|
GpPointF *commonPt
|
|
)
|
|
{
|
|
GpPointF &begin1 = PathPts[ptrEdge1->SortBegin];
|
|
GpPointF &begin2 = PathPts[ptrEdge2->SortBegin];
|
|
GpPointF &end1 = PathPts[ptrEdge1->SortEnd];
|
|
GpPointF &end2 = PathPts[ptrEdge2->SortEnd];
|
|
|
|
if (ClosePt(end1, *commonPt) && ClosePt(end2, *commonPt))
|
|
{
|
|
UpdateDups(ptrEdge1->End, ptrEdge2->End);
|
|
return TRUE;
|
|
}
|
|
else if(ClosePt(end1, *commonPt) && ClosePt(begin2, *commonPt))
|
|
{
|
|
UpdateDups(ptrEdge1->End, ptrEdge2->Begin);
|
|
return TRUE;
|
|
}
|
|
else if(ClosePt(begin1, *commonPt) && ClosePt(begin2, *commonPt))
|
|
{
|
|
UpdateDups(ptrEdge1->Begin, ptrEdge2->Begin);
|
|
return TRUE;
|
|
}
|
|
else if(ClosePt(begin1, *commonPt) && ClosePt(end2, *commonPt))
|
|
{
|
|
UpdateDups(ptrEdge1->Begin, ptrEdge2->End);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Returns TRUE if point with index inew is already in the "list" of dups
|
|
* for point with index loop.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::IsLinked(INT loop, INT inew)
|
|
{
|
|
PointListNode *ptNode;
|
|
|
|
ptNode = &PtList[loop];
|
|
BOOL isLinked = FALSE;
|
|
INT i = ptNode->Dup;
|
|
INT prev_i = -1; // Added to prevent an infinite loop.
|
|
|
|
while(!isLinked && i != -1 && i != prev_i && i != loop)
|
|
{
|
|
if(i == inew)
|
|
{
|
|
isLinked = TRUE;
|
|
break;
|
|
}
|
|
|
|
ptNode = &PtList[i];
|
|
prev_i = i;
|
|
i = ptNode->Dup;
|
|
}
|
|
|
|
return isLinked;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Joins the list of duplicate points for points pt1 and pt2.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
VOID PathSelfIntersectRemover::UpdateDups(INT pt1, INT pt2)
|
|
{
|
|
PointListNode *ptNode1;
|
|
PointListNode *ptNode2;
|
|
|
|
if (pt1 == pt2)
|
|
{
|
|
WARNING(("NOT REACHED"));
|
|
return;
|
|
}
|
|
|
|
ptNode1 = &PtList[pt1];
|
|
ptNode2 = &PtList[pt2];
|
|
|
|
if ((ptNode1->Dup == -1) && (ptNode2->Dup == -1))
|
|
{
|
|
ptNode1->Dup = pt2;
|
|
ptNode2->Dup = pt1;
|
|
return;
|
|
}
|
|
|
|
if ((ptNode1->Dup == -1) && (ptNode2->Dup != -1))
|
|
{
|
|
ptNode1->Dup = ptNode2->Dup;
|
|
ptNode2->Dup = pt1;
|
|
return;
|
|
}
|
|
|
|
if ((ptNode1->Dup != -1) && (ptNode2->Dup == -1))
|
|
{
|
|
ptNode2->Dup = ptNode1->Dup;
|
|
ptNode1->Dup = pt2;
|
|
return;
|
|
}
|
|
|
|
if ((ptNode1->Dup != -1) && (ptNode2->Dup != -1))
|
|
{
|
|
if (!IsLinked(pt1, pt2))
|
|
{
|
|
INT dupTemp;
|
|
|
|
dupTemp = ptNode2->Dup;
|
|
ptNode2->Dup = ptNode1->Dup;
|
|
ptNode1->Dup = dupTemp;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Delete edges from the active edge table; Indices of edges to delete
|
|
* are stored in EdgesToDelete1..3. Deletes the highest index edge first.
|
|
* Returns NULL if fails due to out of memory error.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::DeleteEdges()
|
|
{
|
|
INT minIndex;
|
|
INT midIndex;
|
|
INT maxIndex;
|
|
|
|
if (EdgesToDelete1 > EdgesToDelete2)
|
|
{
|
|
if (EdgesToDelete1 > EdgesToDelete3)
|
|
{
|
|
minIndex = EdgesToDelete1;
|
|
if (EdgesToDelete2 > EdgesToDelete3)
|
|
{
|
|
midIndex = EdgesToDelete2;
|
|
maxIndex = EdgesToDelete3;
|
|
}
|
|
else
|
|
{
|
|
midIndex = EdgesToDelete3;
|
|
maxIndex = EdgesToDelete2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
minIndex = EdgesToDelete3;
|
|
midIndex = EdgesToDelete1;
|
|
maxIndex = EdgesToDelete2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (EdgesToDelete2 > EdgesToDelete3)
|
|
{
|
|
minIndex = EdgesToDelete2;
|
|
if (EdgesToDelete1 > EdgesToDelete3)
|
|
{
|
|
midIndex = EdgesToDelete1;
|
|
maxIndex = EdgesToDelete3;
|
|
}
|
|
else
|
|
{
|
|
midIndex = EdgesToDelete3;
|
|
maxIndex = EdgesToDelete1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
minIndex = EdgesToDelete3;
|
|
midIndex = EdgesToDelete2;
|
|
maxIndex = EdgesToDelete1;
|
|
}
|
|
}
|
|
|
|
if (minIndex == -1)
|
|
{
|
|
return TRUE;
|
|
}
|
|
// delete the first one
|
|
if (!DeleteEdgeFromList(&ActiveEdgeList, minIndex))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (midIndex == -1)
|
|
{
|
|
return TRUE;
|
|
}
|
|
// delete the second one
|
|
if (!DeleteEdgeFromList(&ActiveEdgeList, midIndex))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (maxIndex == -1)
|
|
{
|
|
return TRUE;
|
|
}
|
|
// delete the third one
|
|
if (!DeleteEdgeFromList(&ActiveEdgeList, maxIndex))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Edge with index index into the array of Active edges needs to be deleted.
|
|
* Store its index for deletion.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PathSelfIntersectRemover::MarkToDelete(INT index)
|
|
{
|
|
ASSERT((EdgesToDelete1 == -1) ||
|
|
(EdgesToDelete2 == -1) ||
|
|
(EdgesToDelete3 == -1));
|
|
|
|
if (EdgesToDelete1 == -1 )
|
|
{
|
|
EdgesToDelete1 = index;
|
|
return;
|
|
}
|
|
|
|
if (EdgesToDelete2 == -1 )
|
|
{
|
|
EdgesToDelete2 = index;
|
|
return;
|
|
}
|
|
|
|
if (EdgesToDelete3 == -1 )
|
|
{
|
|
EdgesToDelete3 = index;
|
|
return;
|
|
}
|
|
return; // error
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Edge ptrEdge needs to be added to the array with Active edges. Make a
|
|
* copy of the edge and store it, so that it can be later added.
|
|
* We have to add new edges after the edges marked for deletion are deleted.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PathSelfIntersectRemover::MarkToAdd(Edge *ptrEdge)
|
|
{
|
|
ASSERT(!(FlgAdd1 && FlgAdd2 && FlgAdd3));
|
|
|
|
if (!FlgAdd1)
|
|
{
|
|
AddToActive1 = *ptrEdge;
|
|
FlgAdd1 = TRUE;
|
|
return;
|
|
}
|
|
|
|
if (!FlgAdd2)
|
|
{
|
|
AddToActive2 = *ptrEdge;
|
|
FlgAdd2 = TRUE;
|
|
return;
|
|
}
|
|
|
|
if (!FlgAdd3)
|
|
{
|
|
AddToActive3 = *ptrEdge;
|
|
FlgAdd3 = TRUE;
|
|
return;
|
|
}
|
|
return; // this the error condition, which should never happen.
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Add new edges to the active edge table. The edges are stored in
|
|
* AddToActive1..3. flag FlgAdd1..3 specify if the given edge needs to
|
|
* be added or not. Returns if fails due to out of memory.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::AddNewEdges()
|
|
{
|
|
if (FlgAdd1)
|
|
{
|
|
AddToActive1.SortBegin = AddToActive1.Begin;
|
|
AddToActive1.SortEnd = AddToActive1.End;
|
|
AddToActive1.Normalize();
|
|
AddToActive1.YCur = PathPts[AddToActive1.SortBegin].Y;
|
|
|
|
AddToActive1.Next = LIST_END;
|
|
|
|
//Edge Insertion
|
|
|
|
if(Ok != EdgeList.Add(AddToActive1))
|
|
{
|
|
return FALSE; // out of memory.
|
|
}
|
|
|
|
InsertEdgeIntoList(
|
|
&ActiveEdgeList,
|
|
EdgeList.GetCount()-1,
|
|
CompareYCurLine
|
|
);
|
|
}
|
|
|
|
if (FlgAdd2)
|
|
{
|
|
AddToActive2.SortBegin = AddToActive2.Begin;
|
|
AddToActive2.SortEnd = AddToActive2.End;
|
|
AddToActive2.Normalize();
|
|
AddToActive2.YCur = PathPts[AddToActive2.SortBegin].Y;
|
|
|
|
AddToActive2.Next = LIST_END;
|
|
|
|
//Edge Insertion
|
|
|
|
if(Ok != EdgeList.Add(AddToActive2))
|
|
{
|
|
return FALSE; // out of memory.
|
|
}
|
|
|
|
InsertEdgeIntoList(
|
|
&ActiveEdgeList,
|
|
EdgeList.GetCount()-1,
|
|
CompareYCurLine
|
|
);
|
|
}
|
|
|
|
if (FlgAdd3)
|
|
{
|
|
AddToActive3.SortBegin = AddToActive3.Begin;
|
|
AddToActive3.SortEnd = AddToActive3.End;
|
|
AddToActive3.Normalize();
|
|
AddToActive3.YCur = PathPts[AddToActive3.SortBegin].Y;
|
|
|
|
AddToActive3.Next = LIST_END;
|
|
|
|
//Edge Insertion
|
|
|
|
if(Ok != EdgeList.Add(AddToActive3))
|
|
{
|
|
return FALSE; // out of memory.
|
|
}
|
|
|
|
InsertEdgeIntoList(
|
|
&ActiveEdgeList,
|
|
EdgeList.GetCount()-1,
|
|
CompareYCurLine
|
|
);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Find all intersections for the current X value. Intersection points
|
|
* will be inserted into the Edges array and information about their
|
|
* order into PtList. Returns FALSE on out of memory.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::FindIntersectsForX()
|
|
{
|
|
Edge *ptrEdge1 = NULL;
|
|
Edge *ptrEdge2 = NULL;
|
|
Edge *ptrEdge3 = NULL;
|
|
|
|
// If we find an intersection, we will be breaking up edges - need two more
|
|
// line variables for the new edges.
|
|
Edge newEdge1(this);
|
|
Edge newEdge2(this);
|
|
|
|
GpPointF intersectPt(0,0);
|
|
INT result = DONOT_INTERS;
|
|
BOOL breakFirst = TRUE;
|
|
INT dup1 = -1;
|
|
INT dup2 = -1;
|
|
INT ptIndex1 = -1;
|
|
INT ptIndex2 = -1;
|
|
PointListNode *ptNode = NULL;
|
|
INT edgeIndex1, edgeIndex2, edgeIndexOld;
|
|
REAL yCur2;
|
|
BOOL deleteEdge1;
|
|
BOOL deleteEdge2;
|
|
BOOL edge1Deleted;
|
|
BOOL edge2Deleted;
|
|
|
|
// Go through all active edges but consider only consecutive pairs of egdes
|
|
// because they are sorted in y. There is no need to check every edge with
|
|
// every other edge in general case. However, when we have multiple edges
|
|
// with the same current y value and end points, we have to consider all of
|
|
// them.
|
|
|
|
edgeIndexOld = LIST_END;
|
|
edgeIndex1 = ActiveEdgeList;
|
|
|
|
if(edgeIndex1 == LIST_END)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
edgeIndex2 = EdgeList[ActiveEdgeList].Next;
|
|
|
|
while (edgeIndex1 != LIST_END && edgeIndex2 != LIST_END)
|
|
{
|
|
edge1Deleted = FALSE;
|
|
edge2Deleted = FALSE;
|
|
|
|
// Get the next two edges
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
yCur2 = ptrEdge2->YCur;
|
|
|
|
FlgAdd1 = FALSE;
|
|
FlgAdd2 = FALSE;
|
|
FlgAdd3 = FALSE;
|
|
|
|
EdgesToDelete1 = -1;
|
|
EdgesToDelete2 = -1;
|
|
EdgesToDelete3 = -1;
|
|
|
|
// Do they intersect?
|
|
result = IntersectEdge(ptrEdge1, ptrEdge2, &intersectPt);
|
|
if (result == INTERSECT)
|
|
{
|
|
// check if both edges need to be broken up, we may have
|
|
// a "T" intersection
|
|
if (IsTIntersection(ptrEdge1, ptrEdge2, &intersectPt,
|
|
&breakFirst, &dup1))
|
|
{
|
|
// only one edge needs to be broken up
|
|
// The index of the new point will be:
|
|
ptIndex1 = PathPts.GetCount();
|
|
// Update the dup field
|
|
// we have idup from IsTIntersection - it returned the index of
|
|
// the end point which is the same as the point of intersection
|
|
// It is a duplicate of the new point
|
|
ptNode = &PtList[dup1];
|
|
if (ptNode->Dup == -1)
|
|
{
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
else
|
|
{
|
|
dup1 = ptNode->Dup;
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
if (breakFirst)
|
|
{
|
|
// we need to break up the first edge
|
|
if (!BreakEdge(ptrEdge1, &intersectPt, &newEdge2, dup1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
|
|
// Check if the left side of ptIntersect shouldn't be
|
|
// removed. This is the case when
|
|
// ptrEdge1->yCur == intersectPt.y && intersectPt.x == xCur;
|
|
if (ptrEdge1->YCur >= intersectPt.Y && intersectPt.X <= XCur)
|
|
{
|
|
MarkToDelete(edgeIndex1);
|
|
edge1Deleted = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we need to break up the first edge
|
|
if (!BreakEdge(ptrEdge2, &intersectPt, &newEdge1, dup1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
// Check if the left side of ptIntersect shouldn't be
|
|
// removed. This is the case when
|
|
// ptrEdge2->yCur == intersectPt.y && intersectPt.x == xCur;
|
|
if (ptrEdge2->YCur >= intersectPt.Y && intersectPt.X <= XCur)
|
|
{
|
|
MarkToDelete(edgeIndex2);
|
|
edge2Deleted = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// both need to be broken up
|
|
// We need to add two new points. They are identical and
|
|
// will be duplicates. Let's get thei indecies.
|
|
ptIndex1 = PathPts.GetCount();
|
|
ptIndex2 = ptIndex1+1;
|
|
if (!BreakEdge(ptrEdge1, &intersectPt, &newEdge2, ptIndex2))
|
|
return FALSE;
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
if (!BreakEdge(ptrEdge2, &intersectPt, &newEdge1, ptIndex1))
|
|
return FALSE;
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
// Let's delete what we will not need any more - the left hand
|
|
// sides of the old edges only if the intersection point is on
|
|
// the scanline
|
|
deleteEdge2 = (ptrEdge2->YCur >= intersectPt.Y &&
|
|
intersectPt.X <= XCur);
|
|
deleteEdge1 = (ptrEdge1->YCur >= intersectPt.Y &&
|
|
intersectPt.X <= XCur);
|
|
if (deleteEdge2)
|
|
{
|
|
MarkToDelete(edgeIndex2);
|
|
edge2Deleted = TRUE;
|
|
}
|
|
|
|
if (deleteEdge1)
|
|
{
|
|
MarkToDelete(edgeIndex1);
|
|
edge1Deleted = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else if (result == COLINEAR)
|
|
{
|
|
BOOL three;
|
|
GpPointF intersectPt2(0,0);
|
|
BOOL breakSecond;
|
|
// The line segments must overlap or both are vertical...
|
|
// Find out if they overlap and if this is the case,
|
|
// Let's pick the begin point with the greater x.
|
|
// Overlap will check if the two edges overlap and return
|
|
// a point of intersection as well as information of which edge
|
|
// to break up.
|
|
if (Overlap(ptrEdge1, ptrEdge2, &intersectPt, &intersectPt2, &breakFirst,
|
|
&breakSecond, &three, &dup1, &dup2))
|
|
{
|
|
if (breakFirst)
|
|
{
|
|
if (!three)
|
|
{
|
|
// As before, get the index, so we can set up
|
|
// the duplicates
|
|
ptIndex1 = PathPts.GetCount();
|
|
// Update the dup field
|
|
ptNode = &PtList[dup1];
|
|
if (ptNode->Dup == -1)
|
|
{
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
else
|
|
{
|
|
dup1 = ptNode->Dup;
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
if (!BreakEdge(ptrEdge1, &intersectPt, &newEdge2, dup1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
}
|
|
else
|
|
{
|
|
ptIndex1 = PathPts.GetCount();
|
|
ptIndex2 = ptIndex1+1;
|
|
// we need to break this edge into 3 pieces
|
|
|
|
// Update the dup field
|
|
ptNode = &PtList[dup1];
|
|
if (ptNode->Dup == -1)
|
|
{
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
else
|
|
{
|
|
dup1 = ptNode->Dup;
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
|
|
// Update the dup field
|
|
ptNode = &PtList[dup2];
|
|
if (ptNode->Dup == -1)
|
|
{
|
|
ptNode->Dup = ptIndex2;
|
|
}
|
|
else
|
|
{
|
|
dup2 = ptNode->Dup;
|
|
ptNode->Dup = ptIndex2;
|
|
}
|
|
if (!BreakEdgeIn3(ptrEdge1, &intersectPt, &intersectPt2,
|
|
&newEdge1, &newEdge2, dup1, dup2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
}
|
|
deleteEdge1 = (ptrEdge1->YCur >= intersectPt.Y &&
|
|
intersectPt.X <= XCur);
|
|
if (deleteEdge1)
|
|
{
|
|
MarkToDelete(edgeIndex1);
|
|
edge1Deleted = TRUE;
|
|
}
|
|
}
|
|
if (breakSecond)
|
|
{
|
|
if (!three)
|
|
{
|
|
// break ptrEdge2
|
|
// Update the dup field
|
|
ptIndex1 = PathPts.GetCount();
|
|
ptNode = &PtList[dup2];
|
|
if (ptNode->Dup == -1)
|
|
{
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
else
|
|
{
|
|
dup2 = ptNode->Dup;
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
if (!BreakEdge(ptrEdge2, &intersectPt2, &newEdge1, dup2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
deleteEdge2 = ptrEdge2->YCur >= intersectPt2.Y &&
|
|
intersectPt2.X <= XCur;
|
|
|
|
if (deleteEdge2)
|
|
{
|
|
MarkToDelete(edgeIndex2);
|
|
edge2Deleted = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ptIndex1 = PathPts.GetCount();
|
|
ptIndex2 = ptIndex1+1;
|
|
// we need to break this edge into 3 pieces
|
|
|
|
// Update the dup field
|
|
ptNode = &PtList[dup1];
|
|
if (ptNode->Dup == -1)
|
|
{
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
else
|
|
{
|
|
dup1 = ptNode->Dup;
|
|
ptNode->Dup = ptIndex1;
|
|
}
|
|
|
|
// Update the dup field
|
|
ptNode = &PtList[dup2];
|
|
if (ptNode->Dup == -1)
|
|
{
|
|
ptNode->Dup = ptIndex2;
|
|
}
|
|
else
|
|
{
|
|
dup2 = ptNode->Dup;
|
|
ptNode->Dup = ptIndex2;
|
|
}
|
|
if (!BreakEdgeIn3(ptrEdge2, &intersectPt, &intersectPt2,
|
|
&newEdge1, &newEdge2, dup1, dup2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// BreakEdge can cause a realloc, thus invalidating
|
|
// ptrEdge1 and ptrEdge2
|
|
|
|
ptrEdge1 = &EdgeList[edgeIndex1];
|
|
ptrEdge2 = &EdgeList[edgeIndex2];
|
|
|
|
deleteEdge2 = ptrEdge2->YCur >= intersectPt.Y &&
|
|
intersectPt.X <= XCur;
|
|
|
|
if (deleteEdge2)
|
|
{
|
|
MarkToDelete(edgeIndex2);
|
|
edge2Deleted = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool modifiedList = false;
|
|
|
|
// If we're deleting any edges, we've modified the list.
|
|
|
|
if((EdgesToDelete1 != -1) ||
|
|
(EdgesToDelete2 != -1) ||
|
|
(EdgesToDelete3 != -1))
|
|
{
|
|
modifiedList = true;
|
|
}
|
|
|
|
if (!DeleteEdges())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// If we're adding any edges, we've modified the list.
|
|
|
|
if(FlgAdd1 || FlgAdd2 || FlgAdd3)
|
|
{
|
|
modifiedList = true;
|
|
}
|
|
|
|
if (!AddNewEdges())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Note: We check all vertically adjacent pairs of edges. Because
|
|
// the AET is sorted vertically and we're only looking for the
|
|
// intersection with the x coordinate closest to XCur, this is
|
|
// sufficient (and O(n) instead of O(n^2)).
|
|
|
|
if(modifiedList)
|
|
{
|
|
// back up to a stable position if we invalidate our list.
|
|
// this makes assumptions on where in the list we add or
|
|
// delete elements based on the sort order for the ActiveEdgeList.
|
|
|
|
edgeIndex1 = edgeIndexOld;
|
|
|
|
if(edgeIndexOld == LIST_END)
|
|
{
|
|
edgeIndex1 = ActiveEdgeList;
|
|
}
|
|
|
|
if(edgeIndex1 != LIST_END)
|
|
{
|
|
edgeIndex2 = EdgeList[edgeIndex1].Next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
edgeIndexOld = edgeIndex1;
|
|
edgeIndex2 = EdgeList[edgeIndex2].Next;
|
|
edgeIndex1 = EdgeList[edgeIndex1].Next;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Returns TRUE if two collinear edges ptrEdge1 and ptrEdge2 overlap.
|
|
* There are 4 ways in which edges can overlap and depending on the
|
|
* case, either none, one or both edges need to be broken up. In some
|
|
* cases one edge may need to broken into 3 pieces.
|
|
* Return values:
|
|
* split1 - set to TRUE if ptrEdge1 needs to be split
|
|
* split2 - set to TRUE if ptrEdge2 needs to be split
|
|
* split3 - set to TRUE if an edge needs to be broken into 3 pieces
|
|
* intersect1 - intersection point (where edge needs to be split)
|
|
* intersect2 - second point (if the edge needs to be broken into 3
|
|
* pieces or for the second edge if both edges need to
|
|
* be broken up)
|
|
* dupIndex1 - index of the duplicate point to intersect1,
|
|
* dupIndex2 - index of the duplicate point to intersect2,
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::Overlap(
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2,
|
|
GpPointF *intersect1,
|
|
GpPointF *intersect2,
|
|
BOOL *split1,
|
|
BOOL *split2,
|
|
BOOL *split3,
|
|
INT *dupIndex1,
|
|
INT *dupIndex2
|
|
)
|
|
{
|
|
// We are assuming that the lines are colinear and they both belong
|
|
// to the active edges table which means that their x ranges overlap
|
|
// We need to check if they are vertical and if yes, find out if their
|
|
// y ranges overlap. If they are not vertical, they must overlap or
|
|
// they share a common end point.
|
|
|
|
*split3 = FALSE;
|
|
|
|
GpPointF &begin1 = PathPts[ptrEdge1->SortBegin];
|
|
GpPointF &begin2 = PathPts[ptrEdge2->SortBegin];
|
|
GpPointF &end1 = PathPts[ptrEdge1->SortEnd];
|
|
GpPointF &end2 = PathPts[ptrEdge2->SortEnd];
|
|
|
|
// Calculate bounding box for each edge:
|
|
GpPointF min1(
|
|
min(begin1.X, end1.X),
|
|
min(begin1.Y, end1.Y));
|
|
GpPointF max1(
|
|
max(begin1.X, end1.X),
|
|
max(begin1.Y, end1.Y));
|
|
GpPointF min2(
|
|
min(begin2.X, end2.X),
|
|
min(begin2.Y, end2.Y));
|
|
GpPointF max2(
|
|
max(begin2.X, end2.X),
|
|
max(begin2.Y, end2.Y));
|
|
|
|
// Abort if the bounding box of either edge is empty.
|
|
|
|
if(IsClosePointF(min1, max1) ||
|
|
IsClosePointF(min2, max2) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// If the edges are not vertical, they must overlap, because both of them
|
|
// are in the active edge array. We only need to chose a point, which will
|
|
// be used as an intersection point.
|
|
if (!ptrEdge1->IsVertical())
|
|
{
|
|
// Let's check first if they share a common end point
|
|
|
|
// if they share just a common point, return FALSE
|
|
if (CloseReal(min1.X,max2.X))
|
|
{
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortEnd)
|
|
{
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortEnd);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (CloseReal(min2.X,max1.X))
|
|
{
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortBegin)
|
|
{
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortBegin);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// the edges must overlap, we need to break both of them
|
|
|
|
// There are 4 different ways in which edges may overlap
|
|
|
|
// case4: edges are identical
|
|
// ----------------
|
|
// ----------------
|
|
// No edges need to be broken up, but do we need to update some
|
|
// dup values?
|
|
if (CloseReal(max1.X, max2.X) && CloseReal(min1.X, min2.X))
|
|
{
|
|
// for now, we will return FALSE
|
|
// I think, that it should be OK.
|
|
// TODO: - Review
|
|
// !!! Is this still ok? - t-wehunt
|
|
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortBegin)
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortBegin);
|
|
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortEnd)
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortEnd);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// case2: the edges overlap and share one end point
|
|
// --------------
|
|
// ----------------------
|
|
// The longer edge needs to be broken into 2 pieces
|
|
if (CloseReal(min1.X, min2.X) && max1.X < max2.X)
|
|
{
|
|
// --------------
|
|
// ----------------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortBegin)
|
|
{
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortBegin);
|
|
}
|
|
*split1 = FALSE;
|
|
*split2 = TRUE;
|
|
*dupIndex2 = ptrEdge1->SortEnd;
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if (CloseReal(min1.X, min2.X) && max1.X > max2.X)
|
|
{
|
|
// ----------------------
|
|
// --------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortBegin)
|
|
{
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortBegin);
|
|
}
|
|
*split1 = TRUE;
|
|
*split2 = FALSE;
|
|
*dupIndex1 = ptrEdge2->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
return TRUE;
|
|
}
|
|
else if (CloseReal(max1.X, max2.X) && min1.X > min2.X)
|
|
{
|
|
// ----------------------
|
|
// --------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortEnd)
|
|
{
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortEnd);
|
|
}
|
|
*split1 = FALSE;
|
|
*split2 = TRUE;
|
|
*dupIndex2 = ptrEdge1->SortBegin;
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if (CloseReal(max1.X, max2.X) && min1.X < min2.X)
|
|
{
|
|
// --------------
|
|
// ----------------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortEnd)
|
|
{
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortEnd);
|
|
}
|
|
*split1 = TRUE;
|
|
*split2 = FALSE;
|
|
*dupIndex1 = ptrEdge2->SortBegin;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
return TRUE;
|
|
}
|
|
|
|
// case1: one is "inside" of another
|
|
// ---------
|
|
// -----------------
|
|
// In this case, the longer edge has to be broken into 3 pieces
|
|
|
|
if (min1.X < min2.X && max1.X > max2.X)
|
|
{
|
|
// intersection points are the end points of the second edge
|
|
// we are breaking up the first edge
|
|
*split1 = TRUE;
|
|
*split2 = FALSE;
|
|
*split3 = TRUE;
|
|
*dupIndex1 = ptrEdge2->SortBegin;
|
|
*dupIndex2 = ptrEdge2->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if (min1.X > min2.X && max1.X < max2.X)
|
|
{
|
|
// intersection points are the end points of the first edge
|
|
// we are breaking up the second edge
|
|
*split1 = FALSE;
|
|
*split2 = TRUE;
|
|
*split3 = TRUE;
|
|
*dupIndex1 = ptrEdge1->SortBegin;
|
|
*dupIndex2 = ptrEdge1->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
|
|
// case3: edges overlap
|
|
// ---------------
|
|
// -----------------
|
|
// Each edge has to be broken up INT 2 pieces
|
|
else if (max1.X < max2.X && min1.X < min2.X)
|
|
{
|
|
*split1 = TRUE;
|
|
*split2 = TRUE;
|
|
*dupIndex1 = ptrEdge2->SortBegin;
|
|
*dupIndex2 = ptrEdge1->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if (max2.X < max1.X && min2.X < min1.X)
|
|
{
|
|
// -----------------
|
|
// ---------------
|
|
*split1 = TRUE;
|
|
*split2 = TRUE;
|
|
*dupIndex1 = ptrEdge2->SortEnd;
|
|
*dupIndex2 = ptrEdge1->SortBegin;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// The edges are vertical.
|
|
// We have to test for the same cases using y intervals
|
|
|
|
// if they share just a common point, return FALSE
|
|
if (CloseReal(min1.Y, max2.Y))
|
|
{
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortEnd)
|
|
{
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortEnd);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (CloseReal(min2.Y, max1.Y))
|
|
{
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortBegin)
|
|
{
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortBegin);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// These edges may not overlap at all
|
|
if (min1.Y > max2.Y)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (min2.Y > max1.Y)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// case4: edges are identical
|
|
// ----------------
|
|
// ----------------
|
|
// No edges need to be broken up, but do we need to update some
|
|
// dup values?
|
|
if (CloseReal(max1.Y, max2.Y) && CloseReal(min1.Y, min2.Y))
|
|
{
|
|
// for now, we will return FALSE
|
|
// I think, that it should be OK.
|
|
// TODO: - Review
|
|
// !!! IS this still ok? - t-wehunt
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortBegin)
|
|
{
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortBegin);
|
|
}
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortEnd)
|
|
{
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortEnd);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// case2: the edges overlap and share one end point
|
|
// --------------
|
|
// ----------------------
|
|
// The longer edge needs to be broken into 2 pieces
|
|
if (CloseReal(min1.Y, min2.Y) && max1.Y < max2.Y)
|
|
{
|
|
// --------------
|
|
// ----------------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortBegin)
|
|
{
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortBegin);
|
|
}
|
|
*split1 = FALSE;
|
|
*split2 = TRUE;
|
|
*dupIndex2 = ptrEdge1->SortEnd;
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if (CloseReal(min1.Y, min2.Y) && max1.Y > max2.Y)
|
|
{
|
|
// ----------------------
|
|
// --------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortBegin != ptrEdge2->SortBegin)
|
|
{
|
|
UpdateDups(ptrEdge1->SortBegin, ptrEdge2->SortBegin);
|
|
}
|
|
*split1 = TRUE;
|
|
*split2 = FALSE;
|
|
*dupIndex1 = ptrEdge2->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
return TRUE;
|
|
}
|
|
else if (CloseReal(max1.Y, max2.Y) && min1.Y > min2.Y)
|
|
{
|
|
// ----------------------
|
|
// --------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortEnd)
|
|
{
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortEnd);
|
|
}
|
|
*split1 = FALSE;
|
|
*split2 = TRUE;
|
|
*dupIndex2 = ptrEdge1->SortBegin;
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if (CloseReal(max1.Y, max2.Y) && min1.Y < min2.Y)
|
|
{
|
|
// --------------
|
|
// ----------------------
|
|
// We have to update the dups of the shared point
|
|
|
|
if (ptrEdge1->SortEnd != ptrEdge2->SortEnd)
|
|
{
|
|
UpdateDups(ptrEdge1->SortEnd, ptrEdge2->SortEnd);
|
|
}
|
|
*split1 = TRUE;
|
|
*split2 = FALSE;
|
|
*dupIndex1 = ptrEdge2->SortBegin;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
return TRUE;
|
|
}
|
|
|
|
// case1: one is "inside" of another
|
|
// ---------
|
|
// -----------------
|
|
// In this case, the longer edge has to be broken into 3 pieces
|
|
|
|
if (min1.Y < min2.Y && max1.Y > max2.Y)
|
|
{
|
|
// intersection points are the end points of the second edge
|
|
// we are breaking up the first edge
|
|
*split1 = TRUE;
|
|
*split2 = FALSE;
|
|
*split3 = TRUE;
|
|
*dupIndex1 = ptrEdge2->SortBegin;
|
|
*dupIndex2 = ptrEdge2->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if(min1.Y > min2.Y && max1.Y < max2.Y)
|
|
{
|
|
// intersection points are the end points of the first edge
|
|
// we are breaking up the second edge
|
|
*split1 = FALSE;
|
|
*split2 = TRUE;
|
|
*split3 = TRUE;
|
|
*dupIndex1 = ptrEdge1->SortBegin;
|
|
*dupIndex2 = ptrEdge1->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// case3: edges overlap
|
|
// ---------------
|
|
// -----------------
|
|
// Each edge has to be broken up INT 2 pieces
|
|
else if (max1.Y < max2.Y && min1.Y < min2.Y)
|
|
{
|
|
*split1 = TRUE;
|
|
*split2 = TRUE;
|
|
*dupIndex1 = ptrEdge2->SortBegin;
|
|
*dupIndex2 = ptrEdge1->SortEnd;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
else if (max2.Y < max1.Y && min2.Y < min1.Y)
|
|
{
|
|
// -----------------
|
|
// ---------------
|
|
*split1 = TRUE;
|
|
*split2 = TRUE;
|
|
*dupIndex1 = ptrEdge2->SortEnd;
|
|
*dupIndex2 = ptrEdge1->SortBegin;
|
|
*intersect1 = PathPts[*dupIndex1];
|
|
*intersect2 = PathPts[*dupIndex2];
|
|
return TRUE;
|
|
}
|
|
|
|
WARNING(("Couldn't resolve overlapping edges"));
|
|
return FALSE; // Shouldn't get here
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Break edge in 3 pieces.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::BreakEdgeIn3(
|
|
Edge *ptrEdge,
|
|
GpPointF *ptrPt1,
|
|
GpPointF *ptrPt2,
|
|
Edge *ptrNew1,
|
|
Edge *ptrNew2,
|
|
INT dupIndex1,
|
|
INT dupIndex2
|
|
)
|
|
{
|
|
INT iptt1, ipto1; // Indecies to the point arrays
|
|
INT iptt2, ipto2;
|
|
PointListNode ptord1; // Pointers to the linked list and point information
|
|
PointListNode ptord2;
|
|
PointListNode *pptordBeg = NULL;
|
|
PointListNode *pptordEnd = NULL;
|
|
|
|
// Let's first add the intersection points to the array of points.
|
|
// Don't add identical points to the last point in the path.
|
|
// iptt is the index of this point
|
|
GpPointF &lastPt = PathPts.Last();
|
|
if (ClosePt(*ptrPt1, lastPt))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (PathPts.Add(*ptrPt1) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
iptt1 = (PathPts.GetCount())-1;
|
|
}
|
|
|
|
if (PathPts.Add(*ptrPt2) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
iptt2 = (PathPts.GetCount())-1;
|
|
}
|
|
|
|
// we have to figure out how to link in the new points, it depends on
|
|
// the direction of the new edge; iptt1 has the x coordinate <= than
|
|
// iptt2.
|
|
|
|
if (ptrEdge->Begin == ptrEdge->SortBegin)
|
|
{
|
|
// PointListNode record for the new point.
|
|
ptord1.Prev = ptrEdge->Begin;
|
|
ptord1.Next = iptt2;
|
|
|
|
ptord2.Prev = iptt1;
|
|
ptord2.Next = ptrEdge->End;
|
|
}
|
|
else
|
|
{
|
|
// PointListNode record for the new point.
|
|
ptord1.Prev = iptt2;
|
|
ptord1.Next = ptrEdge->End;
|
|
|
|
ptord2.Prev = ptrEdge->Begin;
|
|
ptord2.Next = iptt1;
|
|
}
|
|
|
|
// Update the duplicate field with the iptDup value passed in
|
|
ptord1.Dup = dupIndex1;
|
|
ptord2.Dup = dupIndex2;
|
|
|
|
// Inside set to TRUE is the default
|
|
ptord1.Inside = TRUE;
|
|
ptord1.Used = FALSE;
|
|
|
|
ptord2.Inside = TRUE;
|
|
ptord2.Used = FALSE;
|
|
|
|
// And now add it to the array.
|
|
if (PtList.Add(ptord1) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ipto1 = (PtList.GetCount()-1);
|
|
}
|
|
|
|
if (PtList.Add(ptord2) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ipto2 = (PtList.GetCount()-1);
|
|
}
|
|
|
|
// Update ptord records for next and prev
|
|
pptordBeg = &PtList[ptrEdge->Begin];
|
|
pptordEnd = &PtList[ptrEdge->End];
|
|
if (ptrEdge->Begin == ptrEdge->SortBegin)
|
|
{
|
|
pptordBeg->Next = ipto1;
|
|
pptordEnd->Prev = ipto2;
|
|
}
|
|
else
|
|
{
|
|
pptordBeg->Next = ipto2;
|
|
pptordEnd->Prev = ipto1;
|
|
}
|
|
|
|
// Both arrays - ptt and pto must have exactly the same # of elements
|
|
ASSERTMSG((iptt2 == ipto2),("Assert failed."));
|
|
|
|
//GpPointF pfpvBegin = *(PathPts.PGet(ptrEdge->SortBegin));
|
|
//GpPointF pfpvEnd = *(PathPts.PGet(ptrEdge->SortEnd));
|
|
|
|
// Lets create the new line segments. The sorted order of
|
|
// end points is easy - intersection point must be before the SortEnd.
|
|
ptrNew1->SortBegin = iptt1;
|
|
ptrNew1->SortEnd = iptt2;
|
|
|
|
// remember the original end point of the edge
|
|
ptrNew1->OrigBegin = ptrEdge->OrigBegin;
|
|
ptrNew1->OrigEnd = ptrEdge->OrigEnd;
|
|
|
|
ptrNew2->SortBegin = iptt2;
|
|
ptrNew2->SortEnd = ptrEdge->SortEnd;
|
|
|
|
// remember the original end point of the edge
|
|
ptrNew2->OrigBegin = ptrEdge->OrigBegin;
|
|
ptrNew2->OrigEnd = ptrEdge->OrigEnd;
|
|
|
|
// Also iptt (new point) becomes the new SortEnd of the old edge
|
|
ptrEdge->SortEnd = iptt1;
|
|
|
|
// Now, depending on whether the edge being broken up was swapped or not,
|
|
// the new edges need to be swapped
|
|
if (ptrEdge->Begin == ptrEdge->SortBegin)
|
|
{
|
|
// not swapped
|
|
ptrEdge->End = iptt1;
|
|
ptrNew1->Begin = ptrNew1->SortBegin;
|
|
ptrNew1->End = ptrNew1->SortEnd;
|
|
ptrNew2->Begin = ptrNew2->SortBegin;
|
|
ptrNew2->End = ptrNew2->SortEnd;
|
|
}
|
|
else
|
|
{
|
|
// swapped
|
|
ptrEdge->Begin = iptt1;
|
|
ptrNew1->Begin = ptrNew1->SortEnd;
|
|
ptrNew1->End = ptrNew1->SortBegin;
|
|
ptrNew2->Begin = ptrNew2->SortEnd;
|
|
ptrNew2->End = ptrNew2->SortBegin;
|
|
}
|
|
|
|
ptrNew1->Next = LIST_END;
|
|
ptrNew2->Next = LIST_END;
|
|
|
|
// If the point of intersection is on the scan line, we need to insert
|
|
// the new edge to the Active table, otherwise to the table with edges
|
|
// to process.
|
|
|
|
if (CloseReal(XCur, ptrPt1->X))
|
|
{
|
|
MarkToAdd(ptrNew1);
|
|
}
|
|
else
|
|
{
|
|
if(Ok != EdgeList.Add(*ptrNew1))
|
|
{
|
|
return FALSE; // out of memory
|
|
}
|
|
|
|
InsertEdgeIntoList(
|
|
&InactiveEdgeList,
|
|
EdgeList.GetCount()-1,
|
|
CompareLine
|
|
);
|
|
}
|
|
|
|
if (CloseReal(XCur, ptrPt2->X))
|
|
{
|
|
MarkToAdd(ptrNew2);
|
|
}
|
|
else
|
|
{
|
|
if(Ok != EdgeList.Add(*ptrNew2))
|
|
{
|
|
return FALSE; // out of memory
|
|
}
|
|
|
|
InsertEdgeIntoList(
|
|
&InactiveEdgeList,
|
|
EdgeList.GetCount()-1,
|
|
CompareLine
|
|
);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Breaks edge ptrEdge. We have found intersection point intersectPt, which is
|
|
* guranteed to be somewhere on the line segment (not an end point).
|
|
* The 'left' part of the edge will either remain in the active edges
|
|
* or will be removed (only if the intersection point has the current
|
|
* x value. In the latter case, the right edge segment will need to be
|
|
* inserted to active edges, otherwise (the former case) it will go
|
|
* to InactiveEdgeList. If it needs to go to active edges, Breakedge cannot
|
|
* insert it because it would disrupt the order of edges there before
|
|
* both edges broken up are handled. The caller would have to handle
|
|
* the insertion in such case. Therefore, we return the new line
|
|
* segment newEdge and a BOOL value specifying if the caller has to insert
|
|
* the newEdge edge.
|
|
* dupIndex is the index of the duplicate point created by this intersection:
|
|
* When two edges intersect, we have to insert two points (identical)
|
|
* to maintain the same shape of the polygon. These two points are
|
|
* called duplicates.
|
|
* Return FALSE on out of memory.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::BreakEdge(
|
|
Edge *ptrEdge,
|
|
GpPointF *intersectPt,
|
|
Edge *newEdge,
|
|
INT dupIndex
|
|
)
|
|
{
|
|
INT iptt, ipto; // Indecies to the point arrays
|
|
PointListNode ptNode; // Pointers to the linked list and point information
|
|
PointListNode *ptNodeBeg = NULL;
|
|
PointListNode *ptNodeEnd = NULL;
|
|
|
|
// Let's first add the intersection point to the array of points.
|
|
|
|
if (PathPts.Add(*intersectPt) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
iptt = (PathPts.GetCount())-1;
|
|
}
|
|
|
|
// PointListNode record for the new point. It is in the middle of the
|
|
// edge, so Begin and End point of the edge will be its previous and next
|
|
ptNode.Prev = ptrEdge->Begin;
|
|
ptNode.Next = ptrEdge->End;
|
|
// Update the duplicate field with the dupIndex value passed in
|
|
ptNode.Dup = dupIndex;
|
|
// Inside set to TRUE is the default
|
|
ptNode.Inside = TRUE;
|
|
ptNode.Used = FALSE;
|
|
|
|
// And now add it to the array.
|
|
if (PtList.Add(ptNode) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ipto = (PtList.GetCount()-1);
|
|
}
|
|
|
|
// Update ptNode records for next and prev
|
|
ptNodeBeg = &PtList[ptrEdge->Begin];
|
|
ptNodeEnd = &PtList[ptrEdge->End];
|
|
ptNodeBeg->Next = ipto;
|
|
ptNodeEnd->Prev = ipto;
|
|
|
|
// Both arrays - ptt and pto must have exactly the same # of elements
|
|
ASSERTMSG((iptt == ipto),("Assert failed."));
|
|
|
|
// remember the original end point of the edge
|
|
newEdge->OrigBegin = ptrEdge->OrigBegin;
|
|
newEdge->OrigEnd = ptrEdge->OrigEnd;
|
|
|
|
// Lets create the new line segment. The sorted order of end points
|
|
// is easy - intersection point must be before the SortEnd.
|
|
newEdge->SortBegin = iptt;
|
|
newEdge->SortEnd = ptrEdge->SortEnd;
|
|
|
|
// Also iptt (new point) becomes the new SortEnd of the old edge
|
|
ptrEdge->SortEnd = iptt;
|
|
|
|
// Now, depending on whether the edge being broken up was swapped or not,
|
|
// the new edges need to be swapped
|
|
if (ptrEdge->Begin == ptrEdge->SortBegin)
|
|
{
|
|
// not swapped
|
|
ptrEdge->End = iptt;
|
|
newEdge->Begin = newEdge->SortBegin;
|
|
newEdge->End = newEdge->SortEnd;
|
|
}
|
|
else
|
|
{
|
|
// swapped
|
|
ptrEdge->Begin = iptt;
|
|
newEdge->Begin = newEdge->SortEnd;
|
|
newEdge->End = newEdge->SortBegin;
|
|
}
|
|
|
|
newEdge->Next = LIST_END;
|
|
|
|
// If the point of intersection is on the scan line, we need to insert
|
|
// the new edge to the Active table, otherwise to the table with edges
|
|
// to process.
|
|
|
|
if (CloseReal(XCur, intersectPt->X))
|
|
{
|
|
MarkToAdd(newEdge);
|
|
}
|
|
else
|
|
{
|
|
if(Ok != EdgeList.Add(*newEdge))
|
|
{
|
|
return FALSE; // out of memory
|
|
}
|
|
|
|
InsertEdgeIntoList(
|
|
&InactiveEdgeList,
|
|
EdgeList.GetCount()-1,
|
|
CompareLine
|
|
);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Eliminate Self Intersections in a widened path
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus PathSelfIntersectRemover::RemoveSelfIntersects()
|
|
{
|
|
CanAddPts = FALSE;
|
|
|
|
// ------ Phase 1:
|
|
|
|
INT count = EdgeList.GetCount();
|
|
if (count <= 0)
|
|
{
|
|
return Ok; //nothing to correct
|
|
}
|
|
|
|
// Sort all the edges in EdgeList array.
|
|
|
|
Edge *edges = EdgeList.GetDataBuffer();
|
|
QuickSortEdges(edges, edges+count-1);
|
|
|
|
InactiveEdgeList = 0; // Point to the first element in the inactive list.
|
|
|
|
// Initialize the linked list Next pointers:
|
|
|
|
for(int i = 0; i < count-1; i++)
|
|
{
|
|
EdgeList[i].Next = i+1;
|
|
}
|
|
|
|
EdgeList[i].Next = LIST_END;
|
|
|
|
if (!FindIntersects())
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
|
|
// ------ Phase 2:
|
|
|
|
// FindIntersects orphans all the edges from the list. Reconstruct and
|
|
// re-sort using QuickSort. This works out faster because the incremental
|
|
// sort during FindIntersects would have been at least O(n^2).
|
|
|
|
count = EdgeList.GetCount();
|
|
|
|
// we never actually delete anything from the array - only from the
|
|
// linked list, so if we had count > 0 above, we must have a larger or
|
|
// the same count now.
|
|
|
|
ASSERT(count > 0);
|
|
|
|
// Sort all the edges in the EdgeList array.
|
|
|
|
edges = EdgeList.GetDataBuffer();
|
|
QuickSortEdges(edges, edges+count-1);
|
|
|
|
InactiveEdgeList = 0; // Point to the first element in the inactive list.
|
|
|
|
// Initialize the linked list Next pointers:
|
|
|
|
for(int i = 0; i < count-1; i++)
|
|
{
|
|
EdgeList[i].Next = i+1;
|
|
}
|
|
|
|
EdgeList[i].Next = LIST_END;
|
|
|
|
if(!EliminatePoints())
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
// ... move on to phase 3 - collection.
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Eliminate Self Intersections.
|
|
*
|
|
* This is considered 'Phase 2' of the algorithm.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::EliminatePoints()
|
|
{
|
|
// Initialize the array of active edges.
|
|
// Take the first edge from the edge array and add it to the active edgs.
|
|
// Add all other edges, which start at the same x.
|
|
|
|
if (InactiveEdgeList == LIST_END)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
XCur = PathPts[EdgeList[InactiveEdgeList].SortBegin].X;
|
|
|
|
// "move" all edges starting at xCur to the active edge array
|
|
// PathPts in the active edge array will be sorted by y for the
|
|
// given xCur.
|
|
|
|
AddActiveForXScan(&InactiveEdgeList);
|
|
|
|
// As long as the Active edge array is not empty, keep scanning
|
|
|
|
while (TRUE)
|
|
{
|
|
// Scan the active edge array for the current XCur;
|
|
if (!ScanActive())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RemoveVertAll();
|
|
|
|
// Update the XCur using edges from InactiveEdgeList
|
|
if (!ClosestActive(InactiveEdgeList))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Remove all edges which end BEFORE XCur;
|
|
|
|
ClearActiveListExclusiveX();
|
|
|
|
// Add new edges, which begin at XCur
|
|
AddActiveForXScan(&InactiveEdgeList);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Scan through all active edges during the phase of edge elimination.
|
|
* Calculates winding number to the left and to the right of the current
|
|
* x value - XCur. Whenever finds an edge which has 0 winding number
|
|
* on one side, marks it as an outside edge.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::ScanActive()
|
|
{
|
|
Edge *ptrEdge = NULL;
|
|
Edge *plnNext = NULL;
|
|
|
|
GpPointF *pfpvBegin = NULL;
|
|
GpPointF *pfpvEnd = NULL;
|
|
|
|
// We are starting from the outside.
|
|
INT lwindLeft = 0;
|
|
INT lwindRight = 0;
|
|
|
|
// Look at all edges in the ective edge array.
|
|
INT index = ActiveEdgeList;
|
|
|
|
while (index != LIST_END)
|
|
{
|
|
ptrEdge = &EdgeList[index];
|
|
index = ptrEdge->Next;
|
|
|
|
// Get the end points
|
|
ASSERTMSG((ptrEdge->SortBegin < PathPts.GetCount()), ("FATAL ERROR."));
|
|
pfpvBegin = &PathPts[ptrEdge->SortBegin];
|
|
|
|
ASSERTMSG((ptrEdge->SortEnd < PathPts.GetCount()), ("FATAL ERROR."));
|
|
pfpvEnd = &PathPts[ptrEdge->SortEnd];
|
|
|
|
// Is it vertical?
|
|
if (ptrEdge->IsVertical())
|
|
{
|
|
if (NewInterval(ptrEdge))
|
|
{
|
|
if (lwindLeft == 0 || lwindRight == 0)
|
|
{
|
|
MarkVertOutside();
|
|
}
|
|
}
|
|
RemoveVert(ptrEdge->YCur, TRUE /*inclusive*/);
|
|
|
|
//add it to the active vertical edges
|
|
//Edge insertion
|
|
if (ActiveVertEdges.InsertSorted(*ptrEdge,
|
|
(DynSortArrayCompareFunc)&(CompareVertLine),
|
|
this) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (lwindLeft == 0 || lwindRight == 0)
|
|
{
|
|
MarkVertOutside();
|
|
}
|
|
|
|
RemoveVert(ptrEdge->YCur, TRUE /*inclusive*/);
|
|
|
|
// Edge is not vertical, does it have an end point
|
|
// on this scan line
|
|
if ((!CloseReal(pfpvBegin->X, XCur)) &&
|
|
(!CloseReal(pfpvEnd->X, XCur)))
|
|
{
|
|
// we are crossing edge in the middle, so both winding numbers
|
|
// need to be updated
|
|
if (lwindLeft == 0 || lwindRight == 0)
|
|
{
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
if (ptrEdge->SortBegin == ptrEdge->Begin)
|
|
{
|
|
lwindLeft++;
|
|
lwindRight++;
|
|
}
|
|
else
|
|
{
|
|
lwindLeft--;
|
|
lwindRight--;
|
|
}
|
|
if (lwindLeft == 0 || lwindRight == 0)
|
|
{
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
}
|
|
else if ((CloseReal(pfpvBegin->X, XCur)) &&
|
|
(!CloseReal(pfpvEnd->X, XCur)))
|
|
{
|
|
//right edge
|
|
if (lwindRight == 0)
|
|
{
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
if (ptrEdge->SortBegin == ptrEdge->Begin)
|
|
{
|
|
lwindRight++;
|
|
}
|
|
else
|
|
{
|
|
lwindRight--;
|
|
}
|
|
if (lwindRight == 0)
|
|
{
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
}
|
|
else if ((!CloseReal(pfpvBegin->X, XCur)) &&
|
|
(CloseReal(pfpvEnd->X, XCur)))
|
|
{
|
|
//left edge
|
|
if (lwindLeft == 0)
|
|
{
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
if (ptrEdge->SortBegin == ptrEdge->Begin)
|
|
{
|
|
lwindLeft++;
|
|
}
|
|
else
|
|
{
|
|
lwindLeft--;
|
|
}
|
|
if (lwindLeft == 0)
|
|
{
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
WARNING(("Edge is not vertical, but not in XCur"));
|
|
}
|
|
// if we crossed to te outside, all current vertical edges need
|
|
// to be marked
|
|
if (lwindLeft == 0 || lwindRight == 0)
|
|
{
|
|
MarkVertOutside();
|
|
}
|
|
RemoveVert(ptrEdge->YCur, TRUE /*inclusive*/);
|
|
}
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Mark the one or two of the current vertical edges as outside.
|
|
* If there is an even number of edge going in both directions, it means that
|
|
* the winding number is the same on both sides. Since we are being called,
|
|
* it must be 0. In this case, we pick one edge going up and one edge going
|
|
* down and mark both of them as outside.
|
|
* If there is more edges going in one direction than another, then the winding
|
|
* numbers are different, but one of them is 0. We pick one of the edges from
|
|
* that set edges (up or down) which is larger. So if more edges go down, we
|
|
* pick one of those edges and mark them as outside.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PathSelfIntersectRemover::MarkVertOutside()
|
|
{
|
|
Edge *ptrEdge = NULL;
|
|
INT index = 0;
|
|
INT ndown = 0;
|
|
INT nup = 0;
|
|
INT idown = -1;
|
|
INT iup = -1;
|
|
|
|
// we will mark the first one
|
|
|
|
while (index < ActiveVertEdges.GetCount() )
|
|
{
|
|
//PGet on edges
|
|
ptrEdge = &ActiveVertEdges[index];
|
|
if (ptrEdge->SortBegin == ptrEdge->Begin)
|
|
{
|
|
//goes down
|
|
ndown++;
|
|
idown = index;
|
|
}
|
|
else
|
|
{
|
|
nup++;
|
|
iup = index;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (ndown > nup)
|
|
{
|
|
//PGet on edges
|
|
ptrEdge = &ActiveVertEdges[idown];
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
else if (nup > ndown)
|
|
{
|
|
//PGet on edges
|
|
ptrEdge = &ActiveVertEdges[iup];
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
else
|
|
{
|
|
if (nup != 0)
|
|
{
|
|
if (iup > -1)
|
|
{
|
|
//PGet on edges
|
|
ptrEdge = &ActiveVertEdges[iup];
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
}
|
|
if (ndown != 0)
|
|
{
|
|
if (idown > -1)
|
|
{
|
|
//PGet on edges
|
|
ptrEdge = &ActiveVertEdges[idown];
|
|
ptrEdge->MarkOutside();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Returns TRUE if edge ptrEdge belongs to a different interval than the
|
|
* edges currently stored in ActiveVertEdges.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::NewInterval(Edge *ptrEdge)
|
|
{
|
|
Edge *plnOld;
|
|
|
|
if (ActiveVertEdges.GetCount() <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//PGet on edges
|
|
plnOld = &ActiveVertEdges.First();
|
|
|
|
REAL fpY1, fpY2;
|
|
|
|
fpY1 = PathPts[plnOld->SortEnd].Y;
|
|
fpY2 = PathPts[ptrEdge->SortEnd].Y;
|
|
|
|
if (CloseReal(fpY1, fpY2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (fpY1 < fpY2)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Remove all the edges from the active edge array.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PathSelfIntersectRemover::RemoveVertAll()
|
|
{
|
|
if( ActiveVertEdges.GetCount() > 0)
|
|
{
|
|
ActiveVertEdges.Reset(FALSE);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Remove vertical edges which have end points below given y value.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PathSelfIntersectRemover::RemoveVert(REAL y, BOOL inclusive)
|
|
{
|
|
Edge *ptrEdge = NULL;
|
|
|
|
// edges are sorted, so as soon as we find an end point with y value > y
|
|
// we are done
|
|
while (ActiveVertEdges.GetCount() > 0)
|
|
{
|
|
//PGet on edges
|
|
ptrEdge = &ActiveVertEdges.First();
|
|
REAL fpY;
|
|
fpY = PathPts[ptrEdge->SortEnd].Y;
|
|
if (inclusive)
|
|
{
|
|
if (fpY < y || CloseReal(fpY,y))
|
|
{
|
|
ActiveVertEdges.DeleteAt(0);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fpY < y && (!CloseReal(fpY,y)))
|
|
{
|
|
ActiveVertEdges.DeleteAt(0);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Scan the ActiveEdgeList for all edges which end at XCur (inclusive) and
|
|
* simply orphan them. This is used in the first phase of the algorithm
|
|
* (FindIntersects) which passes each edge from the InactiveEdgeList through
|
|
* the ActiveEdgeList. When the edges are no longer needed they're removed
|
|
* from all lists.
|
|
*
|
|
* Created
|
|
*
|
|
* 12/27/2000 asecchia
|
|
*
|
|
\**************************************************************************/
|
|
|
|
void PathSelfIntersectRemover::ClearActiveListInclusiveX()
|
|
{
|
|
INT *pIndex = &ActiveEdgeList;
|
|
|
|
while(*pIndex != LIST_END)
|
|
{
|
|
Edge *pEdge = &EdgeList[*pIndex];
|
|
GpPointF *pSortEnd = &PathPts[pEdge->SortEnd];
|
|
|
|
// inclusive check.
|
|
|
|
if((pSortEnd->X < XCur) || CloseReal(pSortEnd->X, XCur))
|
|
{
|
|
// delete the item and advance.
|
|
|
|
*pIndex = pEdge->Next; // point past the deleted item.
|
|
pEdge->Next = LIST_END; // disconnect the deleted item.
|
|
}
|
|
else
|
|
{
|
|
pIndex = &EdgeList[*pIndex].Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Remove from ActiveEdgeList all edges which end at XCur. This uses
|
|
* an exclusive check. This code is used for the second phase of the algorithm
|
|
* (EliminatePoints). Each edge removed is orphaned from the list because
|
|
* the linked list representation will not be used after this phase.
|
|
*
|
|
* Created:
|
|
*
|
|
* 12/23/2000 asecchia
|
|
*
|
|
\**************************************************************************/
|
|
|
|
void PathSelfIntersectRemover::ClearActiveListExclusiveX()
|
|
{
|
|
INT *pIndex = &ActiveEdgeList;
|
|
|
|
while(*pIndex != LIST_END)
|
|
{
|
|
Edge *pEdge = &EdgeList[*pIndex];
|
|
GpPointF *pSortEnd = &PathPts[pEdge->SortEnd];
|
|
|
|
// exclusive check.
|
|
|
|
if((pSortEnd->X < XCur) && !CloseReal(pSortEnd->X, XCur))
|
|
{
|
|
// delete the item and advance.
|
|
|
|
*pIndex = pEdge->Next; // point past the deleted item.
|
|
pEdge->Next = LIST_END; // disconnect the deleted item.
|
|
}
|
|
else
|
|
{
|
|
pIndex = &EdgeList[*pIndex].Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Add all edges with begin point at xCur to the active edge table.
|
|
* Update the index piLn to the edge array to point to the next edge, which
|
|
* will have to be considered.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
void PathSelfIntersectRemover::AddActiveForX(INT *inactiveHead)
|
|
{
|
|
// We have a new current x, let's calculate new curent Y's for each edge
|
|
// They are needed to insert the new active edges in the right order
|
|
|
|
RecalcActiveYCur();
|
|
|
|
InsertNewEdges(
|
|
&ActiveEdgeList, // insert
|
|
inactiveHead, // remove
|
|
XCur,
|
|
CompareYCurLine
|
|
);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Add all edges with begin point at xCur to the active edge table.
|
|
* Update the index piLn to the edge array to point to the next edge, which
|
|
* will have to be considered.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
void PathSelfIntersectRemover::AddActiveForXScan(INT *inactiveHead)
|
|
{
|
|
// We have a new current x, let's calculate new curent Y's for each edge
|
|
// They are needed to insert the new active edges in the right order
|
|
|
|
RecalcActiveYCur();
|
|
|
|
InsertNewEdges(
|
|
&ActiveEdgeList, // insert
|
|
inactiveHead, // remove
|
|
XCur,
|
|
CompareYScanCurLine
|
|
);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Calculate the yCur position for each edge in the active edge table.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PathSelfIntersectRemover::RecalcActiveYCur(VOID)
|
|
{
|
|
REAL dx = 0;
|
|
REAL dy = 0;
|
|
REAL dxCur = 0;
|
|
|
|
GpPointF fpvBegin(0,0);
|
|
GpPointF fpvEnd(0,0);
|
|
GpPointF fpvOrigBegin(0,0);
|
|
GpPointF fpvOrigEnd(0,0);
|
|
|
|
Edge *ptrEdge = NULL;
|
|
INT active = ActiveEdgeList;
|
|
|
|
// Go through all active egdes
|
|
|
|
while(active != LIST_END)
|
|
{
|
|
ptrEdge = &EdgeList[active];
|
|
|
|
// If the edge will have an end point at XCur,
|
|
// use this end point's y value.
|
|
|
|
fpvBegin = PathPts[ptrEdge->SortBegin];
|
|
fpvEnd = PathPts[ptrEdge->SortEnd];
|
|
fpvOrigBegin = PathPts[ptrEdge->OrigBegin];
|
|
fpvOrigEnd = PathPts[ptrEdge->OrigEnd];
|
|
|
|
if ((XCur == fpvEnd.X) || (fpvEnd.X == fpvBegin.X))
|
|
{
|
|
ptrEdge->YCur = fpvEnd.Y;
|
|
}
|
|
else
|
|
{
|
|
// Calculate the slope numerator and denominator
|
|
dx = fpvOrigEnd.X - fpvOrigBegin.X;
|
|
dy = fpvOrigEnd.Y - fpvOrigBegin.Y;
|
|
dxCur = XCur - fpvOrigBegin.X;
|
|
ptrEdge->YCur = dy * dxCur / dx + fpvOrigBegin.Y;
|
|
}
|
|
|
|
active = EdgeList[active].Next;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Compare two lines. Lines are sorted based on the yCur value.
|
|
* If yCur's are identical, lines are sorted based on the begin point.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* -1 if ptrEdge1 < ptrEdge2
|
|
* 0 if ther are equal
|
|
* 1 if ptrEdge1 > ptrEdge2
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
CompareYScanCurLine(
|
|
PathSelfIntersectRemover *ptrCorrector,
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2
|
|
)
|
|
{
|
|
if (!ptrEdge1->CloseReal(ptrEdge1->YCur, ptrEdge2->YCur))
|
|
{
|
|
if(ptrEdge1->YCur < ptrEdge2->YCur)
|
|
{
|
|
return -1;
|
|
}
|
|
if(ptrEdge1->YCur > ptrEdge2->YCur)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
// All left and vertical edges need to go before right edges if they have
|
|
// the same yCur;
|
|
// A left edge has a begin point < xCur, right edge x = xCur
|
|
|
|
BOOL fLeft1 = TRUE;
|
|
BOOL fLeft2 = TRUE;
|
|
|
|
fLeft1 = ((ptrCorrector->PathPts[ptrEdge1->SortBegin].X <
|
|
ptrCorrector->XCur &&
|
|
ptrEdge1->CloseReal(ptrCorrector->PathPts[ptrEdge1->SortEnd].X,
|
|
ptrCorrector->XCur)) ||
|
|
ptrEdge1->IsVertical());
|
|
fLeft2 = ((ptrCorrector->PathPts[ptrEdge2->SortBegin].X <
|
|
ptrCorrector->XCur &&
|
|
ptrEdge2->CloseReal(ptrCorrector->PathPts[ptrEdge2->SortEnd].X,
|
|
ptrCorrector->XCur)) ||
|
|
ptrEdge2->IsVertical());
|
|
|
|
if (fLeft1 && (!fLeft2))
|
|
return 1;
|
|
if ((!fLeft1) && fLeft2)
|
|
return -1;
|
|
|
|
REAL slope1(0.0);
|
|
REAL slope2(0.0);
|
|
|
|
// For vertical edges, we actually store them as +/-INF, and
|
|
// can use the sign to determine the direction.
|
|
|
|
if (ptrEdge1->IsVertical())
|
|
{
|
|
// Avoid (inf x 0) which causes a real indefinite.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge1->OrigEnd].Y -
|
|
ptrCorrector->PathPts[ptrEdge1->OrigBegin].Y
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
slope1 = SignReal( ptrCorrector->PathPts[ptrEdge1->OrigEnd].Y
|
|
- ptrCorrector->PathPts[ptrEdge1->OrigBegin].Y)
|
|
* FP_INF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Avoid divide by zero.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge1->OrigEnd].X -
|
|
ptrCorrector->PathPts[ptrEdge1->OrigBegin].X
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
slope1 = ptrCorrector->PathPts[ptrEdge1->OrigEnd].Y
|
|
- ptrCorrector->PathPts[ptrEdge1->OrigBegin].Y;
|
|
|
|
slope1 = slope1 /
|
|
(ptrCorrector->PathPts[ptrEdge1->OrigEnd].X
|
|
- ptrCorrector->PathPts[ptrEdge1->OrigBegin].X);
|
|
}
|
|
}
|
|
|
|
if (ptrEdge2->IsVertical())
|
|
{
|
|
// Avoid (inf x 0) which causes a real indefinite.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge2->OrigEnd].Y -
|
|
ptrCorrector->PathPts[ptrEdge2->OrigBegin].Y
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
slope2 = SignReal(ptrCorrector->PathPts[ptrEdge2->OrigEnd].Y
|
|
- ptrCorrector->PathPts[ptrEdge2->OrigBegin].Y)
|
|
* FP_INF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Avoid divide by zero.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge2->OrigEnd].X -
|
|
ptrCorrector->PathPts[ptrEdge2->OrigBegin].X
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
slope2 = ptrCorrector->PathPts[ptrEdge2->OrigEnd].Y
|
|
- ptrCorrector->PathPts[ptrEdge2->OrigBegin].Y;
|
|
|
|
slope2 = slope2 /
|
|
(ptrCorrector->PathPts[ptrEdge2->OrigEnd].X
|
|
- ptrCorrector->PathPts[ptrEdge2->OrigBegin].X);
|
|
}
|
|
}
|
|
|
|
if (slope1 < slope2)
|
|
return -1;
|
|
|
|
if (slope1 > slope2)
|
|
return 1;
|
|
|
|
// slopes are equal
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].Y <
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].Y >
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return 1;
|
|
|
|
// (ptrCorrector->PathPts.PGet(ptrEdge1->SortEnd))->Y ==
|
|
// (ptrCorrector->PathPts.PGet(ptrEdge2->SortEnd))->Y
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].X <
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].X)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].X >
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].X)
|
|
return 1;
|
|
|
|
// Now, all point coordinates are exactly the same, but in some cases,
|
|
// we may have two identical edges with the coordinates, these edges
|
|
// may have different points (actual indecies into points array, not
|
|
// coordinate values). We want to consider them as identical only if
|
|
// the indecies are the same.
|
|
|
|
if (ptrEdge1->SortBegin < ptrEdge2->SortBegin)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortBegin > ptrEdge2->SortBegin)
|
|
return 1;
|
|
|
|
if (ptrEdge1->SortEnd < ptrEdge2->SortEnd)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortEnd > ptrEdge2->SortEnd)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Compare two lines. Lines are sorted based on the yCur value.
|
|
* If yCur's are identical, lines are sorted based on the begin point.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* -1 if ptrEdge1 < ptrEdge2
|
|
* 0 if ther are equal
|
|
* 1 if ptrEdge1 > ptrEdge2
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
CompareYCurLine(
|
|
PathSelfIntersectRemover *ptrCorrector,
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2
|
|
)
|
|
{
|
|
if (!ptrEdge1->CloseReal(ptrEdge1->YCur, ptrEdge2->YCur))
|
|
{
|
|
if(ptrEdge1->YCur < ptrEdge2->YCur)
|
|
return -1;
|
|
|
|
if(ptrEdge1->YCur > ptrEdge2->YCur)
|
|
return 1;
|
|
}
|
|
|
|
// We have to sort based on slope.
|
|
|
|
REAL slope1(0.0);
|
|
REAL slope2(0.0);
|
|
|
|
// For vertical edges, we actually store them as +/-INF, and
|
|
// can use the sign to determine the direction.
|
|
if (ptrEdge1->IsVertical())
|
|
{
|
|
// Avoid INF * 0.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge1->OrigEnd].Y -
|
|
ptrCorrector->PathPts[ptrEdge1->OrigBegin].Y
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
|
|
slope1 = SignReal(
|
|
ptrCorrector->PathPts[ptrEdge1->OrigEnd].Y -
|
|
ptrCorrector->PathPts[ptrEdge1->OrigBegin].Y ) * FP_INF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Avoid divide by zero.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge1->OrigEnd].X -
|
|
ptrCorrector->PathPts[ptrEdge1->OrigBegin].X
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
slope1 = ptrCorrector->PathPts[ptrEdge1->OrigEnd].Y
|
|
- ptrCorrector->PathPts[ptrEdge1->OrigBegin].Y;
|
|
|
|
slope1 = slope1 /
|
|
(ptrCorrector->PathPts[ptrEdge1->OrigEnd].X
|
|
- ptrCorrector->PathPts[ptrEdge1->OrigBegin].X);
|
|
}
|
|
}
|
|
|
|
if (ptrEdge2->IsVertical())
|
|
{
|
|
// Avoid INF * 0.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge2->OrigEnd].Y -
|
|
ptrCorrector->PathPts[ptrEdge2->OrigBegin].Y
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
slope2 = SignReal(
|
|
ptrCorrector->PathPts[ptrEdge2->OrigEnd].Y -
|
|
ptrCorrector->PathPts[ptrEdge2->OrigBegin].Y) * FP_INF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Avoid divide by zero.
|
|
|
|
if( REALABS(
|
|
ptrCorrector->PathPts[ptrEdge2->OrigEnd].X -
|
|
ptrCorrector->PathPts[ptrEdge2->OrigBegin].X
|
|
) > REAL_EPSILON
|
|
)
|
|
{
|
|
slope2 = ptrCorrector->PathPts[ptrEdge2->OrigEnd].Y -
|
|
ptrCorrector->PathPts[ptrEdge2->OrigBegin].Y;
|
|
|
|
slope2 = slope2 /
|
|
(ptrCorrector->PathPts[ptrEdge2->OrigEnd].X
|
|
- ptrCorrector->PathPts[ptrEdge2->OrigBegin].X);
|
|
}
|
|
}
|
|
|
|
if (slope1 < slope2)
|
|
return -1;
|
|
|
|
if (slope1 > slope2)
|
|
return 1;
|
|
|
|
// slopes are equal
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].Y <
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].Y >
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return 1;
|
|
|
|
// (ptrCorrector->PathPts.PGet(ptrEdge1->SortEnd))->Y ==
|
|
// (ptrCorrector->PathPts.PGet(ptrEdge2->SortEnd))->Y
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].X <
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].X)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].X >
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].X)
|
|
return 1;
|
|
|
|
// Now, all point coordinates are exactly the same, but in some cases,
|
|
// we may have two identical edges with the coordinates, these edges
|
|
// may have different points (actual indecies into points array, not
|
|
// coordinate values). We want to consider them as identical only if the
|
|
// indecies are the same.
|
|
|
|
if (ptrEdge1->SortBegin < ptrEdge2->SortBegin)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortBegin > ptrEdge2->SortBegin)
|
|
return 1;
|
|
|
|
if (ptrEdge1->SortEnd < ptrEdge2->SortEnd)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortEnd > ptrEdge2->SortEnd)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Compares lines based on the y coordinate of the end point.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
CompareVertLine(
|
|
PathSelfIntersectRemover *ptrCorrector,
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2
|
|
)
|
|
{
|
|
if(ptrCorrector->PathPts[ptrEdge1->SortEnd].Y <
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return -1;
|
|
|
|
if(ptrCorrector->PathPts[ptrEdge1->SortEnd].Y >
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return 1;
|
|
|
|
if(ptrCorrector->PathPts[ptrEdge1->SortBegin].Y <
|
|
ptrCorrector->PathPts[ptrEdge2->SortBegin].Y)
|
|
return -1;
|
|
|
|
if(ptrCorrector->PathPts[ptrEdge1->SortBegin].Y >
|
|
ptrCorrector->PathPts[ptrEdge2->SortBegin].Y)
|
|
return 1;
|
|
|
|
// Now, all point coordinates are exactly the same, but in some cases,
|
|
// we may have two identical edges with the coordinates, these edges may
|
|
// have different points (actual indecies into points array, not
|
|
// coordinate values). We want to consider them as identical only if the
|
|
// indecies are the same.
|
|
|
|
if (ptrEdge1->SortBegin < ptrEdge2->SortBegin)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortBegin > ptrEdge2->SortBegin)
|
|
return 1;
|
|
|
|
if (ptrEdge1->SortEnd < ptrEdge2->SortEnd)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortEnd > ptrEdge2->SortEnd)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Finds the x value of the closest end point (in x) - the next scan line.
|
|
* Depending on the phase of the algorithm, it needs to look at edges in
|
|
* different arrays. The new value is stored in XCur.
|
|
* Returns FALSE if there are no more points to look at - we are done.
|
|
* In this case, XCur is set to longLast.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::ClosestActive(INT arrayIndex)
|
|
{
|
|
REAL xClosest = 0;
|
|
|
|
// We need to find the next X value for the scan line:
|
|
// take the minimum of any end point of active edges and the begin point
|
|
// of the first edge which will be inserted to active edges.
|
|
|
|
// Let's first look at the possible new edge
|
|
|
|
if (arrayIndex == LIST_END) // no more edges to add
|
|
{
|
|
xClosest = FP_INF;
|
|
}
|
|
else
|
|
{
|
|
xClosest = PathPts[EdgeList[arrayIndex].SortBegin].X;
|
|
}
|
|
|
|
INT active = ActiveEdgeList;
|
|
|
|
// Now, look at all active edges
|
|
|
|
while (active != LIST_END)
|
|
{
|
|
REAL xActive = PathPts[EdgeList[active].SortEnd].X;
|
|
|
|
if((xClosest > xActive) &&
|
|
(xActive > XCur) &&
|
|
(!CloseReal(xActive, XCur)))
|
|
{
|
|
xClosest = xActive;
|
|
}
|
|
|
|
active = EdgeList[active].Next;
|
|
}
|
|
|
|
if (xClosest != FP_INF)
|
|
{
|
|
XCur = xClosest;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Returns the new path. New path contains of 1 or more subpaths.
|
|
* The subpaths are stored as a list of points and a list of points
|
|
* per polygon.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus PathSelfIntersectRemover::GetNewPoints(
|
|
DynPointFArray *pts,
|
|
DynIntArray *polyCounts
|
|
)
|
|
{
|
|
GpPointF *pptToCopy = NULL;
|
|
INT iptFirst = 0;
|
|
GpStatus status;
|
|
|
|
if (PathPts.GetCount() <= 0)
|
|
{
|
|
return Ok;
|
|
}
|
|
|
|
if (PtList.GetCount() <= 0)
|
|
{
|
|
return Ok;
|
|
}
|
|
|
|
// Initialize the array which will contain the resulting paths
|
|
// We don't know haw many points we are going to have
|
|
// Guess? TODO!
|
|
if ((status = pts->ReserveSpace(2*PathPts.GetCount()/3)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if ((status = polyCounts->ReserveSpace(2*PathPts.GetCount())) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
// Collect paths as long as there are unused (outside) points
|
|
INT cptOld = 0;
|
|
INT npt = 0;
|
|
|
|
while (!AllPointsUsed(&iptFirst))
|
|
{
|
|
if (!CollectPath(iptFirst))
|
|
{
|
|
return GenericError;
|
|
}
|
|
// How many points do we have in the last path?
|
|
npt = ResultPts.GetCount() - cptOld;
|
|
// Add the point count to the poly array
|
|
if ((status = polyCounts->Add(npt)) != Ok)
|
|
{
|
|
return status;
|
|
}
|
|
// Save the count of points
|
|
cptOld = ResultPts.GetCount();
|
|
}
|
|
|
|
pts->ReplaceWith(&ResultPts);
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Return TRUE if all points have been used (added to the resulting paths)
|
|
* or are inside. If returns FALSE, returns the index to the next unused
|
|
* point.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::AllPointsUsed(INT *piptUnUsed)
|
|
{
|
|
PointListNode *ptNode = NULL;
|
|
INT iptord = 0;
|
|
|
|
// TODO: Create a static or a member which would remember the last
|
|
// point returned here, so we don't have to start from the beginning each
|
|
// time
|
|
|
|
IntersectsWereRemoved = FALSE;
|
|
while (iptord != -1)
|
|
{
|
|
if (iptord >= PtList.GetCount())
|
|
{
|
|
break;
|
|
}
|
|
ptNode = &PtList[iptord];
|
|
if (!ptNode->Used)
|
|
{
|
|
if (ptNode->Inside)
|
|
{
|
|
IntersectsWereRemoved = TRUE;
|
|
iptord = ptNode->Next;
|
|
}
|
|
else
|
|
{
|
|
*piptUnUsed = iptord;
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iptord = ptNode->Next;
|
|
}
|
|
}
|
|
|
|
*piptUnUsed = -1;
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* After the process of eliminating edges and marking points as inside
|
|
* and outside, we need to go through the linked list of points and
|
|
* build paths from the edges which are outside. CollectPath will
|
|
* collect one path starting from point with index iptFirst. It
|
|
* returns the actual value of this point in pptFirst.
|
|
* CollectPath doesn't check if iptFirst is marked as Inside or Outside.
|
|
* CollectPath returns FALSE on out of memory.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PathSelfIntersectRemover::CollectPath(INT iptFirst)
|
|
{
|
|
PointListNode *ptNode = NULL;
|
|
GpPointF *pfpvThis = NULL;
|
|
INT iptord = iptFirst;
|
|
GpPointF fpvFirst(0,0);
|
|
|
|
// Copy the first point, it always belongs to the path, we don't check
|
|
// here if it is outside. AllPointsUsed did that.
|
|
|
|
if (ResultPts.Add(PathPts[iptFirst]) != Ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Store the point, so we can compare with it
|
|
fpvFirst = PathPts[iptFirst];
|
|
|
|
// Get the index of the next point and update the fUsed field for the
|
|
// copied point.
|
|
ptNode = &PtList[iptord];
|
|
ptNode->Used = TRUE;
|
|
iptord = ptNode->Next;
|
|
BOOL fhavePptord = FALSE;
|
|
|
|
// The end of a subpath will have the next iptord == -1
|
|
while(iptord != -1)
|
|
{
|
|
if (!fhavePptord)
|
|
{
|
|
ptNode = &PtList[iptord];
|
|
}
|
|
fhavePptord = FALSE;
|
|
if (ptNode->Inside)
|
|
{
|
|
// The edge starting in this point is inside, but the
|
|
// point itself isn't.
|
|
// If it is the closing point (the same as the first one)
|
|
// return the collected subpath.
|
|
pfpvThis = &PathPts[iptord];
|
|
if (ClosePt(*pfpvThis, fpvFirst))
|
|
{
|
|
ptNode->Used = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
// If the point is not the same as the first one and we have
|
|
// already been here, something is wrong
|
|
if (ptNode->Used)
|
|
{
|
|
WARNING(("We have an infinite loop"));
|
|
iptord = -1;
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//This is the beginning of an inside edge, our path changes.
|
|
//Let's get the duplicate point
|
|
INT oldiptord = iptord;
|
|
ptNode->Used = TRUE;
|
|
iptord = ptNode->Dup;
|
|
if (iptord >= 0)
|
|
{
|
|
ptNode = &PtList[iptord];
|
|
}
|
|
else
|
|
{
|
|
WARNING(("Dup is negative (1)"));
|
|
return FALSE;
|
|
}
|
|
while (ptNode->Inside || ptNode->Used)
|
|
{
|
|
iptord = ptNode->Dup;
|
|
if (iptord == oldiptord)
|
|
{
|
|
WARNING(("Loop, cannot find a duplicate"));
|
|
return FALSE;
|
|
}
|
|
else if (iptord >= 0)
|
|
{
|
|
ptNode = &PtList[iptord];
|
|
}
|
|
else
|
|
{
|
|
WARNING(("Dup is negative (2)"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
fhavePptord = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The point is outside
|
|
if (ptNode->Used)
|
|
{
|
|
//This may be a longer list of dups, keep looking
|
|
INT oldiptord2 = iptord;
|
|
ptNode->Used = TRUE;
|
|
iptord = ptNode->Dup;
|
|
if (iptord >= 0)
|
|
{
|
|
ptNode = &PtList[iptord];
|
|
}
|
|
else
|
|
{
|
|
WARNING(("Dup is negative (3)"));
|
|
return FALSE;
|
|
}
|
|
while (ptNode->Inside || ptNode->Used)
|
|
{
|
|
iptord = ptNode->Dup;
|
|
if (iptord == oldiptord2)
|
|
{
|
|
WARNING(("Loop, cannot find a duplicate (2)"));
|
|
return FALSE;
|
|
}
|
|
else if (iptord >= 0)
|
|
{
|
|
ptNode = &PtList[iptord];
|
|
}
|
|
else
|
|
{
|
|
WARNING(("Dup is negative (4)"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
fhavePptord = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//New point, everything is OK
|
|
if (ResultPts.Add(PathPts[iptord]) != Ok)
|
|
{
|
|
WARNING(("Could not append to array"));
|
|
return FALSE;
|
|
}
|
|
ptNode->Used = TRUE;
|
|
iptord = ptNode->Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Function used to compare lines when we sort them by x coordinate of the
|
|
* Begin point (SortBegin - smaller x).
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* Created:
|
|
*
|
|
* 6/15/1999 t-wehunt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT CompareLine(
|
|
PathSelfIntersectRemover *ptrCorrector,
|
|
Edge *ptrEdge1,
|
|
Edge *ptrEdge2
|
|
)
|
|
{
|
|
//PGet on edges
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortBegin].X <
|
|
ptrCorrector->PathPts[ptrEdge2->SortBegin].X)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortBegin].X >
|
|
ptrCorrector->PathPts[ptrEdge2->SortBegin].X)
|
|
return 1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortBegin].Y <
|
|
ptrCorrector->PathPts[ptrEdge2->SortBegin].Y)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortBegin].Y >
|
|
ptrCorrector->PathPts[ptrEdge2->SortBegin].Y)
|
|
return 1;
|
|
|
|
// Begin points must be exactly the same
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].X <
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].X)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].X >
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].X)
|
|
return 1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].Y <
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return -1;
|
|
|
|
if (ptrCorrector->PathPts[ptrEdge1->SortEnd].Y >
|
|
ptrCorrector->PathPts[ptrEdge2->SortEnd].Y)
|
|
return 1;
|
|
|
|
// Now, all point coordinates are exactly the same, but in some cases,
|
|
// we may have two identical edges with the coordinates, these edges
|
|
// may have different points (actual indecies into points array, not
|
|
// coordinate values). We want to consider them as identical only if the
|
|
// indecies are the same.
|
|
|
|
if (ptrEdge1->SortBegin < ptrEdge2->SortBegin)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortBegin > ptrEdge2->SortBegin)
|
|
return 1;
|
|
|
|
if (ptrEdge1->SortEnd < ptrEdge2->SortEnd)
|
|
return -1;
|
|
|
|
if (ptrEdge1->SortEnd > ptrEdge2->SortEnd)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|