1
/****************************/
2
/* class_aperture_macro.cpp */
3
/****************************/
7
* This program source code file is part of KICAD, a free EDA CAD application.
9
* Copyright (C) 1992-2010 Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr>
10
* Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
11
* Copyright (C) 1992-2010 Kicad Developers, see change_log.txt for contributors.
13
* This program is free software; you can redistribute it and/or
14
* modify it under the terms of the GNU General Public License
15
* as published by the Free Software Foundation; either version 2
16
* of the License, or (at your option) any later version.
18
* This program is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* You should have received a copy of the GNU General Public License
24
* along with this program; if not, you may find one here:
25
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
26
* or you may search the http://www.gnu.org website for the version 2 license,
27
* or you may write to the Free Software Foundation, Inc.,
28
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
37
/** helper Function mapPt
38
* translates a point from the aperture macro coordinate system to our
39
* deci-mils coordinate system.
40
* @return wxPoint - The gerbview coordinate system vector.
42
extern wxPoint mapPt( double x, double y, bool isMetric ); // defined it rs274d.cpp
46
* converts a distance given in floating point to our deci-mils
48
extern int scale( double aCoord, bool isMetric ); // defined it rs274d.cpp
52
* Function mapExposure
53
* translates the first parameter from an aperture macro into a current
55
* @param aParent = a GERBER_DRAW_ITEM that handle:
56
* ** m_Exposure A dynamic setting which can change throughout the
57
* reading of the gerber file, and it indicates whether the current tool
59
* ** m_ImageNegative A dynamic setting which can change throughout the reading
60
* of the gerber file, and it indicates whether the current D codes are to
61
* be interpreted as erasures or not.
62
* @return true to draw with current color, false to draw with alt color (erase)
64
bool AM_PRIMITIVE::mapExposure( GERBER_DRAW_ITEM* aParent )
67
switch( primitive_id )
73
case AMP_LINE_LOWER_LEFT:
77
// All have an exposure parameter and can return true or false
78
switch( GetExposure() )
80
case 0: // exposure always OFF
85
case 1: // exposure always OON
89
case 2: // reverse exposure
90
exposure = !aParent->m_LayerNegative;
98
return true; // All have no exposure parameter and must return true (no change for exposure)
102
return exposure ^ aParent->m_ImageNegative;
107
* Function GetExposure
108
* returns the first parameter in integer form. Some but not all primitives
109
* use the first parameter as an exposure control.
111
int AM_PRIMITIVE::GetExposure() const
113
// No D_CODE* for GetValue()
114
wxASSERT( params.size() && params[0].IsImmediate() );
115
return (int) params[0].GetValue( NULL );
118
/** function DrawBasicShape
119
* Draw the primitive shape for flashed items.
121
void AM_PRIMITIVE::DrawBasicShape( GERBER_DRAW_ITEM* aParent,
124
int aColor, int aAltColor,
128
static std::vector<wxPoint> polybuffer; // create a static buffer to avoid a lot of memory reallocation
131
wxPoint curPos = aShapePos;
132
D_CODE* tool = aParent->GetDcodeDescr();
133
bool gerberMetric = aParent->m_UnitsMetric;
135
if( mapExposure( aParent ) == false )
137
EXCHG(aColor, aAltColor);
140
switch( primitive_id )
142
case AMP_CIRCLE: // Circle, given diameter and position
144
/* Generated by an aperture macro declaration like:
145
* "1,1,0.3,0.5, 1.0*"
146
* type (1), exposure, diameter, pos.x, pos.y
147
* type is not stored in parameters list, so the first parameter is exposure
149
curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), gerberMetric );
150
int radius = scale( params[1].GetValue( tool ), gerberMetric ) / 2;
152
GRCircle( aClipBox, aDC, curPos.x, curPos.y, radius, aColor );
154
GRFilledCircle( aClipBox, aDC, curPos, radius, aColor );
159
case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
161
/* Generated by an aperture macro declaration like:
162
* "2,1,0.3,0,0, 0.5, 1.0,-135*"
163
* type (2), exposure, width, start.x, start.y, end.x, end.y, rotation
164
* type is not stored in parameters list, so the first parameter is exposure
166
ConvertShapeToPolygon( aParent, polybuffer, gerberMetric );
169
rotation = wxRound( params[6].GetValue( tool ) * 10.0 );
172
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
173
RotatePoint( &polybuffer[ii], rotation );
176
// Move to current position:
177
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
178
polybuffer[ii] += curPos;
180
GRClosedPoly( aClipBox, aDC,
181
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
185
case AMP_LINE_CENTER:
187
/* Generated by an aperture macro declaration like:
188
* "21,1,0.3,0.03,0,0,-135*"
189
* type (21), exposure, ,width, height, center pos.x, center pos.y, rotation
190
* type is not stored in parameters list, so the first parameter is exposure
192
ConvertShapeToPolygon( aParent, polybuffer, gerberMetric );
195
rotation = wxRound( params[5].GetValue( tool ) * 10.0 );
198
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
199
RotatePoint( &polybuffer[ii], rotation );
202
// Move to current position:
203
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
204
polybuffer[ii] += curPos;
206
GRClosedPoly( aClipBox, aDC,
207
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
211
case AMP_LINE_LOWER_LEFT:
213
/* Generated by an aperture macro declaration like:
214
* "22,1,0.3,0.03,0,0,-135*"
215
* type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation
216
* type is not stored in parameters list, so the first parameter is exposure
218
ConvertShapeToPolygon( aParent, polybuffer, gerberMetric );
221
rotation = wxRound( params[5].GetValue( tool ) * 10.0 );
224
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
225
RotatePoint( &polybuffer[ii], rotation );
228
// Move to current position:
229
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
230
polybuffer[ii] += curPos;
232
GRClosedPoly( aClipBox, aDC,
233
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
239
/* Generated by an aperture macro declaration like:
240
* "7, 0,0,1.0,0.3,0.01,-13*"
241
* type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation
242
* type is not stored in parameters list, so the first parameter is center.x
244
curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), gerberMetric );
245
/* int outerRadius = scale( params[2].GetValue( tool ), gerberMetric ) / 2;
247
GRCircle( aClipBox, aDC, curPos.x, curPos.y, outerRadius, aColor );
249
GRFilledCircle( aClipBox, aDC, curPos, outerRadius, aColor );
251
ConvertShapeToPolygon( aParent, polybuffer, gerberMetric );
254
rotation = wxRound( params[5].GetValue( tool ) * 10.0 );
256
// Because a thermal shape has 4 identical sub-shapes, only one is created in polybuffer.
257
// We must draw 4 sub-shapes rotated by 90 deg
258
std::vector<wxPoint> subshape_poly;
259
for( int ii = 0; ii < 4; ii++ )
261
subshape_poly = polybuffer;
262
int sub_rotation = rotation + 900 * ii;
263
for( unsigned jj = 0; jj < subshape_poly.size(); jj++ )
264
RotatePoint( &subshape_poly[jj], sub_rotation );
266
// Move to current position:
267
for( unsigned jj = 0; jj < subshape_poly.size(); jj++ )
268
subshape_poly[jj] += curPos;
270
GRClosedPoly( aClipBox, aDC,
271
subshape_poly.size(), &subshape_poly[0], true, aAltColor,
277
case AMP_MOIRE: // A cross hair with n concentric circles
279
curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ),
282
/* Generated by an aperture macro declaration like:
283
* "6,0,0,0.125,.01,0.01,3,0.003,0.150,0"
284
* type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness, crosshaire len, rotation
285
* type is not stored in parameters list, so the first parameter is pos.x
287
int outerDiam = scale( params[2].GetValue( tool ), gerberMetric );
288
int penThickness = scale( params[3].GetValue( tool ), gerberMetric );
289
int gap = scale( params[4].GetValue( tool ), gerberMetric );
290
int numCircles = wxRound( params[5].GetValue( tool ) );
292
// adjust outerDiam by this on each nested circle
293
int diamAdjust = (gap + penThickness); //*2; //Should we use * 2 ?
294
for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust )
300
// draw the border of the pen's path using two circles, each as narrow as possible
301
GRCircle( aClipBox, aDC, curPos.x, curPos.y, outerDiam / 2, 0, aColor );
302
GRCircle( aClipBox, aDC, curPos.x, curPos.y,
303
outerDiam / 2 - penThickness, 0, aColor );
307
GRCircle( aClipBox, aDC, curPos.x, curPos.y,
308
(outerDiam - penThickness) / 2, penThickness, aColor );
313
ConvertShapeToPolygon( aParent, polybuffer, gerberMetric );
316
rotation = wxRound( params[8].GetValue( tool ) * 10.0 );
319
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
320
RotatePoint( &polybuffer[ii], rotation );
323
// Move to current position:
324
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
325
polybuffer[ii] += curPos;
327
GRClosedPoly( aClipBox, aDC,
328
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
334
/* Generated by an aperture macro declaration like:
335
* "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25"
336
* type(4), exposure, corners count, corner1.x, corner.1y, ..., rotation
337
* type is not stored in parameters list, so the first parameter is exposure
339
int numPoints = (int) params[1].GetValue( tool );
340
rotation = wxRound( params[numPoints * 2 + 4].GetValue( tool ) * 10.0 );
342
// Read points. numPoints does not include the starting point, so add 1.
343
for( int i = 0; i<numPoints + 1; ++i )
346
pos.x = scale( params[jj].GetValue( tool ), gerberMetric );
347
pos.y = scale( params[jj + 1].GetValue( tool ), gerberMetric );
348
polybuffer.push_back(pos);
350
// rotate polygon and move it to the actual position
352
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
354
NEGATE(polybuffer[ii].y);
355
RotatePoint( &polybuffer[ii], rotation );
358
// Move to current position:
359
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
360
polybuffer[ii] += curPos;
362
GRClosedPoly( aClipBox, aDC,
363
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
367
case AMP_POLYGON: // Is a regular polygon
368
/* Generated by an aperture macro declaration like:
369
* "5,1,0.6,0,0,0.5,25"
370
* type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation
371
* type is not stored in parameters list, so the first parameter is exposure
373
curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ),
375
// Creates the shape:
376
ConvertShapeToPolygon( aParent, polybuffer, gerberMetric );
378
// rotate polygon and move it to the actual position
379
rotation = wxRound( params[5].GetValue( tool ) * 10.0 );
380
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
382
NEGATE(polybuffer[ii].y);
383
RotatePoint( &polybuffer[ii], rotation );
384
polybuffer[ii] += curPos;
386
GRClosedPoly( aClipBox, aDC,
387
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
391
// not yet supported, waiting for you.
396
D( printf( "AM_PRIMITIVE::DrawBasicShape() err: unknown prim id %d\n",primitive_id) );
402
/** function ConvertShapeToPolygon (virtual)
403
* convert a shape to an equivalent polygon.
404
* Arcs and circles are approximated by segments
405
* Useful when a shape is not a graphic primitive (shape with hole,
406
* rotated shape ... ) and cannot be easily drawn.
407
* note for some schapes conbining circles and solid lines (rectangles), only rectangles are converted
408
* because circles are very easy to draw (no rotation problem) so convert them in polygons,
409
* and draw them as polygons is not a good idea.
411
void AM_PRIMITIVE::ConvertShapeToPolygon( GERBER_DRAW_ITEM* aParent,
412
std::vector<wxPoint>& aBuffer,
415
D_CODE* tool = aParent->GetDcodeDescr();
417
switch( primitive_id )
419
case AMP_CIRCLE: // Circle, currently convertion not needed
423
case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
425
int width = scale( params[1].GetValue( tool ), aUnitsMetric );
426
wxPoint start = mapPt( params[2].GetValue( tool ),
427
params[3].GetValue( tool ), aUnitsMetric );
428
wxPoint end = mapPt( params[4].GetValue( tool ),
429
params[5].GetValue( tool ), aUnitsMetric );
430
wxPoint delta = end - start;
431
int len = wxRound( hypot( delta.x, delta.y ) );
433
// To build the polygon, we must create a horizonta polygon starting to "start"
434
// and rotate it to have it end point to "end"
436
currpt.y += width / 2; // Upper left
437
aBuffer.push_back( currpt );
438
currpt.x = len; // Upper right
439
aBuffer.push_back( currpt );
440
currpt.y -= width; // lower right
441
aBuffer.push_back( currpt );
442
currpt.x = 0; // Upper left
443
aBuffer.push_back( currpt );
445
// Rotate rectangle and move it to the actual start point
446
int angle = wxRound( atan2( delta.y, delta.x ) * 1800.0 / M_PI );
447
for( unsigned ii = 0; ii < 4; ii++ )
449
RotatePoint( &aBuffer[ii], -angle );
450
aBuffer[ii] += start;
451
NEGATE( aBuffer[ii].y );
456
case AMP_LINE_CENTER:
458
wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), aUnitsMetric );
459
wxPoint pos = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ), aUnitsMetric );
463
pos.y -= size.y / 2; // Lower left
464
aBuffer.push_back( pos );
465
pos.y += size.y; // Upper left
466
aBuffer.push_back( pos );
467
pos.x += size.x; // Upper right
468
aBuffer.push_back( pos );
469
pos.y -= size.y; // lower right
470
aBuffer.push_back( pos );
474
case AMP_LINE_LOWER_LEFT:
476
wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), aUnitsMetric );
477
wxPoint lowerLeft = mapPt( params[3].GetValue( tool ), params[4].GetValue(
478
tool ), aUnitsMetric );
481
NEGATE( lowerLeft.y );
482
aBuffer.push_back( lowerLeft );
483
lowerLeft.y += size.y; // Upper left
484
aBuffer.push_back( lowerLeft );
485
lowerLeft.x += size.x; // Upper right
486
aBuffer.push_back( lowerLeft );
487
lowerLeft.y -= size.y; // lower right
488
aBuffer.push_back( lowerLeft );
490
// Negate y coordinates:
491
for( unsigned ii = 0; ii < aBuffer.size(); ii++ )
492
NEGATE( aBuffer[ii].y );
498
// Only 1/4 of the full shape is built, because the other 3 shapes will be draw from this first
499
// rotated by 90, 180 and 270 deg.
500
// params = center.x (unused here), center.y (unused here), outside diam, inside diam, crosshair thickness
501
int outerRadius = scale( params[2].GetValue( tool ), aUnitsMetric ) / 2;
502
int innerRadius = scale( params[3].GetValue( tool ), aUnitsMetric ) / 2;
503
int halfthickness = scale( params[4].GetValue( tool ), aUnitsMetric ) / 2;
504
int angle_start = wxRound( asin(
505
(double) halfthickness / innerRadius ) * 1800 / M_PI );
507
// Draw shape in the first cadrant (X and Y > 0)
508
wxPoint pos, startpos;
511
startpos.x = innerRadius;
512
int angle_end = 900 - angle_start;
514
for( angle = angle_start; angle < angle_end; angle += 100 )
517
RotatePoint( &pos, angle );
518
aBuffer.push_back( pos );
523
RotatePoint( &pos, angle_end );
524
aBuffer.push_back( pos );
527
startpos.x = outerRadius;
529
angle_start = wxRound( asin( (double) halfthickness / outerRadius ) * 1800 / M_PI );
530
angle_end = 900 - angle_start;
532
// First point, near Y axis, outer arc
533
for( angle = angle_end; angle > angle_start; angle -= 100 )
536
RotatePoint( &pos, angle );
537
aBuffer.push_back( pos );
542
RotatePoint( &pos, angle_start );
543
aBuffer.push_back( pos );
545
aBuffer.push_back( aBuffer[0] ); // Close poly
549
case AMP_MOIRE: // A cross hair with n concentric circles. Only the cros is build as polygon
550
// because circles can be drawn easily
552
int crossHairThickness = scale( params[6].GetValue( tool ), aUnitsMetric );
553
int crossHairLength = scale( params[7].GetValue( tool ), aUnitsMetric );
555
// Create cross. First create 1/4 of the shape.
556
// Others point are the same, totated by 90, 180 and 270 deg
557
wxPoint pos( crossHairThickness / 2, crossHairLength / 2 );
558
aBuffer.push_back( pos );
559
pos.y = crossHairThickness / 2;
560
aBuffer.push_back( pos );
561
pos.x = -crossHairLength / 2;
562
aBuffer.push_back( pos );
563
pos.y = -crossHairThickness / 2;
564
aBuffer.push_back( pos );
566
// Copy the 4 shape, rotated by 90, 180 and 270 deg
567
for( int jj = 900; jj <= 2700; jj += 900 )
569
for( int ii = 0; ii < 4; ii++ )
572
RotatePoint( &pos, jj );
573
aBuffer.push_back( pos );
580
// already is a polygon. Do nothing
583
case AMP_POLYGON: // Creates a regular polygon
585
int vertexcount = wxRound( params[1].GetValue( tool ) );
586
int radius = scale( params[4].GetValue( tool ), aUnitsMetric ) / 2;
587
// rs274x said: vertex count = 3 ... 10, and the first corner is on the X axis
588
if( vertexcount < 3 )
590
if( vertexcount > 10 )
592
for( int ii = 0; ii <= vertexcount; ii++ )
594
wxPoint pos( radius, 0);
595
RotatePoint( &pos, ii * 3600 / vertexcount );
596
aBuffer.push_back( pos );
608
/** function DrawApertureMacroShape
609
* Draw the primitive shape for flashed items.
610
* When an item is flashed, this is the shape of the item
612
void APERTURE_MACRO::DrawApertureMacroShape( GERBER_DRAW_ITEM* aParent,
613
EDA_Rect* aClipBox, wxDC* aDC,
614
int aColor, int aAltColor,
615
wxPoint aShapePos, bool aFilledShape )
617
for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
618
prim_macro != primitives.end(); ++prim_macro )
620
prim_macro->DrawBasicShape( aParent, aClipBox, aDC,