KiCad PCB EDA Suite
eda_shape.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 <[email protected]>
6  * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
7  * Copyright (C) 1992-2021 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 <bezier_curves.h>
28 #include <base_units.h>
30 #include <eda_draw_frame.h>
31 #include <geometry/shape_simple.h>
32 #include <geometry/shape_segment.h>
33 #include <geometry/shape_circle.h>
34 #include <macros.h>
35 #include <math/util.h> // for KiROUND
36 #include <eda_shape.h>
37 #include <plotters/plotter.h>
38 
39 
40 EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill, bool eeWinding ) :
41  m_endsSwapped( false ),
42  m_shape( aType ),
43  m_width( aLineWidth ),
44  m_fill( aFill ),
45  m_editState( 0 ),
46  m_eeWinding( eeWinding )
47 {
48 }
49 
50 
52 {
53 }
54 
55 
56 wxString EDA_SHAPE::ShowShape() const
57 {
58  switch( m_shape )
59  {
60  case SHAPE_T::SEGMENT: return _( "Line" );
61  case SHAPE_T::RECT: return _( "Rect" );
62  case SHAPE_T::ARC: return _( "Arc" );
63  case SHAPE_T::CIRCLE: return _( "Circle" );
64  case SHAPE_T::BEZIER: return _( "Bezier Curve" );
65  case SHAPE_T::POLY: return _( "Polygon" );
66  default: return wxT( "??" );
67  }
68 }
69 
70 
72 {
73  switch( m_shape )
74  {
75  case SHAPE_T::SEGMENT: return "S_SEGMENT";
76  case SHAPE_T::RECT: return "S_RECT";
77  case SHAPE_T::ARC: return "S_ARC";
78  case SHAPE_T::CIRCLE: return "S_CIRCLE";
79  case SHAPE_T::POLY: return "S_POLYGON";
80  case SHAPE_T::BEZIER: return "S_CURVE";
81  case SHAPE_T::LAST: return "!S_LAST!"; // Synthetic value, but if we come across it then
82  // we're going to want to know.
83  }
84 
85  return wxEmptyString; // Just to quiet GCC.
86 }
87 
88 
89 void EDA_SHAPE::setPosition( const wxPoint& aPos )
90 {
91  move( aPos - getPosition() );
92 }
93 
94 
95 wxPoint EDA_SHAPE::getPosition() const
96 {
97  if( m_shape == SHAPE_T::ARC )
98  return getCenter();
99  else if( m_shape == SHAPE_T::POLY )
100  return (wxPoint) m_poly.CVertex( 0 );
101  else
102  return m_start;
103 }
104 
105 
106 double EDA_SHAPE::GetLength() const
107 {
108  double length = 0.0;
109 
110  switch( m_shape )
111  {
112  case SHAPE_T::BEZIER:
113  for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
114  length += GetLineLength( m_bezierPoints[ ii - 1], m_bezierPoints[ii] );
115 
116  return length;
117 
118  case SHAPE_T::SEGMENT:
119  return GetLineLength( GetStart(), GetEnd() );
120 
121  case SHAPE_T::POLY:
122  for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
123  length += m_poly.COutline( 0 ).CSegment( ii ).Length();
124 
125  return length;
126 
127  case SHAPE_T::ARC:
128  return 2 * M_PI * GetRadius() * ( GetArcAngle() / 3600.0 );
129 
130  default:
132  return 0.0;
133  }
134 }
135 
136 
137 void EDA_SHAPE::move( const wxPoint& aMoveVector )
138 {
139  switch ( m_shape )
140  {
141  case SHAPE_T::ARC:
142  case SHAPE_T::SEGMENT:
143  case SHAPE_T::RECT:
144  case SHAPE_T::CIRCLE:
145  m_start += aMoveVector;
146  m_end += aMoveVector;
147  m_arcCenter += aMoveVector;
148  break;
149 
150  case SHAPE_T::POLY:
151  m_poly.Move( VECTOR2I( aMoveVector ) );
152  break;
153 
154  case SHAPE_T::BEZIER:
155  m_start += aMoveVector;
156  m_end += aMoveVector;
157  m_bezierC1 += aMoveVector;
158  m_bezierC2 += aMoveVector;
159 
160  for( wxPoint& pt : m_bezierPoints)
161  pt += aMoveVector;
162 
163  break;
164 
165  default:
167  break;
168  }
169 }
170 
171 
172 void EDA_SHAPE::scale( double aScale )
173 {
174  auto scalePt = [&]( wxPoint& pt )
175  {
176  pt.x = KiROUND( pt.x * aScale );
177  pt.y = KiROUND( pt.y * aScale );
178  };
179 
180  switch( m_shape )
181  {
182  case SHAPE_T::ARC:
183  case SHAPE_T::SEGMENT:
184  case SHAPE_T::RECT:
185  scalePt( m_start );
186  scalePt( m_end );
187  scalePt( m_arcCenter );
188  break;
189 
190  case SHAPE_T::CIRCLE: // ring or circle
191  scalePt( m_start );
192  m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
193  m_end.y = m_start.y;
194  break;
195 
196  case SHAPE_T::POLY: // polygon
197  {
198  std::vector<wxPoint> pts;
199 
200  for( const VECTOR2I& pt : m_poly.Outline( 0 ).CPoints() )
201  {
202  pts.emplace_back( pt );
203  scalePt( pts.back() );
204  }
205 
206  SetPolyPoints( pts );
207  }
208  break;
209 
210  case SHAPE_T::BEZIER:
211  scalePt( m_start );
212  scalePt( m_end );
213  scalePt( m_bezierC1 );
214  scalePt( m_bezierC2 );
215  break;
216 
217  default:
219  break;
220  }
221 }
222 
223 
224 void EDA_SHAPE::rotate( const wxPoint& aRotCentre, double aAngle )
225 {
226  switch( m_shape )
227  {
228  case SHAPE_T::SEGMENT:
229  case SHAPE_T::CIRCLE:
230  RotatePoint( &m_start, aRotCentre, aAngle );
231  RotatePoint( &m_end, aRotCentre, aAngle );
232  break;
233 
234  case SHAPE_T::ARC:
235  RotatePoint( &m_start, aRotCentre, aAngle );
236  RotatePoint( &m_end, aRotCentre, aAngle );
237  RotatePoint( &m_arcCenter, aRotCentre, aAngle );
238  break;
239 
240  case SHAPE_T::RECT:
241  if( KiROUND( aAngle ) % 900 == 0 )
242  {
243  RotatePoint( &m_start, aRotCentre, aAngle );
244  RotatePoint( &m_end, aRotCentre, aAngle );
245  break;
246  }
247 
248  // Convert non-cartesian-rotated rect to a diamond
251  m_poly.NewOutline();
252  m_poly.Append( m_start );
253  m_poly.Append( m_end.x, m_start.y );
254  m_poly.Append( m_end );
255  m_poly.Append( m_start.x, m_end.y );
256 
258 
259  case SHAPE_T::POLY:
260  m_poly.Rotate( -DECIDEG2RAD( aAngle ), VECTOR2I( aRotCentre ) );
261  break;
262 
263  case SHAPE_T::BEZIER:
264  RotatePoint( &m_start, aRotCentre, aAngle);
265  RotatePoint( &m_end, aRotCentre, aAngle);
266  RotatePoint( &m_bezierC1, aRotCentre, aAngle);
267  RotatePoint( &m_bezierC2, aRotCentre, aAngle);
268 
269  for( wxPoint& pt : m_bezierPoints )
270  RotatePoint( &pt, aRotCentre, aAngle);
271 
272  break;
273 
274  default:
276  break;
277  }
278 }
279 
280 
281 void EDA_SHAPE::flip( const wxPoint& aCentre, bool aFlipLeftRight )
282 {
283  switch ( m_shape )
284  {
285  case SHAPE_T::SEGMENT:
286  case SHAPE_T::RECT:
287  if( aFlipLeftRight )
288  {
289  m_start.x = aCentre.x - ( m_start.x - aCentre.x );
290  m_end.x = aCentre.x - ( m_end.x - aCentre.x );
291  }
292  else
293  {
294  m_start.y = aCentre.y - ( m_start.y - aCentre.y );
295  m_end.y = aCentre.y - ( m_end.y - aCentre.y );
296  }
297 
298  std::swap( m_start, m_end );
299  break;
300 
301  case SHAPE_T::CIRCLE:
302  if( aFlipLeftRight )
303  {
304  m_start.x = aCentre.x - ( m_start.x - aCentre.x );
305  m_end.x = aCentre.x - ( m_end.x - aCentre.x );
306  }
307  else
308  {
309  m_start.y = aCentre.y - ( m_start.y - aCentre.y );
310  m_end.y = aCentre.y - ( m_end.y - aCentre.y );
311  }
312  break;
313 
314  case SHAPE_T::ARC:
315  if( aFlipLeftRight )
316  {
317  m_start.x = aCentre.x - ( m_start.x - aCentre.x );
318  m_end.x = aCentre.x - ( m_end.x - aCentre.x );
319  m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
320  }
321  else
322  {
323  m_start.y = aCentre.y - ( m_start.y - aCentre.y );
324  m_end.y = aCentre.y - ( m_end.y - aCentre.y );
325  m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
326  }
327 
328  std::swap( m_start, m_end );
329  break;
330 
331  case SHAPE_T::POLY:
332  m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, VECTOR2I( aCentre ) );
333  break;
334 
335  case SHAPE_T::BEZIER:
336  if( aFlipLeftRight )
337  {
338  m_start.x = aCentre.x - ( m_start.x - aCentre.x );
339  m_end.x = aCentre.x - ( m_end.x - aCentre.x );
340  m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
341  m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x );
342  }
343  else
344  {
345  m_start.y = aCentre.y - ( m_start.y - aCentre.y );
346  m_end.y = aCentre.y - ( m_end.y - aCentre.y );
347  m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
348  m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
349  }
350 
351  // Rebuild the poly points shape
352  {
353  std::vector<wxPoint> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
354  BEZIER_POLY converter( ctrlPoints );
355  converter.GetPoly( m_bezierPoints, m_width );
356  }
357  break;
358 
359  default:
361  break;
362  }
363 }
364 
365 
367 {
368  // Has meaning only for S_CURVE DRAW_SEGMENT shape
369  if( m_shape != SHAPE_T::BEZIER )
370  {
371  m_bezierPoints.clear();
372  return;
373  }
374 
375  // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
377 }
378 
379 
380 const std::vector<wxPoint> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const
381 {
382  std::vector<wxPoint> bezierPoints;
383 
384  // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
385  std::vector<wxPoint> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
386  BEZIER_POLY converter( ctrlPoints );
387  converter.GetPoly( bezierPoints, aMinSegLen );
388 
389  return bezierPoints;
390 }
391 
392 
393 wxPoint EDA_SHAPE::getCenter() const
394 {
395  switch( m_shape )
396  {
397  case SHAPE_T::ARC:
398  return m_arcCenter;
399 
400  case SHAPE_T::CIRCLE:
401  return m_start;
402 
403  case SHAPE_T::SEGMENT:
404  // Midpoint of the line
405  return ( m_start + m_end ) / 2;
406 
407  case SHAPE_T::POLY:
408  case SHAPE_T::RECT:
409  case SHAPE_T::BEZIER:
410  return getBoundingBox().Centre();
411 
412  default:
414  return wxPoint();
415  }
416 }
417 
418 
419 void EDA_SHAPE::SetCenter( const wxPoint& aCenter )
420 {
421  switch( m_shape )
422  {
423  case SHAPE_T::ARC:
424  m_arcCenter = aCenter;
425  break;
426 
427  case SHAPE_T::CIRCLE:
428  m_start = aCenter;
429  break;
430 
431  default:
433  }
434 }
435 
436 
437 wxPoint EDA_SHAPE::GetArcMid() const
438 {
439  wxPoint mid = m_start;
440  RotatePoint( &mid, m_arcCenter, -GetArcAngle() / 2.0 );
441  return mid;
442 }
443 
444 
445 void EDA_SHAPE::CalcArcAngles( double& aStartAngle, double& aEndAngle ) const
446 {
447  VECTOR2D startRadial( GetStart() - getCenter() );
448  VECTOR2D endRadial( GetEnd() - getCenter() );
449 
450  aStartAngle = 180.0 / M_PI * atan2( startRadial.y, startRadial.x );
451  aEndAngle = 180.0 / M_PI * atan2( endRadial.y, endRadial.x );
452 
453  if( aEndAngle == aStartAngle )
454  aEndAngle = aStartAngle + 360.0; // ring, not null
455 
456  if( aStartAngle > aEndAngle )
457  {
458  if( aEndAngle < 0 )
459  aEndAngle = NormalizeAngleDegrees( aEndAngle, 0.0, 360.0 );
460  else
461  aStartAngle = NormalizeAngleDegrees( aStartAngle, -360.0, 0.0 );
462  }
463 }
464 
465 
467 {
468  double radius = 0.0;
469 
470  switch( m_shape )
471  {
472  case SHAPE_T::ARC:
473  radius = GetLineLength( m_arcCenter, m_start );
474  break;
475 
476  case SHAPE_T::CIRCLE:
477  radius = GetLineLength( m_start, m_end );
478  break;
479 
480  default:
482  }
483 
484  // don't allow degenerate circles/arcs
485  return std::max( 1, KiROUND( radius ) );
486 }
487 
488 
489 void EDA_SHAPE::SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd )
490 {
491  m_start = aStart;
492  m_end = aEnd;
493  m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
494  m_endsSwapped = false;
495 }
496 
497 
499 {
500  double startAngle;
501  double endAngle;
502 
503  CalcArcAngles( startAngle, endAngle );
504 
505  return ( endAngle - startAngle ) * 10;
506 }
507 
508 
509 void EDA_SHAPE::SetArcAngleAndEnd( double aAngle, bool aCheckNegativeAngle )
510 {
511  m_end = m_start;
513 
514  if( aCheckNegativeAngle && aAngle < 0 )
515  {
516  std::swap( m_start, m_end );
517  m_endsSwapped = true;
518  }
519 }
520 
521 
522 void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
523 {
524  EDA_UNITS units = aFrame->GetUserUnits();
525  ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
526  wxString msg;
527 
528  wxString shape = _( "Shape" );
529 
530  switch( m_shape )
531  {
532  case SHAPE_T::CIRCLE:
533  aList.emplace_back( shape, _( "Circle" ) );
534 
535  msg = MessageTextFromValue( units, GetRadius() );
536  aList.emplace_back( _( "Radius" ), msg );
537  break;
538 
539  case SHAPE_T::ARC:
540  aList.emplace_back( shape, _( "Arc" ) );
541 
542  msg.Printf( wxT( "%.1f" ), GetArcAngle() / 10.0 );
543  aList.emplace_back( _( "Angle" ), msg );
544 
545  msg = MessageTextFromValue( units, GetRadius() );
546  aList.emplace_back( _( "Radius" ), msg );
547  break;
548 
549  case SHAPE_T::BEZIER:
550  aList.emplace_back( shape, _( "Curve" ) );
551 
552  msg = MessageTextFromValue( units, GetLength() );
553  aList.emplace_back( _( "Length" ), msg );
554  break;
555 
556  case SHAPE_T::POLY:
557  aList.emplace_back( shape, _( "Polygon" ) );
558 
559  msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() );
560  aList.emplace_back( _( "Points" ), msg );
561  break;
562 
563  case SHAPE_T::RECT:
564  aList.emplace_back( shape, _( "Rectangle" ) );
565 
566  msg = MessageTextFromValue( units, std::abs( GetEnd().x - GetStart().x ) );
567  aList.emplace_back( _( "Width" ), msg );
568 
569  msg = MessageTextFromValue( units, std::abs( GetEnd().y - GetStart().y ) );
570  aList.emplace_back( _( "Height" ), msg );
571  break;
572 
573  case SHAPE_T::SEGMENT:
574  {
575  aList.emplace_back( shape, _( "Segment" ) );
576 
577  msg = MessageTextFromValue( units, GetLineLength( GetStart(), GetEnd() ) );
578  aList.emplace_back( _( "Length" ), msg );
579 
580  // angle counter-clockwise from 3'o-clock
581  const double deg = RAD2DEG( atan2( (double)( GetStart().y - GetEnd().y ),
582  (double)( GetEnd().x - GetStart().x ) ) );
583  aList.emplace_back( _( "Angle" ), wxString::Format( "%.1f", deg ) );
584  }
585  break;
586 
587  default:
588  aList.emplace_back( shape, _( "Unrecognized" ) );
589  break;
590  }
591 
592  aList.emplace_back( _( "Line width" ), MessageTextFromValue( units, m_width ) );
593 }
594 
595 
597 {
598  EDA_RECT bbox;
599 
600  switch( m_shape )
601  {
602  case SHAPE_T::RECT:
603  for( wxPoint& pt : GetRectCorners() )
604  bbox.Merge( pt );
605 
606  break;
607 
608  case SHAPE_T::SEGMENT:
609  bbox.SetOrigin( GetStart() );
610  bbox.SetEnd( GetEnd() );
611  break;
612 
613  case SHAPE_T::CIRCLE:
614  bbox.SetOrigin( GetStart() );
615  bbox.Inflate( GetRadius() );
616  break;
617 
618  case SHAPE_T::ARC:
619  computeArcBBox( bbox );
620  break;
621 
622  case SHAPE_T::POLY:
623  if( m_poly.IsEmpty() )
624  break;
625 
626  for( auto iter = m_poly.CIterate(); iter; iter++ )
627  {
628  wxPoint pt( iter->x, iter->y );
629 
631  pt += getParentPosition();
632 
633  bbox.Merge( pt );
634  }
635 
636  break;
637 
638  case SHAPE_T::BEZIER:
639  bbox.SetOrigin( GetStart() );
640  bbox.Merge( GetBezierC1() );
641  bbox.Merge( GetBezierC2() );
642  bbox.Merge( GetEnd() );
643  break;
644 
645  default:
647  break;
648  }
649 
650  bbox.Inflate( std::max( 0, m_width / 2 ) );
651  bbox.Normalize();
652 
653  return bbox;
654 }
655 
656 
657 bool EDA_SHAPE::hitTest( const wxPoint& aPosition, int aAccuracy ) const
658 {
659  int maxdist = aAccuracy;
660 
661  if( m_width > 0 )
662  maxdist += m_width / 2;
663 
664  switch( m_shape )
665  {
666  case SHAPE_T::CIRCLE:
667  {
668  int radius = GetRadius();
669  int dist = KiROUND( EuclideanNorm( aPosition - getCenter() ) );
670 
671  if( IsFilled() )
672  return dist <= radius + maxdist; // Filled circle hit-test
673  else
674  return abs( radius - dist ) <= maxdist; // Ring hit-test
675  }
676 
677  case SHAPE_T::ARC:
678  {
679  if( EuclideanNorm( aPosition - m_start ) <= maxdist )
680  return true;
681 
682  if( EuclideanNorm( aPosition - m_end ) <= maxdist )
683  return true;
684 
685  wxPoint relPos = aPosition - getCenter();
686  int radius = GetRadius();
687  int dist = KiROUND( EuclideanNorm( relPos ) );
688 
689  if( abs( radius - dist ) <= maxdist )
690  {
691  double startAngle;
692  double endAngle;
693  CalcArcAngles( startAngle, endAngle );
694 
695  if( m_eeWinding && NormalizeAngleDegrees( startAngle - endAngle, -180.0, 180.0 ) > 0 )
696  std::swap( startAngle, endAngle );
697 
698  double relPosAngle = 180.0 / M_PI * atan2( relPos.y, relPos.x );
699 
700  startAngle = NormalizeAngleDegrees( startAngle, 0.0, 360.0 );
701  endAngle = NormalizeAngleDegrees( endAngle, 0.0, 360.0 );
702  relPosAngle = NormalizeAngleDegrees( relPosAngle, 0.0, 360.0 );
703 
704  if( endAngle > startAngle )
705  return relPosAngle >= startAngle && relPosAngle <= endAngle;
706  else
707  return relPosAngle >= startAngle || relPosAngle <= endAngle;
708  }
709 
710  return false;
711  }
712 
713  case SHAPE_T::BEZIER:
714  const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( m_width );
715 
716  for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
717  {
718  if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
719  return true;
720  }
721 
722  return false;
723 
724  case SHAPE_T::SEGMENT:
725  return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
726 
727  case SHAPE_T::RECT:
728  if( IsFilled() ) // Filled rect hit-test
729  {
730  SHAPE_POLY_SET poly;
731  poly.NewOutline();
732 
733  for( const wxPoint& pt : GetRectCorners() )
734  poly.Append( pt );
735 
736  return poly.Collide( VECTOR2I( aPosition ), maxdist );
737  }
738  else // Open rect hit-test
739  {
740  std::vector<wxPoint> pts = GetRectCorners();
741 
742  return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
743  || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
744  || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
745  || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
746  }
747 
748  case SHAPE_T::POLY:
749  if( IsFilled() )
750  {
751  return m_poly.Collide( VECTOR2I( aPosition ), maxdist );
752  }
753  else
754  {
756  return m_poly.CollideEdge( VECTOR2I( aPosition ), dummy, maxdist );
757  }
758 
759  default:
761  return false;
762  }
763 }
764 
765 
766 bool EDA_SHAPE::hitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
767 {
768  EDA_RECT arect = aRect;
769  arect.Normalize();
770  arect.Inflate( aAccuracy );
771 
772  EDA_RECT arcRect;
773  EDA_RECT bb = getBoundingBox();
774 
775  switch( m_shape )
776  {
777  case SHAPE_T::CIRCLE:
778  // Test if area intersects or contains the circle:
779  if( aContained )
780  {
781  return arect.Contains( bb );
782  }
783  else
784  {
785  // If the rectangle does not intersect the bounding box, this is a much quicker test
786  if( !aRect.Intersects( bb ) )
787  {
788  return false;
789  }
790  else
791  {
792  return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
793  }
794  }
795 
796  case SHAPE_T::ARC:
797  // Test for full containment of this arc in the rect
798  if( aContained )
799  {
800  return arect.Contains( bb );
801  }
802  // Test if the rect crosses the arc
803  else
804  {
805  arcRect = bb.Common( arect );
806 
807  /* All following tests must pass:
808  * 1. Rectangle must intersect arc BoundingBox
809  * 2. Rectangle must cross the outside of the arc
810  */
811  return arcRect.Intersects( arect ) &&
813  }
814 
815  case SHAPE_T::RECT:
816  if( aContained )
817  {
818  return arect.Contains( bb );
819  }
820  else
821  {
822  std::vector<wxPoint> pts = GetRectCorners();
823 
824  // Account for the width of the lines
825  arect.Inflate( GetWidth() / 2 );
826  return ( arect.Intersects( pts[0], pts[1] )
827  || arect.Intersects( pts[1], pts[2] )
828  || arect.Intersects( pts[2], pts[3] )
829  || arect.Intersects( pts[3], pts[0] ) );
830  }
831 
832  case SHAPE_T::SEGMENT:
833  if( aContained )
834  {
835  return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
836  }
837  else
838  {
839  // Account for the width of the line
840  arect.Inflate( GetWidth() / 2 );
841  return arect.Intersects( GetStart(), GetEnd() );
842  }
843 
844  case SHAPE_T::POLY:
845  if( aContained )
846  {
847  return arect.Contains( bb );
848  }
849  else
850  {
851  // Fast test: if aRect is outside the polygon bounding box,
852  // rectangles cannot intersect
853  if( !arect.Intersects( bb ) )
854  return false;
855 
856  // Account for the width of the line
857  arect.Inflate( GetWidth() / 2 );
858 
859  // Polygons in footprints use coordinates relative to the footprint.
860  // Therefore, instead of using m_poly, we make a copy which is translated
861  // to the actual location in the board.
862  double orientation = 0.0;
863  wxPoint offset = getParentPosition();
864 
865  if( getParentOrientation() )
866  orientation = -DECIDEG2RAD( getParentOrientation() );
867 
868  SHAPE_LINE_CHAIN poly = m_poly.Outline( 0 );
869  poly.Rotate( orientation );
870  poly.Move( offset );
871 
872  int count = poly.GetPointCount();
873 
874  for( int ii = 0; ii < count; ii++ )
875  {
876  VECTOR2I vertex = poly.GetPoint( ii );
877 
878  // Test if the point is within aRect
879  if( arect.Contains( ( wxPoint ) vertex ) )
880  return true;
881 
882  if( ii + 1 < count )
883  {
884  VECTOR2I vertexNext = poly.GetPoint( ii + 1 );
885 
886  // Test if this edge intersects aRect
887  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
888  return true;
889  }
890  else if( poly.IsClosed() )
891  {
892  VECTOR2I vertexNext = poly.GetPoint( 0 );
893 
894  // Test if this edge intersects aRect
895  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
896  return true;
897  }
898  }
899 
900  return false;
901  }
902 
903  case SHAPE_T::BEZIER:
904  if( aContained )
905  {
906  return arect.Contains( bb );
907  }
908  else
909  {
910  // Fast test: if aRect is outside the polygon bounding box,
911  // rectangles cannot intersect
912  if( !arect.Intersects( bb ) )
913  return false;
914 
915  // Account for the width of the line
916  arect.Inflate( GetWidth() / 2 );
917  unsigned count = m_bezierPoints.size();
918 
919  for( unsigned ii = 1; ii < count; ii++ )
920  {
921  wxPoint vertex = m_bezierPoints[ ii - 1];
922  wxPoint vertexNext = m_bezierPoints[ii];
923 
924  // Test if the point is within aRect
925  if( arect.Contains( ( wxPoint ) vertex ) )
926  return true;
927 
928  // Test if this edge intersects aRect
929  if( arect.Intersects( vertex, vertexNext ) )
930  return true;
931  }
932 
933  return false;
934  }
935 
936  default:
938  return false;
939  }
940 }
941 
942 
943 std::vector<wxPoint> EDA_SHAPE::GetRectCorners() const
944 {
945  std::vector<wxPoint> pts;
946  wxPoint topLeft = GetStart();
947  wxPoint botRight = GetEnd();
948 
949  // Un-rotate rect topLeft and botRight
950  if( KiROUND( getParentOrientation() ) % 900 != 0 )
951  {
952  topLeft -= getParentPosition();
953  RotatePoint( &topLeft, -getParentOrientation() );
954 
955  botRight -= getParentPosition();
956  RotatePoint( &botRight, -getParentOrientation() );
957  }
958 
959  // Set up the un-rotated 4 corners
960  pts.emplace_back( topLeft );
961  pts.emplace_back( botRight.x, topLeft.y );
962  pts.emplace_back( botRight );
963  pts.emplace_back( topLeft.x, botRight.y );
964 
965  // Now re-rotate the 4 corners to get a diamond
966  if( KiROUND( getParentOrientation() ) % 900 != 0 )
967  {
968  for( wxPoint& pt : pts )
969  {
971  pt += getParentPosition();
972  }
973  }
974 
975  return pts;
976 }
977 
978 
980 {
981  wxPoint start = m_start;
982  wxPoint end = m_end;
983  double t1, t2;
984 
985  CalcArcAngles( t1, t2 );
986 
987  if( m_eeWinding && NormalizeAngleDegrees( t1 - t2, -180.0, 180.0 ) > 0 )
988  std::swap( start, end );
989 
990  // Do not include the center, which is not necessarily inside the BB of an arc with a small
991  // included angle
992  aBBox.SetOrigin( start );
993  aBBox.Merge( end );
994 
995  // Determine the starting quarter
996  // 0 right-bottom
997  // 1 left-bottom
998  // 2 left-top
999  // 3 right-top
1000  unsigned int quarter;
1001 
1002  if( start.x < m_arcCenter.x )
1003  {
1004  if( start.y <= m_arcCenter.y )
1005  quarter = 2;
1006  else
1007  quarter = 1;
1008  }
1009  else if( start.x == m_arcCenter.x )
1010  {
1011  if( start.y < m_arcCenter.y )
1012  quarter = 3;
1013  else
1014  quarter = 1;
1015  }
1016  else
1017  {
1018  if( start.y < m_arcCenter.y )
1019  quarter = 3;
1020  else
1021  quarter = 0;
1022  }
1023 
1024  int radius = GetRadius();
1025  VECTOR2I startRadial = start - m_arcCenter;
1026  VECTOR2I endRadial = end - m_arcCenter;
1027  double angleStart = ArcTangente( startRadial.y, startRadial.x );
1028  double arcAngle = RAD2DECIDEG( endRadial.Angle() - startRadial.Angle() );
1029  int angle = (int) NormalizeAnglePos( angleStart ) % 900 + NormalizeAnglePos( arcAngle );
1030 
1031  while( angle > 900 )
1032  {
1033  switch( quarter )
1034  {
1035  case 0: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y + radius ) ); break; // down
1036  case 1: aBBox.Merge( wxPoint( m_arcCenter.x - radius, m_arcCenter.y ) ); break; // left
1037  case 2: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y - radius ) ); break; // up
1038  case 3: aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y ) ); break; // right
1039  }
1040 
1041  ++quarter %= 4;
1042  angle -= 900;
1043  }
1044 }
1045 
1046 
1047 void EDA_SHAPE::SetPolyPoints( const std::vector<wxPoint>& aPoints )
1048 {
1050  m_poly.NewOutline();
1051 
1052  for ( const wxPoint& p : aPoints )
1053  m_poly.Append( p.x, p.y );
1054 }
1055 
1056 
1057 std::vector<SHAPE*> EDA_SHAPE::MakeEffectiveShapes() const
1058 {
1059  std::vector<SHAPE*> effectiveShapes;
1060 
1061  switch( m_shape )
1062  {
1063  case SHAPE_T::ARC:
1064  effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle() / 10.0,
1065  m_width ) );
1066  break;
1067 
1068  case SHAPE_T::SEGMENT:
1069  effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, m_width ) );
1070  break;
1071 
1072  case SHAPE_T::RECT:
1073  {
1074  std::vector<wxPoint> pts = GetRectCorners();
1075 
1076  if( IsFilled() )
1077  effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1078 
1079  if( m_width > 0 || !IsFilled() )
1080  {
1081  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], m_width ) );
1082  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], m_width ) );
1083  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], m_width ) );
1084  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], m_width ) );
1085  }
1086  }
1087  break;
1088 
1089  case SHAPE_T::CIRCLE:
1090  {
1091  if( IsFilled() )
1092  effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1093 
1094  if( m_width > 0 || !IsFilled() )
1095  {
1096  // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC
1097  SHAPE_ARC circle( getCenter(), GetEnd(), 360.0 );
1098  SHAPE_LINE_CHAIN l = circle.ConvertToPolyline();
1099 
1100  for( int i = 0; i < l.SegmentCount(); i++ )
1101  {
1102  effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A, l.Segment( i ).B,
1103  m_width ) );
1104  }
1105  }
1106 
1107  break;
1108  }
1109 
1110  case SHAPE_T::BEZIER:
1111  {
1112  auto bezierPoints = buildBezierToSegmentsPointsList( GetWidth() );
1113  wxPoint start_pt = bezierPoints[0];
1114 
1115  for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1116  {
1117  wxPoint end_pt = bezierPoints[jj];
1118  effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, m_width ) );
1119  start_pt = end_pt;
1120  }
1121 
1122  break;
1123  }
1124 
1125  case SHAPE_T::POLY:
1126  {
1128 
1130  l.Move( getParentPosition() );
1131 
1132  if( IsFilled() )
1133  effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1134 
1135  if( m_width > 0 || !IsFilled() )
1136  {
1137  for( int i = 0; i < l.SegmentCount(); i++ )
1138  effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ), m_width ) );
1139  }
1140  }
1141  break;
1142 
1143  default:
1145  break;
1146  }
1147 
1148  return effectiveShapes;
1149 }
1150 
1151 
1152 void EDA_SHAPE::DupPolyPointsList( std::vector<wxPoint>& aBuffer ) const
1153 {
1154  if( m_poly.OutlineCount() )
1155  {
1156  int pointCount = m_poly.COutline( 0 ).PointCount();
1157 
1158  if( pointCount )
1159  {
1160  aBuffer.reserve( pointCount );
1161 
1162  for ( auto iter = m_poly.CIterate(); iter; iter++ )
1163  aBuffer.emplace_back( iter->x, iter->y );
1164  }
1165  }
1166 }
1167 
1168 
1170 {
1171  // return true if the polygonal shape is valid (has more than 2 points)
1172  if( GetPolyShape().OutlineCount() == 0 )
1173  return false;
1174 
1175  const SHAPE_LINE_CHAIN& outline = ( (SHAPE_POLY_SET&)GetPolyShape() ).Outline( 0 );
1176 
1177  return outline.PointCount() > 2;
1178 }
1179 
1180 
1182 {
1183  // return the number of corners of the polygonal shape
1184  // this shape is expected to be only one polygon without hole
1185  if( GetPolyShape().OutlineCount() )
1186  return GetPolyShape().VertexCount( 0 );
1187 
1188  return 0;
1189 }
1190 
1191 
1192 void EDA_SHAPE::beginEdit( const wxPoint& aPosition )
1193 {
1194  switch( GetShape() )
1195  {
1196  case SHAPE_T::SEGMENT:
1197  case SHAPE_T::CIRCLE:
1198  case SHAPE_T::RECT:
1199  SetStart( aPosition );
1200  SetEnd( aPosition );
1201  break;
1202 
1203  case SHAPE_T::ARC:
1204  SetArcGeometry( aPosition, aPosition, aPosition );
1205  m_editState = 1;
1206  break;
1207 
1208  case SHAPE_T::POLY:
1209  m_poly.NewOutline();
1210  m_poly.Outline( 0 ).SetClosed( false );
1211 
1212  // Start and end of the first segment (co-located for now)
1213  m_poly.Outline( 0 ).Append( aPosition );
1214  m_poly.Outline( 0 ).Append( aPosition, true );
1215  break;
1216 
1217  default:
1219  }
1220 }
1221 
1222 
1223 bool EDA_SHAPE::continueEdit( const wxPoint& aPosition )
1224 {
1225  switch( GetShape() )
1226  {
1227  case SHAPE_T::ARC:
1228  case SHAPE_T::SEGMENT:
1229  case SHAPE_T::CIRCLE:
1230  case SHAPE_T::RECT:
1231  return false;
1232 
1233  case SHAPE_T::POLY:
1234  {
1235  SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1236 
1237  // do not add zero-length segments
1238  if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1239  poly.Append( aPosition, true );
1240  }
1241  return true;
1242 
1243  default:
1245  return false;
1246  }
1247 }
1248 
1249 
1250 void EDA_SHAPE::calcEdit( const wxPoint& aPosition )
1251 {
1252 #define sq( x ) pow( x, 2 )
1253 
1254  switch( GetShape() )
1255  {
1256  case SHAPE_T::SEGMENT:
1257  case SHAPE_T::CIRCLE:
1258  case SHAPE_T::RECT:
1259  SetEnd( aPosition );
1260  break;
1261 
1262  case SHAPE_T::ARC:
1263  {
1264  int radius = GetRadius();
1265 
1266  // Edit state 0: drawing: place start
1267  // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1268  // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1269  // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1270  // Edit state 4: point edit: move center
1271  // Edit state 5: point edit: move arc-mid-point
1272 
1273  switch( m_editState )
1274  {
1275  case 0:
1276  SetArcGeometry( aPosition, aPosition, aPosition );
1277  return;
1278 
1279  case 1:
1280  m_end = aPosition;
1281  radius = KiROUND( sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 ) );
1282  break;
1283 
1284  case 2:
1285  case 3:
1286  {
1287  wxPoint v = m_start - m_end;
1288  double chordBefore = sq( v.x ) + sq( v.y );
1289 
1290  if( m_editState == 2 )
1291  m_start = aPosition;
1292  else
1293  m_end = aPosition;
1294 
1295  v = m_start - m_end;
1296  double chordAfter = sq( v.x ) + sq( v.y );
1297  double ratio = chordAfter / chordBefore;
1298 
1299  if( ratio != 0 )
1300  {
1301  radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
1302  int( sqrt( chordAfter ) / 2 ) + 1 );
1303  }
1304  }
1305  break;
1306 
1307  case 4:
1308  {
1309  double chordA = GetLineLength( m_start, aPosition );
1310  double chordB = GetLineLength( m_end, aPosition );
1311  radius = int( ( chordA + chordB ) / 2.0 ) + 1;
1312  }
1313  break;
1314 
1315  case 5:
1316  SetArcGeometry( GetStart(), aPosition, GetEnd() );
1317  return;
1318  }
1319 
1320  // Calculate center based on start, end, and radius
1321  //
1322  // Let 'l' be the length of the chord and 'm' the middle point of the chord
1323  double l = GetLineLength( m_start, m_end );
1324  wxPoint m = ( m_start + m_end ) / 2;
1325 
1326  // Calculate 'd', the vector from the chord midpoint to the center
1327  wxPoint d;
1328  d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_start.y - m_end.y ) / l );
1329  d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_end.x - m_start.x ) / l );
1330 
1331  wxPoint c1 = m + d;
1332  wxPoint c2 = m - d;
1333 
1334  // Solution gives us 2 centers; we need to pick one:
1335  switch( m_editState )
1336  {
1337  case 1:
1338  {
1339  // Keep center clockwise from chord while drawing
1340  wxPoint chordVector = m_end - m_start;
1341  double chordAngle = ArcTangente( chordVector.y, chordVector.x );
1342  NORMALIZE_ANGLE_POS( chordAngle );
1343 
1344  wxPoint c1Test = c1;
1345  RotatePoint( &c1Test, m_start, -chordAngle );
1346 
1347  m_arcCenter = c1Test.x > 0 ? c2 : c1;
1348  }
1349  break;
1350 
1351  case 2:
1352  case 3:
1353  // Pick the one closer to the old center
1354  m_arcCenter = GetLineLength( c1, m_arcCenter ) < GetLineLength( c2, m_arcCenter ) ? c1 : c2;
1355  break;
1356 
1357  case 4:
1358  // Pick the one closer to the mouse position
1359  m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
1360  break;
1361  }
1362  }
1363  break;
1364 
1365  case SHAPE_T::POLY:
1366  m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1367  break;
1368 
1369  default:
1371  }
1372 }
1373 
1374 
1376 {
1377  switch( GetShape() )
1378  {
1379  case SHAPE_T::ARC:
1380  case SHAPE_T::SEGMENT:
1381  case SHAPE_T::CIRCLE:
1382  case SHAPE_T::RECT:
1383  break;
1384 
1385  case SHAPE_T::POLY:
1386  {
1387  SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1388 
1389  // do not include last point twice
1390  if( poly.GetPointCount() > 2 )
1391  {
1392  if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1393  {
1394  poly.SetClosed( true );
1395  poly.Remove( poly.GetPointCount() - 1 );
1396  }
1397  }
1398  }
1399  break;
1400 
1401  default:
1403  }
1404 }
1405 
1406 
1408 {
1409  EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1410  assert( image );
1411 
1412  std::swap( m_width, image->m_width );
1413  std::swap( m_start, image->m_start );
1414  std::swap( m_end, image->m_end );
1415  std::swap( m_arcCenter, image->m_arcCenter );
1416  std::swap( m_shape, image->m_shape );
1417  std::swap( m_bezierC1, image->m_bezierC1 );
1418  std::swap( m_bezierC2, image->m_bezierC2 );
1419  std::swap( m_bezierPoints, image->m_bezierPoints );
1420  std::swap( m_poly, image->m_poly );
1421 }
1422 
1423 
1424 int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1425 {
1426 #define EPSILON 2 // Should be enough for rounding errors on calculated items
1427 
1428 #define TEST( a, b ) { if( a != b ) return a - b; }
1429 #define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1430 #define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1431 
1432  TEST_PT( m_start, aOther->m_start );
1433  TEST_PT( m_end, aOther->m_end );
1434 
1435  TEST( (int) m_shape, (int) aOther->m_shape );
1436 
1437  if( m_shape == SHAPE_T::ARC )
1438  {
1439  TEST_PT( m_arcCenter, aOther->m_arcCenter );
1440  }
1441  else if( m_shape == SHAPE_T::BEZIER )
1442  {
1443  TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1444  TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1445  }
1446  else if( m_shape == SHAPE_T::POLY )
1447  {
1448  TEST( m_poly.TotalVertices(), aOther->m_poly.TotalVertices() );
1449 
1450  for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1451  TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1452  }
1453 
1454  TEST_E( m_width, aOther->m_width );
1455  TEST( (int) m_fill, (int) aOther->m_fill );
1456 
1457  return 0;
1458 }
1459 
1460 
1462  int aClearanceValue,
1463  int aError, ERROR_LOC aErrorLoc,
1464  bool ignoreLineWidth ) const
1465 {
1466  int width = ignoreLineWidth ? 0 : m_width;
1467 
1468  width += 2 * aClearanceValue;
1469 
1470  switch( m_shape )
1471  {
1472  case SHAPE_T::CIRCLE:
1473  if( IsFilled() )
1474  {
1475  TransformCircleToPolygon( aCornerBuffer, getCenter(), GetRadius() + width / 2, aError,
1476  aErrorLoc );
1477  }
1478  else
1479  {
1480  TransformRingToPolygon( aCornerBuffer, getCenter(), GetRadius(), width, aError,
1481  aErrorLoc );
1482  }
1483 
1484  break;
1485 
1486  case SHAPE_T::RECT:
1487  {
1488  std::vector<wxPoint> pts = GetRectCorners();
1489 
1490  if( IsFilled() )
1491  {
1492  aCornerBuffer.NewOutline();
1493 
1494  for( const wxPoint& pt : pts )
1495  aCornerBuffer.Append( pt );
1496  }
1497 
1498  if( width > 0 || !IsFilled() )
1499  {
1500  // Add in segments
1501  TransformOvalToPolygon( aCornerBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1502  TransformOvalToPolygon( aCornerBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1503  TransformOvalToPolygon( aCornerBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1504  TransformOvalToPolygon( aCornerBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1505  }
1506 
1507  break;
1508  }
1509 
1510  case SHAPE_T::ARC:
1511  TransformArcToPolygon( aCornerBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError,
1512  aErrorLoc );
1513  break;
1514 
1515  case SHAPE_T::SEGMENT:
1516  TransformOvalToPolygon( aCornerBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1517  break;
1518 
1519  case SHAPE_T::POLY:
1520  {
1521  if( !IsPolyShapeValid() )
1522  break;
1523 
1524  // The polygon is expected to be a simple polygon; not self intersecting, no hole.
1525  double orientation = getParentOrientation();
1526  wxPoint offset = getParentPosition();
1527 
1528  // Build the polygon with the actual position and orientation:
1529  std::vector<wxPoint> poly;
1530  DupPolyPointsList( poly );
1531 
1532  for( wxPoint& point : poly )
1533  {
1534  RotatePoint( &point, orientation );
1535  point += offset;
1536  }
1537 
1538  if( IsFilled() )
1539  {
1540  aCornerBuffer.NewOutline();
1541 
1542  for( const wxPoint& point : poly )
1543  aCornerBuffer.Append( point.x, point.y );
1544  }
1545 
1546  if( width > 0 || !IsFilled() )
1547  {
1548  wxPoint pt1( poly[ poly.size() - 1] );
1549 
1550  for( const wxPoint& pt2 : poly )
1551  {
1552  if( pt2 != pt1 )
1553  TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError, aErrorLoc );
1554 
1555  pt1 = pt2;
1556  }
1557  }
1558 
1559  break;
1560  }
1561 
1562  case SHAPE_T::BEZIER:
1563  {
1564  std::vector<wxPoint> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
1565  BEZIER_POLY converter( ctrlPts );
1566  std::vector< wxPoint> poly;
1567  converter.GetPoly( poly, m_width );
1568 
1569  for( unsigned ii = 1; ii < poly.size(); ii++ )
1570  {
1571  TransformOvalToPolygon( aCornerBuffer, poly[ii - 1], poly[ii], width, aError,
1572  aErrorLoc );
1573  }
1574 
1575  break;
1576  }
1577 
1578  default:
1580  break;
1581  }
1582 }
1583 
1584 
1585 static struct EDA_SHAPE_DESC
1586 {
1588  {
1590  .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
1591  .Map( SHAPE_T::RECT, _HKI( "Rectangle" ) )
1592  .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
1593  .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
1594  .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
1595  .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
1597  .Map( PLOT_DASH_TYPE::DEFAULT, _HKI( "Default" ) )
1598  .Map( PLOT_DASH_TYPE::SOLID, _HKI( "Solid" ) )
1599  .Map( PLOT_DASH_TYPE::DASH, _HKI( "Dashed" ) )
1600  .Map( PLOT_DASH_TYPE::DOT, _HKI( "Dotted" ) )
1601  .Map( PLOT_DASH_TYPE::DASHDOT, _HKI( "Dash-Dot" ) );
1602 
1605  propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
1607  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
1609  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
1611  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
1613  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
1615  // TODO: m_arcCenter, m_bezierC1, m_bezierC2, m_poly
1616  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
1618  }
1619 } _EDA_SHAPE_DESC;
1620 
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
int Length() const
Return the length (this).
Definition: seg.h:350
int TotalVertices() const
Delete aIdx-th polygon from the set.
int GetStartY()
Definition: eda_shape.h:98
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
SHAPE_T m_shape
Definition: eda_shape.h:296
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:41
void endEdit()
Definition: eda_shape.cpp:1375
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:65
int GetWidth() const
Definition: eda_shape.h:89
Plot settings, and plotting engines (PostScript, Gerber, HPGL and DXF)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
wxPoint m_bezierC1
Definition: eda_shape.h:304
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:222
void Merge(const EDA_RECT &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: eda_rect.cpp:432
virtual size_t GetPointCount() const override
T NormalizeAngle360Max(T Angle)
Normalize angle to be >=-360.0 and <= 360.0 Angle can be equal to -360 or +360.
Definition: trigo.h:241
#define sq(x)
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:126
void SetArcGeometry(const wxPoint &aStart, const wxPoint &aMid, const wxPoint &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:489
marker for list end
wxPoint getCenter() const
Definition: eda_shape.cpp:393
Implementation of conversion functions that require both schematic and board internal units.
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:97
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
void GetPoly(std::vector< wxPoint > &aOutput, int aMinSegLen=0)
Convert a Bezier curve to a polygon.
bool hitTest(const wxPoint &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:657
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the aGlobalIndex-th vertex in the poly set.
void SetStartY(int y)
Definition: eda_shape.h:107
void Move(const VECTOR2I &aVector) override
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth) const
Convert the shape to a closed polygon.
Definition: eda_shape.cpp:1461
bool IsEmpty() const
int GetEndY()
Definition: eda_shape.h:123
void calcEdit(const wxPoint &aPosition)
Definition: eda_shape.cpp:1250
double RAD2DEG(double rad)
Definition: trigo.h:230
void computeArcBBox(EDA_RECT &aBBox) const
Definition: eda_shape.cpp:979
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill, bool eeWinding)
Definition: eda_shape.cpp:40
int VertexCount(int aOutline=-1, int aHole=-1) const
Return the number of points in the shape poly set.
std::vector< SHAPE * > MakeEffectiveShapes() const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.cpp:1057
static ENUM_MAP< T > & Instance()
Definition: property.h:510
static struct EDA_SHAPE_DESC _EDA_SHAPE_DESC
void DupPolyPointsList(std::vector< wxPoint > &aBuffer) const
Duplicate the list of corners in a std::vector<wxPoint>
Definition: eda_shape.cpp:1152
double GetArcAngle() const
Definition: eda_shape.cpp:498
double RAD2DECIDEG(double rad)
Definition: trigo.h:234
void CalcArcAngles(double &aStartAngle, double &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
Definition: eda_shape.cpp:445
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
virtual wxPoint getParentPosition() const =0
void SetOrigin(const wxPoint &pos)
Definition: eda_rect.h:121
virtual ORIGIN_TRANSFORMS & GetOriginTransforms()
Return a reference to the default ORIGIN_TRANSFORMS object.
bool IntersectsCircleEdge(const wxPoint &aCenter, const int aRadius, const int aWidth) const
Test for intersection between this rect and the edge (radius) of a circle.
Definition: eda_rect.cpp:331
void SetArcAngleAndEnd(double aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:509
EDA_RECT Common(const EDA_RECT &aRect) const
Return the area that is common with another rectangle.
Definition: eda_rect.cpp:489
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
void beginEdit(const wxPoint &aStartPoint)
Definition: eda_shape.cpp:1192
void Rotate(double aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void SetEndY(int y)
Definition: eda_shape.h:132
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:290
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:522
The base class for create windows for drawing purpose.
wxString ShowShape() const
Definition: eda_shape.cpp:56
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1169
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
int GetStartX()
Definition: eda_shape.h:99
int PointCount() const
Return the number of points (vertices) in this line chain.
#define REGISTER_TYPE(x)
Definition: property_mgr.h:248
bool Contains(const wxPoint &aPoint) const
Definition: eda_rect.cpp:57
wxPoint m_end
Definition: eda_shape.h:300
FILL_T
Definition: eda_shape.h:53
wxPoint m_arcCenter
Definition: eda_shape.h:302
void Mirror(bool aX=true, bool aY=false, const VECTOR2I &aRef={ 0, 0 })
Mirror the line points about y or x (or both)
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
int GetEndX()
Definition: eda_shape.h:124
This file contains miscellaneous commonly used macros and functions.
wxPoint m_bezierC2
Definition: eda_shape.h:305
bool IsFilled() const
Definition: eda_shape.h:81
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:101
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
const wxPoint & GetBezierC1() const
Definition: eda_shape.h:145
void rotate(const wxPoint &aRotCentre, double aAngle)
Definition: eda_shape.cpp:224
int GetPointCount() const
Definition: eda_shape.cpp:1181
std::vector< wxPoint > GetRectCorners() const
Definition: eda_shape.cpp:943
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
#define TEST(a, b)
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
bool m_endsSwapped
Definition: eda_shape.h:295
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:122
double GetLength() const
Return the length of the track using the hypotenuse calculation.
Definition: eda_shape.cpp:106
void Move(const VECTOR2I &aVector) override
bool IsClosed() const override
const std::vector< VECTOR2I > & CPoints() const
void move(const wxPoint &aMoveVector)
Definition: eda_shape.cpp:137
Represent a set of closed polygons.
SHAPE_T
Definition: eda_shape.h:40
SHAPE_LINE_CHAIN & Outline(int aIndex)
void SetEnd(int x, int y)
Definition: eda_rect.h:182
std::vector< wxPoint > m_bezierPoints
Definition: eda_shape.h:307
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:308
bool TestSegmentHit(const wxPoint &aRefPoint, const wxPoint &aStart, const wxPoint &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:129
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, const wxPoint &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
const wxPoint & GetBezierC2() const
Definition: eda_shape.h:148
int m_editState
Definition: eda_shape.h:310
#define TEST_E(a, b)
FILL_T m_fill
Definition: eda_shape.h:298
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition: property.h:612
const std::vector< wxPoint > buildBezierToSegmentsPointsList(int aMinSegLen) const
Definition: eda_shape.cpp:380
#define _(s)
void SetWidth(int aWidth)
Definition: eda_shape.h:88
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
void SetCenter(const wxPoint &aCenter)
Definition: eda_shape.cpp:419
double Angle() const
Compute the angle of the vector.
Definition: vector2d.h:307
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:71
void setPosition(const wxPoint &aPos)
Definition: eda_shape.cpp:89
int NewOutline()
Creates a new hole in a given outline.
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
int SegmentCount() const
Return the number of segments in this line chain.
virtual const VECTOR2I GetPoint(int aIndex) const override
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:1424
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:207
wxPoint m_start
Definition: eda_shape.h:299
PLOT_DASH_TYPE
Dashed line types.
Definition: plotter.h:104
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void Rotate(double aAngle, const VECTOR2I &aCenter=VECTOR2I(0, 0)) override
Rotate all vertices by a given angle.
EDA_UNITS
Definition: eda_units.h:38
void Normalize()
Ensures that the height ant width are positive.
Definition: eda_rect.cpp:35
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
void SetEndX(int x)
Definition: eda_shape.h:138
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:366
const EDA_RECT getBoundingBox() const
Definition: eda_shape.cpp:596
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX &aClosestVertex, int aClearance=0) const
Check whether aPoint collides with any edge of any of the contours of the polygon.
Bezier curves to polygon converter.
Definition: bezier_curves.h:36
SEG Segment(int aIndex)
Return a copy of the aIndex-th segment in the line chain.
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
Definition: eda_shape.cpp:1047
#define TEST_PT(a, b)
bool m_eeWinding
Definition: eda_shape.h:311
const SHAPE_LINE_CHAIN ConvertToPolyline(double aAccuracy=DefaultAccuracyForPCB(), double *aEffectiveAccuracy=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
Definition: shape_arc.cpp:455
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
A class to perform either relative or absolute display origin transforms for a single axis of a point...
virtual double getParentOrientation() const =0
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SwapShape(EDA_SHAPE *aImage)
Definition: eda_shape.cpp:1407
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
T NormalizeAnglePos(T Angle)
Normalize angle to be in the 0.0 .. 360.0 range: angle is in 1/10 degrees.
Definition: trigo.h:281
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void AddProperty(PROPERTY_BASE *aProperty)
Register a property.
wxPoint GetArcMid() const
Definition: eda_shape.cpp:437
VECTOR2I A
Definition: seg.h:48
Handle the component boundary box.
Definition: eda_rect.h:42
double DECIDEG2RAD(double deg)
Definition: trigo.h:233
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:91
void TransformOvalToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aStart, const wxPoint &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
double NormalizeAngleDegrees(double Angle, double aMin, double aMax)
Normalize angle to be aMin < angle <= aMax angle is in degrees.
Definition: trigo.h:327
wxPoint Centre() const
Definition: eda_rect.h:55
bool Intersects(const EDA_RECT &aRect) const
Test for a common area between rectangles.
Definition: eda_rect.cpp:150
wxPoint getPosition() const
Definition: eda_shape.cpp:95
void TransformArcToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aStart, const wxPoint &aMid, const wxPoint &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:62
SHAPE_T GetShape() const
Definition: eda_shape.h:92
void scale(double aScale)
Definition: eda_shape.cpp:172
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:183
int m_width
Definition: eda_shape.h:297
int GetRadius() const
Definition: eda_shape.cpp:466
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:454
void flip(const wxPoint &aCentre, bool aFlipLeftRight)
Definition: eda_shape.cpp:281
#define _HKI(x)
void TransformRingToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
bool continueEdit(const wxPoint &aPosition)
Definition: eda_shape.cpp:1223
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Inflate the rectangle horizontally by dx and vertically by dy.
Definition: eda_rect.cpp:364
void SetStartX(int x)
Definition: eda_shape.h:113
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
VECTOR2I B
Definition: seg.h:49