KiCad PCB EDA Suite
am_primitive.cpp
Go to the documentation of this file.
1 
5 /*
6  * This program source code file is part of KiCad, a free EDA CAD application.
7  *
8  * Copyright (C) 1992-2017 Jean-Pierre Charras <jp.charras at wanadoo.fr>
9  * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
10  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, you may find one here:
24  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
25  * or you may search the http://www.gnu.org website for the version 2 license,
26  * or you may write to the Free Software Foundation, Inc.,
27  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
28  */
29 
30 #include <trigo.h>
31 #include <convert_to_biu.h>
33 #include <math/util.h> // for KiROUND
34 
35 #include <gerbview.h>
36 #include <gerber_file_image.h>
37 
38 
42 extern int scaletoIU( double aCoord, bool isMetric );
43 
44 
51 static wxPoint mapPt( double x, double y, bool isMetric )
52 {
53  wxPoint ret( scaletoIU( x, isMetric ), scaletoIU( y, isMetric ) );
54 
55  return ret;
56 }
57 
58 
60 {
61  /*
62  * Some but not all primitives use the first parameter as an exposure control.
63  * Others are always ON.
64  * In a aperture macro shape, a basic primitive with exposure off is a hole in the shape
65  * it is NOT a negative shape
66  */
67  wxASSERT( params.size() );
68 
69  switch( primitive_id )
70  {
71  case AMP_CIRCLE:
72  case AMP_LINE2:
73  case AMP_LINE20:
74  case AMP_LINE_CENTER:
76  case AMP_OUTLINE:
77  case AMP_POLYGON:
78  // All have an exposure parameter and can return a value (0 or 1)
79  return params[0].GetValue( aParent->GetDcodeDescr() ) != 0;
80  break;
81 
82  case AMP_THERMAL: // Exposure is always on
83  case AMP_MOIRE: // Exposure is always on
84  case AMP_EOF:
85  case AMP_UNKNOWN:
86  default:
87  return 1; // All have no exposure parameter and are always 0N return true
88  break;
89  }
90 }
91 
92 
93 // TODO(snh): Remove hard coded count
94 const int seg_per_circle = 64; // Number of segments to approximate a circle
95 
96 
97 void AM_PRIMITIVE::DrawBasicShape( const GERBER_DRAW_ITEM* aParent, SHAPE_POLY_SET& aShapeBuffer,
98  const wxPoint& aShapePos )
99 {
100 #define TO_POLY_SHAPE \
101  { \
102  if( polybuffer.size() > 1 ) \
103  { \
104  aShapeBuffer.NewOutline(); \
105  \
106  for( unsigned jj = 0; jj < polybuffer.size(); jj++ ) \
107  aShapeBuffer.Append( polybuffer[jj].x, polybuffer[jj].y ); \
108  \
109  aShapeBuffer.Append( polybuffer[0].x, polybuffer[0].y ); \
110  } \
111  }
112 
113  // Draw the primitive shape for flashed items.
114  // Create a static buffer to avoid a lot of memory reallocation.
115  static std::vector<wxPoint> polybuffer;
116  polybuffer.clear();
117 
118  wxPoint curPos = aShapePos;
119  D_CODE* tool = aParent->GetDcodeDescr();
120  double rotation;
121 
122  switch( primitive_id )
123  {
124  case AMP_CIRCLE: // Circle, given diameter and position
125  {
126  /* Generated by an aperture macro declaration like:
127  * "1,1,0.3,0.5, 1.0*"
128  * type (1), exposure, diameter, pos.x, pos.y, <rotation>
129  * <rotation> is a optional parameter: rotation from origin.
130  * type is not stored in parameters list, so the first parameter is exposure
131  */
132  ConvertShapeToPolygon( aParent, polybuffer );
133 
134  // shape rotation (if any):
135  if( params.size() >= 5 )
136  {
137  rotation = params[4].GetValue( tool ) * 10.0;
138 
139  if( rotation != 0)
140  {
141  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
142  RotatePoint( &polybuffer[ii], -rotation );
143  }
144  }
145 
146 
147  // Move to current position:
148  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
149  {
150  polybuffer[ii] += curPos;
151  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
152  }
153 
155  break;
156  }
157 
158  case AMP_LINE2:
159  case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
160  {
161  /* Vector Line, Primitive Code 20.
162  * A vector line is a rectangle defined by its line width, start and end points.
163  * The line ends are rectangular.
164  */
165  /* Generated by an aperture macro declaration like:
166  * "2,1,0.3,0,0, 0.5, 1.0,-135*"
167  * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation
168  * type is not stored in parameters list, so the first parameter is exposure
169  */
170  ConvertShapeToPolygon( aParent, polybuffer );
171 
172  // shape rotation:
173  rotation = params[6].GetValue( tool ) * 10.0;
174 
175  if( rotation != 0)
176  {
177  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
178  RotatePoint( &polybuffer[ii], -rotation );
179  }
180 
181  // Move to current position:
182  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
183  {
184  polybuffer[ii] += curPos;
185  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
186  }
187 
189  break;
190  }
191 
192  case AMP_LINE_CENTER:
193  {
194  /* Center Line, Primitive Code 21
195  * A center line primitive is a rectangle defined by its width, height, and center point
196  */
197  /* Generated by an aperture macro declaration like:
198  * "21,1,0.3,0.03,0,0,-135*"
199  * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation
200  * type is not stored in parameters list, so the first parameter is exposure
201  */
202  ConvertShapeToPolygon( aParent, polybuffer );
203 
204  // shape rotation:
205  rotation = params[5].GetValue( tool ) * 10.0;
206 
207  if( rotation != 0 )
208  {
209  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
210  RotatePoint( &polybuffer[ii], -rotation );
211  }
212 
213  // Move to current position:
214  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
215  {
216  polybuffer[ii] += curPos;
217  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
218  }
219 
221  break;
222  }
223 
224  case AMP_LINE_LOWER_LEFT:
225  {
226  /* Generated by an aperture macro declaration like:
227  * "22,1,0.3,0.03,0,0,-135*"
228  * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation
229  * type is not stored in parameters list, so the first parameter is exposure
230  */
231  ConvertShapeToPolygon( aParent, polybuffer );
232 
233  // shape rotation:
234  rotation = params[5].GetValue( tool ) * 10.0;
235  if( rotation != 0)
236  {
237  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
238  RotatePoint( &polybuffer[ii], -rotation );
239  }
240 
241  // Move to current position:
242  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
243  {
244  polybuffer[ii] += curPos;
245  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
246  }
247 
249  break;
250  }
251 
252  case AMP_THERMAL:
253  {
254  /* Generated by an aperture macro declaration like:
255  * "7, 0,0,1.0,0.3,0.01,-13*"
256  * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation
257  * type is not stored in parameters list, so the first parameter is center.x
258  *
259  * The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is always
260  * on.
261  */
262  std::vector<wxPoint> subshape_poly;
263  curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric );
264  ConvertShapeToPolygon( aParent, subshape_poly );
265 
266  // shape rotation:
267  rotation = params[5].GetValue( tool ) * 10.0;
268 
269  // Because a thermal shape has 4 identical sub-shapes, only one is created in subshape_poly.
270  // We must draw 4 sub-shapes rotated by 90 deg
271  for( int ii = 0; ii < 4; ii++ )
272  {
273  polybuffer = subshape_poly;
274  double sub_rotation = rotation + 900 * ii;
275 
276  for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
277  RotatePoint( &polybuffer[jj], -sub_rotation );
278 
279  // Move to current position:
280  for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
281  {
282  polybuffer[jj] += curPos;
283  polybuffer[jj] = aParent->GetABPosition( polybuffer[jj] );
284  }
285 
287  }
288  }
289  break;
290 
291  case AMP_MOIRE:
292  {
293  /* Moire, Primitive Code 6
294  * The moire primitive is a cross hair centered on concentric rings (annuli).
295  * Exposure is always on.
296  */
297  curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ),
298  m_GerbMetric );
299 
300  /* Generated by an aperture macro declaration like:
301  * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0"
302  * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness,
303  * crosshair len, rotation. The type is not stored in parameters list, so the first
304  * parameter is pos.x.
305  */
306  int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric );
307  int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric );
308  int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric );
309  int numCircles = KiROUND( params[5].GetValue( tool ) );
310 
311  // Draw circles:
312  wxPoint center = aParent->GetABPosition( curPos );
313 
314  // adjust outerDiam by this on each nested circle
315  int diamAdjust = ( gap + penThickness ) * 2;
316 
317  for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust )
318  {
319  if( outerDiam <= 0 )
320  break;
321 
322  // Note: outerDiam is the outer diameter of the ring.
323  // the ring graphic diameter is (outerDiam - penThickness)
324  if( outerDiam <= penThickness )
325  { // No room to draw a ring (no room for the hole):
326  // draw a circle instead (with no hole), with the right diameter
327  TransformCircleToPolygon( aShapeBuffer, center, outerDiam / 2, ARC_HIGH_DEF,
328  ERROR_INSIDE );
329  }
330  else
331  {
332  TransformRingToPolygon( aShapeBuffer, center, ( outerDiam - penThickness ) / 2,
333  penThickness, ARC_HIGH_DEF, ERROR_INSIDE );
334  }
335  }
336 
337  // Draw the cross:
338  ConvertShapeToPolygon( aParent, polybuffer );
339 
340  rotation = params[8].GetValue( tool ) * 10.0;
341 
342  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
343  {
344  // shape rotation:
345  RotatePoint( &polybuffer[ii], -rotation );
346 
347  // Move to current position:
348  polybuffer[ii] += curPos;
349  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
350  }
351 
353  break;
354  }
355 
356  case AMP_OUTLINE:
357  {
358  /* Outline, Primitive Code 4
359  * An outline primitive is an area enclosed by an n-point polygon defined by its start
360  * point and n
361  * subsequent points. The outline must be closed, i.e. the last point must be equal to
362  * the start point. There must be at least one subsequent point (to close the outline).
363  * The outline of the primitive is actually the contour (see 2.6) that consists of linear
364  * segments only, so it must conform to all the requirements described for contours.
365  * Warning: Make no mistake: n is the number of subsequent points, being the number of
366  * vertices of the outline or one less than the number of coordinate pairs.
367  */
368  /* Generated by an aperture macro declaration like:
369  * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25"
370  * type(4), exposure, corners count, corner1.x, corner.1y, ..., corner1.x, corner.1y,
371  * rotation
372  * type is not stored in parameters list, so the first parameter is exposure
373  */
374  // params[0] is the exposure and params[1] is the corners count after the first corner
375  int numCorners = (int) params[1].GetValue( tool );
376 
377  // the shape rotation is the last param of list, after corners
378  int last_prm = params.size() - 1;
379  rotation = params[last_prm].GetValue( tool ) * 10.0;
380  wxPoint pos;
381 
382  // Read points.
383  // Note: numCorners is the polygon corner count, following the first corner
384  // * the polygon is always closed,
385  // * therefore the last XY coordinate is the same as the first
386  int prm_idx = 2; // params[2] is the first X coordinate
387 
388  for( int i = 0; i <= numCorners; ++i )
389  {
390  pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
391  prm_idx++;
392  pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
393  prm_idx++;
394  polybuffer.push_back(pos);
395 
396  // Guard: ensure prm_idx < last_prm
397  // I saw malformed gerber files with numCorners = number
398  // of coordinates instead of number of coordinates following the first point
399  if( prm_idx >= last_prm )
400  break;
401  }
402 
403  // rotate polygon and move it to the actual position shape rotation:
404  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
405  {
406  RotatePoint( &polybuffer[ii], -rotation );
407  }
408 
409  // Move to current position:
410  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
411  {
412  polybuffer[ii] += curPos;
413  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
414  }
415 
417  break;
418  }
419 
420  case AMP_POLYGON:
421  /* Polygon, Primitive Code 5
422  * A polygon primitive is a regular polygon defined by the number of vertices n, the
423  * center point and the diameter of the circumscribed circle
424  */
425  /* Generated by an aperture macro declaration like:
426  * "5,1,0.6,0,0,0.5,25"
427  * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation
428  * type is not stored in parameters list, so the first parameter is exposure
429  */
430  curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric );
431 
432  // Creates the shape:
433  ConvertShapeToPolygon( aParent, polybuffer );
434 
435  // rotate polygon and move it to the actual position
436  rotation = params[5].GetValue( tool ) * 10.0;
437  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
438  {
439  RotatePoint( &polybuffer[ii], -rotation );
440  polybuffer[ii] += curPos;
441  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
442  }
443 
445 
446  break;
447 
448  case AMP_EOF:
449  // not yet supported, waiting for you.
450  break;
451 
452  case AMP_UNKNOWN:
453  default:
454  break;
455  }
456 }
457 
458 
460  std::vector<wxPoint>& aBuffer )
461 {
462  D_CODE* tool = aParent->GetDcodeDescr();
463 
464  switch( primitive_id )
465  {
466  case AMP_CIRCLE:
467  {
468  /* Generated by an aperture macro declaration like:
469  * "1,1,0.3,0.5, 1.0*"
470  * type (1), exposure, diameter, pos.x, pos.y, <rotation>
471  * <rotation> is a optional parameter: rotation from origin.
472  * type is not stored in parameters list, so the first parameter is exposure
473  */
474  int radius = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ) / 2;
475 
476  // A circle primitive can have a 0 size (for instance when used in roundrect macro),
477  // so skip it
478  if( radius <= 0 )
479  break;
480 
481  wxPoint center = mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ),
482  m_GerbMetric );
483  wxPoint corner;
484  const int delta = 3600 / seg_per_circle; // rot angle in 0.1 degree
485 
486  for( int angle = 0; angle < 3600; angle += delta )
487  {
488  corner.x = radius;
489  corner.y = 0;
490  RotatePoint( &corner, angle );
491  corner += center;
492  aBuffer.push_back( corner );
493  }
494 
495  break;
496  }
497 
498  case AMP_LINE2:
499  case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
500  {
501  int width = scaletoIU( params[1].GetValue( tool ), m_GerbMetric );
502  wxPoint start = mapPt( params[2].GetValue( tool ),
503  params[3].GetValue( tool ), m_GerbMetric );
504  wxPoint end = mapPt( params[4].GetValue( tool ),
505  params[5].GetValue( tool ), m_GerbMetric );
506  wxPoint delta = end - start;
507  int len = KiROUND( EuclideanNorm( delta ) );
508 
509  // To build the polygon, we must create a horizontal polygon starting to "start"
510  // and rotate it to have the end point to "end"
511  wxPoint currpt;
512  currpt.y += width / 2; // Upper left
513  aBuffer.push_back( currpt );
514  currpt.x = len; // Upper right
515  aBuffer.push_back( currpt );
516  currpt.y -= width; // lower right
517  aBuffer.push_back( currpt );
518  currpt.x = 0; // lower left
519  aBuffer.push_back( currpt );
520 
521  // Rotate rectangle and move it to the actual start point
522  double angle = ArcTangente( delta.y, delta.x );
523 
524  for( unsigned ii = 0; ii < 4; ii++ )
525  {
526  RotatePoint( &aBuffer[ii], -angle );
527  aBuffer[ii] += start;
528  }
529 
530  break;
531  }
532 
533  case AMP_LINE_CENTER:
534  {
535  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
536  m_GerbMetric );
537  wxPoint pos = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ),
538  m_GerbMetric );
539 
540  // Build poly:
541  pos.x -= size.x / 2;
542  pos.y -= size.y / 2; // Lower left
543  aBuffer.push_back( pos );
544  pos.y += size.y; // Upper left
545  aBuffer.push_back( pos );
546  pos.x += size.x; // Upper right
547  aBuffer.push_back( pos );
548  pos.y -= size.y; // lower right
549  aBuffer.push_back( pos );
550  break;
551  }
552 
553  case AMP_LINE_LOWER_LEFT:
554  {
555  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
556  m_GerbMetric );
557  wxPoint lowerLeft = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ),
558  m_GerbMetric );
559 
560  // Build poly:
561  aBuffer.push_back( lowerLeft );
562  lowerLeft.y += size.y; // Upper left
563  aBuffer.push_back( lowerLeft );
564  lowerLeft.x += size.x; // Upper right
565  aBuffer.push_back( lowerLeft );
566  lowerLeft.y -= size.y; // lower right
567  aBuffer.push_back( lowerLeft );
568  break;
569  }
570 
571  case AMP_THERMAL:
572  {
573  // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from
574  // this first rotated by 90, 180 and 270 deg.
575  // params = center.x (unused here), center.y (unused here), outside diam, inside diam,
576  // crosshair thickness.
577  int outerRadius = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2;
578  int innerRadius = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ) / 2;
579 
580  // Safety checks to guarantee no divide-by-zero
581  outerRadius = std::max( 1, outerRadius );
582  innerRadius = std::max( 1, innerRadius );
583 
584  int halfthickness = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
585  double angle_start = RAD2DECIDEG( asin( (double) halfthickness / innerRadius ) );
586 
587  // Draw shape in the first quadrant (X and Y > 0)
588  wxPoint pos, startpos;
589 
590  // Inner arc
591  startpos.x = innerRadius;
592  double angle_end = 900 - angle_start;
593 
594  for( double angle = angle_start; angle < angle_end; angle += 100 )
595  {
596  pos = startpos;
597  RotatePoint( &pos, angle );
598  aBuffer.push_back( pos );
599  }
600 
601  // Last point
602  pos = startpos;
603  RotatePoint( &pos, angle_end );
604  aBuffer.push_back( pos );
605 
606  // outer arc
607  startpos.x = outerRadius;
608  startpos.y = 0;
609  angle_start = RAD2DECIDEG( asin( (double) halfthickness / outerRadius ) );
610  angle_end = 900 - angle_start;
611 
612  // First point, near Y axis, outer arc
613  for( double angle = angle_end; angle > angle_start; angle -= 100 )
614  {
615  pos = startpos;
616  RotatePoint( &pos, angle );
617  aBuffer.push_back( pos );
618  }
619 
620  // last point
621  pos = startpos;
622  RotatePoint( &pos, angle_start );
623  aBuffer.push_back( pos );
624 
625  aBuffer.push_back( aBuffer[0] ); // Close poly
626  }
627  break;
628 
629  case AMP_MOIRE:
630  {
631  // A cross hair with n concentric circles. Only the cross is built as
632  // polygon because circles can be drawn easily
633  int crossHairThickness = scaletoIU( params[6].GetValue( tool ), m_GerbMetric );
634  int crossHairLength = scaletoIU( params[7].GetValue( tool ), m_GerbMetric );
635 
636  // Create cross. First create 1/4 of the shape.
637  // Others point are the same, rotated by 90, 180 and 270 deg
638  wxPoint pos( crossHairThickness / 2, crossHairLength / 2 );
639  aBuffer.push_back( pos );
640  pos.y = crossHairThickness / 2;
641  aBuffer.push_back( pos );
642  pos.x = -crossHairLength / 2;
643  aBuffer.push_back( pos );
644  pos.y = -crossHairThickness / 2;
645  aBuffer.push_back( pos );
646 
647  // Copy the 4 shape, rotated by 90, 180 and 270 deg
648  for( int jj = 1; jj <= 3; jj ++ )
649  {
650  for( int ii = 0; ii < 4; ii++ )
651  {
652  pos = aBuffer[ii];
653  RotatePoint( &pos, jj*900 );
654  aBuffer.push_back( pos );
655  }
656  }
657 
658  break;
659  }
660 
661  case AMP_OUTLINE:
662  // already is a polygon. Do nothing
663  break;
664 
665  case AMP_POLYGON: // Creates a regular polygon
666  {
667  int vertexcount = KiROUND( params[1].GetValue( tool ) );
668  int radius = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
669 
670  // rs274x said: vertex count = 3 ... 10, and the first corner is on the X axis
671  if( vertexcount < 3 )
672  vertexcount = 3;
673 
674  if( vertexcount > 10 )
675  vertexcount = 10;
676 
677  for( int ii = 0; ii <= vertexcount; ii++ )
678  {
679  wxPoint pos( radius, 0);
680  RotatePoint( &pos, ii * 3600 / vertexcount );
681  aBuffer.push_back( pos );
682  }
683 
684  break;
685  }
686 
687  case AMP_COMMENT:
688  case AMP_UNKNOWN:
689  case AMP_EOF:
690  break;
691  }
692 }
693 
694 
696 {
697  int dim = -1;
698  D_CODE* tool = aParent->GetDcodeDescr();
699 
700  switch( primitive_id )
701  {
702  case AMP_CIRCLE:
703  // params = exposure, diameter, pos.x, pos.y
704  dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // Diameter
705  break;
706 
707  case AMP_LINE2:
708  case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
709  dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // line width
710  break;
711 
712  case AMP_LINE_CENTER:
713  {
714  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
715  m_GerbMetric );
716  dim = std::min(size.x, size.y);
717  break;
718  }
719 
720  case AMP_LINE_LOWER_LEFT:
721  {
722  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
723  m_GerbMetric );
724  dim = std::min(size.x, size.y);
725  break;
726  }
727 
728  case AMP_THERMAL:
729  {
730  // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from
731  // this first rotated by 90, 180 and 270 deg.
732  // params = center.x (unused here), center.y (unused here), outside diam, inside diam,
733  // crosshair thickness.
734  dim = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2; // Outer diam
735  break;
736  }
737 
738  case AMP_MOIRE: // A cross hair with n concentric circles.
739  dim = scaletoIU( params[7].GetValue( tool ), m_GerbMetric ); // = cross hair len
740  break;
741 
742  case AMP_OUTLINE: // a free polygon :
743  {
744  // dim = min side of the bounding box (this is a poor criteria, but what is a good
745  // criteria b?)
746  // exposure, corners count, corner1.x, corner.1y, ..., rotation
747  // note: corners count is the count of corners following corner1
748  int numPoints = (int) params[1].GetValue( tool );
749 
750  // Read points. numPoints does not include the starting point, so add 1.
751  // and calculate the bounding box;
752  wxSize pos_min, pos_max, pos;
753  int prm_idx = 2; // params[2] is the first X coordinate
754  int last_prm = params.size() - 1;
755 
756  for( int i = 0; i<= numPoints; ++i )
757  {
758  pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
759  prm_idx++;
760  pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
761  prm_idx++;
762 
763  if( i == 0 )
764  {
765  pos_min = pos_max = pos;
766  }
767  else
768  {
769  // upper right corner:
770  if( pos_min.x > pos.x )
771  pos_min.x = pos.x;
772 
773  if( pos_min.y > pos.y )
774  pos_min.y = pos.y;
775 
776  // lower left corner:
777  if( pos_max.x < pos.x )
778  pos_max.x = pos.x;
779 
780  if( pos_max.y < pos.y )
781  pos_max.y = pos.y;
782  }
783 
784  // Guard: ensure prm_idx < last_prm (last prm is orientation)
785  // I saw malformed gerber files with numCorners = number
786  // of coordinates instead of number of coordinates following the first point
787  if( prm_idx >= last_prm )
788  break;
789  }
790 
791  // calculate dim
792  wxSize size;
793  size.x = pos_max.x - pos_min.x;
794  size.y = pos_max.y - pos_min.y;
795  dim = std::min( size.x, size.y );
796  break;
797  }
798 
799  case AMP_POLYGON: // Regular polygon
800  dim = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2; // Radius
801  break;
802 
803  case AMP_COMMENT:
804  case AMP_UNKNOWN:
805  case AMP_EOF:
806  break;
807  }
808 
809  return dim;
810 }
811 
812 
814  const wxPoint& aShapePos )
815 {
816  SHAPE_POLY_SET holeBuffer;
817  bool hasHole = false;
818 
820 
821  for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
822  prim_macro != primitives.end(); ++prim_macro )
823  {
824  if( prim_macro->primitive_id == AMP_COMMENT )
825  continue;
826 
827  if( prim_macro->IsAMPrimitiveExposureOn( aParent ) )
828  {
829  prim_macro->DrawBasicShape( aParent, m_shape, aShapePos );
830  }
831  else
832  {
833  prim_macro->DrawBasicShape( aParent, holeBuffer, aShapePos );
834 
835  if( holeBuffer.OutlineCount() ) // we have a new hole in shape: remove the hole
836  {
838  holeBuffer.RemoveAllContours();
839  hasHole = true;
840  }
841  }
842  }
843 
844  // Merge and cleanup basic shape polygons
846 
847  // If a hole is defined inside a polygon, we must fracture the polygon
848  // to be able to drawn it (i.e link holes by overlapping edges)
849  if( hasHole )
851 
852  m_boundingBox = EDA_RECT( wxPoint( 0, 0 ), wxSize( 1, 1 ) );
853  auto bb = m_shape.BBox();
854  wxPoint center( bb.Centre().x, bb.Centre().y );
855  m_boundingBox.Move( aParent->GetABPosition( center ) );
856  m_boundingBox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
857 
858  return &m_shape;
859 }
860 
861 
863  wxDC* aDC, const COLOR4D& aColor,
864  const wxPoint& aShapePos, bool aFilledShape )
865 {
866  SHAPE_POLY_SET* shapeBuffer = GetApertureMacroShape( aParent, aShapePos );
867 
868  if( shapeBuffer->OutlineCount() == 0 )
869  return;
870 
871  for( int ii = 0; ii < shapeBuffer->OutlineCount(); ii++ )
872  {
873  SHAPE_LINE_CHAIN& poly = shapeBuffer->Outline( ii );
874 
875  GRClosedPoly( aClipBox, aDC, poly.PointCount(), (wxPoint*) &poly.CPoint( 0 ), aFilledShape,
876  aColor, aColor );
877  }
878 }
879 
880 
882 {
883  int dim = -1;
884 
885  for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
886  prim_macro != primitives.end(); ++prim_macro )
887  {
888  int pdim = prim_macro->GetShapeDim( aParent );
889 
890  if( dim < pdim )
891  dim = pdim;
892  }
893 
894  return dim;
895 }
896 
897 
898 double APERTURE_MACRO::GetLocalParam( const D_CODE* aDcode, unsigned aParamId ) const
899 {
900  // find parameter descr.
901  const AM_PARAM * param = nullptr;
902 
903  for( unsigned ii = 0; ii < m_localparamStack.size(); ii ++ )
904  {
905  if( m_localparamStack[ii].GetIndex() == aParamId )
906  {
907  param = &m_localparamStack[ii];
908  break;
909  }
910  }
911 
912  if ( param == nullptr ) // not found
913  return 0.0;
914 
915  // Evaluate parameter
916  double value = param->GetValue( aDcode );
917 
918  return value;
919 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
bool m_GerbMetric
Definition: am_primitive.h:99
double GetValue(const D_CODE *aDcode) const
Definition: am_param.cpp:74
void Move(const wxPoint &aMoveVector)
Move the rectangle by the aMoveVector.
Definition: eda_rect.cpp:51
D_CODE * GetDcodeDescr() const
Return the GetDcodeDescr of this object, or NULL.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
bool IsAMPrimitiveExposureOn(const GERBER_DRAW_ITEM *aParent) const
double RAD2DECIDEG(double rad)
Definition: trigo.h:234
EDA_RECT m_boundingBox
The bounding box of the item, calculated by GetApertureMacroShape.
Definition: am_primitive.h:243
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
int PointCount() const
Return the number of points (vertices) in this line chain.
double GetLocalParam(const D_CODE *aDcode, unsigned aParamId) const
Usually, parameters are defined inside the aperture primitive using immediate mode or deferred mode.
int GetShapeDim(const GERBER_DRAW_ITEM *aParent)
Calculate a value that can be used to evaluate the size of text when displaying the D-Code of an item...
AM_PARAMS m_localparamStack
Definition: am_primitive.h:240
void DrawBasicShape(const GERBER_DRAW_ITEM *aParent, SHAPE_POLY_SET &aShapeBuffer, const wxPoint &aShapePos)
Draw (in fact generate the actual polygonal shape of) the primitive shape of an aperture macro instan...
int scaletoIU(double aCoord, bool isMetric)
Convert a distance given in floating point to our internal units.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
void ConvertShapeToPolygon(const GERBER_DRAW_ITEM *aParent, std::vector< wxPoint > &aBuffer)
Convert a shape to an equivalent polygon.
wxPoint GetABPosition(const wxPoint &aXYPosition) const
Return the image position of aPosition for this object.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Hold a parameter value for an "aperture macro" as defined within standard RS274X.
Definition: am_param.h:284
#define TO_POLY_SHAPE
const int seg_per_circle
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, const wxPoint &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
static wxPoint mapPt(double x, double y, bool isMetric)
Translate a point from the aperture macro coordinate system to our deci-mils coordinate system.
void Simplify(POLYGON_MODE aFastMode)
void GRClosedPoly(EDA_RECT *ClipBox, wxDC *DC, int n, const wxPoint *Points, bool Fill, const COLOR4D &Color, const COLOR4D &BgColor)
Draw a closed polyline and fill it if Fill, in object space.
Definition: gr_basic.cpp:507
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
void DrawApertureMacroShape(GERBER_DRAW_ITEM *aParent, EDA_RECT *aClipBox, wxDC *aDC, const COLOR4D &aColor, const wxPoint &aShapePos, bool aFilledShape)
Draw the primitive shape for flashed items.
SHAPE_POLY_SET m_shape
The shape of the item, calculated by GetApertureMacroShape.
Definition: am_primitive.h:242
AM_PRIMITIVES primitives
A sequence of AM_PRIMITIVEs.
Definition: am_primitive.h:232
A gerber DCODE (also called Aperture) definition.
Definition: dcode.h:80
Represent a polyline (an zero-thickness chain of connected line segments).
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
Handle the component boundary box.
Definition: eda_rect.h:42
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
constexpr int delta
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:183
int GetShapeDim(GERBER_DRAW_ITEM *aParent)
Calculate a value that can be used to evaluate the size of text when displaying the D-Code of an item...
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
void TransformRingToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
AM_PARAMS params
A sequence of parameters used by.
Definition: am_primitive.h:97
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Inflate the rectangle horizontally by dx and vertically by dy.
Definition: eda_rect.cpp:364
AM_PRIMITIVE_ID primitive_id
The primitive type.
Definition: am_primitive.h:96
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
SHAPE_POLY_SET * GetApertureMacroShape(const GERBER_DRAW_ITEM *aParent, const wxPoint &aShapePos)
Calculate the primitive shape for flashed items.