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
40std::ostream& boost_test_print_type( std::ostream& os, const ARRAY_OPTIONS::TRANSFORM& aObj )
41{
42 os << "TRANSFORM[ " << aObj.m_offset << " r " << aObj.m_rotation.AsDegrees() << "deg"
43 << " ]";
44 return os;
45}
46
47
52{
53 return KI_TEST::IsVecWithinTol<VECTOR2I>( aL.m_offset, aR.m_offset, 1 )
54 && KI_TEST::IsWithin<double>( aL.m_rotation.AsDegrees(),
55 aR.m_rotation.AsDegrees(), 0.001 );
56}
57
58
66void CheckArrayTransforms( const ARRAY_OPTIONS& aOpts, const VECTOR2I& aPos,
67 const std::vector<ARRAY_OPTIONS::TRANSFORM>& aExp )
68{
69 std::vector<ARRAY_OPTIONS::TRANSFORM> transforms;
70
71 for( int i = 0; i < aOpts.GetArraySize(); ++i )
72 {
73 transforms.push_back( aOpts.GetTransform( i, aPos ) );
74 }
75
76 BOOST_CHECK_EQUAL( transforms.size(), aExp.size() );
77
78 for( unsigned i = 0; i < std::min( transforms.size(), aExp.size() ); ++i )
79 {
80 BOOST_TEST_CONTEXT( "Index " << i )
81 {
82 BOOST_CHECK_PREDICATE( TransformIsClose, ( transforms[i] )( aExp[i] ) );
83 }
84 }
85}
86
87
91BOOST_AUTO_TEST_SUITE( ArrayOptions )
92
93
95{
96 int m_nx;
97 int m_ny;
104};
105
107{
108 std::string m_case_name;
111 std::vector<ARRAY_OPTIONS::TRANSFORM> m_exp_transforms;
112};
113
114
115// clang-format off
116static const std::vector<GRID_ARRAY_TEST_CASE> grid_geom_cases = {
117 {
118 "2x3 rect grid",
119 {
120 2,
121 3,
122 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
123 { 0, 0 },
124 1,
125 true,
126 false,
127 true,
128 },
129 { 0, 0 },
130 {
131 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
132 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
133 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
134 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
135 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
136 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
137 },
138 },
139 {
140 "2x3 offset grid",
141 {
142 2,
143 3,
144 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
145 { schIUScale.mmToIU( 0.1 ), schIUScale.mmToIU( 0.2 ) },
146 1,
147 true,
148 false,
149 true,
150 },
151 { 0, 0 },
152 {
153 // add the offsets for each positions
154 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
155 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0.2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
156 { { schIUScale.mmToIU( 0.1 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
157 { { schIUScale.mmToIU( 2.1 ), schIUScale.mmToIU( 2.2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
158 { { schIUScale.mmToIU( 0.2 ), schIUScale.mmToIU( 4.0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
159 { { schIUScale.mmToIU( 2.2 ), schIUScale.mmToIU( 4.2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
160 },
161 },
162 {
163 "2x3 stagger rows",
164 {
165 2,
166 3,
167 { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 2 ) },
168 { 0, 0 },
169 3,
170 true,
171 false,
172 true,
173 },
174 { 0, 0 },
175 {
176 // add the offsets for each positions
177 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
178 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
179 { { schIUScale.mmToIU( 1 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
180 { { schIUScale.mmToIU( 4 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
181 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
182 { { schIUScale.mmToIU( 5 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
183 },
184 },
185 {
186 "2x3 stagger cols",
187 {
188 2,
189 3,
190 { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 2 ) },
191 { 0, 0 },
192 2,
193 false,
194 false,
195 true,
196 },
197 { 0, 0 },
198 {
199 // add the offsets for each positions
200 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
201 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 1 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
202 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
203 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 3 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
204 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
205 { { schIUScale.mmToIU( 3 ), schIUScale.mmToIU( 5 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
206 },
207 },
208 {
209 "2x3 rect alternate",
210 {
211 2,
212 3,
213 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
214 { 0, 0 },
215 1,
216 true,
217 true,
218 true,
219 },
220 { 0, 0 },
221 {
222 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
223 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
224 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
225 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
226 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
227 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
228 },
229 },
230 {
231 "2x3 rect v then h",
232 {
233 2,
234 3,
235 { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) },
236 { 0, 0 },
237 1,
238 true,
239 false,
240 false,
241 },
242 { 0, 0 },
243 {
244 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
245 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
246 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
247 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
248 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 2 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
249 { { schIUScale.mmToIU( 2 ), schIUScale.mmToIU( 4 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
250 },
251 },
252};
253// clang-format on
254
255
259BOOST_AUTO_TEST_CASE( GridGeometry )
260{
261 for( const auto& c : grid_geom_cases )
262 {
263 BOOST_TEST_CONTEXT( c.m_case_name )
264 {
265 ARRAY_GRID_OPTIONS grid_opts;
266
267 grid_opts.m_nx = c.m_geom.m_nx;
268 grid_opts.m_ny = c.m_geom.m_ny;
269 grid_opts.m_delta = c.m_geom.m_delta;
270 grid_opts.m_offset = c.m_geom.m_offset;
271 grid_opts.m_stagger = c.m_geom.m_stagger;
272 grid_opts.m_stagger_rows = c.m_geom.m_stagger_by_row;
273 grid_opts.m_reverseNumberingAlternate = c.m_geom.m_alternate_numbers;
274 grid_opts.m_horizontalThenVertical = c.m_geom.m_h_then_v;
275
276 CheckArrayTransforms( grid_opts, c.m_item_pos, c.m_exp_transforms );
277 }
278 }
279}
280
281
283{
284 int n;
287 bool rotate;
288};
289
291{
292 std::string m_case_name;
295 std::vector<ARRAY_OPTIONS::TRANSFORM> m_exp_transforms;
296};
297
298
299// clang-format off
300static const std::vector<CIRC_ARRAY_TEST_CASE> circ_geom_cases = {
301 {
302 "Quad, no rotate items",
303 {
304 4,
305 0,
306 { 0, 0 },
307 false,
308 },
309 { schIUScale.mmToIU( 10 ), 0 },
310 {
311 // diamond shape
312 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
313 { { schIUScale.mmToIU( -10 ), schIUScale.mmToIU( -10 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
314 { { schIUScale.mmToIU( -20 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
315 { {schIUScale.mmToIU( -10 ), schIUScale.mmToIU( 10 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
316 },
317 },
318 {
319 "Quad, rotate items",
320 {
321 4,
322 0,
323 { 0, 0 },
324 true,
325 },
326 { schIUScale.mmToIU( 10 ), 0 },
327 {
328 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
329 { { schIUScale.mmToIU( -10 ), schIUScale.mmToIU( -10 ) }, EDA_ANGLE( 90.0, DEGREES_T ) },
330 { { schIUScale.mmToIU( -20 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 180.0, DEGREES_T ) },
331 { {schIUScale.mmToIU( -10 ), schIUScale.mmToIU( 10 ) }, EDA_ANGLE( 270.0, DEGREES_T ) },
332 },
333 },
334 {
335 "Three pts, 90 deg angle",
336 {
337 3,
338 45.0,
339 { 0, 0 },
340 true,
341 },
342 { schIUScale.mmToIU( 10 ), 0 },
343 {
344 { { schIUScale.mmToIU( 0 ), schIUScale.mmToIU( 0 ) }, EDA_ANGLE( 0.0, DEGREES_T ) },
345 // 10 * [ 1-sin(45), sin(45) ]
346 { { schIUScale.mmToIU( -2.9289321881 ), schIUScale.mmToIU( -7.0710678118 ) }, EDA_ANGLE( 45.0, DEGREES_T ) },
347 { { schIUScale.mmToIU( -10 ), schIUScale.mmToIU( -10 ) }, EDA_ANGLE( 90.0, DEGREES_T ) },
348 },
349 },
350};
351// clang-format on
352
356BOOST_AUTO_TEST_CASE( CircularGeometry )
357{
358 for( const auto& c : circ_geom_cases )
359 {
360 BOOST_TEST_CONTEXT( c.m_case_name )
361 {
362 ARRAY_CIRCULAR_OPTIONS grid_opts;
363
364 grid_opts.m_nPts = c.m_geom.n;
365 grid_opts.m_angle = EDA_ANGLE( c.m_geom.angle_offset, DEGREES_T );
366 grid_opts.m_centre = c.m_geom.centre;
367 grid_opts.m_rotateItems = c.m_geom.rotate;
368
369 CheckArrayTransforms( grid_opts, c.m_item_pos, c.m_exp_transforms );
370 }
371 }
372}
373
379void CheckArrayNumbering( const ARRAY_OPTIONS& aOpts, const std::vector<std::string>& aExp )
380{
381 std::vector<std::string> names;
382
383 for( int i = 0; i < aOpts.GetArraySize(); ++i )
384 {
385 names.push_back( aOpts.GetItemNumber( i ).ToStdString() );
386 }
387
388 BOOST_CHECK_EQUAL_COLLECTIONS( names.begin(), names.end(), aExp.begin(), aExp.end() );
389}
390
391
393{
396 std::string m_start_at_x;
397 std::string m_start_at_y;
400 int m_nx;
401 int m_ny;
402};
403
404
406{
407 std::string m_case_name;
409 std::vector<std::string> m_exp_names;
410};
411
412
413// clang-format off
414static const std::vector<GRID_ARRAY_NAMING_CASE> grid_name_cases = {
415 {
416 "Linear grid",
417 {
420 "1",
421 "2",
422 false,
423 false, // doesn't matter
424 2,
425 3,
426 },
427 { "1", "2", "3", "4", "5", "6" },
428 },
429 {
430 // Tests a 2d grid
431 "2D grid A1",
432 {
435 "A",
436 "1",
437 true,
438 true,
439 2,
440 3,
441 },
442 { "A1", "B1", "A2", "B2", "A3", "B3" },
443 },
444 {
445 // Tests a 2d grid
446 "2D grid 11",
447 {
450 "1",
451 "1",
452 true,
453 false,
454 2,
455 3,
456 },
457 // moving down the "long axis" first
458 // so the first coordinate has a range of 1-3, the second 1-2
459 { "11", "21", "31", "12", "22", "32" },
460 },
461 {
462 // Tests a 2d grid, with different types and offsets (and alphabet wrap)
463 "2D grid offsets",
464 {
467 "5",
468 "Z",
469 true,
470 true,
471 2,
472 3,
473 },
474 { "5Z", "6Z", "5AA", "6AA", "5AB", "6AB" },
475 },
476};
477// clang-format on
478
479
484{
485 for( const auto& c : grid_name_cases )
486 {
487 BOOST_TEST_CONTEXT( c.m_case_name )
488 {
489 ARRAY_GRID_OPTIONS grid_opts;
490
491 grid_opts.m_nx = c.m_prms.m_nx;
492 grid_opts.m_ny = c.m_prms.m_ny;
493
494 grid_opts.m_horizontalThenVertical = c.m_prms.m_h_then_v;
495
496 grid_opts.m_pri_axis.SetAxisType( c.m_prms.m_pri_type );
497 grid_opts.m_sec_axis.SetAxisType( c.m_prms.m_sec_type );
498
499 grid_opts.m_pri_axis.SetOffset( c.m_prms.m_start_at_x );
500 grid_opts.m_sec_axis.SetOffset( c.m_prms.m_start_at_y );
501
502 grid_opts.m_2dArrayNumbering = c.m_prms.m_2d_numbering;
503
504 // other grid settings (geom) can be defaulted, as they don't affect numbering
505
506 CheckArrayNumbering( grid_opts, c.m_exp_names );
507 }
508 }
509}
510
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:104
void SetAxisType(NUMBERING_TYPE aType)
Set the axis numbering type.
Definition: array_axis.cpp:98
@ 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:113
@ 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
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.
std::ostream & boost_test_print_type(std::ostream &os, const ARRAY_OPTIONS::TRANSFORM &aObj)
Define a stream function for logging this type.
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)
BOOST_AUTO_TEST_SUITE_END()