KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_preview_3d_model.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) 2016 Mario Luzeiro <[email protected]>
5 * Copyright (C) 2015 Cirilo Bernardo <[email protected]>
6 * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <3d_rendering/opengl/render_3d_opengl.h> // Must be included before any GL header
24
25#include <cmath>
26
31#include <tool/tool_manager.h>
35#include <base_units.h>
36#include <bitmaps.h>
37#include <board.h>
39#include <dpi_scaling_common.h>
40#include <footprint.h>
41#include <pad.h>
42#include <3d_math.h>
43#include <core/profile.h>
48#include <scoped_set_reset.h>
50#include <wx/filename.h>
51#include <cmath>
52#include <limits>
53#include <lset.h>
54#include <pgm_base.h>
55#include <project_pcb.h>
58#include <widgets/wx_infobar.h>
62
63#if defined(__linux__) || defined(__FreeBSD__)
65#else
67#endif
68
70static constexpr double ALIGN_ANIMATION_SECONDS = 1.0;
71static constexpr int ALIGN_ANIMATION_INTERVAL_MS = 16;
72
73
74static wxString evaluateTextCtrl( const wxString& aValue )
75{
76 // NUMERIC_EVALUATOR doesn't handle UTF-8 multi-byte characters properly,
77 // so skip evaluation if the string contains non-ASCII characters (e.g., degree symbols)
78 for( wxUniChar c : aValue )
79 {
80 if( !c.IsAscii() )
81 return aValue;
82 }
83
84 // Attempt to evaluate formula; if successful return result, otherwise return original
86
87 if( eval.Process( aValue ) )
88 return eval.Result();
89
90 return aValue;
91}
92
93
100static double normalizeRotation( double aRotation )
101{
102 double normalized = std::fmod( aRotation, 2.0 * MAX_ROTATION );
103
104 if( normalized <= -MAX_ROTATION )
105 normalized += 2.0 * MAX_ROTATION;
106 else if( normalized > MAX_ROTATION )
107 normalized -= 2.0 * MAX_ROTATION;
108
109 if( normalized == -0.0 )
110 normalized = 0.0;
111
112 return normalized;
113}
114
115
116static double rotationFromString( const wxString& aValue )
117{
119
120 return normalizeRotation( rotation );
121}
122
123
125 std::vector<FP_3DMODEL>* aParentModelList ) :
127 m_parentFrame( aFrame ),
128 m_previewPane( nullptr ),
129 m_infobar( nullptr ),
130 m_alignmentInfoBar( nullptr ),
134{
135 m_userUnits = m_parentFrame->GetUserUnits();
136
137 m_dummyBoard = new BOARD();
138
139 m_dummyBoard->SetProject( &aFrame->Prj(), true );
140 m_dummyBoard->SetEmbeddedFilesDelegate( aFrame->GetBoard() );
141
142 // This board will only be used to hold a footprint for viewing
143 m_dummyBoard->SetBoardUse( BOARD_USE::FPHOLDER );
144
145 BOARD_DESIGN_SETTINGS parent_bds = aFrame->GetDesignSettings();
146 BOARD_DESIGN_SETTINGS& dummy_bds = m_dummyBoard->GetDesignSettings();
147 dummy_bds.SetBoardThickness( parent_bds.GetBoardThickness() );
149 BOARD_STACKUP& dummy_board_stackup = m_dummyBoard->GetDesignSettings().GetStackupDescriptor();
150 dummy_board_stackup.RemoveAll();
151 dummy_board_stackup.BuildDefaultStackupList( &dummy_bds, 2 );
152
153 m_selected = -1;
154
155 m_previewLabel->SetFont( KIUI::GetStatusFont( this ) );
156
157 // Set the bitmap of 3D view buttons:
164 m_bpvISO->SetBitmap( KiBitmapBundle( BITMAPS::ortho ) );
168
169 // Set the min and max values of spin buttons (mandatory on Linux)
170 // They are not used, so they are set to min and max 32 bits int values
171 // (the min and max values supported by a wxSpinButton)
172 // It avoids blocking the up or down arrows when reaching this limit after
173 // a few clicks.
174 wxSpinButton* spinButtonList[] =
175 {
179 };
180
181 for( wxSpinButton* button : spinButtonList )
182 button->SetRange(INT_MIN, INT_MAX );
183
184 for( TEXT_CTRL_EVAL* rotCtrl : { xrot, yrot, zrot } )
185 {
186 rotCtrl->SetCustomEval(
187 [&]( TEXT_CTRL_EVAL* aCtrl )
188 {
189 double value = rotationFromString( evaluateTextCtrl( aCtrl->GetValue() ) );
190 aCtrl->SetValue( formatRotationValue( value ) );
191 } );
192 }
193
194 for( TEXT_CTRL_EVAL* scaleCtrl : { xscale, yscale, zscale } )
195 {
196 scaleCtrl->SetCustomEval(
197 [&]( TEXT_CTRL_EVAL* aCtrl )
198 {
200 evaluateTextCtrl( aCtrl->GetValue() ) );
201 aCtrl->SetValue( formatScaleValue( value ) );
202 } );
203 }
204
205 m_parentModelList = aParentModelList;
206
207 m_dummyFootprint = new FOOTPRINT( *aFootprint );
208 m_dummyFootprint->SetParentGroup( nullptr );
209
210 // Ensure the footprint is shown like in Fp editor: rot 0, not flipped
211 // to avoid mistakes when setting the3D shape position/rotation
212 if( m_dummyFootprint->IsFlipped() )
214
215 m_dummyFootprint->SetOrientation( ANGLE_0 );
216
218
219 // Create the 3D canvas
222 PROJECT_PCB::Get3DCacheManager( &aFrame->Prj() ) );
223
224 try
225 {
226#if defined(__linux__) || defined(__FreeBSD__)
227 m_spaceMouse = std::make_unique<SPNAV_VIEWER_PLUGIN>( m_previewPane );
228#else
229 m_spaceMouse = std::make_unique<NL_FOOTPRINT_PROPERTIES_PLUGIN>( m_previewPane );
230#endif
231 m_spaceMouse->SetFocus( true );
232 }
233 catch( const std::exception& e )
234 {
235 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ), wxS( "%s" ), e.what() );
236 }
237 catch( ... )
238 {
239 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
240 wxT( "Unknown exception during SpaceMouse initialization" ) );
241 }
242
243 m_boardAdapter.SetBoard( m_dummyBoard );
244 m_boardAdapter.m_IsBoardView = false;
245
246 // Force display 3D models, regardless the 3D viewer options.
247 m_boardAdapter.m_IsPreviewer = true;
248
249 loadSettings();
250
251 // Don't show placeholder models in the footprint properties 3D preview
252 if( m_boardAdapter.m_Cfg )
253 m_boardAdapter.m_Cfg->m_Render.show_missing_models = false;
254
255 // Create the manager
257 m_toolManager->SetEnvironment( m_dummyBoard, nullptr, nullptr, nullptr, this );
258
261 m_previewPane->SetEventDispatcher( m_toolDispatcher );
262
263 // Register tools
264 m_toolManager->RegisterTool( new EDA_3D_CONTROLLER );
265 m_toolManager->InitTools();
266
267 // Run the viewer control tool, it is supposed to be always active
268 m_toolManager->InvokeTool( "3DViewer.Control" );
269
270 m_infobar = new WX_INFOBAR( this );
271 m_previewPane->SetInfoBar( m_infobar );
272 m_alignmentInfoBar = new WX_INFOBAR( this );
273
274 m_SizerPanelView->Add( m_infobar, 0, wxEXPAND, 0 );
275 m_SizerPanelView->Add( m_alignmentInfoBar, 0, wxEXPAND, 0 );
276 m_SizerPanelView->Add( m_previewPane, 1, wxEXPAND, 5 );
277
278 for( wxEventType eventType : { wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT } )
279 Connect( eventType, wxMenuEventHandler( PANEL_PREVIEW_3D_MODEL::OnMenuEvent ), nullptr, this );
280
281 aFrame->Connect( EDA_EVT_UNITS_CHANGED, wxCommandEventHandler( PANEL_PREVIEW_3D_MODEL::onUnitsChanged ),
282 nullptr, this );
283
284 Bind( wxCUSTOM_PANEL_SHOWN_EVENT, &PANEL_PREVIEW_3D_MODEL::onPanelShownEvent, this );
285 Bind( wxEVT_CHAR_HOOK, &PANEL_PREVIEW_3D_MODEL::onAlignKey, this );
286 m_previewPane->Bind( wxEVT_CHAR_HOOK, &PANEL_PREVIEW_3D_MODEL::onAlignKey, this );
287
288 m_alignAnimTimer.SetOwner( this );
289 Bind( wxEVT_TIMER, &PANEL_PREVIEW_3D_MODEL::onAlignAnimation, this, m_alignAnimTimer.GetId() );
290}
291
292
294{
295 m_alignAnimTimer.Stop();
296 m_previewPane->SetPickHandler( {} );
297 m_previewPane->SetHoverHandler( {} );
299
300 // Shutdown all running tools
301 if( m_toolManager )
302 m_toolManager->ShutdownAllTools();
303
304 // Restore the 3D viewer Render settings, that can be modified by the panel tools
305 if( m_boardAdapter.m_Cfg )
306 m_boardAdapter.m_Cfg->m_Render = m_initialRender;
307
308 delete m_dummyBoard;
309 delete m_previewPane;
310}
311
312
313void PANEL_PREVIEW_3D_MODEL::OnMenuEvent( wxMenuEvent& aEvent )
314{
315 if( !m_toolDispatcher )
316 aEvent.Skip();
317 else
318 m_toolDispatcher->DispatchWxEvent( aEvent );
319}
320
321
323{
324 wxCHECK_RET( m_previewPane, wxT( "Cannot load settings to null canvas" ) );
325
326 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
327
328 // TODO(JE) use all control options
329 m_boardAdapter.m_MousewheelPanning = settings->m_Input.scroll_modifier_zoom != 0;
330
332 {
333 // Save the 3D viewer render settings, to restore it after closing the preview
334 m_initialRender = cfg->m_Render;
335
336 m_boardAdapter.m_Cfg = cfg;
337
338 m_previewPane->SetAnimationEnabled( cfg->m_Camera.animation_enabled );
339 m_previewPane->SetMovingSpeedMultiplier( cfg->m_Camera.moving_speed_multiplier );
340 m_previewPane->SetProjectionMode( cfg->m_Camera.projection_mode );
341 }
342}
343
344
346{
347 return wxString::Format( wxT( "%.4f" ),
348 aValue );
349}
350
351
353{
354 // Sigh. Did we really need differentiated +/- 0.0?
355 if( aValue == -0.0 )
356 aValue = 0.0;
357
358 return wxString::Format( wxT( "%.2f%s" ),
359 aValue,
361}
362
363
365{
366 // Convert from internal units (mm) to user units
368 aValue /= 25.4;
369 else if( m_userUnits == EDA_UNITS::MILS )
370 aValue /= 25.4 / 1e3;
371
372 return wxString::Format( wxT( "%.6f%s" ),
373 aValue,
375}
376
377
379{
381 || m_selected >= (int) m_parentModelList->size() )
382 {
383 return false;
384 }
385
387 const std::string extension = wxFileName( model.m_Filename ).GetExt().utf8_string();
388
389 return model.m_Show
392 && std::isfinite( model.m_Scale.x ) && model.m_Scale.x != 0.0
393 && std::isfinite( model.m_Scale.y ) && model.m_Scale.y != 0.0
394 && std::isfinite( model.m_Scale.z ) && model.m_Scale.z != 0.0;
395}
396
397
398void PANEL_PREVIEW_3D_MODEL::onAlignUpdateUI( wxUpdateUIEvent& aEvent )
399{
400 aEvent.Enable( canAlign() );
401}
402
403
405{
406 if( !canAlign() )
407 return nullptr;
408
409 wxString basePath;
410
411 try
412 {
413 auto row = PROJECT_PCB::FootprintLibAdapter( m_dummyBoard->GetProject() )
414 ->GetRow( m_dummyFootprint->GetFPID().GetLibNickname() );
415
416 if( row )
417 basePath = LIBRARY_MANAGER::GetFullURI( *row, true );
418 }
419 catch( const IO_ERROR& )
420 {
421 // Models may have absolute paths even when the footprint library is unavailable.
422 }
423
425 return m_boardAdapter.Get3dCacheManager()->GetModel(
426 m_parentModelList->at( m_selected ).m_Filename, basePath,
427 { m_dummyFootprint->GetEmbeddedFiles(), m_dummyBoard->GetEmbeddedFiles() } );
428}
429
430
436
437
439{
440 m_previewPane->JoinBgWorker();
441
442 if( m_alignBodyShown )
443 {
444 if( EXTRUDED_3D_BODY* body = m_dummyFootprint->GetExtrudedBody() )
445 body->m_show = *m_alignBodyShown;
446
447 m_alignBodyShown.reset();
448 }
449
450 m_boardAdapter.SetVisibilityOverride( std::nullopt );
451 m_alignLayers.reset();
452}
453
454
456{
457 m_previewPane->SetPickHandler( {} );
458 m_previewPane->SetHoverHandler( {} );
460 m_alignButton->SetLabel( _( "Align" ) );
461 Layout();
463}
464
465
467{
468 if( !m_alignmentInfoBar->IsShown() )
469 return;
470
471 // Dismiss() does not hide the bar when its notebook page is already hidden.
472 if( m_alignmentInfoBar->IsShownOnScreen() )
473 m_alignmentInfoBar->Dismiss();
474 else
475 m_alignmentInfoBar->Hide();
476
477 Layout();
478}
479
480
482{
484
485 if( m_alignAnimating || !m_alignFaceTriangles.empty() )
486 {
488 m_alignFaceTriangles.clear();
489 m_alignHoverRegion.reset();
490 m_alignHoverPad = nullptr;
492 }
493
495 return;
496
497 const bool picking = m_alignState != ALIGN_STATE::SOLVED;
499 m_alignRegions.clear();
501 m_alignSolutions.clear();
502 m_alignGeometry = nullptr;
503
504 if( picking )
505 stopPicking();
506}
507
508
509void PANEL_PREVIEW_3D_MODEL::onAlignKey( wxKeyEvent& aEvent )
510{
511 if( aEvent.GetKeyCode() == WXK_ESCAPE
513 {
515 return;
516 }
517
518 aEvent.Skip();
519}
520
521
522void PANEL_PREVIEW_3D_MODEL::onAlign( wxCommandEvent& aEvent )
523{
525 {
527 return;
528 }
529
530 if( !canAlign() )
531 return;
532
534 {
537 return;
538 }
539
540 const S3DMODEL* geometry = alignmentModel();
541
542 if( !geometry )
543 return;
544
545 const auto& scale = m_parentModelList->at( m_selected ).m_Scale;
546 m_alignRegions = MODEL_ALIGN::BuildRegions( *geometry, glm::dvec3( scale.x, scale.y, scale.z ) );
547
548 if( m_alignRegions.empty() )
549 return;
550
551 m_alignGeometry = geometry;
553
554 for( size_t i = 0; i < m_alignRegions.size(); ++i )
555 {
556 for( const std::array<unsigned int, 2>& source : m_alignRegions[i].sourceTriangles )
557 m_alignTriangleRegions.emplace( source, i );
558 }
559
560 if( EXTRUDED_3D_BODY* body = m_dummyFootprint->GetExtrudedBody() )
561 {
562 m_alignBodyShown = body->m_show;
563 body->m_show = false;
564 }
565
566 m_alignLayers = m_boardAdapter.GetVisibleLayers();
567 auto layers = *m_alignLayers;
568
571 {
572 layers.reset( layer );
573 }
574
575 m_boardAdapter.SetVisibilityOverride( layers );
577 m_alignButton->SetLabel( _( "Cancel alignment" ) );
578 Layout();
579 m_previewPane->SetPickHandler( [this]( const RAY& aRay ) { return pickAlignment( aRay ); } );
580 m_previewPane->SetHoverHandler( [this]( const std::optional<RAY>& aRay ) { hoverAlignment( aRay ); } );
581 m_alignmentInfoBar->ShowMessage( _( "Click the model face that sits on the board. Press Esc to cancel." ),
582 wxICON_INFORMATION );
584}
585
586
588{
589 m_previewPane->JoinBgWorker();
590
591 if( !canAlign() )
592 {
594 return true;
595 }
596
598 {
599 std::optional<size_t> region = alignmentRegionAt( aRay );
600
601 if( !region )
602 return true;
603
604 m_alignSeed = *region;
606 m_alignHoverRegion.reset();
608 auto layers = *m_alignLayers;
609 layers.set( LAYER_3D_COPPER_TOP );
610 layers.reset( LAYER_3D_COPPER_BOTTOM );
611 layers.reset( LAYER_3D_SOLDERMASK_TOP );
612 layers.reset( LAYER_3D_SOLDERPASTE );
613 m_boardAdapter.SetVisibilityOverride( layers );
614 m_alignmentInfoBar->ShowMessage( _( "Click the footprint pad for that contact. Press Esc to cancel." ),
615 wxICON_INFORMATION );
619 return true;
620 }
621
623 return false;
624
625 PAD* clicked = m_previewPane->PickFootprintPad( aRay, *m_dummyFootprint );
626
627 if( !clicked || ( clicked->GetAttribute() != PAD_ATTRIB::SMD
628 && clicked->GetAttribute() != PAD_ATTRIB::PTH ) )
629 {
630 return true;
631 }
632
633 std::vector<MODEL_ALIGN::PAD> pads;
634 size_t clickedIndex = 0;
635 const VECTOR2I origin = m_dummyFootprint->GetPosition();
636
637 for( const PAD* pad : m_dummyFootprint->Pads() )
638 {
639 if( pad == clicked )
640 clickedIndex = pads.size();
641
642 const VECTOR2I pos = pad->GetPosition() - origin;
643 const VECTOR2I size = pad->GetSize( F_Cu );
644 const VECTOR2I drill = pad->GetDrillSize();
645 MODEL_ALIGN::PAD item;
646 item.number = pad->GetNumber().utf8_string();
647 item.position = glm::dvec2( pos.x, -pos.y ) / pcbIUScale.IU_PER_MM;
648 item.size = glm::dvec2( size.x, size.y ) / pcbIUScale.IU_PER_MM;
649 item.drill = glm::dvec2( drill.x, drill.y ) / pcbIUScale.IU_PER_MM;
650 item.rotation = pad->GetOrientationDegrees();
654 pads.push_back( std::move( item ) );
655 }
656
657 const auto& rotation = m_parentModelList->at( m_selected ).m_Rotation;
659 m_alignRegions, m_alignSeed, MODEL_ALIGN::BuildPadGroup( pads, clickedIndex ), pads,
660 glm::dvec3( -rotation.x, -rotation.y, -rotation.z ) );
661
662 if( m_alignSolutions.empty() )
663 {
665 return true;
666 }
667
669 m_alignSolution = 0;
670 m_alignHoverPad = nullptr;
671 stopPicking();
673
674 if( m_alignSolutions.size() == 1 )
675 {
676 // Nothing to cycle through, so leave the session behind and keep the result.
678 m_alignRegions.clear();
680 m_alignSolutions.clear();
681 m_alignGeometry = nullptr;
682 }
683
684 return true;
685}
686
687
689{
692 const VECTOR3D fromRotation = model.m_Rotation;
693 const VECTOR3D fromOffset = model.m_Offset;
694
695 xrot->ChangeValue( formatRotationValue( normalizeRotation( solution.rotation.x ) ) );
696 yrot->ChangeValue( formatRotationValue( normalizeRotation( solution.rotation.y ) ) );
697 zrot->ChangeValue( formatRotationValue( normalizeRotation( solution.rotation.z ) ) );
698 xoff->ChangeValue( formatOffsetValue( solution.offset.x ) );
699 yoff->ChangeValue( formatOffsetValue( solution.offset.y ) );
700 zoff->ChangeValue( formatOffsetValue( solution.offset.z ) );
701
702 {
703 SCOPED_SET_RESET<bool> updating( m_alignUpdating, true );
704 wxCommandEvent event;
705 updateOrientation( event );
706 }
707
708 if( m_alignSeed < m_alignRegions.size() )
710
711 startAlignmentAnimation( fromRotation, fromOffset );
712
713 if( m_alignSolutions.size() > 1 )
714 {
715 m_alignmentInfoBar->ShowMessage( wxString::Format( _( "Solution %d of %d, press Align to cycle." ),
716 (int) m_alignSolution + 1,
717 (int) m_alignSolutions.size() ),
718 wxICON_INFORMATION );
719 }
720 else
721 {
723 }
724}
725
726
728{
730 const VECTOR3D& rotation = m_alignAnimating ? m_alignAnimRotation : model.m_Rotation;
731 const VECTOR3D& offset = m_alignAnimating ? m_alignAnimOffset : model.m_Offset;
732
733 // The region vertices already carry the model scale.
734 return m_boardAdapter.GetFootprintMatrix( *m_dummyFootprint )
735 * CalcModelMatrix( SFVEC3F( offset.x, offset.y, offset.z ),
736 SFVEC3F( rotation.x, rotation.y, rotation.z ), SFVEC3F( 1.0f ) );
737}
738
739
740std::vector<SFVEC3F> PANEL_PREVIEW_3D_MODEL::alignmentRegionTriangles( size_t aRegion ) const
741{
742 const MODEL_ALIGN::REGION& region = m_alignRegions.at( aRegion );
743 std::vector<SFVEC3F> triangles;
744
745 triangles.reserve( region.triangles.size() * 3 );
746
747 for( const std::array<unsigned int, 3>& triangle : region.triangles )
748 {
749 for( unsigned int index : triangle )
750 {
751 const glm::dvec3& vertex = region.vertices[index];
752 triangles.emplace_back( vertex.x, vertex.y, vertex.z );
753 }
754 }
755
756 return triangles;
757}
758
759
760std::vector<SFVEC3F> PANEL_PREVIEW_3D_MODEL::alignmentPadTriangles( const PAD& aPad ) const
761{
762 const PCB_LAYER_ID layer = m_dummyFootprint->IsFlipped() ? B_Cu : F_Cu;
763 const float units = m_boardAdapter.BiuTo3dUnits();
764 const float z = m_boardAdapter.GetFootprintZPos( m_dummyFootprint->IsFlipped() );
765 SHAPE_POLY_SET outline = *aPad.GetEffectivePolygon( layer, ERROR_INSIDE );
766 std::vector<SFVEC3F> triangles;
767
768 outline.CacheTriangulation( false );
769
770 for( unsigned int i = 0; i < outline.TriangulatedPolyCount(); ++i )
771 {
772 const SHAPE_POLY_SET::TRIANGULATED_POLYGON* polygon = outline.TriangulatedPolygon( i );
773
774 for( size_t j = 0; j < polygon->GetTriangleCount(); ++j )
775 {
776 VECTOR2I a;
777 VECTOR2I b;
778 VECTOR2I c;
779 polygon->GetTriangle( (int) j, a, b, c );
780
781 for( const VECTOR2I& point : { a, b, c } )
782 triangles.emplace_back( point.x * units, -point.y * units, z );
783 }
784 }
785
786 return triangles;
787}
788
789
791{
792 std::vector<EDA_3D_CANVAS::OVERLAY> overlays;
793
795 {
796 m_previewPane->SetOverlays( {} );
797 m_previewPane->Request_refresh();
798 return;
799 }
800
801 if( !m_alignFaceTriangles.empty() )
802 {
804 overlay.transform = alignmentModelMatrix();
805 overlay.vertices = m_alignFaceTriangles;
806 overlay.color = SFVEC4F( 0.05f, 0.85f, 0.25f, 0.35f );
807 overlay.alwaysVisible = true;
808 overlays.push_back( std::move( overlay ) );
809 }
810
812 {
814 overlay.transform = alignmentModelMatrix();
816 overlay.color = SFVEC4F( 0.35f, 1.0f, 0.45f, 0.30f );
817 overlays.push_back( std::move( overlay ) );
818 }
819
821 {
824 overlay.color = SFVEC4F( 0.35f, 1.0f, 0.45f, 0.45f );
825 overlay.alwaysVisible = true;
826 overlays.push_back( std::move( overlay ) );
827 }
828
829 m_previewPane->SetOverlays( std::move( overlays ) );
830 m_previewPane->Request_refresh();
831}
832
833
834std::optional<size_t> PANEL_PREVIEW_3D_MODEL::alignmentRegionAt( const RAY& aRay )
835{
836 if( !m_alignGeometry || !canAlign() )
837 return std::nullopt;
838
839 std::optional<EDA_3D_CANVAS::MODEL_HIT> hit =
842
843 if( !hit )
844 return std::nullopt;
845
846 auto region = m_alignTriangleRegions.find( { hit->mesh, hit->triangle / 3 } );
847
848 if( region == m_alignTriangleRegions.end() )
849 return std::nullopt;
850
851 return region->second;
852}
853
854
855void PANEL_PREVIEW_3D_MODEL::hoverAlignment( const std::optional<RAY>& aRay )
856{
857 std::optional<size_t> region;
858 const PAD* pad = nullptr;
859
860 if( aRay && canAlign() )
861 {
863 region = alignmentRegionAt( *aRay );
865 pad = m_previewPane->PickFootprintPad( *aRay, *m_dummyFootprint );
866 }
867
868 if( pad && pad->GetAttribute() != PAD_ATTRIB::SMD && pad->GetAttribute() != PAD_ATTRIB::PTH )
869 pad = nullptr;
870
871 if( region == m_alignHoverRegion && pad == m_alignHoverPad )
872 return;
873
874 m_alignHoverRegion = region;
877}
878
879
881{
883
884 m_alignAnimFromRotation = aRotation;
885 m_alignAnimFromOffset = aOffset;
886 m_alignAnimRotation = aRotation;
887 m_alignAnimOffset = aOffset;
888
889 if( !m_previewPane->GetAnimationEnabled()
890 || ( aRotation == model.m_Rotation && aOffset == model.m_Offset ) )
891 {
892 m_alignFaceTriangles.clear();
894 return;
895 }
896
897 m_alignAnimating = true;
899 m_alignAnimTimer.Start( ALIGN_ANIMATION_INTERVAL_MS, wxTIMER_CONTINUOUS );
901}
902
903
909
910
912{
914}
915
916
918{
919 if( !m_alignAnimating )
920 return;
921
923 const double elapsed = ( GetRunningMicroSecs() - m_alignAnimStart ) / 1e6;
924 const double t = std::clamp( elapsed / ALIGN_ANIMATION_SECONDS, 0.0, 1.0 );
925 const float eased = (float) ( t * t * ( 3.0 - 2.0 * t ) );
926
927 const SFVEC3F rotation = InterpolateModelRotation(
929 SFVEC3F( model.m_Rotation.x, model.m_Rotation.y, model.m_Rotation.z ), eased );
930
931 m_alignAnimRotation = VECTOR3D( rotation.x, rotation.y, rotation.z );
932 m_alignAnimOffset = VECTOR3D( std::lerp( m_alignAnimFromOffset.x, model.m_Offset.x, (double) eased ),
933 std::lerp( m_alignAnimFromOffset.y, model.m_Offset.y, (double) eased ),
934 std::lerp( m_alignAnimFromOffset.z, model.m_Offset.z, (double) eased ) );
935
936 if( t >= 1.0 )
937 {
938 // Land on the stored angles rather than on a re-decomposed equivalent of them.
939 m_alignAnimRotation = model.m_Rotation;
940 m_alignAnimOffset = model.m_Offset;
941 }
942
944 {
947 preview.m_Offset = m_alignAnimOffset;
948 }
949
950 if( t >= 1.0 )
951 {
953 m_alignFaceTriangles.clear();
954 }
955
957}
958
959
961{
963
964 m_extrudedBody = nullptr;
965 m_opacity->Enable( true );
966
967 if( m_parentModelList && idx >= 0 && idx < (int) m_parentModelList->size() )
968 {
969 m_selected = idx;
970 const FP_3DMODEL& modelInfo = m_parentModelList->at( (unsigned) m_selected );
971
972 // Use ChangeValue() instead of SetValue(). It's not the user making the change, so we
973 // don't want to generate wxEVT_GRID_CELL_CHANGED events.
974 xscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.x ) );
975 yscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.y ) );
976 zscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.z ) );
977
978 // Rotation is stored in the file as positive-is-CW, but we use positive-is-CCW in the GUI
979 // to match the rest of KiCad
980 xrot->ChangeValue( formatRotationValue( -modelInfo.m_Rotation.x ) );
981 yrot->ChangeValue( formatRotationValue( -modelInfo.m_Rotation.y ) );
982 zrot->ChangeValue( formatRotationValue( -modelInfo.m_Rotation.z ) );
983
984 xoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.x ) );
985 yoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.y ) );
986 zoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.z ) );
987
988 m_opacity->SetValue( modelInfo.m_Opacity * 100.0 );
989 }
990 else
991 {
992 m_selected = -1;
993
994 xscale->ChangeValue( wxEmptyString );
995 yscale->ChangeValue( wxEmptyString );
996 zscale->ChangeValue( wxEmptyString );
997
998 xrot->ChangeValue( wxEmptyString );
999 yrot->ChangeValue( wxEmptyString );
1000 zrot->ChangeValue( wxEmptyString );
1001
1002 xoff->ChangeValue( wxEmptyString );
1003 yoff->ChangeValue( wxEmptyString );
1004 zoff->ChangeValue( wxEmptyString );
1005
1006 m_opacity->SetValue( 100 );
1007 }
1008}
1009
1010
1012{
1014 m_extrudedBody = aBody;
1015
1016 if( aBody )
1017 {
1018 xscale->ChangeValue( formatScaleValue( aBody->m_scale.x ) );
1019 yscale->ChangeValue( formatScaleValue( aBody->m_scale.y ) );
1020 zscale->ChangeValue( formatScaleValue( aBody->m_scale.z ) );
1021
1022 xrot->ChangeValue( formatRotationValue( -aBody->m_rotation.x ) );
1023 yrot->ChangeValue( formatRotationValue( -aBody->m_rotation.y ) );
1024 zrot->ChangeValue( formatRotationValue( -aBody->m_rotation.z ) );
1025
1026 xoff->ChangeValue( formatOffsetValue( aBody->m_offset.x ) );
1027 yoff->ChangeValue( formatOffsetValue( aBody->m_offset.y ) );
1028 zoff->ChangeValue( formatOffsetValue( aBody->m_offset.z ) );
1029
1030 m_opacity->SetValue( 100 );
1031 m_opacity->Enable( false );
1032 }
1033 else
1034 {
1036 }
1037}
1038
1039
1041{
1042 if( m_extrudedBody )
1043 {
1045 evaluateTextCtrl( xscale->GetValue() ) );
1047 evaluateTextCtrl( yscale->GetValue() ) );
1049 evaluateTextCtrl( zscale->GetValue() ) );
1050
1051 m_extrudedBody->m_rotation.x = -rotationFromString( evaluateTextCtrl( xrot->GetValue() ) );
1052 m_extrudedBody->m_rotation.y = -rotationFromString( evaluateTextCtrl( yrot->GetValue() ) );
1053 m_extrudedBody->m_rotation.z = -rotationFromString( evaluateTextCtrl( zrot->GetValue() ) );
1054
1056 evaluateTextCtrl( xoff->GetValue() ) )
1057 / pcbIUScale.IU_PER_MM;
1059 evaluateTextCtrl( yoff->GetValue() ) )
1060 / pcbIUScale.IU_PER_MM;
1062 evaluateTextCtrl( zoff->GetValue() ) )
1063 / pcbIUScale.IU_PER_MM;
1064
1065 UpdateDummyFootprint( true );
1066 onModify();
1067 }
1068 else if( m_parentModelList && m_selected >= 0 && m_selected < (int) m_parentModelList->size() )
1069 {
1070 // Write settings back to the parent
1071 FP_3DMODEL* modelInfo = &m_parentModelList->at( (unsigned) m_selected );
1072 const VECTOR3D previousScale = modelInfo->m_Scale;
1073 const VECTOR3D previousRotation = modelInfo->m_Rotation;
1074 const VECTOR3D previousOffset = modelInfo->m_Offset;
1075
1077 evaluateTextCtrl( xscale->GetValue() ) );
1079 evaluateTextCtrl( yscale->GetValue() ) );
1081 evaluateTextCtrl( zscale->GetValue() ) );
1082
1083 // Rotation is stored in the file as positive-is-CW, but we use positive-is-CCW in the GUI
1084 // to match the rest of KiCad
1085 modelInfo->m_Rotation.x = -rotationFromString( evaluateTextCtrl( xrot->GetValue() ) );
1086 modelInfo->m_Rotation.y = -rotationFromString( evaluateTextCtrl( yrot->GetValue() ) );
1087 modelInfo->m_Rotation.z = -rotationFromString( evaluateTextCtrl( zrot->GetValue() ) );
1088
1090 evaluateTextCtrl( xoff->GetValue() ) )
1091 / pcbIUScale.IU_PER_MM;
1093 evaluateTextCtrl( yoff->GetValue() ) )
1094 / pcbIUScale.IU_PER_MM;
1096 evaluateTextCtrl( zoff->GetValue() ) )
1097 / pcbIUScale.IU_PER_MM;
1098
1099 if( previousScale == modelInfo->m_Scale && previousRotation == modelInfo->m_Rotation
1100 && previousOffset == modelInfo->m_Offset )
1101 {
1102 return;
1103 }
1104
1105 // Update the dummy footprint for the preview
1106 UpdateDummyFootprint( false );
1107 onModify();
1108 }
1109}
1110
1111
1112void PANEL_PREVIEW_3D_MODEL::onOpacitySlider( wxCommandEvent& event )
1113{
1114 if( m_parentModelList && m_selected >= 0 && m_selected < (int) m_parentModelList->size() )
1115 {
1116 // Write settings back to the parent
1117 FP_3DMODEL* modelInfo = &m_parentModelList->at( (unsigned) m_selected );
1118
1119 modelInfo->m_Opacity = m_opacity->GetValue() / 100.0;
1120
1121 // Update the dummy footprint for the preview
1122 UpdateDummyFootprint( false );
1123 onModify();
1124 }
1125}
1126
1127
1128void PANEL_PREVIEW_3D_MODEL::setBodyStyleView( wxCommandEvent& event )
1129{
1131
1132 // turn ON or OFF options to show the board body if OFF, solder paste, soldermask
1133 // and board body are hidden, to allows a good view of the 3D model and its pads.
1135
1136 if( !cfg )
1137 return;
1138
1140
1141 m_previewPane->ReloadRequest();
1142 m_previewPane->Refresh();
1143}
1144
1145
1146void PANEL_PREVIEW_3D_MODEL::View3DSettings( wxCommandEvent& event )
1147{
1148 BOARD_DESIGN_SETTINGS bds = m_dummyBoard->GetDesignSettings();
1149 int thickness = bds.GetBoardThickness();
1150
1151 WX_UNIT_ENTRY_DIALOG dlg( m_parentFrame, _( "3D Preview Options" ), _( "Board thickness:" ), thickness );
1152
1153 if( dlg.ShowModal() != wxID_OK )
1154 return;
1155
1156 bds.SetBoardThickness( dlg.GetValue() );
1157
1158 BOARD_STACKUP& boardStackup = m_dummyBoard->GetDesignSettings().GetStackupDescriptor();
1159 boardStackup.RemoveAll();
1160 boardStackup.BuildDefaultStackupList( &bds, 2 );
1161
1162 UpdateDummyFootprint( true );
1163
1164 m_previewPane->ReloadRequest();
1165 m_previewPane->Refresh();
1166}
1167
1168
1169void PANEL_PREVIEW_3D_MODEL::doIncrementScale( wxSpinEvent& event, double aSign )
1170{
1171 wxSpinButton* spinCtrl = dynamic_cast<wxSpinButton*>( event.GetEventObject() );
1172
1173 wxCHECK( spinCtrl, /* void */ );
1174
1175 wxTextCtrl * textCtrl = xscale;
1176
1177 if( spinCtrl == m_spinYscale )
1178 textCtrl = yscale;
1179 else if( spinCtrl == m_spinZscale )
1180 textCtrl = zscale;
1181
1182 double step = SCALE_INCREMENT;
1183
1184 if( wxGetMouseState().ShiftDown( ) )
1185 step = SCALE_INCREMENT_FINE;
1186
1187 double value = EDA_UNIT_UTILS::UI::DoubleValueFromString( unityScale, EDA_UNITS::UNSCALED, textCtrl->GetValue() );
1188
1189 value += ( step * aSign );
1190 value = std::max( 1/MAX_SCALE, value );
1191 value = std::min( value, MAX_SCALE );
1192
1193 textCtrl->SetValue( formatScaleValue( value ) );
1194}
1195
1196
1197void PANEL_PREVIEW_3D_MODEL::doIncrementRotation( wxSpinEvent& aEvent, double aSign )
1198{
1199 wxSpinButton* spinCtrl = dynamic_cast<wxSpinButton*>( aEvent.GetEventObject() );
1200
1201 wxCHECK( spinCtrl, /* void */ );
1202
1203 wxTextCtrl* textCtrl = xrot;
1204
1205 if( spinCtrl == m_spinYrot )
1206 textCtrl = yrot;
1207 else if( spinCtrl == m_spinZrot )
1208 textCtrl = zrot;
1209
1210 double step = ROTATION_INCREMENT;
1211
1212 if( wxGetMouseState().ShiftDown( ) )
1214
1215 double value = rotationFromString( textCtrl->GetValue() );
1216
1217 value = normalizeRotation( value + step * aSign );
1218
1219 textCtrl->SetValue( formatRotationValue( value ) );
1220}
1221
1222
1223void PANEL_PREVIEW_3D_MODEL::doIncrementOffset( wxSpinEvent& event, double aSign )
1224{
1225 wxSpinButton* spinCtrl = dynamic_cast<wxSpinButton*>( event.GetEventObject() );
1226
1227 wxCHECK( spinCtrl, /* void */ );
1228
1229 wxTextCtrl * textCtrl = xoff;
1230
1231 if( spinCtrl == m_spinYoffset )
1232 textCtrl = yoff;
1233 else if( spinCtrl == m_spinZoffset )
1234 textCtrl = zoff;
1235
1236 double step_mm = OFFSET_INCREMENT_MM;
1237
1238 if( wxGetMouseState().ShiftDown( ) )
1239 step_mm = OFFSET_INCREMENT_MM_FINE;
1240
1242 {
1243 step_mm = 25.4*OFFSET_INCREMENT_MIL/1000;
1244
1245 if( wxGetMouseState().ShiftDown( ) )
1246 step_mm = 25.4*OFFSET_INCREMENT_MIL_FINE/1000;;
1247 }
1248
1249 double value_mm = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, m_userUnits, textCtrl->GetValue() )
1250 / pcbIUScale.IU_PER_MM;
1251
1252 value_mm += ( step_mm * aSign );
1253 value_mm = std::max( -MAX_OFFSET, value_mm );
1254 value_mm = std::min( value_mm, MAX_OFFSET );
1255
1256 textCtrl->SetValue( formatOffsetValue( value_mm ) );
1257}
1258
1259
1261{
1262 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
1263
1264 wxCHECK( textCtrl, /* void */ );
1265
1266 double step = SCALE_INCREMENT;
1267
1268 if( event.ShiftDown( ) )
1269 step = SCALE_INCREMENT_FINE;
1270
1271 if( event.GetWheelRotation() >= 0 )
1272 step = -step;
1273
1274 double value = EDA_UNIT_UTILS::UI::DoubleValueFromString( unityScale, EDA_UNITS::UNSCALED, textCtrl->GetValue() );
1275
1276 value += step;
1277 value = std::max( 1/MAX_SCALE, value );
1278 value = std::min( value, MAX_SCALE );
1279
1280 textCtrl->SetValue( formatScaleValue( value ) );
1281}
1282
1283
1285{
1286 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
1287
1288 wxCHECK( textCtrl, /* void */ );
1289
1290 double step = ROTATION_INCREMENT;
1291
1292 if( event.ShiftDown( ) )
1294
1295 if( event.GetWheelRotation() >= 0 )
1296 step = -step;
1297
1298 double value = rotationFromString( textCtrl->GetValue() );
1299
1300 value = normalizeRotation( value + step );
1301
1302 textCtrl->SetValue( formatRotationValue( value ) );
1303}
1304
1305
1307{
1308 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
1309
1310 wxCHECK( textCtrl, /* void */ );
1311
1312 double step_mm = OFFSET_INCREMENT_MM;
1313
1314 if( event.ShiftDown( ) )
1315 step_mm = OFFSET_INCREMENT_MM_FINE;
1316
1318 {
1319 step_mm = 25.4*OFFSET_INCREMENT_MIL/1000.0;
1320
1321 if( event.ShiftDown( ) )
1322 step_mm = 25.4*OFFSET_INCREMENT_MIL_FINE/1000.0;
1323 }
1324
1325 if( event.GetWheelRotation() >= 0 )
1326 step_mm = -step_mm;
1327
1328 double value_mm = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, m_userUnits, textCtrl->GetValue() )
1329 / pcbIUScale.IU_PER_MM;
1330
1331 value_mm += step_mm;
1332 value_mm = std::max( -MAX_OFFSET, value_mm );
1333 value_mm = std::min( value_mm, MAX_OFFSET );
1334
1335 textCtrl->SetValue( formatOffsetValue( value_mm ) );
1336}
1337
1338
1339void PANEL_PREVIEW_3D_MODEL::onUnitsChanged( wxCommandEvent& aEvent )
1340{
1342 / pcbIUScale.IU_PER_MM;
1344 / pcbIUScale.IU_PER_MM;
1346 / pcbIUScale.IU_PER_MM;
1347
1348 PCB_BASE_FRAME* frame = static_cast<PCB_BASE_FRAME*>( aEvent.GetClientData() );
1349 m_userUnits = frame->GetUserUnits();
1350
1351 xoff->SetValue( formatOffsetValue( xoff_mm ) );
1352 yoff->SetValue( formatOffsetValue( yoff_mm ) );
1353 zoff->SetValue( formatOffsetValue( zoff_mm ) );
1354
1355 aEvent.Skip();
1356}
1357
1358
1359void PANEL_PREVIEW_3D_MODEL::onPanelShownEvent( wxCommandEvent& aEvent )
1360{
1361 if( !aEvent.GetInt() )
1363
1364 if( m_spaceMouse )
1365 {
1366 m_spaceMouse->SetFocus( static_cast<bool>( aEvent.GetInt() ) );
1367 }
1368
1369 aEvent.Skip();
1370}
1371
1372
1374{
1375 // The background hit-test rebuild reads the same footprint and embedded files.
1376 m_previewPane->JoinBgWorker();
1377
1378 if( !m_alignUpdating )
1380
1381 m_dummyFootprint->Models().clear();
1382 m_dummySelectedModel = std::numeric_limits<size_t>::max();
1383
1385 {
1388 && &model != &m_parentModelList->at( m_selected ) ) )
1389 {
1390 continue;
1391 }
1392
1393 if( model.m_Show )
1394 {
1395 if( m_selected >= 0 && &model == &m_parentModelList->at( m_selected ) )
1396 m_dummySelectedModel = m_dummyFootprint->Models().size();
1397
1398 m_dummyFootprint->Models().push_back( model );
1399 }
1400 }
1401
1403
1404 if( m_extrudedBody && !m_dummyFootprint->HasExtrudedBody() )
1405 m_extrudedBody = nullptr;
1406
1407 if( aReloadRequired )
1408 m_previewPane->ReloadRequest();
1409
1410 m_previewPane->Request_refresh();
1411}
1412
1413
1420
1421
1423{
1424 m_previewPane->JoinBgWorker();
1425 m_dummyFootprint->ClearEmbeddedFiles();
1426
1428 {
1429 for( const auto& [name, file] : m_localEmbeddedFiles->EmbeddedFileMap() )
1430 {
1431 m_dummyFootprint->AddFile(
1432 new EMBEDDED_FILES::EMBEDDED_FILE( *file ) );
1433 }
1434 }
1435}
1436
1437
1439{
1440 KIWAY_HOLDER* kiwayHolder = dynamic_cast<KIWAY_HOLDER*>( wxGetTopLevelParent( this ) );
1441
1442 if( kiwayHolder && kiwayHolder->GetType() == KIWAY_HOLDER::DIALOG )
1443 static_cast<DIALOG_SHIM*>( kiwayHolder )->OnModify();
1444}
SFVEC3F InterpolateModelRotation(const SFVEC3F &aStart, const SFVEC3F &aEnd, float aT)
Interpolate between two model orientations along the shortest arc.
Definition 3d_math.cpp:73
glm::mat4 CalcModelMatrix(const SFVEC3F &aOffset, const SFVEC3F &aRotation, const SFVEC3F &aScale)
Build the model-to-footprint transform used by rendering and picking.
Definition 3d_math.cpp:34
Defines math related functions.
int index
const char * name
@ ERROR_INSIDE
static std::optional< VECTOR2D > normalized(const VECTOR2D &aVec)
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr EDA_IU_SCALE unityScale
Definition base_units.h:131
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ show_board_body
@ options_3drender
@ FPHOLDER
Definition board.h:402
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
Container for design settings for a BOARD object.
void SetEnabledLayers(const LSET &aMask)
Change the bit-mask of enabled layers to aMask.
int GetBoardThickness() const
The full thickness of the board including copper and masks.
void SetBoardThickness(int aThickness)
Manage layers needed to make a physical board.
void RemoveAll()
Delete all items in list and clear the list.
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:80
int ShowModal() override
EDA_3D_ACTIONS.
Implement a canvas based on a wxGLCanvas.
Handle view actions for various 3D canvases.
VECTOR3D m_offset
Definition footprint.h:127
VECTOR3D m_rotation
Definition footprint.h:126
VECTOR3D m_scale
Definition footprint.h:125
VECTOR3D m_Offset
3D model offset (mm)
Definition footprint.h:184
double m_Opacity
Definition footprint.h:185
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition footprint.h:183
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition footprint.h:182
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
A mix in class which holds the location of a wxWindow's KIWAY.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
HOLDER_TYPE GetType() const
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false)
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
static const LSET & FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition lset.cpp:718
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition lset.cpp:725
wxString Result() const
bool Process(const wxString &aString)
static const wxGLAttributes GetAttributesList(ANTIALIASING_MODE aAntiAliasingMode, bool aAlpha=false)
Get a list of attributes to pass to wxGLCanvas.
Definition pad.h:61
PAD_ATTRIB GetAttribute() const
Definition pad.h:560
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:1240
PANEL_PREVIEW_3D_MODEL_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
wxString formatOffsetValue(double aValue)
void onAlign(wxCommandEvent &aEvent) override
void onMouseWheelRot(wxMouseEvent &event) override
void View3DSettings(wxCommandEvent &event) override
size_t m_dummySelectedModel
Selected model in the dummy footprint.
void onOpacitySlider(wxCommandEvent &event) override
void setBodyStyleView(wxCommandEvent &event) override
void hoverAlignment(const std::optional< RAY > &aRay)
void onMouseWheelScale(wxMouseEvent &event) override
EDA_3D_VIEWER_SETTINGS::RENDER_SETTINGS m_initialRender
The 3d viewer Render initial settings (must be saved and restored)
std::vector< SFVEC3F > m_alignFaceTriangles
Picked face, kept until the move finishes.
void UpdateDummyFootprint(bool aRelaodRequired=true)
Copy shapes from the current shape list which are flagged for preview to the copy of footprint that i...
wxString formatScaleValue(double aValue)
void doIncrementRotation(wxSpinEvent &aEvent, double aSign)
void loadSettings()
Load 3D relevant settings from the user configuration.
glm::mat4 alignmentModelMatrix() const
Transform carrying the selected model's geometry into world units.
std::optional< bool > m_alignBodyShown
void onAlignKey(wxKeyEvent &aEvent)
void onAlignUpdateUI(wxUpdateUIEvent &aEvent) override
void OnMenuEvent(wxMenuEvent &aEvent)
The TOOL_DISPATCHER needs these to work around some issues in wxWidgets where the menu events aren't ...
void onUnitsChanged(wxCommandEvent &aEvent)
void doIncrementOffset(wxSpinEvent &aEvent, double aSign)
ALIGN_STATE m_alignState
Index into m_parentInfoList.
void startAlignmentAnimation(const VECTOR3D &aRotation, const VECTOR3D &aOffset)
Slide the preview from its current placement to the solved one.
std::vector< SFVEC3F > alignmentRegionTriangles(size_t aRegion) const
Model-space triangle soup of one region, for the selection overlay.
PANEL_PREVIEW_3D_MODEL(wxWindow *aParent, PCB_BASE_FRAME *aFrame, FOOTPRINT *aFootprint, std::vector< FP_3DMODEL > *aParentModelList)
std::vector< MODEL_ALIGN::REGION > m_alignRegions
void onAlignAnimation(wxTimerEvent &aEvent)
void updateAlignmentOverlays()
Push the selection and roll-over overlays to the canvas.
std::optional< size_t > alignmentRegionAt(const RAY &aRay)
Region under the ray, as an index into m_alignRegions.
std::vector< SFVEC3F > alignmentPadTriangles(const PAD &aPad) const
World-space triangle soup covering a pad's copper shape.
void SetEmbeddedFilesDelegate(EMBEDDED_FILES *aDelegate)
EXTRUDED_3D_BODY * m_extrudedBody
void SetExtrusionTransformMode(EXTRUDED_3D_BODY *aBody)
void doIncrementScale(wxSpinEvent &aEvent, double aSign)
bool pickAlignment(const RAY &aRay)
void updateOrientation(wxCommandEvent &event) override
It will receive the events from editing the fields.
wxString formatRotationValue(double aValue)
void onMouseWheelOffset(wxMouseEvent &event) override
std::optional< size_t > m_alignHoverRegion
std::optional< std::bitset< LAYER_3D_END > > m_alignLayers
std::unique_ptr< NL_FOOTPRINT_PROPERTIES_PLUGIN > m_spaceMouse
std::vector< MODEL_ALIGN::ALIGN_SOLUTION > m_alignSolutions
std::map< std::array< unsigned int, 2 >, size_t > m_alignTriangleRegions
void onPanelShownEvent(wxCommandEvent &aEvent)
EMBEDDED_FILES * m_localEmbeddedFiles
void SetSelectedModel(int idx)
Set the currently selected index in the model list so that the scale/rotation/offset controls can be ...
std::vector< FP_3DMODEL > * m_parentModelList
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
BOARD * GetBoard() const
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Return the BOARD_DESIGN_SETTINGS for the open project.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
RAII class that sets an value at construction and resets it to the original value at destruction.
void GetTriangle(int index, VECTOR2I &a, VECTOR2I &b, VECTOR2I &c) const
Represent a set of closed polygons.
virtual void CacheTriangulation(bool aSimplify=false, const TASK_SUBMITTER &aSubmitter={})
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
unsigned int TriangulatedPolyCount() const
Return the number of triangulated polygons.
wxTextCtrl wrapper to handle math expression evaluation.
void SetValue(const wxString &aValue) override
Set a new value in evaluator buffer and display it in the wxTextCtrl.
TOOL_MANAGER * m_toolManager
TOOL_DISPATCHER * m_toolDispatcher
ACTIONS * m_actions
Master controller class:
EDA_UNITS GetUserUnits() const
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
An extension of WX_TEXT_ENTRY_DIALOG that uses UNIT_BINDER to request a dimension (e....
int GetValue()
Return the value in internal units.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static const std::string StepFileAbrvExtension
static const std::string StepFileExtension
static const std::string StepZFileAbrvExtension
std::string source
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:582
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:581
@ LAYER_3D_BOARD
Definition layer_ids.h:576
@ LAYER_3D_PLATED_BARRELS
Definition layer_ids.h:646
@ LAYER_3D_COPPER_TOP
Definition layer_ids.h:577
@ LAYER_3D_SOLDERPASTE
Definition layer_ids.h:583
@ LAYER_3D_COPPER_BOTTOM
Definition layer_ids.h:578
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:60
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue to a double.
KICOMMON_API wxString GetText(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
std::vector< REGION > BuildRegions(const S3DMODEL &aModel, const glm::dvec3 &aScale)
std::vector< PAD > BuildPadGroup(const std::vector< PAD > &aPads, size_t aClickedPad)
Return the clicked pad first, followed by its congruent, deduplicated peers.
std::vector< ALIGN_SOLUTION > SolveAlignment(const std::vector< REGION > &aRegions, size_t aSeed, const std::vector< PAD > &aPadGroup, const std::vector< PAD > &aAllPads, const glm::dvec3 &aCurrentRotation)
The first element of aPadGroup must be the clicked pad; all coordinates are millimetres,...
declaration of the nl_footprint_properties_plugin class
Declaration of the cogl_att_list class.
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ PTH
Plated through hole pad.
Definition padstack.h:97
static double normalizeRotation(double aRotation)
Normalize a rotation in degrees to the half-open range (-MAX_ROTATION, MAX_ROTATION].
static constexpr int ALIGN_ANIMATION_INTERVAL_MS
static constexpr double ALIGN_ANIMATION_SECONDS
Duration and frame interval of the slide from the old placement to the aligned one.
static double rotationFromString(const wxString &aValue)
static wxString evaluateTextCtrl(const wxString &aValue)
#define MAX_SCALE
#define MAX_ROTATION
#define MAX_OFFSET
#define OFFSET_INCREMENT_MM_FINE
#define OFFSET_INCREMENT_MIL
#define SCALE_INCREMENT
#define ROTATION_INCREMENT_FINE
#define OFFSET_INCREMENT_MM
#define ROTATION_INCREMENT
#define OFFSET_INCREMENT_MIL_FINE
#define SCALE_INCREMENT_FINE
#define PANEL_PREVIEW_3D_MODEL_ID
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
std::shared_ptr< PNS_LOG_VIEWER_OVERLAY > overlay
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
T * GetAppSettings(const char *aFilename)
const int scale
A translucent triangle soup drawn over the scene.
glm::dvec3 rotation
GUI degrees of Rz * Ry * Rx; negate when storing in FP_3DMODEL.
glm::dvec3 offset
Footprint-local millimetres, Y up.
std::string number
PAD_ATTRIBUTE attribute
glm::dvec2 position
Footprint-local millimetres, Y up.
double rotation
Degrees in the Y-up frame.
std::vector< std::array< unsigned int, 3 > > triangles
std::vector< glm::dvec3 > vertices
Scaled model coordinates, millimetres.
Definition ray.h:59
Store the a model based on meshes and materials.
Definition c3dmodel.h:111
KIBIS_MODEL * model
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR3< double > VECTOR3D
Definition vector3.h:230
bool compareFileExtensions(const std::string &aExtension, const std::vector< std::string > &aReference, bool aCaseSensitive)
Compare the given extension against the reference extensions to see if it matches any of the referenc...
Definition of file extensions used in Kicad.
glm::vec3 SFVEC3F
Definition xv3d_types.h:40
glm::vec4 SFVEC4F
Definition xv3d_types.h:42