KiCad PCB EDA Suite
export_vrml.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2009-2013 Lorenzo Mercantonio
5  * Copyright (C) 2014-2017 Cirilo Bernardo
6  * Copyright (C) 2018 Jean-Pierre Charras jp.charras at wanadoo.fr
7  * Copyright (C) 2004-2018 KiCad Developers, see AUTHORS.txt for contributors.
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 #include <cmath>
28 #include <exception>
29 #include <fstream>
30 #include <iomanip>
31 #include <vector>
32 #include <wx/dir.h>
33 
34 #include "3d_cache/3d_cache.h"
35 #include "3d_cache/3d_info.h"
36 #include "class_board.h"
37 #include "class_edge_mod.h"
38 #include "class_module.h"
39 #include "class_pcb_text.h"
40 #include "class_track.h"
41 #include "class_zone.h"
42 #include "convert_to_biu.h"
43 #include "draw_graphic_text.h"
44 #include "macros.h"
45 #include "pgm_base.h"
46 #include "plugins/3dapi/ifsg_all.h"
47 #include "streamwrapper.h"
48 #include "vrml_layer.h"
49 #include "pcb_edit_frame.h"
50 #include "../../kicad/kicad.h"
51 
53 
54 #include <zone_filler.h>
55 
56 // minimum width (mm) of a VRML line
57 #define MIN_VRML_LINEWIDTH 0.05 // previously 0.12
58 
59 // offset for art layers, mm (silk, paste, etc)
60 #define ART_OFFSET 0.025
61 // offset for plating
62 #define PLATE_OFFSET 0.005
63 
64 static S3D_CACHE* cache;
65 static bool USE_INLINES; // true to use legacy inline{} behavior
66 static bool USE_DEFS; // true to reuse component definitions
67 static bool USE_RELPATH; // true to use relative paths in VRML inline{}
68 static double WORLD_SCALE = 1.0; // scaling from 0.1 in to desired VRML unit
69 static double BOARD_SCALE; // scaling from mm to desired VRML world scale
70 static const int PRECISION = 6; // legacy precision factor (now set to 6)
71 static wxString SUBDIR_3D; // legacy 3D subdirectory
72 static wxString PROJ_DIR; // project directory
73 
74 struct VRML_COLOR
75 {
76  float diffuse_red;
77  float diffuse_grn;
78  float diffuse_blu;
79 
80  float spec_red;
81  float spec_grn;
82  float spec_blu;
83 
84  float emit_red;
85  float emit_grn;
86  float emit_blu;
87 
88  float ambient;
89  float transp;
90  float shiny;
91 
93  {
94  // default green
95  diffuse_red = 0.13;
96  diffuse_grn = 0.81;
97  diffuse_blu = 0.22;
98  spec_red = 0.01;
99  spec_grn = 0.08;
100  spec_blu = 0.02;
101  emit_red = 0.0;
102  emit_grn = 0.0;
103  emit_blu = 0.0;
104 
105  ambient = 0.8;
106  transp = 0;
107  shiny = 0.02;
108  }
109 
110  VRML_COLOR( float dr, float dg, float db,
111  float sr, float sg, float sb,
112  float er, float eg, float eb,
113  float am, float tr, float sh )
114  {
115  diffuse_red = dr;
116  diffuse_grn = dg;
117  diffuse_blu = db;
118  spec_red = sr;
119  spec_grn = sg;
120  spec_blu = sb;
121  emit_red = er;
122  emit_grn = eg;
123  emit_blu = eb;
124 
125  ambient = am;
126  transp = tr;
127  shiny = sh;
128  }
129 };
130 
132 {
139 };
140 
142 static SGNODE* sgmaterial[VRML_COLOR_LAST] = { NULL };
143 
145 {
146 private:
147  double m_layer_z[PCB_LAYER_ID_COUNT];
148 
149  int m_iMaxSeg; // max. sides to a small circle
150  double m_arcMinLen, m_arcMaxLen; // min and max lengths of an arc chord
151 
152 public:
154  VRML_LAYER m_holes;
155  VRML_LAYER m_board;
156  VRML_LAYER m_top_copper;
157  VRML_LAYER m_bot_copper;
158  VRML_LAYER m_top_silk;
159  VRML_LAYER m_bot_silk;
160  VRML_LAYER m_top_tin;
161  VRML_LAYER m_bot_tin;
162  VRML_LAYER m_plated_holes;
163 
164  std::list< SGNODE* > m_components;
165 
167 
168  double m_minLineWidth; // minimum width of a VRML line segment
169 
170  double m_tx; // global translation along X
171  double m_ty; // global translation along Y
172 
173  double m_brd_thickness; // depth of the PCB
174 
177 
178  MODEL_VRML() : m_OutputPCB( (SGNODE*) NULL )
179  {
180  for( unsigned i = 0; i < DIM( m_layer_z ); ++i )
181  m_layer_z[i] = 0;
182 
183  m_holes.GetArcParams( m_iMaxSeg, m_arcMinLen, m_arcMaxLen );
184 
185  // this default only makes sense if the output is in mm
186  m_brd_thickness = 1.6;
187 
188  // pcb green
189  colors[ VRML_COLOR_PCB ] = VRML_COLOR( .07, .3, .12, .01, .03, .01,
190  0, 0, 0, 0.8, 0, 0.02 );
191  // track green
192  colors[ VRML_COLOR_TRACK ] = VRML_COLOR( .08, .5, .1, .01, .05, .01,
193  0, 0, 0, 0.8, 0, 0.02 );
194  // silkscreen white
195  colors[ VRML_COLOR_SILK ] = VRML_COLOR( .9, .9, .9, .1, .1, .1,
196  0, 0, 0, 0.9, 0, 0.02 );
197  // pad silver
198  colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761,
199  0, 0, 0, 0.8, 0, 0.8 );
200 
201  m_plainPCB = false;
202  SetOffset( 0.0, 0.0 );
203  m_text_layer = F_Cu;
204  m_text_width = 1;
205  m_minLineWidth = MIN_VRML_LINEWIDTH;
206  }
207 
209  {
210  // destroy any unassociated material appearances
211  for( int j = 0; j < VRML_COLOR_LAST; ++j )
212  {
213  if( sgmaterial[j] && NULL == S3D::GetSGNodeParent( sgmaterial[j] ) )
214  S3D::DestroyNode( sgmaterial[j] );
215 
216  sgmaterial[j] = NULL;
217  }
218 
219  if( !m_components.empty() )
220  {
221  IFSG_TRANSFORM tmp( false );
222 
223  for( auto i : m_components )
224  {
225  tmp.Attach( i );
226  tmp.SetParent( NULL );
227  }
228 
229  m_components.clear();
230  m_OutputPCB.Destroy();
231  }
232  }
233 
235  {
236  return colors[aIndex];
237  }
238 
239  void SetOffset( double aXoff, double aYoff )
240  {
241  m_tx = aXoff;
242  m_ty = -aYoff;
243 
244  m_holes.SetVertexOffsets( aXoff, aYoff );
245  m_board.SetVertexOffsets( aXoff, aYoff );
246  m_top_copper.SetVertexOffsets( aXoff, aYoff );
247  m_bot_copper.SetVertexOffsets( aXoff, aYoff );
248  m_top_silk.SetVertexOffsets( aXoff, aYoff );
249  m_bot_silk.SetVertexOffsets( aXoff, aYoff );
250  m_top_tin.SetVertexOffsets( aXoff, aYoff );
251  m_bot_tin.SetVertexOffsets( aXoff, aYoff );
252  m_plated_holes.SetVertexOffsets( aXoff, aYoff );
253  }
254 
255  double GetLayerZ( LAYER_NUM aLayer )
256  {
257  if( unsigned( aLayer ) >= DIM( m_layer_z ) )
258  return 0;
259 
260  return m_layer_z[ aLayer ];
261  }
262 
263  void SetLayerZ( LAYER_NUM aLayer, double aValue )
264  {
265  m_layer_z[aLayer] = aValue;
266  }
267 
268  // set the scaling of the VRML world
269  bool SetScale( double aWorldScale )
270  {
271  if( aWorldScale < 0.001 || aWorldScale > 10.0 )
272  throw( std::runtime_error( "WorldScale out of range (valid range is 0.001 to 10.0)" ) );
273 
274  m_OutputPCB.SetScale( aWorldScale * 2.54 );
275  WORLD_SCALE = aWorldScale * 2.54;
276 
277  return true;
278  }
279 
280 };
281 
282 
283 // static var. for dealing with text
285 
286 
287 // select the VRML layer object to draw on; return true if
288 // a layer has been selected.
289 static bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer )
290 {
291  switch( layer )
292  {
293  case B_Cu:
294  *vlayer = &aModel.m_bot_copper;
295  break;
296 
297  case F_Cu:
298  *vlayer = &aModel.m_top_copper;
299  break;
300 
301  case B_SilkS:
302  *vlayer = &aModel.m_bot_silk;
303  break;
304 
305  case F_SilkS:
306  *vlayer = &aModel.m_top_silk;
307  break;
308 
309  default:
310  return false;
311  }
312 
313  return true;
314 }
315 
316 static void create_vrml_shell( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
317  VRML_LAYER* layer, double top_z, double bottom_z );
318 
319 static void create_vrml_plane( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
320  VRML_LAYER* layer, double aHeight, bool aTopPlane );
321 
322 static void write_triangle_bag( std::ostream& aOut_file, VRML_COLOR& aColor,
323  VRML_LAYER* aLayer, bool aPlane, bool aTop,
324  double aTop_z, double aBottom_z )
325 {
326  /* A lot of nodes are not required, but blender sometimes chokes
327  * without them */
328  static const char* shape_boiler[] =
329  {
330  "Transform {\n",
331  " children [\n",
332  " Group {\n",
333  " children [\n",
334  " Shape {\n",
335  " appearance Appearance {\n",
336  " material Material {\n",
337  0, // Material marker
338  " }\n",
339  " }\n",
340  " geometry IndexedFaceSet {\n",
341  " solid TRUE\n",
342  " coord Coordinate {\n",
343  " point [\n",
344  0, // Coordinates marker
345  " ]\n",
346  " }\n",
347  " coordIndex [\n",
348  0, // Index marker
349  " ]\n",
350  " }\n",
351  " }\n",
352  " ]\n",
353  " }\n",
354  " ]\n",
355  "}\n",
356  0 // End marker
357  };
358 
359  int marker_found = 0, lineno = 0;
360 
361  while( marker_found < 4 )
362  {
363  if( shape_boiler[lineno] )
364  aOut_file << shape_boiler[lineno];
365  else
366  {
367  marker_found++;
368 
369  switch( marker_found )
370  {
371  case 1: // Material marker
372  aOut_file << " diffuseColor " << std::setprecision(3);
373  aOut_file << aColor.diffuse_red << " ";
374  aOut_file << aColor.diffuse_grn << " ";
375  aOut_file << aColor.diffuse_blu << "\n";
376 
377  aOut_file << " specularColor ";
378  aOut_file << aColor.spec_red << " ";
379  aOut_file << aColor.spec_grn << " ";
380  aOut_file << aColor.spec_blu << "\n";
381 
382  aOut_file << " emissiveColor ";
383  aOut_file << aColor.emit_red << " ";
384  aOut_file << aColor.emit_grn << " ";
385  aOut_file << aColor.emit_blu << "\n";
386 
387  aOut_file << " ambientIntensity " << aColor.ambient << "\n";
388  aOut_file << " transparency " << aColor.transp << "\n";
389  aOut_file << " shininess " << aColor.shiny << "\n";
390  break;
391 
392  case 2:
393 
394  if( aPlane )
395  aLayer->WriteVertices( aTop_z, aOut_file, PRECISION );
396  else
397  aLayer->Write3DVertices( aTop_z, aBottom_z, aOut_file, PRECISION );
398 
399  aOut_file << "\n";
400  break;
401 
402  case 3:
403 
404  if( aPlane )
405  aLayer->WriteIndices( aTop, aOut_file );
406  else
407  aLayer->Write3DIndices( aOut_file );
408 
409  aOut_file << "\n";
410  break;
411 
412  default:
413  break;
414  }
415  }
416 
417  lineno++;
418  }
419 }
420 
421 
422 static void write_layers( MODEL_VRML& aModel, BOARD* aPcb,
423  const char* aFileName, OSTREAM* aOutputFile )
424 {
425  // VRML_LAYER board;
426  aModel.m_board.Tesselate( &aModel.m_holes );
427  double brdz = aModel.m_brd_thickness / 2.0
428  - ( Millimeter2iu( ART_OFFSET / 2.0 ) ) * BOARD_SCALE;
429 
430  if( USE_INLINES )
431  {
432  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_PCB ),
433  &aModel.m_board, false, false, brdz, -brdz );
434  }
435  else
436  {
437  create_vrml_shell( aModel.m_OutputPCB, VRML_COLOR_PCB, &aModel.m_board, brdz, -brdz );
438  }
439 
440  if( aModel.m_plainPCB )
441  {
442  if( !USE_INLINES )
443  S3D::WriteVRML( aFileName, true, aModel.m_OutputPCB.GetRawPtr(), USE_DEFS, true );
444 
445  return;
446  }
447 
448  // VRML_LAYER m_top_copper;
449  aModel.m_top_copper.Tesselate( &aModel.m_holes );
450 
451  if( USE_INLINES )
452  {
453  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TRACK ),
454  &aModel.m_top_copper, true, true,
455  aModel.GetLayerZ( F_Cu ), 0 );
456  }
457  else
458  {
460  aModel.GetLayerZ( F_Cu ), true );
461  }
462 
463  // VRML_LAYER m_top_tin;
464  aModel.m_top_tin.Tesselate( &aModel.m_holes );
465 
466  if( USE_INLINES )
467  {
468  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TIN ),
469  &aModel.m_top_tin, true, true,
470  aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
471  0 );
472  }
473  else
474  {
476  aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
477  true );
478  }
479 
480  // VRML_LAYER m_bot_copper;
481  aModel.m_bot_copper.Tesselate( &aModel.m_holes );
482 
483  if( USE_INLINES )
484  {
485  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TRACK ),
486  &aModel.m_bot_copper, true, false,
487  aModel.GetLayerZ( B_Cu ), 0 );
488  }
489  else
490  {
492  aModel.GetLayerZ( B_Cu ), false );
493  }
494 
495  // VRML_LAYER m_bot_tin;
496  aModel.m_bot_tin.Tesselate( &aModel.m_holes );
497 
498  if( USE_INLINES )
499  {
500  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TIN ),
501  &aModel.m_bot_tin, true, false,
502  aModel.GetLayerZ( B_Cu )
503  - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
504  0 );
505  }
506  else
507  {
509  aModel.GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
510  false );
511  }
512 
513  // VRML_LAYER PTH;
514  aModel.m_plated_holes.Tesselate( NULL, true );
515 
516  if( USE_INLINES )
517  {
518  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TIN ),
519  &aModel.m_plated_holes, false, false,
520  aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
521  aModel.GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE );
522  }
523  else
524  {
526  aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
527  aModel.GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE );
528  }
529 
530  // VRML_LAYER m_top_silk;
531  aModel.m_top_silk.Tesselate( &aModel.m_holes );
532 
533  if( USE_INLINES )
534  {
535  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_SILK ), &aModel.m_top_silk,
536  true, true, aModel.GetLayerZ( F_SilkS ), 0 );
537  }
538  else
539  {
541  aModel.GetLayerZ( F_SilkS ), true );
542  }
543 
544  // VRML_LAYER m_bot_silk;
545  aModel.m_bot_silk.Tesselate( &aModel.m_holes );
546 
547  if( USE_INLINES )
548  {
549  write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_SILK ), &aModel.m_bot_silk,
550  true, false, aModel.GetLayerZ( B_SilkS ), 0 );
551  }
552  else
553  {
555  aModel.GetLayerZ( B_SilkS ), false );
556  }
557 
558  if( !USE_INLINES )
559  S3D::WriteVRML( aFileName, true, aModel.m_OutputPCB.GetRawPtr(), true, true );
560 }
561 
562 
563 static void compute_layer_Zs( MODEL_VRML& aModel, BOARD* pcb )
564 {
565  int copper_layers = pcb->GetCopperLayerCount();
566 
567  // We call it 'layer' thickness, but it's the whole board thickness!
569  double half_thickness = aModel.m_brd_thickness / 2;
570 
571  // Compute each layer's Z value, more or less like the 3d view
572  for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq )
573  {
574  PCB_LAYER_ID i = *seq;
575 
576  if( i < copper_layers )
577  aModel.SetLayerZ( i, half_thickness - aModel.m_brd_thickness * i / (copper_layers - 1) );
578  else
579  aModel.SetLayerZ( i, - half_thickness ); // bottom layer
580  }
581 
582  /* To avoid rounding interference, we apply an epsilon to each
583  * successive layer */
584  double epsilon_z = Millimeter2iu( ART_OFFSET ) * BOARD_SCALE;
585  aModel.SetLayerZ( B_Paste, -half_thickness - epsilon_z * 4 );
586  aModel.SetLayerZ( B_Adhes, -half_thickness - epsilon_z * 3 );
587  aModel.SetLayerZ( B_SilkS, -half_thickness - epsilon_z * 2 );
588  aModel.SetLayerZ( B_Mask, -half_thickness - epsilon_z );
589  aModel.SetLayerZ( F_Mask, half_thickness + epsilon_z );
590  aModel.SetLayerZ( F_SilkS, half_thickness + epsilon_z * 2 );
591  aModel.SetLayerZ( F_Adhes, half_thickness + epsilon_z * 3 );
592  aModel.SetLayerZ( F_Paste, half_thickness + epsilon_z * 4 );
593  aModel.SetLayerZ( Dwgs_User, half_thickness + epsilon_z * 5 );
594  aModel.SetLayerZ( Cmts_User, half_thickness + epsilon_z * 6 );
595  aModel.SetLayerZ( Eco1_User, half_thickness + epsilon_z * 7 );
596  aModel.SetLayerZ( Eco2_User, half_thickness + epsilon_z * 8 );
597  aModel.SetLayerZ( Edge_Cuts, 0 );
598 }
599 
600 
601 static void export_vrml_line( MODEL_VRML& aModel, LAYER_NUM layer,
602  double startx, double starty,
603  double endx, double endy, double width )
604 {
605  VRML_LAYER* vlayer;
606 
607  if( !GetLayer( aModel, layer, &vlayer ) )
608  return;
609 
610  if( width < aModel.m_minLineWidth)
611  width = aModel.m_minLineWidth;
612 
613  starty = -starty;
614  endy = -endy;
615 
616  double angle = atan2( endy - starty, endx - startx ) * 180.0 / M_PI;
617  double length = Distance( startx, starty, endx, endy ) + width;
618  double cx = ( startx + endx ) / 2.0;
619  double cy = ( starty + endy ) / 2.0;
620 
621  if( !vlayer->AddSlot( cx, cy, length, width, angle, false ) )
622  throw( std::runtime_error( vlayer->GetError() ) );
623 }
624 
625 
626 static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
627  double startx, double starty,
628  double endx, double endy, double width )
629 {
630  VRML_LAYER* vlayer;
631 
632  if( !GetLayer( aModel, layer, &vlayer ) )
633  return;
634 
635  if( width < aModel.m_minLineWidth )
636  width = aModel.m_minLineWidth;
637 
638  starty = -starty;
639  endy = -endy;
640 
641  double hole, radius;
642 
643  radius = Distance( startx, starty, endx, endy ) + ( width / 2);
644  hole = radius - width;
645 
646  if( !vlayer->AddCircle( startx, starty, radius, false ) )
647  throw( std::runtime_error( vlayer->GetError() ) );
648 
649  if( hole > 0.0001 )
650  {
651  if( !vlayer->AddCircle( startx, starty, hole, true ) )
652  throw( std::runtime_error( vlayer->GetError() ) );
653  }
654 }
655 
656 
657 static void export_vrml_arc( MODEL_VRML& aModel, LAYER_NUM layer,
658  double centerx, double centery,
659  double arc_startx, double arc_starty,
660  double width, double arc_angle )
661 {
662  VRML_LAYER* vlayer;
663 
664  if( !GetLayer( aModel, layer, &vlayer ) )
665  return;
666 
667  if( width < aModel.m_minLineWidth )
668  width = aModel.m_minLineWidth;
669 
670  centery = -centery;
671  arc_starty = -arc_starty;
672 
673  if( !vlayer->AddArc( centerx, centery, arc_startx, arc_starty, width, -arc_angle, false ) )
674  throw( std::runtime_error( vlayer->GetError() ) );
675 
676 }
677 
678 
679 static void export_vrml_polygon( MODEL_VRML& aModel, LAYER_NUM layer,
680  DRAWSEGMENT *aOutline, double aOrientation, wxPoint aPos )
681 {
682  const int circleSegmentsCount = 32;
683 
684  if( aOutline->IsPolyShapeValid() )
685  {
686  SHAPE_POLY_SET shape = aOutline->GetPolyShape();
687  VRML_LAYER* vlayer;
688 
689  if( !GetLayer( aModel, layer, &vlayer ) )
690  return;
691 
692  if( aOutline->GetWidth() )
693  {
694  shape.Inflate( aOutline->GetWidth()/2, circleSegmentsCount );
696  }
697 
698  shape.Rotate( -aOrientation, VECTOR2I( 0, 0 ) );
699  shape.Move( aPos );
700  const SHAPE_LINE_CHAIN& outline = shape.COutline( 0 );
701 
702  int seg = vlayer->NewContour();
703 
704  for( int j = 0; j < outline.PointCount(); j++ )
705  {
706  if( !vlayer->AddVertex( seg, outline.CPoint( j ).x * BOARD_SCALE,
707  -outline.CPoint( j ).y * BOARD_SCALE ) )
708  throw( std::runtime_error( vlayer->GetError() ) );
709  }
710 
711  vlayer->EnsureWinding( seg, false );
712  }
713 }
714 
715 
716 static void export_vrml_drawsegment( MODEL_VRML& aModel, DRAWSEGMENT* drawseg )
717 {
718  LAYER_NUM layer = drawseg->GetLayer();
719  double w = drawseg->GetWidth() * BOARD_SCALE;
720  double x = drawseg->GetStart().x * BOARD_SCALE;
721  double y = drawseg->GetStart().y * BOARD_SCALE;
722  double xf = drawseg->GetEnd().x * BOARD_SCALE;
723  double yf = drawseg->GetEnd().y * BOARD_SCALE;
724  double r = sqrt( pow( x - xf, 2 ) + pow( y - yf, 2 ) );
725 
726  // Items on the edge layer are handled elsewhere; just return
727  if( layer == Edge_Cuts )
728  return;
729 
730  switch( drawseg->GetShape() )
731  {
732  case S_ARC:
733  export_vrml_arc( aModel, layer,
734  (double) drawseg->GetCenter().x * BOARD_SCALE,
735  (double) drawseg->GetCenter().y * BOARD_SCALE,
736  (double) drawseg->GetArcStart().x * BOARD_SCALE,
737  (double) drawseg->GetArcStart().y * BOARD_SCALE,
738  w, drawseg->GetAngle() / 10 );
739  break;
740 
741  case S_CIRCLE:
742  // Break circles into two 180 arcs to prevent the vrml hole from obscuring objects
743  // within the hole area of the circle.
744  export_vrml_arc( aModel, layer, x, y, x, y-r, w, 180.0 );
745  export_vrml_arc( aModel, layer, x, y, x, y+r, w, 180.0 );
746  break;
747 
748  case S_POLYGON:
749  export_vrml_polygon( aModel, layer, drawseg, 0.0, wxPoint( 0, 0 ) );
750  break;
751 
752  default:
753  export_vrml_line( aModel, layer, x, y, xf, yf, w );
754  break;
755  }
756 }
757 
758 
759 /* C++ doesn't have closures and neither continuation forms... this is
760  * for coupling the vrml_text_callback with the common parameters */
761 static void vrml_text_callback( int x0, int y0, int xf, int yf, void* aData )
762 {
763  LAYER_NUM m_text_layer = model_vrml->m_text_layer;
764  int m_text_width = model_vrml->m_text_width;
765 
766  export_vrml_line( *model_vrml, m_text_layer,
767  x0 * BOARD_SCALE, y0 * BOARD_SCALE,
768  xf * BOARD_SCALE, yf * BOARD_SCALE,
769  m_text_width * BOARD_SCALE );
770 }
771 
772 
773 static void export_vrml_pcbtext( MODEL_VRML& aModel, TEXTE_PCB* text )
774 {
775  model_vrml->m_text_layer = text->GetLayer();
776  model_vrml->m_text_width = text->GetThickness();
777 
778  wxSize size = text->GetTextSize();
779 
780  if( text->IsMirrored() )
781  size.x = -size.x;
782 
783  COLOR4D color = COLOR4D::BLACK; // not actually used, but needed by DrawGraphicText
784 
785  if( text->IsMultilineAllowed() )
786  {
787  wxArrayString strings_list;
788  wxStringSplit( text->GetShownText(), strings_list, '\n' );
789  std::vector<wxPoint> positions;
790  positions.reserve( strings_list.Count() );
791  text->GetPositionsOfLinesOfMultilineText( positions, strings_list.Count() );
792 
793  for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
794  {
795  wxString& txt = strings_list.Item( ii );
796  DrawGraphicText( NULL, NULL, positions[ii], color,
797  txt, text->GetTextAngle(), size,
798  text->GetHorizJustify(), text->GetVertJustify(),
799  text->GetThickness(), text->IsItalic(),
800  true,
802  }
803  }
804  else
805  {
806  DrawGraphicText( NULL, NULL, text->GetTextPos(), color,
807  text->GetShownText(), text->GetTextAngle(), size,
808  text->GetHorizJustify(), text->GetVertJustify(),
809  text->GetThickness(), text->IsItalic(),
810  true,
812  }
813 }
814 
815 
816 static void export_vrml_drawings( MODEL_VRML& aModel, BOARD* pcb )
817 {
818  // draw graphic items
819  for( auto drawing : pcb->Drawings() )
820  {
821  PCB_LAYER_ID layer = drawing->GetLayer();
822 
823  if( layer != F_Cu && layer != B_Cu && layer != B_SilkS && layer != F_SilkS )
824  continue;
825 
826  switch( drawing->Type() )
827  {
828  case PCB_LINE_T:
829  export_vrml_drawsegment( aModel, (DRAWSEGMENT*) drawing );
830  break;
831 
832  case PCB_TEXT_T:
833  export_vrml_pcbtext( aModel, (TEXTE_PCB*) drawing );
834  break;
835 
836  default:
837  break;
838  }
839  }
840 }
841 
842 
843 // board edges and cutouts
844 static void export_vrml_board( MODEL_VRML& aModel, BOARD* aPcb )
845 {
846  SHAPE_POLY_SET pcbOutlines; // stores the board main outlines
847  wxString msg;
848 
849  if( !aPcb->GetBoardPolygonOutlines( pcbOutlines, &msg ) )
850  {
851  msg << "\n\n" <<
852  _( "Unable to calculate the board outlines; fall back to using the board boundary box." );
853  wxMessageBox( msg );
854  }
855 
856  int seg;
857 
858  for( int cnt = 0; cnt < pcbOutlines.OutlineCount(); cnt++ )
859  {
860  const SHAPE_LINE_CHAIN& outline = pcbOutlines.COutline( cnt );
861 
862  seg = aModel.m_board.NewContour();
863 
864  for( int j = 0; j < outline.PointCount(); j++ )
865  {
866  aModel.m_board.AddVertex( seg, (double)outline.CPoint(j).x * BOARD_SCALE,
867  -((double)outline.CPoint(j).y * BOARD_SCALE ) );
868 
869  }
870 
871  aModel.m_board.EnsureWinding( seg, false );
872 
873  // Generate holes:
874  for( int ii = 0; ii < pcbOutlines.HoleCount( cnt ); ii++ )
875  {
876  const SHAPE_LINE_CHAIN& hole = pcbOutlines.Hole( cnt, ii );
877 
878  seg = aModel.m_holes.NewContour();
879 
880  if( seg < 0 )
881  {
882  msg << "\n\n" <<
883  _( "VRML Export Failed: Could not add holes to contours." );
884  wxMessageBox( msg );
885 
886  return;
887  }
888 
889  for( int j = 0; j < hole.PointCount(); j++ )
890  {
891  aModel.m_holes.AddVertex( seg, (double)hole.CPoint(j).x * BOARD_SCALE,
892  -((double)hole.CPoint(j).y * BOARD_SCALE ) );
893 
894  }
895 
896  aModel.m_holes.EnsureWinding( seg, true );
897  }
898  }
899 }
900 
901 
902 static void export_round_padstack( MODEL_VRML& aModel, BOARD* pcb,
903  double x, double y, double r,
904  LAYER_NUM bottom_layer, LAYER_NUM top_layer,
905  double hole )
906 {
907  LAYER_NUM layer = top_layer;
908  bool thru = true;
909 
910  // if not a thru hole do not put a hole in the board
911  if( top_layer != F_Cu || bottom_layer != B_Cu )
912  thru = false;
913 
914  if( thru && hole > 0 )
915  aModel.m_holes.AddCircle( x, -y, hole, true );
916 
917  if( aModel.m_plainPCB )
918  return;
919 
920  while( 1 )
921  {
922  if( layer == B_Cu )
923  {
924  aModel.m_bot_copper.AddCircle( x, -y, r );
925 
926  if( hole > 0 && !thru )
927  aModel.m_bot_copper.AddCircle( x, -y, hole, true );
928 
929  }
930  else if( layer == F_Cu )
931  {
932  aModel.m_top_copper.AddCircle( x, -y, r );
933 
934  if( hole > 0 && !thru )
935  aModel.m_top_copper.AddCircle( x, -y, hole, true );
936 
937  }
938 
939  if( layer == bottom_layer )
940  break;
941 
942  layer = bottom_layer;
943  }
944 }
945 
946 
947 static void export_vrml_via( MODEL_VRML& aModel, BOARD* aPcb, const VIA* aVia )
948 {
949  double x, y, r, hole;
950  PCB_LAYER_ID top_layer, bottom_layer;
951 
952  hole = aVia->GetDrillValue() * BOARD_SCALE / 2.0;
953  r = aVia->GetWidth() * BOARD_SCALE / 2.0;
954  x = aVia->GetStart().x * BOARD_SCALE;
955  y = aVia->GetStart().y * BOARD_SCALE;
956  aVia->LayerPair( &top_layer, &bottom_layer );
957 
958  // do not render a buried via
959  if( top_layer != F_Cu && bottom_layer != B_Cu )
960  return;
961 
962  // Export the via padstack
963  export_round_padstack( aModel, aPcb, x, y, r, bottom_layer, top_layer, hole );
964 }
965 
966 
967 static void export_vrml_tracks( MODEL_VRML& aModel, BOARD* pcb )
968 {
969  for( TRACK* track = pcb->m_Track; track; track = track->Next() )
970  {
971  if( track->Type() == PCB_VIA_T )
972  {
973  export_vrml_via( aModel, pcb, (const VIA*) track );
974  }
975  else if( ( track->GetLayer() == B_Cu || track->GetLayer() == F_Cu )
976  && !aModel.m_plainPCB )
977  export_vrml_line( aModel, track->GetLayer(),
978  track->GetStart().x * BOARD_SCALE,
979  track->GetStart().y * BOARD_SCALE,
980  track->GetEnd().x * BOARD_SCALE,
981  track->GetEnd().y * BOARD_SCALE,
982  track->GetWidth() * BOARD_SCALE );
983  }
984 }
985 
986 
987 static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
988 {
989 
990  for( int ii = 0; ii < aPcb->GetAreaCount(); ii++ )
991  {
992  ZONE_CONTAINER* zone = aPcb->GetArea( ii );
993 
994  VRML_LAYER* vl;
995 
996  if( !GetLayer( aModel, zone->GetLayer(), &vl ) )
997  continue;
998 
999  // fixme: this modifies the board where it shouldn't, but I don't have the time
1000  // to clean this up - TW
1001  if( !zone->IsFilled() )
1002  {
1003  ZONE_FILLER filler( aPcb );
1004  zone->SetFillMode( ZFM_POLYGONS ); // use filled polygons
1005  filler.Fill( { zone } );
1006  }
1007 
1008  const SHAPE_POLY_SET& poly = zone->GetFilledPolysList();
1009 
1010  for( int i = 0; i < poly.OutlineCount(); i++ )
1011  {
1012  const SHAPE_LINE_CHAIN& outline = poly.COutline( i );
1013 
1014  int seg = vl->NewContour();
1015 
1016  for( int j = 0; j < outline.PointCount(); j++ )
1017  {
1018  if( !vl->AddVertex( seg, (double)outline.CPoint( j ).x * BOARD_SCALE,
1019  -((double)outline.CPoint( j ).y * BOARD_SCALE ) ) )
1020  throw( std::runtime_error( vl->GetError() ) );
1021 
1022  }
1023 
1024  vl->EnsureWinding( seg, false );
1025  }
1026  }
1027 }
1028 
1029 
1031 {
1032  if( module->IsVisible() )
1033  {
1034  wxSize size = module->GetTextSize();
1035 
1036  if( module->IsMirrored() )
1037  size.x = -size.x; // Text is mirrored
1038 
1039  model_vrml->m_text_layer = module->GetLayer();
1040  model_vrml->m_text_width = module->GetThickness();
1041 
1042  DrawGraphicText( NULL, NULL, module->GetTextPos(), BLACK,
1043  module->GetShownText(), module->GetDrawRotation(), size,
1044  module->GetHorizJustify(), module->GetVertJustify(),
1045  module->GetThickness(), module->IsItalic(),
1046  true,
1048  }
1049 }
1050 
1051 
1052 static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline,
1053  MODULE* aModule )
1054 {
1055  LAYER_NUM layer = aOutline->GetLayer();
1056  double x = aOutline->GetStart().x * BOARD_SCALE;
1057  double y = aOutline->GetStart().y * BOARD_SCALE;
1058  double xf = aOutline->GetEnd().x * BOARD_SCALE;
1059  double yf = aOutline->GetEnd().y * BOARD_SCALE;
1060  double w = aOutline->GetWidth() * BOARD_SCALE;
1061 
1062  switch( aOutline->GetShape() )
1063  {
1064  case S_SEGMENT:
1065  export_vrml_line( aModel, layer, x, y, xf, yf, w );
1066  break;
1067 
1068  case S_ARC:
1069  export_vrml_arc( aModel, layer, x, y, xf, yf, w, aOutline->GetAngle() / 10 );
1070  break;
1071 
1072  case S_CIRCLE:
1073  export_vrml_circle( aModel, layer, x, y, xf, yf, w );
1074  break;
1075 
1076  case S_POLYGON:
1077  export_vrml_polygon( aModel, layer, aOutline, aModule->GetOrientationRadians(),
1078  aModule->GetPosition() );
1079  break;
1080 
1081  default:
1082  break;
1083  }
1084 }
1085 
1086 
1087 static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aTinLayer, D_PAD* aPad )
1088 {
1089  // The (maybe offset) pad position
1090  wxPoint pad_pos = aPad->ShapePos();
1091  double pad_x = pad_pos.x * BOARD_SCALE;
1092  double pad_y = pad_pos.y * BOARD_SCALE;
1093  wxSize pad_delta = aPad->GetDelta();
1094 
1095  double pad_dx = pad_delta.x * BOARD_SCALE / 2.0;
1096  double pad_dy = pad_delta.y * BOARD_SCALE / 2.0;
1097 
1098  double pad_w = aPad->GetSize().x * BOARD_SCALE / 2.0;
1099  double pad_h = aPad->GetSize().y * BOARD_SCALE / 2.0;
1100 
1101  switch( aPad->GetShape() )
1102  {
1103  case PAD_SHAPE_CIRCLE:
1104 
1105  if( !aTinLayer->AddCircle( pad_x, -pad_y, pad_w, false ) )
1106  throw( std::runtime_error( aTinLayer->GetError() ) );
1107 
1108  break;
1109 
1110  case PAD_SHAPE_OVAL:
1111 
1112  if( !aTinLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0,
1113  aPad->GetOrientation()/10.0, false ) )
1114  throw( std::runtime_error( aTinLayer->GetError() ) );
1115 
1116  break;
1117 
1118  case PAD_SHAPE_ROUNDRECT:
1119  {
1120  SHAPE_POLY_SET polySet;
1121  int segmentToCircleCount = 32;
1122  const int corner_radius = aPad->GetRoundRectCornerRadius( aPad->GetSize() );
1123  TransformRoundRectToPolygon( polySet, wxPoint( 0, 0 ), aPad->GetSize(),
1124  0.0, corner_radius, segmentToCircleCount );
1125  std::vector< wxRealPoint > cornerList;
1126  // TransformRoundRectToPolygon creates only one convex polygon
1127  SHAPE_LINE_CHAIN poly( polySet.Outline( 0 ) );
1128 
1129  for( int ii = 0; ii < poly.PointCount(); ++ii )
1130  cornerList.push_back( wxRealPoint( poly.Point( ii ).x * BOARD_SCALE,
1131  -poly.Point( ii ).y * BOARD_SCALE ) );
1132 
1133  // Close polygon
1134  cornerList.push_back( cornerList[0] );
1135  if( !aTinLayer->AddPolygon( cornerList, pad_x, -pad_y, aPad->GetOrientation() ) )
1136  throw( std::runtime_error( aTinLayer->GetError() ) );
1137 
1138  break;
1139  }
1140 
1141  case PAD_SHAPE_CUSTOM:
1142  {
1143  SHAPE_POLY_SET polySet;
1144  int segmentToCircleCount = 32;
1145  std::vector< wxRealPoint > cornerList;
1146  aPad->MergePrimitivesAsPolygon( &polySet, segmentToCircleCount );
1147 
1148  for( int cnt = 0; cnt < polySet.OutlineCount(); ++cnt )
1149  {
1150  SHAPE_LINE_CHAIN& poly = polySet.Outline( cnt );
1151  cornerList.clear();
1152 
1153  for( int ii = 0; ii < poly.PointCount(); ++ii )
1154  cornerList.push_back( wxRealPoint( poly.Point( ii ).x * BOARD_SCALE,
1155  -poly.Point( ii ).y * BOARD_SCALE ) );
1156 
1157  // Close polygon
1158  cornerList.push_back( cornerList[0] );
1159 
1160  if( !aTinLayer->AddPolygon( cornerList, pad_x, -pad_y, aPad->GetOrientation() ) )
1161  throw( std::runtime_error( aTinLayer->GetError() ) );
1162  }
1163 
1164  break;
1165  }
1166 
1167  case PAD_SHAPE_RECT:
1168  // Just to be sure :D
1169  pad_dx = 0;
1170  pad_dy = 0;
1171 
1172  case PAD_SHAPE_TRAPEZOID:
1173  {
1174  double coord[8] =
1175  {
1176  -pad_w + pad_dy, -pad_h - pad_dx,
1177  -pad_w - pad_dy, pad_h + pad_dx,
1178  +pad_w - pad_dy, -pad_h + pad_dx,
1179  +pad_w + pad_dy, pad_h - pad_dx
1180  };
1181 
1182  for( int i = 0; i < 4; i++ )
1183  {
1184  RotatePoint( &coord[i * 2], &coord[i * 2 + 1], aPad->GetOrientation() );
1185  coord[i * 2] += pad_x;
1186  coord[i * 2 + 1] += pad_y;
1187  }
1188 
1189  int lines;
1190 
1191  lines = aTinLayer->NewContour();
1192 
1193  if( lines < 0 )
1194  throw( std::runtime_error( aTinLayer->GetError() ) );
1195 
1196  if( !aTinLayer->AddVertex( lines, coord[0], -coord[1] ) )
1197  throw( std::runtime_error( aTinLayer->GetError() ) );
1198 
1199  if( !aTinLayer->AddVertex( lines, coord[4], -coord[5] ) )
1200  throw( std::runtime_error( aTinLayer->GetError() ) );
1201 
1202  if( !aTinLayer->AddVertex( lines, coord[6], -coord[7] ) )
1203  throw( std::runtime_error( aTinLayer->GetError() ) );
1204 
1205  if( !aTinLayer->AddVertex( lines, coord[2], -coord[3] ) )
1206  throw( std::runtime_error( aTinLayer->GetError() ) );
1207 
1208  if( !aTinLayer->EnsureWinding( lines, false ) )
1209  throw( std::runtime_error( aTinLayer->GetError() ) );
1210 
1211  break;
1212  }
1213 
1214  default:
1215  break;
1216  }
1217 }
1218 
1219 
1220 static void export_vrml_pad( MODEL_VRML& aModel, BOARD* aPcb, D_PAD* aPad )
1221 {
1222  double hole_drill_w = (double) aPad->GetDrillSize().x * BOARD_SCALE / 2.0;
1223  double hole_drill_h = (double) aPad->GetDrillSize().y * BOARD_SCALE / 2.0;
1224  double hole_drill = std::min( hole_drill_w, hole_drill_h );
1225  double hole_x = aPad->GetPosition().x * BOARD_SCALE;
1226  double hole_y = aPad->GetPosition().y * BOARD_SCALE;
1227 
1228  // Export the hole on the edge layer
1229  if( hole_drill > 0 )
1230  {
1231  bool pth = false;
1232 
1233  if( ( aPad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
1234  && !aModel.m_plainPCB )
1235  pth = true;
1236 
1237  if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
1238  {
1239  // Oblong hole (slot)
1240 
1241  if( pth )
1242  {
1243  aModel.m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0 + PLATE_OFFSET,
1244  hole_drill_h * 2.0 + PLATE_OFFSET,
1245  aPad->GetOrientation()/10.0, true, true );
1246 
1247  aModel.m_plated_holes.AddSlot( hole_x, -hole_y,
1248  hole_drill_w * 2.0, hole_drill_h * 2.0,
1249  aPad->GetOrientation()/10.0, true, false );
1250  }
1251  else
1252  {
1253  aModel.m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
1254  aPad->GetOrientation()/10.0, true, false );
1255 
1256  }
1257  }
1258  else
1259  {
1260  // Drill a round hole
1261 
1262  if( pth )
1263  {
1264  aModel.m_holes.AddCircle( hole_x, -hole_y, hole_drill + PLATE_OFFSET, true, true );
1265  aModel.m_plated_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
1266  }
1267  else
1268  {
1269  aModel.m_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
1270  }
1271 
1272  }
1273  }
1274 
1275  if( aModel.m_plainPCB )
1276  return;
1277 
1278  // The pad proper, on the selected layers
1279  LSET layer_mask = aPad->GetLayerSet();
1280 
1281  if( layer_mask[B_Cu] )
1282  {
1283  if( layer_mask[B_Mask] )
1284  export_vrml_padshape( aModel, &aModel.m_bot_tin, aPad );
1285  else
1286  export_vrml_padshape( aModel, &aModel.m_bot_copper, aPad );
1287  }
1288  if( layer_mask[F_Cu] )
1289  {
1290  if( layer_mask[F_Mask] )
1291  export_vrml_padshape( aModel, &aModel.m_top_tin, aPad );
1292  else
1293  export_vrml_padshape( aModel, &aModel.m_top_copper, aPad );
1294  }
1295 
1296 }
1297 
1298 
1299 // From axis/rot to quaternion
1300 static void build_quat( double x, double y, double z, double a, double q[4] )
1301 {
1302  double sina = sin( a / 2 );
1303 
1304  q[0] = x * sina;
1305  q[1] = y * sina;
1306  q[2] = z * sina;
1307  q[3] = cos( a / 2 );
1308 }
1309 
1310 
1311 // From quaternion to axis/rot
1312 static void from_quat( double q[4], double rot[4] )
1313 {
1314  rot[3] = acos( q[3] ) * 2;
1315 
1316  for( int i = 0; i < 3; i++ )
1317  {
1318  rot[i] = q[i] / sin( rot[3] / 2 );
1319  }
1320 }
1321 
1322 
1323 // Quaternion composition
1324 static void compose_quat( double q1[4], double q2[4], double qr[4] )
1325 {
1326  double tmp[4];
1327 
1328  tmp[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1];
1329  tmp[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2];
1330  tmp[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0];
1331  tmp[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2];
1332 
1333  qr[0] = tmp[0];
1334  qr[1] = tmp[1];
1335  qr[2] = tmp[2];
1336  qr[3] = tmp[3];
1337 }
1338 
1339 
1340 static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb,
1341  MODULE* aModule, std::ostream* aOutputFile )
1342 {
1343  if( !aModel.m_plainPCB )
1344  {
1345  // Reference and value
1346  if( aModule->Reference().IsVisible() )
1347  export_vrml_text_module( &aModule->Reference() );
1348 
1349  if( aModule->Value().IsVisible() )
1350  export_vrml_text_module( &aModule->Value() );
1351 
1352  // Export module edges
1353  for( EDA_ITEM* item = aModule->GraphicalItemsList(); item; item = item->Next() )
1354  {
1355  switch( item->Type() )
1356  {
1357  case PCB_MODULE_TEXT_T:
1358  export_vrml_text_module( static_cast<TEXTE_MODULE*>( item ) );
1359  break;
1360 
1361  case PCB_MODULE_EDGE_T:
1362  export_vrml_edge_module( aModel, static_cast<EDGE_MODULE*>( item ),
1363  aModule );
1364  break;
1365 
1366  default:
1367  break;
1368  }
1369  }
1370  }
1371 
1372  // Export pads
1373  for( D_PAD* pad = aModule->PadsList(); pad; pad = pad->Next() )
1374  export_vrml_pad( aModel, aPcb, pad );
1375 
1376  bool isFlipped = aModule->GetLayer() == B_Cu;
1377 
1378  // Export the object VRML model(s)
1379  auto sM = aModule->Models().begin();
1380  auto eM = aModule->Models().end();
1381 
1382  wxFileName subdir( SUBDIR_3D, "" );
1383 
1384  while( sM != eM )
1385  {
1386  SGNODE* mod3d = (SGNODE*) cache->Load( sM->m_Filename );
1387 
1388  if( NULL == mod3d )
1389  {
1390  ++sM;
1391  continue;
1392  }
1393 
1394  /* Calculate 3D shape rotation:
1395  * this is the rotation parameters, with an additional 180 deg rotation
1396  * for footprints that are flipped
1397  * When flipped, axis rotation is the horizontal axis (X axis)
1398  */
1399  double rotx = -sM->m_Rotation.x;
1400  double roty = -sM->m_Rotation.y;
1401  double rotz = -sM->m_Rotation.z;
1402 
1403  if( isFlipped )
1404  {
1405  rotx += 180.0;
1406  roty = -roty;
1407  rotz = -rotz;
1408  }
1409 
1410  // Do some quaternion munching
1411  double q1[4], q2[4], rot[4];
1412  build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 );
1413  build_quat( 0, 1, 0, DEG2RAD( roty ), q2 );
1414  compose_quat( q1, q2, q1 );
1415  build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 );
1416  compose_quat( q1, q2, q1 );
1417 
1418  // Note here aModule->GetOrientation() is in 0.1 degrees,
1419  // so module rotation has to be converted to radians
1420  build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 );
1421  compose_quat( q1, q2, q1 );
1422  from_quat( q1, rot );
1423 
1424  double offsetFactor = 1000.0f * IU_PER_MILS / 25.4f;
1425 
1426  // adjust 3D shape local offset position
1427  // they are given in mm, so they are converted in board IU.
1428  double offsetx = sM->m_Offset.x * offsetFactor;
1429  double offsety = sM->m_Offset.y * offsetFactor;
1430  double offsetz = sM->m_Offset.z * offsetFactor;
1431 
1432  if( isFlipped )
1433  offsetz = -offsetz;
1434  else // In normal mode, Y axis is reversed in Pcbnew.
1435  offsety = -offsety;
1436 
1437  RotatePoint( &offsetx, &offsety, aModule->GetOrientation() );
1438 
1439  SGPOINT trans;
1440  trans.x = ( offsetx + aModule->GetPosition().x ) * BOARD_SCALE + aModel.m_tx;
1441  trans.y = -(offsety + aModule->GetPosition().y) * BOARD_SCALE - aModel.m_ty;
1442  trans.z = (offsetz * BOARD_SCALE ) + aModel.GetLayerZ( aModule->GetLayer() );
1443 
1444  if( USE_INLINES )
1445  {
1446  wxFileName srcFile = cache->GetResolver()->ResolvePath( sM->m_Filename );
1447  wxFileName dstFile;
1448  dstFile.SetPath( SUBDIR_3D );
1449  dstFile.SetName( srcFile.GetName() );
1450  dstFile.SetExt( "wrl" );
1451 
1452  // copy the file if necessary
1453  wxDateTime srcModTime = srcFile.GetModificationTime();
1454  wxDateTime destModTime = srcModTime;
1455 
1456  destModTime.SetToCurrent();
1457 
1458  if( dstFile.FileExists() )
1459  destModTime = dstFile.GetModificationTime();
1460 
1461  if( srcModTime != destModTime )
1462  {
1463  wxLogDebug( "Copying 3D model %s to %s.",
1464  GetChars( srcFile.GetFullPath() ),
1465  GetChars( dstFile.GetFullPath() ) );
1466 
1467  wxString fileExt = srcFile.GetExt();
1468  fileExt.LowerCase();
1469 
1470  // copy VRML models and use the scenegraph library to
1471  // translate other model types
1472  if( fileExt == "wrl" )
1473  {
1474  if( !wxCopyFile( srcFile.GetFullPath(), dstFile.GetFullPath() ) )
1475  continue;
1476  }
1477  else
1478  {
1479  if( !S3D::WriteVRML( dstFile.GetFullPath().ToUTF8(), true, mod3d, USE_DEFS, true ) )
1480  continue;
1481  }
1482  }
1483 
1484  (*aOutputFile) << "Transform {\n";
1485 
1486  // only write a rotation if it is >= 0.1 deg
1487  if( std::abs( rot[3] ) > 0.0001745 )
1488  {
1489  (*aOutputFile) << " rotation " << std::setprecision( 5 );
1490  (*aOutputFile) << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
1491  }
1492 
1493  (*aOutputFile) << " translation " << std::setprecision( PRECISION );
1494  (*aOutputFile) << trans.x << " ";
1495  (*aOutputFile) << trans.y << " ";
1496  (*aOutputFile) << trans.z << "\n";
1497 
1498  (*aOutputFile) << " scale ";
1499  (*aOutputFile) << sM->m_Scale.x << " ";
1500  (*aOutputFile) << sM->m_Scale.y << " ";
1501  (*aOutputFile) << sM->m_Scale.z << "\n";
1502 
1503  (*aOutputFile) << " children [\n Inline {\n url \"";
1504 
1505  if( USE_RELPATH )
1506  {
1507  wxFileName tmp = dstFile;
1508  tmp.SetExt( "" );
1509  tmp.SetName( "" );
1510  tmp.RemoveLastDir();
1511  dstFile.MakeRelativeTo( tmp.GetPath() );
1512  }
1513 
1514  wxString fn = dstFile.GetFullPath();
1515  fn.Replace( "\\", "/" );
1516  (*aOutputFile) << TO_UTF8( fn ) << "\"\n } ]\n";
1517  (*aOutputFile) << " }\n";
1518  }
1519  else
1520  {
1521  IFSG_TRANSFORM* modelShape = new IFSG_TRANSFORM( aModel.m_OutputPCB.GetRawPtr() );
1522 
1523  // only write a rotation if it is >= 0.1 deg
1524  if( std::abs( rot[3] ) > 0.0001745 )
1525  modelShape->SetRotation( SGVECTOR( rot[0], rot[1], rot[2] ), rot[3] );
1526 
1527  modelShape->SetTranslation( trans );
1528  modelShape->SetScale( SGPOINT( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ) );
1529 
1530  if( NULL == S3D::GetSGNodeParent( mod3d ) )
1531  {
1532  aModel.m_components.push_back( mod3d );
1533  modelShape->AddChildNode( mod3d );
1534  }
1535  else
1536  {
1537  modelShape->AddRefNode( mod3d );
1538  }
1539 
1540  }
1541 
1542  ++sM;
1543  }
1544 }
1545 
1546 
1547 bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMtoWRMLunit,
1548  bool aExport3DFiles, bool aUseRelativePaths,
1549  bool aUsePlainPCB, const wxString& a3D_Subdir,
1550  double aXRef, double aYRef )
1551 {
1552  BOARD* pcb = GetBoard();
1553  bool ok = true;
1554 
1555  USE_INLINES = aExport3DFiles;
1556  USE_DEFS = true;
1557  USE_RELPATH = aUseRelativePaths;
1558 
1559  cache = Prj().Get3DCacheManager();
1560  PROJ_DIR = Prj().GetProjectPath();
1561  SUBDIR_3D = a3D_Subdir;
1562  MODEL_VRML model3d;
1563  model_vrml = &model3d;
1564  model3d.SetScale( aMMtoWRMLunit );
1565 
1566  if( USE_INLINES )
1567  {
1568  BOARD_SCALE = MM_PER_IU / 2.54;
1569  model3d.SetOffset( -aXRef / 2.54, aYRef / 2.54 );
1570  }
1571  else
1572  {
1573  BOARD_SCALE = MM_PER_IU;
1574  model3d.SetOffset( -aXRef, aYRef );
1575  }
1576 
1577  // plain PCB or else PCB with copper and silkscreen
1578  model3d.m_plainPCB = aUsePlainPCB;
1579 
1580  try
1581  {
1582 
1583  // Preliminary computation: the z value for each layer
1584  compute_layer_Zs(model3d, pcb);
1585 
1586  // board edges and cutouts
1587  export_vrml_board(model3d, pcb);
1588 
1589  // Drawing and text on the board
1590  if( !aUsePlainPCB )
1591  export_vrml_drawings( model3d, pcb );
1592 
1593  // Export vias and trackage
1594  export_vrml_tracks( model3d, pcb );
1595 
1596  // Export zone fills
1597  if( !aUsePlainPCB )
1598  export_vrml_zones( model3d, pcb);
1599 
1600  if( USE_INLINES )
1601  {
1602  // check if the 3D Subdir exists - create if not
1603  wxFileName subdir( SUBDIR_3D, "" );
1604 
1605  if( ! subdir.DirExists() )
1606  {
1607  if( !wxDir::Make( subdir.GetFullPath() ) )
1608  throw( std::runtime_error( "Could not create 3D model subdirectory" ) );
1609  }
1610 
1611  OPEN_OSTREAM( output_file, TO_UTF8( aFullFileName ) );
1612 
1613  if( output_file.fail() )
1614  {
1615  std::ostringstream ostr;
1616  ostr << "Could not open file '" << TO_UTF8( aFullFileName ) << "'";
1617  throw( std::runtime_error( ostr.str().c_str() ) );
1618  }
1619 
1620  output_file.imbue( std::locale( "C" ) );
1621 
1622  // Begin with the usual VRML boilerplate
1623  wxString fn = aFullFileName;
1624  fn.Replace( "\\" , "/" );
1625  output_file << "#VRML V2.0 utf8\n";
1626  output_file << "WorldInfo {\n";
1627  output_file << " title \"" << TO_UTF8( fn ) << " - Generated by Pcbnew\"\n";
1628  output_file << "}\n";
1629  output_file << "Transform {\n";
1630  output_file << " scale " << std::setprecision( PRECISION );
1631  output_file << WORLD_SCALE << " ";
1632  output_file << WORLD_SCALE << " ";
1633  output_file << WORLD_SCALE << "\n";
1634  output_file << " children [\n";
1635 
1636  // Export footprints
1637  for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
1638  export_vrml_module( model3d, pcb, module, &output_file );
1639 
1640  // write out the board and all layers
1641  write_layers( model3d, pcb, TO_UTF8( aFullFileName ), &output_file );
1642 
1643  // Close the outer 'transform' node
1644  output_file << "]\n}\n";
1645 
1646  CLOSE_STREAM( output_file );
1647  }
1648  else
1649  {
1650  // Export footprints
1651  for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
1652  export_vrml_module( model3d, pcb, module, NULL );
1653 
1654  // write out the board and all layers
1655  write_layers( model3d, pcb, TO_UTF8( aFullFileName ), NULL );
1656  }
1657  }
1658  catch( const std::exception& e )
1659  {
1660  wxString msg;
1661  msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
1662  wxMessageBox( msg );
1663 
1664  ok = false;
1665  }
1666 
1667  return ok;
1668 }
1669 
1670 
1672 {
1673  if( colorIdx == -1 )
1674  colorIdx = VRML_COLOR_PCB;
1675  else if( colorIdx == VRML_COLOR_LAST )
1676  return NULL;
1677 
1678  if( sgmaterial[colorIdx] )
1679  return sgmaterial[colorIdx];
1680 
1681  IFSG_APPEARANCE vcolor( (SGNODE*) NULL );
1682  VRML_COLOR* cp = &colors[colorIdx];
1683 
1684  vcolor.SetSpecular( cp->spec_red, cp->spec_grn, cp->spec_blu );
1685  vcolor.SetDiffuse( cp->diffuse_red, cp->diffuse_grn, cp->diffuse_blu );
1686  vcolor.SetShininess( cp->shiny );
1687  // NOTE: XXX - replace with a better equation; using this definition
1688  // of ambient will not yield the best results
1689  vcolor.SetAmbient( cp->ambient, cp->ambient, cp->ambient );
1690  vcolor.SetTransparency( cp->transp );
1691 
1692  sgmaterial[colorIdx] = vcolor.GetRawPtr();
1693 
1694  return sgmaterial[colorIdx];
1695 }
1696 
1697 
1698 static void create_vrml_plane( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
1699  VRML_LAYER* layer, double top_z, bool aTopPlane )
1700 {
1701  std::vector< double > vertices;
1702  std::vector< int > idxPlane;
1703  std::vector< int > idxSide;
1704 
1705  if( !(*layer).Get2DTriangles( vertices, idxPlane, top_z, aTopPlane ) )
1706  {
1707 #ifdef DEBUG
1708  do {
1709  std::ostringstream ostr;
1710  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1711  ostr << " * [INFO] no vertex data";
1712  wxLogDebug( "%s\n", ostr.str().c_str() );
1713  } while( 0 );
1714 #endif
1715 
1716  return;
1717  }
1718 
1719  if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
1720  {
1721 #ifdef DEBUG
1722  do {
1723  std::ostringstream ostr;
1724  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1725  ostr << " * [BUG] index lists are not a multiple of 3 (not a triangle list)";
1726  wxLogDebug( "%s\n", ostr.str().c_str() );
1727  } while( 0 );
1728 #endif
1729 
1730  throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a triangle list)" ) );
1731  }
1732 
1733  std::vector< SGPOINT > vlist;
1734  size_t nvert = vertices.size() / 3;
1735  size_t j = 0;
1736 
1737  for( size_t i = 0; i < nvert; ++i, j+= 3 )
1738  vlist.push_back( SGPOINT( vertices[j], vertices[j+1], vertices[j+2] ) );
1739 
1740  // create the intermediate scenegraph
1741  IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1742  IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1743  IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1744  IFSG_COORDS cp( face ); // coordinates for all faces
1745  cp.SetCoordsList( nvert, &vlist[0] );
1746  IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1747  coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1748  IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1749 
1750  // set the normals
1751  if( aTopPlane )
1752  {
1753  for( size_t i = 0; i < nvert; ++i )
1754  norms.AddNormal( 0.0, 0.0, 1.0 );
1755  }
1756  else
1757  {
1758  for( size_t i = 0; i < nvert; ++i )
1759  norms.AddNormal( 0.0, 0.0, -1.0 );
1760  }
1761 
1762  // assign a color from the palette
1763  SGNODE* modelColor = getSGColor( colorID );
1764 
1765  if( NULL != modelColor )
1766  {
1767  if( NULL == S3D::GetSGNodeParent( modelColor ) )
1768  shape.AddChildNode( modelColor );
1769  else
1770  shape.AddRefNode( modelColor );
1771  }
1772 
1773  return;
1774 }
1775 
1776 
1777 static void create_vrml_shell( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
1778  VRML_LAYER* layer, double top_z, double bottom_z )
1779 {
1780  std::vector< double > vertices;
1781  std::vector< int > idxPlane;
1782  std::vector< int > idxSide;
1783 
1784  if( top_z < bottom_z )
1785  {
1786  double tmp = top_z;
1787  top_z = bottom_z;
1788  bottom_z = tmp;
1789  }
1790 
1791  if( !(*layer).Get3DTriangles( vertices, idxPlane, idxSide, top_z, bottom_z ) )
1792  {
1793 #ifdef DEBUG
1794  do {
1795  std::ostringstream ostr;
1796  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1797  ostr << " * [INFO] no vertex data";
1798  wxLogDebug( "%s\n", ostr.str().c_str() );
1799  } while( 0 );
1800 #endif
1801 
1802  return;
1803  }
1804 
1805  if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
1806  {
1807 #ifdef DEBUG
1808  do {
1809  std::ostringstream ostr;
1810  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
1811  ostr << " * [BUG] index lists are not a multiple of 3 (not a triangle list)";
1812  wxLogDebug( "%s\n", ostr.str().c_str() );
1813  } while( 0 );
1814 #endif
1815 
1816  throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a triangle list)" ) );
1817  }
1818 
1819  std::vector< SGPOINT > vlist;
1820  size_t nvert = vertices.size() / 3;
1821  size_t j = 0;
1822 
1823  for( size_t i = 0; i < nvert; ++i, j+= 3 )
1824  vlist.push_back( SGPOINT( vertices[j], vertices[j+1], vertices[j+2] ) );
1825 
1826  // create the intermediate scenegraph
1827  IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1828  IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1829  IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1830  IFSG_COORDS cp( face ); // coordinates for all faces
1831  cp.SetCoordsList( nvert, &vlist[0] );
1832  IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1833  coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1834  IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1835 
1836  // number of TOP (and bottom) vertices
1837  j = nvert / 2;
1838 
1839  // set the TOP normals
1840  for( size_t i = 0; i < j; ++i )
1841  norms.AddNormal( 0.0, 0.0, 1.0 );
1842 
1843  // set the BOTTOM normals
1844  for( size_t i = 0; i < j; ++i )
1845  norms.AddNormal( 0.0, 0.0, -1.0 );
1846 
1847  // assign a color from the palette
1848  SGNODE* modelColor = getSGColor( colorID );
1849 
1850  if( NULL != modelColor )
1851  {
1852  if( NULL == S3D::GetSGNodeParent( modelColor ) )
1853  shape.AddChildNode( modelColor );
1854  else
1855  shape.AddRefNode( modelColor );
1856  }
1857 
1858  // create a second shape describing the vertical walls of the extrusion
1859  // using per-vertex-per-face-normals
1860  shape.NewNode( tx0 );
1861  shape.AddRefNode( modelColor ); // set the color to be the same as the top/bottom
1862  face.NewNode( shape );
1863  cp.NewNode( face ); // new vertex list
1864  norms.NewNode( face ); // new normals list
1865  coordIdx.NewNode( face ); // new index list
1866 
1867  // populate the new per-face vertex list and its indices and normals
1868  std::vector< int >::iterator sI = idxSide.begin();
1869  std::vector< int >::iterator eI = idxSide.end();
1870 
1871  size_t sidx = 0; // index to the new coord set
1872  SGPOINT p1, p2, p3;
1873  SGVECTOR vnorm;
1874 
1875  while( sI != eI )
1876  {
1877  p1 = vlist[*sI];
1878  cp.AddCoord( p1 );
1879  ++sI;
1880 
1881  p2 = vlist[*sI];
1882  cp.AddCoord( p2 );
1883  ++sI;
1884 
1885  p3 = vlist[*sI];
1886  cp.AddCoord( p3 );
1887  ++sI;
1888 
1889  vnorm.SetVector( S3D::CalcTriNorm( p1, p2, p3 ) );
1890  norms.AddNormal( vnorm );
1891  norms.AddNormal( vnorm );
1892  norms.AddNormal( vnorm );
1893 
1894  coordIdx.AddIndex( (int)sidx );
1895  ++sidx;
1896  coordIdx.AddIndex( (int)sidx );
1897  ++sidx;
1898  coordIdx.AddIndex( (int)sidx );
1899  ++sidx;
1900  }
1901 }
void SetLayerZ(LAYER_NUM aLayer, double aValue)
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
Definition: common.cpp:183
bool MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon=NULL, int aCircleToSegmentsCount=32)
Merge all basic shapes, converted to a polygon in one polygon, in m_customShapeAsPolygon.
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:673
bool SetTranslation(const SGPOINT &aTranslation)
#define DIM(x)
of elements in an array
Definition: macros.h:98
static double BOARD_SCALE
Definition: export_vrml.cpp:69
static void vrml_text_callback(int x0, int y0, int xf, int yf, void *aData)
static void export_vrml_pad(MODEL_VRML &aModel, BOARD *aPcb, D_PAD *aPad)
SGNODE * GetRawPtr(void)
Function GetRawPtr() returns the raw internal SGNODE pointer.
Definition: ifsg_node.cpp:66
Class ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:60
TEXTE_MODULE & Reference()
Definition: class_module.h:513
static S3D_CACHE * cache
Definition: export_vrml.cpp:64
double x
Definition: sg_base.h:70
VRML_COLOR_INDEX
bool IsMultilineAllowed() const
Definition: eda_text.h:201
double GetDrawRotation() const
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes...
Definition: macros.h:53
static void compose_quat(double q1[4], double q2[4], double qr[4])
PAD_ATTR_T GetAttribute() const
Definition: class_pad.h:405
TEXTE_PCB class definition.
static double WORLD_SCALE
Definition: export_vrml.cpp:68
const wxPoint & GetTextPos() const
Definition: eda_text.h:237
Class IFSG_COORDS is the wrapper for SGCOORDS.
Definition: ifsg_coords.h:40
bool IsItalic() const
Definition: eda_text.h:183
Class IFSG_COORDINDEX is the wrapper for SGCOORDINDEX.
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:65
static bool USE_INLINES
Definition: export_vrml.cpp:65
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, wxString *aErrorText=NULL)
Function GetBoardPolygonOutlines Extracts the board outlines and build a closed polygon from lines...
const wxPoint GetCenter() const override
Function GetCenter()
bool Attach(SGNODE *aNode) override
Function Attach associates a given SGNODE* with this wrapper.
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
int PointCount() const
Function PointCount()
float transp
Definition: export_vrml.cpp:89
VRML_LAYER m_bot_tin
VRML_LAYER m_plated_holes
PROJECT & Prj()
Definition: kicad.cpp:292
VRML_LAYER m_holes
Class BOARD to handle a board.
void TransformRoundRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, int aCircleToSegmentsCount)
Function TransformRoundRectToPolygon convert a rectangle with rounded corners to a polygon Convert ar...
bool IsFilled() const
Definition: class_zone.h:195
int GetRoundRectCornerRadius() const
Function GetRoundRectCornerRadius Has meaning only for rounded rect pads.
Definition: class_pad.h:531
int color
Definition: DXF_plotter.cpp:62
polygon (not yet used for tracks, but could be in microwave apps)
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
bool SetTransparency(float aTransparency)
static void create_vrml_plane(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double aHeight, bool aTopPlane)
MODULE * Next() const
Definition: class_module.h:123
#define OSTREAM
Definition: streamwrapper.h:99
int GetCopperLayerCount() const
Function GetCopperLayerCount.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline
virtual PCB_LAYER_ID GetLayer() const override
Function GetLayer returns the primary layer this item is on.
Definition: class_zone.cpp:175
bool AddRefNode(SGNODE *aNode)
Function AddRefNode adds a reference to an existing node which is not owned by (not a child of) this ...
Definition: ifsg_node.cpp:199
class TEXTE_PCB, text on a layer
Definition: typeinfo.h:92
static MODEL_VRML * model_vrml
void SetVector(double aXVal, double aYVal, double aZVal)
Definition: sg_base.cpp:292
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Returns the reference to aHole-th hole in the aIndex-th outline
void SetOffset(double aXoff, double aYoff)
static void compute_layer_Zs(MODEL_VRML &aModel, BOARD *pcb)
Classes to handle copper zones.
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: class_pad.h:388
usual segment : line with rounded ends
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition: ifsg_api.cpp:636
static void write_triangle_bag(std::ostream &aOut_file, VRML_COLOR &aColor, VRML_LAYER *aLayer, bool aPlane, bool aTop, double aTop_z, double aBottom_z)
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
bool SetParent(SGNODE *aParent)
Function SetParent sets the parent SGNODE of this object.
Definition: ifsg_node.cpp:108
bool IsPolyShapeValid() const
int OutlineCount() const
Returns the number of outlines in the set
static SGNODE * sgmaterial[VRML_COLOR_LAST]
Class SGNODE represents the base class of all Scene Graph nodes.
Definition: sg_node.h:76
const wxSize & GetDrillSize() const
Definition: class_pad.h:275
collects header files for all SG* wrappers and the API
virtual wxString GetShownText() const override
Returns the string actually shown after processing of the base text.
float spec_blu
Definition: export_vrml.cpp:82
bool SetScale(const SGPOINT &aScale)
IFSG_TRANSFORM m_OutputPCB
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
defines the basic data associated with a single 3D model.
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:216
#define abs(a)
Definition: auxiliary.h:84
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:203
bool SetScale(double aWorldScale)
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:94
static void export_vrml_text_module(TEXTE_MODULE *module)
bool AddChildNode(SGNODE *aNode)
Function AddChildNode adds a node as a child owned by this node.
Definition: ifsg_node.cpp:249
void DrawGraphicText(EDA_RECT *aClipBox, wxDC *aDC, const wxPoint &aPos, COLOR4D aColor, const wxString &aText, double aOrient, const wxSize &aSize, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, void(*aCallback)(int x0, int y0, int xf, int yf, void *aData), void *aCallbackData, PLOTTER *aPlotter)
Function DrawGraphicText Draw a graphic text (like module texts)
double GetTextAngle() const
Definition: eda_text.h:177
Functions relatives to tracks, vias and segments used to fill zones.
bool AddIndex(int aIndex)
Function AddIndex adds a single index to the list.
Definition: ifsg_index.cpp:83
int GetThickness() const
Function GetThickness returns pen width.
Definition: eda_text.h:167
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Function WriteVRML writes out the given node and its subnodes to a VRML2 file.
Definition: ifsg_api.cpp:81
This file contains miscellaneous commonly used macros and functions.
static void export_vrml_module(MODEL_VRML &aModel, BOARD *aPcb, MODULE *aModule, std::ostream *aOutputFile)
const wxPoint & GetArcStart() const
virtual wxString GetShownText() const
Returns the string actually shown after processing of the base text.
Definition: eda_text.h:148
static void build_quat(double x, double y, double z, double a, double q[4])
BOARD_ITEM * Next() const
static void export_vrml_circle(MODEL_VRML &aModel, LAYER_NUM layer, double startx, double starty, double endx, double endy, double width)
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
bool AddNormal(double aXValue, double aYValue, double aZValue)
static void export_vrml_padshape(MODEL_VRML &aModel, VRML_LAYER *aTinLayer, D_PAD *aPad)
float ambient
Definition: export_vrml.cpp:88
float diffuse_grn
Definition: export_vrml.cpp:77
PCB_LAYER_ID
A quick note on layer IDs:
STROKE_T GetShape() const
VRML_LAYER m_bot_silk
Class LSET is a set of PCB_LAYER_IDs.
static void export_vrml_drawsegment(MODEL_VRML &aModel, DRAWSEGMENT *drawseg)
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
Class IFSG_NORMALS is the wrapper for the SGNORMALS class.
Definition: ifsg_normals.h:40
VRML_LAYER m_top_silk
void Inflate(int aFactor, int aCircleSegmentsCount)
Performs outline inflation/deflation, using round corners.
bool SetAmbient(float aRVal, float aGVal, float aBVal)
void Move(const VECTOR2I &aVector) override
double GetOrientation() const
Definition: class_module.h:189
Class SHAPE_POLY_SET.
TEXTE_MODULE & Value()
read/write accessors:
Definition: class_module.h:512
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
const wxPoint & GetStart() const
Definition: class_track.h:122
Arcs (with rounded ends)
LSET GetLayerSet() const override
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
Definition: class_pad.h:402
static void export_round_padstack(MODEL_VRML &aModel, BOARD *pcb, double x, double y, double r, LAYER_NUM bottom_layer, LAYER_NUM top_layer, double hole)
VRML_LAYER m_board
bool Fill(std::vector< ZONE_CONTAINER * > aZones, bool aCheck=false)
Definition: zone_filler.cpp:83
D_PAD * Next() const
Definition: class_pad.h:160
#define MIN_VRML_LINEWIDTH
Definition: export_vrml.cpp:57
static bool USE_RELPATH
Definition: export_vrml.cpp:67
static SGNODE * getSGColor(VRML_COLOR_INDEX colorIdx)
const wxSize & GetSize() const
Definition: class_pad.h:269
#define OPEN_OSTREAM(var, name)
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:538
float spec_red
Definition: export_vrml.cpp:80
void GetPositionsOfLinesOfMultilineText(std::vector< wxPoint > &aPositions, int aLineCount) const
Function GetPositionsOfLinesOfMultilineText Populates aPositions with the position of each line of a ...
Definition: eda_text.cpp:315
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
static void export_vrml_arc(MODEL_VRML &aModel, LAYER_NUM layer, double centerx, double centery, double arc_startx, double arc_starty, double width, double arc_angle)
static wxString SUBDIR_3D
Definition: export_vrml.cpp:71
static void export_vrml_pcbtext(MODEL_VRML &aModel, TEXTE_PCB *text)
int GetAreaCount() const
Function GetAreaCount.
Definition: class_board.h:1020
SHAPE_POLY_SET & GetPolyShape()
static void export_vrml_tracks(MODEL_VRML &aModel, BOARD *pcb)
static void export_vrml_board(MODEL_VRML &aModel, BOARD *aPcb)
static void export_vrml_drawings(MODEL_VRML &aModel, BOARD *pcb)
double GetOrientationRadians() const
Definition: class_module.h:191
float spec_grn
Definition: export_vrml.cpp:81
const SHAPE_POLY_SET & GetFilledPolysList() const
Function GetFilledPolysList returns a reference to the list of filled polygons.
Definition: class_zone.h:561
VRML_LAYER m_bot_copper
void Fracture(POLYGON_MODE aFastMode)
Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the oute...
Class LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
LAYER_NUM m_text_layer
static void write_layers(MODEL_VRML &aModel, BOARD *aPcb, const char *aFileName, OSTREAM *aOutputFile)
float emit_grn
Definition: export_vrml.cpp:85
BOARD * GetBoard()
std::list< SGNODE * > m_components
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
wxString ResolvePath(const wxString &aFileName)
Function ResolvePath determines the full path of the given file name.
static wxString PROJ_DIR
Definition: export_vrml.cpp:72
bool SetIndices(size_t nIndices, int *aIndexList)
Function SetIndices sets the number of indices and creates a copy of the given index data...
Definition: ifsg_index.cpp:63
class TEXTE_MODULE, text in a footprint
Definition: typeinfo.h:93
EDA_TEXT_VJUSTIFY_T GetVertJustify() const
Definition: eda_text.h:204
SGLIB_API SGVECTOR CalcTriNorm(const SGPOINT &p1, const SGPOINT &p2, const SGPOINT &p3)
Function CalcTriNorm returns the normal vector of a triangle described by vertices p1...
Definition: ifsg_api.cpp:606
static void export_vrml_polygon(MODEL_VRML &aModel, LAYER_NUM layer, DRAWSEGMENT *aOutline, double aOrientation, wxPoint aPos)
bool SetSpecular(float aRVal, float aGVal, float aBVal)
std::list< MODULE_3D_SETTINGS > & Models()
Definition: class_module.h:179
static void export_vrml_zones(MODEL_VRML &aModel, BOARD *aPcb)
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: class_zone.h:180
TRACK * Next() const
Definition: class_track.h:99
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
double GetAngle() const
see class PGM_BASE
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
ZONE_CONTAINER * GetArea(int index) const
Function GetArea returns the Area (Zone Container) at a given index.
Definition: class_board.h:991
bool ExportVRML_File(const wxString &aFullFileName, double aMMtoWRMLunit, bool aExport3DFiles, bool aUseRelativePaths, bool aUsePlainPCB, const wxString &a3D_Subdir, double aXRef, double aYRef)
Function ExportVRML_File Creates the file(s) exporting current BOARD to a VRML file.
float emit_blu
Definition: export_vrml.cpp:86
bool SetShininess(float aShininess)
double DEG2RAD(double deg)
Definition: trigo.h:199
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:170
bool IsMirrored() const
Definition: eda_text.h:192
DLIST< MODULE > m_Modules
Definition: class_board.h:248
float diffuse_red
Definition: export_vrml.cpp:76
Class SHAPE_LINE_CHAIN.
bool IsVisible() const
Definition: eda_text.h:189
double m_arcMinLen
int GetWidth() const
Definition: class_track.h:116
Class IFSG_FACESET is the wrapper for the SGFACESET class.
Definition: ifsg_faceset.h:40
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
Class IFSG_TRANSFORM is the wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH...
size_t i
Definition: json11.cpp:597
SGLIB_API void DestroyNode(SGNODE *aNode)
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:210
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees, but soon degrees.
Definition: class_pad.h:382
static void create_vrml_shell(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double top_z, double bottom_z)
double DECIDEG2RAD(double deg)
Definition: trigo.h:203
static void from_quat(double q[4], double rot[4])
const wxSize & GetDelta() const
Definition: class_pad.h:272
#define IU_PER_MILS
Definition: plotter.cpp:134
defines the display data cache manager for 3D models
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:154
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
int GetWidth() const
double m_brd_thickness
wxPoint ShapePos() const
Definition: class_pad.cpp:517
static const int PRECISION
Definition: export_vrml.cpp:70
static bool GetLayer(MODEL_VRML &aModel, LAYER_NUM layer, VRML_LAYER **vlayer)
double m_minLineWidth
static bool USE_DEFS
Definition: export_vrml.cpp:66
FILENAME_RESOLVER * GetResolver(void)
Definition: 3d_cache.cpp:683
VECTOR2I & Point(int aIndex)
Function Point()
DLIST< BOARD_ITEM > & GraphicalItemsList()
Definition: class_module.h:166
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
#define PLATE_OFFSET
Definition: export_vrml.cpp:62
DLIST< D_PAD > & PadsList()
Definition: class_module.h:163
static void export_vrml_via(MODEL_VRML &aModel, BOARD *aPcb, const VIA *aVia)
static void export_vrml_edge_module(MODEL_VRML &aModel, EDGE_MODULE *aOutline, MODULE *aModule)
void Destroy(void)
Function Destroy deletes the object held by this wrapper.
Definition: ifsg_node.cpp:54
DLIST< TRACK > m_Track
Definition: class_board.h:249
Module description (excepted pads)
Definition: colors.h:45
const wxSize & GetTextSize() const
Definition: eda_text.h:228
#define CLOSE_STREAM(var)
SCENEGRAPH * Load(const wxString &aModelFile)
Function Load attempts to load the scene data for a model; it will consult the internal cache list an...
Definition: 3d_cache.cpp:276
static void export_vrml_line(MODEL_VRML &aModel, LAYER_NUM layer, double startx, double starty, double endx, double endy, double width)
VRML_LAYER m_top_copper
double Distance(double x1, double y1, double x2, double y2)
const wxPoint GetPosition() const override
Definition: class_pad.h:220
VRML_LAYER m_top_tin
EDGE_MODULE class definition.
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
void Rotate(double aAngle, const VECTOR2I &aCenter)
Function Rotate rotates all vertices by a given angle.
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
VRML_COLOR & GetColor(VRML_COLOR_INDEX aIndex)
float emit_red
Definition: export_vrml.cpp:84
const wxPoint GetPosition() const override
Definition: class_module.h:184
bool AddCoord(double aXValue, double aYValue, double aZValue)
float diffuse_blu
Definition: export_vrml.cpp:78
double GetLayerZ(LAYER_NUM aLayer)
static VRML_COLOR colors[VRML_COLOR_LAST]
DLIST_ITERATOR_WRAPPER< BOARD_ITEM > Drawings()
Definition: class_board.h:255
#define ART_OFFSET
Definition: export_vrml.cpp:60
Class IFSG_SHAPE is the wrapper for the SGSHAPE class.
Definition: ifsg_shape.h:40
#define min(a, b)
Definition: auxiliary.h:85
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
VRML_COLOR(float dr, float dg, float db, float sr, float sg, float sb, float er, float eg, float eb, float am, float tr, float sh)
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39