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