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 <dick@softplc.com>
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  wxPoint aShapePos )
99 {
100 #define TO_POLY_SHAPE \
101  { \
102  if( polybuffer.size() > 1 ) \
103  { \
104  aShapeBuffer.NewOutline(); \
105  for( unsigned jj = 0; jj < polybuffer.size(); jj++ ) \
106  aShapeBuffer.Append( polybuffer[jj].x, polybuffer[jj].y ); \
107  aShapeBuffer.Append( polybuffer[0].x, polybuffer[0].y ); \
108  } \
109  }
110 
111  // Draw the primitive shape for flashed items.
112  // Create a static buffer to avoid a lot of memory reallocation.
113  static std::vector<wxPoint> polybuffer;
114  polybuffer.clear();
115 
116  wxPoint curPos = aShapePos;
117  D_CODE* tool = aParent->GetDcodeDescr();
118  double rotation;
119 
120  switch( primitive_id )
121  {
122  case AMP_CIRCLE: // Circle, given diameter and position
123  {
124  /* Generated by an aperture macro declaration like:
125  * "1,1,0.3,0.5, 1.0*"
126  * type (1), exposure, diameter, pos.x, pos.y, <rotation>
127  * <rotation> is a optional parameter: rotation from origin.
128  * type is not stored in parameters list, so the first parameter is exposure
129  */
130  ConvertShapeToPolygon( aParent, polybuffer );
131 
132  // shape rotation (if any):
133  if( params.size() >= 5 )
134  {
135  rotation = params[4].GetValue( tool ) * 10.0;
136 
137  if( rotation != 0)
138  {
139  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
140  RotatePoint( &polybuffer[ii], -rotation );
141  }
142  }
143 
144 
145  // Move to current position:
146  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
147  {
148  polybuffer[ii] += curPos;
149  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
150  }
151 
153  break;
154  }
155 
156  case AMP_LINE2:
157  case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
158  {
159  /* Vector Line, Primitive Code 20.
160  * A vector line is a rectangle defined by its line width, start and end points.
161  * The line ends are rectangular.
162  */
163  /* Generated by an aperture macro declaration like:
164  * "2,1,0.3,0,0, 0.5, 1.0,-135*"
165  * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation
166  * type is not stored in parameters list, so the first parameter is exposure
167  */
168  ConvertShapeToPolygon( aParent, polybuffer );
169 
170  // shape rotation:
171  rotation = params[6].GetValue( tool ) * 10.0;
172 
173  if( rotation != 0)
174  {
175  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
176  RotatePoint( &polybuffer[ii], -rotation );
177  }
178 
179  // Move to current position:
180  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
181  {
182  polybuffer[ii] += curPos;
183  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
184  }
185 
187  break;
188  }
189 
190  case AMP_LINE_CENTER:
191  {
192  /* Center Line, Primitive Code 21
193  * A center line primitive is a rectangle defined by its width, height, and center point
194  */
195  /* Generated by an aperture macro declaration like:
196  * "21,1,0.3,0.03,0,0,-135*"
197  * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation
198  * type is not stored in parameters list, so the first parameter is exposure
199  */
200  ConvertShapeToPolygon( aParent, polybuffer );
201 
202  // shape rotation:
203  rotation = params[5].GetValue( tool ) * 10.0;
204 
205  if( rotation != 0 )
206  {
207  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
208  RotatePoint( &polybuffer[ii], -rotation );
209  }
210 
211  // Move to current position:
212  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
213  {
214  polybuffer[ii] += curPos;
215  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
216  }
217 
219  break;
220  }
221 
222  case AMP_LINE_LOWER_LEFT:
223  {
224  /* Generated by an aperture macro declaration like:
225  * "22,1,0.3,0.03,0,0,-135*"
226  * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation
227  * type is not stored in parameters list, so the first parameter is exposure
228  */
229  ConvertShapeToPolygon( aParent, polybuffer );
230 
231  // shape rotation:
232  rotation = params[5].GetValue( tool ) * 10.0;
233  if( rotation != 0)
234  {
235  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
236  RotatePoint( &polybuffer[ii], -rotation );
237  }
238 
239  // Move to current position:
240  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
241  {
242  polybuffer[ii] += curPos;
243  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
244  }
245 
247  break;
248  }
249 
250  case AMP_THERMAL:
251  {
252  /* Generated by an aperture macro declaration like:
253  * "7, 0,0,1.0,0.3,0.01,-13*"
254  * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation
255  * type is not stored in parameters list, so the first parameter is center.x
256  *
257  * The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is always
258  * on.
259  */
260  std::vector<wxPoint> subshape_poly;
261  curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric );
262  ConvertShapeToPolygon( aParent, subshape_poly );
263 
264  // shape rotation:
265  rotation = params[5].GetValue( tool ) * 10.0;
266 
267  // Because a thermal shape has 4 identical sub-shapes, only one is created in subshape_poly.
268  // We must draw 4 sub-shapes rotated by 90 deg
269  for( int ii = 0; ii < 4; ii++ )
270  {
271  polybuffer = subshape_poly;
272  double sub_rotation = rotation + 900 * ii;
273 
274  for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
275  RotatePoint( &polybuffer[jj], -sub_rotation );
276 
277  // Move to current position:
278  for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
279  {
280  polybuffer[jj] += curPos;
281  polybuffer[jj] = aParent->GetABPosition( polybuffer[jj] );
282  }
283 
285  }
286  }
287  break;
288 
289  case AMP_MOIRE:
290  {
291  /* Moire, Primitive Code 6
292  * The moire primitive is a cross hair centered on concentric rings (annuli).
293  * Exposure is always on.
294  */
295  curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ),
296  m_GerbMetric );
297 
298  /* Generated by an aperture macro declaration like:
299  * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0"
300  * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness,
301  * crosshair len, rotation. The type is not stored in parameters list, so the first
302  * parameter is pos.x.
303  */
304  int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric );
305  int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric );
306  int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric );
307  int numCircles = KiROUND( params[5].GetValue( tool ) );
308 
309  // Draw circles:
310  wxPoint center = aParent->GetABPosition( curPos );
311 
312  // adjust outerDiam by this on each nested circle
313  int diamAdjust = ( gap + penThickness ) * 2;
314 
315  for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust )
316  {
317  if( outerDiam <= 0 )
318  break;
319 
320  // Note: outerDiam is the outer diameter of the ring.
321  // the ring graphic diameter is (outerDiam - penThickness)
322  if( outerDiam <= penThickness )
323  { // No room to draw a ring (no room for the hole):
324  // draw a circle instead (with no hole), with the right diameter
325  TransformCircleToPolygon( aShapeBuffer, center, outerDiam / 2, ARC_HIGH_DEF,
326  ERROR_INSIDE );
327  }
328  else
329  {
330  TransformRingToPolygon( aShapeBuffer, center, ( outerDiam - penThickness ) / 2,
331  penThickness, ARC_HIGH_DEF, ERROR_INSIDE );
332  }
333  }
334 
335  // Draw the cross:
336  ConvertShapeToPolygon( aParent, polybuffer );
337 
338  rotation = params[8].GetValue( tool ) * 10.0;
339 
340  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
341  {
342  // shape rotation:
343  RotatePoint( &polybuffer[ii], -rotation );
344 
345  // Move to current position:
346  polybuffer[ii] += curPos;
347  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
348  }
349 
351  break;
352  }
353 
354  case AMP_OUTLINE:
355  {
356  /* Outline, Primitive Code 4
357  * An outline primitive is an area enclosed by an n-point polygon defined by its start
358  * point and n
359  * subsequent points. The outline must be closed, i.e. the last point must be equal to
360  * the start point. There must be at least one subsequent point (to close the outline).
361  * The outline of the primitive is actually the contour (see 2.6) that consists of linear
362  * segments only, so it must conform to all the requirements described for contours.
363  * Warning: Make no mistake: n is the number of subsequent points, being the number of
364  * vertices of the outline or one less than the number of coordinate pairs.
365  */
366  /* Generated by an aperture macro declaration like:
367  * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25"
368  * type(4), exposure, corners count, corner1.x, corner.1y, ..., corner1.x, corner.1y,
369  * rotation
370  * type is not stored in parameters list, so the first parameter is exposure
371  */
372  // params[0] is the exposure and params[1] is the corners count after the first corner
373  int numCorners = (int) params[1].GetValue( tool );
374 
375  // the shape rotation is the last param of list, after corners
376  int last_prm = params.size() - 1;
377  rotation = params[last_prm].GetValue( tool ) * 10.0;
378  wxPoint pos;
379 
380  // Read points.
381  // Note: numCorners is the polygon corner count, following the first corner
382  // * the polygon is always closed,
383  // * therefore the last XY coordinate is the same as the first
384  int prm_idx = 2; // params[2] is the first X coordinate
385 
386  for( int i = 0; i <= numCorners; ++i )
387  {
388  pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
389  prm_idx++;
390  pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
391  prm_idx++;
392  polybuffer.push_back(pos);
393 
394  // Guard: ensure prm_idx < last_prm
395  // I saw malformed gerber files with numCorners = number
396  // of coordinates instead of number of coordinates following the first point
397  if( prm_idx >= last_prm )
398  break;
399  }
400 
401  // rotate polygon and move it to the actual position shape rotation:
402  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
403  {
404  RotatePoint( &polybuffer[ii], -rotation );
405  }
406 
407  // Move to current position:
408  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
409  {
410  polybuffer[ii] += curPos;
411  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
412  }
413 
415  break;
416  }
417 
418  case AMP_POLYGON:
419  /* Polygon, Primitive Code 5
420  * A polygon primitive is a regular polygon defined by the number of vertices n, the
421  * center point and the diameter of the circumscribed circle
422  */
423  /* Generated by an aperture macro declaration like:
424  * "5,1,0.6,0,0,0.5,25"
425  * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation
426  * type is not stored in parameters list, so the first parameter is exposure
427  */
428  curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric );
429 
430  // Creates the shape:
431  ConvertShapeToPolygon( aParent, polybuffer );
432 
433  // rotate polygon and move it to the actual position
434  rotation = params[5].GetValue( tool ) * 10.0;
435  for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
436  {
437  RotatePoint( &polybuffer[ii], -rotation );
438  polybuffer[ii] += curPos;
439  polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
440  }
441 
443 
444  break;
445 
446  case AMP_EOF:
447  // not yet supported, waiting for you.
448  break;
449 
450  case AMP_UNKNOWN:
451  default:
452  break;
453  }
454 }
455 
456 
458  std::vector<wxPoint>& aBuffer )
459 {
460  D_CODE* tool = aParent->GetDcodeDescr();
461 
462  switch( primitive_id )
463  {
464  case AMP_CIRCLE:
465  {
466  /* Generated by an aperture macro declaration like:
467  * "1,1,0.3,0.5, 1.0*"
468  * type (1), exposure, diameter, pos.x, pos.y, <rotation>
469  * <rotation> is a optional parameter: rotation from origin.
470  * type is not stored in parameters list, so the first parameter is exposure
471  */
472  int radius = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ) / 2;
473 
474  // A circle primitive can have a 0 size (for instance when used in roundrect macro),
475  // so skip it
476  if( radius <= 0 )
477  break;
478 
479  wxPoint center = mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ),
480  m_GerbMetric );
481  wxPoint corner;
482  const int delta = 3600 / seg_per_circle; // rot angle in 0.1 degree
483 
484  for( int angle = 0; angle < 3600; angle += delta )
485  {
486  corner.x = radius;
487  corner.y = 0;
488  RotatePoint( &corner, angle );
489  corner += center;
490  aBuffer.push_back( corner );
491  }
492 
493  break;
494  }
495 
496  case AMP_LINE2:
497  case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
498  {
499  int width = scaletoIU( params[1].GetValue( tool ), m_GerbMetric );
500  wxPoint start = mapPt( params[2].GetValue( tool ),
501  params[3].GetValue( tool ), m_GerbMetric );
502  wxPoint end = mapPt( params[4].GetValue( tool ),
503  params[5].GetValue( tool ), m_GerbMetric );
504  wxPoint delta = end - start;
505  int len = KiROUND( EuclideanNorm( delta ) );
506 
507  // To build the polygon, we must create a horizontal polygon starting to "start"
508  // and rotate it to have the end point to "end"
509  wxPoint currpt;
510  currpt.y += width / 2; // Upper left
511  aBuffer.push_back( currpt );
512  currpt.x = len; // Upper right
513  aBuffer.push_back( currpt );
514  currpt.y -= width; // lower right
515  aBuffer.push_back( currpt );
516  currpt.x = 0; // lower left
517  aBuffer.push_back( currpt );
518 
519  // Rotate rectangle and move it to the actual start point
520  double angle = ArcTangente( delta.y, delta.x );
521 
522  for( unsigned ii = 0; ii < 4; ii++ )
523  {
524  RotatePoint( &aBuffer[ii], -angle );
525  aBuffer[ii] += start;
526  }
527 
528  break;
529  }
530 
531  case AMP_LINE_CENTER:
532  {
533  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
534  m_GerbMetric );
535  wxPoint pos = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ),
536  m_GerbMetric );
537 
538  // Build poly:
539  pos.x -= size.x / 2;
540  pos.y -= size.y / 2; // Lower left
541  aBuffer.push_back( pos );
542  pos.y += size.y; // Upper left
543  aBuffer.push_back( pos );
544  pos.x += size.x; // Upper right
545  aBuffer.push_back( pos );
546  pos.y -= size.y; // lower right
547  aBuffer.push_back( pos );
548  break;
549  }
550 
551  case AMP_LINE_LOWER_LEFT:
552  {
553  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
554  m_GerbMetric );
555  wxPoint lowerLeft = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ),
556  m_GerbMetric );
557 
558  // Build poly:
559  aBuffer.push_back( lowerLeft );
560  lowerLeft.y += size.y; // Upper left
561  aBuffer.push_back( lowerLeft );
562  lowerLeft.x += size.x; // Upper right
563  aBuffer.push_back( lowerLeft );
564  lowerLeft.y -= size.y; // lower right
565  aBuffer.push_back( lowerLeft );
566  break;
567  }
568 
569  case AMP_THERMAL:
570  {
571  // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from
572  // this first rotated by 90, 180 and 270 deg.
573  // params = center.x (unused here), center.y (unused here), outside diam, inside diam,
574  // crosshair thickness.
575  int outerRadius = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2;
576  int innerRadius = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ) / 2;
577 
578  // Safety checks to guarantee no divide-by-zero
579  outerRadius = std::max( 1, outerRadius );
580  innerRadius = std::max( 1, innerRadius );
581 
582  int halfthickness = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
583  double angle_start = RAD2DECIDEG( asin( (double) halfthickness / innerRadius ) );
584 
585  // Draw shape in the first quadrant (X and Y > 0)
586  wxPoint pos, startpos;
587 
588  // Inner arc
589  startpos.x = innerRadius;
590  double angle_end = 900 - angle_start;
591 
592  for( double angle = angle_start; angle < angle_end; angle += 100 )
593  {
594  pos = startpos;
595  RotatePoint( &pos, angle );
596  aBuffer.push_back( pos );
597  }
598 
599  // Last point
600  pos = startpos;
601  RotatePoint( &pos, angle_end );
602  aBuffer.push_back( pos );
603 
604  // outer arc
605  startpos.x = outerRadius;
606  startpos.y = 0;
607  angle_start = RAD2DECIDEG( asin( (double) halfthickness / outerRadius ) );
608  angle_end = 900 - angle_start;
609 
610  // First point, near Y axis, outer arc
611  for( double angle = angle_end; angle > angle_start; angle -= 100 )
612  {
613  pos = startpos;
614  RotatePoint( &pos, angle );
615  aBuffer.push_back( pos );
616  }
617 
618  // last point
619  pos = startpos;
620  RotatePoint( &pos, angle_start );
621  aBuffer.push_back( pos );
622 
623  aBuffer.push_back( aBuffer[0] ); // Close poly
624  }
625  break;
626 
627  case AMP_MOIRE:
628  {
629  // A cross hair with n concentric circles. Only the cross is built as
630  // polygon because circles can be drawn easily
631  int crossHairThickness = scaletoIU( params[6].GetValue( tool ), m_GerbMetric );
632  int crossHairLength = scaletoIU( params[7].GetValue( tool ), m_GerbMetric );
633 
634  // Create cross. First create 1/4 of the shape.
635  // Others point are the same, rotated by 90, 180 and 270 deg
636  wxPoint pos( crossHairThickness / 2, crossHairLength / 2 );
637  aBuffer.push_back( pos );
638  pos.y = crossHairThickness / 2;
639  aBuffer.push_back( pos );
640  pos.x = -crossHairLength / 2;
641  aBuffer.push_back( pos );
642  pos.y = -crossHairThickness / 2;
643  aBuffer.push_back( pos );
644 
645  // Copy the 4 shape, rotated by 90, 180 and 270 deg
646  for( int jj = 1; jj <= 3; jj ++ )
647  {
648  for( int ii = 0; ii < 4; ii++ )
649  {
650  pos = aBuffer[ii];
651  RotatePoint( &pos, jj*900 );
652  aBuffer.push_back( pos );
653  }
654  }
655 
656  break;
657  }
658 
659  case AMP_OUTLINE:
660  // already is a polygon. Do nothing
661  break;
662 
663  case AMP_POLYGON: // Creates a regular polygon
664  {
665  int vertexcount = KiROUND( params[1].GetValue( tool ) );
666  int radius = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
667 
668  // rs274x said: vertex count = 3 ... 10, and the first corner is on the X axis
669  if( vertexcount < 3 )
670  vertexcount = 3;
671 
672  if( vertexcount > 10 )
673  vertexcount = 10;
674 
675  for( int ii = 0; ii <= vertexcount; ii++ )
676  {
677  wxPoint pos( radius, 0);
678  RotatePoint( &pos, ii * 3600 / vertexcount );
679  aBuffer.push_back( pos );
680  }
681 
682  break;
683  }
684 
685  case AMP_COMMENT:
686  case AMP_UNKNOWN:
687  case AMP_EOF:
688  break;
689  }
690 }
691 
692 
694 {
695  int dim = -1;
696  D_CODE* tool = aParent->GetDcodeDescr();
697 
698  switch( primitive_id )
699  {
700  case AMP_CIRCLE:
701  // params = exposure, diameter, pos.x, pos.y
702  dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // Diameter
703  break;
704 
705  case AMP_LINE2:
706  case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
707  dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // line width
708  break;
709 
710  case AMP_LINE_CENTER:
711  {
712  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
713  m_GerbMetric );
714  dim = std::min(size.x, size.y);
715  break;
716  }
717 
718  case AMP_LINE_LOWER_LEFT:
719  {
720  wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ),
721  m_GerbMetric );
722  dim = std::min(size.x, size.y);
723  break;
724  }
725 
726  case AMP_THERMAL:
727  {
728  // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from
729  // this first rotated by 90, 180 and 270 deg.
730  // params = center.x (unused here), center.y (unused here), outside diam, inside diam,
731  // crosshair thickness.
732  dim = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2; // Outer diam
733  break;
734  }
735 
736  case AMP_MOIRE: // A cross hair with n concentric circles.
737  dim = scaletoIU( params[7].GetValue( tool ), m_GerbMetric ); // = cross hair len
738  break;
739 
740  case AMP_OUTLINE: // a free polygon :
741  {
742  // dim = min side of the bounding box (this is a poor criteria, but what is a good
743  // criteria b?)
744  // exposure, corners count, corner1.x, corner.1y, ..., rotation
745  // note: corners count is the count of corners following corner1
746  int numPoints = (int) params[1].GetValue( tool );
747 
748  // Read points. numPoints does not include the starting point, so add 1.
749  // and calculate the bounding box;
750  wxSize pos_min, pos_max, pos;
751  int prm_idx = 2; // params[2] is the first X coordinate
752  int last_prm = params.size() - 1;
753 
754  for( int i = 0; i<= numPoints; ++i )
755  {
756  pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
757  prm_idx++;
758  pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
759  prm_idx++;
760 
761  if( i == 0 )
762  {
763  pos_min = pos_max = pos;
764  }
765  else
766  {
767  // upper right corner:
768  if( pos_min.x > pos.x )
769  pos_min.x = pos.x;
770 
771  if( pos_min.y > pos.y )
772  pos_min.y = pos.y;
773 
774  // lower left corner:
775  if( pos_max.x < pos.x )
776  pos_max.x = pos.x;
777 
778  if( pos_max.y < pos.y )
779  pos_max.y = pos.y;
780  }
781 
782  // Guard: ensure prm_idx < last_prm (last prm is orientation)
783  // I saw malformed gerber files with numCorners = number
784  // of coordinates instead of number of coordinates following the first point
785  if( prm_idx >= last_prm )
786  break;
787  }
788 
789  // calculate dim
790  wxSize size;
791  size.x = pos_max.x - pos_min.x;
792  size.y = pos_max.y - pos_min.y;
793  dim = std::min( size.x, size.y );
794  break;
795  }
796 
797  case AMP_POLYGON: // Regular polygon
798  dim = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2; // Radius
799  break;
800 
801  case AMP_COMMENT:
802  case AMP_UNKNOWN:
803  case AMP_EOF:
804  break;
805  }
806 
807  return dim;
808 }
809 
810 
812  wxPoint aShapePos )
813 {
814  SHAPE_POLY_SET holeBuffer;
815  bool hasHole = false;
816 
818 
819  for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
820  prim_macro != primitives.end(); ++prim_macro )
821  {
822  if( prim_macro->primitive_id == AMP_COMMENT )
823  continue;
824 
825  if( prim_macro->IsAMPrimitiveExposureOn( aParent ) )
826  {
827  prim_macro->DrawBasicShape( aParent, m_shape, aShapePos );
828  }
829  else
830  {
831  prim_macro->DrawBasicShape( aParent, holeBuffer, aShapePos );
832 
833  if( holeBuffer.OutlineCount() ) // we have a new hole in shape: remove the hole
834  {
836  holeBuffer.RemoveAllContours();
837  hasHole = true;
838  }
839  }
840  }
841 
842  // Merge and cleanup basic shape polygons
844 
845  // If a hole is defined inside a polygon, we must fracture the polygon
846  // to be able to drawn it (i.e link holes by overlapping edges)
847  if( hasHole )
849 
850  m_boundingBox = EDA_RECT( wxPoint( 0, 0 ), wxSize( 1, 1 ) );
851  auto bb = m_shape.BBox();
852  wxPoint center( bb.Centre().x, bb.Centre().y );
853  m_boundingBox.Move( aParent->GetABPosition( center ) );
854  m_boundingBox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
855 
856  return &m_shape;
857 }
858 
859 
861  wxDC* aDC, COLOR4D aColor, wxPoint aShapePos,
862  bool aFilledShape )
863 {
864  SHAPE_POLY_SET* shapeBuffer = GetApertureMacroShape( aParent, aShapePos );
865 
866  if( shapeBuffer->OutlineCount() == 0 )
867  return;
868 
869  for( int ii = 0; ii < shapeBuffer->OutlineCount(); ii++ )
870  {
871  SHAPE_LINE_CHAIN& poly = shapeBuffer->Outline( ii );
872 
873  GRClosedPoly( aClipBox, aDC, poly.PointCount(), (wxPoint*) &poly.CPoint( 0 ), aFilledShape,
874  aColor, aColor );
875  }
876 }
877 
878 
880 {
881  int dim = -1;
882 
883  for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
884  prim_macro != primitives.end(); ++prim_macro )
885  {
886  int pdim = prim_macro->GetShapeDim( aParent );
887 
888  if( dim < pdim )
889  dim = pdim;
890  }
891 
892  return dim;
893 }
894 
895 
896 double APERTURE_MACRO::GetLocalParam( const D_CODE* aDcode, unsigned aParamId ) const
897 {
898  // find parameter descr.
899  const AM_PARAM * param = nullptr;
900 
901  for( unsigned ii = 0; ii < m_localparamStack.size(); ii ++ )
902  {
903  if( m_localparamStack[ii].GetIndex() == aParamId )
904  {
905  param = &m_localparamStack[ii];
906  break;
907  }
908  }
909 
910  if ( param == nullptr ) // not found
911  return 0.0;
912 
913  // Evaluate parameter
914  double value = param->GetValue( aDcode );
915 
916  return value;
917 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:148
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.
void DrawBasicShape(const GERBER_DRAW_ITEM *aParent, SHAPE_POLY_SET &aShapeBuffer, wxPoint aShapePos)
Draw (in fact generate the actual polygonal shape of) the primitive shape of an aperture macro instan...
void DrawApertureMacroShape(GERBER_DRAW_ITEM *aParent, EDA_RECT *aClipBox, wxDC *aDC, COLOR4D aColor, wxPoint aShapePos, bool aFilledShape)
Draw the primitive shape for flashed items.
bool IsAMPrimitiveExposureOn(const GERBER_DRAW_ITEM *aParent) const
double RAD2DECIDEG(double rad)
Definition: trigo.h:236
EDA_RECT m_boundingBox
The bounding box of the item, calculated by GetApertureMacroShape.
Definition: am_primitive.h:241
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
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...
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, wxPoint aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
AM_PARAMS m_localparamStack
Definition: am_primitive.h:238
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.
SHAPE_POLY_SET * GetApertureMacroShape(const GERBER_DRAW_ITEM *aParent, wxPoint aShapePos)
Calculate the primitive shape for flashed items.
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
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 Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
SHAPE_POLY_SET m_shape
The shape of the item, calculated by GetApertureMacroShape.
Definition: am_primitive.h:240
AM_PRIMITIVES primitives
A sequence of AM_PRIMITIVEs.
Definition: am_primitive.h:230
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
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:182
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...
void GRClosedPoly(EDA_RECT *ClipBox, wxDC *DC, int n, const wxPoint *Points, bool Fill, COLOR4D Color, COLOR4D BgColor)
Draw a closed polygon onto the drawing context aDC and optionally fills and/or draws a border around ...
Definition: gr_basic.cpp:553
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
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
void TransformRingToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
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