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