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