123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- #include "math/util/decomposePoly.h"
- // twoIndices Methods
- twoIndices::twoIndices()
- {
- i1 = i2 = 0;
- }
- twoIndices::twoIndices(U8 a, U8 b)
- {
- i1 = a;
- i2 = b;
- }
- // decompTri Methods
- decompTri::decompTri()
- {
- mVertIdx[0] = 0;
- mVertIdx[1] = 0;
- mVertIdx[2] = 0;
- }
- void decompTri::orderVerts()
- {
- // Bubble sort, smallest to largest
- U8 temp;
- for (U8 i = 2; i > 0; i--)
- {
- for (U8 j = 0; j < i; j++)
- {
- if (mVertIdx[j] > mVertIdx[j + 1])
- {
- temp = mVertIdx[j];
- mVertIdx[j] = mVertIdx[j + 1];
- mVertIdx[j + 1] = temp;
- }
- }
- }
- }
- void decompTri::setVert(U8 val, U8 idx)
- {
- if(idx < 3)
- mVertIdx[idx] = val;
- }
- U8 decompTri::getVert(U8 idx)
- {
- if(idx < 3)
- return mVertIdx[idx];
- return mVertIdx[2];
- }
- twoIndices decompTri::getOtherVerts(U8 idx)
- {
- if(idx == mVertIdx[0])
- return twoIndices(mVertIdx[1], mVertIdx[2]);
- if(idx == mVertIdx[1])
- return twoIndices(mVertIdx[0], mVertIdx[2]);
- if(idx == mVertIdx[2])
- return twoIndices(mVertIdx[0], mVertIdx[1]);
- return twoIndices(0,0);
- }
- // decompPoly Methods
- void decompPoly::addVert(Point3F &newVert)
- {
- bool found = false;
- for(U8 i = 0; i < mVertList.size(); i++)
- {
- if(newVert == mVertList[i])
- found = true;
- }
- if(!found)
- mVertList.push_back(newVert);
- }
- void decompPoly::initEdgeList()
- {
- mEdgeList.clear();
- S32 next = 0;
- for(S32 i = 0; i < mVertList.size(); i++)
- {
- if(i == mVertList.size()-1)
- next = 0;
- else
- next = i+1;
- mEdgeList.push_back(twoIndices(i, next));
- }
- }
- bool decompPoly::sameSide(Point3F &p1, Point3F &p2, Point3F &l1, Point3F &l2)
- {
- return ((p1.x-l1.x)*(l2.y-l1.y)-(l2.x-l1.x)*(p1.y-l1.y))*((p2.x-l1.x)*(l2.y-l1.y)-(l2.x-l1.x)*(p2.y-l1.y)) > 0;
- }
- bool decompPoly::isInside(decompTri &tri, U8 vertIdx)
- {
- Point3F a(mVertList[tri.getVert(0)]);
- Point3F b(mVertList[tri.getVert(1)]);
- Point3F c(mVertList[tri.getVert(2)]);
- Point3F p(mVertList[vertIdx]);
- return (sameSide(p,a,b,c) && sameSide(p,b,a,c) && sameSide(p,c,a,b));
- }
- U8 decompPoly::leftmost()
- {
- F32 xMin = 9999999.f;
- U8 idx = 0;
- for(U8 i = 0; i < mVertList.size(); i++)
- {
- if(mVertList[i].x < xMin && isVertInEdgeList(i))
- {
- xMin = mVertList[i].x;
- idx = i;
- }
- }
- return idx;
- }
- U8 decompPoly::rightmost()
- {
- F32 xMax = -9999999.f;
- U8 idx = 0;
- for(U8 i = 0; i < mVertList.size(); i++)
- {
- if(mVertList[i].x > xMax && isVertInEdgeList(i))
- {
- xMax = mVertList[i].x;
- idx = i;
- }
- }
- return idx;
- }
- U8 decompPoly::uppermost()
- {
- F32 yMax = -9999999.f;
- U8 idx = 0;
- for(U8 i = 0; i < mVertList.size(); i++)
- {
- if(mVertList[i].y > yMax && isVertInEdgeList(i))
- {
- yMax = mVertList[i].y;
- idx = i;
- }
- }
- return idx;
- }
- U8 decompPoly::lowermost()
- {
- F32 yMin = 9999999.f;
- U8 idx = 0;
- for(U8 i = 0; i < mVertList.size(); i++)
- {
- if(mVertList[i].y < yMin && isVertInEdgeList(i))
- {
- yMin = mVertList[i].y;
- idx = i;
- }
- }
- return idx;
- }
- twoIndices decompPoly::findEdges(U8 vertIdx, bool ¬Unique)
- {
- U8 found = 0;
- U8 edgeIdx[2];
- for(U8 i = 0; i < mEdgeList.size(); i++)
- {
- if(mEdgeList[i].i1 == vertIdx || mEdgeList[i].i2 == vertIdx)
- {
- edgeIdx[found++] = i;
- }
- }
- notUnique = found > 2;
- return twoIndices(edgeIdx[0], edgeIdx[1]);
- }
- decompTri decompPoly::formTriFromEdges(U8 idx1, U8 idx2)
- {
- decompTri tri;
- tri.setVert(mEdgeList[idx1].i1, 0);
- tri.setVert(mEdgeList[idx1].i2, 1);
- if(mEdgeList[idx2].i1 == tri.getVert(0) || mEdgeList[idx2].i1 == tri.getVert(1))
- {
- tri.setVert(mEdgeList[idx2].i2, 2);
- }
- else
- {
- tri.setVert(mEdgeList[idx2].i1, 2);
- }
- return tri;
- }
- twoIndices decompPoly::leftmostInsideVerts(U8 idx1, U8 idx2)
- {
- F32 xMin = 9999999.f;
- S32 left[] = {-1, -1};
- for(U8 i = 0; i < 2; i++)
- {
- for(U8 j = 0; j < mInsideVerts.size(); j++)
- {
- if(mVertList[mInsideVerts[j]].x < xMin && mInsideVerts[j] != left[0])
- {
- xMin = mVertList[mInsideVerts[j]].x;
- left[i] = mInsideVerts[j];
- }
- }
- if(mVertList[idx1].x < xMin && idx1 != left[0])
- {
- xMin = mVertList[idx1].x;
- left[i] = idx1;
- }
- if(mVertList[idx2].x < xMin && idx2 != left[0])
- {
- xMin = mVertList[idx2].x;
- left[i] = idx2;
- }
- xMin = 9999999.f;
- }
- return twoIndices(left[0], left[1]);
- }
- twoIndices decompPoly::rightmostInsideVerts(U8 idx1, U8 idx2)
- {
- F32 xMax = -9999999.f;
- S32 right[] = {-1, -1};
- for(U8 i = 0; i < 2; i++)
- {
- for(U8 j = 0; j < mInsideVerts.size(); j++)
- {
- if(mVertList[mInsideVerts[j]].x > xMax && mInsideVerts[j] != right[0])
- {
- xMax = mVertList[mInsideVerts[j]].x;
- right[i] = mInsideVerts[j];
- }
- }
- if(mVertList[idx1].x > xMax && idx1 != right[0])
- {
- xMax = mVertList[idx1].x;
- right[i] = idx1;
- }
- if(mVertList[idx2].x > xMax && idx2 != right[0])
- {
- xMax = mVertList[idx2].x;
- right[i] = idx2;
- }
- xMax = -9999999.f;
- }
- return twoIndices(right[0], right[1]);
- }
- twoIndices decompPoly::uppermostInsideVerts(U8 idx1, U8 idx2)
- {
- F32 yMax = -9999999.f;
- S32 up[] = {-1, -1};
- for(U8 i = 0; i < 2; i++)
- {
- for(U8 j = 0; j < mInsideVerts.size(); j++)
- {
- if(mVertList[mInsideVerts[j]].y > yMax && mInsideVerts[j] != up[0])
- {
- yMax = mVertList[mInsideVerts[j]].y;
- up[i] = mInsideVerts[j];
- }
- }
- if(mVertList[idx1].y > yMax && idx1 != up[0])
- {
- yMax = mVertList[idx1].y;
- up[i] = idx1;
- }
- if(mVertList[idx2].y > yMax && idx2 != up[0])
- {
- yMax = mVertList[idx2].y;
- up[i] = idx2;
- }
- yMax = -9999999.f;
- }
- return twoIndices(up[0], up[1]);
- }
- twoIndices decompPoly::lowermostInsideVerts(U8 idx1, U8 idx2)
- {
- F32 yMin = 9999999.f;
- S32 down[] = {-1, -1};
- for(U8 i = 0; i < 2; i++)
- {
- for(U8 j = 0; j < mInsideVerts.size(); j++)
- {
- if(mVertList[mInsideVerts[j]].y < yMin && mInsideVerts[j] != down[0])
- {
- yMin = mVertList[mInsideVerts[j]].y;
- down[i] = mInsideVerts[j];
- }
- }
- if(mVertList[idx1].y < yMin && idx1 != down[0])
- {
- yMin = mVertList[idx1].y;
- down[i] = idx1;
- }
- if(mVertList[idx2].y < yMin && idx2 != down[0])
- {
- yMin = mVertList[idx2].y;
- down[i] = idx2;
- }
- yMin = 9999999.f;
- }
- return twoIndices(down[0], down[1]);
- }
- void decompPoly::findPointsInside()
- {
- mInsideVerts.clear();
- for(U8 i = 0; i < mVertList.size(); i++)
- {
- if(i != mTestTri.getVert(0) && i != mTestTri.getVert(1) && i != mTestTri.getVert(2))
- {
- if(isInside(mTestTri, i))
- {
- mInsideVerts.push_back(i);
- }
- }
- }
- }
- void decompPoly::addRemoveEdge(U8 idx1, U8 idx2)
- {
- twoIndices edge1(idx1, idx2);
- if(!mEdgeList.remove(edge1))
- mEdgeList.push_back(edge1);
- }
- void decompPoly::newPoly()
- {
- mVertList.clear();
- mEdgeList.clear();
- mInsideVerts.clear();
- mTris.clear();
- }
- bool decompPoly::isVertInEdgeList(U8 idx)
- {
- for(U8 i = 0; i < mEdgeList.size(); i++)
- {
- if(mEdgeList[i].i1 == idx || mEdgeList[i].i2 == idx)
- return true;
- }
- return false;
- }
- Point3F decompPoly::getVert(U8 idx)
- {
- if(idx < mVertList.size())
- return mVertList[idx];
- return Point3F(0.0f, 0.0f, 0.0f);
- }
- U8 decompPoly::getTriIdx(U32 tri, U8 idx)
- {
- if(tri < mTris.size() && idx < 3)
- return mTris[tri].getVert(idx);
- return 0;
- }
- bool decompPoly::checkEdgeLength(F32 len)
- {
- Point3F p1, p2;
- for(U8 i = 0; i < mEdgeList.size(); i++)
- {
- p1 = mVertList[mEdgeList[i].i1];
- p2 = mVertList[mEdgeList[i].i2];
- p1 = p2 - p1;
- if(p1.len() < len)
- return false;
- }
- return true;
- }
- //---------------------------------------------------
- // Concave polygon decomposition into triangles
- //
- // Based upon this resource:
- // http://www.siggraph.org/education/materials/HyperGraph/scanline/outprims/polygon1.htm
- //
- // 1. Find leftmost vertex in polygon (smallest value along x-axis).
- // 2. Find the two edges adjacent to this vertex.
- // 3. Form a test tri by connecting the open side of these two edges.
- // 4. See if any of the vertices in the poly, besides the ones that form the test tri, are inside the
- // test tri.
- // 5. If there are verts inside, find the 3 leftmost of the inside verts and the test tri verts, form
- // a new test tri from these verts, and repeat from step 4. When no verts are inside, continue.
- // 6. Save test tri as a valid triangle.
- // 7. Remove the edges that the test tri and poly share, and add the new edges from the test tri to
- // the poly to form a new closed poly with the test tri subtracted out.
- //
- // Note: our polygon can contain no more than 255 verts. Complex polygons can create situations where
- // a vertex has more than 2 adjacent edges, causing step 2 to fail. This method improves on the resource
- // listed above by not limiting the decomposition to the leftmost side only. If step 2 fails, the
- // algorithm also tries to decompose the polygon from the top, right, and bottom until one succeeds or
- // they all fail. If they all fail, the polygon is too complex to be decomposed with this method.
- bool decompPoly::decompose()
- {
- // Must have at least 3 verts to form a poly
- if(mVertList.size() < 3)
- return false;
- // Clear out any previously stored tris
- mTris.clear();
- // Initialize the edge list with the default edges
- initEdgeList();
- twoIndices otherVerts, outerVerts2;
- U32 counter = 0;
- U8 uniqueVertAttempt = 0;
- U8 formTriAttempt = 0;
- bool notUnique = false;
- // The main decomposition loop
- while(mEdgeList.size() > 3)
- {
- // Find outermost vert in poly, LMV
- U8 outerVertIdx;
- switch(uniqueVertAttempt)
- {
- case 0: outerVertIdx = leftmost(); break;
- case 1: outerVertIdx = rightmost(); break;
- case 2: outerVertIdx = uppermost(); break;
- case 3: outerVertIdx = uppermost(); break;
- default: outerVertIdx = leftmost();
- }
- // Find edges that share LMV
- twoIndices edgesIdx = findEdges(outerVertIdx, notUnique);
- // If vert shares more than two edges, try decomposing from different direction
- if(notUnique)
- {
- if(uniqueVertAttempt < 4)
- {
- uniqueVertAttempt++;
- continue;
- }
- else
- {
- newPoly();
- return false;
- }
- }
- // Sanity check
- if(edgesIdx.i1 >= mEdgeList.size() || edgesIdx.i2 >= mEdgeList.size())
- {
- newPoly();
- return false;
- }
- // Form the test tri from these 2 edges
- mTestTri = formTriFromEdges(edgesIdx.i1, edgesIdx.i2);
- // See if there are any other poly verts inside the test tri
- findPointsInside();
- // If there are verts inside, repeat procedure until there are none
- while(mInsideVerts.size() > 0 && formTriAttempt++ < mVertList.size())
- {
- // Get the two verts of the test tri that are not the LMV
- otherVerts = mTestTri.getOtherVerts(outerVertIdx);
- // Get the 2 outermost verts of the inside and test tri verts (excluding LMV)
- switch(uniqueVertAttempt)
- {
- case 0: outerVerts2 = leftmostInsideVerts(otherVerts.i1, otherVerts.i2); break;
- case 1: outerVerts2 = rightmostInsideVerts(otherVerts.i1, otherVerts.i2); break;
- case 2: outerVerts2 = uppermostInsideVerts(otherVerts.i1, otherVerts.i2); break;
- case 3: outerVerts2 = uppermostInsideVerts(otherVerts.i1, otherVerts.i2); break;
- default: outerVerts2 = leftmostInsideVerts(otherVerts.i1, otherVerts.i2);
- }
- // Form a new test tri from LMV, and the 2 new leftmost verts above
- mTestTri.setVert(outerVerts2.i1, 0);
- mTestTri.setVert(outerVertIdx, 1);
- mTestTri.setVert(outerVerts2.i2, 2);
- // See if there are verts inside this new test tri
- findPointsInside();
- }
- // We have found a valid tri
- mTris.push_back(mTestTri);
- // Remove edges common to test tri and poly... add unique edges from test tri to poly
- addRemoveEdge(mTestTri.getVert(0), mTestTri.getVert(1));
- addRemoveEdge(mTestTri.getVert(1), mTestTri.getVert(2));
- addRemoveEdge(mTestTri.getVert(0), mTestTri.getVert(2));
- // This should never take 255 iterations... we must be stuck in an infinite loop
- if(counter++ > 255)
- {
- newPoly();
- return false;
- }
- // Reset attempts
- formTriAttempt = 0;
- uniqueVertAttempt = 0;
- }
- // The last tri
- mTris.push_back(formTriFromEdges(0,1));
- // The verts need to be ordered to draw correctly
- for(U8 i = 0; i < mTris.size(); i++)
- {
- mTris[i].orderVerts();
- }
- return true;
- }
|