khanat-opennel-code/code/nel/src/misc/polygon.cpp
2016-12-19 13:15:25 +01:00

2271 lines
55 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdmisc.h"
#include "nel/misc/polygon.h"
#include "nel/misc/plane.h"
#include "nel/misc/triangle.h"
using namespace std;
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLMISC
{
//==================================//
// CPolygon implementation //
//==================================//
// ***************************************************************************
CPolygon::CPolygon(const CVector &a, const CVector &b, const CVector &c)
{
Vertices.reserve(3);
Vertices.push_back(a);
Vertices.push_back(b);
Vertices.push_back(c);
}
// ***************************************************************************
void CPolygon::toTriFan(std::vector<NLMISC::CTriangle> &dest) const
{
sint count = (sint) Vertices.size() - 2;
for(sint k = 0; k < count; ++k)
{
dest.push_back(CTriangle(Vertices[0], Vertices[k + 1], Vertices[k + 2]));
}
}
// ***************************************************************************
float CPolygon::computeArea() const
{
float area = 0.f;
sint numVerts = (sint) Vertices.size();
for(sint k = 0; k < numVerts - 2; ++k)
{
CVector v0 = Vertices[k + 1] - Vertices[0];
CVector v1 = Vertices[k + 2] - Vertices[0];
area += (v0 ^ v1).norm();
}
return 0.5f * fabsf(area);
}
// ***************************************************************************
void CPolygon::clip(const CPlane *planes, uint nPlanes)
{
if(nPlanes==0 || getNumVertices()==0)
return;
// The final polygon has at maximum currentVertices+number of clipping planes.
// For performance, the vectors are static, so reallocation rarely occurs.
static vector<CVector> tab0, tab1;
tab0.resize(getNumVertices()+nPlanes);
tab1.resize(getNumVertices()+nPlanes);
// Init tab0 with Vertices.
copy(Vertices.begin(), Vertices.end(), tab0.begin());
CVector *in=&(*tab0.begin()), *out= &(*tab1.begin());
sint nin= getNumVertices(), nout;
for(sint i=0;i<(sint)nPlanes;i++)
{
nout= planes[i].clipPolygonBack(in, out, nin);
swap(in, out);
nin= nout;
if(nin==0)
break;
}
// Final result in "in".
Vertices.resize(nin);
if(nin>0)
{
memcpy(&(*Vertices.begin()), in, nin*sizeof(CVector));
}
}
// ***************************************************************************
void CPolygon::clip(const std::vector<CPlane> &planes)
{
if(planes.empty())
return;
clip(&(*planes.begin()), (uint)planes.size());
}
// ***************************************************************************
void CPolygon::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
f.serialVersion(0);
f.serialCont(Vertices);
}
// ***************************************************************************
void CPolygon::getBestTriplet(uint &index0,uint &index1,uint &index2)
{
nlassert(Vertices.size() >= 3);
uint i, j, k;
float bestArea = 0.f;
const uint numVerts = (uint)Vertices.size();
for (i = 0; i < numVerts; ++i)
{
for (j = 0; j < numVerts; ++j)
{
if (i != j)
{
for (k = 0; k < numVerts; ++k)
{
if (k != i && k != j)
{
CVector v0 = Vertices[j] - Vertices[i];
CVector v1 = Vertices[k] - Vertices[i];
float area = (v0 ^ v1).norm();
if (area > bestArea)
{
bestArea = area;
index0 = i;
index1 = j;
index2 = k;
}
}
}
}
}
}
}
// ***************************************************************************
void CPolygon::buildBasis(CMatrix &dest)
{
nlassert(Vertices.size() > 3);
uint i1, i2, i3;
getBestTriplet(i1, i2, i3);
CVector v1 = (Vertices[i2] - Vertices[i1]).normed();
CVector v2 = (Vertices[i3] - Vertices[i1]).normed();
CVector K = v2 ^ v1;
CVector I = v1 - (v1 * K) * v1;
CVector J = K ^ I;
dest.setRot(I, J, K);
dest.setPos(Vertices[i1]);
}
// ***************************************************************************
class CConcavePolygonsVertexDesc
{
public:
CConcavePolygonsVertexDesc (float length, uint index)
{
Length = length;
Index = index;
}
// Length > 0
float Length;
// First vertex index
uint Index;
};
typedef std::map<float, CConcavePolygonsVertexDesc> TCConcavePolygonsVertexMap;
// ***************************************************************************
bool CPolygon::toConvexPolygonsEdgeIntersect (const CVector2f& a0, const CVector2f& a1, const CVector2f& b0, const CVector2f& b1)
{
// both vertical?
if( a0.x-a1.x==0 && b0.x-b1.x==0 )
return false;
// compute intersection of both lines
CVector2f intersection;
// first edge vertical?
if(a0.x - a1.x==0)
{
float Ab = (b0.y - b1.y) / (b0.x - b1.x);
// Intersection
intersection.x = a0.x;
intersection.y = b0.y + (a0.x-b0.x) * Ab;
}
// second edge vertical?
else if(b0.x - b1.x==0)
{
float Aa = (a0.y - a1.y) / (a0.x - a1.x);
// Intersection
intersection.x = b0.x;
intersection.y = a0.y + (b0.x-a0.x) * Aa;
}
// standard case
else
{
float Aa = (a0.y - a1.y) / (a0.x - a1.x);
float Ba = a0.y - a0.x * Aa;
float Ab = (b0.y - b1.y) / (b0.x - b1.x);
float Bb = b0.y - b0.x * Ab;
// colinear?
if(Aa==Ab)
return false;
// Intersection
intersection.x = (Bb - Ba) / (Aa - Ab);
intersection.y = Aa * intersection.x + Ba;
}
// In it ?
return ( ( (a0-intersection)*(a1-intersection) < 0 ) && ( (b0-intersection)*(b1-intersection) < 0 ) );
}
// ***************************************************************************
class CBSPNode2v
{
public:
CBSPNode2v ()
{
Back = NULL;
Front = NULL;
}
CBSPNode2v ( const CPlane &plane, CVector p0, CVector p1, uint v0, uint v1 ) : Plane (plane), P0 (p0), P1 (p1)
{
Back = NULL;
Front = NULL;
Parent = NULL;
V0 = v0;
V1 = v1;
}
~CBSPNode2v ()
{
if (Front)
delete Front;
if (Back)
delete Back;
}
void insert (CBSPNode2v *node)
{
// Front ?
bool p0Front = (Plane * node->P0) > 0;
bool p1Front = (Plane * node->P1) > 0;
if (p0Front && p1Front)
{
// Front child ?
if (Front)
Front->insert (node);
else
{
// Link left
Front = node;
node->Parent = this;
}
}
else if ((!p0Front) && (!p1Front))
{
// Back child ?
if (Back)
Back->insert (node);
else
{
// Link left
Back = node;
node->Parent = this;
}
}
else
{
// Split vertex
CVector newVertex = Plane.intersect (node->P0, node->P1);
// New node
CBSPNode2v *newNode = new CBSPNode2v (node->Plane, node->P0, newVertex, node->V0, node->V1);
// Old node
node->P0 = newVertex;
// Insert child
CBSPNode2v **p0Parent;
CBSPNode2v **p1Parent;
// Get insertion pointer
if (p0Front)
{
p0Parent = &Front;
p1Parent = &Back;
}
else
{
p0Parent = &Back;
p1Parent = &Front;
}
// Insert children
if (*p0Parent)
{
(*p0Parent)->insert (newNode);
}
else
{
*p0Parent = newNode;
newNode->Parent = this;
}
// Insert children
if (*p1Parent)
{
(*p1Parent)->insert (node);
}
else
{
*p1Parent = node;
node->Parent = this;
}
}
}
bool intersect (const CVector &p0, const CVector &p1, uint v0, uint v1) const
{
// Front ?
bool p0Front = (Plane * p0) > 0;
bool p1Front = (Plane * p1) > 0;
if (p0Front != p1Front)
if ( (v0 != V0) && (v0 != V1) && (v1 != V0) && (v1 != V1) )
if (CPolygon::toConvexPolygonsEdgeIntersect ((CVector2f) P0, (CVector2f) P1, (CVector2f) p0, (CVector2f) p1))
return true;
if (p0Front || p1Front)
{
if (Front)
if (Front->intersect (p0, p1, v0, v1))
return true;
}
if ((!p0Front) || (!p1Front))
{
if (Back)
if (Back->intersect (p0, p1, v0, v1))
return true;
}
return false;
}
CBSPNode2v *Back, *Front, *Parent;
CPlane Plane;
CVector P0;
CVector P1;
uint V0;
uint V1;
};
// ***************************************************************************
bool CPolygon::toConvexPolygonsLeft (const std::vector<CVector> &vertex, uint a, uint b, uint c)
{
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;
}
// ***************************************************************************
bool CPolygon::toConvexPolygonsLeftOn (const std::vector<CVector> &vertex, uint a, uint b, uint c)
{
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;
}
// ***************************************************************************
bool CPolygon::toConvexPolygonsInCone (const std::vector<CVector> &vertex, uint a, uint b)
{
// Prev and next
uint a0 = a+1;
if (a0==vertex.size())
a0=0;
uint a1;
if (a==0)
a1= (uint)vertex.size()-1;
else
a1= a-1;
if (toConvexPolygonsLeftOn (vertex, a, a1, a0) )
{
return toConvexPolygonsLeft ( vertex, a, b, a0) && toConvexPolygonsLeft ( vertex, b, a, a1);
}
else
{
return !( toConvexPolygonsLeft ( vertex, a, b, a1) && toConvexPolygonsLeft ( vertex, b, a, a0) );
}
}
// ***************************************************************************
bool CPolygon::toConvexPolygonsDiagonal (const std::vector<CVector> &vertex, const CBSPNode2v &bsp, uint a, uint b)
{
// Check it is a border
if ( ( (b - a) == 1) || ( (a - b) == 1) || ( (a==0) && (b ==(vertex.size()-1))) || ( (b==0) && (a ==(vertex.size()-1))) )
return true;
// Check visibility
if (toConvexPolygonsInCone (vertex, a, b) && toConvexPolygonsInCone (vertex, b, a))
{
// Intersection ?
return !bsp.intersect (vertex[a], vertex[b], a, b);
}
return false;
}
// ***************************************************************************
void CPolygon::toConvexPolygonsLocalAndBSP (std::vector<CVector> &localVertices, CBSPNode2v &root, const CMatrix &basis) const
{
// Invert matrix
CMatrix invert = basis;
invert.invert ();
// Insert vertices in an ordered table
uint vertexCount = (uint)Vertices.size();
TCConcavePolygonsVertexMap vertexMap;
localVertices.resize (vertexCount);
uint i, j;
// Transform the vertex
for (i=0; i<vertexCount; i++)
{
CVector local = invert*Vertices[i];
localVertices[i] = CVector (local.x, local.y, 0);
}
// Plane direction
i=0;
j=(uint)Vertices.size()-1;
CVector normal = localVertices[i] - localVertices[j];
normal = normal ^ CVector::K;
CPlane clipPlane;
clipPlane.make(normal, localVertices[i]);
// Build the BSP root
root = CBSPNode2v (clipPlane, localVertices[i], localVertices[j], i, j);
// Insert all others edges
j=i++;
for (; i<Vertices.size(); i++)
{
// Plane direction
normal = localVertices[i] - localVertices[j];
normal = normal ^ CVector::K;
clipPlane.make(normal, localVertices[i]);
// Build the BSP root
root.insert ( new CBSPNode2v (clipPlane, localVertices[i], localVertices[j], i, j) );
j=i;
}
}
// ***************************************************************************
bool CPolygon::toConvexPolygons (std::list<CPolygon>& outputPolygons, const CMatrix& basis) const
{
// Some vertices ?
if (Vertices.size()>2)
{
// Local vertices
std::vector<CVector> localVertices;
// Build the BSP root
CBSPNode2v root;
// Build the local array and the BSP
toConvexPolygonsLocalAndBSP (localVertices, root, basis);
// Build a vertex list
std::list<uint> vertexList;
uint i;
for (i=0; i<Vertices.size(); i++)
vertexList.push_back (i);
// Clip ears while there is some polygons
std::list<uint>::iterator current=vertexList.begin();
std::list<uint>::iterator begin=vertexList.begin();
do
{
again:;
// Search for a diagonal
bool found = false;
// Get next vertex
std::list<uint>::iterator first = current;
std::list<uint>::iterator lastPreviousPrevious=current;
std::list<uint>::iterator lastPrevious=current;
lastPrevious++;
if (lastPrevious==vertexList.end())
lastPrevious = vertexList.begin();
std::list<uint>::iterator currentNext = lastPrevious;
std::list<uint>::iterator last = lastPrevious;
last++;
if (last==vertexList.end())
last = vertexList.begin();
while (last != current)
{
// Is a diagonal ?
if (
(toConvexPolygonsDiagonal (localVertices, root, *lastPreviousPrevious, *last)) &&
(toConvexPolygonsDiagonal (localVertices, root, *currentNext, *last)) &&
(toConvexPolygonsDiagonal (localVertices, root, *last, *current))
)
{
// Find one
found = true;
}
else
{
// Come back
last = lastPrevious;
lastPrevious = lastPreviousPrevious;
break;
}
// Next vertex
lastPreviousPrevious = lastPrevious;
lastPrevious = last++;
if (last==vertexList.end())
last = vertexList.begin();
}
// Last polygon ?
if (last==current)
{
// Add a polygon
outputPolygons.push_back (CPolygon());
CPolygon &back = outputPolygons.back ();
back.Vertices.reserve (vertexList.size());
// Add each vertex in the new polygon
current=vertexList.begin();
while (current!=vertexList.end())
{
back.Vertices.push_back (Vertices[*current]);
current++;
}
// Exit
return true;
}
else
{
std::list<uint>::iterator firstNext = current;
std::list<uint>::iterator firstNextNext = currentNext;
if (first != vertexList.begin())
first--;
else
{
first = vertexList.end();
first--;
}
while (current != first)
{
// Is a diagonal ?
if (
(toConvexPolygonsDiagonal (localVertices, root, *firstNextNext, *first)) &&
(toConvexPolygonsDiagonal (localVertices, root, *lastPrevious, *first)) &&
(toConvexPolygonsDiagonal (localVertices, root, *last, *first))
)
{
// Find one
found = true;
}
else
{
// Come back
first = firstNext;
break;
}
// Next vertex
firstNextNext = firstNext;
firstNext = first;
if (first==vertexList.begin())
{
first = vertexList.end();
first--;
}
else
first--;
}
}
// Found ?
if (found)
{
// Count vertex
outputPolygons.push_back (CPolygon());
CPolygon &back = outputPolygons.back ();
// Vertex count
uint vertexCount = 1;
current = first;
while (current != last)
{
vertexCount++;
current++;
if (current == vertexList.end())
current = vertexList.begin();
}
// Alloc vertices
back.Vertices.reserve (vertexCount);
// Copy and remove vertices
back.Vertices.push_back (Vertices[*first]);
first++;
if (first == vertexList.end())
first = vertexList.begin();
while (first != last)
{
back.Vertices.push_back (Vertices[*first]);
// Remove from list
first = vertexList.erase (first);
if (first == vertexList.end())
first = vertexList.begin();
nlassert (first != vertexList.end());
}
back.Vertices.push_back (Vertices[*first]);
current = begin = last;
goto again;
}
// Next current
current++;
if (current == vertexList.end())
current = vertexList.begin ();
}
while (current != begin);
}
return false;
}
// ***************************************************************************
bool CPolygon::chain (const std::vector<CPolygon> &other, const CMatrix& basis)
{
// Local vertices
std::vector<CVector> localVertices;
// Build the BSP root
CBSPNode2v root;
// Build the local array and the BSP
toConvexPolygonsLocalAndBSP (localVertices, root, basis);
// Local vertices
std::vector<std::vector<CVector> > localVerticesOther (other.size());
// Build the BSP root
std::vector<CBSPNode2v> rootOther (other.size());
// Build a copy of the polygons
std::vector<CPolygon> copy = other;
// Main copy
CPolygon mainCopy = *this;
// For each other polygons
uint o;
for (o=0; o<other.size(); o++)
{
// Build the local array and the BSP
other[o].toConvexPolygonsLocalAndBSP (localVerticesOther[o], rootOther[o], basis);
}
// Look for a couple..
uint thisCount = (uint)Vertices.size();
uint i, j;
for (o=0; o<other.size(); o++)
{
uint otherCount = (uint)other[o].Vertices.size();
// Try to link in the main polygon
for (i=0; i<thisCount; i++)
{
for (j=0; j<otherCount; j++)
{
// Test this segement
if (!root.intersect (localVertices[i], localVerticesOther[o][j], i, 0xffffffff))
{
// Test each other polygons
uint otherO;
for (otherO=0; otherO<other.size(); otherO++)
{
// Intersect ?
if (rootOther[otherO].intersect (localVertices[i], localVerticesOther[o][j], 0xffffffff, (otherO == o)?j:0xffffffff))
break;
}
// Continue ?
if (otherO==other.size())
{
// Insert new vertices
mainCopy.Vertices.insert (mainCopy.Vertices.begin()+i, 2+otherCount, CVector());
// Copy the first vertex
mainCopy.Vertices[i] = mainCopy.Vertices[i+otherCount+2];
// Copy the new vertices
uint k;
for (k=0; k<otherCount; k++)
{
uint index = j+k;
if (index>=otherCount)
index -= otherCount;
mainCopy.Vertices[i+k+1] = copy[o].Vertices[index];
}
// Copy the last one
mainCopy.Vertices[i+otherCount+1] = copy[o].Vertices[j];
break;
}
}
}
if (j!=otherCount)
break;
}
// Not found ?
if (i==thisCount)
{
// Try to link in the sub polygons
uint otherToCheck;
for (otherToCheck=o+1; otherToCheck<other.size(); otherToCheck++)
{
uint otherToCheckCount = (uint)other[otherToCheck].Vertices.size();
for (i=0; i<otherToCheckCount; i++)
{
for (j=0; j<otherCount; j++)
{
// Test this segement
if (!rootOther[otherToCheck].intersect (localVerticesOther[otherToCheck][i], localVerticesOther[o][j], i, 0xffffffff))
{
// Test each other polygons
uint otherO;
for (otherO=0; otherO<other.size(); otherO++)
{
// Intersect ?
if (rootOther[otherO].intersect (localVerticesOther[otherToCheck][i], localVerticesOther[o][j], (otherToCheck == otherO)?i:0xffffffff, (otherO == o)?j:0xffffffff))
break;
}
// Continue ?
if (otherO==other.size())
{
// Insert new vertices
copy[otherToCheck].Vertices.insert (copy[otherToCheck].Vertices.begin()+i, 2+otherCount, CVector());
// Copy the first vertex
copy[otherToCheck].Vertices[i] = copy[otherToCheck].Vertices[i+otherCount+2];
// Copy the new vertices
uint k;
for (k=0; k<otherCount; k++)
{
uint index = j+k;
if (index>=otherCount)
index -= otherCount;
copy[otherToCheck].Vertices[i+k+1] = copy[otherO].Vertices[index];
}
// Copy the last one
copy[otherToCheck].Vertices[i+otherCount+1] = copy[otherO].Vertices[j];
break;
}
}
}
if (j!=otherCount)
break;
}
if (i!=otherToCheckCount)
break;
}
if (otherToCheck==other.size())
{
// Not ok
return false;
}
}
}
// Ok
*this = mainCopy;
return true;
}
// ***************************************************************************
//====================================//
// CPolygon2d implementation //
//====================================//
// ***************************************************************************
CPolygon2D::CPolygon2D(const CPolygon &src, const CMatrix &projMat)
{
fromPolygon(src, projMat);
}
// ***************************************************************************
void CPolygon2D::fromPolygon(const CPolygon &src, const CMatrix &projMat /*=CMatrix::Identity*/)
{
uint size = (uint)src.Vertices.size();
Vertices.resize(size);
for (uint k = 0; k < size; ++k)
{
CVector proj = projMat * src.Vertices[k];
Vertices[k].set(proj.x, proj.y);
}
}
// ***************************************************************************
bool CPolygon2D::isConvex()
{
bool Front = true, Back = false;
// we apply a dummy algo for now : check whether every vertex is in the same side
// of every plane defined by a segment of this poly
uint numVerts = (uint)Vertices.size();
if (numVerts < 3) return true;
CVector segStart, segEnd;
CPlane clipPlane;
for (TVec2fVect::const_iterator it = Vertices.begin(); it != Vertices.end(); ++it)
{
segStart.set(it->x, it->y, 0); // segment start
segEnd.set((it + 1)->x, (it + 1)->y, 0); // segment end
float n = (segStart - segEnd).norm(); // segment norm
if (n != 0)
{
clipPlane.make(segStart, segEnd, (n > 10 ? n : 10) * CVector::K + segStart); // make a plane, with this segment and the poly normal
// check each other vertices against this plane
for (TVec2fVect::const_iterator it2 = Vertices.begin(); it2 != Vertices.end(); ++it2)
{
if (it2 != it && it2 != (it + 1)) // the vertices must not be part of the test plane (because of imprecision)
{
float dist = clipPlane * CVector(it2->x, it2-> y, 0);
if (dist != 0) // midlle pos
{
if (dist > 0) Front = true; else Back = true;
if (Front && Back) return false; // there are both front end back vertices -> failure
}
}
}
}
}
return true;
}
// ***************************************************************************
void CPolygon2D::buildConvexHull(CPolygon2D &dest) const
{
nlassert(&dest != this);
if (this->Vertices.size() == 3) // with 3 points it is always convex
{
dest = *this;
return;
}
uint k, l;
uint numVerts = (uint)Vertices.size();
CVector2f p, curr, prev;
uint pIndex, p1Index, p2Index, pCurr, pPrev;
// this is not optimized, but not used in realtime.. =)
nlassert(numVerts >= 3);
dest.Vertices.clear();
typedef std::set<uint> TIndexSet;
TIndexSet leftIndex;
for (k = 0; k < Vertices.size(); ++k)
{
leftIndex.insert(k);
}
// 1) find the highest point p of the set. We are sure it belongs to the hull
pIndex = 0;
p = Vertices[0];
for (k = 1; k < numVerts; ++k)
{
if (Vertices[k].y < p.y)
{
pIndex = k;
p = Vertices[k];
}
}
leftIndex.erase(pIndex);
float bestCP = 1.1f;
p1Index = p2Index = pIndex;
for (k = 0; k < numVerts; ++k)
{
if (k != pIndex)
{
for (l = 0; l < numVerts; ++l)
{
if (l != pIndex && l != k)
{
CVector2f seg1 = (Vertices[l] - p).normed();
CVector2f seg2 = (Vertices[k] - p).normed();
//CVector cp = CVector(seg1.x, seg1.y, 0) ^ CVector(seg2.x, seg2.y, 0);
//float n = fabsf(cp.z);
float n = seg1 * seg2;
if (n < bestCP)
{
p1Index = l;
p2Index = k;
bestCP = n;
}
}
}
}
}
leftIndex.erase(p2Index);
// start from the given triplet, and complete the poly until we reach the first point
pCurr = p2Index;
pPrev = pIndex;
curr = Vertices[pCurr];
prev = Vertices[pPrev];
// create the first triplet vertices
dest.Vertices.push_back(Vertices[p1Index]);
dest.Vertices.push_back(prev);
dest.Vertices.push_back(curr);
uint step = 0;
for(;;)
{
bestCP = 1.1f;
CVector2f seg2 = (prev - curr).normed();
TIndexSet::iterator bestIt = leftIndex.end();
for (TIndexSet::iterator it = leftIndex.begin(); it != leftIndex.end(); ++it)
{
if (step == 0 && *it == p1Index) continue;
CVector2f seg1 = (Vertices[*it] - curr).normed();
float n = seg1 * seg2;
if (n < bestCP)
{
bestCP = n;
bestIt = it;
}
}
nlassert(bestIt != leftIndex.end());
if (*bestIt == p1Index)
{
return; // if we reach the start point we have finished
}
prev = curr;
curr = Vertices[*bestIt];
pPrev = pCurr;
pCurr = *bestIt;
// add new point to the destination
dest.Vertices.push_back(curr);
++step;
leftIndex.erase(bestIt);
}
}
// ***************************************************************************
void CPolygon2D::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
(void)f.serialVersion(0);
f.serialCont(Vertices);
}
// ***************************************************************************
/// get the best triplet of vector. e.g the triplet that has the best surface
void CPolygon2D::getBestTriplet(uint &index0, uint &index1, uint &index2)
{
nlassert(Vertices.size() >= 3);
uint i, j, k;
float bestArea = 0.f;
const uint numVerts = (uint)Vertices.size();
for (i = 0; i < numVerts; ++i)
{
for (j = 0; j < numVerts; ++j)
{
if (i != j)
{
for (k = 0; k < numVerts; ++k)
{
if (k != i && k != j)
{
CVector2f v0 = Vertices[j] - Vertices[i];
CVector2f v1 = Vertices[k] - Vertices[i];
float area = fabsf((CVector(v0.x, v0.y, 0) ^ CVector(v1.x, v1.y, 0)).norm());
if (area > bestArea)
{
bestArea = area;
index0 = i;
index1 = j;
index2 = k;
}
}
}
}
}
}
}
/// ***************************************************************************************
// scan a an edge of a poly and write it into a table
static void ScanEdge(CPolygon2D::TRasterVect &outputVect, sint topY, const CVector2f &v1, const CVector2f &v2, bool rightEdge = true)
{
const uint rol16 = 65536;
sint ceilY1 = (sint) ceilf(v1.y);
sint height;
float deltaX, deltaY;
float fInverseSlope;
sint iInverseSlope, iPosX;
// check whether this segment gives a contribution to the final poly
height = (sint) (ceilf(v2.y) - ceilY1);
if (height <= 0) return;
// compute slope
deltaY = v2.y - v1.y;
deltaX = v2.x - v1.x;
fInverseSlope = deltaX / deltaY;
CPolygon2D::TRasterVect::iterator outputIt = outputVect.begin() + (ceilY1 - topY);
// slope with ints
iInverseSlope = (sint) (rol16 * fInverseSlope);
// sub-pixel accuracy
iPosX = (int) (rol16 * (v1.x + fInverseSlope * (ceilY1 - v1.y)));
const CPolygon2D::TRasterVect::iterator endIt = outputIt + height;
if (rightEdge)
{
do
{
outputIt->second = iPosX >> 16;
iPosX += iInverseSlope;
++outputIt;
}
while (outputIt != endIt);
}
else
{
iPosX += (rol16 - 1);
do
{
outputIt->first = iPosX >> 16;
iPosX += iInverseSlope;
++outputIt;
}
while (outputIt != endIt);
}
}
// *******************************************************************************
// This function alow to cycle forward through a vertex vector like if it was a circular list
static inline CPolygon2D::TVec2fVect::const_iterator Next(const CPolygon2D::TVec2fVect::const_iterator &it, const CPolygon2D::TVec2fVect &cont)
{
nlassert(cont.size() != 0);
if ((it + 1) == cont.end()) return cont.begin();
return (it + 1);
}
// *******************************************************************************
// This function alow to cycle backward through a (non null) vertex vector like if it was a circular list
static inline CPolygon2D::TVec2fVect::const_iterator Prev(const CPolygon2D::TVec2fVect::const_iterator &it, const CPolygon2D::TVec2fVect &cont)
{
nlassert(cont.size() != 0);
if (it == cont.begin()) return cont.end() - 1;
return (it - 1);
}
// *******************************************************************************
bool CPolygon2D::isCCWOriented() const
{
const TVec2fVect &V = Vertices;
nlassert(Vertices.size() >= 3);
// compute highest and lowest pos of the poly
float fHighest = V[0].y;
float fLowest = fHighest;
// iterators to the highest and lowest vertex
TVec2fVect::const_iterator it = V.begin() ;
const TVec2fVect::const_iterator endIt = V.end();
TVec2fVect::const_iterator pHighest = V.begin();
do
{
if (it->y < fHighest)
{
fHighest = it->y;
pHighest = it;
}
fLowest = std::max(fLowest, it->y);
++it;
}
while (it != endIt);
// we seek this vertex
TVec2fVect::const_iterator pHighestRight = pHighest;
if (fLowest == fHighest)
{
// special case : degenerate poly
while (pHighestRight->x == pHighest->x)
{
pHighestRight = Next(pHighestRight, V);
if (pHighestRight == pHighest) return false; // the poly is reduced to a point, returns an abritrary value
}
return pHighest->x <= pHighestRight->x;
}
// iterator to the first vertex that has an y different from the top vertex
while (Next(pHighestRight, V)->y == fHighest)
{
pHighestRight = Next(pHighestRight, V);
}
// iterator to the first vertex after pHighestRight, that has the same y than the highest vertex
TVec2fVect::const_iterator pHighestLeft = Next(pHighestRight, V);
// seek the vertex
while (pHighestLeft->y != fHighest)
{
pHighestLeft = Next(pHighestLeft, V);
}
TVec2fVect::const_iterator pPrevHighestLeft = Prev(pHighestLeft, V);
// we need to get the orientation of the polygon
// There are 2 case : flat, and non-flat top
// check for flat top
if (pHighestLeft->x != pHighestRight->x)
{
// compare right and left side
return pHighestLeft->x <= pHighestRight->x;
}
// The top of the poly is sharp
// We perform a cross product of the 2 highest vect to get its orientation
float deltaXN = Next(pHighestRight, V)->x - pHighestRight->x;
float deltaYN = Next(pHighestRight, V)->y - pHighestRight->y;
float deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
float deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
return (deltaXN * deltaYP - deltaYN * deltaXP) >= 0;
}
// *******************************************************************************
void CPolygon2D::computeBorders(TRasterVect &borders, sint &highestY) const
{
#ifdef NL_DEBUG
checkValidBorders();
#endif
// an 'alias' to the vertices
const TVec2fVect &V = Vertices;
if (Vertices.size() < 3)
{
borders.clear();
return;
}
bool ccw; // set to true when it has a counter clock wise orientation
// compute highest and lowest pos of the poly
float fHighest = V[0].y;
float fLowest = fHighest;
// iterators to the thighest and lowest vertex
TVec2fVect::const_iterator pLowest = V.begin(), pHighest = V.begin();
TVec2fVect::const_iterator it = V.begin() ;
const TVec2fVect::const_iterator endIt = V.end();
do
{
if (it->y > fLowest)
{
fLowest = it->y;
pLowest = it;
}
else
if (it->y < fHighest)
{
fHighest = it->y;
pHighest = it;
}
++it;
}
while (it != endIt);
sint iHighest = (sint) ceilf(fHighest) ;
sint iLowest = (sint) ceilf(fLowest) ;
highestY = iHighest;
/// check poly height, and discard null height
uint polyHeight = iLowest - iHighest;
if (polyHeight <= 0)
{
borders.clear();
return;
}
borders.resize(polyHeight);
// iterator to the first vertex that has an y different from the top vertex
TVec2fVect::const_iterator pHighestRight = pHighest;
// we seek this vertex
while (Next(pHighestRight, V)->y == fHighest)
{
pHighestRight = Next(pHighestRight, V);
}
// iterator to the first vertex after pHighestRight, that has the same y than the highest vertex
TVec2fVect::const_iterator pHighestLeft = Next(pHighestRight, V);
// seek the vertex
while (pHighestLeft->y != fHighest)
{
pHighestLeft = Next(pHighestLeft, V);
}
TVec2fVect::const_iterator pPrevHighestLeft = Prev(pHighestLeft, V);
// we need to get the orientation of the polygon
// There are 2 case : flat, and non-flat top
// check for flat top
if (pHighestLeft->x != pHighestRight->x)
{
// compare right and left side
if (pHighestLeft->x > pHighestRight->x)
{
ccw = true; // the list is CCW oriented
std::swap(pHighestLeft, pHighestRight);
}
else
{
ccw = false; // the list is CW oriented
}
}
else
{
// The top of the poly is sharp
// We perform a cross product of the 2 highest vect to get its orientation
const float deltaXN = Next(pHighestRight, V)->x - pHighestRight->x;
const float deltaYN = Next(pHighestRight, V)->y - pHighestRight->y;
const float deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
const float deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
if ((deltaXN * deltaYP - deltaYN * deltaXP) < 0)
{
ccw = true; // the list is CCW oriented
std::swap(pHighestLeft, pHighestRight);
}
else
{
ccw = false; // the list is CW oriented
}
}
// compute borders
TVec2fVect::const_iterator currV, nextV; // current and next vertex
if (!ccw) // clock wise order ?
{
currV = pHighestRight ;
// compute right edge from top to bottom
do
{
nextV = Next(currV, V);
ScanEdge(borders, iHighest, *currV, *nextV, true);
currV = nextV;
}
while (currV != pLowest); // repeat until we reach the bottom vertex
// compute left edge from bottom to top
do
{
nextV = Next(currV, V);
ScanEdge(borders, iHighest, *nextV, *currV, false);
currV = nextV;
}
while (currV != pHighestLeft);
}
else // ccw order
{
currV = pHighestLeft;
// compute left edge from top to bottom
do
{
nextV = Next(currV, V);
ScanEdge(borders, iHighest, *currV, *nextV, false) ;
currV = nextV;
}
while (currV != pLowest) ;
// compute right edge from bottom to top
do
{
nextV = Next(currV, V);
ScanEdge(borders, iHighest, *nextV, *currV, true);
currV = nextV;
}
while (currV != pHighestRight) ;
}
}
//=========================================================================
// scan outer right edge of a poly
static void ScanOuterEdgeRight(CPolygon2D::TRaster *r, float x1, float y1, float x2, float y2, sint minY)
{
CPolygon2D::TRaster *currRaster;
float deltaX, deltaY;
float inverseSlope;
sint32 iInverseSlope, iposx;
sint height;
deltaX = x2 - x1;
height = (sint) (ceilf(y2) - floorf(y1)) ;
if (height <= 0) return;
if (deltaX >= 0.f)
{
if (height == 1)
{
currRaster = r + ((sint) floorf(y1) - minY);
currRaster->second = std::max((sint) floorf(x2), currRaster->second);
}
else
{
deltaY = y2 - y1;
if(deltaY)
inverseSlope = deltaX / deltaY;
else
inverseSlope = 0;
iInverseSlope = (sint32) (65536.0 * inverseSlope);
currRaster = r + ((sint) floorf(y1) - minY);
iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
if (ceilf(y1) == y1)
{
iposx += iInverseSlope;
}
do
{
currRaster->second = std::max((sint) (iposx >> 16), currRaster->second);
iposx += iInverseSlope;
++ currRaster;
-- height;
}
while (height != 1);
// correction for last line
currRaster->second = std::max((sint) floorf(x2), currRaster->second);
}
}
else
{
deltaY = y2 - y1;
if(deltaY)
inverseSlope = deltaX / deltaY;
else
inverseSlope = 0;
iInverseSlope = (sint32) (65536.0 * inverseSlope);
currRaster = r + ((sint) floorf(y1) - minY);
currRaster->second = std::max((sint) floorf(x1), currRaster->second);
++ currRaster;
iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
if (ceilf(y1) == y1)
{
iposx += iInverseSlope;
}
while (--height)
{
currRaster->second = std::max((sint) (iposx >> 16), currRaster->second);
iposx += iInverseSlope;
++ currRaster;
}
}
}
//=========================================================================
// scan outer left edge of a poly
static void ScanOuterEdgeLeft(CPolygon2D::TRaster *r, float x1, float y1, float x2, float y2, sint minY)
{
CPolygon2D::TRaster *currRaster;
float deltaX, deltaY;
float inverseSlope;
sint32 iInverseSlope, iposx;
sint height;
deltaX = x2 - x1;
height = (sint) (ceilf(y2) - floorf(y1)) ;
if (height <= 0) return;
if (deltaX < 0.f)
{
if (height == 1)
{
currRaster = r + ((sint) floorf(y1) - minY);
currRaster->first = std::min((sint) floorf(x2), currRaster->first);
}
else
{
deltaY = y2 - y1;
if(deltaY)
inverseSlope = deltaX / deltaY;
else
inverseSlope = 0;
iInverseSlope = (sint32) (65536.0 * inverseSlope);
currRaster = r + ((sint) floorf(y1) - minY);
iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
if (ceilf(y1) == y1)
{
iposx += iInverseSlope;
}
do
{
currRaster->first = std::min((sint) (iposx >> 16), currRaster->first);
iposx += iInverseSlope;
++ currRaster;
-- height;
}
while (height != 1);
// correction for last line
currRaster->first = std::min((sint) floorf(x2), currRaster->first);
}
}
else
{
deltaY = y2 - y1;
if(deltaY)
inverseSlope = deltaX / deltaY;
else
inverseSlope = 0;
iInverseSlope = (sint32) (65536.0 * inverseSlope);
currRaster = r + ((sint) floorf(y1) - minY);
currRaster->first = std::min((sint) floorf(x1), currRaster->first);
++ currRaster;
iposx = (sint32) (65536.0 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
if (ceilf(y1) == y1)
{
iposx += iInverseSlope;
}
while (--height)
{
currRaster->first = std::min((sint) (iposx >> 16), currRaster->first);
iposx += iInverseSlope;
++ currRaster;
}
}
}
// *******************************************************************************
void CPolygon2D::computeOuterBorders(TRasterVect &borders, sint &minimumY) const
{
#ifdef NL_DEBUG
checkValidBorders();
#endif
borders.clear();
// NB : this version is not much optimized, because of the min/max test
// during rasterization.
// TODO : optimize if needed ...
if (Vertices.empty())
{
minimumY = -1;
return;
}
const CVector2f *first = &Vertices[0];
const CVector2f *last = first + Vertices.size();
const CVector2f *curr = first, *next, *plowest ,*phighest;
const CVector2f *pHighestRight, *pHighestRightNext, *pHighestLeft;
const CVector2f *pPrevHighestLeft;
double deltaXN, deltaYN, deltaXP, deltaYP;
bool ccw; // true if CCW oriented
sint polyHeight;
sint highest, lowest;
float fright = curr->x;
float fleft = curr->x;
float fhighest = curr->y;
float flowest = curr->y;
plowest = phighest = curr;
// compute highest and lowest pos of the poly
do
{
fright = std::max(fright, curr->x);
fleft = std::min(fleft, curr->x);
if (curr->y > flowest)
{
flowest = curr->y;
plowest = curr;
}
if (curr->y < fhighest)
{
fhighest = curr->y;
phighest = curr;
}
++curr;
}
while (curr != last);
highest = (sint) floorf(fhighest);
lowest = (sint) floorf(flowest);
polyHeight = lowest - highest + 1;
nlassert(polyHeight > 0);
// make room for rasters
borders.resize(polyHeight);
// fill with xmin / xman
sint ileft = (sint) floorf(fleft);
sint iright = (sint) ceilf(fright);
minimumY = highest;
if (flowest == fhighest) // special case : degenerate poly
{
borders.resize(1);
borders.front().first = ileft;
borders.front().second = ileft;
return;
}
//
for(TRasterVect::iterator it = borders.begin(); it != borders.end(); ++it)
{
it->second = ileft;
it->first = iright;
}
pHighestRight = phighest;
for (;;)
{
pHighestRightNext = pHighestRight + 1;
if (pHighestRightNext == last) pHighestRightNext = first;
if (pHighestRightNext->y != pHighestRight->y) break;
pHighestRight = pHighestRightNext;
}
pPrevHighestLeft = pHighestRight;
pHighestLeft = pHighestRight;
++pHighestLeft;
if (pHighestLeft == last) pHighestLeft = first;
while (pHighestLeft->y != fhighest)
{
pPrevHighestLeft = pHighestLeft;
++pHighestLeft;
if (pHighestLeft == last) pHighestLeft = first;
}
// we need to get the orientation of the polygon
// There are 2 case : flat, and non-flat top
// check for flat top
if (pHighestLeft->x != pHighestRight->x)
{
// compare right and left side
if (pHighestLeft->x > pHighestRight->x)
{
ccw = true; // the list is CCW oriented
std::swap(pHighestLeft, pHighestRight);
}
else
{
ccw = false; // the list is CW oriented
}
}
else
{
pHighestRightNext = pHighestRight + 1;
if (pHighestRightNext == last) pHighestRightNext = first;
deltaXN = pHighestRightNext->x - pHighestRight->x;
deltaYN = pHighestRightNext->y - pHighestRight->y;
deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
if ((deltaXN * deltaYP - deltaYN * deltaXP) < 0)
{
ccw = true;
std::swap(pHighestLeft, pHighestRight);
}
else
{
ccw = false;
}
}
if (!ccw)
{
// clock wise oriented list
curr = pHighestRight;
do
{
next = curr + 1;
if (next == last) next = first;
ScanOuterEdgeRight(&borders[0], curr->x, curr->y, next->x, next->y, minimumY);
curr = next;
}
while (curr != plowest);
do
{
next = curr + 1;
if (next == last) next = first;
ScanOuterEdgeLeft(&borders[0], next->x, next->y, curr->x, curr->y, minimumY);
curr = next;
}
while (curr != pHighestLeft);
}
else
{
// ccw oriented
curr = pHighestLeft;
do
{
next = curr + 1;
if (next == last) next = first;
ScanOuterEdgeLeft(&borders[0], curr->x, curr->y, next->x, next->y, minimumY);
curr = next;
}
while (curr != plowest);
do
{
next = curr + 1;
if (next == last) next = first;
ScanOuterEdgeRight(&borders[0], next->x, next->y, curr->x, curr->y, minimumY);
curr = next;
}
while (curr != pHighestRight);
}
}
//=========================================================================
// scan inner right edge of a poly
static void ScanInnerEdge(CPolygon2D::TRaster *r, float x1, float y1, float x2, float y2, sint minY, bool rightEdge)
{
const uint rol16 = 65536;
CPolygon2D::TRaster *currRaster;
float deltaX, deltaY;
float inverseSlope;
sint32 iInverseSlope, iposx;
sint height;
deltaX = x2 - x1;
height = (sint) (ceilf(y2) - floorf(y1));
if (height <= 0) return;
deltaY = y2 - y1;
if(deltaY)
inverseSlope = deltaX / deltaY;
else
inverseSlope = 0;
iInverseSlope = (sint32) (rol16 * inverseSlope);
currRaster = r + ((sint) floorf(y1) - minY);
//
iposx = (sint32) (rol16 * (x1 + inverseSlope * (ceilf(y1) - y1))); // sub-pixel accuracy
if (rightEdge)
{
iposx -= rol16 - 1;
if (deltaX >= 0.f)
{
// start of segment
if (floorf(y1) != y1)
{
currRaster->second = std::min((sint) floorf(x1) - 1, currRaster->second);
++ currRaster;
-- height;
if (height == 0) return;
}
do
{
currRaster->second = std::min((sint) (iposx >> 16), currRaster->second);
iposx += iInverseSlope;
++ currRaster;
}
while (--height);
}
else
{
// start of segment
if (floorf(y1) != y1)
{
currRaster->second = std::min((sint) (iposx >> 16), currRaster->second);
++ currRaster;
-- height;
if (height == 0) return;
}
while (--height)
{
iposx += iInverseSlope;
currRaster->second = std::min((sint) (iposx >> 16), currRaster->second);
++ currRaster;
}
// fill bottom of segment
currRaster->second = std::min((sint) floorf(x2) - 1, currRaster->second);
}
}
else
{
iposx += rol16 - 1;
if (deltaX < 0.f)
{
// start of segment
if (floorf(y1) != y1)
{
currRaster->first = std::max((sint) ceilf(x1), currRaster->first);
++ currRaster;
-- height;
if (height == 0) return;
}
do
{
currRaster->first = std::max((sint) (iposx >> 16), currRaster->first);
iposx += iInverseSlope;
++ currRaster;
}
while (--height);
}
else
{
// start of segment
if (floorf(y1) != y1)
{
currRaster->first = std::max((sint) (iposx >> 16), currRaster->first);
++ currRaster;
-- height;
if (height == 0) return;
}
while (--height)
{
iposx += iInverseSlope;
currRaster->first = std::max((sint) (iposx >> 16), currRaster->first);
++ currRaster;
}
// fill bottom of segment
currRaster->first = std::max((sint) ceilf(x2), currRaster->first);
}
}
}
// *******************************************************************************
void CPolygon2D::computeInnerBorders(TRasterVect &borders, sint &minimumY) const
{
#ifdef NL_DEBUG
checkValidBorders();
#endif
borders.clear();
if (Vertices.empty())
{
minimumY = -1;
return;
}
const CVector2f *first = &Vertices[0];
const CVector2f *last = first + Vertices.size();
const CVector2f *curr = first, *next, *plowest ,*phighest;
const CVector2f *pHighestRight, *pHighestRightNext, *pHighestLeft;
const CVector2f *pPrevHighestLeft;
double deltaXN, deltaYN, deltaXP, deltaYP;
bool ccw; // true if CCW oriented
sint polyHeight;
sint highest, lowest;
float fright = curr->x;
float fleft = curr->x;
float fhighest = curr->y;
float flowest = curr->y;
plowest = phighest = curr;
// compute highest (with lowest y) and lowest (with highest y) points of the poly
do
{
fright = std::max(fright, curr->x);
fleft = std::min(fleft, curr->x);
if (curr->y > flowest)
{
flowest = curr->y;
plowest = curr;
}
if (curr->y < fhighest)
{
fhighest = curr->y;
phighest = curr;
}
++curr;
}
while (curr != last);
if (flowest == fhighest)
{
minimumY = -1;
return;
}
highest = (sint) floorf(fhighest);
lowest = (sint) ceilf(flowest);
polyHeight = lowest - highest;
minimumY = highest;
if (polyHeight == 0)
{
minimumY = -1;
return;
}
// make room for rasters
borders.resize(polyHeight);
// fill with xmin / xman
sint ileft = (sint) floorf(fleft) - 1;
sint iright = (sint) ceilf(fright);
for(TRasterVect::iterator it = borders.begin(); it != borders.end(); ++it)
{
it->second = iright;
it->first = ileft;
}
pHighestRight = phighest;
for (;;)
{
pHighestRightNext = pHighestRight + 1;
if (pHighestRightNext == last) pHighestRightNext = first;
if (pHighestRightNext->y != pHighestRight->y) break;
pHighestRight = pHighestRightNext;
}
pPrevHighestLeft = pHighestRight;
pHighestLeft = pHighestRight;
++pHighestLeft;
if (pHighestLeft == last) pHighestLeft = first;
while (pHighestLeft->y != fhighest)
{
pPrevHighestLeft = pHighestLeft;
++pHighestLeft;
if (pHighestLeft == last) pHighestLeft = first;
}
// we need to get the orientation of the polygon
// There are 2 case : flat, and non-flat top
// check for flat top
if (pHighestLeft->x != pHighestRight->x)
{
// compare right and left side
if (pHighestLeft->x > pHighestRight->x)
{
ccw = true; // the list is CCW oriented
std::swap(pHighestLeft, pHighestRight);
}
else
{
ccw = false; // the list is CW oriented
}
}
else
{
pHighestRightNext = pHighestRight + 1;
if (pHighestRightNext == last) pHighestRightNext = first;
deltaXN = pHighestRightNext->x - pHighestRight->x;
deltaYN = pHighestRightNext->y - pHighestRight->y;
deltaXP = pPrevHighestLeft->x - pHighestLeft->x;
deltaYP = pPrevHighestLeft->y - pHighestLeft->y;
if ((deltaXN * deltaYP - deltaYN * deltaXP) < 0)
{
ccw = true;
std::swap(pHighestLeft, pHighestRight);
}
else
{
ccw = false;
}
}
if (!ccw)
{
// cw oriented
curr = pHighestRight;
do
{
next = curr + 1;
if (next == last) next = first;
ScanInnerEdge(&borders[0], curr->x, curr->y, next->x, next->y, minimumY, true);
curr = next;
}
while (curr != plowest);
do
{
next = curr + 1;
if (next == last) next = first;
ScanInnerEdge(&borders[0], next->x, next->y, curr->x, curr->y, minimumY, false);
curr = next;
}
while (curr != pHighestLeft);
}
else
{
// ccw oriented
curr = pHighestLeft;
do
{
next = curr + 1;
if (next == last) next = first;
ScanInnerEdge(&borders[0], curr->x, curr->y, next->x, next->y, minimumY, false);
curr = next;
}
while (curr != plowest);
do
{
next = curr + 1;
if (next == last) next = first;
ScanInnerEdge(&borders[0], next->x, next->y, curr->x, curr->y, minimumY, true);
curr = next;
}
while (curr != pHighestRight);
}
// fix for top
if (floorf(fhighest) != fhighest)
{
borders[0].first = 1;
borders[0].second = 0;
}
// fix for bottom
if (floorf(flowest) != flowest)
{
borders.back().first = 1;
borders.back().second = 0;
}
}
// *******************************************************************************
void CPolygon2D::checkValidBorders() const
{
for (uint k = 0; k < Vertices.size(); ++k)
{
nlassert(Vertices[k].x >= -32000.f); // coordinate too big !
nlassert(Vertices[k].x < 32000.f); // coordinate too big !
nlassert(Vertices[k].y >= -32000.f); // coordinate too big !
nlassert(Vertices[k].y < 32000.f); // coordinate too big !
}
}
// *******************************************************************************
/// Sum the dot product of this poly vertices against a plane
float CPolygon2D::sumDPAgainstLine(float a, float b, float c) const
{
float sum = 0.f;
for (uint k = 0; k < Vertices.size(); ++k)
{
const CVector2f &p = Vertices[k];
sum += a * p.x + b * p.y + c;
}
return sum;
}
// *******************************************************************************
bool CPolygon2D::getNonNullSeg(uint &index) const
{
nlassert(!Vertices.empty());
float bestLength = 0.f;
sint bestIndex = -1;
for (uint k = 0; k < Vertices.size() - 1; ++k)
{
float norm2 = (Vertices[k + 1] - Vertices[k]).sqrnorm();
if ( norm2 > bestLength)
{
bestLength = norm2;
bestIndex = (int) k;
}
}
float norm2 = (Vertices[Vertices.size() - 1] - Vertices[0]).sqrnorm();
if ( norm2 > bestLength)
{
index = (uint)Vertices.size() - 1;
return true;
}
if (bestIndex != -1)
{
index = bestIndex;
return true;
}
else
{
return false;
}
}
// *******************************************************************************
void CPolygon2D::getLineEquation(uint index, float &a, float &b, float &c) const
{
nlassert(index < Vertices.size());
const CVector2f &v0 = getSegRef0(index);
const CVector2f &v1 = getSegRef1(index);
NLMISC::CVector2f seg = v0 - v1;
a = seg.y;
b = - seg.x;
c = - v0.x * a - v0.y * b;
}
// *******************************************************************************
bool CPolygon2D::intersect(const CPolygon2D &other) const
{
nlassert(!other.Vertices.empty());
uint nonNullSegIndex;
/// get the orientation of this poly
if (getNonNullSeg(nonNullSegIndex))
{
float a0, b0, c0; /// contains the seg 2d equation
getLineEquation(nonNullSegIndex, a0, b0, c0);
float orient = sumDPAgainstLine(a0, b0, c0);
for (uint k = 0; k < Vertices.size(); ++k)
{
/// don't check against a null segment
if ( (getSegRef0(k) - getSegRef1(k)).sqrnorm() == 0.f) continue;
/// get the line equation of the current segment
float a, b, c; /// contains the seg 2d equation
getLineEquation(k, a, b, c);
uint l;
for (l = 0; l < other.Vertices.size(); ++l)
{
const CVector2f &ov = other.Vertices[l];
if ( orient * (ov.x * a + ov.y * b +c) > 0.f) break;
}
if (l == other.Vertices.size()) // all point on the outside
{
return false; // outside
}
}
return true;
}
else // this poly is just a point
{
return other.contains(Vertices[0]);
}
}
// *******************************************************************************
bool CPolygon2D::contains(const CVector2f &p, bool hintIsConvex /*= true*/) const
{
if (hintIsConvex)
{
uint numVerts = (uint)Vertices.size();
nlassert(numVerts >= 0.f);
for (uint k = 0; k < numVerts; ++k)
{
if (getSegRef0(k) != getSegRef1(k))
{
float a, b, c; /// contains the seg 2d equation
getLineEquation(k, a, b, c);
float orient = a * p.x + b * p.y + c;
for(uint l = k + 1; l < numVerts; ++l)
{
getLineEquation(l, a, b, c);
float newOrient = a * p.x + b * p.y + c;
if (newOrient * orient < 0.f) return false;
}
return true;
}
}
// the poly reduces to a point
return p == Vertices[0];
}
else
{
// concave case
static std::vector<float> xInter;
xInter.clear();
for(uint k = 0; k < Vertices.size(); ++k)
{
const CVector2f &p0 = getSegRef0(k);
const CVector2f &p1 = getSegRef1(k);
if (p0.y == p1.y)
{
if (p.y == p0.y)
{
if ((p.x >= p0.x && p.x <= p1.x)
|| (p.x >= p1.x && p.x <= p0.x))
{
return true;
}
}
}
if ((p.y >= p0.y && p.y < p1.y) ||
(p.y >= p1.y && p.y < p0.y)
)
{
float inter = p0.x + (p.y - p0.y) * (p1.x - p0.x) / (p1.y- p0.y);
xInter.push_back(inter);
}
}
if (xInter.size() < 2) return false;
std::sort(xInter.begin(), xInter.end());
for(uint k = 0; k < xInter.size() - 1; ++k)
{
if (p.x >= xInter[k] && p.x <= xInter[k + 1])
{
return (k & 1) == 0;
}
}
return false;
}
}
// *******************************************************************************
CPolygon2D::CPolygon2D(const CTriangle &tri, const CMatrix &projMat)
{
Vertices.resize(3);
NLMISC::CVector proj[3] = { projMat * tri.V0, projMat * tri.V1, projMat * tri.V2 };
Vertices[0].set(proj[0].x, proj[0].y);
Vertices[1].set(proj[1].x, proj[1].y);
Vertices[2].set(proj[2].x, proj[2].y);
}
// *******************************************************************************
void CPolygon2D::getBoundingRect(CVector2f &minCorner, CVector2f &maxCorner) const
{
nlassert(!Vertices.empty());
minCorner = maxCorner = Vertices[0];
uint numVertices = (uint)Vertices.size();
for(uint k = 0; k < numVertices; ++k)
{
minCorner.minof(minCorner, Vertices[k]);
maxCorner.maxof(minCorner, Vertices[k]);
}
}
// *******************************************************************************
bool operator ==(const CPolygon2D &lhs,const CPolygon2D &rhs)
{
if (lhs.Vertices.size() != rhs.Vertices.size()) return false;
return std::equal(lhs.Vertices.begin(), lhs.Vertices.end(), rhs.Vertices.begin());
}
// *******************************************************************************
bool operator < (const CPolygon2D &lhs, const CPolygon2D &rhs)
{
if (lhs.Vertices.size() != rhs.Vertices.size()) return lhs.Vertices.size() < rhs.Vertices.size();
for(uint k = 0; k < lhs.Vertices.size(); ++k)
{
if (lhs.Vertices[k] != rhs.Vertices[k]) return lhs.Vertices[k] < rhs.Vertices[k];
}
return false;
}
// *******************************************************************************
static inline bool testSegmentIntersection(const CVector2f &a, const CVector2f &b,
const CVector2f &c, const CVector2f &d)
{
double denom = a.x * double(d.y - c.y) +
b.x * double(c.y - d.y) +
d.x * double(b.y - a.y) +
c.x * double(a.y - b.y);
if (denom == 0) return false;
//
double num = a.x * double(d.y - c.y) +
c.x * double(a.y - d.y) +
d.x * double(c.y - a.y);
if (num == 0 || (num == denom)) return false;
double lambda = num / denom;
if (lambda <= 0 || lambda >= 1) return false;
//
num = - (a.x * double(c.y - b.y) +
b.x * double(a.y - c.y) +
c.x * double(b.y - a.y));
if (num == 0 || (num == denom)) return false;
lambda = num / denom;
if (lambda <= 0 || lambda >= 1) return false;
return true;
}
// *******************************************************************************
bool CPolygon2D::selfIntersect() const
{
if (Vertices.size() < 3) return false;
uint numEdges = (uint)Vertices.size();
for(uint k = 0; k < numEdges; ++k)
{
// test intersection with all other edges that don't share a vertex with this one
const CVector2f &p0 = getSegRef0(k);
const CVector2f &p1 = getSegRef1(k);
for(uint l = 0; l < k; ++l)
{
const CVector2f &v0 = getSegRef0(l);
const CVector2f &v1 = getSegRef1(l);
if (v0 == p0 || v0 == p1 || v1 == p0 || v1 == p1) continue;
//
if (testSegmentIntersection(p0, p1, v0, v1)) return true;
}
}
return false;
}
} // NLMISC