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 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
48
49 // Image Size section
50 wxStaticText* imageSizeHeader = new wxStaticText( this, wxID_ANY, _( "Image Size" ) );
51 imageSizeHeader->SetFont( imageSizeHeader->GetFont().Bold() );
52 mainSizer->Add( imageSizeHeader, 0, wxLEFT | wxTOP, 10 );
53
54 wxFlexGridSizer* sizeGrid = new wxFlexGridSizer( 2, 4, 5, 5 );
55 sizeGrid->AddGrowableCol( 1 );
56
57 // Width row
58 sizeGrid->Add( new wxStaticText( this, wxID_ANY, _( "Width:" ) ), 0, wxALIGN_CENTER_VERTICAL );
59 m_spinWidth = new wxSpinCtrlDouble( this, wxID_ANY );
60 m_spinWidth->SetRange( 1, 50000 );
61 m_spinWidth->SetDigits( 0 );
62 sizeGrid->Add( m_spinWidth, 1, wxEXPAND );
63
64 // Lock button - will span 2 rows
65 m_lockButton = new wxBitmapButton( this, wxID_ANY,
67 sizeGrid->Add( m_lockButton, 0, wxALIGN_CENTER | wxALL, 2 );
68
69 // Size units choice
70 m_choiceSizeUnits = new wxChoice( this, wxID_ANY );
71 m_choiceSizeUnits->Append( _( "pixels" ) );
72 m_choiceSizeUnits->Append( _( "%" ) );
73 m_choiceSizeUnits->Append( _( "mm" ) );
74 m_choiceSizeUnits->Append( _( "in" ) );
75 m_choiceSizeUnits->SetSelection( static_cast<int>( m_sizeUnits ) );
76 sizeGrid->Add( m_choiceSizeUnits, 0, wxEXPAND );
77
78 // Height row
79 sizeGrid->Add( new wxStaticText( this, wxID_ANY, _( "Height:" ) ), 0, wxALIGN_CENTER_VERTICAL );
80 m_spinHeight = new wxSpinCtrlDouble( this, wxID_ANY );
81 m_spinHeight->SetRange( 1, 50000 );
82 m_spinHeight->SetDigits( 0 );
83 sizeGrid->Add( m_spinHeight, 1, wxEXPAND );
84
85 sizeGrid->AddSpacer( 0 ); // Empty space where lock button is
86 sizeGrid->AddSpacer( 0 ); // Empty space for units column
87
88 mainSizer->Add( sizeGrid, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10 );
89
90 // Pixel size display
91 m_pixelSizeLabel = new wxStaticText( this, wxID_ANY, wxEmptyString );
92 m_pixelSizeLabel->SetFont( m_pixelSizeLabel->GetFont().Smaller() );
93 mainSizer->Add( m_pixelSizeLabel, 0, wxLEFT | wxTOP, 10 );
94
95 // Separator
96 wxStaticLine* line = new wxStaticLine( this );
97 mainSizer->Add( line, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10 );
98
99 // Resolution section
100 wxStaticText* resolutionHeader = new wxStaticText( this, wxID_ANY, _( "Resolution" ) );
101 resolutionHeader->SetFont( resolutionHeader->GetFont().Bold() );
102 mainSizer->Add( resolutionHeader, 0, wxLEFT | wxTOP, 10 );
103
104 wxFlexGridSizer* resGrid = new wxFlexGridSizer( 2, 3, 5, 5 );
105 resGrid->AddGrowableCol( 1 );
106
107 // X Resolution row
108 resGrid->Add( new wxStaticText( this, wxID_ANY, _( "X resolution:" ) ), 0, wxALIGN_CENTER_VERTICAL );
109 m_spinXResolution = new wxSpinCtrlDouble( this, wxID_ANY );
110 m_spinXResolution->SetRange( 1, 10000 );
111 m_spinXResolution->SetDigits( 3 );
112 resGrid->Add( m_spinXResolution, 1, wxEXPAND );
113
114 // Resolution units choice
115 m_choiceResolutionUnits = new wxChoice( this, wxID_ANY );
116 m_choiceResolutionUnits->Append( _( "pixels/in" ) );
117 m_choiceResolutionUnits->Append( _( "pixels/mm" ) );
118 m_choiceResolutionUnits->SetSelection( static_cast<int>( m_resolutionUnits ) );
119 resGrid->Add( m_choiceResolutionUnits, 0, wxEXPAND );
120
121 // Y Resolution row
122 resGrid->Add( new wxStaticText( this, wxID_ANY, _( "Y resolution:" ) ), 0, wxALIGN_CENTER_VERTICAL );
123 m_spinYResolution = new wxSpinCtrlDouble( this, wxID_ANY );
124 m_spinYResolution->SetRange( 1, 10000 );
125 m_spinYResolution->SetDigits( 3 );
126 resGrid->Add( m_spinYResolution, 1, wxEXPAND );
127 resGrid->AddSpacer( 0 ); // Empty space for units column
128
129 mainSizer->Add( resGrid, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10 );
130
131 // Dialog buttons
132 wxStdDialogButtonSizer* btnSizer = CreateStdDialogButtonSizer( wxOK | wxCANCEL );
133 mainSizer->Add( btnSizer, 0, wxEXPAND | wxALL, 10 );
134
135 SetSizerAndFit( mainSizer );
136 Centre();
137
138 // Set initial values AFTER creating all controls
139 m_spinWidth->SetValue( m_width );
140 m_spinHeight->SetValue( m_height );
141 m_spinXResolution->SetValue( m_xResolution );
142 m_spinYResolution->SetValue( m_yResolution );
144
145 // Bind events AFTER setting initial values
146 m_lockButton->Bind( wxEVT_BUTTON, &DIALOG_EXPORT_3D_IMAGE::OnLockToggle, this );
147 m_spinWidth->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnWidthChange, this );
148 m_spinHeight->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnHeightChange, this );
149 m_spinXResolution->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnXResolutionChange, this );
150 m_spinYResolution->Bind( wxEVT_SPINCTRLDOUBLE, &DIALOG_EXPORT_3D_IMAGE::OnYResolutionChange, this );
153}
154
155
157{
158 m_spinWidth->SetValue( m_width );
159 m_spinHeight->SetValue( m_height );
160 m_spinXResolution->SetValue( m_xResolution );
161 m_spinYResolution->SetValue( m_yResolution );
162
163 m_choiceSizeUnits->SetSelection( static_cast<int>( m_sizeUnits ) );
164 m_choiceResolutionUnits->SetSelection( static_cast<int>( m_resolutionUnits ) );
165
167
170 return true;
171}
172
173
175{
176 double width = m_spinWidth->GetValue();
177 double height = m_spinHeight->GetValue();
178 double xRes = m_spinXResolution->GetValue();
179 double yRes = m_spinYResolution->GetValue();
180
182 {
183 xRes *= 25.4;
184 yRes *= 25.4;
185 }
186
187 wxSize pixelSize = GetPixelSize( width, height, xRes, yRes, m_sizeUnits );
188 m_width = pixelSize.GetWidth();
189 m_height = pixelSize.GetHeight();
190
191 m_xResolution = m_spinXResolution->GetValue();
192 m_yResolution = m_spinYResolution->GetValue();
193
194 if( m_cfg )
195 {
196 m_cfg->width = m_width;
197 m_cfg->height = m_height;
198 m_cfg->x_resolution = m_xResolution;
199 m_cfg->y_resolution = m_yResolution;
200 m_cfg->size_units = static_cast<int>( m_sizeUnits );
201 m_cfg->resolution_units = static_cast<int>( m_resolutionUnits );
202 m_cfg->lock_aspect_ratio = m_lockAspectRatio;
203 }
204
205 return true;
206}
207
208
209void DIALOG_EXPORT_3D_IMAGE::OnLockToggle( wxCommandEvent& aEvent )
210{
212
214 {
217 }
218 else
219 {
221 }
222}
223
224
225void DIALOG_EXPORT_3D_IMAGE::OnWidthChange( wxSpinDoubleEvent& aEvent )
226{
228 {
229 double width = m_spinWidth->GetValue();
230 double height;
231
233 height = m_originalSize.GetWidth() * width / 100.0 / m_aspectRatio;
234 else
235 height = width / m_aspectRatio;
236
237 // Ensure height is not less than 1
238 if( height < 1 )
239 height = 1;
240
241 m_spinHeight->SetValue( height );
242 }
243
245}
246
247
248void DIALOG_EXPORT_3D_IMAGE::OnHeightChange( wxSpinDoubleEvent& aEvent )
249{
251 {
252 double height = m_spinHeight->GetValue();
253 double width;
254
256 width = m_originalSize.GetHeight() * height / 100.0 * m_aspectRatio;
257 else
258 width = height * m_aspectRatio;
259
260 // Ensure width is not less than 1
261 if( width < 1 )
262 width = 1;
263
264 m_spinWidth->SetValue( width );
265 }
266
268}
269
270
271void DIALOG_EXPORT_3D_IMAGE::OnXResolutionChange( wxSpinDoubleEvent& aEvent )
272{
274}
275
276
277void DIALOG_EXPORT_3D_IMAGE::OnYResolutionChange( wxSpinDoubleEvent& aEvent )
278{
280}
281
282
283void DIALOG_EXPORT_3D_IMAGE::OnSizeUnitChange( wxCommandEvent& aEvent )
284{
285 SIZE_UNITS oldUnits = m_sizeUnits;
286 m_sizeUnits = static_cast<SIZE_UNITS>( m_choiceSizeUnits->GetSelection() );
287 ConvertSizeUnits( oldUnits, m_sizeUnits );
289}
290
291
293{
295 m_resolutionUnits = static_cast<RESOLUTION_UNITS>( m_choiceResolutionUnits->GetSelection() );
298}
299
300
301wxSize DIALOG_EXPORT_3D_IMAGE::GetPixelSize( double aWidth, double aHeight, double aXResolution, double aYResolution, SIZE_UNITS aSizeUnits ) const
302{
303 switch( aSizeUnits )
304 {
306 return wxSize( static_cast<int>( aWidth ), static_cast<int>( aHeight ) );
308 return wxSize( static_cast<int>( aWidth * m_originalSize.GetWidth() / 100.0 ),
309 static_cast<int>( aHeight * m_originalSize.GetHeight() / 100.0 ) );
310 case SIZE_UNITS::MM:
311 return wxSize( static_cast<int>( aWidth * aXResolution / 25.4 ),
312 static_cast<int>( aHeight * aYResolution / 25.4 ) );
314 return wxSize( static_cast<int>( aWidth * aXResolution ),
315 static_cast<int>( aHeight * aYResolution ) );
316 default:
317 break;
318 }
319
320 return wxSize( 10, 10 ); // Should not happen
321}
322
323
325{
326 double width = m_spinWidth->GetValue();
327 double height = m_spinHeight->GetValue();
328 double xRes = m_spinXResolution->GetValue();
329 double yRes = m_spinYResolution->GetValue();
330
331 // Convert resolution to standard pixels/inch
333 {
334 xRes *= 25.4; // Convert mm to inches
335 yRes *= 25.4;
336 }
337
338 wxSize pixelSize = GetPixelSize( width, height, xRes, yRes, m_sizeUnits );
339
340 m_pixelSizeLabel->SetLabel( wxString::Format( _( "%d × %d pixels" ), pixelSize.GetWidth(), pixelSize.GetHeight() ) );
341}
342
343
345{
346 m_aspectRatio = m_spinWidth->GetValue() / m_spinHeight->GetValue();
347}
348
349
351{
352 if( aFromUnit == aToUnit )
353 return;
354
355 double width = m_spinWidth->GetValue();
356 double height = m_spinHeight->GetValue();
357 double xRes = m_spinXResolution->GetValue();
358 double yRes = m_spinYResolution->GetValue();
359
360 // Convert resolution to standard pixels/inch
362 {
363 xRes *= 25.4; // Convert mm to inches
364 yRes *= 25.4;
365 }
366
367 // Convert to pixels first
368 wxSize pixelSize = GetPixelSize( width, height, xRes, yRes, aFromUnit );
369
370 // Convert from pixels to target unit
371 switch( aToUnit )
372 {
374 m_spinWidth->SetValue( pixelSize.GetWidth() );
375 m_spinHeight->SetValue( pixelSize.GetHeight() );
376 break;
378 m_spinWidth->SetValue( pixelSize.GetWidth() * 100.0 / m_originalSize.GetWidth() );
379 m_spinHeight->SetValue( pixelSize.GetHeight() * 100.0 / m_originalSize.GetHeight() );
380 break;
381 case SIZE_UNITS::MM:
382 m_spinWidth->SetValue( pixelSize.GetWidth() * 25.4 / xRes );
383 m_spinHeight->SetValue( pixelSize.GetHeight() * 25.4 / yRes );
384 break;
386 m_spinWidth->SetValue( pixelSize.GetWidth() / xRes );
387 m_spinHeight->SetValue( pixelSize.GetHeight() / yRes );
388 break;
389 }
390}
391
392
394{
395 if( aFromUnit == aToUnit )
396 return;
397
398 double xRes = m_spinXResolution->GetValue();
399 double yRes = m_spinYResolution->GetValue();
400
402 {
403 // Convert from pixels/inch to pixels/mm
404 m_spinXResolution->SetValue( xRes / 25.4 );
405 m_spinYResolution->SetValue( yRes / 25.4 );
406 }
407 else if( aFromUnit == RESOLUTION_UNITS::PIXELS_PER_MM && aToUnit == RESOLUTION_UNITS::PIXELS_PER_INCH )
408 {
409 // Convert from pixels/mm to pixels/inch
410 m_spinXResolution->SetValue( xRes * 25.4 );
411 m_spinYResolution->SetValue( yRes * 25.4 );
412 }
413}
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)
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)