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