Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / misc / polygon.cpp
blob856255c3480c0e68b5878941867a2d5ed3700350
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdmisc.h"
19 #include "nel/misc/polygon.h"
20 #include "nel/misc/plane.h"
21 #include "nel/misc/triangle.h"
24 using namespace std;
25 using namespace NLMISC;
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 namespace NLMISC
35 //==================================//
36 // CPolygon implementation //
37 //==================================//
39 // ***************************************************************************
40 CPolygon::CPolygon(const CVector &a, const CVector &b, const CVector &c)
42 Vertices.reserve(3);
43 Vertices.push_back(a);
44 Vertices.push_back(b);
45 Vertices.push_back(c);
48 // ***************************************************************************
49 void CPolygon::toTriFan(std::vector<NLMISC::CTriangle> &dest) const
51 sint count = (sint) Vertices.size() - 2;
52 for(sint k = 0; k < count; ++k)
54 dest.push_back(CTriangle(Vertices[0], Vertices[k + 1], Vertices[k + 2]));
58 // ***************************************************************************
59 float CPolygon::computeArea() const
61 float area = 0.f;
62 sint numVerts = (sint) Vertices.size();
63 for(sint k = 0; k < numVerts - 2; ++k)
65 CVector v0 = Vertices[k + 1] - Vertices[0];
66 CVector v1 = Vertices[k + 2] - Vertices[0];
67 area += (v0 ^ v1).norm();
69 return 0.5f * fabsf(area);
72 // ***************************************************************************
73 void CPolygon::clip(const CPlane *planes, uint nPlanes)
75 if(nPlanes==0 || getNumVertices()==0)
76 return;
78 // The final polygon has at maximum currentVertices+number of clipping planes.
79 // For performance, the vectors are static, so reallocation rarely occurs.
80 static vector<CVector> tab0, tab1;
81 tab0.resize(getNumVertices()+nPlanes);
82 tab1.resize(getNumVertices()+nPlanes);
83 // Init tab0 with Vertices.
84 copy(Vertices.begin(), Vertices.end(), tab0.begin());
85 CVector *in=&(*tab0.begin()), *out= &(*tab1.begin());
86 sint nin= getNumVertices(), nout;
87 for(sint i=0;i<(sint)nPlanes;i++)
89 nout= planes[i].clipPolygonBack(in, out, nin);
90 swap(in, out);
91 nin= nout;
92 if(nin==0)
93 break;
96 // Final result in "in".
97 Vertices.resize(nin);
98 if(nin>0)
100 memcpy(&(*Vertices.begin()), in, nin*sizeof(CVector));
105 // ***************************************************************************
106 void CPolygon::clip(const std::vector<CPlane> &planes)
108 if(planes.empty())
109 return;
110 clip(&(*planes.begin()), (uint)planes.size());
115 // ***************************************************************************
116 void CPolygon::serial(NLMISC::IStream &f)
118 f.serialVersion(0);
119 f.serialCont(Vertices);
122 // ***************************************************************************
123 void CPolygon::getBestTriplet(uint &index0,uint &index1,uint &index2)
125 nlassert(Vertices.size() >= 3);
126 uint i, j, k;
127 float bestArea = 0.f;
128 const uint numVerts = (uint)Vertices.size();
129 for (i = 0; i < numVerts; ++i)
131 for (j = 0; j < numVerts; ++j)
133 if (i != j)
135 for (k = 0; k < numVerts; ++k)
137 if (k != i && k != j)
139 CVector v0 = Vertices[j] - Vertices[i];
140 CVector v1 = Vertices[k] - Vertices[i];
141 float area = (v0 ^ v1).norm();
142 if (area > bestArea)
144 bestArea = area;
145 index0 = i;
146 index1 = j;
147 index2 = k;
157 // ***************************************************************************
158 void CPolygon::buildBasis(CMatrix &dest)
160 nlassert(Vertices.size() > 3);
161 uint i1, i2, i3;
162 getBestTriplet(i1, i2, i3);
163 CVector v1 = (Vertices[i2] - Vertices[i1]).normed();
164 CVector v2 = (Vertices[i3] - Vertices[i1]).normed();
165 CVector K = v2 ^ v1;
166 CVector I = v1 - (v1 * K) * v1;
167 CVector J = K ^ I;
168 dest.setRot(I, J, K);
169 dest.setPos(Vertices[i1]);
172 // ***************************************************************************
174 class CConcavePolygonsVertexDesc
176 public:
178 CConcavePolygonsVertexDesc (float length, uint index)
180 Length = length;
181 Index = index;
184 // Length > 0
185 float Length;
187 // First vertex index
188 uint Index;
190 typedef std::map<float, CConcavePolygonsVertexDesc> TCConcavePolygonsVertexMap;
193 // ***************************************************************************
194 bool CPolygon::toConvexPolygonsEdgeIntersect (const CVector2f& a0, const CVector2f& a1, const CVector2f& b0, const CVector2f& b1)
196 // both vertical?
197 if( a0.x-a1.x==0 && b0.x-b1.x==0 )
198 return false;
200 // compute intersection of both lines
201 CVector2f intersection;
203 // first edge vertical?
204 if(a0.x - a1.x==0)
206 float Ab = (b0.y - b1.y) / (b0.x - b1.x);
208 // Intersection
209 intersection.x = a0.x;
210 intersection.y = b0.y + (a0.x-b0.x) * Ab;
212 // second edge vertical?
213 else if(b0.x - b1.x==0)
215 float Aa = (a0.y - a1.y) / (a0.x - a1.x);
217 // Intersection
218 intersection.x = b0.x;
219 intersection.y = a0.y + (b0.x-a0.x) * Aa;
221 // standard case
222 else
224 float Aa = (a0.y - a1.y) / (a0.x - a1.x);
225 float Ba = a0.y - a0.x * Aa;
226 float Ab = (b0.y - b1.y) / (b0.x - b1.x);
227 float Bb = b0.y - b0.x * Ab;
229 // colinear?
230 if(Aa==Ab)
231 return false;
233 // Intersection
234 intersection.x = (Bb - Ba) / (Aa - Ab);
235 intersection.y = Aa * intersection.x + Ba;
238 // In it ?
239 return ( ( (a0-intersection)*(a1-intersection) < 0 ) && ( (b0-intersection)*(b1-intersection) < 0 ) );
242 // ***************************************************************************
244 class CBSPNode2v
246 public:
247 CBSPNode2v ()
249 Back = NULL;
250 Front = NULL;
252 CBSPNode2v ( const CPlane &plane, CVector p0, CVector p1, uint v0, uint v1 ) : Plane (plane), P0 (p0), P1 (p1)
254 Back = NULL;
255 Front = NULL;
256 Parent = NULL;
257 V0 = v0;
258 V1 = v1;
260 ~CBSPNode2v ()
262 if (Front)
263 delete Front;
264 if (Back)
265 delete Back;
268 void insert (CBSPNode2v *node)
270 // Front ?
271 bool p0Front = (Plane * node->P0) > 0;
272 bool p1Front = (Plane * node->P1) > 0;
273 if (p0Front && p1Front)
275 // Front child ?
276 if (Front)
277 Front->insert (node);
278 else
280 // Link left
281 Front = node;
282 node->Parent = this;
285 else if ((!p0Front) && (!p1Front))
287 // Back child ?
288 if (Back)
289 Back->insert (node);
290 else
292 // Link left
293 Back = node;
294 node->Parent = this;
297 else
299 // Split vertex
300 CVector newVertex = Plane.intersect (node->P0, node->P1);
302 // New node
303 CBSPNode2v *newNode = new CBSPNode2v (node->Plane, node->P0, newVertex, node->V0, node->V1);
305 // Old node
306 node->P0 = newVertex;
308 // Insert child
309 CBSPNode2v **p0Parent;
310 CBSPNode2v **p1Parent;
312 // Get insertion pointer
313 if (p0Front)
315 p0Parent = &Front;
316 p1Parent = &Back;
318 else
320 p0Parent = &Back;
321 p1Parent = &Front;
324 // Insert children
325 if (*p0Parent)
327 (*p0Parent)->insert (newNode);
329 else
331 *p0Parent = newNode;
332 newNode->Parent = this;
335 // Insert children
336 if (*p1Parent)
338 (*p1Parent)->insert (node);
340 else
342 *p1Parent = node;
343 node->Parent = this;
348 bool intersect (const CVector &p0, const CVector &p1, uint v0, uint v1) const
350 // Front ?
351 bool p0Front = (Plane * p0) > 0;
352 bool p1Front = (Plane * p1) > 0;
354 if (p0Front != p1Front)
355 if ( (v0 != V0) && (v0 != V1) && (v1 != V0) && (v1 != V1) )
356 if (CPolygon::toConvexPolygonsEdgeIntersect ((CVector2f) P0, (CVector2f) P1, (CVector2f) p0, (CVector2f) p1))
357 return true;
359 if (p0Front || p1Front)
361 if (Front)
362 if (Front->intersect (p0, p1, v0, v1))
363 return true;
366 if ((!p0Front) || (!p1Front))
368 if (Back)
369 if (Back->intersect (p0, p1, v0, v1))
370 return true;
373 return false;
376 CBSPNode2v *Back, *Front, *Parent;
377 CPlane Plane;
378 CVector P0;
379 CVector P1;
380 uint V0;
381 uint V1;
384 // ***************************************************************************
386 bool CPolygon::toConvexPolygonsLeft (const std::vector<CVector> &vertex, uint a, uint b, uint c)
388 return ( (vertex[b].x - vertex[a].x) * (vertex[c].y - vertex[a].y) - (vertex[c].x - vertex[a].x) * (vertex[b].y - vertex[a].y) ) < 0;
391 // ***************************************************************************
393 bool CPolygon::toConvexPolygonsLeftOn (const std::vector<CVector> &vertex, uint a, uint b, uint c)
395 return ( (vertex[b].x - vertex[a].x) * (vertex[c].y - vertex[a].y) - (vertex[c].x - vertex[a].x) * (vertex[b].y - vertex[a].y) ) <= 0;
398 // ***************************************************************************
400 bool CPolygon::toConvexPolygonsInCone (const std::vector<CVector> &vertex, uint a, uint b)
402 // Prev and next
403 uint a0 = a+1;
404 if (a0==vertex.size())
405 a0=0;
406 uint a1;
407 if (a==0)
408 a1= (uint)vertex.size()-1;
409 else
410 a1= a-1;
412 if (toConvexPolygonsLeftOn (vertex, a, a1, a0) )
414 return toConvexPolygonsLeft ( vertex, a, b, a0) && toConvexPolygonsLeft ( vertex, b, a, a1);
416 else
418 return !( toConvexPolygonsLeft ( vertex, a, b, a1) && toConvexPolygonsLeft ( vertex, b, a, a0) );
422 // ***************************************************************************
424 bool CPolygon::toConvexPolygonsDiagonal (const std::vector<CVector> &vertex, const CBSPNode2v &bsp, uint a, uint b)
426 // Check it is a border
427 if ( ( (b - a) == 1) || ( (a - b) == 1) || ( (a==0) && (b ==(vertex.size()-1))) || ( (b==0) && (a ==(vertex.size()-1))) )
428 return true;
430 // Check visibility
431 if (toConvexPolygonsInCone (vertex, a, b) && toConvexPolygonsInCone (vertex, b, a))
433 // Intersection ?
434 return !bsp.intersect (vertex[a], vertex[b], a, b);
436 return false;
439 // ***************************************************************************
441 void CPolygon::toConvexPolygonsLocalAndBSP (std::vector<CVector> &localVertices, CBSPNode2v &root, const CMatrix &basis) const
443 // Invert matrix
444 CMatrix invert = basis;
445 invert.invert ();
447 // Insert vertices in an ordered table
448 uint vertexCount = (uint)Vertices.size();
449 TCConcavePolygonsVertexMap vertexMap;
450 localVertices.resize (vertexCount);
451 uint i, j;
453 // Transform the vertex
454 for (i=0; i<vertexCount; i++)
456 CVector local = invert*Vertices[i];
457 localVertices[i] = CVector (local.x, local.y, 0);
460 // Plane direction
461 i=0;
462 j=(uint)Vertices.size()-1;
463 CVector normal = localVertices[i] - localVertices[j];
464 normal = normal ^ CVector::K;
465 CPlane clipPlane;
466 clipPlane.make(normal, localVertices[i]);
468 // Build the BSP root
469 root = CBSPNode2v (clipPlane, localVertices[i], localVertices[j], i, j);
471 // Insert all others edges
472 j=i++;
473 for (; i<Vertices.size(); i++)
475 // Plane direction
476 normal = localVertices[i] - localVertices[j];
477 normal = normal ^ CVector::K;
478 clipPlane.make(normal, localVertices[i]);
480 // Build the BSP root
481 root.insert ( new CBSPNode2v (clipPlane, localVertices[i], localVertices[j], i, j) );
483 j=i;
488 // ***************************************************************************
489 bool CPolygon::toConvexPolygons (std::list<CPolygon>& outputPolygons, const CMatrix& basis) const
491 // Some vertices ?
492 if (Vertices.size()>2)
494 // Local vertices
495 std::vector<CVector> localVertices;
497 // Build the BSP root
498 CBSPNode2v root;
500 // Build the local array and the BSP
501 toConvexPolygonsLocalAndBSP (localVertices, root, basis);
503 // Build a vertex list
504 std::list<uint> vertexList;
505 uint i;
506 for (i=0; i<Vertices.size(); i++)
507 vertexList.push_back (i);
509 // Clip ears while there is some polygons
510 std::list<uint>::iterator current=vertexList.begin();
511 std::list<uint>::iterator begin=vertexList.begin();
514 again:;
515 // Search for a diagonal
516 bool found = false;
518 // Get next vertex
519 std::list<uint>::iterator first = current;
520 std::list<uint>::iterator lastPreviousPrevious=current;
521 std::list<uint>::iterator lastPrevious=current;
522 lastPrevious++;
523 if (lastPrevious==vertexList.end())
524 lastPrevious = vertexList.begin();
525 std::list<uint>::iterator currentNext = lastPrevious;
526 std::list<uint>::iterator last = lastPrevious;
527 last++;
528 if (last==vertexList.end())
529 last = vertexList.begin();
530 while (last != current)
532 // Is a diagonal ?
533 if (
534 (toConvexPolygonsDiagonal (localVertices, root, *lastPreviousPrevious, *last)) &&
535 (toConvexPolygonsDiagonal (localVertices, root, *currentNext, *last)) &&
536 (toConvexPolygonsDiagonal (localVertices, root, *last, *current))
539 // Find one
540 found = true;
542 else
544 // Come back
545 last = lastPrevious;
546 lastPrevious = lastPreviousPrevious;
547 break;
550 // Next vertex
551 lastPreviousPrevious = lastPrevious;
552 lastPrevious = last++;
553 if (last==vertexList.end())
554 last = vertexList.begin();
557 // Last polygon ?
558 if (last==current)
560 // Add a polygon
561 outputPolygons.push_back (CPolygon());
562 CPolygon &back = outputPolygons.back ();
563 back.Vertices.reserve (vertexList.size());
565 // Add each vertex in the new polygon
566 current=vertexList.begin();
567 while (current!=vertexList.end())
569 back.Vertices.push_back (Vertices[*current]);
570 current++;
573 // Exit
574 return true;
576 else
578 std::list<uint>::iterator firstNext = current;
579 std::list<uint>::iterator firstNextNext = currentNext;
580 if (first != vertexList.begin())
581 first--;
582 else
584 first = vertexList.end();
585 first--;
588 while (current != first)
590 // Is a diagonal ?
591 if (
592 (toConvexPolygonsDiagonal (localVertices, root, *firstNextNext, *first)) &&
593 (toConvexPolygonsDiagonal (localVertices, root, *lastPrevious, *first)) &&
594 (toConvexPolygonsDiagonal (localVertices, root, *last, *first))
597 // Find one
598 found = true;
600 else
602 // Come back
603 first = firstNext;
604 break;
607 // Next vertex
608 firstNextNext = firstNext;
609 firstNext = first;
610 if (first==vertexList.begin())
612 first = vertexList.end();
613 first--;
615 else
616 first--;
620 // Found ?
621 if (found)
623 // Count vertex
624 outputPolygons.push_back (CPolygon());
625 CPolygon &back = outputPolygons.back ();
627 // Vertex count
628 uint vertexCount = 1;
629 current = first;
630 while (current != last)
632 vertexCount++;
633 current++;
634 if (current == vertexList.end())
635 current = vertexList.begin();
638 // Alloc vertices
639 back.Vertices.reserve (vertexCount);
641 // Copy and remove vertices
642 back.Vertices.push_back (Vertices[*first]);
643 first++;
644 if (first == vertexList.end())
645 first = vertexList.begin();
646 while (first != last)
648 back.Vertices.push_back (Vertices[*first]);
650 // Remove from list
651 first = vertexList.erase (first);
652 if (first == vertexList.end())
653 first = vertexList.begin();
654 nlassert (first != vertexList.end());
656 back.Vertices.push_back (Vertices[*first]);
657 current = begin = last;
658 goto again;
661 // Next current
662 current++;
663 if (current == vertexList.end())
664 current = vertexList.begin ();
666 while (current != begin);
668 return false;
671 // ***************************************************************************
673 bool CPolygon::chain (const std::vector<CPolygon> &other, const CMatrix& basis)
675 // Local vertices
676 std::vector<CVector> localVertices;
678 // Build the BSP root
679 CBSPNode2v root;
681 // Build the local array and the BSP
682 toConvexPolygonsLocalAndBSP (localVertices, root, basis);
684 // Local vertices
685 std::vector<std::vector<CVector> > localVerticesOther (other.size());
687 // Build the BSP root
688 std::vector<CBSPNode2v> rootOther (other.size());
690 // Build a copy of the polygons
691 std::vector<CPolygon> copy = other;
693 // Main copy
694 CPolygon mainCopy = *this;
696 // For each other polygons
697 uint o;
698 for (o=0; o<other.size(); o++)
700 // Build the local array and the BSP
701 other[o].toConvexPolygonsLocalAndBSP (localVerticesOther[o], rootOther[o], basis);
704 // Look for a couple..
705 uint thisCount = (uint)Vertices.size();
706 uint i, j;
707 for (o=0; o<other.size(); o++)
709 uint otherCount = (uint)other[o].Vertices.size();
711 // Try to link in the main polygon
712 for (i=0; i<thisCount; i++)
714 for (j=0; j<otherCount; j++)
716 // Test this segement
717 if (!root.intersect (localVertices[i], localVerticesOther[o][j], i, 0xffffffff))
719 // Test each other polygons
720 uint otherO;
721 for (otherO=0; otherO<other.size(); otherO++)
723 // Intersect ?
724 if (rootOther[otherO].intersect (localVertices[i], localVerticesOther[o][j], 0xffffffff, (otherO == o)?j:0xffffffff))
725 break;
728 // Continue ?
729 if (otherO==other.size())
731 // Insert new vertices
732 mainCopy.Vertices.insert (mainCopy.Vertices.begin()+i, 2+otherCount, CVector());
734 // Copy the first vertex
735 mainCopy.Vertices[i] = mainCopy.Vertices[i+otherCount+2];
737 // Copy the new vertices
738 uint k;
739 for (k=0; k<otherCount; k++)
741 uint index = j+k;
742 if (index>=otherCount)
743 index -= otherCount;
744 mainCopy.Vertices[i+k+1] = copy[o].Vertices[index];
747 // Copy the last one
748 mainCopy.Vertices[i+otherCount+1] = copy[o].Vertices[j];
749 break;
753 if (j!=otherCount)
754 break;
757 // Not found ?
758 if (i==thisCount)
760 // Try to link in the sub polygons
761 uint otherToCheck;
762 for (otherToCheck=o+1; otherToCheck<other.size(); otherToCheck++)
764 uint otherToCheckCount = (uint)other[otherToCheck].Vertices.size();
765 for (i=0; i<otherToCheckCount; i++)
767 for (j=0; j<otherCount; j++)
769 // Test this segement
770 if (!rootOther[otherToCheck].intersect (localVerticesOther[otherToCheck][i], localVerticesOther[o][j], i, 0xffffffff))
772 // Test each other polygons
773 uint otherO;
774 for (otherO=0; otherO<other.size(); otherO++)
776 // Intersect ?
777 if (rootOther[otherO].intersect (localVerticesOther[otherToCheck][i], localVerticesOther[o][j], (otherToCheck == otherO)?i:0xffffffff, (otherO == o)?j:0xffffffff))
778 break;
781 // Continue ?
782 if (otherO==other.size())
784 // Insert new vertices
785 copy[otherToCheck].Vertices.insert (copy[otherToCheck].Vertices.begin()+i, 2+otherCount, CVector());
787 // Copy the first vertex
788 copy[otherToCheck].Vertices[i] = copy[otherToCheck].Vertices[i+otherCount+2];
790 // Copy the new vertices
791 uint k;
792 for (k=0; k<otherCount; k++)
794 uint index = j+k;
795 if (index>=otherCount)
796 index -= otherCount;
797 copy[otherToCheck].Vertices[i+k+1] = copy[otherO].Vertices[index];
800 // Copy the last one
801 copy[otherToCheck].Vertices[i+otherCount+1] = copy[otherO].Vertices[j];
802 break;
806 if (j!=otherCount)
807 break;
809 if (i!=otherToCheckCount)
810 break;
812 if (otherToCheck==other.size())
814 // Not ok
815 return false;
820 // Ok
821 *this = mainCopy;
822 return true;
825 // ***************************************************************************
828 //====================================//
829 // CPolygon2d implementation //
830 //====================================//
834 // ***************************************************************************
835 CPolygon2D::CPolygon2D(const CPolygon &src, const CMatrix &projMat)
837 fromPolygon(src, projMat);
840 // ***************************************************************************
841 void CPolygon2D::fromPolygon(const CPolygon &src, const CMatrix &projMat /*=CMatrix::Identity*/)
843 uint size = (uint)src.Vertices.size();
844 Vertices.resize(size);
845 for (uint k = 0; k < size; ++k)
847 CVector proj = projMat * src.Vertices[k];
848 Vertices[k].set(proj.x, proj.y);
852 // ***************************************************************************
853 bool CPolygon2D::isConvex()
855 bool Front = true, Back = false;
856 // we apply a dummy algo for now : check whether every vertex is in the same side
857 // of every plane defined by a segment of this poly
858 uint numVerts = (uint)Vertices.size();
859 if (numVerts < 3) return true;
860 CVector segStart, segEnd;
861 CPlane clipPlane;
862 for (TVec2fVect::const_iterator it = Vertices.begin(); it != Vertices.end(); ++it)
864 segStart.set(it->x, it->y, 0); // segment start
865 segEnd.set((it + 1)->x, (it + 1)->y, 0); // segment end
866 float n = (segStart - segEnd).norm(); // segment norm
867 if (n != 0)
869 clipPlane.make(segStart, segEnd, (n > 10 ? n : 10) * CVector::K + segStart); // make a plane, with this segment and the poly normal
870 // check each other vertices against this plane
871 for (TVec2fVect::const_iterator it2 = Vertices.begin(); it2 != Vertices.end(); ++it2)
873 if (it2 != it && it2 != (it + 1)) // the vertices must not be part of the test plane (because of imprecision)
876 float dist = clipPlane * CVector(it2->x, it2-> y, 0);
877 if (dist != 0) // midlle pos
879 if (dist > 0) Front = true; else Back = true;
880 if (Front && Back) return false; // there are both front end back vertices -> failure
886 return true;
889 // ***************************************************************************
891 void CPolygon2D::buildConvexHull(CPolygon2D &dest) const
893 nlassert(&dest != this);
895 if (this->Vertices.size() == 3) // with 3 points it is always convex
897 dest = *this;
898 return;
900 uint k, l;
901 uint numVerts = (uint)Vertices.size();
902 CVector2f p, curr, prev;
903 uint pIndex, p1Index, p2Index, pCurr, pPrev;
904 // this is not optimized, but not used in realtime.. =)
905 nlassert(numVerts >= 3);
906 dest.Vertices.clear();
908 typedef std::set<uint> TIndexSet;
909 TIndexSet leftIndex;
910 for (k = 0; k < Vertices.size(); ++k)
912 leftIndex.insert(k);
916 // 1) find the highest point p of the set. We are sure it belongs to the hull
917 pIndex = 0;
918 p = Vertices[0];
919 for (k = 1; k < numVerts; ++k)
921 if (Vertices[k].y < p.y)
923 pIndex = k;
924 p = Vertices[k];
928 leftIndex.erase(pIndex);
931 float bestCP = 1.1f;
932 p1Index = p2Index = pIndex;
934 for (k = 0; k < numVerts; ++k)
936 if (k != pIndex)
938 for (l = 0; l < numVerts; ++l)
940 if (l != pIndex && l != k)
942 CVector2f seg1 = (Vertices[l] - p).normed();
943 CVector2f seg2 = (Vertices[k] - p).normed();
945 //CVector cp = CVector(seg1.x, seg1.y, 0) ^ CVector(seg2.x, seg2.y, 0);
946 //float n = fabsf(cp.z);
947 float n = seg1 * seg2;
948 if (n < bestCP)
950 p1Index = l;
951 p2Index = k;
952 bestCP = n;
960 leftIndex.erase(p2Index);
964 // start from the given triplet, and complete the poly until we reach the first point
965 pCurr = p2Index;
966 pPrev = pIndex;
968 curr = Vertices[pCurr];
969 prev = Vertices[pPrev];
971 // create the first triplet vertices
972 dest.Vertices.push_back(Vertices[p1Index]);
973 dest.Vertices.push_back(prev);
974 dest.Vertices.push_back(curr);
976 uint step = 0;
978 for(;;)
980 bestCP = 1.1f;
981 CVector2f seg2 = (prev - curr).normed();
982 TIndexSet::iterator bestIt = leftIndex.end();
983 for (TIndexSet::iterator it = leftIndex.begin(); it != leftIndex.end(); ++it)
985 if (step == 0 && *it == p1Index) continue;
986 CVector2f seg1 = (Vertices[*it] - curr).normed();
987 float n = seg1 * seg2;
988 if (n < bestCP)
990 bestCP = n;
991 bestIt = it;
995 nlassert(bestIt != leftIndex.end());
996 if (*bestIt == p1Index)
998 return; // if we reach the start point we have finished
1000 prev = curr;
1001 curr = Vertices[*bestIt];
1002 pPrev = pCurr;
1003 pCurr = *bestIt;
1004 // add new point to the destination
1005 dest.Vertices.push_back(curr);
1006 ++step;
1007 leftIndex.erase(bestIt);
1011 // ***************************************************************************
1014 void CPolygon2D::serial(NLMISC::IStream &f)
1016 (void)f.serialVersion(0);
1017 f.serialCont(Vertices);
1020 // ***************************************************************************
1021 /// get the best triplet of vector. e.g the triplet that has the best surface
1022 void CPolygon2D::getBestTriplet(uint &index0, uint &index1, uint &index2)
1024 nlassert(Vertices.size() >= 3);
1025 uint i, j, k;
1026 float bestArea = 0.f;
1027 const uint numVerts = (uint)Vertices.size();
1028 for (i = 0; i < numVerts; ++i)
1030 for (j = 0; j < numVerts; ++j)
1032 if (i != j)
1034 for (k = 0; k < numVerts; ++k)
1036 if (k != i && k != j)
1038 CVector2f v0 = Vertices[j] - Vertices[i];
1039 CVector2f v1 = Vertices[k] - Vertices[i];
1040 float area = fabsf((CVector(v0.x, v0.y, 0) ^ CVector(v1.x, v1.y, 0)).norm());
1041 if (area > bestArea)
1043 bestArea = area;
1044 index0 = i;
1045 index1 = j;
1046 index2 = k;
1056 /// ***************************************************************************************
1057 // scan a an edge of a poly and write it into a table
1058 static void ScanEdge(CPolygon2D::TRasterVect &outputVect, sint topY, const CVector2f &v1, const CVector2f &v2, bool rightEdge = true)
1060 const uint rol16 = 65536;
1061 sint ceilY1 = (sint) ceilf(v1.y);
1062 sint height;
1063 float deltaX, deltaY;
1064 float fInverseSlope;
1065 sint iInverseSlope, iPosX;
1067 // check whether this segment gives a contribution to the final poly
1068 height = (sint) (ceilf(v2.y) - ceilY1);
1069 if (height <= 0) return;
1071 // compute slope
1072 deltaY = v2.y - v1.y;
1073 deltaX = v2.x - v1.x;
1074 fInverseSlope = deltaX / deltaY;
1077 CPolygon2D::TRasterVect::iterator outputIt = outputVect.begin() + (ceilY1 - topY);
1079 // slope with ints
1080 iInverseSlope = (sint) (rol16 * fInverseSlope);
1082 // sub-pixel accuracy
1083 iPosX = (int) (rol16 * (v1.x + fInverseSlope * (ceilY1 - v1.y)));
1085 const CPolygon2D::TRasterVect::iterator endIt = outputIt + height;
1086 if (rightEdge)
1090 outputIt->second = iPosX >> 16;
1091 iPosX += iInverseSlope;
1092 ++outputIt;
1094 while (outputIt != endIt);
1096 else
1098 iPosX += (rol16 - 1);
1101 outputIt->first = iPosX >> 16;
1102 iPosX += iInverseSlope;
1103 ++outputIt;
1105 while (outputIt != endIt);
1110 // *******************************************************************************
1111 // This function alow to cycle forward through a vertex vector like if it was a circular list
1112 static inline CPolygon2D::TVec2fVect::const_iterator Next(const CPolygon2D::TVec2fVect::const_iterator &it, const CPolygon2D::TVec2fVect &cont)
1114 nlassert(cont.size() != 0);
1115 if ((it + 1) == cont.end()) return cont.begin();
1116 return (it + 1);
1120 // *******************************************************************************
1121 // This function alow to cycle backward through a (non null) vertex vector like if it was a circular list
1122 static inline CPolygon2D::TVec2fVect::const_iterator Prev(const CPolygon2D::TVec2fVect::const_iterator &it, const CPolygon2D::TVec2fVect &cont)
1124 nlassert(cont.size() != 0);
1125 if (it == cont.begin()) return cont.end() - 1;
1126 return (it - 1);
1130 // *******************************************************************************
1131 bool CPolygon2D::isCCWOriented() const
1133 const TVec2fVect &V = Vertices;
1134 nlassert(Vertices.size() >= 3);
1135 // compute highest and lowest pos of the poly
1136 float fHighest = V[0].y;
1137 float fLowest = fHighest;
1138 // iterators to the highest and lowest vertex
1139 TVec2fVect::const_iterator it = V.begin() ;
1140 const TVec2fVect::const_iterator endIt = V.end();
1141 TVec2fVect::const_iterator pHighest = V.begin();
1144 if (it->y < fHighest)
1146 fHighest = it->y;
1147 pHighest = it;
1149 fLowest = std::max(fLowest, it->y);
1150 ++it;
1152 while (it != endIt);
1153 // we seek this vertex
1154 TVec2fVect::const_iterator pHighestRight = pHighest;
1155 if (fLowest == fHighest)
1157 // special case : degenerate poly
1158 while (pHighestRight->x == pHighest->x)
1160 pHighestRight = Next(pHighestRight, V);
1161 if (pHighestRight == pHighest) return false; // the poly is reduced to a point, returns an abritrary value
1163 return pHighest->x <= pHighestRight->x;
1165 // iterator to the first vertex that has an y different from the top vertex
1166 while (Next(pHighestRight, V)->y == fHighest)
1168 pHighestRight = Next(pHighestRight, V);
1171 // iterator to the first vertex after pHighestRight, that has the same y than the highest vertex
1172 TVec2fVect::const_iterator pHighestLeft = Next(pHighestRight, V);
1173 // seek the vertex
1174 while (pHighestLeft->y != fHighest)
1176 pHighestLeft = Next(pHighestLeft, V);
1178 TVec2fVect::const_iterator pPrevHighestLeft = Prev(pHighestLeft, V);
1179 // we need to get the orientation of the polygon
1180 // There are 2 case : flat, and non-flat top
1181 // check for flat top
1182 if (pHighestLeft->x != pHighestRight->x)
1184 // compare right and left side
1185 return pHighestLeft->x <= pHighestRight->x;
1187 // The top of the poly is sharp
1188 // We perform a cross product of the 2 highest vect to get its orientation
1189 float deltaXN = Next(pHighestRight, V)->x - pHighestRight->x;
1190 float deltaYN = Next(pHighestRight, V)->y - pHighestRight->y;
1191 float deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
1192 float deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
1193 return (deltaXN * deltaYP - deltaYN * deltaXP) >= 0;
1196 // *******************************************************************************
1197 void CPolygon2D::computeBorders(TRasterVect &borders, sint &highestY) const
1199 #ifdef NL_DEBUG
1200 checkValidBorders();
1201 #endif
1202 // an 'alias' to the vertices
1203 const TVec2fVect &V = Vertices;
1204 if (Vertices.size() < 3)
1206 borders.clear();
1207 return;
1209 bool ccw; // set to true when it has a counter clock wise orientation
1211 // compute highest and lowest pos of the poly
1212 float fHighest = V[0].y;
1213 float fLowest = fHighest;
1215 // iterators to the thighest and lowest vertex
1216 TVec2fVect::const_iterator pLowest = V.begin(), pHighest = V.begin();
1217 TVec2fVect::const_iterator it = V.begin() ;
1218 const TVec2fVect::const_iterator endIt = V.end();
1221 if (it->y > fLowest)
1223 fLowest = it->y;
1224 pLowest = it;
1226 else
1227 if (it->y < fHighest)
1229 fHighest = it->y;
1230 pHighest = it;
1232 ++it;
1234 while (it != endIt);
1237 sint iHighest = (sint) ceilf(fHighest) ;
1238 sint iLowest = (sint) ceilf(fLowest) ;
1240 highestY = iHighest;
1243 /// check poly height, and discard null height
1244 uint polyHeight = iLowest - iHighest;
1245 if (polyHeight <= 0)
1247 borders.clear();
1248 return;
1251 borders.resize(polyHeight);
1253 // iterator to the first vertex that has an y different from the top vertex
1254 TVec2fVect::const_iterator pHighestRight = pHighest;
1255 // we seek this vertex
1256 while (Next(pHighestRight, V)->y == fHighest)
1258 pHighestRight = Next(pHighestRight, V);
1261 // iterator to the first vertex after pHighestRight, that has the same y than the highest vertex
1262 TVec2fVect::const_iterator pHighestLeft = Next(pHighestRight, V);
1263 // seek the vertex
1264 while (pHighestLeft->y != fHighest)
1266 pHighestLeft = Next(pHighestLeft, V);
1269 TVec2fVect::const_iterator pPrevHighestLeft = Prev(pHighestLeft, V);
1271 // we need to get the orientation of the polygon
1272 // There are 2 case : flat, and non-flat top
1274 // check for flat top
1275 if (pHighestLeft->x != pHighestRight->x)
1277 // compare right and left side
1278 if (pHighestLeft->x > pHighestRight->x)
1280 ccw = true; // the list is CCW oriented
1281 std::swap(pHighestLeft, pHighestRight);
1283 else
1285 ccw = false; // the list is CW oriented
1288 else
1290 // The top of the poly is sharp
1291 // We perform a cross product of the 2 highest vect to get its orientation
1293 const float deltaXN = Next(pHighestRight, V)->x - pHighestRight->x;
1294 const float deltaYN = Next(pHighestRight, V)->y - pHighestRight->y;
1295 const float deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
1296 const float deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
1297 if ((deltaXN * deltaYP - deltaYN * deltaXP) < 0)
1299 ccw = true; // the list is CCW oriented
1300 std::swap(pHighestLeft, pHighestRight);
1302 else
1304 ccw = false; // the list is CW oriented
1309 // compute borders
1310 TVec2fVect::const_iterator currV, nextV; // current and next vertex
1311 if (!ccw) // clock wise order ?
1313 currV = pHighestRight ;
1314 // compute right edge from top to bottom
1317 nextV = Next(currV, V);
1318 ScanEdge(borders, iHighest, *currV, *nextV, true);
1319 currV = nextV;
1321 while (currV != pLowest); // repeat until we reach the bottom vertex
1323 // compute left edge from bottom to top
1326 nextV = Next(currV, V);
1327 ScanEdge(borders, iHighest, *nextV, *currV, false);
1328 currV = nextV;
1330 while (currV != pHighestLeft);
1332 else // ccw order
1334 currV = pHighestLeft;
1335 // compute left edge from top to bottom
1338 nextV = Next(currV, V);
1339 ScanEdge(borders, iHighest, *currV, *nextV, false) ;
1340 currV = nextV;
1342 while (currV != pLowest) ;
1344 // compute right edge from bottom to top
1347 nextV = Next(currV, V);
1348 ScanEdge(borders, iHighest, *nextV, *currV, true);
1349 currV = nextV;
1351 while (currV != pHighestRight) ;
1355 //=========================================================================
1356 // scan outer right edge of a poly
1357 static void ScanOuterEdgeRight(CPolygon2D::TRaster *r, float x1, float y1, float x2, float y2, sint minY)
1359 CPolygon2D::TRaster *currRaster;
1360 float deltaX, deltaY;
1361 float inverseSlope;
1362 sint32 iInverseSlope, iposx;
1363 sint height;
1364 deltaX = x2 - x1;
1365 height = (sint) (ceilf(y2) - floorf(y1)) ;
1366 if (height <= 0) return;
1367 if (deltaX >= 0.f)
1369 if (height == 1)
1371 currRaster = r + ((sint) floorf(y1) - minY);
1372 currRaster->second = std::max((sint) floorf(x2), currRaster->second);
1374 else
1376 deltaY = y2 - y1;
1377 if(deltaY)
1378 inverseSlope = deltaX / deltaY;
1379 else
1380 inverseSlope = 0;
1381 iInverseSlope = (sint32) (65536.0 * inverseSlope);
1382 currRaster = r + ((sint) floorf(y1) - minY);
1383 iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
1384 if (ceilf(y1) == y1)
1386 iposx += iInverseSlope;
1390 currRaster->second = std::max((sint) (iposx >> 16), currRaster->second);
1391 iposx += iInverseSlope;
1392 ++ currRaster;
1393 -- height;
1395 while (height != 1);
1396 // correction for last line
1397 currRaster->second = std::max((sint) floorf(x2), currRaster->second);
1400 else
1402 deltaY = y2 - y1;
1403 if(deltaY)
1404 inverseSlope = deltaX / deltaY;
1405 else
1406 inverseSlope = 0;
1407 iInverseSlope = (sint32) (65536.0 * inverseSlope);
1408 currRaster = r + ((sint) floorf(y1) - minY);
1409 currRaster->second = std::max((sint) floorf(x1), currRaster->second);
1410 ++ currRaster;
1411 iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
1412 if (ceilf(y1) == y1)
1414 iposx += iInverseSlope;
1416 while (--height)
1418 currRaster->second = std::max((sint) (iposx >> 16), currRaster->second);
1419 iposx += iInverseSlope;
1420 ++ currRaster;
1425 //=========================================================================
1426 // scan outer left edge of a poly
1427 static void ScanOuterEdgeLeft(CPolygon2D::TRaster *r, float x1, float y1, float x2, float y2, sint minY)
1429 CPolygon2D::TRaster *currRaster;
1430 float deltaX, deltaY;
1431 float inverseSlope;
1432 sint32 iInverseSlope, iposx;
1433 sint height;
1434 deltaX = x2 - x1;
1435 height = (sint) (ceilf(y2) - floorf(y1)) ;
1436 if (height <= 0) return;
1437 if (deltaX < 0.f)
1439 if (height == 1)
1441 currRaster = r + ((sint) floorf(y1) - minY);
1442 currRaster->first = std::min((sint) floorf(x2), currRaster->first);
1444 else
1446 deltaY = y2 - y1;
1447 if(deltaY)
1448 inverseSlope = deltaX / deltaY;
1449 else
1450 inverseSlope = 0;
1451 iInverseSlope = (sint32) (65536.0 * inverseSlope);
1452 currRaster = r + ((sint) floorf(y1) - minY);
1453 iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
1454 if (ceilf(y1) == y1)
1456 iposx += iInverseSlope;
1460 currRaster->first = std::min((sint) (iposx >> 16), currRaster->first);
1461 iposx += iInverseSlope;
1462 ++ currRaster;
1463 -- height;
1465 while (height != 1);
1466 // correction for last line
1467 currRaster->first = std::min((sint) floorf(x2), currRaster->first);
1470 else
1472 deltaY = y2 - y1;
1473 if(deltaY)
1474 inverseSlope = deltaX / deltaY;
1475 else
1476 inverseSlope = 0;
1477 iInverseSlope = (sint32) (65536.0 * inverseSlope);
1478 currRaster = r + ((sint) floorf(y1) - minY);
1479 currRaster->first = std::min((sint) floorf(x1), currRaster->first);
1480 ++ currRaster;
1481 iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
1482 if (ceilf(y1) == y1)
1484 iposx += iInverseSlope;
1486 while (--height)
1488 currRaster->first = std::min((sint) (iposx >> 16), currRaster->first);
1489 iposx += iInverseSlope;
1490 ++ currRaster;
1496 // *******************************************************************************
1497 void CPolygon2D::computeOuterBorders(TRasterVect &borders, sint &minimumY) const
1499 #ifdef NL_DEBUG
1500 checkValidBorders();
1501 #endif
1502 borders.clear();
1503 // NB : this version is not much optimized, because of the min/max test
1504 // during rasterization.
1505 // TODO : optimize if needed ...
1507 if (Vertices.empty())
1509 minimumY = -1;
1510 return;
1512 const CVector2f *first = &Vertices[0];
1513 const CVector2f *last = first + Vertices.size();
1515 const CVector2f *curr = first, *next, *plowest ,*phighest;
1516 const CVector2f *pHighestRight, *pHighestRightNext, *pHighestLeft;
1517 const CVector2f *pPrevHighestLeft;
1518 double deltaXN, deltaYN, deltaXP, deltaYP;
1519 bool ccw; // true if CCW oriented
1520 sint polyHeight;
1521 sint highest, lowest;
1523 float fright = curr->x;
1524 float fleft = curr->x;
1525 float fhighest = curr->y;
1526 float flowest = curr->y;
1527 plowest = phighest = curr;
1529 // compute highest and lowest pos of the poly
1532 fright = std::max(fright, curr->x);
1533 fleft = std::min(fleft, curr->x);
1534 if (curr->y > flowest)
1536 flowest = curr->y;
1537 plowest = curr;
1539 if (curr->y < fhighest)
1541 fhighest = curr->y;
1542 phighest = curr;
1544 ++curr;
1546 while (curr != last);
1549 highest = (sint) floorf(fhighest);
1550 lowest = (sint) floorf(flowest);
1552 polyHeight = lowest - highest + 1;
1553 nlassert(polyHeight > 0);
1555 // make room for rasters
1556 borders.resize(polyHeight);
1557 // fill with xmin / xman
1558 sint ileft = (sint) floorf(fleft);
1559 sint iright = (sint) ceilf(fright);
1560 minimumY = highest;
1561 if (flowest == fhighest) // special case : degenerate poly
1564 borders.resize(1);
1565 borders.front().first = ileft;
1566 borders.front().second = ileft;
1567 return;
1570 for(TRasterVect::iterator it = borders.begin(); it != borders.end(); ++it)
1572 it->second = ileft;
1573 it->first = iright;
1578 pHighestRight = phighest;
1579 for (;;)
1581 pHighestRightNext = pHighestRight + 1;
1582 if (pHighestRightNext == last) pHighestRightNext = first;
1583 if (pHighestRightNext->y != pHighestRight->y) break;
1584 pHighestRight = pHighestRightNext;
1587 pPrevHighestLeft = pHighestRight;
1588 pHighestLeft = pHighestRight;
1589 ++pHighestLeft;
1590 if (pHighestLeft == last) pHighestLeft = first;
1592 while (pHighestLeft->y != fhighest)
1594 pPrevHighestLeft = pHighestLeft;
1595 ++pHighestLeft;
1596 if (pHighestLeft == last) pHighestLeft = first;
1600 // we need to get the orientation of the polygon
1601 // There are 2 case : flat, and non-flat top
1603 // check for flat top
1604 if (pHighestLeft->x != pHighestRight->x)
1606 // compare right and left side
1607 if (pHighestLeft->x > pHighestRight->x)
1609 ccw = true; // the list is CCW oriented
1610 std::swap(pHighestLeft, pHighestRight);
1612 else
1614 ccw = false; // the list is CW oriented
1617 else
1619 pHighestRightNext = pHighestRight + 1;
1620 if (pHighestRightNext == last) pHighestRightNext = first;
1621 deltaXN = pHighestRightNext->x - pHighestRight->x;
1622 deltaYN = pHighestRightNext->y - pHighestRight->y;
1623 deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
1624 deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
1625 if ((deltaXN * deltaYP - deltaYN * deltaXP) < 0)
1627 ccw = true;
1628 std::swap(pHighestLeft, pHighestRight);
1630 else
1632 ccw = false;
1636 if (!ccw)
1638 // clock wise oriented list
1639 curr = pHighestRight;
1642 next = curr + 1;
1643 if (next == last) next = first;
1644 ScanOuterEdgeRight(&borders[0], curr->x, curr->y, next->x, next->y, minimumY);
1645 curr = next;
1647 while (curr != plowest);
1650 next = curr + 1;
1651 if (next == last) next = first;
1652 ScanOuterEdgeLeft(&borders[0], next->x, next->y, curr->x, curr->y, minimumY);
1653 curr = next;
1655 while (curr != pHighestLeft);
1657 else
1659 // ccw oriented
1660 curr = pHighestLeft;
1663 next = curr + 1;
1664 if (next == last) next = first;
1665 ScanOuterEdgeLeft(&borders[0], curr->x, curr->y, next->x, next->y, minimumY);
1666 curr = next;
1668 while (curr != plowest);
1671 next = curr + 1;
1672 if (next == last) next = first;
1673 ScanOuterEdgeRight(&borders[0], next->x, next->y, curr->x, curr->y, minimumY);
1674 curr = next;
1676 while (curr != pHighestRight);
1681 //=========================================================================
1682 // scan inner right edge of a poly
1683 static void ScanInnerEdge(CPolygon2D::TRaster *r, float x1, float y1, float x2, float y2, sint minY, bool rightEdge)
1685 const uint rol16 = 65536;
1686 CPolygon2D::TRaster *currRaster;
1687 float deltaX, deltaY;
1688 float inverseSlope;
1689 sint32 iInverseSlope, iposx;
1690 sint height;
1691 deltaX = x2 - x1;
1692 height = (sint) (ceilf(y2) - floorf(y1));
1693 if (height <= 0) return;
1694 deltaY = y2 - y1;
1695 if(deltaY)
1696 inverseSlope = deltaX / deltaY;
1697 else
1698 inverseSlope = 0;
1699 iInverseSlope = (sint32) (rol16 * inverseSlope);
1700 currRaster = r + ((sint) floorf(y1) - minY);
1702 iposx = (sint32) (rol16 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
1703 if (rightEdge)
1705 iposx -= rol16 - 1;
1706 if (deltaX >= 0.f)
1708 // start of segment
1709 if (floorf(y1) != y1)
1711 currRaster->second = std::min((sint) floorf(x1) - 1, currRaster->second);
1712 ++ currRaster;
1713 -- height;
1714 if (height == 0) return;
1718 currRaster->second = std::min((sint) (iposx >> 16), currRaster->second);
1719 iposx += iInverseSlope;
1720 ++ currRaster;
1722 while (--height);
1724 else
1726 // start of segment
1727 if (floorf(y1) != y1)
1729 currRaster->second = std::min((sint) (iposx >> 16), currRaster->second);
1730 ++ currRaster;
1731 -- height;
1732 if (height == 0) return;
1734 while (--height)
1736 iposx += iInverseSlope;
1737 currRaster->second = std::min((sint) (iposx >> 16), currRaster->second);
1738 ++ currRaster;
1740 // fill bottom of segment
1741 currRaster->second = std::min((sint) floorf(x2) - 1, currRaster->second);
1744 else
1746 iposx += rol16 - 1;
1747 if (deltaX < 0.f)
1749 // start of segment
1750 if (floorf(y1) != y1)
1752 currRaster->first = std::max((sint) ceilf(x1), currRaster->first);
1753 ++ currRaster;
1754 -- height;
1755 if (height == 0) return;
1759 currRaster->first = std::max((sint) (iposx >> 16), currRaster->first);
1760 iposx += iInverseSlope;
1761 ++ currRaster;
1763 while (--height);
1765 else
1767 // start of segment
1768 if (floorf(y1) != y1)
1770 currRaster->first = std::max((sint) (iposx >> 16), currRaster->first);
1771 ++ currRaster;
1772 -- height;
1773 if (height == 0) return;
1775 while (--height)
1777 iposx += iInverseSlope;
1778 currRaster->first = std::max((sint) (iposx >> 16), currRaster->first);
1779 ++ currRaster;
1781 // fill bottom of segment
1782 currRaster->first = std::max((sint) ceilf(x2), currRaster->first);
1787 // *******************************************************************************
1788 void CPolygon2D::computeInnerBorders(TRasterVect &borders, sint &minimumY) const
1790 #ifdef NL_DEBUG
1791 checkValidBorders();
1792 #endif
1793 borders.clear();
1794 if (Vertices.empty())
1796 minimumY = -1;
1797 return;
1799 const CVector2f *first = &Vertices[0];
1800 const CVector2f *last = first + Vertices.size();
1802 const CVector2f *curr = first, *next, *plowest ,*phighest;
1803 const CVector2f *pHighestRight, *pHighestRightNext, *pHighestLeft;
1804 const CVector2f *pPrevHighestLeft;
1805 double deltaXN, deltaYN, deltaXP, deltaYP;
1806 bool ccw; // true if CCW oriented
1807 sint polyHeight;
1808 sint highest, lowest;
1810 float fright = curr->x;
1811 float fleft = curr->x;
1812 float fhighest = curr->y;
1813 float flowest = curr->y;
1814 plowest = phighest = curr;
1816 // compute highest (with lowest y) and lowest (with highest y) points of the poly
1819 fright = std::max(fright, curr->x);
1820 fleft = std::min(fleft, curr->x);
1821 if (curr->y > flowest)
1823 flowest = curr->y;
1824 plowest = curr;
1826 if (curr->y < fhighest)
1828 fhighest = curr->y;
1829 phighest = curr;
1831 ++curr;
1833 while (curr != last);
1834 if (flowest == fhighest)
1836 minimumY = -1;
1837 return;
1839 highest = (sint) floorf(fhighest);
1840 lowest = (sint) ceilf(flowest);
1842 polyHeight = lowest - highest;
1843 minimumY = highest;
1844 if (polyHeight == 0)
1846 minimumY = -1;
1847 return;
1849 // make room for rasters
1850 borders.resize(polyHeight);
1851 // fill with xmin / xman
1852 sint ileft = (sint) floorf(fleft) - 1;
1853 sint iright = (sint) ceilf(fright);
1854 for(TRasterVect::iterator it = borders.begin(); it != borders.end(); ++it)
1856 it->second = iright;
1857 it->first = ileft;
1859 pHighestRight = phighest;
1860 for (;;)
1862 pHighestRightNext = pHighestRight + 1;
1863 if (pHighestRightNext == last) pHighestRightNext = first;
1864 if (pHighestRightNext->y != pHighestRight->y) break;
1865 pHighestRight = pHighestRightNext;
1868 pPrevHighestLeft = pHighestRight;
1869 pHighestLeft = pHighestRight;
1870 ++pHighestLeft;
1871 if (pHighestLeft == last) pHighestLeft = first;
1873 while (pHighestLeft->y != fhighest)
1875 pPrevHighestLeft = pHighestLeft;
1876 ++pHighestLeft;
1877 if (pHighestLeft == last) pHighestLeft = first;
1881 // we need to get the orientation of the polygon
1882 // There are 2 case : flat, and non-flat top
1884 // check for flat top
1885 if (pHighestLeft->x != pHighestRight->x)
1887 // compare right and left side
1888 if (pHighestLeft->x > pHighestRight->x)
1890 ccw = true; // the list is CCW oriented
1891 std::swap(pHighestLeft, pHighestRight);
1893 else
1895 ccw = false; // the list is CW oriented
1898 else
1900 pHighestRightNext = pHighestRight + 1;
1901 if (pHighestRightNext == last) pHighestRightNext = first;
1902 deltaXN = pHighestRightNext->x - pHighestRight->x;
1903 deltaYN = pHighestRightNext->y - pHighestRight->y;
1904 deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
1905 deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
1906 if ((deltaXN * deltaYP - deltaYN * deltaXP) < 0)
1908 ccw = true;
1909 std::swap(pHighestLeft, pHighestRight);
1911 else
1913 ccw = false;
1917 if (!ccw)
1919 // cw oriented
1920 curr = pHighestRight;
1923 next = curr + 1;
1924 if (next == last) next = first;
1925 ScanInnerEdge(&borders[0], curr->x, curr->y, next->x, next->y, minimumY, true);
1926 curr = next;
1928 while (curr != plowest);
1931 next = curr + 1;
1932 if (next == last) next = first;
1933 ScanInnerEdge(&borders[0], next->x, next->y, curr->x, curr->y, minimumY, false);
1934 curr = next;
1936 while (curr != pHighestLeft);
1938 else
1940 // ccw oriented
1941 curr = pHighestLeft;
1944 next = curr + 1;
1945 if (next == last) next = first;
1946 ScanInnerEdge(&borders[0], curr->x, curr->y, next->x, next->y, minimumY, false);
1947 curr = next;
1949 while (curr != plowest);
1952 next = curr + 1;
1953 if (next == last) next = first;
1954 ScanInnerEdge(&borders[0], next->x, next->y, curr->x, curr->y, minimumY, true);
1955 curr = next;
1957 while (curr != pHighestRight);
1959 // fix for top
1960 if (floorf(fhighest) != fhighest)
1962 borders[0].first = 1;
1963 borders[0].second = 0;
1965 // fix for bottom
1966 if (floorf(flowest) != flowest)
1968 borders.back().first = 1;
1969 borders.back().second = 0;
1973 // *******************************************************************************
1974 void CPolygon2D::checkValidBorders() const
1976 for (uint k = 0; k < Vertices.size(); ++k)
1978 nlassert(Vertices[k].x >= -32000.f); // coordinate too big !
1979 nlassert(Vertices[k].x < 32000.f); // coordinate too big !
1980 nlassert(Vertices[k].y >= -32000.f); // coordinate too big !
1981 nlassert(Vertices[k].y < 32000.f); // coordinate too big !
1985 // *******************************************************************************
1986 /// Sum the dot product of this poly vertices against a plane
1987 float CPolygon2D::sumDPAgainstLine(float a, float b, float c) const
1989 float sum = 0.f;
1990 for (uint k = 0; k < Vertices.size(); ++k)
1992 const CVector2f &p = Vertices[k];
1993 sum += a * p.x + b * p.y + c;
1995 return sum;
1999 // *******************************************************************************
2000 bool CPolygon2D::getNonNullSeg(uint &index) const
2002 nlassert(!Vertices.empty());
2003 float bestLength = 0.f;
2004 sint bestIndex = -1;
2005 for (uint k = 0; k < Vertices.size() - 1; ++k)
2007 float norm2 = (Vertices[k + 1] - Vertices[k]).sqrnorm();
2008 if ( norm2 > bestLength)
2010 bestLength = norm2;
2011 bestIndex = (int) k;
2014 float norm2 = (Vertices[Vertices.size() - 1] - Vertices[0]).sqrnorm();
2015 if ( norm2 > bestLength)
2017 index = (uint)Vertices.size() - 1;
2018 return true;
2021 if (bestIndex != -1)
2023 index = bestIndex;
2024 return true;
2026 else
2028 return false;
2033 // *******************************************************************************
2034 void CPolygon2D::getLineEquation(uint index, float &a, float &b, float &c) const
2036 nlassert(index < Vertices.size());
2037 const CVector2f &v0 = getSegRef0(index);
2038 const CVector2f &v1 = getSegRef1(index);
2040 NLMISC::CVector2f seg = v0 - v1;
2041 a = seg.y;
2042 b = - seg.x;
2043 c = - v0.x * a - v0.y * b;
2046 // *******************************************************************************
2047 bool CPolygon2D::intersect(const CPolygon2D &other) const
2049 nlassert(!other.Vertices.empty());
2050 uint nonNullSegIndex;
2051 /// get the orientation of this poly
2052 if (getNonNullSeg(nonNullSegIndex))
2054 float a0, b0, c0; /// contains the seg 2d equation
2055 getLineEquation(nonNullSegIndex, a0, b0, c0);
2056 float orient = sumDPAgainstLine(a0, b0, c0);
2058 for (uint k = 0; k < Vertices.size(); ++k)
2060 /// don't check against a null segment
2061 if ( (getSegRef0(k) - getSegRef1(k)).sqrnorm() == 0.f) continue;
2063 /// get the line equation of the current segment
2064 float a, b, c; /// contains the seg 2d equation
2065 getLineEquation(k, a, b, c);
2066 uint l;
2067 for (l = 0; l < other.Vertices.size(); ++l)
2069 const CVector2f &ov = other.Vertices[l];
2070 if ( orient * (ov.x * a + ov.y * b +c) > 0.f) break;
2072 if (l == other.Vertices.size()) // all point on the outside
2074 return false; // outside
2077 return true;
2079 else // this poly is just a point
2081 return other.contains(Vertices[0]);
2085 // *******************************************************************************
2086 bool CPolygon2D::contains(const CVector2f &p, bool hintIsConvex /*= true*/) const
2088 if (hintIsConvex)
2090 uint numVerts = (uint)Vertices.size();
2091 nlassert(numVerts >= 0.f);
2092 for (uint k = 0; k < numVerts; ++k)
2094 if (getSegRef0(k) != getSegRef1(k))
2096 float a, b, c; /// contains the seg 2d equation
2097 getLineEquation(k, a, b, c);
2098 float orient = a * p.x + b * p.y + c;
2099 for(uint l = k + 1; l < numVerts; ++l)
2101 getLineEquation(l, a, b, c);
2102 float newOrient = a * p.x + b * p.y + c;
2103 if (newOrient * orient < 0.f) return false;
2105 return true;
2108 // the poly reduces to a point
2109 return p == Vertices[0];
2111 else
2113 // concave case
2114 static std::vector<float> xInter;
2115 xInter.clear();
2116 for(uint k = 0; k < Vertices.size(); ++k)
2118 const CVector2f &p0 = getSegRef0(k);
2119 const CVector2f &p1 = getSegRef1(k);
2120 if (p0.y == p1.y)
2122 if (p.y == p0.y)
2124 if ((p.x >= p0.x && p.x <= p1.x)
2125 || (p.x >= p1.x && p.x <= p0.x))
2127 return true;
2131 if ((p.y >= p0.y && p.y < p1.y) ||
2132 (p.y >= p1.y && p.y < p0.y)
2136 float inter = p0.x + (p.y - p0.y) * (p1.x - p0.x) / (p1.y- p0.y);
2137 xInter.push_back(inter);
2140 if (xInter.size() < 2) return false;
2141 std::sort(xInter.begin(), xInter.end());
2142 for(uint k = 0; k < xInter.size() - 1; ++k)
2144 if (p.x >= xInter[k] && p.x <= xInter[k + 1])
2146 return (k & 1) == 0;
2149 return false;
2154 // *******************************************************************************
2155 CPolygon2D::CPolygon2D(const CTriangle &tri, const CMatrix &projMat)
2157 Vertices.resize(3);
2158 NLMISC::CVector proj[3] = { projMat * tri.V0, projMat * tri.V1, projMat * tri.V2 };
2159 Vertices[0].set(proj[0].x, proj[0].y);
2160 Vertices[1].set(proj[1].x, proj[1].y);
2161 Vertices[2].set(proj[2].x, proj[2].y);
2164 // *******************************************************************************
2165 void CPolygon2D::getBoundingRect(CVector2f &minCorner, CVector2f &maxCorner) const
2167 nlassert(!Vertices.empty());
2168 minCorner = maxCorner = Vertices[0];
2169 uint numVertices = (uint)Vertices.size();
2170 for(uint k = 0; k < numVertices; ++k)
2172 minCorner.minof(minCorner, Vertices[k]);
2173 maxCorner.maxof(minCorner, Vertices[k]);
2177 // *******************************************************************************
2178 bool operator ==(const CPolygon2D &lhs,const CPolygon2D &rhs)
2180 if (lhs.Vertices.size() != rhs.Vertices.size()) return false;
2181 return std::equal(lhs.Vertices.begin(), lhs.Vertices.end(), rhs.Vertices.begin());
2184 // *******************************************************************************
2185 bool operator < (const CPolygon2D &lhs, const CPolygon2D &rhs)
2187 if (lhs.Vertices.size() != rhs.Vertices.size()) return lhs.Vertices.size() < rhs.Vertices.size();
2188 for(uint k = 0; k < lhs.Vertices.size(); ++k)
2190 if (lhs.Vertices[k] != rhs.Vertices[k]) return lhs.Vertices[k] < rhs.Vertices[k];
2192 return false;
2196 // *******************************************************************************
2197 static inline bool testSegmentIntersection(const CVector2f &a, const CVector2f &b,
2198 const CVector2f &c, const CVector2f &d)
2200 double denom = a.x * double(d.y - c.y) +
2201 b.x * double(c.y - d.y) +
2202 d.x * double(b.y - a.y) +
2203 c.x * double(a.y - b.y);
2205 if (denom == 0) return false;
2207 double num = a.x * double(d.y - c.y) +
2208 c.x * double(a.y - d.y) +
2209 d.x * double(c.y - a.y);
2211 if (num == 0 || (num == denom)) return false;
2212 double lambda = num / denom;
2213 if (lambda <= 0 || lambda >= 1) return false;
2215 num = - (a.x * double(c.y - b.y) +
2216 b.x * double(a.y - c.y) +
2217 c.x * double(b.y - a.y));
2219 if (num == 0 || (num == denom)) return false;
2220 lambda = num / denom;
2221 if (lambda <= 0 || lambda >= 1) return false;
2222 return true;
2226 // *******************************************************************************
2227 bool CPolygon2D::selfIntersect() const
2229 if (Vertices.size() < 3) return false;
2230 uint numEdges = (uint)Vertices.size();
2231 for(uint k = 0; k < numEdges; ++k)
2233 // test intersection with all other edges that don't share a vertex with this one
2234 const CVector2f &p0 = getSegRef0(k);
2235 const CVector2f &p1 = getSegRef1(k);
2236 for(uint l = 0; l < k; ++l)
2238 const CVector2f &v0 = getSegRef0(l);
2239 const CVector2f &v1 = getSegRef1(l);
2240 if (v0 == p0 || v0 == p1 || v1 == p0 || v1 == p1) continue;
2242 if (testSegmentIntersection(p0, p1, v0, v1)) return true;
2245 return false;
2250 } // NLMISC