KiCad PCB EDA Suite
class_drawsegment.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
7  * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 #include <fctsys.h>
28 #include <macros.h>
29 #include <gr_basic.h>
30 #include <bezier_curves.h>
31 #include <pcb_screen.h>
32 #include <trigo.h>
33 #include <msgpanel.h>
34 #include <bitmaps.h>
35 #include <pcb_edit_frame.h>
36 #include <pcbnew.h>
37 #include <class_board.h>
38 #include <class_module.h>
39 #include <class_drawsegment.h>
40 #include <base_units.h>
41 #include <math/util.h> // for KiROUND
42 #include <pgm_base.h>
45 
46 
48  BOARD_ITEM( aParent, idtype )
49 {
50  m_Type = 0;
51  m_Angle = 0;
52  m_Flags = 0;
54  m_Width = Millimeter2iu( DEFAULT_LINE_WIDTH );
55 }
56 
57 
59 {
60 }
61 
62 
63 void DRAWSEGMENT::SetPosition( const wxPoint& aPos )
64 {
65  m_Start = aPos;
66 }
67 
68 
70 {
71  if( m_Shape == S_POLYGON )
72  return (wxPoint) m_Poly.CVertex( 0 );
73  else
74  return m_Start;
75 }
76 
77 
78 double DRAWSEGMENT::GetLength() const
79 {
80  double length = 0.0;
81 
82  switch( m_Shape )
83  {
84  case S_CURVE:
85  for( size_t ii = 1; ii < m_BezierPoints.size(); ++ii )
86  length += GetLineLength( m_BezierPoints[ii - 1], m_BezierPoints[ii] );
87 
88  break;
89 
90  default:
91  length = GetLineLength( GetStart(), GetEnd() );
92  break;
93  }
94 
95  return length;
96 }
97 
98 
99 void DRAWSEGMENT::Move( const wxPoint& aMoveVector )
100 {
101  // Move vector should not affect start/end for polygon since it will
102  // be applied directly to polygon outline.
103  if( m_Shape != S_POLYGON )
104  {
105  m_Start += aMoveVector;
106  m_End += aMoveVector;
107  }
108 
109  switch ( m_Shape )
110  {
111  case S_POLYGON:
112  m_Poly.Move( VECTOR2I( aMoveVector ) );
113  break;
114 
115  case S_CURVE:
116  m_BezierC1 += aMoveVector;
117  m_BezierC2 += aMoveVector;
118 
119  for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ )
120  {
121  m_BezierPoints[ii] += aMoveVector;
122  }
123 
124  break;
125 
126  default:
127  break;
128  }
129 }
130 
131 
132 void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle )
133 {
134  switch( m_Shape )
135  {
136  case S_ARC:
137  case S_SEGMENT:
138  case S_CIRCLE:
139  // these can all be done by just rotating the start and end points
140  RotatePoint( &m_Start, aRotCentre, aAngle);
141  RotatePoint( &m_End, aRotCentre, aAngle);
142  break;
143 
144  case S_POLYGON:
145  m_Poly.Rotate( -DECIDEG2RAD( aAngle ), VECTOR2I( aRotCentre ) );
146  break;
147 
148  case S_CURVE:
149  RotatePoint( &m_Start, aRotCentre, aAngle);
150  RotatePoint( &m_End, aRotCentre, aAngle);
151  RotatePoint( &m_BezierC1, aRotCentre, aAngle);
152  RotatePoint( &m_BezierC2, aRotCentre, aAngle);
153 
154  for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ )
155  {
156  RotatePoint( &m_BezierPoints[ii], aRotCentre, aAngle);
157  }
158  break;
159 
160  case S_RECT:
161  default:
162  // un-handled edge transform
163  wxASSERT_MSG( false, wxT( "DRAWSEGMENT::Rotate not implemented for "
164  + ShowShape( m_Shape ) ) );
165  break;
166  }
167 }
168 
169 
170 void DRAWSEGMENT::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
171 {
172  if( aFlipLeftRight )
173  {
174  m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
175  m_End.x = aCentre.x - ( m_End.x - aCentre.x );
176  }
177  else
178  {
179  m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
180  m_End.y = aCentre.y - ( m_End.y - aCentre.y );
181  }
182 
183  switch ( m_Shape )
184  {
185  case S_ARC:
186  m_Angle = -m_Angle;
187  break;
188 
189  case S_POLYGON:
190  m_Poly.Mirror( aFlipLeftRight, !aFlipLeftRight, VECTOR2I( aCentre ) );
191  break;
192 
193  case S_CURVE:
194  {
195  if( aFlipLeftRight )
196  {
197  m_BezierC1.x = aCentre.x - ( m_BezierC1.x - aCentre.x );
198  m_BezierC2.x = aCentre.x - ( m_BezierC2.x - aCentre.x );
199  }
200  else
201  {
202  m_BezierC1.y = aCentre.y - ( m_BezierC1.y - aCentre.y );
203  m_BezierC2.y = aCentre.y - ( m_BezierC2.y - aCentre.y );
204  }
205 
206  // Rebuild the poly points shape
207  std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
208  BEZIER_POLY converter( ctrlPoints );
209  converter.GetPoly( m_BezierPoints, m_Width );
210  }
211  break;
212 
213  default:
214  break;
215  }
216 
217  // DRAWSEGMENT items are not allowed on copper layers, so
218  // copper layers count is not taken in account in Flip transform
219  SetLayer( FlipLayer( GetLayer() ) );
220 }
221 
222 
224 {
225  // Has meaning only for S_CURVE DRAW_SEGMENT shape
226  if( m_Shape != S_CURVE )
227  {
228  m_BezierPoints.clear();
229  return;
230  }
231  // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
232  std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
233  BEZIER_POLY converter( ctrlPoints );
234  converter.GetPoly( m_BezierPoints, aMinSegLen );
235 }
236 
237 
239 {
240  wxPoint c;
241 
242  switch( m_Shape )
243  {
244  case S_ARC:
245  case S_CIRCLE:
246  c = m_Start;
247  break;
248 
249  case S_SEGMENT:
250  // Midpoint of the line
251  c = ( GetStart() + GetEnd() ) / 2;
252  break;
253 
254  case S_POLYGON:
255  case S_RECT:
256  case S_CURVE:
257  c = GetBoundingBox().Centre();
258  break;
259 
260  default:
261  wxASSERT_MSG( false, "DRAWSEGMENT::GetCentre not implemented for shape"
262  + ShowShape( GetShape() ) );
263  break;
264  }
265 
266  return c;
267 }
268 
269 
271 {
272  wxPoint endPoint( m_End ); // start of arc
273 
274  switch( m_Shape )
275  {
276  case S_ARC:
277  // rotate the starting point of the arc, given by m_End, through the
278  // angle m_Angle to get the ending point of the arc.
279  // m_Start is the arc centre
280  endPoint = m_End; // m_End = start point of arc
281  RotatePoint( &endPoint, m_Start, -m_Angle );
282  break;
283 
284  default:
285  break;
286  }
287 
288  return endPoint; // after rotation, the end of the arc.
289 }
290 
291 
293 {
294  wxPoint endPoint( m_End );
295 
296  switch( m_Shape )
297  {
298  case S_ARC:
299  // rotate the starting point of the arc, given by m_End, through half
300  // the angle m_Angle to get the middle of the arc.
301  // m_Start is the arc centre
302  endPoint = m_End; // m_End = start point of arc
303  RotatePoint( &endPoint, m_Start, -m_Angle / 2.0 );
304  break;
305 
306  default:
307  break;
308  }
309 
310  return endPoint; // after rotation, the end of the arc.
311 }
312 
313 
315 {
316  // due to the Y axis orient atan2 needs - y value
317  double angleStart = ArcTangente( GetArcStart().y - GetCenter().y,
318  GetArcStart().x - GetCenter().x );
319 
320  // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg
321  // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg.
322  // and this is not easy to handle in calculations
323  NORMALIZE_ANGLE_POS( angleStart );
324 
325  return angleStart;
326 }
327 
328 
329 void DRAWSEGMENT::SetAngle( double aAngle )
330 {
331  // m_Angle must be >= -360 and <= +360 degrees
332  m_Angle = NormalizeAngle360Max( aAngle );
333 }
334 
335 
337 {
338  if( !m_Parent || m_Parent->Type() != PCB_MODULE_T )
339  return NULL;
340 
341  return (MODULE*) m_Parent;
342 }
343 
344 
345 void DRAWSEGMENT::Print( PCB_BASE_FRAME* aFrame, wxDC* DC, const wxPoint& aOffset )
346 {
347  int ux0, uy0, dx, dy;
348  int l_trace;
349  int radius;
350 
351  BOARD* brd = GetBoard( );
352 
353  if( brd->IsLayerVisible( GetLayer() ) == false )
354  return;
355 
356  COLOR4D color = Pgm().GetSettingsManager().GetColorSettings()->GetColor( GetLayer() );
357  auto displ_opts = aFrame->GetDisplayOptions();
358 
359  l_trace = m_Width >> 1; // half trace width
360 
361  // Line start point or Circle and Arc center
362  ux0 = m_Start.x + aOffset.x;
363  uy0 = m_Start.y + aOffset.y;
364 
365  // Line end point or circle and arc start point
366  dx = m_End.x + aOffset.x;
367  dy = m_End.y + aOffset.y;
368 
369  bool filled = displ_opts.m_DisplayDrawItemsFill;
370 
371  if( m_Flags & FORCE_SKETCH )
372  filled = SKETCH;
373 
374  switch( m_Shape )
375  {
376  case S_CIRCLE:
377  radius = KiROUND( Distance( ux0, uy0, dx, dy ) );
378 
379  if( filled )
380  {
381  GRCircle( nullptr, DC, ux0, uy0, radius, m_Width, color );
382  }
383  else
384  {
385  GRCircle( nullptr, DC, ux0, uy0, radius - l_trace, color );
386  GRCircle( nullptr, DC, ux0, uy0, radius + l_trace, color );
387  }
388 
389  break;
390 
391  case S_ARC:
392  double StAngle, EndAngle;
393  radius = KiROUND( Distance( ux0, uy0, dx, dy ) );
394  StAngle = ArcTangente( dy - uy0, dx - ux0 );
395  EndAngle = StAngle + m_Angle;
396 
397  if( StAngle > EndAngle )
398  std::swap( StAngle, EndAngle );
399 
400  if( filled )
401  {
402  GRArc( nullptr, DC, ux0, uy0, StAngle, EndAngle, radius, m_Width, color );
403  }
404  else
405  {
406  GRArc( nullptr, DC, ux0, uy0, StAngle, EndAngle, radius - l_trace, color );
407  GRArc( nullptr, DC, ux0, uy0, StAngle, EndAngle, radius + l_trace, color );
408  }
409 
410  break;
411 
412  case S_CURVE:
413  {
415 
416  wxPoint& startp = m_BezierPoints[0];
417 
418  for( unsigned int i = 1; i < m_BezierPoints.size(); i++ )
419  {
420  wxPoint& endp = m_BezierPoints[i];
421 
422  if( filled )
423  GRFilledSegment( nullptr, DC, startp+aOffset, endp+aOffset, m_Width, color );
424  else
425  GRCSegm( nullptr, DC, startp+aOffset, endp+aOffset, m_Width, color );
426 
427  startp = m_BezierPoints[i];
428  }
429  }
430  break;
431 
432  case S_POLYGON:
433  {
434  SHAPE_POLY_SET& outline = GetPolyShape();
435  // Draw the polygon: only one polygon is expected
436  // However we provide a multi polygon shape drawing
437  // ( for the future or to show a non expected shape )
438  for( int jj = 0; jj < outline.OutlineCount(); ++jj )
439  {
440  SHAPE_LINE_CHAIN& poly = outline.Outline( jj );
441  GRClosedPoly( nullptr, DC, poly.PointCount(), (const wxPoint*) &poly.CPoint( 0 ),
443  }
444  }
445  break;
446 
447  default:
448  if( filled )
449  GRFillCSegm( nullptr, DC, ux0, uy0, dx, dy, m_Width, color );
450  else
451  GRCSegm( nullptr, DC, ux0, uy0, dx, dy, m_Width, color );
452 
453  break;
454  }
455 }
456 
457 
458 void DRAWSEGMENT::GetMsgPanelInfo( EDA_UNITS aUnits, std::vector<MSG_PANEL_ITEM>& aList )
459 {
460  wxString msg;
461 
462  msg = _( "Drawing" );
463 
464  aList.emplace_back( _( "Type" ), msg, DARKCYAN );
465 
466  wxString shape = _( "Shape" );
467 
468  switch( m_Shape )
469  {
470  case S_CIRCLE:
471  aList.emplace_back( shape, _( "Circle" ), RED );
472 
473  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
474  aList.emplace_back( _( "Radius" ), msg, RED );
475  break;
476 
477  case S_ARC:
478  aList.emplace_back( shape, _( "Arc" ), RED );
479  msg.Printf( wxT( "%.1f" ), m_Angle / 10.0 );
480  aList.emplace_back( _( "Angle" ), msg, RED );
481 
482  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
483  aList.emplace_back( _( "Radius" ), msg, RED );
484  break;
485 
486  case S_CURVE:
487  aList.emplace_back( shape, _( "Curve" ), RED );
488 
489  msg = MessageTextFromValue( aUnits, GetLength() );
490  aList.emplace_back( _( "Length" ), msg, DARKGREEN );
491  break;
492 
493  case S_POLYGON:
494  aList.emplace_back( shape, _( "Polygon" ), RED );
495 
496  msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() );
497  aList.emplace_back( _( "Points" ), msg, DARKGREEN );
498  break;
499 
500  default:
501  {
502  aList.emplace_back( shape, _( "Segment" ), RED );
503 
504  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
505  aList.emplace_back( _( "Length" ), msg, DARKGREEN );
506 
507  // angle counter-clockwise from 3'o-clock
508  const double deg = RAD2DEG( atan2( (double)( m_Start.y - m_End.y ),
509  (double)( m_End.x - m_Start.x ) ) );
510  msg.Printf( wxT( "%.1f" ), deg );
511  aList.emplace_back( _( "Angle" ), msg, DARKGREEN );
512  }
513  }
514 
515  if( m_Shape == S_POLYGON )
516  {
517  VECTOR2I point0 = GetPolyShape().Outline(0).CPoint(0);
518  wxString origin = wxString::Format( "@(%s, %s)",
519  MessageTextFromValue( aUnits, point0.x ),
520  MessageTextFromValue( aUnits, point0.y ) );
521 
522  aList.emplace_back( _( "Origin" ), origin, DARKGREEN );
523  }
524  else
525  {
526  wxString start = wxString::Format( "@(%s, %s)",
527  MessageTextFromValue( aUnits, GetStart().x ),
528  MessageTextFromValue( aUnits, GetStart().y ) );
529  wxString end = wxString::Format( "@(%s, %s)",
530  MessageTextFromValue( aUnits, GetEnd().x ),
531  MessageTextFromValue( aUnits, GetEnd().y ) );
532 
533  aList.emplace_back( start, end, DARKGREEN );
534  }
535 
536  aList.emplace_back( _( "Layer" ), GetLayerName(), DARKBROWN );
537 
538  msg = MessageTextFromValue( aUnits, m_Width, true );
539  aList.emplace_back( _( "Width" ), msg, DARKCYAN );
540 }
541 
542 
544 {
545  EDA_RECT bbox;
546 
547  bbox.SetOrigin( m_Start );
548 
549  switch( m_Shape )
550  {
551  case S_SEGMENT:
552  bbox.SetEnd( m_End );
553  break;
554 
555  case S_CIRCLE:
556  bbox.Inflate( GetRadius() );
557  break;
558 
559  case S_ARC:
560  computeArcBBox( bbox );
561  break;
562 
563  case S_POLYGON:
564  if( m_Poly.IsEmpty() )
565  break;
566  {
567  wxPoint p_end;
568  MODULE* module = GetParentModule();
569  bool first = true;
570 
571  for( auto iter = m_Poly.CIterate(); iter; iter++ )
572  {
573  wxPoint pt ( iter->x, iter->y );
574 
575  if( module ) // Transform, if we belong to a module
576  {
577  RotatePoint( &pt, module->GetOrientation() );
578  pt += module->GetPosition();
579  }
580 
581 
582  if( first )
583  {
584  p_end = pt;
585  bbox.SetX( pt.x );
586  bbox.SetY( pt.y );
587  first = false;
588  }
589  else
590  {
591 
592  bbox.SetX( std::min( bbox.GetX(), pt.x ) );
593  bbox.SetY( std::min( bbox.GetY(), pt.y ) );
594 
595  p_end.x = std::max( p_end.x, pt.x );
596  p_end.y = std::max( p_end.y, pt.y );
597  }
598  }
599 
600  bbox.SetEnd( p_end );
601  break;
602  }
603 
604  case S_CURVE:
605 
606  bbox.Merge( m_BezierC1 );
607  bbox.Merge( m_BezierC2 );
608  bbox.Merge( m_End );
609  break;
610 
611  default:
612  break;
613  }
614 
615  bbox.Inflate( ((m_Width+1) / 2) + 1 );
616  bbox.Normalize();
617 
618  return bbox;
619 }
620 
621 
622 bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
623 {
624  int maxdist = aAccuracy + ( m_Width / 2 );
625 
626  switch( m_Shape )
627  {
628  case S_CIRCLE:
629  case S_ARC:
630  {
631  wxPoint relPos = aPosition - GetCenter();
632  int radius = GetRadius();
633  int dist = KiROUND( EuclideanNorm( relPos ) );
634 
635  if( abs( radius - dist ) <= maxdist )
636  {
637  if( m_Shape == S_CIRCLE )
638  return true;
639 
640  // For arcs, the test point angle must be >= arc angle start
641  // and <= arc angle end
642  // However angle values > 360 deg are not easy to handle
643  // so we calculate the relative angle between arc start point and teast point
644  // this relative arc should be < arc angle if arc angle > 0 (CW arc)
645  // and > arc angle if arc angle < 0 (CCW arc)
646  double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
647 
648  double arc_hittest = ArcTangente( relPos.y, relPos.x );
649 
650  // Calculate relative angle between the starting point of the arc, and the test point
651  arc_hittest -= arc_angle_start;
652 
653  // Normalise arc_hittest between 0 ... 360 deg
654  NORMALIZE_ANGLE_POS( arc_hittest );
655 
656  // Check angle: inside the arc angle when it is > 0
657  // and outside the not drawn arc when it is < 0
658  if( GetAngle() >= 0.0 )
659  {
660  if( arc_hittest <= GetAngle() )
661  return true;
662  }
663  else
664  {
665  if( arc_hittest >= (3600.0 + GetAngle()) )
666  return true;
667  }
668  }
669  }
670  break;
671 
672  case S_CURVE:
673  ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width );
674 
675  for( unsigned int i= 1; i < m_BezierPoints.size(); i++)
676  {
677  if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i], maxdist ) )
678  return true;
679  }
680  break;
681 
682  case S_SEGMENT:
683  if( TestSegmentHit( aPosition, m_Start, m_End, maxdist ) )
684  return true;
685  break;
686 
687  case S_POLYGON:
688  {
689  if( !IsPolygonFilled() )
690  {
692  auto poly = m_Poly; //todo: Fix CollideEdge to be const
693  return poly.CollideEdge( VECTOR2I( aPosition ), i,
694  std::max( maxdist, Millimeter2iu( 0.25 ) ) );
695  }
696  else
697  return m_Poly.Collide( VECTOR2I( aPosition ), maxdist );
698  }
699  break;
700 
701  default:
702  wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
703  break;
704  }
705 
706  return false;
707 }
708 
709 
710 bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
711 {
712  EDA_RECT arect = aRect;
713  arect.Normalize();
714  arect.Inflate( aAccuracy );
715 
716  EDA_RECT arcRect;
717  EDA_RECT bb = GetBoundingBox();
718 
719  switch( m_Shape )
720  {
721  case S_CIRCLE:
722  // Test if area intersects or contains the circle:
723  if( aContained )
724  return arect.Contains( bb );
725  else
726  {
727  // If the rectangle does not intersect the bounding box, this is a much quicker test
728  if( !aRect.Intersects( bb ) )
729  {
730  return false;
731  }
732  else
733  {
734  return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
735  }
736  }
737  break;
738 
739  case S_ARC:
740  // Test for full containment of this arc in the rect
741  if( aContained )
742  {
743  return arect.Contains( bb );
744  }
745  // Test if the rect crosses the arc
746  else
747  {
748  arcRect = bb.Common( arect );
749 
750  /* All following tests must pass:
751  * 1. Rectangle must intersect arc BoundingBox
752  * 2. Rectangle must cross the outside of the arc
753  */
754  return arcRect.Intersects( arect ) &&
756  }
757  break;
758 
759  case S_SEGMENT:
760  if( aContained )
761  {
762  return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
763  }
764  else
765  {
766  // Account for the width of the line
767  arect.Inflate( GetWidth() / 2 );
768  return arect.Intersects( GetStart(), GetEnd() );
769  }
770 
771  break;
772 
773  case S_POLYGON:
774  if( aContained )
775  {
776  return arect.Contains( bb );
777  }
778  else
779  {
780  // Fast test: if aRect is outside the polygon bounding box,
781  // rectangles cannot intersect
782  if( !arect.Intersects( bb ) )
783  return false;
784 
785  // Account for the width of the line
786  arect.Inflate( GetWidth() / 2 );
787  int count = m_Poly.TotalVertices();
788 
789  for( int ii = 0; ii < count; ii++ )
790  {
791  auto vertex = m_Poly.CVertex( ii );
792  auto vertexNext = m_Poly.CVertex( ( ii + 1 ) % count );
793 
794  // Test if the point is within aRect
795  if( arect.Contains( ( wxPoint ) vertex ) )
796  return true;
797 
798  // Test if this edge intersects aRect
799  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
800  return true;
801  }
802  }
803  break;
804 
805  case S_CURVE: // not yet handled
806  if( aContained )
807  {
808  return arect.Contains( bb );
809  }
810  else
811  {
812  // Fast test: if aRect is outside the polygon bounding box,
813  // rectangles cannot intersect
814  if( !arect.Intersects( bb ) )
815  return false;
816 
817  // Account for the width of the line
818  arect.Inflate( GetWidth() / 2 );
819  unsigned count = m_BezierPoints.size();
820 
821  for( unsigned ii = 1; ii < count; ii++ )
822  {
823  wxPoint vertex = m_BezierPoints[ii-1];
824  wxPoint vertexNext = m_BezierPoints[ii];
825 
826  // Test if the point is within aRect
827  if( arect.Contains( ( wxPoint ) vertex ) )
828  return true;
829 
830  // Test if this edge intersects aRect
831  if( arect.Intersects( vertex, vertexNext ) )
832  return true;
833  }
834  }
835  break;
836 
837 
838  default:
839  wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
840  break;
841  }
842 
843  return false;
844 }
845 
846 
848 {
849  return wxString::Format( _( "Pcb Graphic %s, length %s on %s" ),
850  ShowShape( m_Shape ),
851  MessageTextFromValue( aUnits, GetLength() ),
852  GetLayerName() );
853 }
854 
855 
857 {
858  return add_dashed_line_xpm;
859 }
860 
861 
863 {
864  return new DRAWSEGMENT( *this );
865 }
866 
867 
869 {
870  // For arcs - do not include the center point in the bounding box,
871  // it is redundant for displaying an arc
872  if( m_Shape == S_ARC )
873  {
874  EDA_RECT bbox;
875  bbox.SetOrigin( m_End );
876  computeArcBBox( bbox );
877  return BOX2I( bbox.GetOrigin(), bbox.GetSize() );
878  }
879 
880  return EDA_ITEM::ViewBBox();
881 }
882 
883 
885 {
886  // Do not include the center, which is not necessarily
887  // inside the BB of a arc with a small angle
888  aBBox.SetOrigin( m_End );
889 
890  wxPoint end = m_End;
891  RotatePoint( &end, m_Start, -m_Angle );
892  aBBox.Merge( end );
893 
894  // Determine the starting quarter
895  // 0 right-bottom
896  // 1 left-bottom
897  // 2 left-top
898  // 3 right-top
899  unsigned int quarter = 0; // assume right-bottom
900 
901  if( m_End.x < m_Start.x )
902  {
903  if( m_End.y <= m_Start.y )
904  quarter = 2;
905  else // ( m_End.y > m_Start.y )
906  quarter = 1;
907  }
908  else if( m_End.x >= m_Start.x )
909  {
910  if( m_End.y < m_Start.y )
911  quarter = 3;
912  else if( m_End.x == m_Start.x )
913  quarter = 1;
914  }
915 
916  int radius = GetRadius();
917  int angle = (int) GetArcAngleStart() % 900 + m_Angle;
918  bool directionCW = ( m_Angle > 0 ); // Is the direction of arc clockwise?
919 
920  // Make the angle positive, so we go clockwise and merge points belonging to the arc
921  if( !directionCW )
922  {
923  angle = 900 - angle;
924  quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic
925  }
926 
927  while( angle > 900 )
928  {
929  switch( quarter )
930  {
931  case 0:
932  aBBox.Merge( wxPoint( m_Start.x, m_Start.y + radius ) ); // down
933  break;
934 
935  case 1:
936  aBBox.Merge( wxPoint( m_Start.x - radius, m_Start.y ) ); // left
937  break;
938 
939  case 2:
940  aBBox.Merge( wxPoint( m_Start.x, m_Start.y - radius ) ); // up
941  break;
942 
943  case 3:
944  aBBox.Merge( wxPoint( m_Start.x + radius, m_Start.y ) ); // right
945  break;
946  }
947 
948  if( directionCW )
949  ++quarter;
950  else
951  quarter += 3; // -1 modulo arithmetic
952 
953  quarter %= 4;
954  angle -= 900;
955  }
956 }
957 
958 
959 void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
960 {
962  m_Poly.NewOutline();
963 
964  for ( auto p : aPoints )
965  {
966  m_Poly.Append( p.x, p.y );
967  }
968 }
969 
970 
971 const std::vector<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const
972 {
973  std::vector<wxPoint> rv;
974 
975  if( m_Poly.OutlineCount() )
976  {
977  if( m_Poly.COutline( 0 ).PointCount() )
978  {
979  for ( auto iter = m_Poly.CIterate(); iter; iter++ )
980  {
981  rv.emplace_back( iter->x, iter->y );
982  }
983  }
984  }
985 
986  return rv;
987 }
988 
989 
991 {
992  // return true if the polygonal shape is valid (has more than 2 points)
993  if( GetPolyShape().OutlineCount() == 0 )
994  return false;
995 
996  const SHAPE_LINE_CHAIN& outline = ((SHAPE_POLY_SET&)GetPolyShape()).Outline( 0 );
997 
998  return outline.PointCount() > 2;
999 }
1000 
1001 
1003 {
1004  // return the number of corners of the polygonal shape
1005  // this shape is expected to be only one polygon without hole
1006  if( GetPolyShape().OutlineCount() )
1007  return GetPolyShape().VertexCount( 0 );
1008 
1009  return 0;
1010 }
1011 
1012 
1014 {
1015  DRAWSEGMENT* image = dynamic_cast<DRAWSEGMENT*>( aImage );
1016  assert( image );
1017 
1018  std::swap( m_Width, image->m_Width );
1019  std::swap( m_Start, image->m_Start );
1020  std::swap( m_End, image->m_End );
1021  std::swap( m_Shape, image->m_Shape );
1022  std::swap( m_Type, image->m_Type );
1023  std::swap( m_Angle, image->m_Angle );
1024  std::swap( m_BezierC1, image->m_BezierC1 );
1025  std::swap( m_BezierC2, image->m_BezierC2 );
1026  std::swap( m_BezierPoints, image->m_BezierPoints );
1027  std::swap( m_Poly, image->m_Poly );
1028 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:128
const wxPoint GetArcMid() const
int TotalVertices() const
Returns total number of vertices stored in the set.
EDA_UNITS
Definition: common.h:184
void Rotate(double aAngle, const VECTOR2I &aCenter={ 0, 0 })
Function Rotate rotates all vertices by a given angle.
static wxString ShowShape(STROKE_T aShape)
Function ShowShape converts the enum STROKE_T integer value to a wxString.
double GetOrientation() const
Definition: class_module.h:215
BOX2< VECTOR2I > BOX2I
Definition: box2.h:521
virtual const BOX2I ViewBBox() const override
Function ViewBBox() returns the bounding box of the item covering all its layers.
EDA_ITEM * m_Parent
Linked list: Link (parent struct)
Definition: base_struct.h:183
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:103
int OutlineCount() const
Returns the number of outlines in the set
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:206
void Merge(const EDA_RECT &aRect)
Function Merge modifies the position and size of the rectangle in order to contain aRect.
PNG memory record (file in memory).
Definition: bitmap_def.h:29
virtual void Move(const wxPoint &aMoveVector) override
Function Move move this object.
static const int dist[10][10]
Definition: ar_matrix.cpp:326
const BITMAP_OPAQUE add_dashed_line_xpm[1]
T NormalizeAngle360Max(T Angle)
Normalize angle to be >=-360.0 and <= 360.0 Angle can be equal to -360 or +360.
Definition: trigo.h:226
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX &aClosestVertex, int aClearance=0)
Function CollideEdge Checks whether aPoint collides with any edge of any of the contours of the polyg...
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
Implementation of conversion functions that require both schematic and board internal units.
BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
const wxPoint GetCenter() const override
Function GetCenter()
int GetX() const
Definition: eda_rect.h:111
STROKE_T GetShape() const
void GetPoly(std::vector< wxPoint > &aOutput, int aMinSegLen=0)
Converts Bezier curve to a polygon.
const wxPoint GetArcEnd() const
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Returns the index-th vertex in a given hole outline within a given outline
SHAPE_POLY_SET m_Poly
Stores the S_POLYGON shape.
int color
Definition: DXF_plotter.cpp:61
polygon (not yet used for tracks, but could be in microwave apps)
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Function FlippedLayerNumber.
Definition: lset.cpp:485
bool IsEmpty() const
Returns true if the set is empty (no polygons at all)
virtual BITMAP_DEF GetMenuImage() const override
Function GetMenuImage returns a pointer to an image to be used in menus.
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments Has me...
double RAD2DEG(double rad)
Definition: trigo.h:215
int VertexCount(int aOutline=-1, int aHole=-1) const
Returns the number of vertices in a given outline/hole
double GetArcAngleStart() const
function GetArcAngleStart()
#define DEFAULT_LINE_WIDTH
virtual wxString GetSelectMenuText(EDA_UNITS aUnits) const override
Function GetSelectMenuText returns the text to display to be used in the selection clarification cont...
Struct VERTEX_INDEX.
void SetOrigin(const wxPoint &pos)
Definition: eda_rect.h:131
void GRCSegm(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, int aPenSize, COLOR4D Color)
Definition: gr_basic.cpp:312
const wxPoint GetPosition() const override
bool IntersectsCircleEdge(const wxPoint &aCenter, const int aRadius, const int aWidth) const
IntersectsCircleEdge Tests for intersection between this rect and the edge (radius) of a circle.
EDA_RECT Common(const EDA_RECT &aRect) const
Function Common returns the area that is common with another rectangle.
usual segment : line with rounded ends
void SetPosition(const wxPoint &aPos) override
double m_Angle
Used only for Arcs: Arc angle in 1/10 deg.
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:257
const std::vector< wxPoint > BuildPolyPointsList() const
Build and return the list of corners in a std::vector<wxPoint> It must be used only to convert the SH...
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
int PointCount() const
Function PointCount()
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Function GetDisplayOptions returns the display options current in use Display options are relative to...
virtual void SwapData(BOARD_ITEM *aImage) override
Swap data between aItem and aImage.
bool Contains(const wxPoint &aPoint) const
Function Contains.
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
void Mirror(bool aX=true, bool aY=false, const VECTOR2I &aRef={ 0, 0 })
Mirrors the line points about y or x (or both)
This file contains miscellaneous commonly used macros and functions.
wxPoint m_BezierC1
Bezier Control Point 1.
bool TestSegmentHit(const wxPoint &aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:129
const VECTOR2I & CPoint(int aIndex) const
Function Point()
segment with non rounded ends
class MODULE, a footprint
Definition: typeinfo.h:89
virtual EDA_ITEM * Clone() const override
Function Clone creates a duplicate of this item with linked list members set to NULL.
wxPoint m_Start
Line start point or Circle and Arc center.
#define NULL
void computeArcBBox(EDA_RECT &aBBox) const
void Move(const VECTOR2I &aVector) override
wxPoint m_End
Line end point or circle and arc start point.
SHAPE_POLY_SET.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
void GRFillCSegm(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color)
Definition: gr_basic.cpp:415
const wxPoint GetOrigin() const
Definition: eda_rect.h:114
void SetEnd(int x, int y)
Definition: eda_rect.h:192
Arcs (with rounded ends)
void GRArc(EDA_RECT *ClipBox, wxDC *DC, int xc, int yc, double StAngle, double EndAngle, int r, COLOR4D Color)
Definition: gr_basic.cpp:749
virtual BOARD * GetBoard() const
Function GetBoard returns the BOARD in which this BOARD_ITEM resides, or NULL if none.
MODULE * GetParentModule() const
Function GetParentModule returns a pointer to the parent module, or NULL if DRAWSEGMENT does not belo...
void SetX(int val)
Definition: eda_rect.h:168
STROKE_T m_Shape
Shape: line, Circle, Arc.
Definition: colors.h:60
const wxPoint & GetArcStart() const
bool Collide(const VECTOR2I &aP, int aClearance=0) const override
Function Collide Checks whether the point aP collides with the inside of the polygon set; if the poin...
int GetRadius() const
Function GetRadius returns the radius of this item Has meaning only for arc and circle.
SHAPE_POLY_SET & GetPolyShape()
Bezier Curve.
int NewOutline()
Creates a new empty polygon in the set and returns its index
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
int GetPointCount() const
int GetWidth() const
void SetY(int val)
Definition: eda_rect.h:174
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
virtual void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Function Flip Flip this object, i.e.
void Normalize()
Function Normalize ensures that the height ant width are positive.
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aUseMils, EDA_DATA_TYPE aType)
Definition: base_units.cpp:127
double GetAngle() const
void GetMsgPanelInfo(EDA_UNITS aUnits, std::vector< MSG_PANEL_ITEM > &aList) override
Function GetMsgPanelInfo populates aList of MSG_PANEL_ITEM objects with it's internal state for displ...
Bezier curves to polygon converter.
Definition: bezier_curves.h:35
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
Function IsLayerVisible is a proxy function that calls the correspondent function in m_BoardSettings ...
Definition: class_board.h:431
virtual void Rotate(const wxPoint &aRotCentre, double aAngle) override
Function Rotate Rotate this object.
double GetLength() const
Function GetLength returns the length of the track using the hypotenuse calculation.
see class PGM_BASE
int m_Type
Used in complex associations ( Dimensions.. )
Class to handle a graphic segment.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:163
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
void GRFilledSegment(EDA_RECT *aClipBox, wxDC *aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor)
Definition: gr_basic.cpp:423
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void GRCircle(EDA_RECT *ClipBox, wxDC *DC, int xc, int yc, int r, int width, COLOR4D Color)
Definition: gr_basic.cpp:596
STATUS_FLAGS m_Flags
Flag bits for editing and other uses.
Definition: base_struct.h:189
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
double DECIDEG2RAD(double deg)
Definition: trigo.h:218
void Print(PCB_BASE_FRAME *aFrame, wxDC *DC, const wxPoint &aOffset=ZeroOffset) override
Function Print BOARD_ITEMs have their own color information.
wxPoint m_BezierC2
Bezier Control Point 2.
int GetY() const
Definition: eda_rect.h:112
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:61
EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boards.
Definition: base_struct.h:166
bool IsPolyShapeValid() const
wxPoint Centre() const
Definition: eda_rect.h:62
bool Intersects(const EDA_RECT &aRect) const
Function Intersects tests for a common area between rectangles.
void SetAngle(double aAngle)
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
bool HitTest(const wxPoint &aPosition, int aAccuracy=0) const override
Function HitTest tests if aPosition is contained within or on the bounding box of an item.
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
int m_Width
thickness of lines ...
Module description (excepted pads)
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:162
DRAWSEGMENT(BOARD_ITEM *aParent=NULL, KICAD_T idtype=PCB_LINE_T)
virtual const BOX2I ViewBBox() const override
Function ViewBBox() returns the bounding box of the item covering all its layers.
Message panel definition file.
wxString GetLayerName() const
Function GetLayerName returns the name of the PCB layer on which the item resides.
#define FORCE_SKETCH
Definition: pcbnew.h:44
void GRClosedPoly(EDA_RECT *ClipBox, wxDC *DC, int n, const wxPoint *Points, bool Fill, COLOR4D Color, COLOR4D BgColor)
Function GRClosedPoly draws a closed polygon onto the drawing context aDC and optionally fills and/or...
Definition: gr_basic.cpp:552
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
bool IsPolygonFilled() const
Polygonal shape is not always filled.
const wxPoint GetPosition() const override
Definition: class_module.h:210
PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
const wxSize GetSize() const
Definition: eda_rect.h:103
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
std::vector< wxPoint > m_BezierPoints
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:40
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes.