KiCad PCB EDA Suite
class_drawsegment.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
7  * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 #include <fctsys.h>
28 #include <gr_basic.h>
29 #include <bezier_curves.h>
30 #include <pcb_screen.h>
31 #include <bitmaps.h>
32 #include <pcb_edit_frame.h>
33 #include <class_board.h>
34 #include <class_module.h>
35 #include <class_drawsegment.h>
36 #include <base_units.h>
37 #include <geometry/shape_simple.h>
38 #include <geometry/shape_segment.h>
39 #include <geometry/shape_circle.h>
42 
43 
45  BOARD_ITEM( aParent, idtype )
46 {
47  m_Type = 0;
48  m_Angle = 0;
49  m_Flags = 0;
51  m_Width = Millimeter2iu( DEFAULT_LINE_WIDTH );
52 }
53 
54 
56 {
57 }
58 
59 
60 void DRAWSEGMENT::SetPosition( const wxPoint& aPos )
61 {
62  m_Start = aPos;
63 }
64 
65 
67 {
68  if( m_Shape == S_POLYGON )
69  return (wxPoint) m_Poly.CVertex( 0 );
70  else
71  return m_Start;
72 }
73 
74 
75 double DRAWSEGMENT::GetLength() const
76 {
77  double length = 0.0;
78 
79  switch( m_Shape )
80  {
81  case S_CURVE:
82  for( size_t ii = 1; ii < m_BezierPoints.size(); ++ii )
83  length += GetLineLength( m_BezierPoints[ii - 1], m_BezierPoints[ii] );
84 
85  break;
86 
87  case S_SEGMENT:
88  length = GetLineLength( GetStart(), GetEnd() );
89  break;
90 
91  default:
92  wxASSERT_MSG( false, "DRAWSEGMENT::GetLength not implemented for shape"
93  + ShowShape( GetShape() ) );
94  break;
95  }
96 
97  return length;
98 }
99 
100 
101 void DRAWSEGMENT::Move( const wxPoint& aMoveVector )
102 {
103  // Move vector should not affect start/end for polygon since it will
104  // be applied directly to polygon outline.
105  if( m_Shape != S_POLYGON )
106  {
107  m_Start += aMoveVector;
108  m_End += aMoveVector;
109  }
110 
111  switch ( m_Shape )
112  {
113  case S_POLYGON:
114  m_Poly.Move( VECTOR2I( aMoveVector ) );
115  break;
116 
117  case S_CURVE:
118  m_BezierC1 += aMoveVector;
119  m_BezierC2 += aMoveVector;
120 
121  for( wxPoint& pt : m_BezierPoints)
122  pt += aMoveVector;
123 
124  break;
125 
126  default:
127  break;
128  }
129 }
130 
131 
132 void DRAWSEGMENT::Scale( double aScale )
133 {
134  auto scalePt = [&]( wxPoint& pt )
135  {
136  pt.x = KiROUND( pt.x * aScale );
137  pt.y = KiROUND( pt.y * aScale );
138  };
139 
140  int radius = GetRadius();
141 
142  scalePt( m_Start );
143  scalePt( m_End );
144 
145  // specific parameters:
146  switch( m_Shape )
147  {
148  case S_CURVE:
149  scalePt( m_BezierC1 );
150  scalePt( m_BezierC2 );
151  break;
152 
153  case S_CIRCLE: // ring or circle
154  m_End.x = m_Start.x + KiROUND( radius * aScale );
155  m_End.y = m_Start.y;
156  break;
157 
158  case S_POLYGON: // polygon
159  {
160  std::vector<wxPoint> pts;
161 
162  for( const VECTOR2I& pt : m_Poly.Outline( 0 ).CPoints() )
163  {
164  pts.emplace_back( pt );
165  scalePt( pts.back() );
166  }
167 
168  SetPolyPoints( pts );
169  }
170  break;
171 
172  default:
173  break;
174  }
175 }
176 
177 
178 void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle )
179 {
180  switch( m_Shape )
181  {
182  case S_ARC:
183  case S_SEGMENT:
184  case S_CIRCLE:
185  // these can all be done by just rotating the start and end points
186  RotatePoint( &m_Start, aRotCentre, aAngle);
187  RotatePoint( &m_End, aRotCentre, aAngle);
188  break;
189 
190  case S_RECT:
191  if( KiROUND( aAngle ) % 900 == 0 )
192  {
193  RotatePoint( &m_Start, aRotCentre, aAngle );
194  RotatePoint( &m_End, aRotCentre, aAngle );
195  break;
196  }
197 
198  // Convert non-cartesian-rotated rect to a diamond
199  m_Shape = S_POLYGON;
201  m_Poly.NewOutline();
202  m_Poly.Append( m_Start );
204  m_Poly.Append( m_End );
206 
208 
209  case S_POLYGON:
210  m_Poly.Rotate( -DECIDEG2RAD( aAngle ), VECTOR2I( aRotCentre ) );
211  break;
212 
213  case S_CURVE:
214  RotatePoint( &m_Start, aRotCentre, aAngle);
215  RotatePoint( &m_End, aRotCentre, aAngle);
216  RotatePoint( &m_BezierC1, aRotCentre, aAngle);
217  RotatePoint( &m_BezierC2, aRotCentre, aAngle);
218 
219  for( wxPoint& pt : m_BezierPoints )
220  RotatePoint( &pt, aRotCentre, aAngle);
221 
222  break;
223 
224  default:
225  wxFAIL_MSG( "DRAWSEGMENT::Rotate not implemented for " + STROKE_T_asString( m_Shape ) );
226  break;
227  }
228 }
229 
230 
231 void DRAWSEGMENT::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
232 {
233  if( aFlipLeftRight )
234  {
235  m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
236  m_End.x = aCentre.x - ( m_End.x - aCentre.x );
237  }
238  else
239  {
240  m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
241  m_End.y = aCentre.y - ( m_End.y - aCentre.y );
242  }
243 
244  switch ( m_Shape )
245  {
246  case S_ARC:
247  m_Angle = -m_Angle;
248  break;
249 
250  case S_POLYGON:
251  m_Poly.Mirror( aFlipLeftRight, !aFlipLeftRight, VECTOR2I( aCentre ) );
252  break;
253 
254  case S_CURVE:
255  {
256  if( aFlipLeftRight )
257  {
258  m_BezierC1.x = aCentre.x - ( m_BezierC1.x - aCentre.x );
259  m_BezierC2.x = aCentre.x - ( m_BezierC2.x - aCentre.x );
260  }
261  else
262  {
263  m_BezierC1.y = aCentre.y - ( m_BezierC1.y - aCentre.y );
264  m_BezierC2.y = aCentre.y - ( m_BezierC2.y - aCentre.y );
265  }
266 
267  // Rebuild the poly points shape
268  std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
269  BEZIER_POLY converter( ctrlPoints );
270  converter.GetPoly( m_BezierPoints, m_Width );
271  }
272  break;
273 
274  case S_SEGMENT:
275  case S_RECT:
276  case S_CIRCLE:
277  break;
278 
279  default:
280  wxFAIL_MSG( "DRAWSEGMENT::Flip not implemented for " + STROKE_T_asString( m_Shape ) );
281  break;
282  }
283 
284  // DRAWSEGMENT items are not allowed on copper layers, so
285  // copper layers count is not taken in account in Flip transform
286  SetLayer( FlipLayer( GetLayer() ) );
287 }
288 
289 
291 {
292  // Has meaning only for S_CURVE DRAW_SEGMENT shape
293  if( m_Shape != S_CURVE )
294  {
295  m_BezierPoints.clear();
296  return;
297  }
298  // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
299  std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
300  BEZIER_POLY converter( ctrlPoints );
301  converter.GetPoly( m_BezierPoints, aMinSegLen );
302 }
303 
304 
306 {
307  wxPoint c;
308 
309  switch( m_Shape )
310  {
311  case S_ARC:
312  case S_CIRCLE:
313  c = m_Start;
314  break;
315 
316  case S_SEGMENT:
317  // Midpoint of the line
318  c = ( GetStart() + GetEnd() ) / 2;
319  break;
320 
321  case S_POLYGON:
322  case S_RECT:
323  case S_CURVE:
324  c = GetBoundingBox().Centre();
325  break;
326 
327  default:
328  wxFAIL_MSG( "DRAWSEGMENT::GetCentre not implemented for " + STROKE_T_asString( m_Shape ) );
329  break;
330  }
331 
332  return c;
333 }
334 
335 
337 {
338  wxPoint endPoint( m_End ); // start of arc
339 
340  switch( m_Shape )
341  {
342  case S_ARC:
343  // rotate the starting point of the arc, given by m_End, through the
344  // angle m_Angle to get the ending point of the arc.
345  // m_Start is the arc centre
346  endPoint = m_End; // m_End = start point of arc
347  RotatePoint( &endPoint, m_Start, -m_Angle );
348  break;
349 
350  default:
351  break;
352  }
353 
354  return endPoint; // after rotation, the end of the arc.
355 }
356 
357 
359 {
360  wxPoint endPoint( m_End );
361 
362  switch( m_Shape )
363  {
364  case S_ARC:
365  // rotate the starting point of the arc, given by m_End, through half
366  // the angle m_Angle to get the middle of the arc.
367  // m_Start is the arc centre
368  endPoint = m_End; // m_End = start point of arc
369  RotatePoint( &endPoint, m_Start, -m_Angle / 2.0 );
370  break;
371 
372  default:
373  break;
374  }
375 
376  return endPoint; // after rotation, the end of the arc.
377 }
378 
379 
381 {
382  // due to the Y axis orient atan2 needs - y value
383  double angleStart = ArcTangente( GetArcStart().y - GetCenter().y,
384  GetArcStart().x - GetCenter().x );
385 
386  // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg
387  // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg.
388  // and this is not easy to handle in calculations
389  NORMALIZE_ANGLE_POS( angleStart );
390 
391  return angleStart;
392 }
393 
394 
395 void DRAWSEGMENT::SetAngle( double aAngle )
396 {
397  // m_Angle must be >= -360 and <= +360 degrees
398  m_Angle = NormalizeAngle360Max( aAngle );
399 }
400 
401 
403 {
404  if( !m_Parent || m_Parent->Type() != PCB_MODULE_T )
405  return NULL;
406 
407  return (MODULE*) m_Parent;
408 }
409 
410 
411 void DRAWSEGMENT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
412 {
413  EDA_UNITS units = aFrame->GetUserUnits();
414  wxString msg;
415 
416  msg = _( "Drawing" );
417 
418  aList.emplace_back( _( "Type" ), msg, DARKCYAN );
419 
420  wxString shape = _( "Shape" );
421 
422  switch( m_Shape )
423  {
424  case S_CIRCLE:
425  aList.emplace_back( shape, _( "Circle" ), RED );
426 
427  msg = MessageTextFromValue( units, GetLineLength( m_Start, m_End ) );
428  aList.emplace_back( _( "Radius" ), msg, RED );
429  break;
430 
431  case S_ARC:
432  aList.emplace_back( shape, _( "Arc" ), RED );
433  msg.Printf( wxT( "%.1f" ), m_Angle / 10.0 );
434  aList.emplace_back( _( "Angle" ), msg, RED );
435 
436  msg = MessageTextFromValue( units, GetLineLength( m_Start, m_End ) );
437  aList.emplace_back( _( "Radius" ), msg, RED );
438  break;
439 
440  case S_CURVE:
441  aList.emplace_back( shape, _( "Curve" ), RED );
442 
443  msg = MessageTextFromValue( units, GetLength() );
444  aList.emplace_back( _( "Length" ), msg, DARKGREEN );
445  break;
446 
447  case S_POLYGON:
448  aList.emplace_back( shape, _( "Polygon" ), RED );
449 
450  msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() );
451  aList.emplace_back( _( "Points" ), msg, DARKGREEN );
452  break;
453 
454  case S_RECT:
455  aList.emplace_back( shape, _( "Rectangle" ), RED );
456 
457  msg = MessageTextFromValue( units, std::abs( m_End.x - m_Start.x ) );
458  aList.emplace_back( _( "Width" ), msg, DARKGREEN );
459 
460  msg = MessageTextFromValue( units, std::abs( m_End.y - m_Start.y ) );
461  aList.emplace_back( _( "Height" ), msg, DARKGREEN );
462  break;
463 
464  case S_SEGMENT:
465  {
466  aList.emplace_back( shape, _( "Segment" ), RED );
467 
468  msg = MessageTextFromValue( units, GetLineLength( m_Start, m_End ) );
469  aList.emplace_back( _( "Length" ), msg, DARKGREEN );
470 
471  // angle counter-clockwise from 3'o-clock
472  const double deg = RAD2DEG( atan2( (double)( m_Start.y - m_End.y ),
473  (double)( m_End.x - m_Start.x ) ) );
474  msg.Printf( wxT( "%.1f" ), deg );
475  aList.emplace_back( _( "Angle" ), msg, DARKGREEN );
476  }
477  break;
478 
479  default:
480  aList.emplace_back( shape, _( "Unrecognized" ), RED );
481  break;
482  }
483 
484  if( m_Shape == S_POLYGON )
485  {
486  VECTOR2I point0 = GetPolyShape().Outline(0).CPoint(0);
487  wxString origin = wxString::Format( "@(%s, %s)",
488  MessageTextFromValue( units, point0.x ),
489  MessageTextFromValue( units, point0.y ) );
490 
491  aList.emplace_back( _( "Origin" ), origin, DARKGREEN );
492  }
493  else
494  {
495  wxString start = wxString::Format( "@(%s, %s)",
496  MessageTextFromValue( units, GetStart().x ),
497  MessageTextFromValue( units, GetStart().y ) );
498  wxString end = wxString::Format( "@(%s, %s)",
499  MessageTextFromValue( units, GetEnd().x ),
500  MessageTextFromValue( units, GetEnd().y ) );
501 
502  aList.emplace_back( start, end, DARKGREEN );
503  }
504 
505  aList.emplace_back( _( "Layer" ), GetLayerName(), DARKBROWN );
506 
507  msg = MessageTextFromValue( units, m_Width, true );
508  aList.emplace_back( _( "Width" ), msg, DARKCYAN );
509 }
510 
511 
513 {
514  EDA_RECT bbox;
515 
516  bbox.SetOrigin( m_Start );
517 
518  switch( m_Shape )
519  {
520  case S_RECT:
521  {
522  std::vector<wxPoint> pts;
523  GetRectCorners( &pts );
524 
525  bbox = EDA_RECT(); // re-init for merging
526 
527  for( wxPoint& pt : pts )
528  bbox.Merge( pt );
529  }
530  break;
531 
532  case S_SEGMENT:
533  bbox.SetEnd( m_End );
534  break;
535 
536  case S_CIRCLE:
537  bbox.Inflate( GetRadius() );
538  break;
539 
540  case S_ARC:
541  computeArcBBox( bbox );
542  break;
543 
544  case S_POLYGON:
545  if( m_Poly.IsEmpty() )
546  break;
547  {
548  MODULE* module = GetParentModule();
549  bbox = EDA_RECT(); // re-init for merging
550 
551  for( auto iter = m_Poly.CIterate(); iter; iter++ )
552  {
553  wxPoint pt ( iter->x, iter->y );
554 
555  if( module ) // Transform, if we belong to a module
556  {
557  RotatePoint( &pt, module->GetOrientation() );
558  pt += module->GetPosition();
559  }
560 
561  bbox.Merge( pt );
562  }
563  }
564  break;
565 
566  case S_CURVE:
567  bbox.Merge( m_BezierC1 );
568  bbox.Merge( m_BezierC2 );
569  bbox.Merge( m_End );
570  break;
571 
572  default:
573  wxFAIL_MSG( "DRAWSEGMENT::GetBoundingBox not implemented for "
574  + STROKE_T_asString( m_Shape ) );
575  break;
576  }
577 
578  bbox.Inflate( ((m_Width+1) / 2) + 1 );
579  bbox.Normalize();
580 
581  return bbox;
582 }
583 
584 
585 bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
586 {
587  int maxdist = aAccuracy + ( m_Width / 2 );
588 
589  switch( m_Shape )
590  {
591  case S_CIRCLE:
592  {
593  int radius = GetRadius();
594  int dist = KiROUND( EuclideanNorm( aPosition - GetCenter() ) );
595 
596  if( m_Width == 0 ) // Filled circle hit-test
597  {
598  if( dist <= radius + maxdist )
599  return true;
600  }
601  else // Ring hit-test
602  {
603  if( abs( radius - dist ) <= maxdist )
604  return true;
605  }
606  }
607  break;
608 
609  case S_ARC:
610  {
611  wxPoint relPos = aPosition - GetCenter();
612  int radius = GetRadius();
613  int dist = KiROUND( EuclideanNorm( relPos ) );
614 
615  if( abs( radius - dist ) <= maxdist )
616  {
617  // For arcs, the test point angle must be >= arc angle start
618  // and <= arc angle end
619  // However angle values > 360 deg are not easy to handle
620  // so we calculate the relative angle between arc start point and teast point
621  // this relative arc should be < arc angle if arc angle > 0 (CW arc)
622  // and > arc angle if arc angle < 0 (CCW arc)
623  double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
624 
625  double arc_hittest = ArcTangente( relPos.y, relPos.x );
626 
627  // Calculate relative angle between the starting point of the arc, and the test point
628  arc_hittest -= arc_angle_start;
629 
630  // Normalise arc_hittest between 0 ... 360 deg
631  NORMALIZE_ANGLE_POS( arc_hittest );
632 
633  // Check angle: inside the arc angle when it is > 0
634  // and outside the not drawn arc when it is < 0
635  if( GetAngle() >= 0.0 )
636  {
637  if( arc_hittest <= GetAngle() )
638  return true;
639  }
640  else
641  {
642  if( arc_hittest >= (3600.0 + GetAngle()) )
643  return true;
644  }
645  }
646  }
647  break;
648 
649  case S_CURVE:
650  ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width );
651 
652  for( unsigned int i= 1; i < m_BezierPoints.size(); i++)
653  {
654  if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i], maxdist ) )
655  return true;
656  }
657  break;
658 
659  case S_SEGMENT:
660  if( TestSegmentHit( aPosition, m_Start, m_End, maxdist ) )
661  return true;
662  break;
663 
664  case S_RECT:
665  {
666  std::vector<wxPoint> pts;
667  GetRectCorners( &pts );
668 
669  if( m_Width == 0 ) // Filled rect hit-test
670  {
671  SHAPE_POLY_SET poly;
672  poly.NewOutline();
673 
674  for( const wxPoint& pt : pts )
675  poly.Append( pt );
676 
677  if( poly.Collide( VECTOR2I( aPosition ), maxdist ) )
678  return true;
679  }
680  else // Open rect hit-test
681  {
682  if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
683  || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
684  || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
685  || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ) )
686  {
687  return true;
688  }
689  }
690  }
691  break;
692 
693  case S_POLYGON:
694  {
695  if( !IsPolygonFilled() )
696  {
698  return m_Poly.CollideEdge( VECTOR2I( aPosition ), dummy, maxdist );
699  }
700  else
701  return m_Poly.Collide( VECTOR2I( aPosition ), maxdist );
702  }
703  break;
704 
705  default:
706  wxFAIL_MSG( "DRAWSEGMENT::HitTest (point) not implemented for "
707  + STROKE_T_asString( m_Shape ) );
708  break;
709  }
710 
711  return false;
712 }
713 
714 
715 bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
716 {
717  EDA_RECT arect = aRect;
718  arect.Normalize();
719  arect.Inflate( aAccuracy );
720 
721  EDA_RECT arcRect;
722  EDA_RECT bb = GetBoundingBox();
723 
724  switch( m_Shape )
725  {
726  case S_CIRCLE:
727  // Test if area intersects or contains the circle:
728  if( aContained )
729  return arect.Contains( bb );
730  else
731  {
732  // If the rectangle does not intersect the bounding box, this is a much quicker test
733  if( !aRect.Intersects( bb ) )
734  {
735  return false;
736  }
737  else
738  {
739  return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
740  }
741  }
742  break;
743 
744  case S_ARC:
745  // Test for full containment of this arc in the rect
746  if( aContained )
747  {
748  return arect.Contains( bb );
749  }
750  // Test if the rect crosses the arc
751  else
752  {
753  arcRect = bb.Common( arect );
754 
755  /* All following tests must pass:
756  * 1. Rectangle must intersect arc BoundingBox
757  * 2. Rectangle must cross the outside of the arc
758  */
759  return arcRect.Intersects( arect ) &&
761  }
762  break;
763 
764  case S_RECT:
765  if( aContained )
766  {
767  return arect.Contains( bb );
768  }
769  else
770  {
771  std::vector<wxPoint> pts;
772  GetRectCorners( &pts );
773 
774  // Account for the width of the lines
775  arect.Inflate( GetWidth() / 2 );
776  return ( arect.Intersects( pts[0], pts[1] )
777  || arect.Intersects( pts[1], pts[2] )
778  || arect.Intersects( pts[2], pts[3] )
779  || arect.Intersects( pts[3], pts[0] ) );
780  }
781 
782  break;
783 
784  case S_SEGMENT:
785  if( aContained )
786  {
787  return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
788  }
789  else
790  {
791  // Account for the width of the line
792  arect.Inflate( GetWidth() / 2 );
793  return arect.Intersects( GetStart(), GetEnd() );
794  }
795 
796  break;
797 
798  case S_POLYGON:
799  if( aContained )
800  {
801  return arect.Contains( bb );
802  }
803  else
804  {
805  // Fast test: if aRect is outside the polygon bounding box,
806  // rectangles cannot intersect
807  if( !arect.Intersects( bb ) )
808  return false;
809 
810  // Account for the width of the line
811  arect.Inflate( GetWidth() / 2 );
812  int count = m_Poly.TotalVertices();
813 
814  for( int ii = 0; ii < count; ii++ )
815  {
816  auto vertex = m_Poly.CVertex( ii );
817  auto vertexNext = m_Poly.CVertex( ( ii + 1 ) % count );
818 
819  // Test if the point is within aRect
820  if( arect.Contains( ( wxPoint ) vertex ) )
821  return true;
822 
823  // Test if this edge intersects aRect
824  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
825  return true;
826  }
827  }
828  break;
829 
830  case S_CURVE:
831  if( aContained )
832  {
833  return arect.Contains( bb );
834  }
835  else
836  {
837  // Fast test: if aRect is outside the polygon bounding box,
838  // rectangles cannot intersect
839  if( !arect.Intersects( bb ) )
840  return false;
841 
842  // Account for the width of the line
843  arect.Inflate( GetWidth() / 2 );
844  unsigned count = m_BezierPoints.size();
845 
846  for( unsigned ii = 1; ii < count; ii++ )
847  {
848  wxPoint vertex = m_BezierPoints[ii-1];
849  wxPoint vertexNext = m_BezierPoints[ii];
850 
851  // Test if the point is within aRect
852  if( arect.Contains( ( wxPoint ) vertex ) )
853  return true;
854 
855  // Test if this edge intersects aRect
856  if( arect.Intersects( vertex, vertexNext ) )
857  return true;
858  }
859  }
860  break;
861 
862 
863  default:
864  wxFAIL_MSG( "DRAWSEGMENT::HitTest (rect) not implemented for "
865  + STROKE_T_asString( m_Shape ) );
866  break;
867  }
868 
869  return false;
870 }
871 
872 
874 {
875  return wxString::Format( _( "Pcb Graphic %s on %s" ),
876  ShowShape( m_Shape ),
877  GetLayerName() );
878 }
879 
880 
882 {
883  return add_dashed_line_xpm;
884 }
885 
886 
888 {
889  return new DRAWSEGMENT( *this );
890 }
891 
892 
894 {
895  // For arcs - do not include the center point in the bounding box,
896  // it is redundant for displaying an arc
897  if( m_Shape == S_ARC )
898  {
899  EDA_RECT bbox;
900  bbox.SetOrigin( m_End );
901  computeArcBBox( bbox );
902  return BOX2I( bbox.GetOrigin(), bbox.GetSize() );
903  }
904 
905  return EDA_ITEM::ViewBBox();
906 }
907 
908 
909 void DRAWSEGMENT::GetRectCorners( std::vector<wxPoint>* pts ) const
910 {
911  MODULE* module = GetParentModule();
912  wxPoint topLeft = GetStart();
913  wxPoint botRight = GetEnd();
914 
915  // Un-rotate rect topLeft and botRight
916  if( module && KiROUND( module->GetOrientation() ) % 900 != 0 )
917  {
918  topLeft -= module->GetPosition();
919  RotatePoint( &topLeft, -module->GetOrientation() );
920 
921  botRight -= module->GetPosition();
922  RotatePoint( &botRight, -module->GetOrientation() );
923  }
924 
925  // Set up the un-rotated 4 corners
926  pts->emplace_back( topLeft );
927  pts->emplace_back( botRight.x, topLeft.y );
928  pts->emplace_back( botRight );
929  pts->emplace_back( topLeft.x, botRight.y );
930 
931  // Now re-rotate the 4 corners to get a diamond
932  if( module && KiROUND( module->GetOrientation() ) % 900 != 0 )
933  {
934  for( wxPoint& pt : *pts )
935  {
936  RotatePoint( &pt,module->GetOrientation() );
937  pt += module->GetPosition();
938  }
939  }
940 }
941 
942 
944 {
945  // Do not include the center, which is not necessarily
946  // inside the BB of a arc with a small angle
947  aBBox.SetOrigin( m_End );
948 
949  wxPoint end = m_End;
950  RotatePoint( &end, m_Start, -m_Angle );
951  aBBox.Merge( end );
952 
953  // Determine the starting quarter
954  // 0 right-bottom
955  // 1 left-bottom
956  // 2 left-top
957  // 3 right-top
958  unsigned int quarter = 0; // assume right-bottom
959 
960  if( m_End.x < m_Start.x )
961  {
962  if( m_End.y <= m_Start.y )
963  quarter = 2;
964  else // ( m_End.y > m_Start.y )
965  quarter = 1;
966  }
967  else if( m_End.x >= m_Start.x )
968  {
969  if( m_End.y < m_Start.y )
970  quarter = 3;
971  else if( m_End.x == m_Start.x )
972  quarter = 1;
973  }
974 
975  int radius = GetRadius();
976  int angle = (int) GetArcAngleStart() % 900 + m_Angle;
977  bool directionCW = ( m_Angle > 0 ); // Is the direction of arc clockwise?
978 
979  // Make the angle positive, so we go clockwise and merge points belonging to the arc
980  if( !directionCW )
981  {
982  angle = 900 - angle;
983  quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic
984  }
985 
986  while( angle > 900 )
987  {
988  switch( quarter )
989  {
990  case 0: aBBox.Merge( wxPoint( m_Start.x, m_Start.y + radius ) ); break; // down
991  case 1: aBBox.Merge( wxPoint( m_Start.x - radius, m_Start.y ) ); break; // left
992  case 2: aBBox.Merge( wxPoint( m_Start.x, m_Start.y - radius ) ); break; // up
993  case 3: aBBox.Merge( wxPoint( m_Start.x + radius, m_Start.y ) ); break; // right
994  }
995 
996  if( directionCW )
997  ++quarter;
998  else
999  quarter += 3; // -1 modulo arithmetic
1000 
1001  quarter %= 4;
1002  angle -= 900;
1003  }
1004 }
1005 
1006 
1007 void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
1008 {
1010  m_Poly.NewOutline();
1011 
1012  for ( const wxPoint& p : aPoints )
1013  m_Poly.Append( p.x, p.y );
1014 }
1015 
1016 
1018 {
1019  std::vector<SHAPE*> effectiveShapes;
1020 
1021  switch( m_Shape )
1022  {
1023  case S_ARC:
1024  {
1025  SHAPE_ARC arc( GetCenter(), GetArcStart(), (double) GetAngle() / 10.0 );
1027 
1028  for( int i = 0; i < l.SegmentCount(); i++ )
1029  {
1030  effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A,
1031  l.Segment( i ).B, m_Width ) );
1032  }
1033 
1034  break;
1035  }
1036 
1037  case S_SEGMENT:
1038  effectiveShapes.emplace_back( new SHAPE_SEGMENT( GetStart(), GetEnd(), m_Width ) );
1039  break;
1040 
1041  case S_RECT:
1042  {
1043  std::vector<wxPoint> pts;
1044  GetRectCorners( &pts );
1045 
1046  if( m_Width == 0 )
1047  {
1048  effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1049  }
1050  else
1051  {
1052  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], m_Width ) );
1053  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], m_Width ) );
1054  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], m_Width ) );
1055  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], m_Width ) );
1056  }
1057  }
1058  break;
1059 
1060  case S_CIRCLE:
1061  {
1062  if( m_Width == 0 )
1063  {
1064  effectiveShapes.emplace_back( new SHAPE_CIRCLE( GetCenter(), GetRadius() ) );
1065  }
1066  else
1067  {
1068  // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC
1069  SHAPE_ARC circle( GetCenter(), GetEnd(), 360.0 );
1070  SHAPE_LINE_CHAIN l = circle.ConvertToPolyline();
1071 
1072  for( int i = 0; i < l.SegmentCount(); i++ )
1073  {
1074  effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A,
1075  l.Segment( i ).B, m_Width ) );
1076  }
1077  }
1078 
1079  break;
1080  }
1081 
1082  case S_CURVE:
1083  {
1085  wxPoint start_pt = GetBezierPoints()[0];
1086 
1087  for( unsigned int jj = 1; jj < GetBezierPoints().size(); jj++ )
1088  {
1089  wxPoint end_pt = GetBezierPoints()[jj];
1090  effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, m_Width ) );
1091  start_pt = end_pt;
1092  }
1093 
1094  break;
1095  }
1096 
1097  case S_POLYGON:
1098  {
1100 
1101  if( IsPolygonFilled() )
1102  {
1103  effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1104  }
1105  else
1106  {
1107  for( int i = 0; i < l.SegmentCount(); i++ )
1108  effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ) ) );
1109  }
1110  }
1111  break;
1112 
1113  default:
1114  wxFAIL_MSG( "DRAWSEGMENT::MakeEffectiveShapes unsupported DRAWSEGMENT shape: "
1115  + STROKE_T_asString( m_Shape ) );
1116  break;
1117  }
1118 
1119  return effectiveShapes;
1120 }
1121 
1122 
1123 const std::vector<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const
1124 {
1125  std::vector<wxPoint> rv;
1126 
1127  if( m_Poly.OutlineCount() )
1128  {
1129  if( m_Poly.COutline( 0 ).PointCount() )
1130  {
1131  for ( auto iter = m_Poly.CIterate(); iter; iter++ )
1132  rv.emplace_back( iter->x, iter->y );
1133  }
1134  }
1135 
1136  return rv;
1137 }
1138 
1139 
1141 {
1142  // return true if the polygonal shape is valid (has more than 2 points)
1143  if( GetPolyShape().OutlineCount() == 0 )
1144  return false;
1145 
1146  const SHAPE_LINE_CHAIN& outline = ((SHAPE_POLY_SET&)GetPolyShape()).Outline( 0 );
1147 
1148  return outline.PointCount() > 2;
1149 }
1150 
1151 
1153 {
1154  // return the number of corners of the polygonal shape
1155  // this shape is expected to be only one polygon without hole
1156  if( GetPolyShape().OutlineCount() )
1157  return GetPolyShape().VertexCount( 0 );
1158 
1159  return 0;
1160 }
1161 
1162 
1164 {
1165  DRAWSEGMENT* image = dynamic_cast<DRAWSEGMENT*>( aImage );
1166  assert( image );
1167 
1168  std::swap( m_Width, image->m_Width );
1169  std::swap( m_Start, image->m_Start );
1170  std::swap( m_End, image->m_End );
1171  std::swap( m_Shape, image->m_Shape );
1172  std::swap( m_Type, image->m_Type );
1173  std::swap( m_Angle, image->m_Angle );
1174  std::swap( m_BezierC1, image->m_BezierC1 );
1175  std::swap( m_BezierC2, image->m_BezierC2 );
1176  std::swap( m_BezierPoints, image->m_BezierPoints );
1177  std::swap( m_Poly, image->m_Poly );
1178 }
1179 
1180 
1181 static struct DRAWSEGMENT_DESC
1182 {
1184  {
1188 
1189  propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, int>( _( "Thickness" ),
1191  // TODO show certain properties depending on the shape
1192  propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, double>( _( "Angle" ),
1194  // TODO or may have different names (arcs)
1195  // TODO type?
1196  propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, int>( _( "End X" ),
1198  propMgr.AddProperty( new PROPERTY<DRAWSEGMENT, int>( _( "End Y" ),
1200  }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:128
int TotalVertices() const
Returns total number of vertices stored in the set.
EDA_UNITS
Definition: common.h:198
static wxString ShowShape(STROKE_T aShape)
Function ShowShape converts the enum STROKE_T integer value to a wxString.
double GetOrientation() const
Definition: class_module.h:211
BOX2< VECTOR2I > BOX2I
Definition: box2.h:521
wxPoint GetArcStart() const
SHAPE_SIMPLE.
Definition: shape_simple.h:42
virtual const BOX2I ViewBBox() const override
Function ViewBBox() returns the bounding box of the item covering all its layers.
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:61
EDA_ITEM * m_Parent
Linked list: Link (parent struct)
Definition: base_struct.h:174
#define TYPE_HASH(x)
Macro to generate unique identifier for a type
Definition: property.h:53
#define KI_FALLTHROUGH
int OutlineCount() const
Returns the number of outlines in the set
wxPoint GetArcEnd() const
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:206
void Merge(const EDA_RECT &aRect)
Function Merge modifies the position and size of the rectangle in order to contain aRect.
PNG memory record (file in memory).
Definition: bitmap_def.h:29
virtual void Move(const wxPoint &aMoveVector) override
Function Move move this object.
const BITMAP_OPAQUE add_dashed_line_xpm[1]
T NormalizeAngle360Max(T Angle)
Normalize angle to be >=-360.0 and <= 360.0 Angle can be equal to -360 or +360.
Definition: trigo.h:226
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
Implementation of conversion functions that require both schematic and board internal units.
BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
STROKE_T GetShape() const
void GetPoly(std::vector< wxPoint > &aOutput, int aMinSegLen=0)
Converts Bezier curve to a polygon.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Returns the index-th vertex in a given hole outline within a given outline
SHAPE_POLY_SET m_Poly
Stores the S_POLYGON shape.
polygon (not yet used for tracks, but could be in microwave apps)
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Function FlippedLayerNumber.
Definition: lset.cpp:485
bool IsEmpty() const
Returns true if the set is empty (no polygons at all)
virtual BITMAP_DEF GetMenuImage() const override
Function GetMenuImage returns a pointer to an image to be used in menus.
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments Has me...
double RAD2DEG(double rad)
Definition: trigo.h:215
std::vector< SHAPE * > MakeEffectiveShapes()
Makes a set of SHAPE objects representing the DRAWSEGMENT.
int VertexCount(int aOutline=-1, int aHole=-1) const
Returns the number of vertices in a given outline/hole
double GetArcAngleStart() const
function GetArcAngleStart()
#define DEFAULT_LINE_WIDTH
virtual wxString GetSelectMenuText(EDA_UNITS aUnits) const override
Function GetSelectMenuText returns the text to display to be used in the selection clarification cont...
Struct VERTEX_INDEX.
void SetOrigin(const wxPoint &pos)
Definition: eda_rect.h:131
bool IntersectsCircleEdge(const wxPoint &aCenter, const int aRadius, const int aWidth) const
IntersectsCircleEdge Tests for intersection between this rect and the edge (radius) of a circle.
EDA_RECT Common(const EDA_RECT &aRect) const
Function Common returns the area that is common with another rectangle.
usual segment : line with rounded ends
void Rotate(double aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Function Rotate rotates all vertices by a given angle.
const std::vector< wxPoint > & GetBezierPoints() const
void SetPosition(const wxPoint &aPos) override
double m_Angle
Used only for Arcs: Arc angle in 1/10 deg.
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:257
wxPoint GetArcMid() const
const std::vector< wxPoint > BuildPolyPointsList() const
Build and return the list of corners in a std::vector<wxPoint> It must be used only to convert the SH...
The base class for create windows for drawing purpose.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
int PointCount() const
Function PointCount()
virtual void SwapData(BOARD_ITEM *aImage) override
Swap data between aItem and aImage.
#define REGISTER_TYPE(x)
Helper macro to map type hashes to names
Definition: property_mgr.h:244
bool Contains(const wxPoint &aPoint) const
Function Contains.
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
void Mirror(bool aX=true, bool aY=false, const VECTOR2I &aRef={ 0, 0 })
Mirrors the line points about y or x (or both)
wxPoint m_BezierC1
Bezier Control Point 1.
bool TestSegmentHit(const wxPoint &aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:129
double dist(const double ax, const double ay, const double bx, const double by)
Definition: delauney.h:168
wxPoint GetPosition() const override
const VECTOR2I & CPoint(int aIndex) const
Function Point()
segment with non rounded ends
class MODULE, a footprint
Definition: typeinfo.h:89
virtual EDA_ITEM * Clone() const override
Function Clone creates a duplicate of this item with linked list members set to NULL.
Display value expressed in distance units (mm/inch)
Definition: property.h:47
wxPoint m_Start
Line start point or Circle and Arc center.
#define NULL
void computeArcBBox(EDA_RECT &aBBox) const
void Move(const VECTOR2I &aVector) override
const std::vector< VECTOR2I > & CPoints() const
wxPoint m_End
Line end point or circle and arc start point.
SHAPE_POLY_SET.
Convert decidegrees to degrees for display.
Definition: property.h:49
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
const wxPoint GetOrigin() const
Definition: eda_rect.h:114
void SetEnd(int x, int y)
Definition: eda_rect.h:192
Arcs (with rounded ends)
void Scale(double aScale)
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declares an inheritance relationship between types.
MODULE * GetParentModule() const
Function GetParentModule returns a pointer to the parent module, or NULL if DRAWSEGMENT does not belo...
void SetEndY(int y)
STROKE_T m_Shape
Shape: line, Circle, Arc.
int GetRadius() const
Function GetRadius returns the radius of this item Has meaning only for arc and circle.
SHAPE_POLY_SET & GetPolyShape()
Bezier Curve.
int NewOutline()
Creates a new empty polygon in the set and returns its index
Definition: color4d.h:59
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
int SegmentCount() const
Function SegmentCount()
int GetPointCount() const
bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr) const override
Function Collide Checks whether the point aP is either inside or on the edge of the polygon set.
int GetWidth() const
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
virtual void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Function Flip Flip this object, i.e.
void Normalize()
Function Normalize ensures that the height ant width are positive.
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aUseMils, EDA_DATA_TYPE aType)
Definition: base_units.cpp:124
static wxString STROKE_T_asString(STROKE_T a)
double GetAngle() const
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX &aClosestVertex, int aClearance=0) const
Function CollideEdge Checks whether aPoint collides with any edge of any of the contours of the polyg...
Bezier curves to polygon converter.
Definition: bezier_curves.h:35
SEG Segment(int aIndex)
Function Segment()
virtual void Rotate(const wxPoint &aRotCentre, double aAngle) override
Function Rotate Rotate this object.
double GetLength() const
Function GetLength returns the length of the track using the hypotenuse calculation.
int m_Type
Used in complex associations ( Dimensions.. )
Class to handle a graphic segment.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Function GetMsgPanelInfo populates aList of MSG_PANEL_ITEM objects with it's internal state for displ...
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
STATUS_FLAGS m_Flags
Definition: base_struct.h:176
void AddProperty(PROPERTY_BASE *aProperty)
Registers a property.
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
VECTOR2I A
Definition: seg.h:47
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
double DECIDEG2RAD(double deg)
Definition: trigo.h:218
wxPoint m_BezierC2
Bezier Control Point 2.
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:68
EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boards.
Definition: base_struct.h:159
bool IsPolyShapeValid() const
wxPoint Centre() const
Definition: eda_rect.h:62
void SetEndX(int x)
bool Intersects(const EDA_RECT &aRect) const
Function Intersects tests for a common area between rectangles.
void SetAngle(double aAngle)
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
bool HitTest(const wxPoint &aPosition, int aAccuracy=0) const override
Function HitTest tests if aPosition is contained within or on the bounding box of an item.
Provides class metadata.
Definition: property_mgr.h:58
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
wxPoint GetPosition() const override
Definition: class_module.h:206
int m_Width
thickness of lines ...
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:162
DRAWSEGMENT(BOARD_ITEM *aParent=NULL, KICAD_T idtype=PCB_LINE_T)
virtual const BOX2I ViewBBox() const override
Function ViewBBox() returns the bounding box of the item covering all its layers.
void GetRectCorners(std::vector< wxPoint > *pts) const
wxString GetLayerName() const
Function GetLayerName returns the name of the PCB layer on which the item resides.
const SHAPE_LINE_CHAIN ConvertToPolyline(double aAccuracy=500.0) const
Constructs a SHAPE_LINE_CHAIN of segments from a given arc.
Definition: shape_arc.cpp:231
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
bool IsPolygonFilled() const
Polygonal shape is not always filled.
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
const wxSize GetSize() const
Definition: eda_rect.h:103
wxPoint GetCenter() const override
Function GetCenter()
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
static struct DRAWSEGMENT_DESC _DRAWSEGMENT_DESC
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)
std::vector< wxPoint > m_BezierPoints
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes.
VECTOR2I B
Definition: seg.h:48
void SetWidth(int aWidth)