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-2021 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 #include <wx/msgdlg.h>
33 
34 #include "3d_cache/3d_cache.h"
35 #include "3d_cache/3d_info.h"
36 #include "board.h"
37 #include "board_design_settings.h"
38 #include "fp_shape.h"
39 #include "footprint.h"
40 #include "pad.h"
41 #include "pcb_text.h"
42 #include "pcb_track.h"
43 #include "convert_to_biu.h"
44 #include <core/arraydim.h>
45 #include <filename_resolver.h>
46 #include "plugins/3dapi/ifsg_all.h"
47 #include "streamwrapper.h"
48 #include "vrml_layer.h"
49 #include "pcb_edit_frame.h"
50 
53 #include <macros.h>
54 
55 #include <exporter_vrml.h>
56 
57 // The max error (in mm) to approximate arcs to segments:
58 #define ERR_APPROX_MAX_MM 0.005
59 
60 
62  m_OutputPCB( nullptr )
63 {
64  m_ReuseDef = true;
65  m_precision = 6;
66  m_WorldScale = 1.0;
67  m_Cache3Dmodels = nullptr;
68  m_Pcb = nullptr;
71  m_BoardToVrmlScale = MM_PER_IU;
72 
73  for( int ii = 0; ii < VRML_COLOR_LAST; ++ii )
74  m_sgmaterial[ii] = nullptr;
75 
76  for( unsigned i = 0; i < arrayDim( m_layer_z ); ++i )
77  m_layer_z[i] = 0;
78 
79  // this default only makes sense if the output is in mm
80  m_brd_thickness = 1.6;
81 
82  // pcb green
84  0.12f, 0.20f, 0.19f, 0.01f, 0.03f, 0.01f, 0.0f, 0.0f, 0.0f, 0.8f, 0.0f, 0.02f );
85  // copper color
87  0.72f, 0.45f, 0.2f, 0.01f, 0.05f, 0.01f, 0.0f, 0.0f, 0.0f, 0.8f, 0.0f, 0.02f );
88  // silkscreen white
90  0.7f, 0.7f, 0.9f, 0.1f, 0.1f, 0.1f, 0.0f, 0.0f, 0.0f, 0.9f, 0.0f, 0.02f );
91  // solder paste silver (gray)
92  vrml_colors_list[VRML_COLOR_PASTE] = VRML_COLOR( 0.4f, 0.4f, 0.4f, 0.2f, 0.2f, 0.2f, 0.0f,
93  0.0f, 0.0f, 0.8f, 0.0f, 0.8f );
94  // solder mask green with transparency
96  0.07f, 0.3f, 0.12f, 0.01f, 0.03f, 0.01f, 0.0f, 0.0f, 0.0f, 0.8f, 0.25f, 0.02f );
97 
98  SetOffset( 0.0, 0.0 );
99 }
100 
101 
103 {
104  // destroy any unassociated material appearances
105  for( int j = 0; j < VRML_COLOR_LAST; ++j )
106  {
107  if( m_sgmaterial[j] && nullptr == S3D::GetSGNodeParent( m_sgmaterial[j] ) )
109 
110  m_sgmaterial[j] = nullptr;
111  }
112 
113  if( !m_components.empty() )
114  {
115  IFSG_TRANSFORM tmp( false );
116 
117  for( auto i : m_components )
118  {
119  tmp.Attach( i );
120  tmp.SetParent( nullptr );
121  }
122 
123  m_components.clear();
125  }
126 }
127 
128 
129 bool EXPORTER_PCB_VRML::SetScale( double aWorldScale )
130 {
131  // set the scaling of the VRML world
132  if( aWorldScale < 0.001 || aWorldScale > 10.0 )
133  throw( std::runtime_error( "WorldScale out of range (valid range is 0.001 to 10.0)" ) );
134 
135  m_OutputPCB.SetScale( aWorldScale * 2.54 );
136  m_WorldScale = aWorldScale * 2.54;
137 
138  return true;
139 }
140 
141 
142 void EXPORTER_PCB_VRML::SetOffset( double aXoff, double aYoff )
143 {
144  m_tx = aXoff;
145  m_ty = -aYoff;
146 
147  m_holes.SetVertexOffsets( aXoff, aYoff );
148  m_3D_board.SetVertexOffsets( aXoff, aYoff );
149  m_top_copper.SetVertexOffsets( aXoff, aYoff );
150  m_bot_copper.SetVertexOffsets( aXoff, aYoff );
151  m_top_silk.SetVertexOffsets( aXoff, aYoff );
152  m_bot_silk.SetVertexOffsets( aXoff, aYoff );
153  m_top_paste.SetVertexOffsets( aXoff, aYoff );
154  m_bot_paste.SetVertexOffsets( aXoff, aYoff );
155  m_top_soldermask.SetVertexOffsets( aXoff, aYoff );
156  m_bot_soldermask.SetVertexOffsets( aXoff, aYoff );
157  m_plated_holes.SetVertexOffsets( aXoff, aYoff );
158 }
159 
160 
161 bool EXPORTER_PCB_VRML::GetLayer3D( LAYER_NUM layer, VRML_LAYER** vlayer )
162 {
163  // select the VRML layer object to draw on; return true if
164  // a layer has been selected.
165  switch( layer )
166  {
167  case B_Cu: *vlayer = &m_bot_copper; return true;
168  case F_Cu: *vlayer = &m_top_copper; return true;
169  case B_SilkS: *vlayer = &m_bot_silk; return true;
170  case F_SilkS: *vlayer = &m_top_silk; return true;
171  case B_Mask: *vlayer = &m_bot_soldermask; return true;
172  case F_Mask: *vlayer = &m_top_soldermask; return true;
173  case B_Paste: *vlayer = &m_bot_paste; return true;
174  case F_Paste: *vlayer = &m_top_paste; return true;
175  default: return false;
176  }
177 }
178 
180 {
181  SHAPE_POLY_SET holes, outlines = m_pcbOutlines;
182 
183  // holes is the solder mask opening.
184  // the actual shape is the negative shape of mask opening.
185  PCB_LAYER_ID pcb_layer = F_Mask;
186  VRML_LAYER* vrmllayer = &m_top_soldermask;
187 
188  for( int lcnt = 0; lcnt < 2; lcnt++ )
189  {
190  holes.RemoveAllContours();
191  outlines.RemoveAllContours();
192  outlines = m_pcbOutlines;
193  m_Pcb->ConvertBrdLayerToPolygonalContours( pcb_layer, holes );
194 
195  outlines.BooleanSubtract( holes, SHAPE_POLY_SET::PM_FAST );
196  outlines.Fracture( SHAPE_POLY_SET::PM_FAST );
197  ExportVrmlPolygonSet( vrmllayer, outlines );
198 
199  pcb_layer = B_Mask;
200  vrmllayer = &m_bot_soldermask;
201  }
202 }
203 
204 
206 {
207  SHAPE_POLY_SET outlines;
208 
209  PCB_LAYER_ID pcb_layer[] =
210  {
212  };
213 
214  VRML_LAYER* vrmllayer[] =
215  {
217  nullptr // Sentinel
218  };
219 
220  for( int lcnt = 0; ; lcnt++ )
221  {
222  if( vrmllayer[lcnt] == nullptr )
223  break;
224 
225  outlines.RemoveAllContours();
226  m_Pcb->ConvertBrdLayerToPolygonalContours( pcb_layer[lcnt], outlines );
227  outlines.Fracture( SHAPE_POLY_SET::PM_FAST );
228 
229  ExportVrmlPolygonSet( vrmllayer[lcnt], outlines );
230  }
231 }
232 
233 
235 
236 
237 void EXPORTER_PCB_VRML::write_triangle_bag( std::ostream& aOut_file, const VRML_COLOR& aColor,
238  VRML_LAYER* aLayer, bool aPlane, bool aTop,
239  double aTop_z, double aBottom_z )
240 {
241  // A lot of nodes are not required, but blender sometimes chokes without them.
242  static const char* shape_boiler[] =
243  {
244  "Transform {\n",
245  " children [\n",
246  " Group {\n",
247  " children [\n",
248  " Shape {\n",
249  " appearance Appearance {\n",
250  " material Material {\n",
251  0, // Material marker
252  " }\n",
253  " }\n",
254  " geometry IndexedFaceSet {\n",
255  " solid TRUE\n",
256  " coord Coordinate {\n",
257  " point [\n",
258  0, // Coordinates marker
259  " ]\n",
260  " }\n",
261  " coordIndex [\n",
262  0, // Index marker
263  " ]\n",
264  " }\n",
265  " }\n",
266  " ]\n",
267  " }\n",
268  " ]\n",
269  "}\n",
270  0 // End marker
271  };
272 
273  int marker_found = 0, lineno = 0;
274 
275  while( marker_found < 4 )
276  {
277  if( shape_boiler[lineno] )
278  {
279  aOut_file << shape_boiler[lineno];
280  }
281  else
282  {
283  marker_found++;
284 
285  switch( marker_found )
286  {
287  case 1: // Material marker
288  {
289  std::streamsize lastPrecision = aOut_file.precision();
290  aOut_file << " diffuseColor " << std::setprecision(3);
291  aOut_file << aColor.diffuse_red << " ";
292  aOut_file << aColor.diffuse_grn << " ";
293  aOut_file << aColor.diffuse_blu << "\n";
294 
295  aOut_file << " specularColor ";
296  aOut_file << aColor.spec_red << " ";
297  aOut_file << aColor.spec_grn << " ";
298  aOut_file << aColor.spec_blu << "\n";
299 
300  aOut_file << " emissiveColor ";
301  aOut_file << aColor.emit_red << " ";
302  aOut_file << aColor.emit_grn << " ";
303  aOut_file << aColor.emit_blu << "\n";
304 
305  aOut_file << " ambientIntensity " << aColor.ambient << "\n";
306  aOut_file << " transparency " << aColor.transp << "\n";
307  aOut_file << " shininess " << aColor.shiny << "\n";
308  aOut_file.precision( lastPrecision );
309  }
310  break;
311 
312  case 2:
313 
314  if( aPlane )
315  aLayer->WriteVertices( aTop_z, aOut_file, m_precision );
316  else
317  aLayer->Write3DVertices( aTop_z, aBottom_z, aOut_file, m_precision );
318 
319  aOut_file << "\n";
320  break;
321 
322  case 3:
323 
324  if( aPlane )
325  aLayer->WriteIndices( aTop, aOut_file );
326  else
327  aLayer->Write3DIndices( aOut_file );
328 
329  aOut_file << "\n";
330  break;
331 
332  default:
333  break;
334  }
335  }
336 
337  lineno++;
338  }
339 }
340 
341 
342 void EXPORTER_PCB_VRML::writeLayers( const char* aFileName, OSTREAM* aOutputFile )
343 {
344  // VRML_LAYER board;
345  m_3D_board.Tesselate( &m_holes );
346  double brdz = m_brd_thickness / 2.0
348 
350  {
351  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PCB ),
352  &m_3D_board, false, false, brdz, -brdz );
353  }
354  else
355  {
357  }
358 
359  // VRML_LAYER m_top_copper;
360  m_top_copper.Tesselate( &m_holes );
361 
363  {
365  &m_top_copper, true, true, GetLayerZ( F_Cu ), 0 );
366  }
367  else
368  {
370  GetLayerZ( F_Cu ), true );
371  }
372 
373  // VRML_LAYER m_top_paste;
374  m_top_paste.Tesselate( &m_holes );
375 
377  {
378  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PASTE ),
379  &m_top_paste, true, true,
380  GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) *
382  0 );
383  }
384  else
385  {
387  GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) *
389  true );
390  }
391 
392  // VRML_LAYER m_top_soldermask;
393  m_top_soldermask.Tesselate( &m_holes );
394 
396  {
398  &m_top_soldermask, true, true,
399  GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) *
401  0 );
402  }
403  else
404  {
406  GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) *
408  true );
409  }
410 
411  // VRML_LAYER m_bot_copper;
412  m_bot_copper.Tesselate( &m_holes );
413 
415  {
417  &m_bot_copper, true, false, GetLayerZ( B_Cu ), 0 );
418  }
419  else
420  {
422  GetLayerZ( B_Cu ), false );
423  }
424 
425  // VRML_LAYER m_bot_paste;
426  m_bot_paste.Tesselate( &m_holes );
427 
429  {
430  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PASTE ),
431  &m_bot_paste, true, false,
432  GetLayerZ( B_Cu )
434  0 );
435  }
436  else
437  {
439  GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) *
441  false );
442  }
443 
444  // VRML_LAYER m_bot_mask:
445  m_bot_soldermask.Tesselate( &m_holes );
446 
448  {
450  &m_bot_soldermask, true, false,
451  GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) *
453  0 );
454  }
455  else
456  {
458  GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) *
460  false );
461  }
462 
463  // VRML_LAYER PTH;
464  m_plated_holes.Tesselate( nullptr, true );
465 
467  {
468  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PASTE ),
469  &m_plated_holes, false, false,
470  GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) *
472  GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) *
474  }
475  else
476  {
478  GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) *
480  GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) *
482  }
483 
484  // VRML_LAYER m_top_silk;
485  m_top_silk.Tesselate( &m_holes );
486 
488  {
490  true, true, GetLayerZ( F_SilkS ), 0 );
491  }
492  else
493  {
495  GetLayerZ( F_SilkS ), true );
496  }
497 
498  // VRML_LAYER m_bot_silk;
499  m_bot_silk.Tesselate( &m_holes );
500 
502  {
504  true, false, GetLayerZ( B_SilkS ), 0 );
505  }
506  else
507  {
509  GetLayerZ( B_SilkS ), false );
510  }
511 
513  S3D::WriteVRML( aFileName, true, m_OutputPCB.GetRawPtr(), true, true );
514 }
515 
516 
518 {
519  int copper_layers = m_Pcb->GetCopperLayerCount();
520 
521  // We call it 'layer' thickness, but it's the whole board thickness!
523  double half_thickness = m_brd_thickness / 2;
524 
525  // Compute each layer's Z value, more or less like the 3d view
526  for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq )
527  {
528  PCB_LAYER_ID i = *seq;
529 
530  if( i < copper_layers )
531  SetLayerZ( i, half_thickness - m_brd_thickness * i / (copper_layers - 1) );
532  else
533  SetLayerZ( i, - half_thickness ); // bottom layer
534  }
535 
536  // To avoid rounding interference, we apply an epsilon to each successive layer
537  double epsilon_z = Millimeter2iu( ART_OFFSET ) * m_BoardToVrmlScale;
538  SetLayerZ( B_Paste, -half_thickness - epsilon_z );
539  SetLayerZ( B_Adhes, -half_thickness - epsilon_z );
540  SetLayerZ( B_SilkS, -half_thickness - epsilon_z * 3 );
541  SetLayerZ( B_Mask, -half_thickness - epsilon_z * 2 );
542  SetLayerZ( F_Mask, half_thickness + epsilon_z * 2 );
543  SetLayerZ( F_SilkS, half_thickness + epsilon_z * 3 );
544  SetLayerZ( F_Adhes, half_thickness + epsilon_z );
545  SetLayerZ( F_Paste, half_thickness + epsilon_z );
546  SetLayerZ( Dwgs_User, half_thickness + epsilon_z * 5 );
547  SetLayerZ( Cmts_User, half_thickness + epsilon_z * 6 );
548  SetLayerZ( Eco1_User, half_thickness + epsilon_z * 7 );
549  SetLayerZ( Eco2_User, half_thickness + epsilon_z * 8 );
550  SetLayerZ( Edge_Cuts, 0 );
551 }
552 
553 
554 void EXPORTER_PCB_VRML::ExportVrmlPolygonSet( VRML_LAYER* aVlayer, const SHAPE_POLY_SET& aOutlines )
555 {
556  // Polygons in SHAPE_POLY_SET must be without hole, i.e. holes must be linked
557  // previously to their main outline.
558  for( int icnt = 0; icnt < aOutlines.OutlineCount(); icnt++ )
559  {
560  const SHAPE_LINE_CHAIN& outline = aOutlines.COutline( icnt );
561 
562  int seg = aVlayer->NewContour();
563 
564  for( int jj = 0; jj < outline.PointCount(); jj++ )
565  {
566  if( !aVlayer->AddVertex( seg, outline.CPoint( jj ).x * m_BoardToVrmlScale,
567  -outline.CPoint( jj ).y * m_BoardToVrmlScale ) )
568  throw( std::runtime_error( aVlayer->GetError() ) );
569  }
570 
571  aVlayer->EnsureWinding( seg, false );
572  }
573 }
574 
575 
577 {
579  {
580  wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
581  }
582 
583  int seg;
584 
585  for( int cnt = 0; cnt < m_pcbOutlines.OutlineCount(); cnt++ )
586  {
587  const SHAPE_LINE_CHAIN& outline = m_pcbOutlines.COutline( cnt );
588 
589  seg = m_3D_board.NewContour();
590 
591  for( int j = 0; j < outline.PointCount(); j++ )
592  {
593  m_3D_board.AddVertex( seg, (double)outline.CPoint(j).x * m_BoardToVrmlScale,
594  -((double)outline.CPoint(j).y * m_BoardToVrmlScale ) );
595 
596  }
597 
598  m_3D_board.EnsureWinding( seg, false );
599 
600  // Generate board holes from outlines:
601  for( int ii = 0; ii < m_pcbOutlines.HoleCount( cnt ); ii++ )
602  {
603  const SHAPE_LINE_CHAIN& hole = m_pcbOutlines.Hole( cnt, ii );
604 
605  seg = m_holes.NewContour();
606 
607  if( seg < 0 )
608  {
609  wxLogError( _( "VRML Export Failed: Could not add holes to contours." ) );
610  return;
611  }
612 
613  for( int j = 0; j < hole.PointCount(); j++ )
614  {
615  m_holes.AddVertex( seg, (double) hole.CPoint(j).x * m_BoardToVrmlScale,
616  -( (double) hole.CPoint(j).y * m_BoardToVrmlScale ) );
617  }
618 
619  m_holes.EnsureWinding( seg, true );
620  }
621  }
622 }
623 
624 
626 {
627  PCB_LAYER_ID top_layer, bottom_layer;
628 
629  for( PCB_TRACK* track : m_Pcb->Tracks() )
630  {
631  if( track->Type() != PCB_VIA_T )
632  continue;
633 
634  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
635 
636  via->LayerPair( &top_layer, &bottom_layer );
637 
638  // do not render a buried via
639  if( top_layer != F_Cu && bottom_layer != B_Cu )
640  continue;
641 
642  // Export all via holes to m_holes
643  double hole_radius = via->GetDrillValue() * m_BoardToVrmlScale / 2.0;
644 
645  if( hole_radius <= 0 )
646  continue;
647 
648  double x = via->GetStart().x * m_BoardToVrmlScale;
649  double y = via->GetStart().y * m_BoardToVrmlScale;
650 
651  // Set the optimal number of segments to approximate a circle.
652  // SetArcParams needs a count max, and the minimal and maximal length
653  // of segments
654  double max_error = ERR_APPROX_MAX_MM;
655 
657  max_error /= 2.54; // The board is exported with a size reduced by 2.54
658 
659  int nsides = GetArcToSegmentCount( via->GetDrillValue(),
660  Millimeter2iu( max_error ), 360.0 );
661  double minSegLength = M_PI * 2.0 * hole_radius / nsides;
662  double maxSegLength = minSegLength*2.0;
663 
664  m_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
665  m_plated_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
666 
667  m_holes.AddCircle( x, -y, hole_radius, true, true );
668  m_plated_holes.AddCircle( x, -y, hole_radius, true, false );
669 
670  m_holes.ResetArcParams();
671  m_plated_holes.ResetArcParams();
672  }
673 }
674 
675 
677 {
678  double hole_drill_w = (double) aPad->GetDrillSize().x * m_BoardToVrmlScale / 2.0;
679  double hole_drill_h = (double) aPad->GetDrillSize().y * m_BoardToVrmlScale / 2.0;
680  double hole_drill = std::min( hole_drill_w, hole_drill_h );
681  double hole_x = aPad->GetPosition().x * m_BoardToVrmlScale;
682  double hole_y = aPad->GetPosition().y * m_BoardToVrmlScale;
683 
684  // Export the hole on the edge layer
685  if( hole_drill > 0 )
686  {
687  double max_error = ERR_APPROX_MAX_MM;
688 
690  max_error /= 2.54; // The board is exported with a size reduced by 2.54
691 
692  int nsides = GetArcToSegmentCount( hole_drill,
693  Millimeter2iu( max_error ), 360.0 );
694  double minSegLength = M_PI * hole_drill / nsides;
695  double maxSegLength = minSegLength*2.0;
696 
697  m_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
698  m_plated_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
699 
700  bool pth = false;
701 
702  if( ( aPad->GetAttribute() != PAD_ATTRIB::NPTH ) )
703  pth = true;
704 
705  if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
706  {
707  // Oblong hole (slot)
708 
709  if( pth )
710  {
711  m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0 + PLATE_OFFSET,
712  hole_drill_h * 2.0 + PLATE_OFFSET,
713  aPad->GetOrientation()/10.0, true, true );
714 
715  m_plated_holes.AddSlot( hole_x, -hole_y,
716  hole_drill_w * 2.0, hole_drill_h * 2.0,
717  aPad->GetOrientation()/10.0, true, false );
718  }
719  else
720  {
721  m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
722  aPad->GetOrientation()/10.0, true, false );
723 
724  }
725  }
726  else
727  {
728  // Drill a round hole
729  if( pth )
730  {
731  m_holes.AddCircle( hole_x, -hole_y, hole_drill + PLATE_OFFSET, true, true );
732  m_plated_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
733  }
734  else
735  {
736  m_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
737  }
738 
739  }
740 
741  m_holes.ResetArcParams();
742  m_plated_holes.ResetArcParams();
743  }
744 }
745 
746 
747 // From axis/rot to quaternion
748 static void build_quat( double x, double y, double z, double a, double q[4] )
749 {
750  double sina = sin( a / 2 );
751 
752  q[0] = x * sina;
753  q[1] = y * sina;
754  q[2] = z * sina;
755  q[3] = cos( a / 2 );
756 }
757 
758 
759 // From quaternion to axis/rot
760 static void from_quat( double q[4], double rot[4] )
761 {
762  rot[3] = acos( q[3] ) * 2;
763 
764  for( int i = 0; i < 3; i++ )
765  rot[i] = q[i] / sin( rot[3] / 2 );
766 }
767 
768 
769 // Quaternion composition
770 static void compose_quat( double q1[4], double q2[4], double qr[4] )
771 {
772  double tmp[4];
773 
774  tmp[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1];
775  tmp[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2];
776  tmp[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0];
777  tmp[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2];
778 
779  qr[0] = tmp[0];
780  qr[1] = tmp[1];
781  qr[2] = tmp[2];
782  qr[3] = tmp[3];
783 }
784 
785 
786 void EXPORTER_PCB_VRML::ExportVrmlFootprint( FOOTPRINT* aFootprint, std::ostream* aOutputFile )
787 {
788  // Note: if m_UseInlineModelsInBrdfile is false, the 3D footprint shape is copied to
789  // the vrml board file, and aOutputFile is not used (can be nullptr)
790  // if m_UseInlineModelsInBrdfile is true, the 3D footprint shape is copied to
791  // aOutputFile (with the suitable rotation/translation/scale transform, and the vrml board
792  // file contains only the filename of 3D shapes to add to the full vrml scene
793  wxCHECK( aFootprint, /* void */ );
794 
795  // Export pad holes
796  for( PAD* pad : aFootprint->Pads() )
798 
799  bool isFlipped = aFootprint->GetLayer() == B_Cu;
800 
801  // Export the object VRML model(s)
802  auto sM = aFootprint->Models().begin();
803  auto eM = aFootprint->Models().end();
804 
805  wxFileName subdir( m_Subdir3DFpModels, wxEmptyString );
806 
807  while( sM != eM )
808  {
809  if( !sM->m_Show )
810  {
811  ++sM;
812  continue;
813  }
814 
815  SGNODE* mod3d = (SGNODE*) m_Cache3Dmodels->Load( sM->m_Filename );
816 
817  if( nullptr == mod3d )
818  {
819  ++sM;
820  continue;
821  }
822 
823  /* Calculate 3D shape rotation:
824  * this is the rotation parameters, with an additional 180 deg rotation
825  * for footprints that are flipped
826  * When flipped, axis rotation is the horizontal axis (X axis)
827  */
828  double rotx = -sM->m_Rotation.x;
829  double roty = -sM->m_Rotation.y;
830  double rotz = -sM->m_Rotation.z;
831 
832  if( isFlipped )
833  {
834  rotx += 180.0;
835  roty = -roty;
836  rotz = -rotz;
837  }
838 
839  // Do some quaternion munching
840  double q1[4], q2[4], rot[4];
841  build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 );
842  build_quat( 0, 1, 0, DEG2RAD( roty ), q2 );
843  compose_quat( q1, q2, q1 );
844  build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 );
845  compose_quat( q1, q2, q1 );
846 
847  // Note here aFootprint->GetOrientation() is in 0.1 degrees, so footprint rotation
848  // has to be converted to radians
849  build_quat( 0, 0, 1, DECIDEG2RAD( aFootprint->GetOrientation() ), q2 );
850  compose_quat( q1, q2, q1 );
851  from_quat( q1, rot );
852 
853  double offsetFactor = 1000.0f * IU_PER_MILS / 25.4f;
854 
855  // adjust 3D shape local offset position
856  // they are given in mm, so they are converted in board IU.
857  double offsetx = sM->m_Offset.x * offsetFactor;
858  double offsety = sM->m_Offset.y * offsetFactor;
859  double offsetz = sM->m_Offset.z * offsetFactor;
860 
861  if( isFlipped )
862  offsetz = -offsetz;
863  else
864  offsety = -offsety; // In normal mode, Y axis is reversed in Pcbnew.
865 
866  RotatePoint( &offsetx, &offsety, aFootprint->GetOrientation() );
867 
868  SGPOINT trans;
869  trans.x = ( offsetx + aFootprint->GetPosition().x ) * m_BoardToVrmlScale + m_tx;
870  trans.y = -( offsety + aFootprint->GetPosition().y) * m_BoardToVrmlScale - m_ty;
871  trans.z = (offsetz * m_BoardToVrmlScale ) + GetLayerZ( aFootprint->GetLayer() );
872 
874  {
875  wxCHECK( aOutputFile, /* void */ );
876 
877  int old_precision = aOutputFile->precision();
878  aOutputFile->precision( m_precision );
879 
880  wxFileName srcFile = m_Cache3Dmodels->GetResolver()->ResolvePath( sM->m_Filename );
881  wxFileName dstFile;
882  dstFile.SetPath( m_Subdir3DFpModels );
883  dstFile.SetName( srcFile.GetName() );
884  dstFile.SetExt( wxT( "wrl" ) );
885 
886  // copy the file if necessary
887  wxDateTime srcModTime = srcFile.GetModificationTime();
888  wxDateTime destModTime = srcModTime;
889 
890  destModTime.SetToCurrent();
891 
892  if( dstFile.FileExists() )
893  destModTime = dstFile.GetModificationTime();
894 
895  if( srcModTime != destModTime )
896  {
897  wxString fileExt = srcFile.GetExt();
898  fileExt.LowerCase();
899 
900  // copy VRML models and use the scenegraph library to
901  // translate other model types
902  if( fileExt == wxT( "wrl" ) )
903  {
904  if( !wxCopyFile( srcFile.GetFullPath(), dstFile.GetFullPath() ) )
905  continue;
906  }
907  else
908  {
909  if( !S3D::WriteVRML( dstFile.GetFullPath().ToUTF8(), true, mod3d, m_ReuseDef,
910  true ) )
911  continue;
912  }
913  }
914 
915  (*aOutputFile) << "Transform {\n";
916 
917  // only write a rotation if it is >= 0.1 deg
918  if( std::abs( rot[3] ) > 0.0001745 )
919  {
920  (*aOutputFile) << " rotation ";
921  (*aOutputFile) << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
922  }
923 
924  (*aOutputFile) << " translation ";
925  (*aOutputFile) << trans.x << " ";
926  (*aOutputFile) << trans.y << " ";
927  (*aOutputFile) << trans.z << "\n";
928 
929  (*aOutputFile) << " scale ";
930  (*aOutputFile) << sM->m_Scale.x << " ";
931  (*aOutputFile) << sM->m_Scale.y << " ";
932  (*aOutputFile) << sM->m_Scale.z << "\n";
933 
934  (*aOutputFile) << " children [\n Inline {\n url \"";
935 
937  {
938  wxFileName tmp = dstFile;
939  tmp.SetExt( wxEmptyString );
940  tmp.SetName( wxEmptyString );
941  tmp.RemoveLastDir();
942  dstFile.MakeRelativeTo( tmp.GetPath() );
943  }
944 
945  wxString fn = dstFile.GetFullPath();
946  fn.Replace( wxT( "\\" ), wxT( "/" ) );
947  (*aOutputFile) << TO_UTF8( fn ) << "\"\n } ]\n";
948  (*aOutputFile) << " }\n";
949 
950  aOutputFile->precision( old_precision );
951  }
952  else
953  {
954  IFSG_TRANSFORM* modelShape = new IFSG_TRANSFORM( m_OutputPCB.GetRawPtr() );
955 
956  // only write a rotation if it is >= 0.1 deg
957  if( std::abs( rot[3] ) > 0.0001745 )
958  modelShape->SetRotation( SGVECTOR( rot[0], rot[1], rot[2] ), rot[3] );
959 
960  modelShape->SetTranslation( trans );
961  modelShape->SetScale( SGPOINT( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ) );
962 
963  if( nullptr == S3D::GetSGNodeParent( mod3d ) )
964  {
965  m_components.push_back( mod3d );
966  modelShape->AddChildNode( mod3d );
967  }
968  else
969  {
970  modelShape->AddRefNode( mod3d );
971  }
972 
973  }
974 
975  ++sM;
976  }
977 }
978 
979 
980 bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMtoWRMLunit,
981  bool aExport3DFiles, bool aUseRelativePaths,
982  const wxString& a3D_Subdir,
983  double aXRef, double aYRef )
984 {
985  BOARD* pcb = GetBoard();
986  bool success = true;
987 
988  EXPORTER_PCB_VRML model3d;
989  model_vrml = &model3d;
990  model3d.m_Pcb = GetBoard();
991  model3d.SetScale( aMMtoWRMLunit );
992  model3d.m_UseInlineModelsInBrdfile = aExport3DFiles;
993  model3d.m_Subdir3DFpModels = a3D_Subdir;
994  model3d.m_UseRelPathIn3DModelFilename = aUseRelativePaths;
995  model3d.m_Cache3Dmodels = Prj().Get3DCacheManager();
996 
997  if( model3d.m_UseInlineModelsInBrdfile )
998  {
999  model3d.m_BoardToVrmlScale = MM_PER_IU / 2.54;
1000  model3d.SetOffset( -aXRef / 2.54, aYRef / 2.54 );
1001  }
1002  else
1003  {
1004  model3d.m_BoardToVrmlScale = MM_PER_IU;
1005  model3d.SetOffset( -aXRef, aYRef );
1006  }
1007 
1008  try
1009  {
1010  // Preliminary computation: the z value for each layer
1011  model3d.ComputeLayer3D_Zpos();
1012 
1013  // board edges and cutouts
1014  model3d.ExportVrmlBoard();
1015 
1016  // Draw solder mask layer (negative layer)
1017  model3d.ExportVrmlSolderMask();
1018 #if 1
1019  model3d.ExportVrmlViaHoles();
1020  model3d.ExportStandardLayers();
1021 #else
1022  // Drawing and text on the board
1023  model3d.ExportVrmlDrawings();
1024 
1025  // Export vias and trackage
1026  model3d.ExportVrmlTracks();
1027 
1028  // Export zone fills
1029  model3d.ExportVrmlZones();
1030 #endif
1031  if( model3d.m_UseInlineModelsInBrdfile )
1032  {
1033  // Copy fp 3D models in a folder, and link these files in
1034  // the board .vrml file
1035  model3d.ExportFp3DModelsAsLinkedFile( aFullFileName );
1036  }
1037  else
1038  {
1039  // merge footprints in the .vrml board file
1040  for( FOOTPRINT* footprint : pcb->Footprints() )
1041  model3d.ExportVrmlFootprint( footprint, nullptr );
1042 
1043  // write out the board and all layers
1044  model3d.writeLayers( TO_UTF8( aFullFileName ), nullptr );
1045  }
1046  }
1047  catch( const std::exception& e )
1048  {
1049  wxString msg;
1050  msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
1051  wxMessageBox( msg );
1052 
1053  success = false;
1054  }
1055 
1056  return success;
1057 }
1058 
1059 void EXPORTER_PCB_VRML::ExportFp3DModelsAsLinkedFile( const wxString& aFullFileName )
1060 {
1061  // check if the 3D Subdir exists - create if not
1062  wxFileName subdir( m_Subdir3DFpModels, wxEmptyString );
1063 
1064  if( ! subdir.DirExists() )
1065  {
1066  if( !wxDir::Make( subdir.GetFullPath() ) )
1067  throw( std::runtime_error( "Could not create 3D model subdirectory" ) );
1068  }
1069 
1070  OPEN_OSTREAM( output_file, TO_UTF8( aFullFileName ) );
1071 
1072  if( output_file.fail() )
1073  {
1074  std::ostringstream ostr;
1075  ostr << "Could not open file '" << TO_UTF8( aFullFileName ) << "'";
1076  throw( std::runtime_error( ostr.str().c_str() ) );
1077  }
1078 
1079  output_file.imbue( std::locale::classic() );
1080 
1081  // Begin with the usual VRML boilerplate
1082  wxString fn = aFullFileName;
1083  fn.Replace( wxT( "\\" ) , wxT( "/" ) );
1084  output_file << "#VRML V2.0 utf8\n";
1085  output_file << "WorldInfo {\n";
1086  output_file << " title \"" << TO_UTF8( fn ) << " - Generated by Pcbnew\"\n";
1087  output_file << "}\n";
1088  output_file << "Transform {\n";
1089  output_file << " scale " << std::setprecision( m_precision );
1090  output_file << m_WorldScale << " ";
1091  output_file << m_WorldScale << " ";
1092  output_file << m_WorldScale << "\n";
1093  output_file << " children [\n";
1094 
1095  // Export footprints
1096  for( FOOTPRINT* footprint : m_Pcb->Footprints() )
1097  ExportVrmlFootprint( footprint, &output_file );
1098 
1099  // write out the board and all layers
1100  writeLayers( TO_UTF8( aFullFileName ), &output_file );
1101 
1102  // Close the outer 'transform' node
1103  output_file << "]\n}\n";
1104 
1105  CLOSE_STREAM( output_file );
1106 }
1107 
1109 {
1110  if( colorIdx == -1 )
1111  colorIdx = VRML_COLOR_PCB;
1112  else if( colorIdx == VRML_COLOR_LAST )
1113  return nullptr;
1114 
1115  if( m_sgmaterial[colorIdx] )
1116  return m_sgmaterial[colorIdx];
1117 
1118  IFSG_APPEARANCE vcolor( (SGNODE*) nullptr );
1119  VRML_COLOR* cp = &vrml_colors_list[colorIdx];
1120 
1121  vcolor.SetSpecular( cp->spec_red, cp->spec_grn, cp->spec_blu );
1122  vcolor.SetDiffuse( cp->diffuse_red, cp->diffuse_grn, cp->diffuse_blu );
1123  vcolor.SetShininess( cp->shiny );
1124  // NOTE: XXX - replace with a better equation; using this definition
1125  // of ambient will not yield the best results
1126  vcolor.SetAmbient( cp->ambient, cp->ambient, cp->ambient );
1127  vcolor.SetTransparency( cp->transp );
1128 
1129  m_sgmaterial[colorIdx] = vcolor.GetRawPtr();
1130 
1131  return m_sgmaterial[colorIdx];
1132 }
1133 
1134 
1136  VRML_LAYER* layer, double top_z, bool aTopPlane )
1137 {
1138  std::vector< double > vertices;
1139  std::vector< int > idxPlane;
1140 
1141  if( !( *layer ).Get2DTriangles( vertices, idxPlane, top_z, aTopPlane ) )
1142  {
1143  return;
1144  }
1145 
1146  if( ( idxPlane.size() % 3 ) )
1147  {
1148  throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a triangle "
1149  "list)" ) );
1150  }
1151 
1152  std::vector< SGPOINT > vlist;
1153  size_t nvert = vertices.size() / 3;
1154  size_t j = 0;
1155 
1156  for( size_t i = 0; i < nvert; ++i, j+= 3 )
1157  vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
1158 
1159  // create the intermediate scenegraph
1160  IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1161  IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1162  IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1163  IFSG_COORDS cp( face ); // coordinates for all faces
1164  cp.SetCoordsList( nvert, &vlist[0] );
1165  IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1166  coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1167  IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1168 
1169  // set the normals
1170  if( aTopPlane )
1171  {
1172  for( size_t i = 0; i < nvert; ++i )
1173  norms.AddNormal( 0.0, 0.0, 1.0 );
1174  }
1175  else
1176  {
1177  for( size_t i = 0; i < nvert; ++i )
1178  norms.AddNormal( 0.0, 0.0, -1.0 );
1179  }
1180 
1181  // assign a color from the palette
1182  SGNODE* modelColor = getSGColor( colorID );
1183 
1184  if( nullptr != modelColor )
1185  {
1186  if( nullptr == S3D::GetSGNodeParent( modelColor ) )
1187  shape.AddChildNode( modelColor );
1188  else
1189  shape.AddRefNode( modelColor );
1190  }
1191 }
1192 
1193 
1195  VRML_LAYER* layer, double top_z, double bottom_z )
1196 {
1197  std::vector< double > vertices;
1198  std::vector< int > idxPlane;
1199  std::vector< int > idxSide;
1200 
1201  if( top_z < bottom_z )
1202  {
1203  double tmp = top_z;
1204  top_z = bottom_z;
1205  bottom_z = tmp;
1206  }
1207 
1208  if( !( *layer ).Get3DTriangles( vertices, idxPlane, idxSide, top_z, bottom_z )
1209  || idxPlane.empty() || idxSide.empty() )
1210  {
1211  return;
1212  }
1213 
1214  if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
1215  {
1216  throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a "
1217  "triangle list)" ) );
1218  }
1219 
1220  std::vector< SGPOINT > vlist;
1221  size_t nvert = vertices.size() / 3;
1222  size_t j = 0;
1223 
1224  for( size_t i = 0; i < nvert; ++i, j+= 3 )
1225  vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
1226 
1227  // create the intermediate scenegraph
1228  IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1229  IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1230  IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1231  IFSG_COORDS cp( face ); // coordinates for all faces
1232  cp.SetCoordsList( nvert, &vlist[0] );
1233  IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1234  coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1235  IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1236 
1237  // number of TOP (and bottom) vertices
1238  j = nvert / 2;
1239 
1240  // set the TOP normals
1241  for( size_t i = 0; i < j; ++i )
1242  norms.AddNormal( 0.0, 0.0, 1.0 );
1243 
1244  // set the BOTTOM normals
1245  for( size_t i = 0; i < j; ++i )
1246  norms.AddNormal( 0.0, 0.0, -1.0 );
1247 
1248  // assign a color from the palette
1249  SGNODE* modelColor = getSGColor( colorID );
1250 
1251  if( nullptr != modelColor )
1252  {
1253  if( nullptr == S3D::GetSGNodeParent( modelColor ) )
1254  shape.AddChildNode( modelColor );
1255  else
1256  shape.AddRefNode( modelColor );
1257  }
1258 
1259  // create a second shape describing the vertical walls of the extrusion
1260  // using per-vertex-per-face-normals
1261  shape.NewNode( tx0 );
1262  shape.AddRefNode( modelColor ); // set the color to be the same as the top/bottom
1263  face.NewNode( shape );
1264  cp.NewNode( face ); // new vertex list
1265  norms.NewNode( face ); // new normals list
1266  coordIdx.NewNode( face ); // new index list
1267 
1268  // populate the new per-face vertex list and its indices and normals
1269  std::vector< int >::iterator sI = idxSide.begin();
1270  std::vector< int >::iterator eI = idxSide.end();
1271 
1272  size_t sidx = 0; // index to the new coord set
1273  SGPOINT p1, p2, p3;
1274  SGVECTOR vnorm;
1275 
1276  while( sI != eI )
1277  {
1278  p1 = vlist[*sI];
1279  cp.AddCoord( p1 );
1280  ++sI;
1281 
1282  p2 = vlist[*sI];
1283  cp.AddCoord( p2 );
1284  ++sI;
1285 
1286  p3 = vlist[*sI];
1287  cp.AddCoord( p3 );
1288  ++sI;
1289 
1290  vnorm.SetVector( S3D::CalcTriNorm( p1, p2, p3 ) );
1291  norms.AddNormal( vnorm );
1292  norms.AddNormal( vnorm );
1293  norms.AddNormal( vnorm );
1294 
1295  coordIdx.AddIndex( (int)sidx );
1296  ++sidx;
1297  coordIdx.AddIndex( (int)sidx );
1298  ++sidx;
1299  coordIdx.AddIndex( (int)sidx );
1300  ++sidx;
1301  }
1302 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:759
#define PLATE_OFFSET
Definition: exporter_vrml.h:29
void ExportVrmlFootprint(FOOTPRINT *aFootprint, std::ostream *aOutputFile)
IFSG_TRANSFORM m_OutputPCB
VRML_LAYER m_plated_holes
double GetLayerZ(LAYER_NUM aLayer)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
double x
Definition: sg_base.h:70
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:183
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:1835
static void compose_quat(double q1[4], double q2[4], double qr[4])
std::list< SGNODE * > m_components
IFSG_COORDS is the wrapper for SGCOORDS.
Definition: ifsg_coords.h:40
void ExportVrmlPadHole(PAD *aPad)
IFSG_COORDINDEX is the wrapper for SGCOORDINDEX.
void ConvertBrdLayerToPolygonalContours(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aOutlines) const
Build a set of polygons which are the outlines of copper items (pads, tracks, vias,...
Definition: board.cpp:2129
bool SetTransparency(float aTransparency) noexcept
bool Attach(SGNODE *aNode) override
Function Attach associates a given SGNODE* with this wrapper.
float transp
Definition: exporter_vrml.h:59
VRML_COLOR vrml_colors_list[VRML_COLOR_LAST]
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
VRML_COLOR_INDEX
Definition: exporter_vrml.h:32
#define OSTREAM
void SetLayerZ(LAYER_NUM aLayer, double aValue)
double GetOrientation() const
Definition: footprint.h:191
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
void SetVector(double aXVal, double aYVal, double aZVal)
Definition: sg_base.cpp:233
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
int LAYER_NUM
This can be replaced with int and removed.
Definition: layer_ids.h:41
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition: ifsg_api.cpp:494
SGNODE * getSGColor(VRML_COLOR_INDEX colorIdx)
bool SetParent(SGNODE *aParent)
Function SetParent sets the parent SGNODE of this object.
Definition: ifsg_node.cpp:87
bool ExportVRML_File(const wxString &aFullFileName, double aMMtoWRMLunit, bool aExport3DFiles, bool aUseRelativePaths, const wxString &a3D_Subdir, double aXRef, double aYRef)
Create the file(s) exporting current BOARD to a VRML file.
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
The base class of all Scene Graph nodes.
Definition: sg_node.h:74
collects header files for all SG* wrappers and the API
float spec_blu
Definition: exporter_vrml.h:52
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
VRML_LAYER m_top_silk
int PointCount() const
Return the number of points (vertices) in this line chain.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
VRML_LAYER m_top_copper
bool AddChildNode(SGNODE *aNode)
Function AddChildNode adds a node as a child owned by this node.
Definition: ifsg_node.cpp:148
void ExportFp3DModelsAsLinkedFile(const wxString &aFullFileName)
VRML_LAYER m_bot_silk
VRML_LAYER m_bot_soldermask
PADS & Pads()
Definition: footprint.h:169
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:77
This file contains miscellaneous commonly used macros and functions.
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)
void writeLayers(const char *aFileName, OSTREAM *aOutputFile)
static void build_quat(double x, double y, double z, double a, double q[4])
#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)
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
float ambient
Definition: exporter_vrml.h:58
float diffuse_grn
Definition: exporter_vrml.h:47
const wxSize & GetDrillSize() const
Definition: pad.h:243
like PAD_PTH, but not plated
wxString m_Subdir3DFpModels
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:149
VRML_LAYER m_bot_copper
IFSG_NORMALS is the wrapper for the SGNORMALS class.
Definition: ifsg_normals.h:40
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:349
bool m_UseRelPathIn3DModelFilename
Represent a set of closed polygons.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
void ComputeLayer3D_Zpos()
FOOTPRINTS & Footprints()
Definition: board.h:234
VRML_LAYER m_top_soldermask
VRML_COLOR & GetColor(VRML_COLOR_INDEX aIndex)
#define OPEN_OSTREAM(var, name)
bool GetLayer3D(LAYER_NUM layer, VRML_LAYER **vlayer)
float spec_red
Definition: exporter_vrml.h:50
#define _(s)
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
void create_vrml_shell(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double top_z, double bottom_z)
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
a few functions useful in geometry calculations.
void create_vrml_plane(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double aHeight, bool aTopPlane)
SGNODE * m_sgmaterial[VRML_COLOR_LAST]
bool SetShininess(float aShininess) noexcept
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
float spec_grn
Definition: exporter_vrml.h:51
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.
Definition: layer_ids.h:477
static EXPORTER_PCB_VRML * model_vrml
float emit_grn
Definition: exporter_vrml.h:55
void ExportVrmlSolderMask()
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.
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
bool SetScale(double aWorldScale)
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:464
SHAPE_POLY_SET m_pcbOutlines
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
float emit_blu
Definition: exporter_vrml.h:56
double DEG2RAD(double deg)
Definition: trigo.h:229
wxPoint GetPosition() const override
Definition: pad.h:178
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: pad.h:354
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
float diffuse_red
Definition: exporter_vrml.h:46
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
PAD_ATTRIB GetAttribute() const
Definition: pad.h:371
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
IFSG_FACESET is the wrapper for the SGFACESET class.
Definition: ifsg_faceset.h:40
Definition: layer_ids.h:71
FILENAME_RESOLVER * GetResolver() noexcept
Definition: 3d_cache.cpp:607
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:455
double DECIDEG2RAD(double deg)
Definition: trigo.h:233
static void from_quat(double q[4], double rot[4])
S3D_CACHE * m_Cache3Dmodels
#define ERR_APPROX_MAX_MM
Definition: export_vrml.cpp:58
#define IU_PER_MILS
Definition: plotter.cpp:130
bool SetTranslation(const SGPOINT &aTranslation) noexcept
wxPoint GetPosition() const override
Definition: footprint.h:187
bool SetScale(const SGPOINT &aScale) noexcept
void SetOffset(double aXoff, double aYoff)
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
VRML_LAYER m_bot_paste
VRML_LAYER m_holes
void Destroy(void)
Function Destroy deletes the object held by this wrapper.
Definition: ifsg_node.cpp:55
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
#define CLOSE_STREAM(var)
SCENEGRAPH * Load(const wxString &aModelFile)
Attempt to load the scene data for a model.
Definition: 3d_cache.cpp:284
BOARD * GetBoard() const
void ExportStandardLayers()
VRML_LAYER m_top_paste
Definition: pad.h:57
static constexpr int Millimeter2iu(double mm)
VRML_LAYER m_3D_board
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:143
float emit_red
Definition: exporter_vrml.h:54
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
bool AddCoord(double aXValue, double aYValue, double aZValue)
float diffuse_blu
Definition: exporter_vrml.h:48
TRACKS & Tracks()
Definition: board.h:231
#define ART_OFFSET
Definition: exporter_vrml.h:27
bool m_UseInlineModelsInBrdfile
void ExportVrmlPolygonSet(VRML_LAYER *aVlayer, const SHAPE_POLY_SET &aOutlines)
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.
double m_layer_z[PCB_LAYER_ID_COUNT]