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 wxT( "S_SEGMENT" );
76  case SHAPE_T::RECT: return wxT( "S_RECT" );
77  case SHAPE_T::ARC: return wxT( "S_ARC" );
78  case SHAPE_T::CIRCLE: return wxT( "S_CIRCLE" );
79  case SHAPE_T::POLY: return wxT( "S_POLYGON" );
80  case SHAPE_T::BEZIER: return wxT( "S_CURVE" );
81  case SHAPE_T::LAST: return wxT( "!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  // If none of the input data have changed since we loaded the arc,
440  // keep the original mid point data to minimize churn
443  return m_arcMidData.mid;
444 
445  wxPoint mid = m_start;
446  RotatePoint( &mid, m_arcCenter, -GetArcAngle() / 2.0 );
447  return mid;
448 }
449 
450 
451 void EDA_SHAPE::CalcArcAngles( double& aStartAngle, double& aEndAngle ) const
452 {
453  VECTOR2D startRadial( GetStart() - getCenter() );
454  VECTOR2D endRadial( GetEnd() - getCenter() );
455 
456  aStartAngle = 180.0 / M_PI * atan2( startRadial.y, startRadial.x );
457  aEndAngle = 180.0 / M_PI * atan2( endRadial.y, endRadial.x );
458 
459  if( aEndAngle == aStartAngle )
460  aEndAngle = aStartAngle + 360.0; // ring, not null
461 
462  if( aStartAngle > aEndAngle )
463  {
464  if( aEndAngle < 0 )
465  aEndAngle = NormalizeAngleDegrees( aEndAngle, 0.0, 360.0 );
466  else
467  aStartAngle = NormalizeAngleDegrees( aStartAngle, -360.0, 0.0 );
468  }
469 }
470 
471 
473 {
474  double radius = 0.0;
475 
476  switch( m_shape )
477  {
478  case SHAPE_T::ARC:
479  radius = GetLineLength( m_arcCenter, m_start );
480  break;
481 
482  case SHAPE_T::CIRCLE:
483  radius = GetLineLength( m_start, m_end );
484  break;
485 
486  default:
488  }
489 
490  // don't allow degenerate circles/arcs
491  return std::max( 1, KiROUND( radius ) );
492 }
493 
494 
495 void EDA_SHAPE::SetCachedArcData( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd, const wxPoint& aCenter )
496 {
497  m_arcMidData.start = aStart;
498  m_arcMidData.end = aEnd;
499  m_arcMidData.center = aCenter;
500  m_arcMidData.mid = aMid;
501 }
502 
503 
504 void EDA_SHAPE::SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd )
505 {
506  m_arcMidData = {};
507  m_start = aStart;
508  m_end = aEnd;
509  m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
510  wxPoint new_mid = GetArcMid();
511 
512  m_endsSwapped = false;
513 
514  // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
515  // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
516  SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
517 
518  /*
519  * If the input winding doesn't match our internal winding, the calculated midpoint will end
520  * up on the other side of the arc. In this case, we need to flip the start/end points and
521  * flag this change for the system.
522  */
523 
524  VECTOR2D dist( new_mid - aMid );
525  VECTOR2D dist2( new_mid - m_arcCenter );
526 
527  if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
528  {
529  std::swap( m_start, m_end );
530  m_endsSwapped = true;
531  }
532 
533 }
534 
535 
537 {
538  double startAngle;
539  double endAngle;
540 
541  CalcArcAngles( startAngle, endAngle );
542 
543  return ( endAngle - startAngle ) * 10;
544 }
545 
546 
547 void EDA_SHAPE::SetArcAngleAndEnd( double aAngle, bool aCheckNegativeAngle )
548 {
549  m_end = m_start;
551 
552  if( aCheckNegativeAngle && aAngle < 0 )
553  {
554  std::swap( m_start, m_end );
555  m_endsSwapped = true;
556  }
557 }
558 
559 
560 void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
561 {
562  EDA_UNITS units = aFrame->GetUserUnits();
563  ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
564  wxString msg;
565 
566  wxString shape = _( "Shape" );
567 
568  switch( m_shape )
569  {
570  case SHAPE_T::CIRCLE:
571  aList.emplace_back( shape, _( "Circle" ) );
572 
573  msg = MessageTextFromValue( units, GetRadius() );
574  aList.emplace_back( _( "Radius" ), msg );
575  break;
576 
577  case SHAPE_T::ARC:
578  aList.emplace_back( shape, _( "Arc" ) );
579 
580  msg.Printf( wxT( "%.1f" ), GetArcAngle() / 10.0 );
581  aList.emplace_back( _( "Angle" ), msg );
582 
583  msg = MessageTextFromValue( units, GetRadius() );
584  aList.emplace_back( _( "Radius" ), msg );
585  break;
586 
587  case SHAPE_T::BEZIER:
588  aList.emplace_back( shape, _( "Curve" ) );
589 
590  msg = MessageTextFromValue( units, GetLength() );
591  aList.emplace_back( _( "Length" ), msg );
592  break;
593 
594  case SHAPE_T::POLY:
595  aList.emplace_back( shape, _( "Polygon" ) );
596 
597  msg.Printf( wxT( "%d" ), GetPolyShape().Outline(0).PointCount() );
598  aList.emplace_back( _( "Points" ), msg );
599  break;
600 
601  case SHAPE_T::RECT:
602  aList.emplace_back( shape, _( "Rectangle" ) );
603 
604  msg = MessageTextFromValue( units, std::abs( GetEnd().x - GetStart().x ) );
605  aList.emplace_back( _( "Width" ), msg );
606 
607  msg = MessageTextFromValue( units, std::abs( GetEnd().y - GetStart().y ) );
608  aList.emplace_back( _( "Height" ), msg );
609  break;
610 
611  case SHAPE_T::SEGMENT:
612  {
613  aList.emplace_back( shape, _( "Segment" ) );
614 
615  msg = MessageTextFromValue( units, GetLineLength( GetStart(), GetEnd() ) );
616  aList.emplace_back( _( "Length" ), msg );
617 
618  // angle counter-clockwise from 3'o-clock
619  const double deg = RAD2DEG( atan2( (double)( GetStart().y - GetEnd().y ),
620  (double)( GetEnd().x - GetStart().x ) ) );
621  aList.emplace_back( _( "Angle" ), wxString::Format( "%.1f", deg ) );
622  }
623  break;
624 
625  default:
626  aList.emplace_back( shape, _( "Unrecognized" ) );
627  break;
628  }
629 
630  aList.emplace_back( _( "Line width" ), MessageTextFromValue( units, m_width ) );
631 }
632 
633 
635 {
636  EDA_RECT bbox;
637 
638  switch( m_shape )
639  {
640  case SHAPE_T::RECT:
641  for( wxPoint& pt : GetRectCorners() )
642  bbox.Merge( pt );
643 
644  break;
645 
646  case SHAPE_T::SEGMENT:
647  bbox.SetOrigin( GetStart() );
648  bbox.SetEnd( GetEnd() );
649  break;
650 
651  case SHAPE_T::CIRCLE:
652  bbox.SetOrigin( GetStart() );
653  bbox.Inflate( GetRadius() );
654  break;
655 
656  case SHAPE_T::ARC:
657  computeArcBBox( bbox );
658  break;
659 
660  case SHAPE_T::POLY:
661  if( m_poly.IsEmpty() )
662  break;
663 
664  for( auto iter = m_poly.CIterate(); iter; iter++ )
665  {
666  wxPoint pt( iter->x, iter->y );
667 
669  pt += getParentPosition();
670 
671  bbox.Merge( pt );
672  }
673 
674  break;
675 
676  case SHAPE_T::BEZIER:
677  bbox.SetOrigin( GetStart() );
678  bbox.Merge( GetBezierC1() );
679  bbox.Merge( GetBezierC2() );
680  bbox.Merge( GetEnd() );
681  break;
682 
683  default:
685  break;
686  }
687 
688  bbox.Inflate( std::max( 0, m_width / 2 ) );
689  bbox.Normalize();
690 
691  return bbox;
692 }
693 
694 
695 bool EDA_SHAPE::hitTest( const wxPoint& aPosition, int aAccuracy ) const
696 {
697  int maxdist = aAccuracy;
698 
699  if( m_width > 0 )
700  maxdist += m_width / 2;
701 
702  switch( m_shape )
703  {
704  case SHAPE_T::CIRCLE:
705  {
706  int radius = GetRadius();
707  int dist = KiROUND( EuclideanNorm( aPosition - getCenter() ) );
708 
709  if( IsFilled() )
710  return dist <= radius + maxdist; // Filled circle hit-test
711  else
712  return abs( radius - dist ) <= maxdist; // Ring hit-test
713  }
714 
715  case SHAPE_T::ARC:
716  {
717  if( EuclideanNorm( aPosition - m_start ) <= maxdist )
718  return true;
719 
720  if( EuclideanNorm( aPosition - m_end ) <= maxdist )
721  return true;
722 
723  wxPoint relPos = aPosition - getCenter();
724  int radius = GetRadius();
725  int dist = KiROUND( EuclideanNorm( relPos ) );
726 
727  if( abs( radius - dist ) <= maxdist )
728  {
729  double startAngle;
730  double endAngle;
731  CalcArcAngles( startAngle, endAngle );
732 
733  if( m_eeWinding && NormalizeAngleDegrees( startAngle - endAngle, -180.0, 180.0 ) > 0 )
734  std::swap( startAngle, endAngle );
735 
736  double relPosAngle = 180.0 / M_PI * atan2( relPos.y, relPos.x );
737 
738  startAngle = NormalizeAngleDegrees( startAngle, 0.0, 360.0 );
739  endAngle = NormalizeAngleDegrees( endAngle, 0.0, 360.0 );
740  relPosAngle = NormalizeAngleDegrees( relPosAngle, 0.0, 360.0 );
741 
742  if( endAngle > startAngle )
743  return relPosAngle >= startAngle && relPosAngle <= endAngle;
744  else
745  return relPosAngle >= startAngle || relPosAngle <= endAngle;
746  }
747 
748  return false;
749  }
750 
751  case SHAPE_T::BEZIER:
752  const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( m_width );
753 
754  for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
755  {
756  if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
757  return true;
758  }
759 
760  return false;
761 
762  case SHAPE_T::SEGMENT:
763  return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
764 
765  case SHAPE_T::RECT:
766  if( IsFilled() ) // Filled rect hit-test
767  {
768  SHAPE_POLY_SET poly;
769  poly.NewOutline();
770 
771  for( const wxPoint& pt : GetRectCorners() )
772  poly.Append( pt );
773 
774  return poly.Collide( VECTOR2I( aPosition ), maxdist );
775  }
776  else // Open rect hit-test
777  {
778  std::vector<wxPoint> pts = GetRectCorners();
779 
780  return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
781  || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
782  || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
783  || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
784  }
785 
786  case SHAPE_T::POLY:
787  if( IsFilled() )
788  {
789  return m_poly.Collide( VECTOR2I( aPosition ), maxdist );
790  }
791  else
792  {
794  return m_poly.CollideEdge( VECTOR2I( aPosition ), dummy, maxdist );
795  }
796 
797  default:
799  return false;
800  }
801 }
802 
803 
804 bool EDA_SHAPE::hitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
805 {
806  EDA_RECT arect = aRect;
807  arect.Normalize();
808  arect.Inflate( aAccuracy );
809 
810  EDA_RECT arcRect;
811  EDA_RECT bb = getBoundingBox();
812 
813  switch( m_shape )
814  {
815  case SHAPE_T::CIRCLE:
816  // Test if area intersects or contains the circle:
817  if( aContained )
818  {
819  return arect.Contains( bb );
820  }
821  else
822  {
823  // If the rectangle does not intersect the bounding box, this is a much quicker test
824  if( !aRect.Intersects( bb ) )
825  {
826  return false;
827  }
828  else
829  {
830  return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
831  }
832  }
833 
834  case SHAPE_T::ARC:
835  // Test for full containment of this arc in the rect
836  if( aContained )
837  {
838  return arect.Contains( bb );
839  }
840  // Test if the rect crosses the arc
841  else
842  {
843  arcRect = bb.Common( arect );
844 
845  /* All following tests must pass:
846  * 1. Rectangle must intersect arc BoundingBox
847  * 2. Rectangle must cross the outside of the arc
848  */
849  return arcRect.Intersects( arect ) &&
851  }
852 
853  case SHAPE_T::RECT:
854  if( aContained )
855  {
856  return arect.Contains( bb );
857  }
858  else
859  {
860  std::vector<wxPoint> pts = GetRectCorners();
861 
862  // Account for the width of the lines
863  arect.Inflate( GetWidth() / 2 );
864  return ( arect.Intersects( pts[0], pts[1] )
865  || arect.Intersects( pts[1], pts[2] )
866  || arect.Intersects( pts[2], pts[3] )
867  || arect.Intersects( pts[3], pts[0] ) );
868  }
869 
870  case SHAPE_T::SEGMENT:
871  if( aContained )
872  {
873  return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
874  }
875  else
876  {
877  // Account for the width of the line
878  arect.Inflate( GetWidth() / 2 );
879  return arect.Intersects( GetStart(), GetEnd() );
880  }
881 
882  case SHAPE_T::POLY:
883  if( aContained )
884  {
885  return arect.Contains( bb );
886  }
887  else
888  {
889  // Fast test: if aRect is outside the polygon bounding box,
890  // rectangles cannot intersect
891  if( !arect.Intersects( bb ) )
892  return false;
893 
894  // Account for the width of the line
895  arect.Inflate( GetWidth() / 2 );
896 
897  // Polygons in footprints use coordinates relative to the footprint.
898  // Therefore, instead of using m_poly, we make a copy which is translated
899  // to the actual location in the board.
900  double orientation = 0.0;
901  wxPoint offset = getParentPosition();
902 
903  if( getParentOrientation() )
904  orientation = -DECIDEG2RAD( getParentOrientation() );
905 
906  SHAPE_LINE_CHAIN poly = m_poly.Outline( 0 );
907  poly.Rotate( orientation );
908  poly.Move( offset );
909 
910  int count = poly.GetPointCount();
911 
912  for( int ii = 0; ii < count; ii++ )
913  {
914  VECTOR2I vertex = poly.GetPoint( ii );
915 
916  // Test if the point is within aRect
917  if( arect.Contains( ( wxPoint ) vertex ) )
918  return true;
919 
920  if( ii + 1 < count )
921  {
922  VECTOR2I vertexNext = poly.GetPoint( ii + 1 );
923 
924  // Test if this edge intersects aRect
925  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
926  return true;
927  }
928  else if( poly.IsClosed() )
929  {
930  VECTOR2I vertexNext = poly.GetPoint( 0 );
931 
932  // Test if this edge intersects aRect
933  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
934  return true;
935  }
936  }
937 
938  return false;
939  }
940 
941  case SHAPE_T::BEZIER:
942  if( aContained )
943  {
944  return arect.Contains( bb );
945  }
946  else
947  {
948  // Fast test: if aRect is outside the polygon bounding box,
949  // rectangles cannot intersect
950  if( !arect.Intersects( bb ) )
951  return false;
952 
953  // Account for the width of the line
954  arect.Inflate( GetWidth() / 2 );
955  unsigned count = m_bezierPoints.size();
956 
957  for( unsigned ii = 1; ii < count; ii++ )
958  {
959  wxPoint vertex = m_bezierPoints[ ii - 1];
960  wxPoint vertexNext = m_bezierPoints[ii];
961 
962  // Test if the point is within aRect
963  if( arect.Contains( ( wxPoint ) vertex ) )
964  return true;
965 
966  // Test if this edge intersects aRect
967  if( arect.Intersects( vertex, vertexNext ) )
968  return true;
969  }
970 
971  return false;
972  }
973 
974  default:
976  return false;
977  }
978 }
979 
980 
981 std::vector<wxPoint> EDA_SHAPE::GetRectCorners() const
982 {
983  std::vector<wxPoint> pts;
984  wxPoint topLeft = GetStart();
985  wxPoint botRight = GetEnd();
986 
987  // Un-rotate rect topLeft and botRight
988  if( KiROUND( getParentOrientation() ) % 900 != 0 )
989  {
990  topLeft -= getParentPosition();
991  RotatePoint( &topLeft, -getParentOrientation() );
992 
993  botRight -= getParentPosition();
994  RotatePoint( &botRight, -getParentOrientation() );
995  }
996 
997  // Set up the un-rotated 4 corners
998  pts.emplace_back( topLeft );
999  pts.emplace_back( botRight.x, topLeft.y );
1000  pts.emplace_back( botRight );
1001  pts.emplace_back( topLeft.x, botRight.y );
1002 
1003  // Now re-rotate the 4 corners to get a diamond
1004  if( KiROUND( getParentOrientation() ) % 900 != 0 )
1005  {
1006  for( wxPoint& pt : pts )
1007  {
1009  pt += getParentPosition();
1010  }
1011  }
1012 
1013  return pts;
1014 }
1015 
1016 
1018 {
1019  wxPoint start = m_start;
1020  wxPoint end = m_end;
1021  double t1, t2;
1022 
1023  CalcArcAngles( t1, t2 );
1024 
1025  if( m_eeWinding && NormalizeAngleDegrees( t1 - t2, -180.0, 180.0 ) > 0 )
1026  std::swap( start, end );
1027 
1028  // Do not include the center, which is not necessarily inside the BB of an arc with a small
1029  // included angle
1030  aBBox.SetOrigin( start );
1031  aBBox.Merge( end );
1032 
1033  // Determine the starting quarter
1034  // 0 right-bottom
1035  // 1 left-bottom
1036  // 2 left-top
1037  // 3 right-top
1038  unsigned int quarter;
1039 
1040  if( start.x < m_arcCenter.x )
1041  {
1042  if( start.y <= m_arcCenter.y )
1043  quarter = 2;
1044  else
1045  quarter = 1;
1046  }
1047  else if( start.x == m_arcCenter.x )
1048  {
1049  if( start.y < m_arcCenter.y )
1050  quarter = 3;
1051  else
1052  quarter = 1;
1053  }
1054  else
1055  {
1056  if( start.y < m_arcCenter.y )
1057  quarter = 3;
1058  else
1059  quarter = 0;
1060  }
1061 
1062  int radius = GetRadius();
1063  VECTOR2I startRadial = start - m_arcCenter;
1064  VECTOR2I endRadial = end - m_arcCenter;
1065  double angleStart = ArcTangente( startRadial.y, startRadial.x );
1066  double arcAngle = RAD2DECIDEG( endRadial.Angle() - startRadial.Angle() );
1067  int angle = (int) NormalizeAnglePos( angleStart ) % 900 + NormalizeAnglePos( arcAngle );
1068 
1069  while( angle > 900 )
1070  {
1071  switch( quarter )
1072  {
1073  case 0: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y + radius ) ); break; // down
1074  case 1: aBBox.Merge( wxPoint( m_arcCenter.x - radius, m_arcCenter.y ) ); break; // left
1075  case 2: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y - radius ) ); break; // up
1076  case 3: aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y ) ); break; // right
1077  }
1078 
1079  ++quarter %= 4;
1080  angle -= 900;
1081  }
1082 }
1083 
1084 
1085 void EDA_SHAPE::SetPolyPoints( const std::vector<wxPoint>& aPoints )
1086 {
1088  m_poly.NewOutline();
1089 
1090  for ( const wxPoint& p : aPoints )
1091  m_poly.Append( p.x, p.y );
1092 }
1093 
1094 
1095 std::vector<SHAPE*> EDA_SHAPE::MakeEffectiveShapes() const
1096 {
1097  std::vector<SHAPE*> effectiveShapes;
1098 
1099  switch( m_shape )
1100  {
1101  case SHAPE_T::ARC:
1102  effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle() / 10.0,
1103  m_width ) );
1104  break;
1105 
1106  case SHAPE_T::SEGMENT:
1107  effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, m_width ) );
1108  break;
1109 
1110  case SHAPE_T::RECT:
1111  {
1112  std::vector<wxPoint> pts = GetRectCorners();
1113 
1114  if( IsFilled() )
1115  effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1116 
1117  if( m_width > 0 || !IsFilled() )
1118  {
1119  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], m_width ) );
1120  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], m_width ) );
1121  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], m_width ) );
1122  effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], m_width ) );
1123  }
1124  }
1125  break;
1126 
1127  case SHAPE_T::CIRCLE:
1128  {
1129  if( IsFilled() )
1130  effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1131 
1132  if( m_width > 0 || !IsFilled() )
1133  {
1134  // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC
1135  SHAPE_ARC circle( getCenter(), GetEnd(), 360.0 );
1136  SHAPE_LINE_CHAIN l = circle.ConvertToPolyline();
1137 
1138  for( int i = 0; i < l.SegmentCount(); i++ )
1139  {
1140  effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A, l.Segment( i ).B,
1141  m_width ) );
1142  }
1143  }
1144 
1145  break;
1146  }
1147 
1148  case SHAPE_T::BEZIER:
1149  {
1150  auto bezierPoints = buildBezierToSegmentsPointsList( GetWidth() );
1151  wxPoint start_pt = bezierPoints[0];
1152 
1153  for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1154  {
1155  wxPoint end_pt = bezierPoints[jj];
1156  effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, m_width ) );
1157  start_pt = end_pt;
1158  }
1159 
1160  break;
1161  }
1162 
1163  case SHAPE_T::POLY:
1164  {
1165  if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1166  break;
1167 
1169 
1171  l.Move( getParentPosition() );
1172 
1173  if( IsFilled() )
1174  effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1175 
1176  if( m_width > 0 || !IsFilled() )
1177  {
1178  for( int i = 0; i < l.SegmentCount(); i++ )
1179  effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ), m_width ) );
1180  }
1181  }
1182  break;
1183 
1184  default:
1186  break;
1187  }
1188 
1189  return effectiveShapes;
1190 }
1191 
1192 
1193 void EDA_SHAPE::DupPolyPointsList( std::vector<wxPoint>& aBuffer ) const
1194 {
1195  if( m_poly.OutlineCount() )
1196  {
1197  int pointCount = m_poly.COutline( 0 ).PointCount();
1198 
1199  if( pointCount )
1200  {
1201  aBuffer.reserve( pointCount );
1202 
1203  for ( auto iter = m_poly.CIterate(); iter; iter++ )
1204  aBuffer.emplace_back( iter->x, iter->y );
1205  }
1206  }
1207 }
1208 
1209 
1211 {
1212  // return true if the polygonal shape is valid (has more than 2 points)
1213  if( GetPolyShape().OutlineCount() == 0 )
1214  return false;
1215 
1216  const SHAPE_LINE_CHAIN& outline = ( (SHAPE_POLY_SET&)GetPolyShape() ).Outline( 0 );
1217 
1218  return outline.PointCount() > 2;
1219 }
1220 
1221 
1223 {
1224  // return the number of corners of the polygonal shape
1225  // this shape is expected to be only one polygon without hole
1226  if( GetPolyShape().OutlineCount() )
1227  return GetPolyShape().VertexCount( 0 );
1228 
1229  return 0;
1230 }
1231 
1232 
1233 void EDA_SHAPE::beginEdit( const wxPoint& aPosition )
1234 {
1235  switch( GetShape() )
1236  {
1237  case SHAPE_T::SEGMENT:
1238  case SHAPE_T::CIRCLE:
1239  case SHAPE_T::RECT:
1240  SetStart( aPosition );
1241  SetEnd( aPosition );
1242  break;
1243 
1244  case SHAPE_T::ARC:
1245  SetArcGeometry( aPosition, aPosition, aPosition );
1246  m_editState = 1;
1247  break;
1248 
1249  case SHAPE_T::POLY:
1250  m_poly.NewOutline();
1251  m_poly.Outline( 0 ).SetClosed( false );
1252 
1253  // Start and end of the first segment (co-located for now)
1254  m_poly.Outline( 0 ).Append( aPosition );
1255  m_poly.Outline( 0 ).Append( aPosition, true );
1256  break;
1257 
1258  default:
1260  }
1261 }
1262 
1263 
1264 bool EDA_SHAPE::continueEdit( const wxPoint& aPosition )
1265 {
1266  switch( GetShape() )
1267  {
1268  case SHAPE_T::ARC:
1269  case SHAPE_T::SEGMENT:
1270  case SHAPE_T::CIRCLE:
1271  case SHAPE_T::RECT:
1272  return false;
1273 
1274  case SHAPE_T::POLY:
1275  {
1276  SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1277 
1278  // do not add zero-length segments
1279  if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1280  poly.Append( aPosition, true );
1281  }
1282  return true;
1283 
1284  default:
1286  return false;
1287  }
1288 }
1289 
1290 
1291 void EDA_SHAPE::calcEdit( const wxPoint& aPosition )
1292 {
1293 #define sq( x ) pow( x, 2 )
1294 
1295  switch( GetShape() )
1296  {
1297  case SHAPE_T::SEGMENT:
1298  case SHAPE_T::CIRCLE:
1299  case SHAPE_T::RECT:
1300  SetEnd( aPosition );
1301  break;
1302 
1303  case SHAPE_T::ARC:
1304  {
1305  int radius = GetRadius();
1306 
1307  // Edit state 0: drawing: place start
1308  // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1309  // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1310  // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1311  // Edit state 4: point edit: move center
1312  // Edit state 5: point edit: move arc-mid-point
1313 
1314  switch( m_editState )
1315  {
1316  case 0:
1317  SetArcGeometry( aPosition, aPosition, aPosition );
1318  return;
1319 
1320  case 1:
1321  m_end = aPosition;
1322  radius = KiROUND( sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 ) );
1323  break;
1324 
1325  case 2:
1326  case 3:
1327  {
1328  wxPoint v = m_start - m_end;
1329  double chordBefore = sq( v.x ) + sq( v.y );
1330 
1331  if( m_editState == 2 )
1332  m_start = aPosition;
1333  else
1334  m_end = aPosition;
1335 
1336  v = m_start - m_end;
1337  double chordAfter = sq( v.x ) + sq( v.y );
1338  double ratio = chordAfter / chordBefore;
1339 
1340  if( ratio != 0 )
1341  {
1342  radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
1343  int( sqrt( chordAfter ) / 2 ) + 1 );
1344  }
1345  }
1346  break;
1347 
1348  case 4:
1349  {
1350  double chordA = GetLineLength( m_start, aPosition );
1351  double chordB = GetLineLength( m_end, aPosition );
1352  radius = int( ( chordA + chordB ) / 2.0 ) + 1;
1353  }
1354  break;
1355 
1356  case 5:
1357  SetArcGeometry( GetStart(), aPosition, GetEnd() );
1358  return;
1359  }
1360 
1361  // Calculate center based on start, end, and radius
1362  //
1363  // Let 'l' be the length of the chord and 'm' the middle point of the chord
1364  double l = GetLineLength( m_start, m_end );
1365  wxPoint m = ( m_start + m_end ) / 2;
1366 
1367  // Calculate 'd', the vector from the chord midpoint to the center
1368  wxPoint d;
1369  d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_start.y - m_end.y ) / l );
1370  d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_end.x - m_start.x ) / l );
1371 
1372  wxPoint c1 = m + d;
1373  wxPoint c2 = m - d;
1374 
1375  // Solution gives us 2 centers; we need to pick one:
1376  switch( m_editState )
1377  {
1378  case 1:
1379  {
1380  // Keep center clockwise from chord while drawing
1381  wxPoint chordVector = m_end - m_start;
1382  double chordAngle = ArcTangente( chordVector.y, chordVector.x );
1383  NORMALIZE_ANGLE_POS( chordAngle );
1384 
1385  wxPoint c1Test = c1;
1386  RotatePoint( &c1Test, m_start, -chordAngle );
1387 
1388  m_arcCenter = c1Test.x > 0 ? c2 : c1;
1389  }
1390  break;
1391 
1392  case 2:
1393  case 3:
1394  // Pick the one closer to the old center
1395  m_arcCenter = GetLineLength( c1, m_arcCenter ) < GetLineLength( c2, m_arcCenter ) ? c1 : c2;
1396  break;
1397 
1398  case 4:
1399  // Pick the one closer to the mouse position
1400  m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
1401  break;
1402  }
1403  }
1404  break;
1405 
1406  case SHAPE_T::POLY:
1407  m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1408  break;
1409 
1410  default:
1412  }
1413 }
1414 
1415 
1416 void EDA_SHAPE::endEdit( bool aClosed )
1417 {
1418  switch( GetShape() )
1419  {
1420  case SHAPE_T::ARC:
1421  case SHAPE_T::SEGMENT:
1422  case SHAPE_T::CIRCLE:
1423  case SHAPE_T::RECT:
1424  break;
1425 
1426  case SHAPE_T::POLY:
1427  {
1428  SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1429 
1430  // do not include last point twice
1431  if( poly.GetPointCount() > 2 )
1432  {
1433  if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1434  {
1435  poly.SetClosed( aClosed );
1436  poly.Remove( poly.GetPointCount() - 1 );
1437  }
1438  }
1439  }
1440  break;
1441 
1442  default:
1444  }
1445 }
1446 
1447 
1449 {
1450  EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1451  assert( image );
1452 
1453  std::swap( m_width, image->m_width );
1454  std::swap( m_start, image->m_start );
1455  std::swap( m_end, image->m_end );
1456  std::swap( m_arcCenter, image->m_arcCenter );
1457  std::swap( m_shape, image->m_shape );
1458  std::swap( m_bezierC1, image->m_bezierC1 );
1459  std::swap( m_bezierC2, image->m_bezierC2 );
1460  std::swap( m_bezierPoints, image->m_bezierPoints );
1461  std::swap( m_poly, image->m_poly );
1462 }
1463 
1464 
1465 int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1466 {
1467 #define EPSILON 2 // Should be enough for rounding errors on calculated items
1468 
1469 #define TEST( a, b ) { if( a != b ) return a - b; }
1470 #define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1471 #define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1472 
1473  TEST_PT( m_start, aOther->m_start );
1474  TEST_PT( m_end, aOther->m_end );
1475 
1476  TEST( (int) m_shape, (int) aOther->m_shape );
1477 
1478  if( m_shape == SHAPE_T::ARC )
1479  {
1480  TEST_PT( m_arcCenter, aOther->m_arcCenter );
1481  }
1482  else if( m_shape == SHAPE_T::BEZIER )
1483  {
1484  TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1485  TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1486  }
1487  else if( m_shape == SHAPE_T::POLY )
1488  {
1489  TEST( m_poly.TotalVertices(), aOther->m_poly.TotalVertices() );
1490 
1491  for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1492  TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1493  }
1494 
1495  TEST_E( m_width, aOther->m_width );
1496  TEST( (int) m_fill, (int) aOther->m_fill );
1497 
1498  return 0;
1499 }
1500 
1501 
1503  int aClearanceValue,
1504  int aError, ERROR_LOC aErrorLoc,
1505  bool ignoreLineWidth ) const
1506 {
1507  int width = ignoreLineWidth ? 0 : m_width;
1508 
1509  width += 2 * aClearanceValue;
1510 
1511  switch( m_shape )
1512  {
1513  case SHAPE_T::CIRCLE:
1514  if( IsFilled() )
1515  {
1516  TransformCircleToPolygon( aCornerBuffer, getCenter(), GetRadius() + width / 2, aError,
1517  aErrorLoc );
1518  }
1519  else
1520  {
1521  TransformRingToPolygon( aCornerBuffer, getCenter(), GetRadius(), width, aError,
1522  aErrorLoc );
1523  }
1524 
1525  break;
1526 
1527  case SHAPE_T::RECT:
1528  {
1529  std::vector<wxPoint> pts = GetRectCorners();
1530 
1531  if( IsFilled() )
1532  {
1533  aCornerBuffer.NewOutline();
1534 
1535  for( const wxPoint& pt : pts )
1536  aCornerBuffer.Append( pt );
1537  }
1538 
1539  if( width > 0 || !IsFilled() )
1540  {
1541  // Add in segments
1542  TransformOvalToPolygon( aCornerBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1543  TransformOvalToPolygon( aCornerBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1544  TransformOvalToPolygon( aCornerBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1545  TransformOvalToPolygon( aCornerBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1546  }
1547 
1548  break;
1549  }
1550 
1551  case SHAPE_T::ARC:
1552  TransformArcToPolygon( aCornerBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError,
1553  aErrorLoc );
1554  break;
1555 
1556  case SHAPE_T::SEGMENT:
1557  TransformOvalToPolygon( aCornerBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1558  break;
1559 
1560  case SHAPE_T::POLY:
1561  {
1562  if( !IsPolyShapeValid() )
1563  break;
1564 
1565  // The polygon is expected to be a simple polygon; not self intersecting, no hole.
1566  double orientation = getParentOrientation();
1567  wxPoint offset = getParentPosition();
1568 
1569  // Build the polygon with the actual position and orientation:
1570  std::vector<wxPoint> poly;
1571  DupPolyPointsList( poly );
1572 
1573  for( wxPoint& point : poly )
1574  {
1575  RotatePoint( &point, orientation );
1576  point += offset;
1577  }
1578 
1579  if( IsFilled() )
1580  {
1581  aCornerBuffer.NewOutline();
1582 
1583  for( const wxPoint& point : poly )
1584  aCornerBuffer.Append( point.x, point.y );
1585  }
1586 
1587  if( width > 0 || !IsFilled() )
1588  {
1589  wxPoint pt1( poly[ poly.size() - 1] );
1590 
1591  for( const wxPoint& pt2 : poly )
1592  {
1593  if( pt2 != pt1 )
1594  TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError, aErrorLoc );
1595 
1596  pt1 = pt2;
1597  }
1598  }
1599 
1600  break;
1601  }
1602 
1603  case SHAPE_T::BEZIER:
1604  {
1605  std::vector<wxPoint> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
1606  BEZIER_POLY converter( ctrlPts );
1607  std::vector< wxPoint> poly;
1608  converter.GetPoly( poly, m_width );
1609 
1610  for( unsigned ii = 1; ii < poly.size(); ii++ )
1611  {
1612  TransformOvalToPolygon( aCornerBuffer, poly[ii - 1], poly[ii], width, aError,
1613  aErrorLoc );
1614  }
1615 
1616  break;
1617  }
1618 
1619  default:
1621  break;
1622  }
1623 }
1624 
1625 
1626 static struct EDA_SHAPE_DESC
1627 {
1629  {
1631  .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
1632  .Map( SHAPE_T::RECT, _HKI( "Rectangle" ) )
1633  .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
1634  .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
1635  .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
1636  .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
1638  .Map( PLOT_DASH_TYPE::DEFAULT, _HKI( "Default" ) )
1639  .Map( PLOT_DASH_TYPE::SOLID, _HKI( "Solid" ) )
1640  .Map( PLOT_DASH_TYPE::DASH, _HKI( "Dashed" ) )
1641  .Map( PLOT_DASH_TYPE::DOT, _HKI( "Dotted" ) )
1642  .Map( PLOT_DASH_TYPE::DASHDOT, _HKI( "Dash-Dot" ) );
1643 
1646  propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
1648  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
1650  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
1652  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
1654  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
1656  // TODO: m_arcCenter, m_bezierC1, m_bezierC2, m_poly
1657  propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
1659  }
1660 } _EDA_SHAPE_DESC;
1661 
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:107
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:319
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:41
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:65
int GetWidth() const
Definition: eda_shape.h:98
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:328
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:135
void SetArcGeometry(const wxPoint &aStart, const wxPoint &aMid, const wxPoint &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:504
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:106
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:695
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:116
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:1502
bool IsEmpty() const
int GetEndY()
Definition: eda_shape.h:132
void calcEdit(const wxPoint &aPosition)
Definition: eda_shape.cpp:1291
double RAD2DEG(double rad)
Definition: trigo.h:230
void computeArcBBox(EDA_RECT &aBBox) const
Definition: eda_shape.cpp:1017
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:1095
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:1193
double GetArcAngle() const
Definition: eda_shape.cpp:536
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:451
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:130
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
extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:300
void SetArcAngleAndEnd(double aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:547
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:1233
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:141
void SetCachedArcData(const wxPoint &aStart, const wxPoint &aMid, const wxPoint &aEnd, const wxPoint &aCenter)
Set the data used for mid point caching.
Definition: eda_shape.cpp:495
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:560
The base class for create windows for drawing purpose.
wxString ShowShape() const
Definition: eda_shape.cpp:56
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1210
void endEdit(bool aClosed=true)
Finishes editing the shape.
Definition: eda_shape.cpp:1416
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
int GetStartX()
Definition: eda_shape.h:108
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:323
FILL_T
Definition: eda_shape.h:53
wxPoint m_arcCenter
Definition: eda_shape.h:325
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:133
This file contains miscellaneous commonly used macros and functions.
wxPoint m_bezierC2
Definition: eda_shape.h:329
bool IsFilled() const
Definition: eda_shape.h:90
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:110
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:154
void rotate(const wxPoint &aRotCentre, double aAngle)
Definition: eda_shape.cpp:224
int GetPointCount() const
Definition: eda_shape.cpp:1222
std::vector< wxPoint > GetRectCorners() const
Definition: eda_shape.cpp:981
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:318
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:131
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:191
std::vector< wxPoint > m_bezierPoints
Definition: eda_shape.h:331
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:332
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:157
int m_editState
Definition: eda_shape.h:334
#define TEST_E(a, b)
FILL_T m_fill
Definition: eda_shape.h:321
#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:97
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:1465
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:227
wxPoint m_start
Definition: eda_shape.h:322
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,...
wxPoint mid
Definition: eda_shape.h:65
void SetEndX(int x)
Definition: eda_shape.h:147
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:634
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:1085
#define TEST_PT(a, b)
bool m_eeWinding
Definition: eda_shape.h:335
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:458
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:1448
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:100
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.
wxPoint start
Definition: eda_shape.h:66
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:64
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
wxPoint center
Definition: eda_shape.h:68
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
wxPoint end
Definition: eda_shape.h:67
ARC_MID m_arcMidData
Definition: eda_shape.h:326
SHAPE_T GetShape() const
Definition: eda_shape.h:101
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:320
int GetRadius() const
Definition: eda_shape.cpp:472
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:525
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:1264
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:122
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