1 /*
2  * file: vrml_layer.cpp
3  *
4  * This program source code file is part of KiCad, a free EDA CAD application.
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * Copyright (C) 2013-2017  Cirilo Bernardo
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
25  */
26 
27 // Wishlist:
28 // 1. crop anything outside the board outline on PTH, silk, and copper layers
29 // 2. on the PTH layer, handle cropped holes differently from others;
30 //    these are assumed to be castellated edges and the profile is not
31 //    a closed loop as assumed for all other outlines.
32 // 3. a scheme is needed to tell a castellated edge from a plain board edge
33 
34 
35 #include <sstream>
36 #include <string>
37 #include <iomanip>
38 #include <cmath>
39 #include <vrml_layer.h>
40 #include <trigo.h>
41 
42 #ifndef CALLBACK
43 #define CALLBACK
44 #endif
45 
46 #define GLCALLBACK(x) (( void (CALLBACK*)() )&(x))
47 
48 // minimum sides to a circle
49 #define MIN_NSIDES 6
50 
FormatDoublet(double x,double y,int precision,std::string & strx,std::string & stry)51 static void FormatDoublet( double x, double y, int precision, std::string& strx, std::string& stry )
52 {
53     std::ostringstream ostr;
54 
55     ostr << std::fixed << std::setprecision( precision );
56 
57     ostr << x;
58     strx = ostr.str();
59 
60     ostr.str( "" );
61     ostr << y;
62     stry = ostr.str();
63 
64     while( *strx.rbegin() == '0' )
65         strx.erase( strx.size() - 1 );
66 
67     while( *stry.rbegin() == '0' )
68         stry.erase( stry.size() - 1 );
69 }
70 
71 
FormatSinglet(double x,int precision,std::string & strx)72 static void FormatSinglet( double x, int precision, std::string& strx )
73 {
74     std::ostringstream ostr;
75 
76     ostr << std::fixed << std::setprecision( precision );
77 
78     ostr << x;
79     strx = ostr.str();
80 
81     while( *strx.rbegin() == '0' )
82         strx.erase( strx.size() - 1 );
83 }
84 
85 
calcNSides(double aRadius,double aAngle)86 int VRML_LAYER::calcNSides( double aRadius, double aAngle )
87 {
88     // check #segments on ends of arc
89     int maxSeg = maxArcSeg * aAngle / M_PI;
90 
91     if( maxSeg < 3 )
92         maxSeg = 3;
93 
94     int csides = aRadius * M_PI / minSegLength;
95 
96     if( csides < 0 )
97         csides = -csides;
98 
99     if( csides > maxSeg )
100     {
101         if( csides < 2 * maxSeg )
102             csides /= 2;
103         else
104             csides = ( ( (double) csides ) * minSegLength / maxSegLength );
105     }
106 
107     if( csides < 3 )
108         csides = 3;
109 
110     if( ( csides & 1 ) == 0 )
111         csides += 1;
112 
113     return csides;
114 }
115 
116 
vrml_tess_begin(GLenum cmd,void * user_data)117 static void CALLBACK vrml_tess_begin( GLenum cmd, void* user_data )
118 {
119     VRML_LAYER* lp = (VRML_LAYER*) user_data;
120 
121     lp->glStart( cmd );
122 }
123 
124 
vrml_tess_end(void * user_data)125 static void CALLBACK vrml_tess_end( void* user_data )
126 {
127     VRML_LAYER* lp = (VRML_LAYER*) user_data;
128 
129     lp->glEnd();
130 }
131 
132 
vrml_tess_vertex(void * vertex_data,void * user_data)133 static void CALLBACK vrml_tess_vertex( void* vertex_data, void* user_data )
134 {
135     VRML_LAYER* lp = (VRML_LAYER*) user_data;
136 
137     lp->glPushVertex( (VERTEX_3D*) vertex_data );
138 }
139 
140 
vrml_tess_err(GLenum errorID,void * user_data)141 static void CALLBACK vrml_tess_err( GLenum errorID, void* user_data )
142 {
143     VRML_LAYER* lp = (VRML_LAYER*) user_data;
144 
145     lp->Fault = true;
146     lp->SetGLError( errorID );
147 }
148 
149 
vrml_tess_combine(GLdouble coords[3],VERTEX_3D * vertex_data[4],GLfloat weight[4],void ** outData,void * user_data)150 static void CALLBACK vrml_tess_combine( GLdouble coords[3], VERTEX_3D* vertex_data[4],
151                                         GLfloat weight[4], void** outData, void* user_data )
152 {
153     VRML_LAYER* lp = (VRML_LAYER*) user_data;
154 
155     // the plating is set to true only if all are plated
156     bool plated = vertex_data[0]->pth;
157 
158     if( !vertex_data[1]->pth )
159         plated = false;
160 
161     if( vertex_data[2] && !vertex_data[2]->pth )
162         plated = false;
163 
164     if( vertex_data[3] && !vertex_data[3]->pth )
165         plated = false;
166 
167     *outData = lp->AddExtraVertex( coords[0], coords[1], plated );
168 }
169 
170 
VRML_LAYER()171 VRML_LAYER::VRML_LAYER()
172 {
173     ResetArcParams();
174     offsetX = 0.0;
175     offsetY = 0.0;
176 
177     fix = false;
178     Fault = false;
179     idx = 0;
180     hidx = 0;
181     eidx = 0;
182     ord = 0;
183     glcmd   = 0;
184     pholes  = NULL;
185 
186     tess = gluNewTess();
187 
188     if( !tess )
189         return;
190 
191     // set up the tesselator callbacks
192     gluTessCallback( tess, GLU_TESS_BEGIN_DATA, GLCALLBACK( vrml_tess_begin ) );
193 
194     gluTessCallback( tess, GLU_TESS_VERTEX_DATA, GLCALLBACK( vrml_tess_vertex ) );
195 
196     gluTessCallback( tess, GLU_TESS_END_DATA, GLCALLBACK( vrml_tess_end ) );
197 
198     gluTessCallback( tess, GLU_TESS_ERROR_DATA, GLCALLBACK( vrml_tess_err ) );
199 
200     gluTessCallback( tess, GLU_TESS_COMBINE_DATA, GLCALLBACK( vrml_tess_combine ) );
201 
202     gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
203 
204     gluTessNormal( tess, 0, 0, 1 );
205 }
206 
207 
~VRML_LAYER()208 VRML_LAYER::~VRML_LAYER()
209 {
210     Clear();
211 
212     if( tess )
213     {
214         gluDeleteTess( tess );
215         tess = NULL;
216     }
217 }
218 
219 
ResetArcParams()220 void VRML_LAYER::ResetArcParams()
221 {
222     // arc parameters suitable to mm measurements
223     maxArcSeg = 48;
224     minSegLength = 0.1;
225     maxSegLength = 0.5;
226 }
227 
228 
GetArcParams(int & aMaxSeg,double & aMinLength,double & aMaxLength)229 void VRML_LAYER::GetArcParams( int& aMaxSeg, double& aMinLength, double& aMaxLength )
230 {
231     aMaxSeg = maxArcSeg;
232     aMinLength = minSegLength;
233     aMaxLength = maxSegLength;
234 }
235 
236 
SetArcParams(int aMaxSeg,double aMinLength,double aMaxLength)237 bool VRML_LAYER::SetArcParams( int aMaxSeg, double aMinLength, double aMaxLength )
238 {
239     if( aMaxSeg < 8 )
240         aMaxSeg = 8;
241 
242     if( aMinLength <= 0 || aMaxLength <= aMinLength )
243         return false;
244 
245     maxArcSeg = aMaxSeg;
246     minSegLength = aMinLength;
247     maxSegLength = aMaxLength;
248     return true;
249 }
250 
251 
Clear(void)252 void VRML_LAYER::Clear( void )
253 {
254     int i;
255 
256     fix = false;
257     idx = 0;
258 
259     for( i = contours.size(); i > 0; --i )
260     {
261         delete contours.back();
262         contours.pop_back();
263     }
264 
265     pth.clear();
266 
267     areas.clear();
268 
269     for( i = vertices.size(); i > 0; --i )
270     {
271         delete vertices.back();
272         vertices.pop_back();
273     }
274 
275     clearTmp();
276 }
277 
278 
clearTmp(void)279 void VRML_LAYER::clearTmp( void )
280 {
281     unsigned int i;
282 
283     Fault   = false;
284     hidx    = 0;
285     eidx    = 0;
286     ord = 0;
287     glcmd = 0;
288 
289     triplets.clear();
290     solid.clear();
291 
292     for( i = outline.size(); i > 0; --i )
293     {
294         delete outline.back();
295         outline.pop_back();
296     }
297 
298     ordmap.clear();
299 
300     for( i = extra_verts.size(); i > 0; --i )
301     {
302         delete extra_verts.back();
303         extra_verts.pop_back();
304     }
305 
306     // note: unlike outline and extra_verts,
307     // vlist is not responsible for memory management
308     vlist.clear();
309 
310     // go through the vertex list and reset ephemeral parameters
311     for( i = 0; i < vertices.size(); ++i )
312     {
313         vertices[i]->o = -1;
314     }
315 }
316 
317 
NewContour(bool aPlatedHole)318 int VRML_LAYER::NewContour( bool aPlatedHole )
319 {
320     if( fix )
321         return -1;
322 
323     std::list<int>* contour = new std::list<int>;
324 
325     contours.push_back( contour );
326     areas.push_back( 0.0 );
327 
328     pth.push_back( aPlatedHole );
329 
330     return contours.size() - 1;
331 }
332 
333 
AddVertex(int aContourID,double aXpos,double aYpos)334 bool VRML_LAYER::AddVertex( int aContourID, double aXpos, double aYpos )
335 {
336     if( fix )
337     {
338         error = "AddVertex(): no more vertices may be added (Tesselate was previously executed)";
339         return false;
340     }
341 
342     if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
343     {
344         error = "AddVertex(): aContour is not within a valid range";
345         return false;
346     }
347 
348     VERTEX_3D* vertex = new VERTEX_3D;
349     vertex->x   = aXpos;
350     vertex->y   = aYpos;
351     vertex->i   = idx++;
352     vertex->o   = -1;
353     vertex->pth = pth[ aContourID ];
354 
355     VERTEX_3D* v2 = NULL;
356 
357     if( contours[aContourID]->size() > 0 )
358         v2 = vertices[ contours[aContourID]->back() ];
359 
360     vertices.push_back( vertex );
361     contours[aContourID]->push_back( vertex->i );
362 
363     if( v2 )
364         areas[aContourID] += ( aXpos - v2->x ) * ( aYpos + v2->y );
365 
366     return true;
367 }
368 
369 
EnsureWinding(int aContourID,bool aHoleFlag)370 bool VRML_LAYER::EnsureWinding( int aContourID, bool aHoleFlag )
371 {
372     if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
373     {
374         error = "EnsureWinding(): aContour is outside the valid range";
375         return false;
376     }
377 
378     std::list<int>* cp = contours[aContourID];
379 
380     if( cp->size() < 3 )
381     {
382         error = "EnsureWinding(): there are fewer than 3 vertices";
383         return false;
384     }
385 
386     double dir = areas[aContourID];
387 
388     VERTEX_3D* vp0 = vertices[ cp->back() ];
389     VERTEX_3D* vp1 = vertices[ cp->front() ];
390 
391     dir += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
392 
393     // if dir is positive, winding is CW
394     if( ( aHoleFlag && dir < 0 ) || ( !aHoleFlag && dir > 0 ) )
395     {
396         cp->reverse();
397         areas[aContourID] = -areas[aContourID];
398     }
399 
400     return true;
401 }
402 
403 
AppendCircle(double aXpos,double aYpos,double aRadius,int aContourID,bool aHoleFlag)404 bool VRML_LAYER::AppendCircle( double aXpos, double aYpos, double aRadius, int aContourID,
405                                bool aHoleFlag )
406 {
407     if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
408     {
409         error = "AppendCircle(): invalid contour (out of range)";
410         return false;
411     }
412 
413     int nsides = M_PI * 2.0 * aRadius / minSegLength;
414 
415     if( nsides > maxArcSeg )
416     {
417         if( nsides > 2 * maxArcSeg )
418         {
419             // use segments approx. maxAr
420             nsides = M_PI * 2.0 * aRadius / maxSegLength;
421         }
422         else
423         {
424             nsides /= 2;
425         }
426     }
427 
428     if( nsides < MIN_NSIDES )
429         nsides = MIN_NSIDES;
430 
431     // even numbers give prettier results for circles
432     if( nsides & 1 )
433         nsides += 1;
434 
435     double da = M_PI * 2.0 / nsides;
436 
437     bool fail = false;
438 
439     if( aHoleFlag )
440     {
441         fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
442 
443         for( double angle = da; angle < M_PI * 2; angle += da )
444             fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
445                                 aYpos - aRadius * sin( angle ) );
446     }
447     else
448     {
449         fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
450 
451         for( double angle = da; angle < M_PI * 2; angle += da )
452             fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
453                                 aYpos + aRadius * sin( angle ) );
454     }
455 
456     return !fail;
457 }
458 
459 
AddCircle(double aXpos,double aYpos,double aRadius,bool aHoleFlag,bool aPlatedHole)460 bool VRML_LAYER::AddCircle( double aXpos, double aYpos, double aRadius, bool aHoleFlag,
461                             bool aPlatedHole )
462 {
463     int pad;
464 
465     if( aHoleFlag && aPlatedHole )
466         pad = NewContour( true );
467     else
468         pad = NewContour( false );
469 
470     if( pad < 0 )
471     {
472         error = "AddCircle(): failed to add a contour";
473         return false;
474     }
475 
476     return AppendCircle( aXpos, aYpos, aRadius, pad, aHoleFlag );
477 }
478 
479 
AddSlot(double aCenterX,double aCenterY,double aSlotLength,double aSlotWidth,double aAngle,bool aHoleFlag,bool aPlatedHole)480 bool VRML_LAYER::AddSlot( double aCenterX, double aCenterY, double aSlotLength, double aSlotWidth,
481                           double aAngle, bool aHoleFlag, bool aPlatedHole )
482 {
483     aAngle *= M_PI / 180.0;
484 
485     if( aSlotWidth > aSlotLength )
486     {
487         aAngle += M_PI2;
488         std::swap( aSlotLength, aSlotWidth );
489     }
490 
491     aSlotWidth /= 2.0;
492     aSlotLength = aSlotLength / 2.0 - aSlotWidth;
493 
494     int csides = calcNSides( aSlotWidth, M_PI );
495 
496     double capx, capy;
497 
498     capx    = aCenterX + cos( aAngle ) * aSlotLength;
499     capy    = aCenterY + sin( aAngle ) * aSlotLength;
500 
501     double ang, da;
502     int i;
503     int pad;
504 
505     if( aHoleFlag && aPlatedHole )
506         pad = NewContour( true );
507     else
508         pad = NewContour( false );
509 
510     if( pad < 0 )
511     {
512         error = "AddCircle(): failed to add a contour";
513         return false;
514     }
515 
516     da = M_PI / csides;
517     bool fail = false;
518 
519     if( aHoleFlag )
520     {
521         for( ang = aAngle + M_PI2, i = 0; i < csides; ang -= da, ++i )
522             fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
523                                 capy + aSlotWidth * sin( ang ) );
524 
525         ang = aAngle - M_PI2;
526         fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
527                             capy + aSlotWidth * sin( ang ) );
528 
529         capx    = aCenterX - cos( aAngle ) * aSlotLength;
530         capy    = aCenterY - sin( aAngle ) * aSlotLength;
531 
532         for( ang = aAngle - M_PI2, i = 0; i < csides; ang -= da, ++i )
533             fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
534                                 capy + aSlotWidth * sin( ang ) );
535 
536         ang = aAngle + M_PI2;
537         fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
538                             capy + aSlotWidth * sin( ang ) );
539     }
540     else
541     {
542         for( ang = aAngle - M_PI2, i = 0; i < csides; ang += da, ++i )
543             fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
544                                 capy + aSlotWidth * sin( ang ) );
545 
546         ang = aAngle + M_PI2;
547         fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
548                             capy + aSlotWidth * sin( ang ) );
549 
550         capx    = aCenterX - cos( aAngle ) * aSlotLength;
551         capy    = aCenterY - sin( aAngle ) * aSlotLength;
552 
553         for( ang = aAngle + M_PI2, i = 0; i < csides; ang += da, ++i )
554             fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
555                                 capy + aSlotWidth * sin( ang ) );
556 
557         ang = aAngle - M_PI2;
558         fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
559                             capy + aSlotWidth * sin( ang ) );
560     }
561 
562     return !fail;
563 }
564 
565 
AddPolygon(const std::vector<wxRealPoint> & aPolySet,double aCenterX,double aCenterY,double aAngle)566 bool VRML_LAYER::AddPolygon( const std::vector< wxRealPoint >& aPolySet, double aCenterX,
567                              double aCenterY, double aAngle )
568 {
569     int pad = NewContour( false );
570 
571     if( pad < 0 )
572     {
573         error = "AddPolygon(): failed to add a contour";
574         return false;
575     }
576 
577     for( auto corner : aPolySet )
578     {
579         // The sense of polygon rotations is reversed
580         RotatePoint( &corner.x, &corner.y, -aAngle );
581         AddVertex( pad, aCenterX + corner.x, aCenterY + corner.y );
582     }
583 
584     if( !EnsureWinding( pad, false ) )
585         return false;
586 
587     return true;
588 }
589 
590 
591 // adds an arc to the given center, start point, pen width, and angle (degrees).
AppendArc(double aCenterX,double aCenterY,double aRadius,double aStartAngle,double aAngle,int aContourID)592 bool VRML_LAYER::AppendArc( double aCenterX, double aCenterY, double aRadius,
593                             double aStartAngle, double aAngle, int aContourID )
594 {
595     if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
596     {
597         error = "AppendArc(): invalid contour (out of range)";
598         return false;
599     }
600 
601     aAngle = aAngle / 180.0 * M_PI;
602     aStartAngle = aStartAngle / 180.0 * M_PI;
603 
604     int nsides = calcNSides( aRadius, aAngle );
605 
606     double da = aAngle / nsides;
607 
608     bool fail = false;
609 
610     if( aAngle > 0 )
611     {
612         aAngle += aStartAngle;
613         for( double ang = aStartAngle; ang < aAngle; ang += da )
614             fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
615                                 aCenterY + aRadius * sin( ang ) );
616     }
617     else
618     {
619         aAngle += aStartAngle;
620         for( double ang = aStartAngle; ang > aAngle; ang += da )
621             fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
622                                 aCenterY + aRadius * sin( ang ) );
623     }
624 
625     return !fail;
626 }
627 
628 
AddArc(double aCenterX,double aCenterY,double aStartX,double aStartY,double aArcWidth,double aAngle,bool aHoleFlag,bool aPlatedHole)629 bool VRML_LAYER::AddArc( double aCenterX, double aCenterY, double aStartX, double aStartY,
630                          double aArcWidth, double aAngle, bool aHoleFlag, bool aPlatedHole )
631 {
632     aAngle *= M_PI / 180.0;
633 
634     // we don't accept small angles; in fact, 1 degree ( 0.01745 ) is already
635     // way too small but we must set a limit somewhere
636     if( aAngle < 0.01745 && aAngle > -0.01745 )
637     {
638         error = "AddArc(): angle is too small: abs( angle ) < 1 degree";
639         return false;
640     }
641 
642     double rad = sqrt( (aStartX - aCenterX) * (aStartX - aCenterX)
643                         + (aStartY - aCenterY) * (aStartY - aCenterY) );
644 
645     aArcWidth /= 2.0;    // this is the radius of the caps
646 
647     // we will not accept an arc with an inner radius close to zero so we
648     // set a limit here. the end result will vary somewhat depending on
649     // the output units
650     if( aArcWidth >= ( rad * 1.01 ) )
651     {
652         error = "AddArc(): width/2 exceeds radius*1.01";
653         return false;
654     }
655 
656     // calculate the radii of the outer and inner arcs
657     double  orad    = rad + aArcWidth;
658     double  irad    = rad - aArcWidth;
659 
660     int osides  = calcNSides( orad, aAngle );
661     int isides  = calcNSides( irad, aAngle );
662     int csides  = calcNSides( aArcWidth, M_PI );
663 
664     double  stAngle     = atan2( aStartY - aCenterY, aStartX - aCenterX );
665     double  endAngle    = stAngle + aAngle;
666 
667     // calculate ends of inner and outer arc
668     double  oendx   = aCenterX + orad* cos( endAngle );
669     double  oendy   = aCenterY + orad* sin( endAngle );
670     double  ostx    = aCenterX + orad* cos( stAngle );
671     double  osty    = aCenterY + orad* sin( stAngle );
672 
673     double  iendx   = aCenterX + irad* cos( endAngle );
674     double  iendy   = aCenterY + irad* sin( endAngle );
675     double  istx    = aCenterX + irad* cos( stAngle );
676     double  isty    = aCenterY + irad* sin( stAngle );
677 
678     if( ( aAngle < 0 && !aHoleFlag ) || ( aAngle > 0 && aHoleFlag ) )
679     {
680         aAngle = -aAngle;
681         std::swap( stAngle, endAngle );
682         std::swap( oendx, ostx );
683         std::swap( oendy, osty );
684         std::swap( iendx, istx );
685         std::swap( iendy, isty );
686     }
687 
688     int arc;
689 
690     if( aHoleFlag && aPlatedHole )
691         arc = NewContour( true );
692     else
693         arc = NewContour( false );
694 
695     if( arc < 0 )
696     {
697         error = "AddArc(): could not create a contour";
698         return false;
699     }
700 
701     // trace the outer arc:
702     int i;
703     double  ang;
704     double  da = aAngle / osides;
705 
706     for( ang = stAngle, i = 0; i < osides; ang += da, ++i )
707         AddVertex( arc, aCenterX + orad * cos( ang ), aCenterY + orad * sin( ang ) );
708 
709     // trace the first cap
710     double  capx    = ( iendx + oendx ) / 2.0;
711     double  capy    = ( iendy + oendy ) / 2.0;
712 
713     if( aHoleFlag )
714         da = -M_PI / csides;
715     else
716         da = M_PI / csides;
717 
718     for( ang = endAngle, i = 0; i < csides; ang += da, ++i )
719         AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
720 
721     // trace the inner arc:
722     da = -aAngle / isides;
723 
724     for( ang = endAngle, i = 0; i < isides; ang += da, ++i )
725         AddVertex( arc, aCenterX + irad * cos( ang ), aCenterY + irad * sin( ang ) );
726 
727     // trace the final cap
728     capx    = ( istx + ostx ) / 2.0;
729     capy    = ( isty + osty ) / 2.0;
730 
731     if( aHoleFlag )
732         da = -M_PI / csides;
733     else
734         da = M_PI / csides;
735 
736     for( ang = stAngle + M_PI, i = 0; i < csides; ang += da, ++i )
737         AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
738 
739     return true;
740 }
741 
742 
Tesselate(VRML_LAYER * holes,bool aHolesOnly)743 bool VRML_LAYER::Tesselate( VRML_LAYER* holes, bool aHolesOnly )
744 {
745     if( !tess )
746     {
747         error = "Tesselate(): GLU tesselator was not initialized";
748         return false;
749     }
750 
751     pholes  = holes;
752     Fault   = false;
753 
754     if( aHolesOnly )
755         gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NEGATIVE );
756     else
757         gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
758 
759 
760     if( contours.size() < 1 || vertices.size() < 3 )
761     {
762         error = "Tesselate(): not enough vertices";
763         return false;
764     }
765 
766     // finish the winding calculation on all vertices prior to setting 'fix'
767     if( !fix )
768     {
769         for( unsigned int i = 0; i < contours.size(); ++i )
770         {
771             if( contours[i]->size() < 3 )
772                 continue;
773 
774             VERTEX_3D* vp0 = vertices[ contours[i]->back() ];
775             VERTEX_3D* vp1 = vertices[ contours[i]->front() ];
776             areas[i] += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
777         }
778     }
779 
780     // prevent the addition of any further contours and contour vertices
781     fix = true;
782 
783     // clear temporary internals which may have been used in a previous run
784     clearTmp();
785 
786     // request an outline
787     gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE );
788 
789     // adjust internal indices for extra points and holes
790     if( holes )
791         hidx = holes->GetSize();
792     else
793         hidx = 0;
794 
795     eidx = idx + hidx;
796 
797     if( aHolesOnly && ( checkNContours( true ) == 0 ) )
798     {
799         error = "tesselate(): no hole contours";
800         return false;
801     }
802     else if( !aHolesOnly && ( checkNContours( false ) == 0 ) )
803     {
804         error = "tesselate(): no solid contours";
805         return false;
806     }
807 
808     // open the polygon
809     gluTessBeginPolygon( tess, this );
810 
811     if( aHolesOnly )
812     {
813         pholes = NULL;  // do not accept foreign holes
814         hidx = 0;
815         eidx = idx;
816 
817         // add holes
818         pushVertices( true );
819 
820         gluTessEndPolygon( tess );
821 
822         if( Fault )
823             return false;
824 
825         return true;
826     }
827 
828     // add solid outlines
829     pushVertices( false );
830 
831     // close the polygon
832     gluTessEndPolygon( tess );
833 
834     if( Fault )
835         return false;
836 
837     // if there are no outlines we cannot proceed
838     if( outline.empty() )
839     {
840         error = "tesselate(): no points in result";
841         return false;
842     }
843 
844     // at this point we have a solid outline; add it to the tesselator
845     gluTessBeginPolygon( tess, this );
846 
847     if( !pushOutline( NULL ) )
848         return false;
849 
850     // add the holes contained by this object
851     pushVertices( true );
852 
853     // import external holes (if any)
854     if( hidx && ( holes->Import( idx, tess ) < 0 ) )
855     {
856         std::ostringstream ostr;
857         ostr << "Tesselate():FAILED: " << holes->GetError();
858         error = ostr.str();
859         return false;
860     }
861 
862     if( Fault )
863         return false;
864 
865     // erase the previous outline data and vertex order
866     // but preserve the extra vertices
867     while( !outline.empty() )
868     {
869         delete outline.back();
870         outline.pop_back();
871     }
872 
873     ordmap.clear();
874     ord = 0;
875 
876     // go through the vertex lists and reset ephemeral parameters
877     for( unsigned int i = 0; i < vertices.size(); ++i )
878     {
879         vertices[i]->o = -1;
880     }
881 
882     for( unsigned int i = 0; i < extra_verts.size(); ++i )
883     {
884         extra_verts[i]->o = -1;
885     }
886 
887     // close the polygon; this creates the outline points
888     // and the point ordering list 'ordmap'
889     solid.clear();
890     gluTessEndPolygon( tess );
891 
892     // repeat the last operation but request a tesselated surface
893     // rather than an outline; this creates the triangles list.
894     gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE );
895 
896     gluTessBeginPolygon( tess, this );
897 
898     if( !pushOutline( holes ) )
899         return false;
900 
901     gluTessEndPolygon( tess );
902 
903     if( Fault )
904         return false;
905 
906     return true;
907 }
908 
909 
pushOutline(VRML_LAYER * holes)910 bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
911 {
912     // traverse the outline list to push all used vertices
913     if( outline.size() < 1 )
914     {
915         error = "pushOutline() failed: no vertices to push";
916         return false;
917     }
918 
919     std::list<std::list<int>*>::const_iterator obeg = outline.begin();
920     std::list<std::list<int>*>::const_iterator oend = outline.end();
921 
922     int nc = 0; // number of contours pushed
923 
924     int pi;
925     std::list<int>::const_iterator  begin;
926     std::list<int>::const_iterator  end;
927     GLdouble pt[3];
928     VERTEX_3D* vp;
929 
930     while( obeg != oend )
931     {
932         if( (*obeg)->size() < 3 )
933         {
934             ++obeg;
935             continue;
936         }
937 
938         gluTessBeginContour( tess );
939 
940         begin = (*obeg)->begin();
941         end = (*obeg)->end();
942 
943         while( begin != end )
944         {
945             pi = *begin;
946 
947             if( pi < 0 || (unsigned int) pi > ordmap.size() )
948             {
949                 gluTessEndContour( tess );
950                 error = "pushOutline():BUG: *outline.begin() is not a valid index to ordmap";
951                 return false;
952             }
953 
954             // retrieve the actual index
955             pi = ordmap[pi];
956 
957             vp = getVertexByIndex( pi, holes );
958 
959             if( !vp )
960             {
961                 gluTessEndContour( tess );
962                 error = "pushOutline():: BUG: ordmap[n] is not a valid index to vertices[]";
963                 return false;
964             }
965 
966             pt[0]   = vp->x;
967             pt[1]   = vp->y;
968             pt[2]   = 0.0;
969             gluTessVertex( tess, pt, vp );
970             ++begin;
971         }
972 
973         gluTessEndContour( tess );
974         ++obeg;
975         ++nc;
976     }
977 
978     if( !nc )
979     {
980         error = "pushOutline():: no valid contours available";
981         return false;
982     }
983 
984     return true;
985 }
986 
987 
WriteVertices(double aZcoord,std::ostream & aOutFile,int aPrecision)988 bool VRML_LAYER::WriteVertices( double aZcoord, std::ostream& aOutFile, int aPrecision )
989 {
990     if( ordmap.size() < 3 )
991     {
992         error = "WriteVertices(): not enough vertices";
993         return false;
994     }
995 
996     if( aPrecision < 4 )
997         aPrecision = 4;
998 
999     int i, j;
1000 
1001     VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
1002 
1003     if( !vp )
1004         return false;
1005 
1006     std::string strx, stry, strz;
1007     FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
1008     FormatSinglet( aZcoord, aPrecision, strz );
1009 
1010     aOutFile << strx << " " << stry << " " << strz;
1011 
1012     for( i = 1, j = ordmap.size(); i < j; ++i )
1013     {
1014         vp = getVertexByIndex( ordmap[i], pholes );
1015 
1016         if( !vp )
1017             return false;
1018 
1019         FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
1020 
1021         if( i & 1 )
1022             aOutFile << ", " << strx << " " << stry << " " << strz;
1023         else
1024             aOutFile << ",\n" << strx << " " << stry << " " << strz;
1025     }
1026 
1027     return !aOutFile.fail();
1028 }
1029 
1030 
Write3DVertices(double aTopZ,double aBottomZ,std::ostream & aOutFile,int aPrecision)1031 bool VRML_LAYER::Write3DVertices( double aTopZ, double aBottomZ, std::ostream& aOutFile,
1032                                   int aPrecision )
1033 {
1034     if( ordmap.size() < 3 )
1035     {
1036         error = "Write3DVertices(): insufficient vertices";
1037         return false;
1038     }
1039 
1040     if( aPrecision < 4 )
1041         aPrecision = 4;
1042 
1043     if( aTopZ <= aBottomZ )
1044     {
1045         error = "Write3DVertices(): top <= bottom";
1046         return false;
1047     }
1048 
1049     int i, j;
1050 
1051     VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
1052 
1053     if( !vp )
1054         return false;
1055 
1056     std::string strx, stry, strz;
1057     FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
1058     FormatSinglet( aTopZ, aPrecision, strz );
1059 
1060     aOutFile << strx << " " << stry << " " << strz;
1061 
1062     for( i = 1, j = ordmap.size(); i < j; ++i )
1063     {
1064         vp = getVertexByIndex( ordmap[i], pholes );
1065 
1066         if( !vp )
1067             return false;
1068 
1069         FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
1070 
1071         if( i & 1 )
1072             aOutFile << ", " << strx << " " << stry << " " << strz;
1073         else
1074             aOutFile << ",\n" << strx << " " << stry << " " << strz;
1075     }
1076 
1077     // repeat for the bottom layer
1078     vp = getVertexByIndex( ordmap[0], pholes );
1079     FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
1080     FormatSinglet( aBottomZ, aPrecision, strz );
1081 
1082     bool endl;
1083 
1084     if( i & 1 )
1085     {
1086         aOutFile << ", " << strx << " " << stry << " " << strz;
1087         endl = false;
1088     }
1089     else
1090     {
1091         aOutFile << ",\n" << strx << " " << stry << " " << strz;
1092         endl = true;
1093     }
1094 
1095     for( i = 1, j = ordmap.size(); i < j; ++i )
1096     {
1097         vp = getVertexByIndex( ordmap[i], pholes );
1098         FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
1099 
1100         if( endl )
1101         {
1102             aOutFile << ", " << strx << " " << stry << " " << strz;
1103             endl = false;
1104         }
1105         else
1106         {
1107             aOutFile << ",\n" << strx << " " << stry << " " << strz;
1108             endl = true;
1109         }
1110     }
1111 
1112     return !aOutFile.fail();
1113 }
1114 
1115 
WriteIndices(bool aTopFlag,std::ostream & aOutFile)1116 bool VRML_LAYER::WriteIndices( bool aTopFlag, std::ostream& aOutFile )
1117 {
1118     if( triplets.empty() )
1119     {
1120         error = "WriteIndices(): no triplets (triangular facets) to write";
1121         return false;
1122     }
1123 
1124     // go through the triplet list and write out the indices based on order
1125     std::list<TRIPLET_3D>::const_iterator   tbeg    = triplets.begin();
1126     std::list<TRIPLET_3D>::const_iterator   tend    = triplets.end();
1127 
1128     int i = 1;
1129 
1130     if( aTopFlag )
1131         aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3  << ", -1";
1132     else
1133         aOutFile << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3  << ", -1";
1134 
1135     ++tbeg;
1136 
1137     while( tbeg != tend )
1138     {
1139         if( (i++ & 7) == 4 )
1140         {
1141             i = 1;
1142 
1143             if( aTopFlag )
1144                 aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3  << ", -1";
1145             else
1146                 aOutFile << ",\n" << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3  << ", -1";
1147         }
1148         else
1149         {
1150             if( aTopFlag )
1151                 aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3  << ", -1";
1152             else
1153                 aOutFile << ", " << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3  << ", -1";
1154         }
1155 
1156         ++tbeg;
1157     }
1158 
1159     return !aOutFile.fail();
1160 }
1161 
1162 
Write3DIndices(std::ostream & aOutFile,bool aIncludePlatedHoles)1163 bool VRML_LAYER::Write3DIndices( std::ostream& aOutFile, bool aIncludePlatedHoles )
1164 {
1165     if( outline.empty() )
1166     {
1167         error = "WriteIndices(): no outline available";
1168         return false;
1169     }
1170 
1171     char mark;
1172     bool holes_only = triplets.empty();
1173 
1174     int i = 1;
1175     int idx2 = ordmap.size();    // index to the bottom vertices
1176 
1177     if( !holes_only )
1178     {
1179         mark = ',';
1180 
1181         // go through the triplet list and write out the indices based on order
1182         std::list<TRIPLET_3D>::const_iterator   tbeg    = triplets.begin();
1183         std::list<TRIPLET_3D>::const_iterator   tend    = triplets.end();
1184 
1185         // print out the top vertices
1186         aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3  << ", -1";
1187         ++tbeg;
1188 
1189         while( tbeg != tend )
1190         {
1191             if( (i++ & 7) == 4 )
1192             {
1193                 i = 1;
1194                 aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3  << ", -1";
1195             }
1196             else
1197             {
1198                 aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3  << ", -1";
1199             }
1200 
1201             ++tbeg;
1202         }
1203 
1204         // print out the bottom vertices
1205         tbeg = triplets.begin();
1206 
1207         while( tbeg != tend )
1208         {
1209             if( ( i++ & 7 ) == 4 )
1210             {
1211                 i = 1;
1212                 aOutFile << ",\n"
1213                          << ( tbeg->i2 + idx2 ) << ", " << ( tbeg->i1 + idx2 ) << ", "
1214                          << ( tbeg->i3 + idx2 ) << ", -1";
1215             }
1216             else
1217             {
1218                 aOutFile << ", " << ( tbeg->i2 + idx2 ) << ", " << ( tbeg->i1 + idx2 ) << ", "
1219                          << ( tbeg->i3 + idx2 ) << ", -1";
1220             }
1221 
1222             ++tbeg;
1223         }
1224     }
1225     else
1226         mark = ' ';
1227 
1228     // print out indices for the walls joining top to bottom
1229     int lastPoint;
1230     int curPoint;
1231     int curContour = 0;
1232 
1233     std::list<std::list<int>*>::const_iterator  obeg    = outline.begin();
1234     std::list<std::list<int>*>::const_iterator  oend    = outline.end();
1235     std::list<int>* cp;
1236     std::list<int>::const_iterator  cbeg;
1237     std::list<int>::const_iterator  cend;
1238 
1239     i = 2;
1240 
1241     while( obeg != oend )
1242     {
1243         cp = *obeg;
1244 
1245         if( cp->size() < 3 )
1246         {
1247             ++obeg;
1248             ++curContour;
1249             continue;
1250         }
1251 
1252         cbeg      = cp->begin();
1253         cend      = cp->end();
1254         lastPoint = *(cbeg++);
1255 
1256         // skip all PTH vertices which are not in a solid outline
1257         if( !aIncludePlatedHoles && !solid[curContour]
1258             && getVertexByIndex( ordmap[lastPoint], pholes )->pth )
1259         {
1260             ++obeg;
1261             ++curContour;
1262             continue;
1263         }
1264 
1265         while( cbeg != cend )
1266         {
1267             curPoint = *(cbeg++);
1268 
1269             if( !holes_only )
1270             {
1271                 if( ( i++ & 3 ) == 2 )
1272                 {
1273                     i = 1;
1274                     aOutFile << mark << "\n"
1275                              << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
1276                     aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", "
1277                              << lastPoint + idx2 << ", -1";
1278                 }
1279                 else
1280                 {
1281                     aOutFile << mark << " " << curPoint << ", " << lastPoint << ", "
1282                              << curPoint + idx2;
1283                     aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", "
1284                              << lastPoint + idx2 << ", -1";
1285                 }
1286             }
1287             else
1288             {
1289                 if( (i++ & 3) == 2 )
1290                 {
1291                     i = 1;
1292                     aOutFile << mark << "\n"
1293                              << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
1294                     aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", "
1295                              << lastPoint << ", -1";
1296                 }
1297                 else
1298                 {
1299                     aOutFile << mark << " " << curPoint << ", " << curPoint + idx2 << ", "
1300                              << lastPoint;
1301                     aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", "
1302                              << lastPoint << ", -1";
1303                 }
1304             }
1305 
1306             mark = ',';
1307             lastPoint = curPoint;
1308         }
1309 
1310         // check if the loop needs to be closed
1311         cbeg = cp->begin();
1312         cend = --cp->end();
1313 
1314         curPoint = *(cbeg);
1315         lastPoint  = *(cend);
1316 
1317         if( !holes_only )
1318         {
1319             if( ( i++ & 3 ) == 2 )
1320             {
1321                 aOutFile << ",\n" << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
1322                 aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", "
1323                          << lastPoint + idx2 << ", -1";
1324             }
1325             else
1326             {
1327                 aOutFile << ", " << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
1328                 aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", "
1329                          << lastPoint + idx2 << ", -1";
1330             }
1331         }
1332         else
1333         {
1334             if( ( i++ & 3 ) == 2 )
1335             {
1336                 aOutFile << ",\n" << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
1337                 aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", "
1338                          << lastPoint << ", -1";
1339             }
1340             else
1341             {
1342                 aOutFile << ", " << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
1343                 aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", "
1344                          << lastPoint << ", -1";
1345             }
1346         }
1347 
1348         ++obeg;
1349         ++curContour;
1350     }
1351 
1352     return !aOutFile.fail();
1353 }
1354 
1355 
addTriplet(VERTEX_3D * p0,VERTEX_3D * p1,VERTEX_3D * p2)1356 bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 )
1357 {
1358     double  dx0 = p1->x - p0->x;
1359     double  dx1 = p2->x - p0->x;
1360     double  dx2 = p2->x - p1->x;
1361 
1362     double  dy0 = p1->y - p0->y;
1363     double  dy1 = p2->y - p0->y;
1364     double  dy2 = p2->y - p1->y;
1365 
1366     dx0 *= dx0;
1367     dx1 *= dx1;
1368     dx2 *= dx2;
1369 
1370     dy0 *= dy0;
1371     dy1 *= dy1;
1372     dy2 *= dy2;
1373 
1374     // this number is chosen because we shall only write 9 decimal places
1375     // at most on the VRML output
1376     double err = 0.000000001;
1377 
1378     // test if the triangles are degenerate (equal points)
1379     if( ( dx0 + dy0 ) < err )
1380         return false;
1381 
1382     if( ( dx1 + dy1 ) < err )
1383         return false;
1384 
1385     if( ( dx2 + dy2 ) < err )
1386         return false;
1387 
1388     triplets.emplace_back( p0->o, p1->o, p2->o );
1389 
1390     return true;
1391 }
1392 
1393 
AddExtraVertex(double aXpos,double aYpos,bool aPlatedHole)1394 VERTEX_3D* VRML_LAYER::AddExtraVertex( double aXpos, double aYpos, bool aPlatedHole )
1395 {
1396     VERTEX_3D* vertex = new VERTEX_3D;
1397 
1398     if( eidx == 0 )
1399         eidx = idx + hidx;
1400 
1401     vertex->x   = aXpos;
1402     vertex->y   = aYpos;
1403     vertex->i   = eidx++;
1404     vertex->o   = -1;
1405     vertex->pth = aPlatedHole;
1406 
1407     extra_verts.push_back( vertex );
1408 
1409     return vertex;
1410 }
1411 
1412 
glStart(GLenum cmd)1413 void VRML_LAYER::glStart( GLenum cmd )
1414 {
1415     glcmd = cmd;
1416 
1417     while( !vlist.empty() )
1418         vlist.pop_back();
1419 }
1420 
1421 
glPushVertex(VERTEX_3D * vertex)1422 void VRML_LAYER::glPushVertex( VERTEX_3D* vertex )
1423 {
1424     if( vertex->o < 0 )
1425     {
1426         vertex->o = ord++;
1427         ordmap.push_back( vertex->i );
1428     }
1429 
1430     vlist.push_back( vertex );
1431 }
1432 
1433 
glEnd(void)1434 void VRML_LAYER::glEnd( void )
1435 {
1436     switch( glcmd )
1437     {
1438     case GL_LINE_LOOP:
1439     {
1440         // add the loop to the list of outlines
1441         std::list<int>* loop = new std::list<int>;
1442 
1443         double firstX = 0.0;
1444         double firstY = 0.0;
1445         double lastX = 0.0;
1446         double lastY = 0.0;
1447         double curX, curY;
1448         double area = 0.0;
1449 
1450         if( vlist.size() > 0 )
1451         {
1452             loop->push_back( vlist[0]->o );
1453             firstX = vlist[0]->x;
1454             firstY = vlist[0]->y;
1455             lastX = firstX;
1456             lastY = firstY;
1457         }
1458 
1459         for( size_t i = 1; i < vlist.size(); ++i )
1460         {
1461             loop->push_back( vlist[i]->o );
1462             curX = vlist[i]->x;
1463             curY = vlist[i]->y;
1464             area += ( curX - lastX ) * ( curY + lastY );
1465             lastX = curX;
1466             lastY = curY;
1467         }
1468 
1469         area += ( firstX - lastX ) * ( firstY + lastY );
1470 
1471         outline.push_back( loop );
1472 
1473         if( area <= 0.0 )
1474             solid.push_back( true );
1475         else
1476             solid.push_back( false );
1477         }
1478 
1479         break;
1480 
1481     case GL_TRIANGLE_FAN:
1482         processFan();
1483         break;
1484 
1485     case GL_TRIANGLE_STRIP:
1486         processStrip();
1487         break;
1488 
1489     case GL_TRIANGLES:
1490         processTri();
1491         break;
1492 
1493     default:
1494         break;
1495     }
1496 
1497     while( !vlist.empty() )
1498         vlist.pop_back();
1499 
1500     glcmd = 0;
1501 }
1502 
1503 
SetGLError(GLenum errorID)1504 void VRML_LAYER::SetGLError( GLenum errorID )
1505 {
1506     const char * msg = (const char*)gluErrorString( errorID );
1507 
1508     // If errorID is an illegal id, gluErrorString returns NULL
1509     if( msg )
1510         error = msg;
1511     else
1512         error.clear();
1513 
1514     if( error.empty() )
1515     {
1516         std::ostringstream ostr;
1517         ostr << "Unknown OpenGL error: " << errorID;
1518         error = ostr.str();
1519     }
1520 }
1521 
1522 
processFan(void)1523 void VRML_LAYER::processFan( void )
1524 {
1525     if( vlist.size() < 3 )
1526         return;
1527 
1528     VERTEX_3D* p0 = vlist[0];
1529 
1530     int i;
1531     int end = vlist.size();
1532 
1533     for( i = 2; i < end; ++i )
1534     {
1535         addTriplet( p0, vlist[i - 1], vlist[i] );
1536     }
1537 }
1538 
1539 
processStrip(void)1540 void VRML_LAYER::processStrip( void )
1541 {
1542     // note: (source: http://www.opengl.org/wiki/Primitive)
1543     // GL_TRIANGLE_STRIP​: Every group of 3 adjacent vertices forms a triangle.
1544     // The face direction of the strip is determined by the winding of the
1545     // first triangle. Each successive triangle will have its effective face
1546     // order reverse, so the system compensates for that by testing it in the
1547     // opposite way. A vertex stream of n length will generate n-2 triangles.
1548 
1549     if( vlist.size() < 3 )
1550         return;
1551 
1552     int i;
1553     int end = vlist.size();
1554     bool flip = false;
1555 
1556     for( i = 2; i < end; ++i )
1557     {
1558         if( flip )
1559         {
1560             addTriplet( vlist[i - 1], vlist[i - 2], vlist[i] );
1561             flip = false;
1562         }
1563         else
1564         {
1565             addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
1566             flip = true;
1567         }
1568     }
1569 }
1570 
1571 
processTri(void)1572 void VRML_LAYER::processTri( void )
1573 {
1574     // notes:
1575     // 1. each successive group of 3 vertices is a triangle
1576     // 2. as per OpenGL specification, any incomplete triangles are to be ignored
1577 
1578     if( vlist.size() < 3 )
1579         return;
1580 
1581     int i;
1582     int end = vlist.size();
1583 
1584     for( i = 2; i < end; i += 3 )
1585         addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
1586 }
1587 
1588 
checkNContours(bool holes)1589 int VRML_LAYER::checkNContours( bool holes )
1590 {
1591     int nc = 0;     // number of contours
1592 
1593     if( contours.empty() )
1594         return 0;
1595 
1596     for( size_t i = 0; i < contours.size(); ++i )
1597     {
1598         if( contours[i]->size() < 3 )
1599             continue;
1600 
1601         if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) )
1602             continue;
1603 
1604         ++nc;
1605     }
1606 
1607     return nc;
1608 }
1609 
1610 
pushVertices(bool holes)1611 void VRML_LAYER::pushVertices( bool holes )
1612 {
1613     // push the internally held vertices
1614     unsigned int i;
1615 
1616     std::list<int>::const_iterator  begin;
1617     std::list<int>::const_iterator  end;
1618     GLdouble pt[3];
1619     VERTEX_3D* vp;
1620 
1621     for( i = 0; i < contours.size(); ++i )
1622     {
1623         if( contours[i]->size() < 3 )
1624             continue;
1625 
1626         if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) )
1627             continue;
1628 
1629         gluTessBeginContour( tess );
1630 
1631         begin = contours[i]->begin();
1632         end = contours[i]->end();
1633 
1634         while( begin != end )
1635         {
1636             vp = vertices[ *begin ];
1637             pt[0]   = vp->x;
1638             pt[1]   = vp->y;
1639             pt[2]   = 0.0;
1640             gluTessVertex( tess, pt, vp );
1641             ++begin;
1642         }
1643 
1644         gluTessEndContour( tess );
1645     }
1646 
1647     return;
1648 }
1649 
1650 
getVertexByIndex(int aPointIndex,VRML_LAYER * holes)1651 VERTEX_3D* VRML_LAYER::getVertexByIndex( int aPointIndex, VRML_LAYER* holes )
1652 {
1653     if( aPointIndex < 0 || (unsigned int) aPointIndex >= ( idx + hidx + extra_verts.size() ) )
1654     {
1655         error = "getVertexByIndex():BUG: invalid index";
1656         return NULL;
1657     }
1658 
1659     if( aPointIndex < idx )
1660     {
1661         // vertex is in the vertices[] list
1662         return vertices[ aPointIndex ];
1663     }
1664     else if( aPointIndex >= idx + hidx )
1665     {
1666         // vertex is in the extra_verts[] list
1667         return extra_verts[aPointIndex - idx - hidx];
1668     }
1669 
1670     // vertex is in the holes object
1671     if( !holes )
1672     {
1673         error = "getVertexByIndex():BUG: invalid index";
1674         return NULL;
1675     }
1676 
1677     VERTEX_3D* vp = holes->GetVertexByIndex( aPointIndex );
1678 
1679     if( !vp )
1680     {
1681         std::ostringstream ostr;
1682         ostr << "getVertexByIndex():FAILED: " << holes->GetError();
1683         error = ostr.str();
1684         return NULL;
1685     }
1686 
1687     return vp;
1688 }
1689 
1690 
GetSize(void)1691 int VRML_LAYER::GetSize( void )
1692 {
1693     return vertices.size();
1694 }
1695 
1696 
Import(int start,GLUtesselator * aTesselator)1697 int VRML_LAYER::Import( int start, GLUtesselator* aTesselator )
1698 {
1699     if( start < 0 )
1700     {
1701         error = "Import(): invalid index ( start < 0 )";
1702         return -1;
1703     }
1704 
1705     if( !aTesselator )
1706     {
1707         error = "Import(): NULL tesselator pointer";
1708         return -1;
1709     }
1710 
1711     unsigned int i, j;
1712 
1713     // renumber from 'start'
1714     for( i = 0, j = vertices.size(); i < j; ++i )
1715     {
1716         vertices[i]->i = start++;
1717         vertices[i]->o = -1;
1718     }
1719 
1720     // push each contour to the tesselator
1721     VERTEX_3D* vp;
1722     GLdouble pt[3];
1723 
1724     std::list<int>::const_iterator cbeg;
1725     std::list<int>::const_iterator cend;
1726 
1727     for( i = 0; i < contours.size(); ++i )
1728     {
1729         if( contours[i]->size() < 3 )
1730             continue;
1731 
1732         cbeg = contours[i]->begin();
1733         cend = contours[i]->end();
1734 
1735         gluTessBeginContour( aTesselator );
1736 
1737         while( cbeg != cend )
1738         {
1739             vp = vertices[ *cbeg++ ];
1740             pt[0] = vp->x;
1741             pt[1] = vp->y;
1742             pt[2] = 0.0;
1743             gluTessVertex( aTesselator, pt, vp );
1744         }
1745 
1746         gluTessEndContour( aTesselator );
1747     }
1748 
1749     return start;
1750 }
1751 
1752 
GetVertexByIndex(int aPointIndex)1753 VERTEX_3D* VRML_LAYER::GetVertexByIndex( int aPointIndex )
1754 {
1755     int i0 = vertices[0]->i;
1756 
1757     if( aPointIndex < i0 || aPointIndex >= ( i0 + (int) vertices.size() ) )
1758     {
1759         error = "GetVertexByIndex(): invalid index";
1760         return NULL;
1761     }
1762 
1763     return vertices[aPointIndex - i0];
1764 }
1765 
1766 
GetError(void)1767 const std::string& VRML_LAYER::GetError( void )
1768 {
1769     return error;
1770 }
1771 
1772 
SetVertexOffsets(double aXoffset,double aYoffset)1773 void VRML_LAYER::SetVertexOffsets( double aXoffset, double aYoffset )
1774 {
1775     offsetX = aXoffset;
1776     offsetY = aYoffset;
1777     return;
1778 }
1779 
1780 
Get3DTriangles(std::vector<double> & aVertexList,std::vector<int> & aIndexPlane,std::vector<int> & aIndexSide,double aTopZ,double aBotZ)1781 bool VRML_LAYER::Get3DTriangles( std::vector< double >& aVertexList,
1782                                  std::vector< int > &aIndexPlane, std::vector< int > &aIndexSide,
1783                                  double aTopZ, double aBotZ )
1784 {
1785     aVertexList.clear();
1786     aIndexPlane.clear();
1787     aIndexSide.clear();
1788 
1789     if( ordmap.size() < 3 || outline.empty() )
1790         return false;
1791 
1792     if( aTopZ <= aBotZ )
1793     {
1794         double tmp = aBotZ;
1795         aBotZ = aTopZ;
1796         aTopZ = tmp;
1797     }
1798 
1799     VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
1800 
1801     if( !vp )
1802         return false;
1803 
1804     size_t i;
1805     size_t vsize = ordmap.size();
1806 
1807     // top vertices
1808     for( i = 0; i < vsize; ++i )
1809     {
1810         vp = getVertexByIndex( ordmap[i], pholes );
1811 
1812         if( !vp )
1813         {
1814             aVertexList.clear();
1815             return false;
1816         }
1817 
1818         aVertexList.push_back( vp->x + offsetX );
1819         aVertexList.push_back( vp->y + offsetY );
1820         aVertexList.push_back( aTopZ );
1821     }
1822 
1823     // bottom vertices
1824     for( i = 0; i < vsize; ++i )
1825     {
1826         vp = getVertexByIndex( ordmap[i], pholes );
1827 
1828         aVertexList.push_back( vp->x + offsetX );
1829         aVertexList.push_back( vp->y + offsetY );
1830         aVertexList.push_back( aBotZ );
1831     }
1832 
1833     // create the index lists .. it is difficult to estimate the list size
1834     // a priori so instead we use a vector to help
1835 
1836     bool holes_only = triplets.empty();
1837 
1838     if( !holes_only )
1839     {
1840         // go through the triplet list and write out the indices based on order
1841         std::list< TRIPLET_3D >::const_iterator tbeg = triplets.begin();
1842         std::list< TRIPLET_3D >::const_iterator tend = triplets.end();
1843 
1844         std::vector< int > aIndexBot;
1845 
1846         while( tbeg != tend )
1847         {
1848             // top vertices
1849             aIndexPlane.push_back( (int) tbeg->i1 );
1850             aIndexPlane.push_back( (int) tbeg->i2 );
1851             aIndexPlane.push_back( (int) tbeg->i3 );
1852 
1853             // bottom vertices
1854             aIndexBot.push_back( (int) ( tbeg->i2 + vsize ) );
1855             aIndexBot.push_back( (int) ( tbeg->i1 + vsize ) );
1856             aIndexBot.push_back( (int) ( tbeg->i3 + vsize ) );
1857 
1858             ++tbeg;
1859         }
1860 
1861         aIndexPlane.insert( aIndexPlane.end(), aIndexBot.begin(), aIndexBot.end() );
1862     }
1863 
1864     // compile indices for the walls joining top to bottom
1865     int lastPoint;
1866     int curPoint;
1867     int curContour = 0;
1868 
1869     std::list< std::list< int >* >::const_iterator  obeg = outline.begin();
1870     std::list< std::list< int >* >::const_iterator  oend = outline.end();
1871     std::list< int >* cp;
1872     std::list< int >::const_iterator  cbeg;
1873     std::list< int >::const_iterator  cend;
1874 
1875     i = 2;
1876 
1877     while( obeg != oend )
1878     {
1879         cp = *obeg;
1880 
1881         if( cp->size() < 3 )
1882         {
1883             ++obeg;
1884             ++curContour;
1885             continue;
1886         }
1887 
1888         cbeg      = cp->begin();
1889         cend      = cp->end();
1890         lastPoint = *(cbeg++);
1891 
1892         while( cbeg != cend )
1893         {
1894             curPoint = *(cbeg++);
1895 
1896             if( !holes_only )
1897             {
1898                 aIndexSide.push_back( curPoint );
1899                 aIndexSide.push_back( lastPoint );
1900                 aIndexSide.push_back( (int)( curPoint + vsize ) );
1901 
1902                 aIndexSide.push_back( (int)( curPoint + vsize ) );
1903                 aIndexSide.push_back( lastPoint );
1904                 aIndexSide.push_back( (int)( lastPoint + vsize ) );
1905             }
1906             else
1907             {
1908                 aIndexSide.push_back( curPoint );
1909                 aIndexSide.push_back( (int)( curPoint + vsize ) );
1910                 aIndexSide.push_back( lastPoint );
1911 
1912                 aIndexSide.push_back( (int)( curPoint + vsize ) );
1913                 aIndexSide.push_back( (int)( lastPoint + vsize ) );
1914                 aIndexSide.push_back( lastPoint );
1915             }
1916 
1917             lastPoint = curPoint;
1918         }
1919 
1920         // check if the loop needs to be closed
1921         cbeg = cp->begin();
1922         cend = --cp->end();
1923 
1924         curPoint = *(cbeg);
1925         lastPoint  = *(cend);
1926 
1927         if( !holes_only )
1928         {
1929             aIndexSide.push_back( curPoint );
1930             aIndexSide.push_back( lastPoint );
1931             aIndexSide.push_back( (int)( curPoint + vsize ) );
1932 
1933             aIndexSide.push_back( (int)( curPoint + vsize ) );
1934             aIndexSide.push_back( lastPoint );
1935             aIndexSide.push_back( (int)( lastPoint + vsize ) );
1936         }
1937         else
1938         {
1939             aIndexSide.push_back( curPoint );
1940             aIndexSide.push_back( (int)( curPoint + vsize ) );
1941             aIndexSide.push_back( lastPoint );
1942 
1943             aIndexSide.push_back( (int)( curPoint + vsize ) );
1944             aIndexSide.push_back( (int)( lastPoint + vsize ) );
1945             aIndexSide.push_back( lastPoint );
1946         }
1947 
1948         ++obeg;
1949         ++curContour;
1950     }
1951 
1952     return true;
1953 }
1954 
1955 
Get2DTriangles(std::vector<double> & aVertexList,std::vector<int> & aIndexPlane,double aHeight,bool aTopPlane)1956 bool VRML_LAYER::Get2DTriangles( std::vector< double >& aVertexList,
1957                                  std::vector< int > &aIndexPlane, double aHeight, bool aTopPlane )
1958 {
1959     aVertexList.clear();
1960     aIndexPlane.clear();
1961 
1962     if( ordmap.size() < 3 || outline.empty() )
1963         return false;
1964 
1965     VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
1966 
1967     if( !vp )
1968         return false;
1969 
1970     size_t i;
1971     size_t vsize = ordmap.size();
1972 
1973     // vertices
1974     for( i = 0; i < vsize; ++i )
1975     {
1976         vp = getVertexByIndex( ordmap[i], pholes );
1977 
1978         if( !vp )
1979         {
1980             aVertexList.clear();
1981             return false;
1982         }
1983 
1984         aVertexList.push_back( vp->x + offsetX );
1985         aVertexList.push_back( vp->y + offsetY );
1986         aVertexList.push_back( aHeight );
1987     }
1988 
1989     // create the index lists .. it is difficult to estimate the list size
1990     // a priori so instead we use a vector to help
1991 
1992     if( triplets.empty() )
1993         return false;
1994 
1995     // go through the triplet list and write out the indices based on order
1996     std::list< TRIPLET_3D >::const_iterator tbeg = triplets.begin();
1997     std::list< TRIPLET_3D >::const_iterator tend = triplets.end();
1998 
1999     if( aTopPlane )
2000     {
2001         while( tbeg != tend )
2002         {
2003             // top vertices
2004             aIndexPlane.push_back( (int) tbeg->i1 );
2005             aIndexPlane.push_back( (int) tbeg->i2 );
2006             aIndexPlane.push_back( (int) tbeg->i3 );
2007 
2008             ++tbeg;
2009         }
2010     }
2011     else
2012     {
2013         while( tbeg != tend )
2014         {
2015             // bottom vertices
2016             aIndexPlane.push_back( (int) ( tbeg->i2 ) );
2017             aIndexPlane.push_back( (int) ( tbeg->i1 ) );
2018             aIndexPlane.push_back( (int) ( tbeg->i3 ) );
2019 
2020             ++tbeg;
2021         }
2022     }
2023 
2024     return true;
2025 }
2026