source: GTP/trunk/App/Demos/Vis/FriendlyCulling/src/Polygon3.cpp @ 2913

Revision 2913, 5.6 KB checked in by mattausch, 16 years ago (diff)
Line 
1#include "Polygon3.h"
2#include "AxisAlignedBox3.h"
3#include "Plane3.h"
4
5
6using namespace std;
7
8namespace CHCDemoEngine
9{
10
11Polygon3::Polygon3()
12{
13}
14
15
16Polygon3::Polygon3(const VertexArray &vertices)
17{
18        mVertices.reserve(vertices.size());
19        mVertices = vertices;
20}
21
22
23Polygon3::~Polygon3()
24{
25}
26
27
28Plane3 Polygon3::GetSupportingPlane() const
29{
30        return Plane3(mVertices[0], mVertices[1], mVertices[2]);
31}
32
33
34Vector3 Polygon3::GetNormal() const
35{
36    return Normalize(CrossProd(mVertices[2] - mVertices[1],
37                                                           mVertices[0] - mVertices[1]));
38}
39
40
41void Polygon3::Split(const Plane3 &partition,
42                                         Polygon3 &front,
43                                         Polygon3 &back, float epsilon)
44{
45        Vector3 ptA = mVertices.back();
46        int sideA = partition.Side(ptA, epsilon);
47        bool foundSplit = false;
48    Vector3 lastSplit;
49               
50        /////////////
51        //-- find line - plane intersections
52
53        VertexArray::const_iterator it, it_end = mVertices.end();
54
55        for (it = mVertices.begin(); it != it_end; ++ it)
56        {
57                Vector3 ptB = *it;
58                int sideB = partition.Side(ptB, epsilon);
59       
60                // vertices on different sides => split
61            if (sideB > 0)
62                {
63                        if (sideA < 0)
64                        {
65                                //-- plane - line intersection
66                                Vector3 splitPt = partition.FindIntersection(ptA, ptB);
67                       
68                                // test if split point not too close to previous split point
69                                if (!foundSplit || (!EpsilonEqualV3(splitPt, lastSplit, epsilon)))
70                                {
71                                        // add vertex to both polygons
72                                        front.mVertices.push_back(splitPt);
73                                        back.mVertices.push_back(splitPt);
74                                       
75                                        lastSplit = splitPt;
76                                        foundSplit = true;
77                                }
78                        }
79                        front.mVertices.push_back(ptB);
80                }
81                else if (sideB < 0)
82                {
83                        if (sideA > 0)
84                        {
85                                ////////////
86                                //-- plane - line intersection
87
88                                Vector3 splitPt = partition.FindIntersection(ptA, ptB);
89                               
90                                // test if split point not too close to previous split point
91                                if (!foundSplit || (!EpsilonEqualV3(splitPt, lastSplit, epsilon)))
92                                {
93                                        // add vertex to both polygons
94                                        front.mVertices.push_back(splitPt);
95                                        back.mVertices.push_back(splitPt);
96
97                                        lastSplit = splitPt;
98                                        foundSplit = true;
99                                }       
100                        }
101
102                        back.mVertices.push_back(ptB);
103                }
104                else
105                {
106                        // vertex on plane => add vertex to both polygons
107                        front.mVertices.push_back(ptB);
108                        back.mVertices.push_back(ptB);
109                }
110       
111                ptA = ptB;
112                sideA = sideB;
113        }
114}
115
116
117float Polygon3::GetArea() const
118{
119        Vector3 v = CrossProd(mVertices.back(), mVertices.front());
120   
121    for (int i=0; i < mVertices.size() - 1; ++i)
122                v += CrossProd(mVertices[i], mVertices[i+1]);
123   
124    return 0.5f * fabs(DotProd(GetNormal(), v));
125}
126
127
128int Polygon3::Side(const Plane3 &plane,
129                                   const float epsilon) const
130{
131        int classification = ClassifyPlane(plane, epsilon);
132       
133        if (classification == BACK_SIDE)
134                return -1;
135        else if (classification == FRONT_SIDE)
136                return 1;
137
138        // plane splits polygon or is coincident
139        return 0;
140}
141
142
143int Polygon3::ClassifyPlane(const Plane3 &plane,
144                                                        const float epsilon) const
145{
146        VertexArray::const_iterator it, it_end = mVertices.end();
147
148        bool onFrontSide = false;
149        bool onBackSide = false;
150       
151        int count = 0;
152        // find possible line-plane intersections
153        for (it = mVertices.begin(); it != it_end; ++ it)
154        {
155                const int side = plane.Side(*it, epsilon);
156               
157                if (side > 0)
158                {
159                        onFrontSide = true;
160                }
161                else if (side < 0)
162                {
163                        onBackSide = true;
164                }
165
166                //TODO: check if split goes through vertex
167                if (onFrontSide && onBackSide) // split
168                {
169                        return SPLIT;
170                }
171                // 3 vertices enough to decide if coincident
172                else if (((++ count) >= 3) && !onFrontSide && !onBackSide)
173                {   
174                        return COINCIDENT;
175                }
176        }
177
178        if (onBackSide)
179        {
180                return BACK_SIDE;
181        }
182        else if (onFrontSide)
183        {
184                return FRONT_SIDE;
185        }
186       
187        return COINCIDENT; // plane and polygon are coincident
188}
189
190
191Vector3 Polygon3::Center() const
192{
193        int i;
194        Vector3 sum = mVertices[0];
195        for (i=1; i < mVertices.size(); i++)
196                sum += mVertices[i];
197       
198        return sum / (float)i;
199}
200
201
202bool Polygon3::Valid(const float epsilon) const
203{
204        if (mVertices.size() < 3)
205                return false;
206#if 0
207        // matt: removed for performance issues
208        // check if area exceeds certain size
209        if (GetArea() < 1e-10)
210        {
211                cerr << "area too small: " << GetArea() << std::endl;
212                return false;
213        }
214               
215        Vector3 vtx = mVertices.back();
216        VertexArray::const_iterator it, it_end = mVertices.end();
217
218        for (it = mVertices.begin(); it != it_end; ++it)
219        {
220                if (EpsilonEqualV3(vtx, *it, epsilon))
221                {
222                        cerr << "Double vertices:\n" << *this << endl;
223                        return false;
224                }
225                vtx = *it;
226        }
227#endif
228        return true;
229}
230
231
232// int_lineseg returns 1 if the given line segment intersects a 2D
233// ray travelling in the positive X direction.  This is used in the
234// Jordan curve computation for polygon intersection.
235inline int int_lineseg(float px,
236                                           float py,
237                                           float u1,
238                                           float v1,
239                                           float u2,
240                                           float v2)
241{
242        float t;
243        float ydiff;
244
245        u1 -= px; u2 -= px;       // translate line
246        v1 -= py; v2 -= py;
247
248        if ((v1 > 0 && v2 > 0) ||
249                (v1 < 0 && v2 < 0) ||
250                (u1 < 0 && u2 < 0))
251                return 0;
252
253        if (u1 > 0 && u2 > 0)
254                return 1;
255
256        ydiff = v2 - v1;
257
258        if (fabs(ydiff) < Limits::Small)
259        {       
260                // denominator near 0
261                if (((fabs(v1) > Limits::Small) || (u1 > 0) || (u2 > 0)))
262                        return 0;
263
264                return 1;
265        }
266
267        t = -v1 / ydiff; // Compute parameter
268
269        return (u1 + t * (u2 - u1)) > 0;
270}
271
272
273AxisAlignedBox3 Polygon3::GetBoundingBox() const
274{
275        AxisAlignedBox3 box;
276        box.Initialize();
277
278        VertexArray::const_iterator vit, vit_end = mVertices.end();
279
280        for (vit = mVertices.begin(); vit != vit_end; ++ vit)
281        {
282                box.Include(*vit);
283        }
284
285        return box;
286}
287
288
289}
Note: See TracBrowser for help on using the repository browser.