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