KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_array_options.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) 2019 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
31
32#include <base_units.h>
33#include <trigo.h>
34
35#include <array_options.h>
36
42std::ostream& operator<<( std::ostream& os, const ARRAY_OPTIONS::TRANSFORM& aObj )
43{
44 os << "TRANSFORM[ " << aObj.m_offset << " r " << aObj.m_rotation.AsDegrees() << "deg"
45 << " ]";
46 return os;
47}
48
49
54{
55 return KI_TEST::IsVecWithinTol<VECTOR2I>( aL.m_offset, aR.m_offset, 1 )
56 && KI_TEST::IsWithin<double>( aL.m_rotation.AsDegrees(),
57 aR.m_rotation.AsDegrees(), 0.001 );
58}
59
60
68void CheckArrayTransforms( const ARRAY_OPTIONS& aOpts, const VECTOR2I& aPos,
69 const std::vector<ARRAY_OPTIONS::TRANSFORM>& aExp )
70{
71 std::vector<ARRAY_OPTIONS::TRANSFORM> transforms;
72
73 for( int i = 0; i < aOpts.GetArraySize(); ++i )
74 {
75 transforms.push_back( aOpts.GetTransform( i, aPos ) );
76 }
77
78 BOOST_CHECK_EQUAL( transforms.size(), aExp.size() );
79
80 for( unsigned i = 0; i < std::min( transforms.size(), aExp.size() ); ++i )
81 {
82 BOOST_TEST_CONTEXT( "Index " << i )
83 {
84 BOOST_CHECK_PREDICATE( TransformIsClose, ( transforms[i] )( aExp[i] ) );
85 }
86 }
87}
88
89
93BOOST_AUTO_TEST_SUITE( ArrayOptions )
94
95
97{
98 int m_nx;
99 int m_ny;
106};
107
109{
110 std::string m_case_name;
113 std::vector<ARRAY_OPTIONS::TRANSFORM> m_exp_transforms;
114};
115
116
117// clang-format off
118static const std::vector<GRID_ARRAY_TEST_CASE> grid_geom_cases = {
119 {
120 "2x3 rect grid",
121 {
122 2,
123 3,
124 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
125 { 0, 0 },
126 1,
127 true,
128 false,
129 true,
130 },
131 { 0, 0 },
132 {
133 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
134 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
135 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
136 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
137 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
138 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
139 },
140 },
141 {
142 "2x3 offset grid",
143 {
144 2,
145 3,
146 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
147 { schIUScale.mmToIU( 0.1 ), schIUScale.mmToIU( 0.2 ) },
148 1,
149 true,
150 false,
151 true,
152 },
153 { 0, 0 },
154 {
155 // add the offsets for each positions
156 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
157 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0.2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
158 { { schIUScale.mmToIU( 0.1 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
159 { { schIUScale.mmToIU( 2.1 ), schIUScale.mmToIU( 2.2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
160 { { schIUScale.mmToIU( 0.2 ), schIUScale.mmToIU( 4.0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
161 { { schIUScale.mmToIU( 2.2 ), schIUScale.mmToIU( 4.2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
162 },
163 },
164 {
165 "2x3 stagger rows",
166 {
167 2,
168 3,
169 { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 2 ) },
170 { 0, 0 },
171 3,
172 true,
173 false,
174 true,
175 },
176 { 0, 0 },
177 {
178 // add the offsets for each positions
179 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
180 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
181 { { schIUScale.mmToIU( 1 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
182 { { schIUScale.mmToIU( 4 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
183 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
184 { { schIUScale.mmToIU( 5 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
185 },
186 },
187 {
188 "2x3 stagger cols",
189 {
190 2,
191 3,
192 { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 2 ) },
193 { 0, 0 },
194 2,
195 false,
196 false,
197 true,
198 },
199 { 0, 0 },
200 {
201 // add the offsets for each positions
202 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
203 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 1 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
204 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
205 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 3 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
206 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
207 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 5 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
208 },
209 },
210 {
211 "2x3 rect alternate",
212 {
213 2,
214 3,
215 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
216 { 0, 0 },
217 1,
218 true,
219 true,
220 true,
221 },
222 { 0, 0 },
223 {
224 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
225 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
226 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
227 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
228 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
229 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
230 },
231 },
232 {
233 "2x3 rect v then h",
234 {
235 2,
236 3,
237 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
238 { 0, 0 },
239 1,
240 true,
241 false,
242 false,
243 },
244 { 0, 0 },
245 {
246 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
247 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
248 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
249 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
250 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
251 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
252 },
253 },
254};
255// clang-format on
256
257
261BOOST_AUTO_TEST_CASE( GridGeometry )
262{
263 for( const auto& c : grid_geom_cases )
264 {
265 BOOST_TEST_CONTEXT( c.m_case_name )
266 {
267 ARRAY_GRID_OPTIONS grid_opts;
268
269 grid_opts.m_nx = c.m_geom.m_nx;
270 grid_opts.m_ny = c.m_geom.m_ny;
271 grid_opts.m_delta = c.m_geom.m_delta;
272 grid_opts.m_offset = c.m_geom.m_offset;
273 grid_opts.m_stagger = c.m_geom.m_stagger;
274 grid_opts.m_stagger_rows = c.m_geom.m_stagger_by_row;
275 grid_opts.m_reverseNumberingAlternate = c.m_geom.m_alternate_numbers;
276 grid_opts.m_horizontalThenVertical = c.m_geom.m_h_then_v;
277
278 CheckArrayTransforms( grid_opts, c.m_item_pos, c.m_exp_transforms );
279 }
280 }
281}
282
283
285{
286 int n;
289 bool rotate;
290};
291
293{
294 std::string m_case_name;
297 std::vector<ARRAY_OPTIONS::TRANSFORM> m_exp_transforms;
298};
299
300
301// clang-format off
302static const std::vector<CIRC_ARRAY_TEST_CASE> circ_geom_cases = {
303 {
304 "Quad, no rotate items",
305 {
306 4,
307 0,
308 { 0, 0 },
309 false,
310 },
311 { schIUScale.mmToIU( 10 ), 0 },
312 {
313 // diamond shape
314 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
315 { { schIUScale.mmToIU( -10 ), schIUScale.mmToIU( -10 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
316 { { schIUScale.mmToIU( -20 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
317 { {schIUScale.mmToIU( -10 ), schIUScale.mmToIU( 10 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
318 },
319 },
320 {
321 "Quad, rotate items",
322 {
323 4,
324 0,
325 { 0, 0 },
326 true,
327 },
328 { schIUScale.mmToIU( 10 ), 0 },
329 {
330 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
331 { { schIUScale.mmToIU( -10 ), schIUScale.mmToIU( -10 ) }, EDA_ANGLE( 90.0, DEGREES_T ) },
332 { { schIUScale.mmToIU( -20 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 180.0, DEGREES_T ) },
333 { {schIUScale.mmToIU( -10 ), schIUScale.mmToIU( 10 ) }, EDA_ANGLE( 270.0, DEGREES_T ) },
334 },
335 },
336 {
337 "Three pts, 90 deg angle",
338 {
339 3,
340 45.0,
341 { 0, 0 },
342 true,
343 },
344 { schIUScale.mmToIU( 10 ), 0 },
345 {
346 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
347 // 10 * [ 1-sin(45), sin(45) ]
348 { { schIUScale.mmToIU( -2.9289321881 ), schIUScale.mmToIU( -7.0710678118 ) }, EDA_ANGLE( 45.0, DEGREES_T ) },
349 { { schIUScale.mmToIU( -10 ), schIUScale.mmToIU( -10 ) }, EDA_ANGLE( 90.0, DEGREES_T ) },
350 },
351 },
352};
353// clang-format on
354
358BOOST_AUTO_TEST_CASE( CircularGeometry )
359{
360 for( const auto& c : circ_geom_cases )
361 {
362 BOOST_TEST_CONTEXT( c.m_case_name )
363 {
364 ARRAY_CIRCULAR_OPTIONS grid_opts;
365
366 grid_opts.m_nPts = c.m_geom.n;
367 grid_opts.m_angle = EDA_ANGLE( c.m_geom.angle_offset, DEGREES_T );
368 grid_opts.m_centre = c.m_geom.centre;
369 grid_opts.m_rotateItems = c.m_geom.rotate;
370
371 CheckArrayTransforms( grid_opts, c.m_item_pos, c.m_exp_transforms );
372 }
373 }
374}
375
381void CheckArrayNumbering( const ARRAY_OPTIONS& aOpts, const std::vector<std::string>& aExp )
382{
383 std::vector<std::string> names;
384
385 for( int i = 0; i < aOpts.GetArraySize(); ++i )
386 {
387 names.push_back( aOpts.GetItemNumber( i ).ToStdString() );
388 }
389
390 BOOST_CHECK_EQUAL_COLLECTIONS( names.begin(), names.end(), aExp.begin(), aExp.end() );
391}
392
393
395{
398 std::string m_start_at_x;
399 std::string m_start_at_y;
402 int m_nx;
403 int m_ny;
404};
405
406
408{
409 std::string m_case_name;
411 std::vector<std::string> m_exp_names;
412};
413
414
415// clang-format off
416static const std::vector<GRID_ARRAY_NAMING_CASE> grid_name_cases = {
417 {
418 "Linear grid",
419 {
422 "1",
423 "2",
424 false,
425 false, // doesn't matter
426 2,
427 3,
428 },
429 { "1", "2", "3", "4", "5", "6" },
430 },
431 {
432 // Tests a 2d grid
433 "2D grid A1",
434 {
437 "A",
438 "1",
439 true,
440 true,
441 2,
442 3,
443 },
444 { "A1", "B1", "A2", "B2", "A3", "B3" },
445 },
446 {
447 // Tests a 2d grid
448 "2D grid 11",
449 {
452 "1",
453 "1",
454 true,
455 false,
456 2,
457 3,
458 },
459 // moving down the "long axis" first
460 // so the first coordinate has a range of 1-3, the second 1-2
461 { "11", "21", "31", "12", "22", "32" },
462 },
463 {
464 // Tests a 2d grid, with different types and offsets (and alphabet wrap)
465 "2D grid offsets",
466 {
469 "5",
470 "Z",
471 true,
472 true,
473 2,
474 3,
475 },
476 { "5Z", "6Z", "5AA", "6AA", "5AB", "6AB" },
477 },
478};
479// clang-format on
480
481
486{
487 for( const auto& c : grid_name_cases )
488 {
489 BOOST_TEST_CONTEXT( c.m_case_name )
490 {
491 ARRAY_GRID_OPTIONS grid_opts;
492
493 grid_opts.m_nx = c.m_prms.m_nx;
494 grid_opts.m_ny = c.m_prms.m_ny;
495
496 grid_opts.m_horizontalThenVertical = c.m_prms.m_h_then_v;
497
498 grid_opts.m_pri_axis.SetAxisType( c.m_prms.m_pri_type );
499 grid_opts.m_sec_axis.SetAxisType( c.m_prms.m_sec_type );
500
501 grid_opts.m_pri_axis.SetOffset( c.m_prms.m_start_at_x );
502 grid_opts.m_sec_axis.SetOffset( c.m_prms.m_start_at_y );
503
504 grid_opts.m_2dArrayNumbering = c.m_prms.m_2d_numbering;
505
506 // other grid settings (geom) can be defaulted, as they don't affect numbering
507
508 CheckArrayNumbering( grid_opts, c.m_exp_names );
509 }
510 }
511}
512
513BOOST_AUTO_TEST_SUITE_END()
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
bool SetOffset(const wxString &aOffsetName)
Set the axis start (as a string, which should decode to a valid index in the alphabet)
Definition: array_axis.cpp:102
void SetAxisType(NUMBERING_TYPE aType)
Set the axis numbering type.
Definition: array_axis.cpp:96
@ NUMBERING_NUMERIC
Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11...
Definition: array_axis.h:44
@ NUMBERING_ALPHA_FULL
Full 26-character alphabet.
Definition: array_axis.h:52
Options that govern the setup of an "array" of multiple item.
Definition: array_options.h:38
virtual int GetArraySize() const =0
The number of points in this array.
virtual wxString GetItemNumber(int n) const =0
Get the position number (name) for the n'th array point.
virtual TRANSFORM GetTransform(int aN, const VECTOR2I &aPos) const =0
Get the transform of the n-th point in the array.
double AsDegrees() const
Definition: eda_angle.h:155
@ DEGREES_T
Definition: eda_angle.h:31
long m_nPts
number of point in the array
EDA_ANGLE m_angle
angle between points, or 0 for each point separated by this value (decideg)
bool m_reverseNumberingAlternate
ARRAY_AXIS m_sec_axis
ARRAY_AXIS m_pri_axis
Transform applied to an object by this array.
Definition: array_options.h:60
std::vector< ARRAY_OPTIONS::TRANSFORM > m_exp_transforms
CIRC_ARRAY_GEOM_PARAMS m_geom
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
Declare the test suite.
std::vector< std::string > m_exp_names
GRID_ARRAY_NAMING_PARAMS m_prms
ARRAY_AXIS::NUMBERING_TYPE m_pri_type
ARRAY_AXIS::NUMBERING_TYPE m_sec_type
GRID_ARRAY_GEOM_PARAMS m_geom
std::vector< ARRAY_OPTIONS::TRANSFORM > m_exp_transforms
std::ostream & operator<<(std::ostream &os, const ARRAY_OPTIONS::TRANSFORM &aObj)
Define a stream function for logging this type.
void CheckArrayTransforms(const ARRAY_OPTIONS &aOpts, const VECTOR2I &aPos, const std::vector< ARRAY_OPTIONS::TRANSFORM > &aExp)
Generate all array transforms for an array descriptor and compare against a list of expected transfor...
static const std::vector< CIRC_ARRAY_TEST_CASE > circ_geom_cases
static const std::vector< GRID_ARRAY_TEST_CASE > grid_geom_cases
BOOST_AUTO_TEST_CASE(GridGeometry)
Test of grid array geometry.
void CheckArrayNumbering(const ARRAY_OPTIONS &aOpts, const std::vector< std::string > &aExp)
Generate all array names and check against expected.
bool TransformIsClose(const ARRAY_OPTIONS::TRANSFORM &aL, const ARRAY_OPTIONS::TRANSFORM &aR)
Predicate to see if a ARRAY_OPTIONS::TRANSFORM is equal or nearly equal.
static const std::vector< GRID_ARRAY_NAMING_CASE > grid_name_cases
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
#define BOOST_TEST_CONTEXT(A)