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