KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_export_3d_image.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
22#include <wx/sizer.h>
23#include <wx/stattext.h>
24#include <wx/button.h>
25#include <wx/spinctrl.h>
26#include <wx/choice.h>
27#include <wx/bmpbuttn.h>
28#include <wx/statline.h>
29
30DIALOG_EXPORT_3D_IMAGE::DIALOG_EXPORT_3D_IMAGE( wxWindow* aParent, const wxSize& aCanvasSize,
32 DIALOG_SHIM( aParent, wxID_ANY, _( "Export 3D View" ), wxDefaultPosition, wxDefaultSize,
33 wxDEFAULT_DIALOG_STYLE ),
34 m_cfg( aCfg ),
35 m_originalSize( aCanvasSize ),
36 m_width( aCfg && aCfg->width > 0 ? aCfg->width : aCanvasSize.GetWidth() ),
37 m_height( aCfg && aCfg->height > 0 ? aCfg->height : aCanvasSize.GetHeight() ),
38 m_xResolution( aCfg ? aCfg->x_resolution : 300.0 ),
39 m_yResolution( aCfg ? aCfg->y_resolution : 300.0 ),
40 m_lockAspectRatio( aCfg ? aCfg->lock_aspect_ratio : true ),
41 m_sizeUnits( aCfg ? static_cast<SIZE_UNITS>( aCfg->size_units ) : SIZE_UNITS::PIXELS ),
42 m_resolutionUnits( aCfg ? static_cast<RESOLUTION_UNITS>( aCfg->resolution_units )
44{
45 m_aspectRatio = static_cast<double>(m_width) / static_cast<double>(m_height);
46
47 // State is persisted through EXPORT_IMAGE_SETTINGS, so opt out of the generic
48 // control-state save and restore.
49 OptOut( this );
50
51 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
52
53 // Image Size section
54 wxStaticText* imageSizeHeader = new wxStaticText( this, wxID_ANY, _( "Image Size" ) );
55 imageSizeHeader->SetFont( imageSizeHeader->GetFont().Bold() );
56 mainSizer->Add( imageSizeHeader, 0, wxLEFT | wxTOP, 10 );
57
58 wxFlexGridSizer* sizeGrid = new wxFlexGridSizer( 2, 4, 5, 5 );
59 sizeGrid->AddGrowableCol( 1 );
60
61 // Width row
62 sizeGrid->Add( new wxStaticText( this, wxID_ANY, _( "Width:" ) ), 0, wxALIGN_CENTER_VERTICAL );
63 m_spinWidth = new wxSpinCtrlDouble( this, wxID_ANY );
64 m_spinWidth->SetRange( 1, 50000 );
65 m_spinWidth->SetDigits( 0 );
66 sizeGrid->Add( m_spinWidth, 1, wxEXPAND );
67
68 // Lock button - will span 2 rows
69 m_lockButton = new wxBitmapButton( this, wxID_ANY,
71 sizeGrid->Add( m_lockButton, 0, wxALIGN_CENTER | wxALL, 2 );
72
73 // Size units choice
74 m_choiceSizeUnits = new wxChoice( this, wxID_ANY );
75 m_choiceSizeUnits->Append( _( "pixels" ) );
76 m_choiceSizeUnits->Append( _( "%" ) );
77 m_choiceSizeUnits->Append( _( "mm" ) );
78 m_choiceSizeUnits->Append( _( "in" ) );
79 m_choiceSizeUnits->SetSelection( static_cast<int>( m_sizeUnits ) );
80 sizeGrid->Add( m_choiceSizeUnits, 0, wxEXPAND );
81
82 // Height row
83 sizeGrid->Add( new wxStaticText( this, wxID_ANY, _( "Height:" ) ), 0, wxALIGN_CENTER_VERTICAL );
84 m_spinHeight = new wxSpinCtrlDouble( this, wxID_ANY );
85 m_spinHeight->SetRange( 1, 50000 );
86 m_spinHeight->SetDigits( 0 );
87 sizeGrid->Add( m_spinHeight, 1, wxEXPAND );
88
89 sizeGrid->AddSpacer( 0 ); // Empty space where lock button is
90 sizeGrid->AddSpacer( 0 ); // Empty space for units column
91
92 mainSizer->Add( sizeGrid, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10 );
93
94 // Pixel size display
95 m_pixelSizeLabel = new wxStaticText( this, wxID_ANY, wxEmptyString );
96 m_pixelSizeLabel->SetFont( m_pixelSizeLabel->GetFont().Smaller() );
97 mainSizer->Add( m_pixelSizeLabel, 0, wxLEFT | wxTOP, 10 );
98
99 // Separator
100 wxStaticLine* line = new wxStaticLine( this );
101 mainSizer->Add( line, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10 );
102
103 // Resolution section
104 wxStaticText* resolutionHeader = new wxStaticText( this, wxID_ANY, _( "Resolution" ) );
105 resolutionHeader->SetFont( resolutionHeader->GetFont().Bold() );
106 mainSizer->Add( resolutionHeader, 0, wxLEFT | wxTOP, 10 );
107
108 wxFlexGridSizer* resGrid = new wxFlexGridSizer( 2, 3, 5, 5 );
109 resGrid->AddGrowableCol( 1 );
110
111 // X Resolution row
112 resGrid->Add( new wxStaticText( this, wxID_ANY, _( "X resolution:" ) ), 0, wxALIGN_CENTER_VERTICAL );
113 m_spinXResolution = new wxSpinCtrlDouble( this, wxID_ANY );
114 m_spinXResolution->SetRange( 1, 10000 );
115 m_spinXResolution->SetDigits( 3 );
116 resGrid->Add( m_spinXResolution, 1, wxEXPAND );
117
118 // Resolution units choice
119 m_choiceResolutionUnits = new wxChoice( this, wxID_ANY );
120 m_choiceResolutionUnits->Append( _( "pixels/in" ) );
121 m_choiceResolutionUnits->Append( _( "pixels/mm" ) );
122 m_choiceResolutionUnits->SetSelection( static_cast<int>( m_resolutionUnits ) );
123 resGrid->Add( m_choiceResolutionUnits, 0, wxEXPAND );
124
125 // Y Resolution row
126 resGrid->Add( new wxStaticText( this, wxID_ANY, _( "Y resolution:" ) ), 0, wxALIGN_CENTER_VERTICAL );
127 m_spinYResolution = new wxSpinCtrlDouble( this, wxID_ANY );
128 m_spinYResolution->SetRange( 1, 10000 );
129 m_spinYResolution->SetDigits( 3 );
130 resGrid->Add( m_spinYResolution, 1, wxEXPAND );
131 resGrid->AddSpacer( 0 ); // Empty space for units column
132
133 mainSizer->Add( resGrid, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10 );
134
135 // Dialog buttons
136 wxStdDialogButtonSizer* btnSizer = CreateStdDialogButtonSizer( wxOK | wxCANCEL );
137 mainSizer->Add( btnSizer, 0, wxEXPAND | wxALL, 10 );
138
139 SetSizerAndFit( mainSizer );
140 Centre();
141
142 // Set initial values AFTER creating all controls
143 m_spinWidth->SetValue( m_width );
144 m_spinHeight->SetValue( m_height );
145 m_spinXResolution->SetValue( m_xResolution );
146 m_spinYResolution->SetValue( m_yResolution );
148
149 // Bind events AFTER setting initial values
150 m_lockButton->Bind( wxEVT_BUTTON, &DIALOG_EXPORT_3D_IMAGE::OnLockToggle, this );
151 m_spinWidth->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnWidthChange, this );
152 m_spinHeight->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnHeightChange, this );
153 m_spinXResolution->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnXResolutionChange, this );
154 m_spinYResolution->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnYResolutionChange, this );
157}
158
159
161{
162 double width = m_spinWidth->GetValue();
163 double height = m_spinHeight->GetValue();
164 double xRes = m_spinXResolution->GetValue();
165 double yRes = m_spinYResolution->GetValue();
166
168 {
169 xRes *= 25.4;
170 yRes *= 25.4;
171 }
172
173 wxSize pixelSize = GetPixelSize( width, height, xRes, yRes, m_sizeUnits );
174 m_width = pixelSize.GetWidth();
175 m_height = pixelSize.GetHeight();
176
177 m_xResolution = m_spinXResolution->GetValue();
178 m_yResolution = m_spinYResolution->GetValue();
179
180 if( m_cfg )
181 {
182 m_cfg->width = m_width;
183 m_cfg->height = m_height;
184 m_cfg->x_resolution = m_xResolution;
185 m_cfg->y_resolution = m_yResolution;
186 m_cfg->size_units = static_cast<int>( m_sizeUnits );
187 m_cfg->resolution_units = static_cast<int>( m_resolutionUnits );
188 m_cfg->lock_aspect_ratio = m_lockAspectRatio;
189 }
190
191 return true;
192}
193
194
195void DIALOG_EXPORT_3D_IMAGE::OnLockToggle( wxCommandEvent& aEvent )
196{
198
200 {
203 }
204 else
205 {
207 }
208}
209
210
211void DIALOG_EXPORT_3D_IMAGE::OnWidthChange( wxSpinDoubleEvent& aEvent )
212{
214 {
215 double width = m_spinWidth->GetValue();
216 double height;
217
219 height = m_originalSize.GetWidth() * width / 100.0 / m_aspectRatio;
220 else
221 height = width / m_aspectRatio;
222
223 // Ensure height is not less than 1
224 if( height < 1 )
225 height = 1;
226
227 m_spinHeight->SetValue( height );
228 }
229
231}
232
233
234void DIALOG_EXPORT_3D_IMAGE::OnHeightChange( wxSpinDoubleEvent& aEvent )
235{
237 {
238 double height = m_spinHeight->GetValue();
239 double width;
240
242 width = m_originalSize.GetHeight() * height / 100.0 * m_aspectRatio;
243 else
244 width = height * m_aspectRatio;
245
246 // Ensure width is not less than 1
247 if( width < 1 )
248 width = 1;
249
250 m_spinWidth->SetValue( width );
251 }
252
254}
255
256
257void DIALOG_EXPORT_3D_IMAGE::OnXResolutionChange( wxSpinDoubleEvent& aEvent )
258{
260}
261
262
263void DIALOG_EXPORT_3D_IMAGE::OnYResolutionChange( wxSpinDoubleEvent& aEvent )
264{
266}
267
268
269void DIALOG_EXPORT_3D_IMAGE::OnSizeUnitChange( wxCommandEvent& aEvent )
270{
271 SIZE_UNITS oldUnits = m_sizeUnits;
272 m_sizeUnits = static_cast<SIZE_UNITS>( m_choiceSizeUnits->GetSelection() );
273 ConvertSizeUnits( oldUnits, m_sizeUnits );
275}
276
277
279{
281 m_resolutionUnits = static_cast<RESOLUTION_UNITS>( m_choiceResolutionUnits->GetSelection() );
284}
285
286
287wxSize DIALOG_EXPORT_3D_IMAGE::GetPixelSize( double aWidth, double aHeight, double aXResolution, double aYResolution, SIZE_UNITS aSizeUnits ) const
288{
289 switch( aSizeUnits )
290 {
292 return wxSize( static_cast<int>( aWidth ), static_cast<int>( aHeight ) );
294 return wxSize( static_cast<int>( aWidth * m_originalSize.GetWidth() / 100.0 ),
295 static_cast<int>( aHeight * m_originalSize.GetHeight() / 100.0 ) );
296 case SIZE_UNITS::MM:
297 return wxSize( static_cast<int>( aWidth * aXResolution / 25.4 ),
298 static_cast<int>( aHeight * aYResolution / 25.4 ) );
300 return wxSize( static_cast<int>( aWidth * aXResolution ),
301 static_cast<int>( aHeight * aYResolution ) );
302 default:
303 break;
304 }
305
306 return wxSize( 10, 10 ); // Should not happen
307}
308
309
311{
312 double width = m_spinWidth->GetValue();
313 double height = m_spinHeight->GetValue();
314 double xRes = m_spinXResolution->GetValue();
315 double yRes = m_spinYResolution->GetValue();
316
317 // Convert resolution to standard pixels/inch
319 {
320 xRes *= 25.4; // Convert mm to inches
321 yRes *= 25.4;
322 }
323
324 wxSize pixelSize = GetPixelSize( width, height, xRes, yRes, m_sizeUnits );
325
326 m_pixelSizeLabel->SetLabel( wxString::Format( _( "%d × %d pixels" ), pixelSize.GetWidth(), pixelSize.GetHeight() ) );
327}
328
329
331{
332 m_aspectRatio = m_spinWidth->GetValue() / m_spinHeight->GetValue();
333}
334
335
337{
338 if( aFromUnit == aToUnit )
339 return;
340
341 double width = m_spinWidth->GetValue();
342 double height = m_spinHeight->GetValue();
343 double xRes = m_spinXResolution->GetValue();
344 double yRes = m_spinYResolution->GetValue();
345
346 // Convert resolution to standard pixels/inch
348 {
349 xRes *= 25.4; // Convert mm to inches
350 yRes *= 25.4;
351 }
352
353 // Convert to pixels first
354 wxSize pixelSize = GetPixelSize( width, height, xRes, yRes, aFromUnit );
355
356 // Convert from pixels to target unit
357 switch( aToUnit )
358 {
360 m_spinWidth->SetValue( pixelSize.GetWidth() );
361 m_spinHeight->SetValue( pixelSize.GetHeight() );
362 break;
364 m_spinWidth->SetValue( pixelSize.GetWidth() * 100.0 / m_originalSize.GetWidth() );
365 m_spinHeight->SetValue( pixelSize.GetHeight() * 100.0 / m_originalSize.GetHeight() );
366 break;
367 case SIZE_UNITS::MM:
368 m_spinWidth->SetValue( pixelSize.GetWidth() * 25.4 / xRes );
369 m_spinHeight->SetValue( pixelSize.GetHeight() * 25.4 / yRes );
370 break;
372 m_spinWidth->SetValue( pixelSize.GetWidth() / xRes );
373 m_spinHeight->SetValue( pixelSize.GetHeight() / yRes );
374 break;
375 }
376}
377
378
380{
381 if( aFromUnit == aToUnit )
382 return;
383
384 double xRes = m_spinXResolution->GetValue();
385 double yRes = m_spinYResolution->GetValue();
386
388 {
389 // Convert from pixels/inch to pixels/mm
390 m_spinXResolution->SetValue( xRes / 25.4 );
391 m_spinYResolution->SetValue( yRes / 25.4 );
392 }
393 else if( aFromUnit == RESOLUTION_UNITS::PIXELS_PER_MM && aToUnit == RESOLUTION_UNITS::PIXELS_PER_INCH )
394 {
395 // Convert from pixels/mm to pixels/inch
396 m_spinXResolution->SetValue( xRes * 25.4 );
397 m_spinYResolution->SetValue( yRes * 25.4 );
398 }
399}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
DIALOG_EXPORT_3D_IMAGE(wxWindow *aParent, const wxSize &aCanvasSize, EDA_3D_VIEWER_SETTINGS::EXPORT_IMAGE_SETTINGS *aCfg)
void OnWidthChange(wxSpinDoubleEvent &aEvent)
void OnXResolutionChange(wxSpinDoubleEvent &aEvent)
void OnLockToggle(wxCommandEvent &aEvent)
void OnHeightChange(wxSpinDoubleEvent &aEvent)
wxSize GetPixelSize(double aWidth, double aHeight, double aXResolution, double aYResolution, SIZE_UNITS aSizeUnits) const
void OnResolutionUnitChange(wxCommandEvent &aEvent)
void OnYResolutionChange(wxSpinDoubleEvent &aEvent)
wxSpinCtrlDouble * m_spinYResolution
wxSpinCtrlDouble * m_spinHeight
void ConvertSizeUnits(SIZE_UNITS aFromUnit, SIZE_UNITS aToUnit)
wxSpinCtrlDouble * m_spinXResolution
void OnSizeUnitChange(wxCommandEvent &aEvent)
EDA_3D_VIEWER_SETTINGS::EXPORT_IMAGE_SETTINGS * m_cfg
void ConvertResolutionUnits(RESOLUTION_UNITS aFromUnit, RESOLUTION_UNITS aToUnit)
void OptOut(wxWindow *aWindow)
Opt out of control state saving.
DIALOG_SHIM(wxWindow *aParent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER, const wxString &name=wxDialogNameStr)
#define _(s)