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