KiCad PCB EDA Suite
Loading...
Searching...
No Matches
gerber_draw_item.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) 1992-2017 <Jean-Pierre Charras>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <base_units.h>
22#include <trigo.h>
23#include <bitmaps.h>
24#include <eda_text.h>
25#include <gerbview_frame.h>
27#include <gerber_draw_item.h>
28#include <gerber_file_image.h>
30#include <string_utils.h>
31#include <geometry/shape_arc.h>
32#include <math/util.h> // for KiROUND
33#include <widgets/msgpanel.h>
34
35#include <wx/msgdlg.h>
36
39{
40 m_GerberImageFile = aGerberImageFile;
42 m_Flashed = false;
43 m_DCode = 0;
44 m_UnitsMetric = false;
45 m_LayerNegative = false;
46 m_swapAxis = false;
47 m_mirrorA = false;
48 m_mirrorB = false;
49 m_drawScale.x = m_drawScale.y = 1.0;
50 m_lyrRotation = 0;
51
54}
55
56
60
61
63{
64 m_netAttributes = aNetAttributes;
65
68 {
69 m_GerberImageFile->m_ComponentsList.insert( std::make_pair( m_netAttributes.m_Cmpref, 0 ) );
70 }
71
73 m_GerberImageFile->m_NetnamesList.insert( std::make_pair( m_netAttributes.m_Netname, 0 ) );
74}
75
76
78{
79 // Return the layer this item is on, or 0 if the m_GerberImageFile is null.
80 return m_GerberImageFile ? m_GerberImageFile->m_GraphicLayer : 0;
81}
82
83
84bool GERBER_DRAW_ITEM::GetTextD_CodePrms( int& aSize, VECTOR2I& aPos, EDA_ANGLE& aOrientation )
85{
86 // calculate the best size and orientation of the D_Code text
87
88 if( m_DCode <= 0 )
89 return false; // No D_Code for this item
90
91 if( m_Flashed || m_ShapeType == GBR_ARC )
92 aPos = m_Start;
93 else // it is a line:
94 aPos = ( m_Start + m_End) / 2;
95
96 aPos = GetABPosition( aPos );
97
98 int size; // the best size for the text
99
100 if( GetDcodeDescr() )
101 size = GetDcodeDescr()->GetShapeDim( this );
102 else
103 size = std::min( m_Size.x, m_Size.y );
104
105 aOrientation = ANGLE_HORIZONTAL;
106
107 if( m_Flashed )
108 {
109 // A reasonable size for text is min_dim/3 because most of time this text has 3 chars.
110 aSize = size / 3;
111 }
112 else // this item is a line
113 {
115 EDA_ANGLE angle( delta );
116
117 aOrientation = angle.Normalize90();
118
119 // A reasonable size for text is size/2 because text needs margin below and above it.
120 // a margin = size/4 seems good, expecting the line len is large enough to show 3 chars,
121 // that is the case most of time.
122 aSize = size / 2;
123 }
124
125 return true;
126}
127
128
130{
131 /* Note: RS274Xrevd_e is obscure about the order of transforms:
132 * For instance: Rotation must be made after or before mirroring ?
133 * Note: if something is changed here, GetYXPosition must reflect changes
134 */
135 VECTOR2I abPos = aXYPosition + m_GerberImageFile->m_ImageJustifyOffset;
136
137 // We have also a draw transform (rotation and offset)
138 // order is rotation and after offset
139
140 if( m_swapAxis )
141 std::swap( abPos.x, abPos.y );
142
143 abPos += m_layerOffset + m_GerberImageFile->m_ImageOffset;
144 abPos.x = KiROUND( abPos.x * m_drawScale.x );
145 abPos.y = KiROUND( abPos.y * m_drawScale.y );
146 EDA_ANGLE rotation( m_lyrRotation + m_GerberImageFile->m_ImageRotation, DEGREES_T );
147
148 if( !rotation.IsZero() )
149 RotatePoint( abPos, -rotation );
150
151 // Negate A axis if mirrored
152 if( m_mirrorA )
153 abPos.x = -abPos.x;
154
155 // abPos.y must be negated when no mirror, because draw axis is top to bottom
156 if( !m_mirrorB )
157 abPos.y = -abPos.y;
158
159 // Now generate the draw transform
160 if( !m_GerberImageFile->m_DisplayRotation.IsZero() )
161 RotatePoint( abPos, m_GerberImageFile->m_DisplayRotation );
162
163 abPos.x += KiROUND( m_GerberImageFile->m_DisplayOffset.x * m_drawScale.x );
164 abPos.y += KiROUND( m_GerberImageFile->m_DisplayOffset.y * m_drawScale.y );
165
166 return abPos;
167}
168
169
171{
172 // do the inverse transform made by GetABPosition
173 VECTOR2I xyPos = aABPosition;
174
175 // First, undo the draw transform
176 xyPos.x -= KiROUND( m_GerberImageFile->m_DisplayOffset.x * m_drawScale.x );
177 xyPos.y -= KiROUND( m_GerberImageFile->m_DisplayOffset.y * m_drawScale.y );
178
179 if( !m_GerberImageFile->m_DisplayRotation.IsZero() )
180 RotatePoint( xyPos, -m_GerberImageFile->m_DisplayRotation );
181
182 if( m_mirrorA )
183 xyPos.x = -xyPos.x;
184
185 if( !m_mirrorB )
186 xyPos.y = -xyPos.y;
187
188 EDA_ANGLE rotation( m_lyrRotation + m_GerberImageFile->m_ImageRotation, DEGREES_T );
189
190 if( !rotation.IsZero() )
191 RotatePoint( xyPos, rotation );
192
193 xyPos.x = KiROUND( xyPos.x / m_drawScale.x );
194 xyPos.y = KiROUND( xyPos.y / m_drawScale.y );
195 xyPos -= m_layerOffset + m_GerberImageFile->m_ImageOffset;
196
197 if( m_swapAxis )
198 std::swap( xyPos.x, xyPos.y );
199
200 return xyPos - m_GerberImageFile->m_ImageJustifyOffset;
201}
202
203
205{
206 m_UnitsMetric = m_GerberImageFile->m_GerbMetric;
207 m_swapAxis = m_GerberImageFile->m_SwapAxis; // false if A = X, B = Y;
208
209 // true if A =Y, B = Y
210 m_mirrorA = m_GerberImageFile->m_MirrorA; // true: mirror / axe A
211 m_mirrorB = m_GerberImageFile->m_MirrorB; // true: mirror / axe B
212 m_drawScale = m_GerberImageFile->m_Scale; // A and B scaling factor
213 m_layerOffset = m_GerberImageFile->m_Offset; // Offset from OF command
214
215 // Rotation from RO command:
216 m_lyrRotation = m_GerberImageFile->m_LocalRotation;
217 m_LayerNegative = m_GerberImageFile->GetLayerParams().m_LayerNegative;
218}
219
220
222{
223 switch( m_ShapeType )
224 {
225 case GBR_SEGMENT: return _( "Line" );
226 case GBR_ARC: return _( "Arc" );
227 case GBR_CIRCLE: return _( "Circle" );
228 case GBR_SPOT_OVAL: return wxT( "spot_oval" );
229 case GBR_SPOT_CIRCLE: return wxT( "spot_circle" );
230 case GBR_SPOT_RECT: return wxT( "spot_rect" );
231 case GBR_SPOT_POLY: return wxT( "spot_poly" );
232 case GBR_POLYGON: return wxT( "polygon" );
233
234 case GBR_SPOT_MACRO:
235 {
236 wxString name = wxT( "apt_macro" );
237 D_CODE* dcode = GetDcodeDescr();
238
239 if( dcode && dcode->GetMacro() )
240 name << wxT(" ") << dcode->GetMacro()->m_AmName;
241
242 return name;
243 }
244
245 default: return wxT( "??" );
246 }
247}
248
249
251{
253 return nullptr;
254
255 if( m_GerberImageFile == nullptr )
256 return nullptr;
257
258 return m_GerberImageFile->GetDCODE( m_DCode );
259}
260
261
263{
264 // return a rectangle which is (pos,dim) in nature. therefore the +1
265 BOX2I bbox( m_Start, VECTOR2I( 1, 1 ) );
266 D_CODE* code = GetDcodeDescr();
267
268 // TODO(JE) GERBER_DRAW_ITEM maybe should actually be a number of subclasses.
269 // Until/unless that is changed, we need to do different things depending on
270 // what is actually being represented by this GERBER_DRAW_ITEM.
271
272 switch( m_ShapeType )
273 {
274 case GBR_POLYGON:
275 {
276 BOX2I bb = m_ShapeAsPolygon.BBox();
277 bbox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
278 bbox.SetOrigin( bb.GetOrigin() );
279 break;
280 }
281
282 case GBR_CIRCLE:
283 {
284 double radius = m_Start.Distance( m_End );
285 bbox.Inflate( radius, radius );
286 break;
287 }
288
289 case GBR_ARC:
290 {
291 EDA_ANGLE angle( atan2( double( m_End.y - m_ArcCentre.y ),
292 double( m_End.x - m_ArcCentre.x ) )
293 - atan2( double( m_Start.y - m_ArcCentre.y ),
294 double( m_Start.x - m_ArcCentre.x ) ),
295 RADIANS_T );
296
297 if( m_End == m_Start ) // Arc with the end point = start point is expected to be a circle.
298 angle = ANGLE_360;
299 else
300 angle.Normalize();
301
302 SHAPE_ARC arc( m_ArcCentre, m_Start, angle );
303 bbox = arc.BBox( m_Size.x / 2 ); // m_Size.x is the line thickness
304 break;
305 }
306
307 case GBR_SPOT_CIRCLE:
308 if( code )
309 {
310 int radius = code->m_Size.x >> 1;
311 bbox.Inflate( radius, radius );
312 }
313
314 break;
315
316 case GBR_SPOT_RECT:
317 if( code )
318 bbox.Inflate( code->m_Size.x / 2, code->m_Size.y / 2 );
319
320 break;
321
322 case GBR_SPOT_OVAL:
323 if( code )
324 bbox.Inflate( code->m_Size.x /2, code->m_Size.y / 2 );
325
326 break;
327
328 case GBR_SPOT_MACRO:
329 case GBR_SPOT_POLY:
330 if( code )
331 {
332 if( code->m_Polygon.OutlineCount() == 0 )
333 code->ConvertShapeToPolygon( this );
334
335 bbox.Inflate( code->m_Polygon.BBox().GetWidth() / 2,
336 code->m_Polygon.BBox().GetHeight() / 2 );
337 }
338
339 break;
340
341 case GBR_SEGMENT:
342 if( code && code->m_ApertType == APT_RECT )
343 {
344 if( m_ShapeAsPolygon.OutlineCount() == 0 )
345 {
346 // We cannot initialize m_ShapeAsPolygon, because we are in a const function.
347 // So use a temporary polygon
348 SHAPE_POLY_SET poly_shape;
349 ConvertSegmentToPolygon( &poly_shape );
350 bbox = poly_shape.BBox();
351 }
352
353 else
354 {
355 bbox = m_ShapeAsPolygon.BBox();
356 }
357 }
358 else
359 {
360 int radius = ( m_Size.x + 1 ) / 2;
361
362 int ymax = std::max( m_Start.y, m_End.y ) + radius;
363 int xmax = std::max( m_Start.x, m_End.x ) + radius;
364
365 int ymin = std::min( m_Start.y, m_End.y ) - radius;
366 int xmin = std::min( m_Start.x, m_End.x ) - radius;
367
368 bbox = BOX2I( VECTOR2I( xmin, ymin ), VECTOR2I( xmax - xmin + 1, ymax - ymin + 1 ) );
369 }
370
371 break;
372
373 default:
374 wxASSERT_MSG( false, wxT( "GERBER_DRAW_ITEM shape is unknown!" ) );
375 break;
376 }
377
378 // calculate the corners coordinates in current Gerber axis orientations
379 // because the initial bbox is a horizontal rect, but the bbox in AB position
380 // is the bbox image with perhaps a rotation, we need to calculate the coords of the
381 // corners of the bbox rotated, and then calculate the final bounding box
382 VECTOR2I corners[4];
383 bbox.Normalize();
384
385 // Shape:
386 // 0...1
387 // . .
388 // . .
389 // 3...2
390 corners[0] = bbox.GetOrigin(); // top left
391 corners[2] = bbox.GetEnd(); // bottom right
392 corners[1] = VECTOR2I( corners[2].x, corners[0].y ); // top right
393 corners[3] = VECTOR2I( corners[0].x, corners[2].y ); // bottom left
394
395 VECTOR2I org = GetABPosition( bbox.GetOrigin() );;
396 VECTOR2I end = GetABPosition( bbox.GetEnd() );;
397
398 // Now calculate the bounding box of bbox, if the display image is rotated
399 // It will be perhaps a bit bigger than a better bounding box calculation, but it is fast
400 // and easy
401 // (if not rotated, this is a nop)
402 for( int ii = 0; ii < 4; ii++ )
403 {
404 corners[ii] = GetABPosition( corners[ii] );
405
406 org.x = std::min( org.x, corners[ii].x );
407 org.y = std::min( org.y, corners[ii].y );
408
409 end.x = std::max( end.x, corners[ii].x );
410 end.y = std::max( end.y, corners[ii].y );
411 }
412
413 // Set the corners position:
414 bbox.SetOrigin( org );
415 bbox.SetEnd( end );
416 bbox.Normalize();
417
418 return bbox;
419}
420
421
422void GERBER_DRAW_ITEM::MoveXY( const VECTOR2I& aMoveVector )
423{
424 m_Start += aMoveVector;
425 m_End += aMoveVector;
426 m_ArcCentre += aMoveVector;
427
428 m_ShapeAsPolygon.Move( aMoveVector );
429}
430
431
433{
434 bool isClear = m_LayerNegative ^ m_GerberImageFile->m_ImageNegative;
435
436 // if isClear is true, this item has negative shape
437 return isClear;
438}
439
440
441void GERBER_DRAW_ITEM::Print( wxDC* aDC, const VECTOR2I& aOffset, GBR_DISPLAY_OPTIONS* aOptions )
442{
443 // used when a D_CODE is not found. default D_CODE to draw a flashed item
444 static D_CODE dummyD_CODE( 0 );
445 bool isFilled;
446 int radius;
447 int halfPenWidth;
448 static bool show_err;
449 D_CODE* d_codeDescr = GetDcodeDescr();
450
451 if( d_codeDescr == nullptr )
452 d_codeDescr = &dummyD_CODE;
453
454 COLOR4D color = m_GerberImageFile->GetPositiveDrawColor();
455
456 /* isDark is true if flash is positive and should use a drawing
457 * color other than the background color, else use the background color
458 * when drawing so that an erasure happens.
459 */
460 bool isDark = !(m_LayerNegative ^ m_GerberImageFile->m_ImageNegative);
461
462 if( !isDark )
463 {
464 // draw in background color ("negative" color)
465 color = aOptions->m_NegativeDrawColor;
466 }
467
468 isFilled = aOptions->m_DisplayLinesFill;
469
470 switch( m_ShapeType )
471 {
472 case GBR_POLYGON:
473 isFilled = aOptions->m_DisplayPolygonsFill;
474
475 if( !isDark )
476 isFilled = true;
477
478 PrintGerberPoly( aDC, color, aOffset, isFilled );
479 break;
480
481 case GBR_CIRCLE:
482 radius = KiROUND( m_Start.Distance( m_End ) );
483
484 halfPenWidth = m_Size.x >> 1;
485
486 if( !isFilled )
487 {
488 // draw the border of the pen's path using two circles, each as narrow as possible
489 GRCircle( aDC, GetABPosition( m_Start ), radius - halfPenWidth, 0, color );
490 GRCircle( aDC, GetABPosition( m_Start ), radius + halfPenWidth, 0, color );
491 }
492 else // Filled mode
493 {
494 GRCircle( aDC, GetABPosition( m_Start ), radius, m_Size.x, color );
495 }
496
497 break;
498
499 case GBR_ARC:
500 // Currently, arcs plotted with a rectangular aperture are not supported.
501 // a round pen only is expected.
502 if( !isFilled )
503 {
505 GetABPosition( m_ArcCentre ), 0, color );
506 }
507 else
508 {
510 GetABPosition( m_ArcCentre ), m_Size.x, color );
511 }
512
513 break;
514
515 case GBR_SPOT_CIRCLE:
516 case GBR_SPOT_RECT:
517 case GBR_SPOT_OVAL:
518 case GBR_SPOT_POLY:
519 case GBR_SPOT_MACRO:
520 isFilled = aOptions->m_DisplayFlashedItemsFill;
521 d_codeDescr->DrawFlashedShape( this, aDC, color, m_Start, isFilled );
522 break;
523
524 case GBR_SEGMENT:
525 /* Plot a line from m_Start to m_End.
526 * Usually, a round pen is used, but some Gerber files use a rectangular pen
527 * In fact, any aperture can be used to plot a line.
528 * currently: only a square pen is handled (I believe using a polygon gives a strange plot).
529 */
530 if( d_codeDescr->m_ApertType == APT_RECT )
531 {
532 if( m_ShapeAsPolygon.OutlineCount() == 0 )
534
535 PrintGerberPoly( aDC, color, aOffset, isFilled );
536 }
537 else if( !isFilled )
538 {
539 GRCSegm( aDC, GetABPosition( m_Start ), GetABPosition( m_End ), m_Size.x, color );
540 }
541 else
542 {
544 color );
545 }
546
547 break;
548
549 default:
550 if( !show_err )
551 {
552 wxMessageBox( wxT( "Trace_Segment() type error" ) );
553 show_err = true;
554 }
555
556 break;
557 }
558}
559
560
562{
563 aPolygon->RemoveAllContours();
564 aPolygon->NewOutline();
565
566 VECTOR2I start = m_Start;
568
569 // make calculations more easy if ensure start.x < end.x
570 // (only 2 quadrants to consider)
571 if( start.x > end.x )
572 std::swap( start, end );
573
574 // calculate values relative to start point:
575 VECTOR2I delta = end - start;
576
577 // calculate corners for the first quadrant only (delta.x and delta.y > 0 )
578 // currently, delta.x already is > 0.
579 // make delta.y > 0
580 bool change = delta.y < 0;
581
582 if( change )
583 delta.y = -delta.y;
584
585 // Now create the full polygon.
586 // Due to previous changes, the shape is always something like
587 // 3 4
588 // 2 5
589 // 1 6
590 VECTOR2I corner;
591 corner.x -= m_Size.x/2;
592 corner.y -= m_Size.y/2;
593 VECTOR2I close = corner;
594 aPolygon->Append( VECTOR2I( corner ) ); // Lower left corner, start point (1)
595 corner.y += m_Size.y;
596 aPolygon->Append( VECTOR2I( corner ) ); // upper left corner, start point (2)
597
598 if( delta.x || delta.y )
599 {
600 corner += delta;
601 aPolygon->Append( VECTOR2I( corner ) ); // upper left corner, end point (3)
602 }
603
604 corner.x += m_Size.x;
605 aPolygon->Append( VECTOR2I( corner ) ); // upper right corner, end point (4)
606 corner.y -= m_Size.y;
607 aPolygon->Append( VECTOR2I( corner ) ); // lower right corner, end point (5)
608
609 if( delta.x || delta.y )
610 {
611 corner -= delta;
612 aPolygon->Append( VECTOR2I( corner ) ); // lower left corner, start point (6)
613 }
614
615 aPolygon->Append( VECTOR2I( close ) ); // close the shape
616
617 // Create final polygon:
618 if( change )
619 aPolygon->Mirror( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
620
621 aPolygon->Move( VECTOR2I( start ) );
622}
623
624
629
630
631void GERBER_DRAW_ITEM::PrintGerberPoly( wxDC* aDC, const COLOR4D& aColor, const VECTOR2I& aOffset,
632 bool aFilledShape )
633{
634 if( m_ShapeAsPolygon.OutlineCount() == 0 )
635 return;
636
637 SHAPE_LINE_CHAIN& poly = m_ShapeAsPolygon.Outline( 0 );
638 int pointCount = poly.PointCount() - 1;
639
640 if( pointCount <= 0 )
641 return;
642
643 std::vector<VECTOR2I> points( pointCount );
644
645 for( int ii = 0; ii < pointCount; ii++ )
646 {
647 VECTOR2I p( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
648 points[ii] = p + aOffset;
649 points[ii] = GetABPosition( points[ii] );
650 }
651
652 GRClosedPoly( aDC, pointCount, &points[0], aFilledShape, aColor );
653}
654
655
656void GERBER_DRAW_ITEM::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
657{
658 wxString msg;
659 wxString text;
660
661 msg = ShowGBRShape();
662 aList.emplace_back( _( "Type" ), msg );
663
664 // Display D_Code value with its attributes for items using a DCode:
665 if( m_ShapeType == GBR_POLYGON ) // Has no DCode, but can have an attribute
666 {
667 msg = _( "Attribute" );
668
669 if( m_AperFunction.IsEmpty() )
670 text = _( "No attribute" );
671 else
673 }
674 else
675 {
676 msg.Printf( _( "D Code %d" ), m_DCode );
677 D_CODE* apertDescr = GetDcodeDescr();
678
679 if( !apertDescr || apertDescr->m_AperFunction.IsEmpty() )
680 text = _( "No attribute" );
681 else
682 text = apertDescr->m_AperFunction;
683 }
684
685 aList.emplace_back( msg, text );
686
687 // Display graphic layer name
689 aList.emplace_back( _( "Graphic Layer" ), msg );
690
691 // Display item position
692 auto xStart = EDA_UNIT_UTILS::UI::ToUserUnit( gerbIUScale, aFrame->GetUserUnits(), m_Start.x );
693 auto yStart = EDA_UNIT_UTILS::UI::ToUserUnit( gerbIUScale, aFrame->GetUserUnits(), m_Start.y );
696
697 if( m_Flashed )
698 {
699 msg.Printf( wxT( "(%.4f, %.4f)" ), xStart, yStart );
700 aList.emplace_back( _( "Position" ), msg );
701 }
702 else
703 {
704 msg.Printf( wxT( "(%.4f, %.4f)" ), xStart, yStart );
705 aList.emplace_back( _( "Start" ), msg );
706
707 msg.Printf( wxT( "(%.4f, %.4f)" ), xEnd, yEnd );
708 aList.emplace_back( _( "End" ), msg );
709 }
710
711 // Display item rotation
712 // The full rotation is Image rotation + m_lyrRotation
713 // but m_lyrRotation is specific to this object
714 // so we display only this parameter
715 msg.Printf( wxT( "%f" ), m_lyrRotation );
716 aList.emplace_back( _( "Rotation" ), msg );
717
718 // Display item polarity (item specific)
719 msg = m_LayerNegative ? _("Clear") : _("Dark");
720 aList.emplace_back( _( "Polarity" ), msg );
721
722 // Display mirroring (item specific)
723 msg.Printf( wxT( "A:%s B:%s" ), m_mirrorA ? _( "Yes" ) : _( "No" ),
724 m_mirrorB ? _( "Yes" ) : _( "No" ) );
725 aList.emplace_back( _( "Mirror" ), msg );
726
727 // Display AB axis swap (item specific)
728 msg = m_swapAxis ? wxT( "A=Y B=X" ) : wxT( "A=X B=Y" );
729 aList.emplace_back( _( "AB axis" ), msg );
730
731 // Display net info, if exists
733 return;
734
735 // Build full net info:
736 wxString net_msg;
737 wxString cmp_pad_msg;
738
740 {
741 net_msg = _( "Net:" );
742 net_msg << wxS( " " );
743
744 if( m_netAttributes.m_Netname.IsEmpty() )
745 net_msg << _( "<no net>" );
746 else
747 net_msg << UnescapeString( m_netAttributes.m_Netname );
748 }
749
751 {
752 if( m_netAttributes.m_PadPinFunction.IsEmpty() )
753 {
754 cmp_pad_msg.Printf( _( "Cmp: %s Pad: %s" ),
755 m_netAttributes.m_Cmpref,
756 m_netAttributes.m_Padname.GetValue() );
757 }
758 else
759 {
760 cmp_pad_msg.Printf( _( "Cmp: %s Pad: %s Fct %s" ),
761 m_netAttributes.m_Cmpref,
762 m_netAttributes.m_Padname.GetValue(),
763 m_netAttributes.m_PadPinFunction.GetValue() );
764 }
765 }
766
767 else if( ( m_netAttributes.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_CMP ) )
768 {
769 cmp_pad_msg = _( "Cmp:" );
770 cmp_pad_msg << wxS( " " ) << m_netAttributes.m_Cmpref;
771 }
772
773 aList.emplace_back( net_msg, cmp_pad_msg );
774}
775
776
778{
779 if( m_Flashed )
780 return BITMAPS::pad;
781
782 switch( m_ShapeType )
783 {
784 case GBR_SEGMENT:
785 case GBR_ARC:
786 case GBR_CIRCLE:
787 return BITMAPS::add_line;
788
789 case GBR_SPOT_OVAL:
790 case GBR_SPOT_CIRCLE:
791 case GBR_SPOT_RECT:
792 case GBR_SPOT_POLY:
793 case GBR_SPOT_MACRO:
794 // should be handles by m_Flashed == true
795 return BITMAPS::pad;
796
797 case GBR_POLYGON:
799 }
800
801 return BITMAPS::info;
802}
803
804
805bool GERBER_DRAW_ITEM::HitTest( const VECTOR2I& aRefPos, int aAccuracy ) const
806{
807 // In case the item has a very tiny width defined, allow it to be selected
808 const int MIN_HIT_TEST_RADIUS = gerbIUScale.mmToIU( 0.01 );
809
810 // calculate aRefPos in XY Gerber axis:
811 VECTOR2I ref_pos = GetXYPosition( aRefPos );
812
813 SHAPE_POLY_SET poly;
814
815 switch( m_ShapeType )
816 {
817 case GBR_POLYGON:
818 poly = m_ShapeAsPolygon;
819 return poly.Contains( VECTOR2I( ref_pos ), 0, aAccuracy );
820
821 case GBR_SPOT_POLY:
822 poly = GetDcodeDescr()->m_Polygon;
823 poly.Move( VECTOR2I( m_Start ) );
824 return poly.Contains( VECTOR2I( ref_pos ), 0, aAccuracy );
825
826 case GBR_SPOT_RECT:
827 return GetBoundingBox().Contains( aRefPos );
828
829 case GBR_SPOT_OVAL:
830 {
831 BOX2I bbox = GetBoundingBox();
832
833 if( ! bbox.Contains( aRefPos ) )
834 return false;
835
836 // This is similar to a segment with thickness = min( m_Size.x, m_Size.y )
837 int radius = std::min( m_Size.x, m_Size.y )/2;
838 VECTOR2I start, end;
839
840 if( m_Size.x > m_Size.y ) // Horizontal oval
841 {
842 int len = m_Size.y - m_Size.x;
843 start.x = -len/2;
844 end.x = len/2;
845 }
846 else // Vertical oval
847 {
848 int len = m_Size.x - m_Size.y;
849 start.y = -len/2;
850 end.y = len/2;
851 }
852
853 start += bbox.Centre();
854 end += bbox.Centre();
855
856 if( radius < MIN_HIT_TEST_RADIUS )
857 radius = MIN_HIT_TEST_RADIUS;
858
859 return TestSegmentHit( aRefPos, start, end, radius );
860 }
861
862 case GBR_ARC:
863 {
864 double radius = m_Start.Distance( m_ArcCentre );
865 VECTOR2D test_radius = VECTOR2D( ref_pos ) - VECTOR2D( m_ArcCentre );
866
867 int size = ( ( m_Size.x < MIN_HIT_TEST_RADIUS ) ? MIN_HIT_TEST_RADIUS : m_Size.x );
868
869 // Are we close enough to the radius?
870 bool radius_hit = ( std::fabs( test_radius.EuclideanNorm() - radius) < size );
871
872 if( radius_hit )
873 {
874 // Now check that we are within the arc angle
875
878 EDA_ANGLE start_angle( start );
879 EDA_ANGLE end_angle( end );
880
881 start_angle.Normalize();
882 end_angle.Normalize();
883
884 if( m_Start == m_End )
885 {
886 start_angle = ANGLE_0;
887 end_angle = ANGLE_360;
888 }
889 else if( end_angle < start_angle )
890 {
891 end_angle += ANGLE_360;
892 }
893
894 EDA_ANGLE test_angle( test_radius );
895 test_angle.Normalize();
896
897 return ( test_angle > start_angle && test_angle < end_angle );
898 }
899
900 return false;
901 }
902
903 case GBR_SPOT_MACRO:
904 return m_AbsolutePolygon.Contains( VECTOR2I( aRefPos ), -1, aAccuracy );
905
906 case GBR_SEGMENT:
907 case GBR_CIRCLE:
908 case GBR_SPOT_CIRCLE:
909 break; // handled below.
910 }
911
912 // TODO: a better analyze of the shape (perhaps create a D_CODE::HitTest for flashed items)
913 int radius = std::min( m_Size.x, m_Size.y ) >> 1;
914
915 if( radius < MIN_HIT_TEST_RADIUS )
916 radius = MIN_HIT_TEST_RADIUS;
917
918 if( m_Flashed )
919 return m_Start.Distance( ref_pos ) <= radius;
920 else
921 return TestSegmentHit( ref_pos, m_Start, m_End, radius );
922}
923
924
925bool GERBER_DRAW_ITEM::HitTest( const BOX2I& aRefArea, bool aContained, int aAccuracy ) const
926{
928
929 if( aRefArea.Contains( pos ) )
930 return true;
931
932 pos = GetABPosition( m_End );
933
934 if( aRefArea.Contains( pos ) )
935 return true;
936
937 return false;
938}
939
940
941#if defined(DEBUG)
942
943void GERBER_DRAW_ITEM::Show( int nestLevel, std::ostream& os ) const
944{
945 NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() <<
946 " shape=\"" << m_ShapeType << '"' <<
947 " addr=\"" << std::hex << this << std::dec << '"' <<
948 " layer=\"" << GetLayer() << '"' <<
949 " size=\"" << m_Size << '"' <<
950 " flags=\"" << m_flags << '"' <<
951 "<start" << m_Start << "/>" <<
952 "<end" << m_End << "/>";
953
954 os << "</" << GetClass().Lower().mb_str() << ">\n";
955}
956
957#endif
958
959
960std::vector<int> GERBER_DRAW_ITEM::ViewGetLayers() const
961{
962 std::vector<int> layers( 2 );
963 layers[0] = GERBER_DRAW_LAYER( GetLayer() );
964 layers[1] = GERBER_DCODE_LAYER( layers[0] );
965
966 return layers;
967}
968
969
971{
972 return GetBoundingBox();
973}
974
975
976double GERBER_DRAW_ITEM::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
977{
978 // DCodes will be shown only if zoom is appropriate:
979 // Returns the level of detail of the item.
980 // A level of detail (LOD) is the minimal VIEW scale that
981 // is sufficient for an item to be shown on a given layer.
982 if( IsDCodeLayer( aLayer ) )
983 {
984 int size = 0;
985
986 switch( m_ShapeType )
987 {
988 case GBR_SPOT_MACRO:
989 size = GetDcodeDescr()->m_Polygon.BBox().GetWidth();
990 break;
991
992 case GBR_ARC:
993 size = m_Start.Distance( m_ArcCentre );
994 break;
995
996 default:
997 size = m_Size.x;
998 }
999
1000 // the level of details is chosen experimentally, to show
1001 // only a readable text:
1002 return lodScaleForThreshold( aView, size, gerbIUScale.mmToIU( 3.0 ) );
1003 }
1004
1005 // Other layers are shown without any conditions
1006 return LOD_SHOW;
1007}
1008
1009
1011 const std::vector<KICAD_T>& aScanTypes )
1012{
1013 for( KICAD_T scanType : aScanTypes )
1014 {
1015 if( scanType == Type() )
1016 {
1017 if( INSPECT_RESULT::QUIT == inspector( this, testData ) )
1018 return INSPECT_RESULT::QUIT;
1019 }
1020 }
1021
1023}
1024
1025
1026wxString GERBER_DRAW_ITEM::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
1027{
1028 wxString layerName = GERBER_FILE_IMAGE_LIST::GetImagesList().GetDisplayName( GetLayer(), true );
1029
1030 return wxString::Format( _( "%s (D%d) on layer %d: %s" ),
1031 ShowGBRShape(),
1032 m_DCode,
1033 GetLayer() + 1,
1034 layerName );
1035}
const char * name
constexpr EDA_IU_SCALE gerbIUScale
Definition base_units.h:127
BITMAPS
A list of all bitmap identifiers.
@ add_graphical_polygon
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
wxString m_AmName
The name of the aperture macro as defined like AMVB_RECTANGLE* (name is VB_RECTANGLE)
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:234
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:143
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:294
A gerber DCODE (also called Aperture) definition.
Definition dcode.h:76
APERTURE_MACRO * GetMacro() const
Definition dcode.h:132
void DrawFlashedShape(const GERBER_DRAW_ITEM *aParent, wxDC *aDC, const COLOR4D &aColor, const VECTOR2I &aShapePos, bool aFilledShape)
Draw the dcode shape for flashed items.
Definition dcode.cpp:149
wxString m_AperFunction
the aperture attribute (created by a TA.AperFunction command).
Definition dcode.h:210
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...
Definition dcode.cpp:111
static bool IsValidDcodeValue(int aDcodeValue)
Definition dcode.h:86
VECTOR2I m_Size
Horizontal and vertical dimensions.
Definition dcode.h:197
APERTURE_T m_ApertType
Aperture type ( Line, rectangle, circle, oval poly, macro )
Definition dcode.h:198
SHAPE_POLY_SET m_Polygon
Definition dcode.h:213
void ConvertShapeToPolygon(const GERBER_DRAW_ITEM *aParent)
Convert a shape to an equivalent polygon.
Definition dcode.cpp:288
EDA_ANGLE Normalize()
Definition eda_angle.h:228
EDA_ANGLE Normalize90()
Definition eda_angle.h:260
bool IsZero() const
Definition eda_angle.h:135
The base class for create windows for drawing purpose.
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ITEM_FLAGS m_flags
Definition eda_item.h:608
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:84
bool m_DisplayFlashedItemsFill
Option to draw flashed items (filled/sketch)
COLOR4D m_NegativeDrawColor
The color used to draw negative objects, usually the background color, but not always,...
bool m_DisplayPolygonsFill
Option to draw polygons (filled/sketch)
bool m_DisplayLinesFill
Option to draw line items (filled/sketch)
Information which can be added in a gerber file as attribute of an object.
@ GBR_NETINFO_NET
print info associated to a net (TO.N attribute)
@ GBR_NETINFO_UNSPECIFIED
idle command (no command)
@ GBR_NETINFO_CMP
print info associated to a component (TO.C attribute)
@ GBR_NETINFO_PAD
print info associated to a flashed pad (TO.P attribute)
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
bool GetTextD_CodePrms(int &aSize, VECTOR2I &aPos, EDA_ANGLE &aOrientation)
Return the best size and orientation to display the D_Code on screen.
D_CODE * GetDcodeDescr() const
Return the GetDcodeDescr of this object, or NULL.
void SetLayerParameters()
Initialize parameters from Image and Layer parameters found in the gerber file: m_UnitsMetric,...
wxString ShowGBRShape() const
bool HasNegativeItems()
Optimize screen refresh (when no items are in background color refresh can be faster).
VECTOR2I GetABPosition(const VECTOR2I &aXYPosition) const
Return the image position of aPosition for this object.
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
VECTOR2I GetXYPosition(const VECTOR2I &aABPosition) const
Return the image position of aPosition for this object.
void MoveXY(const VECTOR2I &aMoveVector)
Move this object.
SHAPE_POLY_SET m_ShapeAsPolygon
int GetLayer() const
Return the layer this item is on.
void ConvertSegmentToPolygon()
Convert a lines in m_ShapeAsPolygon to an equivalent polygon.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
GBR_BASIC_SHAPE_TYPE m_ShapeType
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
GERBER_DRAW_ITEM(GERBER_FILE_IMAGE *aGerberparams)
GERBER_FILE_IMAGE * m_GerberImageFile
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
wxString GetClass() const override
void PrintGerberPoly(wxDC *aDC, const COLOR4D &aColor, const VECTOR2I &aOffset, bool aFilledShape)
Print the polygon stored in m_PolyCorners.
void SetNetAttributes(const GBR_NETLIST_METADATA &aNetAttributes)
SHAPE_POLY_SET m_AbsolutePolygon
INSPECT_RESULT Visit(INSPECTOR aInspector, void *aTestData, const std::vector< KICAD_T > &aScanTypes) override
May be re-implemented for each derived class in order to handle all the types given by its member dat...
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
bool HitTest(const VECTOR2I &aRefPos, int aAccuracy=0) const override
Test if the given wxPoint is within the bounds of this object.
GBR_NETLIST_METADATA m_netAttributes
the string given by a TO attribute set in aperture (dcode).
void Print(wxDC *aDC, const VECTOR2I &aOffset, GBR_DISPLAY_OPTIONS *aOptions)
static GERBER_FILE_IMAGE_LIST & GetImagesList()
const wxString GetDisplayName(int aIdx, bool aNameOnly=false, bool aFullName=false)
Get the display name for the layer at aIdx.
Hold the image data and parameters for one gerber file and layer parameters.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
static double lodScaleForThreshold(const KIGFX::VIEW *aView, int aWhatIu, int aThresholdIu)
Get the scale at which aWhatIu would be drawn at the same size as aThresholdIu on screen.
Definition view_item.cpp:35
static constexpr double LOD_SHOW
Return this constant from ViewGetLOD() to show the item unconditionally.
Definition view_item.h:181
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both)
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
EDA_UNITS GetUserUnits() const
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
@ APT_RECT
Definition dcode.h:46
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
@ RADIANS_T
Definition eda_angle.h:31
@ DEGREES_T
Definition eda_angle.h:30
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
INSPECT_RESULT
Definition eda_item.h:44
const INSPECTOR_FUNC & INSPECTOR
std::function passed to nested users by ref, avoids copying std::function.
Definition eda_item.h:91
@ GBR_SPOT_OVAL
@ GBR_SEGMENT
@ GBR_SPOT_POLY
@ GBR_SPOT_RECT
@ GBR_CIRCLE
@ GBR_POLYGON
@ GBR_SPOT_CIRCLE
@ GBR_ARC
@ GBR_SPOT_MACRO
void GRCSegm(wxDC *DC, const VECTOR2I &A, const VECTOR2I &B, int width, const COLOR4D &Color)
Definition gr_basic.cpp:201
void GRCircle(wxDC *aDC, const VECTOR2I &aPos, int aRadius, int aWidth, const COLOR4D &aColor)
Definition gr_basic.cpp:346
void GRFilledSegment(wxDC *aDC, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, const COLOR4D &aColor)
Definition gr_basic.cpp:278
void GRClosedPoly(wxDC *DC, int n, const VECTOR2I *Points, bool Fill, const COLOR4D &Color)
Draw a closed polygon onto the drawing context aDC and optionally fills and/or draws a border around ...
Definition gr_basic.cpp:340
void GRArc(wxDC *aDC, const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, int aWidth, const COLOR4D &aColor)
Definition gr_basic.cpp:367
#define GERBER_DCODE_LAYER(x)
Definition layer_ids.h:564
bool IsDCodeLayer(int aLayer)
Definition layer_ids.h:937
#define GERBER_DRAW_LAYER(x)
Definition layer_ids.h:562
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
Message panel definition file.
KICOMMON_API double ToUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Convert aValue in internal units to the appropriate user units defined by aUnit.
wxString UnescapeString(const wxString &aSource)
int radius
VECTOR2I end
int delta
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition trigo.cpp:173
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ GERBER_DRAW_ITEM_T
Definition typeinfo.h:206
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707