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 <pcb_3d_body.h>
43#include <3d_math.h>
44#include <core/profile.h>
49#include <scoped_set_reset.h>
51#include <wx/filename.h>
52#include <cmath>
53#include <limits>
54#include <lset.h>
55#include <pgm_base.h>
56#include <project_pcb.h>
59#include <widgets/wx_infobar.h>
63
64#if defined(__linux__) || defined(__FreeBSD__)
66#else
68#endif
69
71static constexpr double ALIGN_ANIMATION_SECONDS = 1.0;
72static constexpr int ALIGN_ANIMATION_INTERVAL_MS = 16;
73
74
75static wxString evaluateTextCtrl( const wxString& aValue )
76{
77 // NUMERIC_EVALUATOR doesn't handle UTF-8 multi-byte characters properly,
78 // so skip evaluation if the string contains non-ASCII characters (e.g., degree symbols)
79 for( wxUniChar c : aValue )
80 {
81 if( !c.IsAscii() )
82 return aValue;
83 }
84
85 // Attempt to evaluate formula; if successful return result, otherwise return original
87
88 if( eval.Process( aValue ) )
89 return eval.Result();
90
91 return aValue;
92}
93
94
101static double normalizeRotation( double aRotation )
102{
103 double normalized = std::fmod( aRotation, 2.0 * MAX_ROTATION );
104
105 if( normalized <= -MAX_ROTATION )
106 normalized += 2.0 * MAX_ROTATION;
107 else if( normalized > MAX_ROTATION )
108 normalized -= 2.0 * MAX_ROTATION;
109
110 if( normalized == -0.0 )
111 normalized = 0.0;
112
113 return normalized;
114}
115
116
117static double rotationFromString( const wxString& aValue )
118{
120
121 return normalizeRotation( rotation );
122}
123
124
126 std::vector<FP_3DMODEL>* aParentModelList ) :
128 m_parentFrame( aFrame ),
129 m_previewPane( nullptr ),
130 m_infobar( nullptr ),
131 m_alignmentInfoBar( nullptr ),
135{
136 m_userUnits = m_parentFrame->GetUserUnits();
137
138 m_dummyBoard = new BOARD();
139
140 m_dummyBoard->SetProject( &aFrame->Prj(), true );
141 m_dummyBoard->SetEmbeddedFilesDelegate( aFrame->GetBoard() );
142
143 // This board will only be used to hold a footprint for viewing
144 m_dummyBoard->SetBoardUse( BOARD_USE::FPHOLDER );
145
146 BOARD_DESIGN_SETTINGS& dummyBoard_bds = m_dummyBoard->GetDesignSettings();
147 dummyBoard_bds.SetBoardThickness( aFrame->GetDesignSettings().GetBoardThickness() );
148 dummyBoard_bds.SetEnabledLayers( LSET::FrontMask() | LSET::BackMask() );
149 BOARD_STACKUP& dummy_board_stackup = dummyBoard_bds.GetStackupDescriptor();
150 dummy_board_stackup.RemoveAll();
151 dummy_board_stackup.BuildDefaultStackupList( &dummyBoard_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" ), wxT( "Unknown exception during SpaceMouse initialization" ) );
240 }
241
242 m_boardAdapter.SetBoard( m_dummyBoard );
243 m_boardAdapter.m_IsBoardView = false;
244
245 // Force display 3D models, regardless the 3D viewer options.
246 m_boardAdapter.m_IsPreviewer = true;
247
248 loadSettings();
249
250 // Create the manager
252 m_toolManager->SetEnvironment( m_dummyBoard, nullptr, nullptr, nullptr, this );
253
256 m_previewPane->SetEventDispatcher( m_toolDispatcher );
257
258 // Register tools
259 m_toolManager->RegisterTool( new EDA_3D_CONTROLLER );
260 m_toolManager->InitTools();
261
262 // Run the viewer control tool, it is supposed to be always active
263 m_toolManager->InvokeTool( "3DViewer.Control" );
264
265 m_infobar = new WX_INFOBAR( this );
266 m_previewPane->SetInfoBar( m_infobar );
267 m_alignmentInfoBar = new WX_INFOBAR( this );
268
269 m_SizerPanelView->Add( m_infobar, 0, wxEXPAND, 0 );
270 m_SizerPanelView->Add( m_alignmentInfoBar, 0, wxEXPAND, 0 );
271 m_SizerPanelView->Add( m_previewPane, 1, wxEXPAND, 5 );
272
273 for( wxEventType eventType : { wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT } )
274 Connect( eventType, wxMenuEventHandler( PANEL_PREVIEW_3D_MODEL::OnMenuEvent ), nullptr, this );
275
276 aFrame->Connect( EDA_EVT_UNITS_CHANGED, wxCommandEventHandler( PANEL_PREVIEW_3D_MODEL::onUnitsChanged ),
277 nullptr, this );
278
279 Bind( wxCUSTOM_PANEL_SHOWN_EVENT, &PANEL_PREVIEW_3D_MODEL::onPanelShownEvent, this );
280 Bind( wxEVT_CHAR_HOOK, &PANEL_PREVIEW_3D_MODEL::onAlignKey, this );
281 m_previewPane->Bind( wxEVT_CHAR_HOOK, &PANEL_PREVIEW_3D_MODEL::onAlignKey, this );
282
283 m_alignAnimTimer.SetOwner( this );
284 Bind( wxEVT_TIMER, &PANEL_PREVIEW_3D_MODEL::onAlignAnimation, this, m_alignAnimTimer.GetId() );
285}
286
287
289{
290 m_alignAnimTimer.Stop();
291 m_previewPane->SetPickHandler( {} );
292 m_previewPane->SetHoverHandler( {} );
294
295 // Shutdown all running tools
296 if( m_toolManager )
297 m_toolManager->ShutdownAllTools();
298
299 m_spaceMouse.reset();
300
301 // Stop dispatching events before destroying the tools and their menus.
302 m_previewPane->SetEventDispatcher( nullptr );
303 delete m_toolDispatcher;
304 m_toolDispatcher = nullptr;
305
306 delete m_toolManager;
307 delete m_actions;
308
309 // Restore the 3D viewer Render settings, that can be modified by the panel tools, and
310 // persist the projection, matching how the 3D viewer frame saves its camera settings.
311 if( m_boardAdapter.m_Cfg )
312 {
313 m_boardAdapter.m_Cfg->m_Render = m_initialRender;
314 m_boardAdapter.m_Cfg->m_Preview.projection_mode = m_currentCamera.GetProjection();
315 }
316
317 // The canvas must stop its rendering worker before the board is destroyed.
318 delete m_previewPane;
319 delete m_dummyBoard;
320}
321
322
323void PANEL_PREVIEW_3D_MODEL::OnMenuEvent( wxMenuEvent& aEvent )
324{
325 if( !m_toolDispatcher )
326 aEvent.Skip();
327 else
328 m_toolDispatcher->DispatchWxEvent( aEvent );
329}
330
331
333{
334 wxCHECK_RET( m_previewPane, wxT( "Cannot load settings to null canvas" ) );
335
336 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
337
338 // TODO(JE) use all control options
339 m_boardAdapter.m_MousewheelPanning = settings->m_Input.scroll_modifier_zoom != 0;
340
342
344 {
345 // Save the 3D viewer render settings, to restore it after closing the preview
346 m_initialRender = cfg->m_Render;
347
348 m_boardAdapter.m_Cfg = cfg;
349
350 m_previewPane->SetAnimationEnabled( cfg->m_Camera.animation_enabled );
351 m_previewPane->SetMovingSpeedMultiplier( cfg->m_Camera.moving_speed_multiplier );
352 projectionMode = cfg->m_Preview.projection_mode;
353 }
354
355 m_previewPane->SetProjectionMode( projectionMode );
356}
357
358
360{
361 return wxString::Format( wxT( "%.4f" ),
362 aValue );
363}
364
365
367{
368 // Sigh. Did we really need differentiated +/- 0.0?
369 if( aValue == -0.0 )
370 aValue = 0.0;
371
372 return wxString::Format( wxT( "%.2f%s" ),
373 aValue,
375}
376
377
379{
380 // Convert from internal units (mm) to user units
382 aValue /= 25.4;
383 else if( m_userUnits == EDA_UNITS::MILS )
384 aValue /= 25.4 / 1e3;
385
386 return wxString::Format( wxT( "%.6f%s" ),
387 aValue,
389}
390
391
393{
395 || m_selected >= (int) m_parentModelList->size() )
396 {
397 return false;
398 }
399
401 const std::string extension = wxFileName( model.m_Filename ).GetExt().utf8_string();
402
403 return model.m_Show
406 && std::isfinite( model.m_Scale.x ) && model.m_Scale.x != 0.0
407 && std::isfinite( model.m_Scale.y ) && model.m_Scale.y != 0.0
408 && std::isfinite( model.m_Scale.z ) && model.m_Scale.z != 0.0;
409}
410
411
412void PANEL_PREVIEW_3D_MODEL::onAlignUpdateUI( wxUpdateUIEvent& aEvent )
413{
414 aEvent.Enable( canAlign() );
415}
416
417
419{
420 if( !canAlign() )
421 return nullptr;
422
423 wxString basePath;
424
425 try
426 {
427 auto row = PROJECT_PCB::FootprintLibAdapter( m_dummyBoard->GetProject() )
428 ->GetRow( m_dummyFootprint->GetFPID().GetLibNickname() );
429
430 if( row )
431 basePath = LIBRARY_MANAGER::GetFullURI( *row, true );
432 }
433 catch( const IO_ERROR& )
434 {
435 // Models may have absolute paths even when the footprint library is unavailable.
436 }
437
439 return m_boardAdapter.Get3dCacheManager()->GetModel( m_parentModelList->at( m_selected ).m_Filename,
440 basePath,
441 { m_dummyFootprint->GetEmbeddedFiles(),
442 m_dummyBoard->GetEmbeddedFiles() } );
443}
444
445
451
452
454{
455 m_previewPane->JoinBgWorker();
456
457 if( m_alignBodyShown )
458 {
459 if( PCB_3D_BODY* body = m_dummyFootprint->GetBody() )
460 body->MutableExtruded()->m_show = *m_alignBodyShown;
461
462 m_alignBodyShown.reset();
463 }
464
465 m_boardAdapter.SetVisibilityOverride( std::nullopt );
466 m_alignLayers.reset();
467}
468
469
471{
472 m_previewPane->SetPickHandler( {} );
473 m_previewPane->SetHoverHandler( {} );
475 m_alignButton->SetLabel( _( "Align" ) );
476 Layout();
478}
479
480
482{
483 if( !m_alignmentInfoBar->IsShown() )
484 return;
485
486 // Dismiss() does not hide the bar when its notebook page is already hidden.
487 if( m_alignmentInfoBar->IsShownOnScreen() )
488 m_alignmentInfoBar->Dismiss();
489 else
490 m_alignmentInfoBar->Hide();
491
492 Layout();
493}
494
495
497{
499
500 if( m_alignAnimating || !m_alignFaceTriangles.empty() )
501 {
503 m_alignFaceTriangles.clear();
504 m_alignHoverRegion.reset();
505 m_alignHoverPad = nullptr;
507 }
508
510 return;
511
512 const bool picking = m_alignState != ALIGN_STATE::SOLVED;
514 m_alignRegions.clear();
516 m_alignSolutions.clear();
517 m_alignGeometry = nullptr;
518
519 if( picking )
520 stopPicking();
521}
522
523
524void PANEL_PREVIEW_3D_MODEL::onAlignKey( wxKeyEvent& aEvent )
525{
526 if( aEvent.GetKeyCode() == WXK_ESCAPE
528 {
530 return;
531 }
532
533 aEvent.Skip();
534}
535
536
537void PANEL_PREVIEW_3D_MODEL::onAlign( wxCommandEvent& aEvent )
538{
540 {
542 return;
543 }
544
545 if( !canAlign() )
546 return;
547
549 {
552 return;
553 }
554
555 const S3DMODEL* geometry = alignmentModel();
556
557 if( !geometry )
558 return;
559
560 const auto& scale = m_parentModelList->at( m_selected ).m_Scale;
561 m_alignRegions = MODEL_ALIGN::BuildRegions( *geometry, glm::dvec3( scale.x, scale.y, scale.z ) );
562
563 if( m_alignRegions.empty() )
564 return;
565
566 m_alignGeometry = geometry;
568
569 for( size_t i = 0; i < m_alignRegions.size(); ++i )
570 {
571 for( const std::array<unsigned int, 2>& source : m_alignRegions[i].sourceTriangles )
572 m_alignTriangleRegions.emplace( source, i );
573 }
574
575 if( PCB_3D_BODY* body = m_dummyFootprint->GetBody() )
576 {
577 m_alignBodyShown = body->GetExtruded()->m_show;
578 body->MutableExtruded()->m_show = false;
579 }
580
581 m_alignLayers = m_boardAdapter.GetVisibleLayers();
582 auto layers = *m_alignLayers;
583
586 {
587 layers.reset( layer );
588 }
589
590 m_boardAdapter.SetVisibilityOverride( layers );
592 m_alignButton->SetLabel( _( "Cancel alignment" ) );
593 Layout();
594 m_previewPane->SetPickHandler( [this]( const RAY& aRay ) { return pickAlignment( aRay ); } );
595 m_previewPane->SetHoverHandler( [this]( const std::optional<RAY>& aRay ) { hoverAlignment( aRay ); } );
596 m_alignmentInfoBar->ShowMessage( _( "Click the model face that sits on the board. Press Esc to cancel." ),
597 wxICON_INFORMATION );
599}
600
601
603{
604 m_previewPane->JoinBgWorker();
605
606 if( !canAlign() )
607 {
609 return true;
610 }
611
613 {
614 std::optional<size_t> region = alignmentRegionAt( aRay );
615
616 if( !region )
617 return true;
618
619 m_alignSeed = *region;
621 m_alignHoverRegion.reset();
623 auto layers = *m_alignLayers;
624 layers.set( LAYER_3D_COPPER_TOP );
625 layers.reset( LAYER_3D_COPPER_BOTTOM );
626 layers.reset( LAYER_3D_SOLDERMASK_TOP );
627 layers.reset( LAYER_3D_SOLDERPASTE );
628 m_boardAdapter.SetVisibilityOverride( layers );
629 m_alignmentInfoBar->ShowMessage( _( "Click the footprint pad for that contact. Press Esc to cancel." ),
630 wxICON_INFORMATION );
634 return true;
635 }
636
638 return false;
639
640 PAD* clicked = m_previewPane->PickFootprintPad( aRay, *m_dummyFootprint );
641
642 if( !clicked || ( clicked->GetAttribute() != PAD_ATTRIB::SMD
643 && clicked->GetAttribute() != PAD_ATTRIB::PTH ) )
644 {
645 return true;
646 }
647
648 std::vector<MODEL_ALIGN::PAD> pads;
649 size_t clickedIndex = 0;
650 const VECTOR2I origin = m_dummyFootprint->GetPosition();
651
652 for( const PAD* pad : m_dummyFootprint->Pads() )
653 {
654 if( pad == clicked )
655 clickedIndex = pads.size();
656
657 const VECTOR2I pos = pad->GetPosition() - origin;
658 const VECTOR2I size = pad->GetSize( F_Cu );
659 const VECTOR2I drill = pad->GetDrillSize();
660 MODEL_ALIGN::PAD item;
661 item.number = pad->GetNumber().utf8_string();
662 item.position = glm::dvec2( pos.x, -pos.y ) / pcbIUScale.IU_PER_MM;
663 item.size = glm::dvec2( size.x, size.y ) / pcbIUScale.IU_PER_MM;
664 item.drill = glm::dvec2( drill.x, drill.y ) / pcbIUScale.IU_PER_MM;
665 item.rotation = pad->GetOrientationDegrees();
669 pads.push_back( std::move( item ) );
670 }
671
672 const VECTOR3D& rotation = m_parentModelList->at( m_selected ).m_Rotation;
674 MODEL_ALIGN::BuildPadGroup( pads, clickedIndex ),
675 pads, glm::dvec3( -rotation.x, -rotation.y, -rotation.z ) );
676
677 if( m_alignSolutions.empty() )
678 {
680 return true;
681 }
682
684 m_alignSolution = 0;
685 m_alignHoverPad = nullptr;
686 stopPicking();
688
689 if( m_alignSolutions.size() == 1 )
690 {
691 // Nothing to cycle through, so leave the session behind and keep the result.
693 m_alignRegions.clear();
695 m_alignSolutions.clear();
696 m_alignGeometry = nullptr;
697 }
698
699 return true;
700}
701
702
704{
707 const VECTOR3D fromRotation = model.m_Rotation;
708 const VECTOR3D fromOffset = model.m_Offset;
709
710 xrot->ChangeValue( formatRotationValue( normalizeRotation( solution.rotation.x ) ) );
711 yrot->ChangeValue( formatRotationValue( normalizeRotation( solution.rotation.y ) ) );
712 zrot->ChangeValue( formatRotationValue( normalizeRotation( solution.rotation.z ) ) );
713 xoff->ChangeValue( formatOffsetValue( solution.offset.x ) );
714 yoff->ChangeValue( formatOffsetValue( solution.offset.y ) );
715 zoff->ChangeValue( formatOffsetValue( solution.offset.z ) );
716
717 {
718 SCOPED_SET_RESET<bool> updating( m_alignUpdating, true );
719 wxCommandEvent event;
720 updateOrientation( event );
721 }
722
723 if( m_alignSeed < m_alignRegions.size() )
725
726 startAlignmentAnimation( fromRotation, fromOffset );
727
728 if( m_alignSolutions.size() > 1 )
729 {
730 m_alignmentInfoBar->ShowMessage( wxString::Format( _( "Solution %d of %d, press Align to cycle." ),
731 (int) m_alignSolution + 1,
732 (int) m_alignSolutions.size() ),
733 wxICON_INFORMATION );
734 }
735 else
736 {
738 }
739}
740
741
743{
745 const VECTOR3D& rotation = m_alignAnimating ? m_alignAnimRotation : model.m_Rotation;
746 const VECTOR3D& offset = m_alignAnimating ? m_alignAnimOffset : model.m_Offset;
747
748 // The region vertices already carry the model scale.
749 return m_boardAdapter.GetFootprintMatrix( *m_dummyFootprint )
750 * CalcModelMatrix( SFVEC3F( offset.x, offset.y, offset.z ),
751 SFVEC3F( rotation.x, rotation.y, rotation.z ), SFVEC3F( 1.0f ) );
752}
753
754
755std::vector<SFVEC3F> PANEL_PREVIEW_3D_MODEL::alignmentRegionTriangles( size_t aRegion ) const
756{
757 const MODEL_ALIGN::REGION& region = m_alignRegions.at( aRegion );
758 std::vector<SFVEC3F> triangles;
759
760 triangles.reserve( region.triangles.size() * 3 );
761
762 for( const std::array<unsigned int, 3>& triangle : region.triangles )
763 {
764 for( unsigned int index : triangle )
765 {
766 const glm::dvec3& vertex = region.vertices[index];
767 triangles.emplace_back( vertex.x, vertex.y, vertex.z );
768 }
769 }
770
771 return triangles;
772}
773
774
775std::vector<SFVEC3F> PANEL_PREVIEW_3D_MODEL::alignmentPadTriangles( const PAD& aPad ) const
776{
777 const PCB_LAYER_ID layer = m_dummyFootprint->IsFlipped() ? B_Cu : F_Cu;
778 const float units = m_boardAdapter.BiuTo3dUnits();
779 const float z = m_boardAdapter.GetFootprintZPos( m_dummyFootprint->IsFlipped() );
780 SHAPE_POLY_SET outline = *aPad.GetEffectivePolygon( layer, ERROR_INSIDE );
781 std::vector<SFVEC3F> triangles;
782
783 outline.CacheTriangulation( false );
784
785 for( unsigned int i = 0; i < outline.TriangulatedPolyCount(); ++i )
786 {
787 const SHAPE_POLY_SET::TRIANGULATED_POLYGON* polygon = outline.TriangulatedPolygon( i );
788
789 for( size_t j = 0; j < polygon->GetTriangleCount(); ++j )
790 {
791 VECTOR2I a;
792 VECTOR2I b;
793 VECTOR2I c;
794 polygon->GetTriangle( (int) j, a, b, c );
795
796 for( const VECTOR2I& point : { a, b, c } )
797 triangles.emplace_back( point.x * units, -point.y * units, z );
798 }
799 }
800
801 return triangles;
802}
803
804
806{
807 std::vector<EDA_3D_CANVAS::OVERLAY> overlays;
808
810 {
811 m_previewPane->SetOverlays( {} );
812 m_previewPane->Request_refresh();
813 return;
814 }
815
816 if( !m_alignFaceTriangles.empty() )
817 {
819 overlay.transform = alignmentModelMatrix();
820 overlay.vertices = m_alignFaceTriangles;
821 overlay.color = SFVEC4F( 0.05f, 0.85f, 0.25f, 0.35f );
822 overlay.alwaysVisible = true;
823 overlays.push_back( std::move( overlay ) );
824 }
825
827 {
829 overlay.transform = alignmentModelMatrix();
831 overlay.color = SFVEC4F( 0.35f, 1.0f, 0.45f, 0.30f );
832 overlays.push_back( std::move( overlay ) );
833 }
834
836 {
839 overlay.color = SFVEC4F( 0.35f, 1.0f, 0.45f, 0.45f );
840 overlay.alwaysVisible = true;
841 overlays.push_back( std::move( overlay ) );
842 }
843
844 m_previewPane->SetOverlays( std::move( overlays ) );
845 m_previewPane->Request_refresh();
846}
847
848
849std::optional<size_t> PANEL_PREVIEW_3D_MODEL::alignmentRegionAt( const RAY& aRay )
850{
851 if( !m_alignGeometry || !canAlign() )
852 return std::nullopt;
853
854 std::optional<EDA_3D_CANVAS::MODEL_HIT> hit = m_previewPane->PickModel( aRay, *m_alignGeometry,
857
858 if( !hit )
859 return std::nullopt;
860
861 auto region = m_alignTriangleRegions.find( { hit->mesh, hit->triangle / 3 } );
862
863 if( region == m_alignTriangleRegions.end() )
864 return std::nullopt;
865
866 return region->second;
867}
868
869
870void PANEL_PREVIEW_3D_MODEL::hoverAlignment( const std::optional<RAY>& aRay )
871{
872 std::optional<size_t> region;
873 const PAD* pad = nullptr;
874
875 if( aRay && canAlign() )
876 {
878 region = alignmentRegionAt( *aRay );
880 pad = m_previewPane->PickFootprintPad( *aRay, *m_dummyFootprint );
881 }
882
883 if( pad && pad->GetAttribute() != PAD_ATTRIB::SMD && pad->GetAttribute() != PAD_ATTRIB::PTH )
884 pad = nullptr;
885
886 if( region == m_alignHoverRegion && pad == m_alignHoverPad )
887 return;
888
889 m_alignHoverRegion = region;
892}
893
894
896{
898
899 m_alignAnimFromRotation = aRotation;
900 m_alignAnimFromOffset = aOffset;
901 m_alignAnimRotation = aRotation;
902 m_alignAnimOffset = aOffset;
903
904 if( !m_previewPane->GetAnimationEnabled()
905 || ( aRotation == model.m_Rotation && aOffset == model.m_Offset ) )
906 {
907 m_alignFaceTriangles.clear();
909 return;
910 }
911
912 m_alignAnimating = true;
914 m_alignAnimTimer.Start( ALIGN_ANIMATION_INTERVAL_MS, wxTIMER_CONTINUOUS );
916}
917
918
924
925
927{
929}
930
931
933{
934 if( !m_alignAnimating )
935 return;
936
938 const double elapsed = ( GetRunningMicroSecs() - m_alignAnimStart ) / 1e6;
939 const double t = std::clamp( elapsed / ALIGN_ANIMATION_SECONDS, 0.0, 1.0 );
940 const float eased = (float) ( t * t * ( 3.0 - 2.0 * t ) );
941
942 const SFVEC3F rotation = InterpolateModelRotation(
944 SFVEC3F( model.m_Rotation.x, model.m_Rotation.y, model.m_Rotation.z ), eased );
945
946 m_alignAnimRotation = VECTOR3D( rotation.x, rotation.y, rotation.z );
947 m_alignAnimOffset = VECTOR3D( std::lerp( m_alignAnimFromOffset.x, model.m_Offset.x, (double) eased ),
948 std::lerp( m_alignAnimFromOffset.y, model.m_Offset.y, (double) eased ),
949 std::lerp( m_alignAnimFromOffset.z, model.m_Offset.z, (double) eased ) );
950
951 if( t >= 1.0 )
952 {
953 // Land on the stored angles rather than on a re-decomposed equivalent of them.
954 m_alignAnimRotation = model.m_Rotation;
955 m_alignAnimOffset = model.m_Offset;
956 }
957
959 {
962 preview.m_Offset = m_alignAnimOffset;
963 }
964
965 if( t >= 1.0 )
966 {
968 m_alignFaceTriangles.clear();
969 }
970
972}
973
974
976{
978
979 m_extrudedBody = nullptr;
980 m_opacity->Enable( true );
981
982 if( m_parentModelList && idx >= 0 && idx < (int) m_parentModelList->size() )
983 {
984 m_selected = idx;
985 const FP_3DMODEL& modelInfo = m_parentModelList->at( (unsigned) m_selected );
986
987 // Use ChangeValue() instead of SetValue(). It's not the user making the change, so we
988 // don't want to generate wxEVT_GRID_CELL_CHANGED events.
989 xscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.x ) );
990 yscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.y ) );
991 zscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.z ) );
992
993 // Rotation is stored in the file as positive-is-CW, but we use positive-is-CCW in the GUI
994 // to match the rest of KiCad
995 xrot->ChangeValue( formatRotationValue( -modelInfo.m_Rotation.x ) );
996 yrot->ChangeValue( formatRotationValue( -modelInfo.m_Rotation.y ) );
997 zrot->ChangeValue( formatRotationValue( -modelInfo.m_Rotation.z ) );
998
999 xoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.x ) );
1000 yoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.y ) );
1001 zoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.z ) );
1002
1003 m_opacity->SetValue( modelInfo.m_Opacity * 100.0 );
1004 }
1005 else
1006 {
1007 m_selected = -1;
1008
1009 xscale->ChangeValue( wxEmptyString );
1010 yscale->ChangeValue( wxEmptyString );
1011 zscale->ChangeValue( wxEmptyString );
1012
1013 xrot->ChangeValue( wxEmptyString );
1014 yrot->ChangeValue( wxEmptyString );
1015 zrot->ChangeValue( wxEmptyString );
1016
1017 xoff->ChangeValue( wxEmptyString );
1018 yoff->ChangeValue( wxEmptyString );
1019 zoff->ChangeValue( wxEmptyString );
1020
1021 m_opacity->SetValue( 100 );
1022 }
1023}
1024
1025
1027{
1029 m_extrudedBody = aBody;
1030
1031 if( aBody )
1032 {
1033 const EXTRUDED_3D_BODY& body = *aBody->GetExtruded();
1034
1035 xscale->ChangeValue( formatScaleValue( body.m_scale.x ) );
1036 yscale->ChangeValue( formatScaleValue( body.m_scale.y ) );
1037 zscale->ChangeValue( formatScaleValue( body.m_scale.z ) );
1038
1039 xrot->ChangeValue( formatRotationValue( -body.m_rotation.x ) );
1040 yrot->ChangeValue( formatRotationValue( -body.m_rotation.y ) );
1041 zrot->ChangeValue( formatRotationValue( -body.m_rotation.z ) );
1042
1043 xoff->ChangeValue( formatOffsetValue( body.m_offset.x ) );
1044 yoff->ChangeValue( formatOffsetValue( body.m_offset.y ) );
1045 zoff->ChangeValue( formatOffsetValue( body.m_offset.z ) );
1046
1047 m_opacity->SetValue( 100 );
1048 m_opacity->Enable( false );
1049 }
1050 else
1051 {
1053 }
1054}
1055
1056
1058{
1059 if( m_extrudedBody )
1060 {
1061 EXTRUDED_3D_BODY& body = *m_extrudedBody->MutableExtruded();
1062
1064 evaluateTextCtrl( xscale->GetValue() ) );
1066 evaluateTextCtrl( yscale->GetValue() ) );
1068 evaluateTextCtrl( zscale->GetValue() ) );
1069
1070 body.m_rotation.x = -rotationFromString( evaluateTextCtrl( xrot->GetValue() ) );
1071 body.m_rotation.y = -rotationFromString( evaluateTextCtrl( yrot->GetValue() ) );
1072 body.m_rotation.z = -rotationFromString( evaluateTextCtrl( zrot->GetValue() ) );
1073
1075 evaluateTextCtrl( xoff->GetValue() ) )
1076 / pcbIUScale.IU_PER_MM;
1078 evaluateTextCtrl( yoff->GetValue() ) )
1079 / pcbIUScale.IU_PER_MM;
1081 evaluateTextCtrl( zoff->GetValue() ) )
1082 / pcbIUScale.IU_PER_MM;
1083
1084 UpdateDummyFootprint( true );
1085 onModify();
1086 }
1087 else if( m_parentModelList && m_selected >= 0 && m_selected < (int) m_parentModelList->size() )
1088 {
1089 // Write settings back to the parent
1090 FP_3DMODEL* modelInfo = &m_parentModelList->at( (unsigned) m_selected );
1091 const VECTOR3D previousScale = modelInfo->m_Scale;
1092 const VECTOR3D previousRotation = modelInfo->m_Rotation;
1093 const VECTOR3D previousOffset = modelInfo->m_Offset;
1094
1096 evaluateTextCtrl( xscale->GetValue() ) );
1098 evaluateTextCtrl( yscale->GetValue() ) );
1100 evaluateTextCtrl( zscale->GetValue() ) );
1101
1102 // Rotation is stored in the file as positive-is-CW, but we use positive-is-CCW in the GUI
1103 // to match the rest of KiCad
1104 modelInfo->m_Rotation.x = -rotationFromString( evaluateTextCtrl( xrot->GetValue() ) );
1105 modelInfo->m_Rotation.y = -rotationFromString( evaluateTextCtrl( yrot->GetValue() ) );
1106 modelInfo->m_Rotation.z = -rotationFromString( evaluateTextCtrl( zrot->GetValue() ) );
1107
1109 evaluateTextCtrl( xoff->GetValue() ) )
1110 / pcbIUScale.IU_PER_MM;
1112 evaluateTextCtrl( yoff->GetValue() ) )
1113 / pcbIUScale.IU_PER_MM;
1115 evaluateTextCtrl( zoff->GetValue() ) )
1116 / pcbIUScale.IU_PER_MM;
1117
1118 if( previousScale == modelInfo->m_Scale && previousRotation == modelInfo->m_Rotation
1119 && previousOffset == modelInfo->m_Offset )
1120 {
1121 return;
1122 }
1123
1124 // Update the dummy footprint for the preview
1125 UpdateDummyFootprint( false );
1126 onModify();
1127 }
1128}
1129
1130
1131void PANEL_PREVIEW_3D_MODEL::onOpacitySlider( wxCommandEvent& event )
1132{
1133 if( m_parentModelList && m_selected >= 0 && m_selected < (int) m_parentModelList->size() )
1134 {
1135 // Write settings back to the parent
1136 FP_3DMODEL* modelInfo = &m_parentModelList->at( (unsigned) m_selected );
1137
1138 modelInfo->m_Opacity = m_opacity->GetValue() / 100.0;
1139
1140 // Update the dummy footprint for the preview
1141 UpdateDummyFootprint( false );
1142 onModify();
1143 }
1144}
1145
1146
1147void PANEL_PREVIEW_3D_MODEL::setBodyStyleView( wxCommandEvent& event )
1148{
1150
1151 // turn ON or OFF options to show the board body if OFF, solder paste, soldermask
1152 // and board body are hidden, to allows a good view of the 3D model and its pads.
1154
1155 if( !cfg )
1156 return;
1157
1159
1160 m_previewPane->ReloadRequest();
1161 m_previewPane->Refresh();
1162}
1163
1164
1165void PANEL_PREVIEW_3D_MODEL::View3DSettings( wxCommandEvent& event )
1166{
1167 BOARD_DESIGN_SETTINGS bds = m_dummyBoard->GetDesignSettings();
1168 int thickness = bds.GetBoardThickness();
1169
1170 WX_UNIT_ENTRY_DIALOG dlg( m_parentFrame, _( "3D Preview Options" ), _( "Board thickness:" ), thickness );
1171
1172 if( dlg.ShowModal() != wxID_OK )
1173 return;
1174
1175 bds.SetBoardThickness( dlg.GetValue() );
1176
1177 BOARD_STACKUP& boardStackup = m_dummyBoard->GetDesignSettings().GetStackupDescriptor();
1178 boardStackup.RemoveAll();
1179 boardStackup.BuildDefaultStackupList( &bds, 2 );
1180
1181 UpdateDummyFootprint( true );
1182
1183 m_previewPane->ReloadRequest();
1184 m_previewPane->Refresh();
1185}
1186
1187
1188void PANEL_PREVIEW_3D_MODEL::doIncrementScale( wxSpinEvent& event, double aSign )
1189{
1190 wxSpinButton* spinCtrl = dynamic_cast<wxSpinButton*>( event.GetEventObject() );
1191
1192 wxCHECK( spinCtrl, /* void */ );
1193
1194 wxTextCtrl * textCtrl = xscale;
1195
1196 if( spinCtrl == m_spinYscale )
1197 textCtrl = yscale;
1198 else if( spinCtrl == m_spinZscale )
1199 textCtrl = zscale;
1200
1201 double step = SCALE_INCREMENT;
1202
1203 if( wxGetMouseState().ShiftDown( ) )
1204 step = SCALE_INCREMENT_FINE;
1205
1206 double value = EDA_UNIT_UTILS::UI::DoubleValueFromString( unityScale, EDA_UNITS::UNSCALED, textCtrl->GetValue() );
1207
1208 value += ( step * aSign );
1209 value = std::max( 1/MAX_SCALE, value );
1210 value = std::min( value, MAX_SCALE );
1211
1212 textCtrl->SetValue( formatScaleValue( value ) );
1213}
1214
1215
1216void PANEL_PREVIEW_3D_MODEL::doIncrementRotation( wxSpinEvent& aEvent, double aSign )
1217{
1218 wxSpinButton* spinCtrl = dynamic_cast<wxSpinButton*>( aEvent.GetEventObject() );
1219
1220 wxCHECK( spinCtrl, /* void */ );
1221
1222 wxTextCtrl* textCtrl = xrot;
1223
1224 if( spinCtrl == m_spinYrot )
1225 textCtrl = yrot;
1226 else if( spinCtrl == m_spinZrot )
1227 textCtrl = zrot;
1228
1229 double step = ROTATION_INCREMENT;
1230
1231 if( wxGetMouseState().ShiftDown( ) )
1233
1234 double value = rotationFromString( textCtrl->GetValue() );
1235
1236 value = normalizeRotation( value + step * aSign );
1237
1238 textCtrl->SetValue( formatRotationValue( value ) );
1239}
1240
1241
1242void PANEL_PREVIEW_3D_MODEL::doIncrementOffset( wxSpinEvent& event, double aSign )
1243{
1244 wxSpinButton* spinCtrl = dynamic_cast<wxSpinButton*>( event.GetEventObject() );
1245
1246 wxCHECK( spinCtrl, /* void */ );
1247
1248 wxTextCtrl * textCtrl = xoff;
1249
1250 if( spinCtrl == m_spinYoffset )
1251 textCtrl = yoff;
1252 else if( spinCtrl == m_spinZoffset )
1253 textCtrl = zoff;
1254
1255 double step_mm = OFFSET_INCREMENT_MM;
1256
1257 if( wxGetMouseState().ShiftDown( ) )
1258 step_mm = OFFSET_INCREMENT_MM_FINE;
1259
1261 {
1262 step_mm = 25.4*OFFSET_INCREMENT_MIL/1000;
1263
1264 if( wxGetMouseState().ShiftDown( ) )
1265 step_mm = 25.4*OFFSET_INCREMENT_MIL_FINE/1000;;
1266 }
1267
1268 double value_mm = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, m_userUnits, textCtrl->GetValue() )
1269 / pcbIUScale.IU_PER_MM;
1270
1271 value_mm += ( step_mm * aSign );
1272 value_mm = std::max( -MAX_OFFSET, value_mm );
1273 value_mm = std::min( value_mm, MAX_OFFSET );
1274
1275 textCtrl->SetValue( formatOffsetValue( value_mm ) );
1276}
1277
1278
1280{
1281 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
1282
1283 wxCHECK( textCtrl, /* void */ );
1284
1285 double step = SCALE_INCREMENT;
1286
1287 if( event.ShiftDown( ) )
1288 step = SCALE_INCREMENT_FINE;
1289
1290 if( event.GetWheelRotation() >= 0 )
1291 step = -step;
1292
1293 double value = EDA_UNIT_UTILS::UI::DoubleValueFromString( unityScale, EDA_UNITS::UNSCALED, textCtrl->GetValue() );
1294
1295 value += step;
1296 value = std::max( 1/MAX_SCALE, value );
1297 value = std::min( value, MAX_SCALE );
1298
1299 textCtrl->SetValue( formatScaleValue( value ) );
1300}
1301
1302
1304{
1305 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
1306
1307 wxCHECK( textCtrl, /* void */ );
1308
1309 double step = ROTATION_INCREMENT;
1310
1311 if( event.ShiftDown( ) )
1313
1314 if( event.GetWheelRotation() >= 0 )
1315 step = -step;
1316
1317 double value = rotationFromString( textCtrl->GetValue() );
1318
1319 value = normalizeRotation( value + step );
1320
1321 textCtrl->SetValue( formatRotationValue( value ) );
1322}
1323
1324
1326{
1327 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
1328
1329 wxCHECK( textCtrl, /* void */ );
1330
1331 double step_mm = OFFSET_INCREMENT_MM;
1332
1333 if( event.ShiftDown( ) )
1334 step_mm = OFFSET_INCREMENT_MM_FINE;
1335
1337 {
1338 step_mm = 25.4*OFFSET_INCREMENT_MIL/1000.0;
1339
1340 if( event.ShiftDown( ) )
1341 step_mm = 25.4*OFFSET_INCREMENT_MIL_FINE/1000.0;
1342 }
1343
1344 if( event.GetWheelRotation() >= 0 )
1345 step_mm = -step_mm;
1346
1347 double value_mm = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, m_userUnits, textCtrl->GetValue() )
1348 / pcbIUScale.IU_PER_MM;
1349
1350 value_mm += step_mm;
1351 value_mm = std::max( -MAX_OFFSET, value_mm );
1352 value_mm = std::min( value_mm, MAX_OFFSET );
1353
1354 textCtrl->SetValue( formatOffsetValue( value_mm ) );
1355}
1356
1357
1358void PANEL_PREVIEW_3D_MODEL::onUnitsChanged( wxCommandEvent& aEvent )
1359{
1361 / pcbIUScale.IU_PER_MM;
1363 / pcbIUScale.IU_PER_MM;
1365 / pcbIUScale.IU_PER_MM;
1366
1367 PCB_BASE_FRAME* frame = static_cast<PCB_BASE_FRAME*>( aEvent.GetClientData() );
1368 m_userUnits = frame->GetUserUnits();
1369
1370 xoff->SetValue( formatOffsetValue( xoff_mm ) );
1371 yoff->SetValue( formatOffsetValue( yoff_mm ) );
1372 zoff->SetValue( formatOffsetValue( zoff_mm ) );
1373
1374 aEvent.Skip();
1375}
1376
1377
1378void PANEL_PREVIEW_3D_MODEL::onPanelShownEvent( wxCommandEvent& aEvent )
1379{
1380 if( !aEvent.GetInt() )
1382
1383 if( m_spaceMouse )
1384 m_spaceMouse->SetFocus( static_cast<bool>( aEvent.GetInt() ) );
1385
1386 aEvent.Skip();
1387}
1388
1389
1391{
1392 // The background hit-test rebuild reads the same footprint and embedded files.
1393 m_previewPane->JoinBgWorker();
1394
1395 if( !m_alignUpdating )
1397
1398 m_dummyFootprint->Models().clear();
1399 m_dummySelectedModel = std::numeric_limits<size_t>::max();
1400
1402 {
1405 {
1406 continue;
1407 }
1408
1409 if( model.m_Show )
1410 {
1411 if( m_selected >= 0 && &model == &m_parentModelList->at( m_selected ) )
1412 m_dummySelectedModel = m_dummyFootprint->Models().size();
1413
1414 m_dummyFootprint->Models().push_back( model );
1415 }
1416 }
1417
1419
1420 if( m_extrudedBody && !m_dummyFootprint->HasBody() )
1421 m_extrudedBody = nullptr;
1422
1423 if( aReloadRequired )
1424 m_previewPane->ReloadRequest();
1425
1426 m_previewPane->Request_refresh();
1427}
1428
1429
1436
1437
1439{
1440 m_previewPane->JoinBgWorker();
1441 m_dummyFootprint->ClearEmbeddedFiles();
1442
1444 {
1445 for( const auto& [name, file] : m_localEmbeddedFiles->EmbeddedFileMap() )
1446 m_dummyFootprint->AddFile( new EMBEDDED_FILES::EMBEDDED_FILE( *file ) );
1447 }
1448}
1449
1450
1452{
1453 KIWAY_HOLDER* kiwayHolder = dynamic_cast<KIWAY_HOLDER*>( wxGetTopLevelParent( this ) );
1454
1455 if( kiwayHolder && kiwayHolder->GetType() == KIWAY_HOLDER::DIALOG )
1456 static_cast<DIALOG_SHIM*>( kiwayHolder )->OnModify();
1457}
PROJECTION_TYPE
Camera projection types.
Definition 3d_enums.h:50
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:403
#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.
BOARD_STACKUP & GetStackupDescriptor()
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 pcb_3d_body.h:52
VECTOR3D m_rotation
Definition pcb_3d_body.h:51
VECTOR3D m_Offset
3D model offset (mm)
Definition footprint.h:134
double m_Opacity
Definition footprint.h:135
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition footprint.h:133
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition footprint.h:132
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:1239
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 SetExtrusionTransformMode(PCB_3D_BODY *aBody)
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)
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
A 3D body attached to a footprint (or, later, placed on a board).
Definition pcb_3d_body.h:93
const EXTRUDED_3D_BODY * GetExtruded() const
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:562
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={}, bool aBuildIndexes=false)
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:588
@ LAYER_3D_ADHESIVE
Definition layer_ids.h:590
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:587
@ LAYER_3D_BOARD
Definition layer_ids.h:582
@ LAYER_3D_PLATED_BARRELS
Definition layer_ids.h:652
@ LAYER_3D_COPPER_TOP
Definition layer_ids.h:583
@ LAYER_3D_SOLDERPASTE
Definition layer_ids.h:589
@ LAYER_3D_COPPER_BOTTOM
Definition layer_ids.h:584
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