KiCad PCB EDA Suite
Loading...
Searching...
No Matches
exporter_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-2022 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <exception>
24#include <fstream>
25#include <iomanip>
26#include <vector>
27#include <wx/dir.h>
28#include <wx/msgdlg.h>
29#include <wx/wfstream.h>
30#include <wx/zstream.h>
31
32#include "3d_cache/3d_cache.h"
33#include "3d_cache/3d_info.h"
34#include "board.h"
36#include <fp_lib_table.h>
37#include "footprint.h"
38#include "pad.h"
39#include "pcb_text.h"
40#include "pcb_track.h"
41#include <project_pcb.h>
42#include <core/arraydim.h>
43#include <filename_resolver.h>
45#include "streamwrapper.h"
46#include "vrml_layer.h"
47#include "pcb_edit_frame.h"
48
51#include <macros.h>
52
53#include <exporter_vrml.h>
54
56{
57 pcb_exporter = new EXPORTER_PCB_VRML( aBoard );
58}
59
60
61bool EXPORTER_VRML::ExportVRML_File( PROJECT* aProject, wxString *aMessages,
62 const wxString& aFullFileName, double aMMtoWRMLunit,
63 bool aIncludeUnspecified, bool aIncludeDNP,
64 bool aExport3DFiles, bool aUseRelativePaths,
65 const wxString& a3D_Subdir,
66 double aXRef, double aYRef )
67{
68 return pcb_exporter->ExportVRML_File( aProject, aMessages,
69 aFullFileName, aMMtoWRMLunit,
70 aIncludeUnspecified, aIncludeDNP,
71 aExport3DFiles, aUseRelativePaths,
72 a3D_Subdir, aXRef, aYRef );
73}
74
75
77{
78 delete pcb_exporter;
79}
80
81
82// The max error (in mm) to approximate arcs to segments:
83#define ERR_APPROX_MAX_MM 0.005
84
85
91
97
98static bool g_ColorsLoaded = false;
99
100
102 m_OutputPCB( nullptr )
103{
104 m_board = aBoard;
105 m_ReuseDef = true;
106 m_precision = 6;
107 m_WorldScale = 1.0;
108 m_Cache3Dmodels = nullptr;
112
113 for( int ii = 0; ii < VRML_COLOR_LAST; ++ii )
114 m_sgmaterial[ii] = nullptr;
115
116 for( unsigned i = 0; i < arrayDim( m_layer_z ); ++i )
117 m_layer_z[i] = 0;
118
119 // this default only makes sense if the output is in mm
121
122 // TODO: figure out a way to share all these stackup color definitions...
124
131 COLOR4D boardBody( 0, 0, 0, 0 );
132
134
135 auto findColor =
136 []( const wxString& aColorName, const CUSTOM_COLORS_LIST& aColorSet )
137 {
138 if( aColorName.StartsWith( wxT( "#" ) ) )
139 {
140 return KIGFX::COLOR4D( aColorName );
141 }
142 else
143 {
144 for( const CUSTOM_COLOR_ITEM& color : aColorSet )
145 {
146 if( color.m_ColorName == aColorName )
147 return color.m_Color;
148 }
149 }
150
151 return KIGFX::COLOR4D();
152 };
153
154 for( const BOARD_STACKUP_ITEM* stackupItem : stackup.GetList() )
155 {
156 wxString colorName = stackupItem->GetColor();
157
158 switch( stackupItem->GetType() )
159 {
161 if( stackupItem->GetBrdLayerId() == F_SilkS )
162 topSilk = findColor( colorName, m_SilkscreenColors );
163 else
164 botSilk = findColor( colorName, m_SilkscreenColors );
165 break;
166
168 if( stackupItem->GetBrdLayerId() == F_Mask )
169 topMask = findColor( colorName, m_MaskColors );
170 else
171 botMask = findColor( colorName, m_MaskColors );
172
173 break;
174
176 {
177 KIGFX::COLOR4D layerColor = findColor( colorName, m_BoardColors );
178
179 if( boardBody == COLOR4D( 0, 0, 0, 0 ) )
180 boardBody = layerColor;
181 else
182 boardBody = boardBody.Mix( layerColor, 1.0 - layerColor.a );
183
184 boardBody.a += ( 1.0 - boardBody.a ) * layerColor.a / 2;
185 break;
186 }
187
188 default:
189 break;
190 }
191 }
192
193 if( boardBody == COLOR4D( 0, 0, 0, 0 ) )
194 boardBody = m_DefaultBoardBody;
195
196 const wxString& finishName = stackup.m_FinishType;
197
198 if( finishName.EndsWith( wxT( "OSP" ) ) )
199 {
200 finish = findColor( wxT( "Copper" ), m_FinishColors );
201 }
202 else if( finishName.EndsWith( wxT( "IG" ) )
203 || finishName.EndsWith( wxT( "gold" ) ) )
204 {
205 finish = findColor( wxT( "Gold" ), m_FinishColors );
206 }
207 else if( finishName.StartsWith( wxT( "HAL" ) )
208 || finishName.StartsWith( wxT( "HASL" ) )
209 || finishName.EndsWith( wxT( "tin" ) )
210 || finishName.EndsWith( wxT( "nickel" ) ) )
211 {
212 finish = findColor( wxT( "Tin" ), m_FinishColors );
213 }
214 else if( finishName.EndsWith( wxT( "silver" ) ) )
215 {
216 finish = findColor( wxT( "Silver" ), m_FinishColors );
217 }
218
219 auto toVRMLColor =
220 []( const COLOR4D& aColor, double aSpecular, double aAmbient, double aShiny )
221 {
222 COLOR4D diff = aColor;
223 COLOR4D spec = aColor.Brightened( aSpecular );
224
225 return VRML_COLOR( diff.r, diff.g, diff.b,
226 spec.r, spec.g, spec.b,
227 aAmbient, 1.0 - aColor.a, aShiny );
228 };
229
230 vrml_colors_list[VRML_COLOR_TOP_SILK] = toVRMLColor( topSilk, 0.1, 0.7, 0.02 );
231 vrml_colors_list[VRML_COLOR_BOT_SILK] = toVRMLColor( botSilk, 0.1, 0.7, 0.02 );
232 vrml_colors_list[VRML_COLOR_TOP_SOLDMASK] = toVRMLColor( topMask, 0.3, 0.8, 0.30 );
233 vrml_colors_list[VRML_COLOR_BOT_SOLDMASK] = toVRMLColor( botMask, 0.3, 0.8, 0.30 );
234 vrml_colors_list[VRML_COLOR_PASTE] = toVRMLColor( paste, 0.6, 0.7, 0.70 );
235 vrml_colors_list[VRML_COLOR_COPPER] = toVRMLColor( finish, 0.6, 0.7, 0.90 );
236 vrml_colors_list[VRML_COLOR_PCB] = toVRMLColor( boardBody, 0.1, 0.7, 0.01 );
237
238 SetOffset( 0.0, 0.0 );
239}
240
241
243{
244 // destroy any unassociated material appearances
245 for( int j = 0; j < VRML_COLOR_LAST; ++j )
246 {
247 if( m_sgmaterial[j] && nullptr == S3D::GetSGNodeParent( m_sgmaterial[j] ) )
249
250 m_sgmaterial[j] = nullptr;
251 }
252
253 if( !m_components.empty() )
254 {
255 IFSG_TRANSFORM tmp( false );
256
257 for( auto i : m_components )
258 {
259 tmp.Attach( i );
260 tmp.SetParent( nullptr );
261 }
262
263 m_components.clear();
265 }
266}
267
269{
270 // Initialize the list of colors used in VRML export, but only once.
271 // (The list is static)
272 if( g_ColorsLoaded )
273 return;
274
275#define ADD_COLOR( list, r, g, b, a, name ) \
276 list.emplace_back( r/255.0, g/255.0, b/255.0, a, name )
277
278 ADD_COLOR( m_SilkscreenColors, 245, 245, 245, 1.0, _HKI( "Not specified" ) ); // White
279 ADD_COLOR( m_SilkscreenColors, 20, 51, 36, 1.0, wxT( "Green" ) );
280 ADD_COLOR( m_SilkscreenColors, 181, 19, 21, 1.0, wxT( "Red" ) );
281 ADD_COLOR( m_SilkscreenColors, 2, 59, 162, 1.0, wxT( "Blue" ) );
282 ADD_COLOR( m_SilkscreenColors, 11, 11, 11, 1.0, wxT( "Black" ) );
283 ADD_COLOR( m_SilkscreenColors, 245, 245, 245, 1.0, wxT( "White" ) );
284 ADD_COLOR( m_SilkscreenColors, 32, 2, 53, 1.0, wxT( "Purple" ) );
285 ADD_COLOR( m_SilkscreenColors, 194, 195, 0, 1.0, wxT( "Yellow" ) );
286
287 ADD_COLOR( m_MaskColors, 20, 51, 36, 0.83, _HKI( "Not specified" ) ); // Green
288 ADD_COLOR( m_MaskColors, 20, 51, 36, 0.83, wxT( "Green" ) );
289 ADD_COLOR( m_MaskColors, 91, 168, 12, 0.83, wxT( "Light Green" ) );
290 ADD_COLOR( m_MaskColors, 13, 104, 11, 0.83, wxT( "Saturated Green" ) );
291 ADD_COLOR( m_MaskColors, 181, 19, 21, 0.83, wxT( "Red" ) );
292 ADD_COLOR( m_MaskColors, 210, 40, 14, 0.83, wxT( "Light Red" ) );
293 ADD_COLOR( m_MaskColors, 239, 53, 41, 0.83, wxT( "Red/Orange" ) );
294 ADD_COLOR( m_MaskColors, 2, 59, 162, 0.83, wxT( "Blue" ) );
295 ADD_COLOR( m_MaskColors, 54, 79, 116, 0.83, wxT( "Light Blue 1" ) );
296 ADD_COLOR( m_MaskColors, 61, 85, 130, 0.83, wxT( "Light Blue 2" ) );
297 ADD_COLOR( m_MaskColors, 21, 70, 80, 0.83, wxT( "Green/Blue" ) );
298 ADD_COLOR( m_MaskColors, 11, 11, 11, 0.83, wxT( "Black" ) );
299 ADD_COLOR( m_MaskColors, 245, 245, 245, 0.83, wxT( "White" ) );
300 ADD_COLOR( m_MaskColors, 32, 2, 53, 0.83, wxT( "Purple" ) );
301 ADD_COLOR( m_MaskColors, 119, 31, 91, 0.83, wxT( "Light Purple" ) );
302 ADD_COLOR( m_MaskColors, 194, 195, 0, 0.83, wxT( "Yellow" ) );
303
304 ADD_COLOR( m_PasteColors, 128, 128, 128, 1.0, wxT( "Grey" ) );
305 ADD_COLOR( m_PasteColors, 90, 90, 90, 1.0, wxT( "Dark Grey" ) );
306 ADD_COLOR( m_PasteColors, 213, 213, 213, 1.0, wxT( "Silver" ) );
307
308 ADD_COLOR( m_FinishColors, 184, 115, 50, 1.0, wxT( "Copper" ) );
309 ADD_COLOR( m_FinishColors, 178, 156, 0, 1.0, wxT( "Gold" ) );
310 ADD_COLOR( m_FinishColors, 213, 213, 213, 1.0, wxT( "Silver" ) );
311 ADD_COLOR( m_FinishColors, 160, 160, 160, 1.0, wxT( "Tin" ) );
312
313 ADD_COLOR( m_BoardColors, 51, 43, 22, 0.83, wxT( "FR4 natural, dark" ) );
314 ADD_COLOR( m_BoardColors, 109, 116, 75, 0.83, wxT( "FR4 natural" ) );
315 ADD_COLOR( m_BoardColors, 252, 252, 250, 0.90, wxT( "PTFE natural" ) );
316 ADD_COLOR( m_BoardColors, 205, 130, 0, 0.68, wxT( "Polyimide" ) );
317 ADD_COLOR( m_BoardColors, 92, 17, 6, 0.90, wxT( "Phenolic natural" ) );
318 ADD_COLOR( m_BoardColors, 146, 99, 47, 0.83, wxT( "Brown 1" ) );
319 ADD_COLOR( m_BoardColors, 160, 123, 54, 0.83, wxT( "Brown 2" ) );
320 ADD_COLOR( m_BoardColors, 146, 99, 47, 0.83, wxT( "Brown 3" ) );
321 ADD_COLOR( m_BoardColors, 213, 213, 213, 1.0, wxT( "Aluminum" ) );
322
323 m_DefaultSilkscreen = COLOR4D( 0.94, 0.94, 0.94, 1.0 );
324 m_DefaultSolderMask = COLOR4D( 0.08, 0.20, 0.14, 0.83 );
325 m_DefaultSolderPaste = COLOR4D( 0.50, 0.50, 0.50, 1.0 );
326 m_DefaultSurfaceFinish = COLOR4D( 0.75, 0.61, 0.23, 1.0 );
327 m_DefaultBoardBody = COLOR4D( 0.43, 0.45, 0.30, 0.90 );
328#undef ADD_COLOR
329
330 g_ColorsLoaded = true;
331}
332
333
334bool EXPORTER_PCB_VRML::SetScale( double aWorldScale )
335{
336 // set the scaling of the VRML world
337 if( aWorldScale < 0.001 || aWorldScale > 10.0 )
338 throw( std::runtime_error( "WorldScale out of range (valid range is 0.001 to 10.0)" ) );
339
340 m_OutputPCB.SetScale( aWorldScale * 2.54 );
341 m_WorldScale = aWorldScale * 2.54;
342
343 return true;
344}
345
346
347void EXPORTER_PCB_VRML::SetOffset( double aXoff, double aYoff )
348{
349 m_tx = aXoff;
350 m_ty = -aYoff;
351
352 m_holes.SetVertexOffsets( aXoff, aYoff );
353 m_3D_board.SetVertexOffsets( aXoff, aYoff );
354 m_top_copper.SetVertexOffsets( aXoff, aYoff );
355 m_bot_copper.SetVertexOffsets( aXoff, aYoff );
356 m_top_silk.SetVertexOffsets( aXoff, aYoff );
357 m_bot_silk.SetVertexOffsets( aXoff, aYoff );
358 m_top_paste.SetVertexOffsets( aXoff, aYoff );
359 m_bot_paste.SetVertexOffsets( aXoff, aYoff );
360 m_top_soldermask.SetVertexOffsets( aXoff, aYoff );
361 m_bot_soldermask.SetVertexOffsets( aXoff, aYoff );
362 m_plated_holes.SetVertexOffsets( aXoff, aYoff );
363}
364
365
366bool EXPORTER_PCB_VRML::GetLayer3D( int layer, VRML_LAYER** vlayer )
367{
368 // select the VRML layer object to draw on; return true if
369 // a layer has been selected.
370 switch( layer )
371 {
372 case B_Cu: *vlayer = &m_bot_copper; return true;
373 case F_Cu: *vlayer = &m_top_copper; return true;
374 case B_SilkS: *vlayer = &m_bot_silk; return true;
375 case F_SilkS: *vlayer = &m_top_silk; return true;
376 case B_Mask: *vlayer = &m_bot_soldermask; return true;
377 case F_Mask: *vlayer = &m_top_soldermask; return true;
378 case B_Paste: *vlayer = &m_bot_paste; return true;
379 case F_Paste: *vlayer = &m_top_paste; return true;
380 default: return false;
381 }
382}
383
385{
386 SHAPE_POLY_SET holes, outlines = m_pcbOutlines;
387
388 // holes is the solder mask opening.
389 // the actual shape is the negative shape of mask opening.
390 PCB_LAYER_ID pcb_layer = F_Mask;
391 VRML_LAYER* vrmllayer = &m_top_soldermask;
392
393 for( int lcnt = 0; lcnt < 2; lcnt++ )
394 {
395 holes.RemoveAllContours();
396 outlines.RemoveAllContours();
397 outlines = m_pcbOutlines;
398 m_board->ConvertBrdLayerToPolygonalContours( pcb_layer, holes );
399
400 outlines.BooleanSubtract( holes, SHAPE_POLY_SET::PM_FAST );
402 ExportVrmlPolygonSet( vrmllayer, outlines );
403
404 pcb_layer = B_Mask;
405 vrmllayer = &m_bot_soldermask;
406 }
407}
408
409
411{
412 SHAPE_POLY_SET outlines;
413
414 PCB_LAYER_ID pcb_layer[] =
415 {
417 };
418
419 VRML_LAYER* vrmllayer[] =
420 {
422 nullptr // Sentinel
423 };
424
425 for( int lcnt = 0; ; lcnt++ )
426 {
427 if( vrmllayer[lcnt] == nullptr )
428 break;
429
430 outlines.RemoveAllContours();
431 m_board->ConvertBrdLayerToPolygonalContours( pcb_layer[lcnt], outlines );
434
435 ExportVrmlPolygonSet( vrmllayer[lcnt], outlines );
436 }
437}
438
439
440void EXPORTER_PCB_VRML::write_triangle_bag( std::ostream& aOut_file, const VRML_COLOR& aColor,
441 VRML_LAYER* aLayer, bool aPlane, bool aTop,
442 double aTop_z, double aBottom_z )
443{
444 // A lot of nodes are not required, but blender sometimes chokes without them.
445 static const char* shape_boiler[] =
446 {
447 "Transform {\n",
448 " children [\n",
449 " Group {\n",
450 " children [\n",
451 " Shape {\n",
452 " appearance Appearance {\n",
453 " material Material {\n",
454 0, // Material marker
455 " }\n",
456 " }\n",
457 " geometry IndexedFaceSet {\n",
458 " solid TRUE\n",
459 " coord Coordinate {\n",
460 " point [\n",
461 0, // Coordinates marker
462 " ]\n",
463 " }\n",
464 " coordIndex [\n",
465 0, // Index marker
466 " ]\n",
467 " }\n",
468 " }\n",
469 " ]\n",
470 " }\n",
471 " ]\n",
472 "}\n",
473 0 // End marker
474 };
475
476 int marker_found = 0, lineno = 0;
477
478 while( marker_found < 4 )
479 {
480 if( shape_boiler[lineno] )
481 {
482 aOut_file << shape_boiler[lineno];
483 }
484 else
485 {
486 marker_found++;
487
488 switch( marker_found )
489 {
490 case 1: // Material marker
491 {
492 std::streamsize lastPrecision = aOut_file.precision();
493 aOut_file << " diffuseColor " << std::setprecision(3);
494 aOut_file << aColor.diffuse_red << " ";
495 aOut_file << aColor.diffuse_grn << " ";
496 aOut_file << aColor.diffuse_blu << "\n";
497
498 aOut_file << " specularColor ";
499 aOut_file << aColor.spec_red << " ";
500 aOut_file << aColor.spec_grn << " ";
501 aOut_file << aColor.spec_blu << "\n";
502
503 aOut_file << " emissiveColor ";
504 aOut_file << aColor.emit_red << " ";
505 aOut_file << aColor.emit_grn << " ";
506 aOut_file << aColor.emit_blu << "\n";
507
508 aOut_file << " ambientIntensity " << aColor.ambient << "\n";
509 aOut_file << " transparency " << aColor.transp << "\n";
510 aOut_file << " shininess " << aColor.shiny << "\n";
511 aOut_file.precision( lastPrecision );
512 }
513 break;
514
515 case 2:
516
517 if( aPlane )
518 aLayer->WriteVertices( aTop_z, aOut_file, m_precision );
519 else
520 aLayer->Write3DVertices( aTop_z, aBottom_z, aOut_file, m_precision );
521
522 aOut_file << "\n";
523 break;
524
525 case 3:
526
527 if( aPlane )
528 aLayer->WriteIndices( aTop, aOut_file );
529 else
530 aLayer->Write3DIndices( aOut_file );
531
532 aOut_file << "\n";
533 break;
534
535 default:
536 break;
537 }
538 }
539
540 lineno++;
541 }
542}
543
544
545void EXPORTER_PCB_VRML::writeLayers( const char* aFileName, OSTREAM* aOutputFile )
546{
547 // VRML_LAYER board;
548 m_3D_board.Tesselate( &m_holes );
549 double brdz = m_brd_thickness / 2.0
551
553 {
555 &m_3D_board, false, false, brdz, -brdz );
556 }
557 else
558 {
560 }
561
562 // VRML_LAYER m_top_copper;
563 m_top_copper.Tesselate( &m_holes );
564
566 {
568 &m_top_copper, true, true, GetLayerZ( F_Cu ), 0 );
569 }
570 else
571 {
573 GetLayerZ( F_Cu ), true );
574 }
575
576 // VRML_LAYER m_top_paste;
577 m_top_paste.Tesselate( &m_holes );
578
580 {
582 &m_top_paste, true, true,
585 0 );
586 }
587 else
588 {
592 true );
593 }
594
595 // VRML_LAYER m_top_soldermask;
596 m_top_soldermask.Tesselate( &m_holes );
597
599 {
601 &m_top_soldermask, true, true,
604 0 );
605 }
606 else
607 {
611 true );
612 }
613
614 // VRML_LAYER m_bot_copper;
615 m_bot_copper.Tesselate( &m_holes );
616
618 {
620 &m_bot_copper, true, false, GetLayerZ( B_Cu ), 0 );
621 }
622 else
623 {
625 GetLayerZ( B_Cu ), false );
626 }
627
628 // VRML_LAYER m_bot_paste;
629 m_bot_paste.Tesselate( &m_holes );
630
632 {
634 &m_bot_paste, true, false,
635 GetLayerZ( B_Cu )
637 0 );
638 }
639 else
640 {
644 false );
645 }
646
647 // VRML_LAYER m_bot_mask:
648 m_bot_soldermask.Tesselate( &m_holes );
649
651 {
653 &m_bot_soldermask, true, false,
656 0 );
657 }
658 else
659 {
663 false );
664 }
665
666 // VRML_LAYER PTH;
667 m_plated_holes.Tesselate( nullptr, true );
668
670 {
672 &m_plated_holes, false, false,
677 }
678 else
679 {
685 }
686
687 // VRML_LAYER m_top_silk;
688 m_top_silk.Tesselate( &m_holes );
689
691 {
693 true, true, GetLayerZ( F_SilkS ), 0 );
694 }
695 else
696 {
698 GetLayerZ( F_SilkS ), true );
699 }
700
701 // VRML_LAYER m_bot_silk;
702 m_bot_silk.Tesselate( &m_holes );
703
705 {
707 true, false, GetLayerZ( B_SilkS ), 0 );
708 }
709 else
710 {
712 GetLayerZ( B_SilkS ), false );
713 }
714
716 S3D::WriteVRML( aFileName, true, m_OutputPCB.GetRawPtr(), true, true );
717}
718
719
721{
722 int copper_layers = m_board->GetCopperLayerCount();
723
724 // We call it 'layer' thickness, but it's the whole board thickness!
726 double half_thickness = m_brd_thickness / 2;
727
728 // Compute each layer's Z value, more or less like the 3d view
729 for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq )
730 {
731 int i = static_cast<int>( *seq );
732
733 if( i < copper_layers )
734 SetLayerZ( i, half_thickness - m_brd_thickness * i / (copper_layers - 1) );
735 else
736 SetLayerZ( i, - half_thickness ); // bottom layer
737 }
738
739 // To avoid rounding interference, we apply an epsilon to each successive layer
740 double epsilon_z = pcbIUScale.mmToIU( ART_OFFSET ) * m_BoardToVrmlScale;
741 SetLayerZ( B_Paste, -half_thickness - epsilon_z );
742 SetLayerZ( B_Adhes, -half_thickness - epsilon_z );
743 SetLayerZ( B_SilkS, -half_thickness - epsilon_z * 3 );
744 SetLayerZ( B_Mask, -half_thickness - epsilon_z * 2 );
745 SetLayerZ( F_Mask, half_thickness + epsilon_z * 2 );
746 SetLayerZ( F_SilkS, half_thickness + epsilon_z * 3 );
747 SetLayerZ( F_Adhes, half_thickness + epsilon_z );
748 SetLayerZ( F_Paste, half_thickness + epsilon_z );
749 SetLayerZ( Dwgs_User, half_thickness + epsilon_z * 5 );
750 SetLayerZ( Cmts_User, half_thickness + epsilon_z * 6 );
751 SetLayerZ( Eco1_User, half_thickness + epsilon_z * 7 );
752 SetLayerZ( Eco2_User, half_thickness + epsilon_z * 8 );
753 SetLayerZ( Edge_Cuts, 0 );
754}
755
756
757void EXPORTER_PCB_VRML::ExportVrmlPolygonSet( VRML_LAYER* aVlayer, const SHAPE_POLY_SET& aOutlines )
758{
759 // Polygons in SHAPE_POLY_SET must be without hole, i.e. holes must be linked
760 // previously to their main outline.
761 for( int icnt = 0; icnt < aOutlines.OutlineCount(); icnt++ )
762 {
763 const SHAPE_LINE_CHAIN& outline = aOutlines.COutline( icnt );
764
765 int seg = aVlayer->NewContour();
766
767 for( int jj = 0; jj < outline.PointCount(); jj++ )
768 {
769 if( !aVlayer->AddVertex( seg, outline.CPoint( jj ).x * m_BoardToVrmlScale,
770 -outline.CPoint( jj ).y * m_BoardToVrmlScale ) )
771 throw( std::runtime_error( aVlayer->GetError() ) );
772 }
773
774 aVlayer->EnsureWinding( seg, false );
775 }
776}
777
778
780{
782 {
783 wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
784 }
785
786 int seg;
787
788 for( int cnt = 0; cnt < m_pcbOutlines.OutlineCount(); cnt++ )
789 {
790 const SHAPE_LINE_CHAIN& outline = m_pcbOutlines.COutline( cnt );
791
792 seg = m_3D_board.NewContour();
793
794 for( int j = 0; j < outline.PointCount(); j++ )
795 {
796 m_3D_board.AddVertex( seg, (double)outline.CPoint(j).x * m_BoardToVrmlScale,
797 -((double)outline.CPoint(j).y * m_BoardToVrmlScale ) );
798
799 }
800
801 m_3D_board.EnsureWinding( seg, false );
802
803 // Generate board holes from outlines:
804 for( int ii = 0; ii < m_pcbOutlines.HoleCount( cnt ); ii++ )
805 {
806 const SHAPE_LINE_CHAIN& hole = m_pcbOutlines.Hole( cnt, ii );
807
808 seg = m_holes.NewContour();
809
810 if( seg < 0 )
811 {
812 wxLogError( _( "VRML Export Failed: Could not add holes to contours." ) );
813 return;
814 }
815
816 for( int j = 0; j < hole.PointCount(); j++ )
817 {
818 m_holes.AddVertex( seg, (double) hole.CPoint(j).x * m_BoardToVrmlScale,
819 -( (double) hole.CPoint(j).y * m_BoardToVrmlScale ) );
820 }
821
822 m_holes.EnsureWinding( seg, true );
823 }
824 }
825}
826
827
828
830{
831 PCB_LAYER_ID top_layer, bottom_layer;
832
833 for( PCB_TRACK* track : m_board->Tracks() )
834 {
835 if( track->Type() != PCB_VIA_T )
836 continue;
837
838 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
839
840 via->LayerPair( &top_layer, &bottom_layer );
841
842 // do not render a buried via
843 if( top_layer != F_Cu && bottom_layer != B_Cu )
844 continue;
845
846 // Export all via holes to m_holes
847 double hole_radius = via->GetDrillValue() * m_BoardToVrmlScale / 2.0;
848
849 if( hole_radius <= 0 )
850 continue;
851
852 double x = via->GetStart().x * m_BoardToVrmlScale;
853 double y = via->GetStart().y * m_BoardToVrmlScale;
854
855 // Set the optimal number of segments to approximate a circle.
856 // SetArcParams needs a count max, and the minimal and maximal length
857 // of segments
858 double max_error = ERR_APPROX_MAX_MM;
859
861 max_error /= 2.54; // The board is exported with a size reduced by 2.54
862
863 int nsides = GetArcToSegmentCount( via->GetDrillValue(), pcbIUScale.mmToIU( max_error ),
864 FULL_CIRCLE );
865
866 double minSegLength = M_PI * 2.0 * hole_radius / nsides;
867 double maxSegLength = minSegLength*2.0;
868
869 m_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
870 m_plated_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
871
872 m_holes.AddCircle( x, -y, hole_radius, true, true );
873 m_plated_holes.AddCircle( x, -y, hole_radius, true, false );
874
875 m_holes.ResetArcParams();
876 m_plated_holes.ResetArcParams();
877 }
878}
879
880
882{
883 double hole_drill_w = (double) aPad->GetDrillSize().x * m_BoardToVrmlScale / 2.0;
884 double hole_drill_h = (double) aPad->GetDrillSize().y * m_BoardToVrmlScale / 2.0;
885 double hole_drill = std::min( hole_drill_w, hole_drill_h );
886 double hole_x = aPad->GetPosition().x * m_BoardToVrmlScale;
887 double hole_y = aPad->GetPosition().y * m_BoardToVrmlScale;
888
889 // Export the hole on the edge layer
890 if( hole_drill > 0 )
891 {
892 double max_error = ERR_APPROX_MAX_MM;
893
895 max_error /= 2.54; // The board is exported with a size reduced by 2.54
896
897 int nsides = GetArcToSegmentCount( hole_drill, pcbIUScale.mmToIU( max_error ),
898 FULL_CIRCLE );
899 double minSegLength = M_PI * hole_drill / nsides;
900 double maxSegLength = minSegLength*2.0;
901
902 m_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
903 m_plated_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
904
905 bool pth = false;
906
907 if( ( aPad->GetAttribute() != PAD_ATTRIB::NPTH ) )
908 pth = true;
909
911 {
912 // Oblong hole (slot)
913
914 if( pth )
915 {
916 m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0 + PLATE_OFFSET,
917 hole_drill_h * 2.0 + PLATE_OFFSET,
918 aPad->GetOrientation().AsDegrees(), true, true );
919
920 m_plated_holes.AddSlot( hole_x, -hole_y,
921 hole_drill_w * 2.0, hole_drill_h * 2.0,
922 aPad->GetOrientation().AsDegrees(), true, false );
923 }
924 else
925 {
926 m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
927 aPad->GetOrientation().AsDegrees(), true, false );
928
929 }
930 }
931 else
932 {
933 // Drill a round hole
934 if( pth )
935 {
936 m_holes.AddCircle( hole_x, -hole_y, hole_drill + PLATE_OFFSET, true, true );
937 m_plated_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
938 }
939 else
940 {
941 m_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
942 }
943
944 }
945
946 m_holes.ResetArcParams();
947 m_plated_holes.ResetArcParams();
948 }
949}
950
951
952// From axis/rot to quaternion
953static void build_quat( double x, double y, double z, double a, double q[4] )
954{
955 double sina = sin( a / 2 );
956
957 q[0] = x * sina;
958 q[1] = y * sina;
959 q[2] = z * sina;
960 q[3] = cos( a / 2 );
961}
962
963
964// From quaternion to axis/rot
965static void from_quat( double q[4], double rot[4] )
966{
967 rot[3] = acos( q[3] ) * 2;
968
969 for( int i = 0; i < 3; i++ )
970 rot[i] = q[i] / sin( rot[3] / 2 );
971}
972
973
974// Quaternion composition
975static void compose_quat( double q1[4], double q2[4], double qr[4] )
976{
977 double tmp[4];
978
979 tmp[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1];
980 tmp[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2];
981 tmp[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0];
982 tmp[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2];
983
984 qr[0] = tmp[0];
985 qr[1] = tmp[1];
986 qr[2] = tmp[2];
987 qr[3] = tmp[3];
988}
989
990
991void EXPORTER_PCB_VRML::ExportVrmlFootprint( FOOTPRINT* aFootprint, std::ostream* aOutputFile )
992{
993 // Note: if m_UseInlineModelsInBrdfile is false, the 3D footprint shape is copied to
994 // the vrml board file, and aOutputFile is not used (can be nullptr)
995 // if m_UseInlineModelsInBrdfile is true, the 3D footprint shape is copied to
996 // aOutputFile (with the suitable rotation/translation/scale transform, and the vrml board
997 // file contains only the filename of 3D shapes to add to the full vrml scene
998 wxCHECK( aFootprint, /* void */ );
999
1000 wxString libraryName = aFootprint->GetFPID().GetLibNickname();
1001 wxString footprintBasePath = wxEmptyString;
1002
1003 if( m_board->GetProject() )
1004 {
1005 const FP_LIB_TABLE_ROW* fpRow = nullptr;
1006
1007 try
1008 {
1009 fpRow = PROJECT_PCB::PcbFootprintLibs( m_board->GetProject() )->FindRow( libraryName, false );
1010 }
1011 catch( ... )
1012 {
1013 // Not found: do nothing
1014 }
1015
1016 if( fpRow )
1017 footprintBasePath = fpRow->GetFullURI( true );
1018 }
1019
1020
1021 // Export pad holes
1022 for( PAD* pad : aFootprint->Pads() )
1024
1026 && ( !( aFootprint->GetAttributes() & ( FP_THROUGH_HOLE | FP_SMD ) ) ) )
1027 {
1028 return;
1029 }
1030
1031 if( !m_includeDNP && aFootprint->IsDNP() )
1032 return;
1033
1034 bool isFlipped = aFootprint->GetLayer() == B_Cu;
1035
1036 // Export the object VRML model(s)
1037 auto sM = aFootprint->Models().begin();
1038 auto eM = aFootprint->Models().end();
1039
1040
1041 while( sM != eM )
1042 {
1043 if( !sM->m_Show )
1044 {
1045 ++sM;
1046 continue;
1047 }
1048
1049 SGNODE* mod3d = (SGNODE*) m_Cache3Dmodels->Load( sM->m_Filename, footprintBasePath );
1050
1051 if( nullptr == mod3d )
1052 {
1053 ++sM;
1054 continue;
1055 }
1056
1057 /* Calculate 3D shape rotation:
1058 * this is the rotation parameters, with an additional 180 deg rotation
1059 * for footprints that are flipped
1060 * When flipped, axis rotation is the horizontal axis (X axis)
1061 */
1062 double rotx = -sM->m_Rotation.x;
1063 double roty = -sM->m_Rotation.y;
1064 double rotz = -sM->m_Rotation.z;
1065
1066 if( isFlipped )
1067 {
1068 rotx += 180.0;
1069 roty = -roty;
1070 rotz = -rotz;
1071 }
1072
1073 // Do some quaternion munching
1074 double q1[4], q2[4], rot[4];
1075 build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 );
1076 build_quat( 0, 1, 0, DEG2RAD( roty ), q2 );
1077 compose_quat( q1, q2, q1 );
1078 build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 );
1079 compose_quat( q1, q2, q1 );
1080
1081 // Note here aFootprint->GetOrientation() is in 0.1 degrees, so footprint rotation
1082 // has to be converted to radians
1083 build_quat( 0, 0, 1, aFootprint->GetOrientation().AsRadians(), q2 );
1084 compose_quat( q1, q2, q1 );
1085 from_quat( q1, rot );
1086
1087 double offsetFactor = 1000.0f * pcbIUScale.IU_PER_MILS / 25.4f;
1088
1089 // adjust 3D shape local offset position
1090 // they are given in mm, so they are converted in board IU.
1091 double offsetx = sM->m_Offset.x * offsetFactor;
1092 double offsety = sM->m_Offset.y * offsetFactor;
1093 double offsetz = sM->m_Offset.z * offsetFactor;
1094
1095 if( isFlipped )
1096 offsetz = -offsetz;
1097 else
1098 offsety = -offsety; // In normal mode, Y axis is reversed in Pcbnew.
1099
1100 RotatePoint( &offsetx, &offsety, aFootprint->GetOrientation() );
1101
1102 SGPOINT trans;
1103 trans.x = ( offsetx + aFootprint->GetPosition().x ) * m_BoardToVrmlScale + m_tx;
1104 trans.y = -( offsety + aFootprint->GetPosition().y) * m_BoardToVrmlScale - m_ty;
1105 trans.z = (offsetz * m_BoardToVrmlScale ) + GetLayerZ( aFootprint->GetLayer() );
1106
1108 {
1109 wxCHECK( aOutputFile, /* void */ );
1110
1111 int old_precision = aOutputFile->precision();
1112 aOutputFile->precision( m_precision );
1113
1114 wxFileName srcFile =
1115 m_Cache3Dmodels->GetResolver()->ResolvePath( sM->m_Filename, wxEmptyString );
1116 wxFileName dstFile;
1117 dstFile.SetPath( m_Subdir3DFpModels );
1118 dstFile.SetName( srcFile.GetName() );
1119 dstFile.SetExt( wxT( "wrl" ) );
1120
1121 // copy the file if necessary
1122 wxDateTime srcModTime = srcFile.GetModificationTime();
1123 wxDateTime destModTime = srcModTime;
1124
1125 destModTime.SetToCurrent();
1126
1127 if( dstFile.FileExists() )
1128 destModTime = dstFile.GetModificationTime();
1129
1130 if( srcModTime != destModTime )
1131 {
1132 wxString fileExt = srcFile.GetExt();
1133 fileExt.LowerCase();
1134
1135 // copy VRML models and use the scenegraph library to
1136 // translate other model types
1137 if( fileExt == wxT( "wrl" ) )
1138 {
1139 if( !wxCopyFile( srcFile.GetFullPath(), dstFile.GetFullPath() ) )
1140 {
1141 ++sM;
1142 continue;
1143 }
1144 }
1145 else if( fileExt == wxT( "wrz" ) )
1146 {
1147 wxFileInputStream input_file_stream( srcFile.GetFullPath() );
1148 if( !input_file_stream.IsOk() || input_file_stream.GetSize() == wxInvalidSize )
1149 {
1150 ++sM;
1151 continue;
1152 }
1153
1154 wxZlibInputStream zlib_input_stream( input_file_stream, wxZLIB_GZIP );
1155 wxFFileOutputStream output_file_stream( dstFile.GetFullPath() );
1156 if( !zlib_input_stream.IsOk() || !output_file_stream.IsOk() )
1157 {
1158 output_file_stream.Close();
1159 ++sM;
1160 continue;
1161 }
1162
1163 output_file_stream.Write( zlib_input_stream );
1164 output_file_stream.Close();
1165 }
1166 else
1167 {
1168 if( !S3D::WriteVRML( dstFile.GetFullPath().ToUTF8(), true, mod3d, m_ReuseDef,
1169 true ) )
1170 {
1171 ++sM;
1172 continue;
1173 }
1174 }
1175 }
1176
1177 (*aOutputFile) << "Transform {\n";
1178
1179 // only write a rotation if it is >= 0.1 deg
1180 if( std::abs( rot[3] ) > 0.0001745 )
1181 {
1182 (*aOutputFile) << " rotation ";
1183 (*aOutputFile) << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
1184 }
1185
1186 (*aOutputFile) << " translation ";
1187 (*aOutputFile) << trans.x << " ";
1188 (*aOutputFile) << trans.y << " ";
1189 (*aOutputFile) << trans.z << "\n";
1190
1191 (*aOutputFile) << " scale ";
1192 (*aOutputFile) << sM->m_Scale.x << " ";
1193 (*aOutputFile) << sM->m_Scale.y << " ";
1194 (*aOutputFile) << sM->m_Scale.z << "\n";
1195
1196 (*aOutputFile) << " children [\n Inline {\n url \"";
1197
1199 {
1200 wxFileName tmp = dstFile;
1201 tmp.SetExt( wxT( "" ) );
1202 tmp.SetName( wxT( "" ) );
1203 tmp.RemoveLastDir();
1204 dstFile.MakeRelativeTo( tmp.GetPath() );
1205 }
1206
1207 wxString fn = dstFile.GetFullPath();
1208 fn.Replace( wxT( "\\" ), wxT( "/" ) );
1209 (*aOutputFile) << TO_UTF8( fn ) << "\"\n } ]\n";
1210 (*aOutputFile) << " }\n";
1211
1212 aOutputFile->precision( old_precision );
1213 }
1214 else
1215 {
1216 IFSG_TRANSFORM* modelShape = new IFSG_TRANSFORM( m_OutputPCB.GetRawPtr() );
1217
1218 // only write a rotation if it is >= 0.1 deg
1219 if( std::abs( rot[3] ) > 0.0001745 )
1220 modelShape->SetRotation( SGVECTOR( rot[0], rot[1], rot[2] ), rot[3] );
1221
1222 modelShape->SetTranslation( trans );
1223 modelShape->SetScale( SGPOINT( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ) );
1224
1225 if( nullptr == S3D::GetSGNodeParent( mod3d ) )
1226 {
1227 m_components.push_back( mod3d );
1228 modelShape->AddChildNode( mod3d );
1229 }
1230 else
1231 {
1232 modelShape->AddRefNode( mod3d );
1233 }
1234
1235 }
1236
1237 ++sM;
1238 }
1239}
1240
1241
1242
1243bool EXPORTER_PCB_VRML::ExportVRML_File( PROJECT* aProject, wxString *aMessages,
1244 const wxString& aFullFileName, double aMMtoWRMLunit,
1245 bool aIncludeUnspecified, bool aIncludeDNP,
1246 bool aExport3DFiles, bool aUseRelativePaths,
1247 const wxString& a3D_Subdir,
1248 double aXRef, double aYRef )
1249{
1250 if( aProject == nullptr )
1251 {
1252 if( aMessages )
1253 *aMessages = _( "No project when exporting the VRML file");
1254
1255 return false;
1256 }
1257
1258 SetScale( aMMtoWRMLunit );
1259 m_UseInlineModelsInBrdfile = aExport3DFiles;
1260
1261 wxFileName subdir( a3D_Subdir, wxT( "" ) );
1262 // convert the subdir path to a absolute full one with the output file as the cwd
1263 m_Subdir3DFpModels = subdir.GetAbsolutePath( wxFileName( aFullFileName ).GetPath() );
1264
1265 m_UseRelPathIn3DModelFilename = aUseRelativePaths;
1266 m_includeUnspecified = aIncludeUnspecified;
1267 m_includeDNP = aIncludeDNP;
1269
1270 // When 3D models are separate files, for historical reasons the VRML unit
1271 // is expected to be 0.1 inch (2.54mm) instead of 1mm, so we adjust the m_BoardToVrmlScale
1272 // to match the VRML scale of these external files.
1273 // Otherwise we use 1mm as VRML unit
1275 {
1277 SetOffset( -aXRef / 2.54, aYRef / 2.54 );
1278 }
1279 else
1280 {
1282 SetOffset( -aXRef, aYRef );
1283 }
1284
1285 bool success = true;
1286
1287 try
1288 {
1289 // Preliminary computation: the z value for each layer
1291
1292 // board edges and cutouts
1294
1295 // Draw solder mask layer (negative layer)
1299
1301 {
1302 // Copy fp 3D models in a folder, and link these files in
1303 // the board .vrml file
1304 ExportFp3DModelsAsLinkedFile( aFullFileName );
1305 }
1306 else
1307 {
1308 // merge footprints in the .vrml board file
1309 for( FOOTPRINT* footprint : m_board->Footprints() )
1310 ExportVrmlFootprint( footprint, nullptr );
1311
1312 // write out the board and all layers
1313 writeLayers( TO_UTF8( aFullFileName ), nullptr );
1314 }
1315 }
1316 catch( const std::exception& e )
1317 {
1318 if( aMessages )
1319 *aMessages << _( "VRML Export Failed:\n" ) << From_UTF8( e.what() );
1320
1321 success = false;
1322 }
1323
1324 return success;
1325}
1326
1327bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMtoWRMLunit,
1328 bool aIncludeUnspecified, bool aIncludeDNP,
1329 bool aExport3DFiles, bool aUseRelativePaths,
1330 const wxString& a3D_Subdir,
1331 double aXRef, double aYRef )
1332{
1333 bool success;
1334 wxString msgs;
1335 EXPORTER_VRML model3d( GetBoard() );
1336
1337 success = model3d.ExportVRML_File( &Prj(), &msgs, aFullFileName, aMMtoWRMLunit,
1338 aIncludeUnspecified, aIncludeDNP,
1339 aExport3DFiles, aUseRelativePaths,
1340 a3D_Subdir, aXRef, aYRef );
1341
1342 if( !msgs.IsEmpty() )
1343 wxMessageBox( msgs );
1344
1345 return success;
1346}
1347
1348
1349void EXPORTER_PCB_VRML::ExportFp3DModelsAsLinkedFile( const wxString& aFullFileName )
1350{
1351 // check if the 3D Subdir exists - create if not
1352 if( !wxDir::Exists( m_Subdir3DFpModels ) )
1353 {
1354 if( !wxDir::Make( m_Subdir3DFpModels ) )
1355 throw( std::runtime_error( "Could not create 3D model subdirectory" ) );
1356 }
1357
1358 OPEN_OSTREAM( output_file, TO_UTF8( aFullFileName ) );
1359
1360 if( output_file.fail() )
1361 {
1362 std::ostringstream ostr;
1363 ostr << "Could not open file '" << TO_UTF8( aFullFileName ) << "'";
1364 throw( std::runtime_error( ostr.str().c_str() ) );
1365 }
1366
1367 output_file.imbue( std::locale::classic() );
1368
1369 // Begin with the usual VRML boilerplate
1370 wxString fn = aFullFileName;
1371 fn.Replace( wxT( "\\" ) , wxT( "/" ) );
1372 output_file << "#VRML V2.0 utf8\n";
1373 output_file << "WorldInfo {\n";
1374 output_file << " title \"" << TO_UTF8( fn ) << " - Generated by Pcbnew\"\n";
1375 output_file << "}\n";
1376 output_file << "Transform {\n";
1377 output_file << " scale " << std::setprecision( m_precision );
1378 output_file << m_WorldScale << " ";
1379 output_file << m_WorldScale << " ";
1380 output_file << m_WorldScale << "\n";
1381 output_file << " children [\n";
1382
1383 // Export footprints
1384 for( FOOTPRINT* footprint : m_board->Footprints() )
1385 ExportVrmlFootprint( footprint, &output_file );
1386
1387 // write out the board and all layers
1388 writeLayers( TO_UTF8( aFullFileName ), &output_file );
1389
1390 // Close the outer 'transform' node
1391 output_file << "]\n}\n";
1392
1393 CLOSE_STREAM( output_file );
1394}
1395
1397{
1398 if( colorIdx == -1 )
1399 colorIdx = VRML_COLOR_PCB;
1400 else if( colorIdx == VRML_COLOR_LAST )
1401 return nullptr;
1402
1403 if( m_sgmaterial[colorIdx] )
1404 return m_sgmaterial[colorIdx];
1405
1406 IFSG_APPEARANCE vcolor( (SGNODE*) nullptr );
1407 VRML_COLOR* cp = &vrml_colors_list[colorIdx];
1408
1409 vcolor.SetSpecular( cp->spec_red, cp->spec_grn, cp->spec_blu );
1410 vcolor.SetDiffuse( cp->diffuse_red, cp->diffuse_grn, cp->diffuse_blu );
1411 vcolor.SetShininess( cp->shiny );
1412 // NOTE: XXX - replace with a better equation; using this definition
1413 // of ambient will not yield the best results
1414 vcolor.SetAmbient( cp->ambient, cp->ambient, cp->ambient );
1415 vcolor.SetTransparency( cp->transp );
1416
1417 m_sgmaterial[colorIdx] = vcolor.GetRawPtr();
1418
1419 return m_sgmaterial[colorIdx];
1420}
1421
1422
1424 VRML_LAYER* layer, double top_z, bool aTopPlane )
1425{
1426 std::vector< double > vertices;
1427 std::vector< int > idxPlane;
1428
1429 if( !( *layer ).Get2DTriangles( vertices, idxPlane, top_z, aTopPlane ) )
1430 {
1431 return;
1432 }
1433
1434 if( ( idxPlane.size() % 3 ) )
1435 {
1436 throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a triangle "
1437 "list)" ) );
1438 }
1439
1440 std::vector< SGPOINT > vlist;
1441 size_t nvert = vertices.size() / 3;
1442 size_t j = 0;
1443
1444 for( size_t i = 0; i < nvert; ++i, j+= 3 )
1445 vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
1446
1447 // create the intermediate scenegraph
1448 IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1449 IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1450 IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1451 IFSG_COORDS cp( face ); // coordinates for all faces
1452 cp.SetCoordsList( nvert, &vlist[0] );
1453 IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1454 coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1455 IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1456
1457 // set the normals
1458 if( aTopPlane )
1459 {
1460 for( size_t i = 0; i < nvert; ++i )
1461 norms.AddNormal( 0.0, 0.0, 1.0 );
1462 }
1463 else
1464 {
1465 for( size_t i = 0; i < nvert; ++i )
1466 norms.AddNormal( 0.0, 0.0, -1.0 );
1467 }
1468
1469 // assign a color from the palette
1470 SGNODE* modelColor = getSGColor( colorID );
1471
1472 if( nullptr != modelColor )
1473 {
1474 if( nullptr == S3D::GetSGNodeParent( modelColor ) )
1475 shape.AddChildNode( modelColor );
1476 else
1477 shape.AddRefNode( modelColor );
1478 }
1479}
1480
1481
1483 VRML_LAYER* layer, double top_z, double bottom_z )
1484{
1485 std::vector< double > vertices;
1486 std::vector< int > idxPlane;
1487 std::vector< int > idxSide;
1488
1489 if( top_z < bottom_z )
1490 {
1491 double tmp = top_z;
1492 top_z = bottom_z;
1493 bottom_z = tmp;
1494 }
1495
1496 if( !( *layer ).Get3DTriangles( vertices, idxPlane, idxSide, top_z, bottom_z )
1497 || idxPlane.empty() || idxSide.empty() )
1498 {
1499 return;
1500 }
1501
1502 if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
1503 {
1504 throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a "
1505 "triangle list)" ) );
1506 }
1507
1508 std::vector< SGPOINT > vlist;
1509 size_t nvert = vertices.size() / 3;
1510 size_t j = 0;
1511
1512 for( size_t i = 0; i < nvert; ++i, j+= 3 )
1513 vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
1514
1515 // create the intermediate scenegraph
1516 IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1517 IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1518 IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1519 IFSG_COORDS cp( face ); // coordinates for all faces
1520 cp.SetCoordsList( nvert, &vlist[0] );
1521 IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1522 coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1523 IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1524
1525 // number of TOP (and bottom) vertices
1526 j = nvert / 2;
1527
1528 // set the TOP normals
1529 for( size_t i = 0; i < j; ++i )
1530 norms.AddNormal( 0.0, 0.0, 1.0 );
1531
1532 // set the BOTTOM normals
1533 for( size_t i = 0; i < j; ++i )
1534 norms.AddNormal( 0.0, 0.0, -1.0 );
1535
1536 // assign a color from the palette
1537 SGNODE* modelColor = getSGColor( colorID );
1538
1539 if( nullptr != modelColor )
1540 {
1541 if( nullptr == S3D::GetSGNodeParent( modelColor ) )
1542 shape.AddChildNode( modelColor );
1543 else
1544 shape.AddRefNode( modelColor );
1545 }
1546
1547 // create a second shape describing the vertical walls of the extrusion
1548 // using per-vertex-per-face-normals
1549 shape.NewNode( tx0 );
1550 shape.AddRefNode( modelColor ); // set the color to be the same as the top/bottom
1551 face.NewNode( shape );
1552 cp.NewNode( face ); // new vertex list
1553 norms.NewNode( face ); // new normals list
1554 coordIdx.NewNode( face ); // new index list
1555
1556 // populate the new per-face vertex list and its indices and normals
1557 std::vector< int >::iterator sI = idxSide.begin();
1558 std::vector< int >::iterator eI = idxSide.end();
1559
1560 size_t sidx = 0; // index to the new coord set
1561 SGPOINT p1, p2, p3;
1562 SGVECTOR vnorm;
1563
1564 while( sI != eI )
1565 {
1566 p1 = vlist[*sI];
1567 cp.AddCoord( p1 );
1568 ++sI;
1569
1570 p2 = vlist[*sI];
1571 cp.AddCoord( p2 );
1572 ++sI;
1573
1574 p3 = vlist[*sI];
1575 cp.AddCoord( p3 );
1576 ++sI;
1577
1578 vnorm.SetVector( S3D::CalcTriNorm( p1, p2, p3 ) );
1579 norms.AddNormal( vnorm );
1580 norms.AddNormal( vnorm );
1581 norms.AddNormal( vnorm );
1582
1583 coordIdx.AddIndex( (int)sidx );
1584 ++sidx;
1585 coordIdx.AddIndex( (int)sidx );
1586 ++sidx;
1587 coordIdx.AddIndex( (int)sidx );
1588 ++sidx;
1589 }
1590}
defines the basic data associated with a single 3D model.
int color
Definition: DXF_plotter.cpp:58
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
#define ADD_COLOR(list, r, g, b, a, name)
static bool g_ColorsLoaded
@ BS_ITEM_TYPE_SILKSCREEN
Definition: board_stackup.h:50
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:45
@ BS_ITEM_TYPE_SOLDERMASK
Definition: board_stackup.h:48
int GetBoardThickness() const
The full thickness of the board including copper and masks.
BOARD_STACKUP & GetStackupDescriptor()
Manage one layer needed to make a physical board.
Definition: board_stackup.h:95
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
wxString m_FinishType
The name of external copper finish.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:281
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:2369
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:2742
int GetCopperLayerCount() const
Definition: board.cpp:656
const FOOTPRINTS & Footprints() const
Definition: board.h:322
const TRACKS & Tracks() const
Definition: board.h:320
PROJECT * GetProject() const
Definition: board.h:475
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:797
double AsDegrees() const
Definition: eda_angle.h:155
double AsRadians() const
Definition: eda_angle.h:159
VRML_COLOR & GetColor(VRML_COLOR_INDEX aIndex)
static CUSTOM_COLORS_LIST m_MaskColors
SGNODE * m_sgmaterial[VRML_COLOR_LAST]
static CUSTOM_COLORS_LIST m_PasteColors
static KIGFX::COLOR4D m_DefaultSolderPaste
VRML_LAYER m_top_copper
VRML_LAYER m_top_soldermask
bool GetLayer3D(int layer, VRML_LAYER **vlayer)
bool SetScale(double aWorldScale)
VRML_LAYER m_bot_paste
SGNODE * getSGColor(VRML_COLOR_INDEX colorIdx)
VRML_LAYER m_top_paste
EXPORTER_PCB_VRML(BOARD *aBoard)
VRML_LAYER m_bot_copper
SHAPE_POLY_SET m_pcbOutlines
VRML_LAYER m_plated_holes
void SetLayerZ(int aLayer, double aValue)
void ExportVrmlPolygonSet(VRML_LAYER *aVlayer, const SHAPE_POLY_SET &aOutlines)
void create_vrml_shell(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double top_z, double bottom_z)
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)
std::list< SGNODE * > m_components
static KIGFX::COLOR4D m_DefaultSilkscreen
static CUSTOM_COLORS_LIST m_SilkscreenColors
void SetOffset(double aXoff, double aYoff)
VRML_LAYER m_holes
double GetLayerZ(int aLayer)
bool ExportVRML_File(PROJECT *aProject, wxString *aMessages, const wxString &aFullFileName, double aMMtoWRMLunit, bool aIncludeUnspecified, bool aIncludeDNP, bool aExport3DFiles, bool aUseRelativePaths, const wxString &a3D_Subdir, double aXRef, double aYRef)
Export a VRML file image of the board.
void ExportVrmlPadHole(PAD *aPad)
static CUSTOM_COLORS_LIST m_BoardColors
void create_vrml_plane(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double aHeight, bool aTopPlane)
bool m_UseInlineModelsInBrdfile
wxString m_Subdir3DFpModels
static KIGFX::COLOR4D m_DefaultBoardBody
static CUSTOM_COLORS_LIST m_FinishColors
static KIGFX::COLOR4D m_DefaultSolderMask
double m_layer_z[PCB_LAYER_ID_COUNT]
bool m_UseRelPathIn3DModelFilename
void ExportVrmlFootprint(FOOTPRINT *aFootprint, std::ostream *aOutputFile)
void ExportFp3DModelsAsLinkedFile(const wxString &aFullFileName)
VRML_LAYER m_bot_soldermask
VRML_COLOR vrml_colors_list[VRML_COLOR_LAST]
VRML_LAYER m_3D_board
void writeLayers(const char *aFileName, OSTREAM *aOutputFile)
static KIGFX::COLOR4D m_DefaultSurfaceFinish
VRML_LAYER m_bot_silk
IFSG_TRANSFORM m_OutputPCB
VRML_LAYER m_top_silk
S3D_CACHE * m_Cache3Dmodels
Wrapper to expose an API for writing VRML files, without exposing all the many structures used in the...
Definition: export_vrml.h:33
EXPORTER_VRML(BOARD *aBoard)
EXPORTER_PCB_VRML * pcb_exporter
Definition: export_vrml.h:60
bool ExportVRML_File(PROJECT *aProject, wxString *aMessages, const wxString &aFullFileName, double aMMtoWRMLunit, bool aIncludeUnspecified, bool aIncludeDNP, bool aExport3DFiles, bool aUseRelativePaths, const wxString &a3D_Subdir, double aXRef, double aYRef)
Exports the board and its footprint shapes 3D (vrml files only) as a vrml file.
wxString ResolvePath(const wxString &aFileName, const wxString &aWorkingPath)
Determines the full path of the given file name.
bool IsDNP() const
Definition: footprint.h:754
EDA_ANGLE GetOrientation() const
Definition: footprint.h:212
int GetAttributes() const
Definition: footprint.h:276
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:221
PADS & Pads()
Definition: footprint.h:191
const LIB_ID & GetFPID() const
Definition: footprint.h:233
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:205
VECTOR2I GetPosition() const override
Definition: footprint.h:209
Hold a record identifying a library accessed by the appropriate footprint library #PLUGIN object in t...
Definition: fp_lib_table.h:42
const FP_LIB_TABLE_ROW * FindRow(const wxString &aNickName, bool aCheckIfEnabled=false)
Return an FP_LIB_TABLE_ROW if aNickName is found in this table or in any chained fall back table frag...
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetShininess(float aShininess) noexcept
bool SetAmbient(float aRVal, float aGVal, float aBVal)
bool SetTransparency(float aTransparency) noexcept
IFSG_COORDINDEX is the wrapper for SGCOORDINDEX.
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
IFSG_COORDS is the wrapper for SGCOORDS.
Definition: ifsg_coords.h:41
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
bool AddCoord(double aXValue, double aYValue, double aZValue)
IFSG_FACESET is the wrapper for the SGFACESET class.
Definition: ifsg_faceset.h:41
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
bool AddIndex(int aIndex)
Function AddIndex adds a single index to the list.
Definition: ifsg_index.cpp:57
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 SetParent(SGNODE *aParent)
Function SetParent sets the parent SGNODE of this object.
Definition: ifsg_node.cpp:87
SGNODE * GetRawPtr(void) noexcept
Function GetRawPtr() returns the raw internal SGNODE pointer.
Definition: ifsg_node.cpp:65
void Destroy(void)
Function Destroy deletes the object held by this wrapper.
Definition: ifsg_node.cpp:55
bool AddChildNode(SGNODE *aNode)
Function AddChildNode adds a node as a child owned by this node.
Definition: ifsg_node.cpp:148
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
IFSG_NORMALS is the wrapper for the SGNORMALS class.
Definition: ifsg_normals.h:41
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
bool AddNormal(double aXValue, double aYValue, double aZValue)
IFSG_SHAPE is the wrapper for the SGSHAPE class.
Definition: ifsg_shape.h:41
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
Definition: ifsg_shape.cpp:121
IFSG_TRANSFORM is the wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH.
bool Attach(SGNODE *aNode) override
Function Attach associates a given SGNODE* with this wrapper.
bool SetTranslation(const SGPOINT &aTranslation) noexcept
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
bool SetScale(const SGPOINT &aScale) noexcept
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:392
double g
Green component.
Definition: color4d.h:393
double a
Alpha component.
Definition: color4d.h:395
COLOR4D Brightened(double aFactor) const
Return a color that is brighter by a given factor, without modifying object.
Definition: color4d.h:268
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition: color4d.h:295
double b
Blue component.
Definition: color4d.h:394
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:87
const wxString GetFullURI(bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:521
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:863
Definition: pad.h:59
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: pad.h:359
const VECTOR2I & GetDrillSize() const
Definition: pad.h:257
PAD_ATTRIB GetAttribute() const
Definition: pad.h:377
VECTOR2I GetPosition() const override
Definition: pad.h:201
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:345
BOARD * GetBoard() const
bool ExportVRML_File(const wxString &aFullFileName, double aMMtoWRMLunit, bool aIncludeUnspecified, bool aIncludeDNP, bool aExport3DFiles, bool aUseRelativePaths, const wxString &a3D_Subdir, double aXRef, double aYRef)
Create the file(s) exporting current BOARD to a VRML file.
static FP_LIB_TABLE * PcbFootprintLibs(PROJECT *aProject)
Return the table of footprint libraries without Kiway.
Definition: project_pcb.cpp:37
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
Definition: project_pcb.cpp:77
Container for project specific data.
Definition: project.h:62
FILENAME_RESOLVER * GetResolver() noexcept
Definition: 3d_cache.cpp:596
SCENEGRAPH * Load(const wxString &aModelFile, const wxString &aBasePath)
Attempt to load the scene data for a model.
Definition: 3d_cache.cpp:275
The base class of all Scene Graph nodes.
Definition: sg_node.h:75
double z
Definition: sg_base.h:72
double x
Definition: sg_base.h:70
double y
Definition: sg_base.h:71
void SetVector(double aXVal, double aYVal, double aZVal)
Definition: sg_base.cpp:233
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
void Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
std::vector< CUSTOM_COLOR_ITEM > CUSTOM_COLORS_LIST
#define _HKI(x)
#define _(s)
static constexpr EDA_ANGLE FULL_CIRCLE
Definition: eda_angle.h:433
static void build_quat(double x, double y, double z, double a, double q[4])
static void compose_quat(double q1[4], double q2[4], double qr[4])
static void from_quat(double q[4], double rot[4])
static bool g_ColorsLoaded
#define ERR_APPROX_MAX_MM
VRML_COLOR_INDEX
Definition: exporter_vrml.h:33
@ VRML_COLOR_BOT_SILK
Definition: exporter_vrml.h:41
@ VRML_COLOR_LAST
Definition: exporter_vrml.h:42
@ VRML_COLOR_TOP_SILK
Definition: exporter_vrml.h:40
@ VRML_COLOR_TOP_SOLDMASK
Definition: exporter_vrml.h:37
@ VRML_COLOR_PCB
Definition: exporter_vrml.h:35
@ VRML_COLOR_PASTE
Definition: exporter_vrml.h:39
@ VRML_COLOR_COPPER
Definition: exporter_vrml.h:36
@ VRML_COLOR_BOT_SOLDMASK
Definition: exporter_vrml.h:38
#define PLATE_OFFSET
Definition: exporter_vrml.h:28
#define ART_OFFSET
Definition: exporter_vrml.h:26
@ FP_SMD
Definition: footprint.h:73
@ FP_THROUGH_HOLE
Definition: footprint.h:72
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
collects header files for all SG* wrappers and the API
PROJECT & Prj()
Definition: kicad.cpp:595
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Adhes
Definition: layer_ids.h:97
@ Edge_Cuts
Definition: layer_ids.h:113
@ Dwgs_User
Definition: layer_ids.h:109
@ F_Paste
Definition: layer_ids.h:101
@ Cmts_User
Definition: layer_ids.h:110
@ F_Adhes
Definition: layer_ids.h:98
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ Eco1_User
Definition: layer_ids.h:111
@ F_Mask
Definition: layer_ids.h:107
@ B_Paste
Definition: layer_ids.h:100
@ F_SilkS
Definition: layer_ids.h:104
@ Eco2_User
Definition: layer_ids.h:112
@ B_SilkS
Definition: layer_ids.h:103
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
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
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition: ifsg_api.cpp:494
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
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:149
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:424
@ PAD_DRILL_SHAPE_OBLONG
Definition: pad_shapes.h:55
#define OPEN_OSTREAM(var, name)
#define CLOSE_STREAM(var)
#define OSTREAM
wxString From_UTF8(const char *cstring)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391
A class to handle a custom color (predefined color) for the color picker dialog.
constexpr double IUTomm(int iu) const
Definition: base_units.h:86
const double IU_PER_MILS
Definition: base_units.h:77
const double MM_PER_IU
Definition: base_units.h:78
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
float ambient
Definition: exporter_vrml.h:60
float emit_blu
Definition: exporter_vrml.h:58
float diffuse_red
Definition: exporter_vrml.h:48
float spec_blu
Definition: exporter_vrml.h:54
float diffuse_grn
Definition: exporter_vrml.h:49
float transp
Definition: exporter_vrml.h:61
float spec_grn
Definition: exporter_vrml.h:53
float emit_grn
Definition: exporter_vrml.h:57
float emit_red
Definition: exporter_vrml.h:56
float spec_red
Definition: exporter_vrml.h:52
float diffuse_blu
Definition: exporter_vrml.h:50
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:228
double DEG2RAD(double deg)
Definition: trigo.h:200
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97