KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_graphics_importer_buffer.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 2
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
20#include <qa_utils/file_utils.h>
22
23#include <eda_item.h>
26#include <base_units.h>
27#include <algorithm>
28#include <cstring>
29#include <clocale>
30#include <locale>
31#include <limits>
32#include <wx/ffile.h>
33#include <wx/filefn.h>
34
35
37{
38public:
43
44 void AddLine( const VECTOR2D& aStart, const VECTOR2D& aEnd,
45 const IMPORTED_STROKE& aStroke ) override
46 {
47 m_lines.push_back( { aStart, aEnd } );
49 }
50
51 bool CanImportSourceLayer( const wxString& aSourceLayer ) const override
52 {
53 if( m_enabledSourceLayers.empty() )
54 return true;
55
56 return std::find( m_enabledSourceLayers.begin(), m_enabledSourceLayers.end(), aSourceLayer )
57 != m_enabledSourceLayers.end();
58 }
59
60 void SetCurrentSourceLayer( const wxString& aSourceLayer ) override { m_currentSourceLayer = aSourceLayer; }
61
62 void AddCircle( const VECTOR2D& aCenter, double aRadius, const IMPORTED_STROKE& aStroke,
63 bool aFilled, const COLOR4D& aFillColor ) override {}
64
65 void AddArc( const VECTOR2D& aCenter, const VECTOR2D& aStart, const EDA_ANGLE& aAngle,
66 const IMPORTED_STROKE& aStroke ) override {}
67
68 void AddPolygon( const std::vector<VECTOR2D>& aVertices, const IMPORTED_STROKE& aStroke,
69 bool aFilled, const COLOR4D& aFillColor ) override {}
70
71 void AddText( const VECTOR2D& aOrigin, const wxString& aText, double aHeight, double aWidth,
72 double aThickness, double aOrientation, GR_TEXT_H_ALIGN_T aHJustify,
73 GR_TEXT_V_ALIGN_T aVJustify, const COLOR4D& aColor ) override {}
74
75 void AddSpline( const VECTOR2D& aStart, const VECTOR2D& aBezierControl1,
76 const VECTOR2D& aBezierControl2, const VECTOR2D& aEnd,
77 const IMPORTED_STROKE& aStroke ) override {}
78
79 void AddEllipse( const VECTOR2D& aCenter, double aMajorRadius, double aMinorRadius, const EDA_ANGLE& aRotation,
80 const IMPORTED_STROKE& aStroke, bool aFilled, const COLOR4D& aFillColor ) override
81 {
82 }
83
84 void AddEllipseArc( const VECTOR2D& aCenter, double aMajorRadius, double aMinorRadius, const EDA_ANGLE& aRotation,
85 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle,
86 const IMPORTED_STROKE& aStroke ) override
87 {
88 }
89
90 std::vector<std::pair<VECTOR2D, VECTOR2D>> m_lines;
91 std::vector<wxString> m_lineSourceLayers;
92 std::vector<wxString> m_enabledSourceLayers;
94};
95
96
97BOOST_AUTO_TEST_SUITE( GraphicsImporterBuffer )
98
99
100
107BOOST_AUTO_TEST_CASE( LargeCoordinatesAutoOffset )
108{
110 TEST_GRAPHICS_IMPORTER importer;
111
112 // Add a line with coordinates that would overflow INT32 when converted to IU
113 // 2300mm * 1e6 IU/mm = 2.3e9 > INT32_MAX (2.147e9)
114 VECTOR2D start( 2300.0, 1400.0 );
115 VECTOR2D end( 2350.0, 1450.0 );
116
117 IMPORTED_STROKE stroke( 0.1 );
118 buffer.AddLine( start, end, stroke );
119
120 // Import with no offset set (default is 0,0)
121 BOOST_CHECK( importer.GetImportOffsetMM() == VECTOR2D( 0, 0 ) );
122
123 buffer.ImportTo( importer );
124
125 // After import, the offset should have been automatically adjusted
126 // to move the coordinates into the valid range
127 VECTOR2D offset = importer.GetImportOffsetMM();
128
129 // The offset should be negative of the bounding box origin to bring coordinates near zero
130 BOOST_CHECK_MESSAGE( offset.x < 0, "X offset should be negative to shift large coords down" );
131
132 // Verify the resulting coordinates are within valid IU range
133 if( !importer.m_lines.empty() )
134 {
135 VECTOR2D importedStart = importer.m_lines[0].first;
136 VECTOR2D importedEnd = importer.m_lines[0].second;
137
138 // After offset and scale, coordinates should be manageable
139 double maxCoord = static_cast<double>( std::numeric_limits<int>::max() )
140 / importer.GetMillimeterToIuFactor();
141
142 BOOST_CHECK_MESSAGE( ( importedStart.x + offset.x ) < maxCoord,
143 "Imported X coord should be within valid range" );
144 BOOST_CHECK_MESSAGE( ( importedStart.y + offset.y ) < maxCoord,
145 "Imported Y coord should be within valid range" );
146 }
147}
148
149
153BOOST_AUTO_TEST_CASE( NormalCoordinatesNoOffset )
154{
156 TEST_GRAPHICS_IMPORTER importer;
157
158 // Add a line with normal coordinates that fit within the valid range
159 // 100mm * 1e6 IU/mm = 1e8 << INT32_MAX
160 VECTOR2D start( 100.0, 100.0 );
161 VECTOR2D end( 200.0, 200.0 );
162
163 IMPORTED_STROKE stroke( 0.1 );
164 buffer.AddLine( start, end, stroke );
165
166 buffer.ImportTo( importer );
167
168 // Offset should remain at zero since coordinates are valid
169 VECTOR2D offset = importer.GetImportOffsetMM();
170 BOOST_CHECK_MESSAGE( offset == VECTOR2D( 0, 0 ),
171 "Normal coordinates should not trigger auto-offset" );
172}
173
174
175BOOST_AUTO_TEST_CASE( SourceLayersAreReportedAndCanBeSkipped )
176{
178 TEST_GRAPHICS_IMPORTER importer;
179
180 IMPORTED_STROKE stroke( 0.1 );
181
182 buffer.SetCurrentSourceLayer( wxS( "Outline" ) );
183 buffer.AddLine( VECTOR2D( 0.0, 0.0 ), VECTOR2D( 10.0, 0.0 ), stroke );
184
185 buffer.SetCurrentSourceLayer( wxS( "Guide" ) );
186 buffer.AddLine( VECTOR2D( 0.0, 1.0 ), VECTOR2D( 10.0, 1.0 ), stroke );
187
188 std::vector<wxString> sourceLayers = buffer.GetSourceLayers();
189
190 BOOST_CHECK( std::find( sourceLayers.begin(), sourceLayers.end(), wxS( "Outline" ) ) != sourceLayers.end() );
191 BOOST_CHECK( std::find( sourceLayers.begin(), sourceLayers.end(), wxS( "Guide" ) ) != sourceLayers.end() );
192
193 importer.m_enabledSourceLayers.push_back( wxS( "Outline" ) );
194
195 buffer.ImportTo( importer );
196
197 BOOST_REQUIRE_EQUAL( importer.m_lines.size(), 1 );
198 BOOST_REQUIRE_EQUAL( importer.m_lineSourceLayers.size(), 1 );
199 BOOST_CHECK_EQUAL( importer.m_lineSourceLayers[0], wxS( "Outline" ) );
200}
201
202
203BOOST_AUTO_TEST_CASE( DxfSourceLayersArePreserved )
204{
205 const char* dxf =
206 "0\n"
207 "SECTION\n"
208 "2\n"
209 "HEADER\n"
210 "9\n"
211 "$INSUNITS\n"
212 "70\n"
213 "4\n"
214 "0\n"
215 "ENDSEC\n"
216 "0\n"
217 "SECTION\n"
218 "2\n"
219 "TABLES\n"
220 "0\n"
221 "TABLE\n"
222 "2\n"
223 "LAYER\n"
224 "70\n"
225 "2\n"
226 "0\n"
227 "LAYER\n"
228 "2\n"
229 "Outline\n"
230 "70\n"
231 "0\n"
232 "62\n"
233 "7\n"
234 "6\n"
235 "Continuous\n"
236 "0\n"
237 "LAYER\n"
238 "2\n"
239 "Guide\n"
240 "70\n"
241 "0\n"
242 "62\n"
243 "7\n"
244 "6\n"
245 "Continuous\n"
246 "0\n"
247 "ENDTAB\n"
248 "0\n"
249 "ENDSEC\n"
250 "0\n"
251 "SECTION\n"
252 "2\n"
253 "ENTITIES\n"
254 "0\n"
255 "LINE\n"
256 "8\n"
257 "Outline\n"
258 "10\n"
259 "0\n"
260 "20\n"
261 "0\n"
262 "11\n"
263 "10\n"
264 "21\n"
265 "0\n"
266 "0\n"
267 "LINE\n"
268 "8\n"
269 "Guide\n"
270 "10\n"
271 "0\n"
272 "20\n"
273 "1\n"
274 "11\n"
275 "10\n"
276 "21\n"
277 "1\n"
278 "0\n"
279 "ENDSEC\n"
280 "0\n"
281 "EOF\n";
282
283 KI_TEST::SCOPED_TEMP_DIR tempDir( "kicad_dxf_layers" );
284 const wxString dxfPath = tempDir.CreateChildFileStr( "layers.dxf" );
285
286 BOOST_REQUIRE( !dxfPath.IsEmpty() );
287
288 {
289 wxFFile file( dxfPath, wxT( "wb" ) );
290 BOOST_REQUIRE( file.IsOpened() );
291 BOOST_REQUIRE_EQUAL( file.Write( dxf, strlen( dxf ) ), strlen( dxf ) );
292 }
293
294 DXF_IMPORT_PLUGIN plugin;
295 TEST_GRAPHICS_IMPORTER importer;
296
298 plugin.SetImporter( &importer );
299
300 BOOST_REQUIRE( plugin.Load( dxfPath ) );
301
302 std::vector<wxString> sourceLayers = plugin.GetSourceLayers();
303
304 BOOST_CHECK( std::find( sourceLayers.begin(), sourceLayers.end(), wxS( "Outline" ) ) != sourceLayers.end() );
305 BOOST_CHECK( std::find( sourceLayers.begin(), sourceLayers.end(), wxS( "Guide" ) ) != sourceLayers.end() );
306
307 importer.m_enabledSourceLayers.push_back( wxS( "Outline" ) );
308
309 BOOST_REQUIRE( plugin.Import() );
310
311 BOOST_REQUIRE_EQUAL( importer.m_lines.size(), 1 );
312 BOOST_REQUIRE_EQUAL( importer.m_lineSourceLayers.size(), 1 );
313 BOOST_CHECK_EQUAL( importer.m_lineSourceLayers[0], wxS( "Outline" ) );
314}
315
316
317BOOST_AUTO_TEST_CASE( NativeDxfImportPreservesLocales )
318{
319 DXF_IMPORT_PLUGIN plugin;
320 TEST_GRAPHICS_IMPORTER importer;
322 plugin.SetImporter( &importer );
323
324 const std::string cLocale = std::setlocale( LC_ALL, nullptr );
325 const std::string cppLocale = std::locale().name();
326 const wxString path = wxString::FromUTF8( KI_TEST::GetTestDataRootDir() )
327 + wxS( "/common/import_gfx/issue22127_fusion360_splines.dxf" );
328
329 BOOST_REQUIRE( plugin.Load( path ) );
330 BOOST_REQUIRE( plugin.Import() );
331 BOOST_CHECK_EQUAL( std::string( std::setlocale( LC_ALL, nullptr ) ), cLocale );
332 BOOST_CHECK_EQUAL( std::locale().name(), cppLocale );
333}
334
335
const char * name
constexpr double PCB_IU_PER_MM
Pcbnew IU is 1 nanometer.
Definition base_units.h:68
bool Import() override
Actually imports the file.
std::vector< wxString > GetSourceLayers() const
bool Load(const wxString &aFileName) override
Load file for import.
void SetUnit(DXF_IMPORT_UNITS aUnit)
Set the default units when importing DXFs.
virtual void SetImporter(GRAPHICS_IMPORTER *aImporter) override
Set the receiver of the imported shapes.
void AddLine(const VECTOR2D &aStart, const VECTOR2D &aEnd, const IMPORTED_STROKE &aStroke) override
Create an object representing a line segment.
void ImportTo(GRAPHICS_IMPORTER &aImporter)
void SetCurrentSourceLayer(const wxString &aSourceLayer) override
Set the source layer for the next buffered shape to be imported.
std::vector< wxString > GetSourceLayers() const
double m_millimeterToIu
Factor to convert millimeters to Internal Units.
const VECTOR2D & GetImportOffsetMM() const
A clone of IMPORTED_STROKE, but with floating-point width.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
wxString CreateChildFileStr(const wxString &aName) const
Create and return the path to a direct child file as a wxString.
void AddLine(const VECTOR2D &aStart, const VECTOR2D &aEnd, const IMPORTED_STROKE &aStroke) override
Create an object representing a line segment.
void AddCircle(const VECTOR2D &aCenter, double aRadius, const IMPORTED_STROKE &aStroke, bool aFilled, const COLOR4D &aFillColor) override
Create an object representing a circle.
std::vector< wxString > m_lineSourceLayers
bool CanImportSourceLayer(const wxString &aSourceLayer) const override
Return true if shapes from a given source layer should be imported.
void AddText(const VECTOR2D &aOrigin, const wxString &aText, double aHeight, double aWidth, double aThickness, double aOrientation, GR_TEXT_H_ALIGN_T aHJustify, GR_TEXT_V_ALIGN_T aVJustify, const COLOR4D &aColor) override
Create an object representing a text.
std::vector< std::pair< VECTOR2D, VECTOR2D > > m_lines
std::vector< wxString > m_enabledSourceLayers
void AddEllipseArc(const VECTOR2D &aCenter, double aMajorRadius, double aMinorRadius, const EDA_ANGLE &aRotation, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle, const IMPORTED_STROKE &aStroke) override
Create an object representing an elliptical arc.
void AddEllipse(const VECTOR2D &aCenter, double aMajorRadius, double aMinorRadius, const EDA_ANGLE &aRotation, const IMPORTED_STROKE &aStroke, bool aFilled, const COLOR4D &aFillColor) override
Create an object representing a closed ellipse.
void AddPolygon(const std::vector< VECTOR2D > &aVertices, const IMPORTED_STROKE &aStroke, bool aFilled, const COLOR4D &aFillColor) override
Create an object representing a polygon.
void AddSpline(const VECTOR2D &aStart, const VECTOR2D &aBezierControl1, const VECTOR2D &aBezierControl2, const VECTOR2D &aEnd, const IMPORTED_STROKE &aStroke) override
Create an object representing an arc.
void AddArc(const VECTOR2D &aCenter, const VECTOR2D &aStart, const EDA_ANGLE &aAngle, const IMPORTED_STROKE &aStroke) override
Create an object representing an arc.
void SetCurrentSourceLayer(const wxString &aSourceLayer) override
Set the source layer for the next buffered shape to be imported.
std::string GetTestDataRootDir()
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(LargeCoordinatesAutoOffset)
Test that large coordinates that would overflow when converted to internal units are automatically of...
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
VECTOR2I end
BOOST_CHECK_EQUAL(result, "25.4")
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
VECTOR2< double > VECTOR2D
Definition vector2d.h:682