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-2018 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 
32 #include <fctsys.h>
33 #include <macros.h>
34 #include <gr_basic.h>
35 #include <bezier_curves.h>
36 #include <class_drawpanel.h>
37 #include <pcb_screen.h>
38 #include <trigo.h>
39 #include <msgpanel.h>
40 #include <bitmaps.h>
41 
42 #include <pcb_edit_frame.h>
43 
44 #include <pcbnew.h>
45 
46 #include <class_board.h>
47 #include <class_module.h>
48 #include <class_drawsegment.h>
49 #include <base_units.h>
50 
51 
53  BOARD_ITEM( aParent, idtype )
54 {
55  m_Type = 0;
56  m_Angle = 0;
57  m_Flags = 0;
59  m_Width = Millimeter2iu( DEFAULT_LINE_WIDTH );
60 }
61 
62 
64 {
65 }
66 
67 void DRAWSEGMENT::SetPosition( const wxPoint& aPos )
68 {
69  m_Start = aPos;
70 }
71 
72 const wxPoint DRAWSEGMENT::GetPosition() const
73 {
74  if( m_Shape == S_POLYGON )
75  return (wxPoint) m_Poly.CVertex( 0 );
76  else
77  return m_Start;
78 }
79 
80 void DRAWSEGMENT::Move( const wxPoint& aMoveVector )
81 {
82  m_Start += aMoveVector;
83  m_End += aMoveVector;
84 
85  switch ( m_Shape )
86  {
87  case S_POLYGON:
88  for( auto iter = m_Poly.Iterate(); iter; iter++ )
89  {
90  (*iter) += VECTOR2I( aMoveVector );
91  }
92  break;
93 
94  case S_CURVE:
95  m_BezierC1 += aMoveVector;
96  m_BezierC2 += aMoveVector;
97 
98  for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ )
99  {
100  m_BezierPoints[ii] += aMoveVector;
101  }
102 
103  break;
104 
105  default:
106  break;
107  }
108 }
109 
110 
111 void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle )
112 {
113  switch( m_Shape )
114  {
115  case S_ARC:
116  case S_SEGMENT:
117  case S_CIRCLE:
118  // these can all be done by just rotating the start and end points
119  RotatePoint( &m_Start, aRotCentre, aAngle);
120  RotatePoint( &m_End, aRotCentre, aAngle);
121  break;
122 
123  case S_POLYGON:
124  for( auto iter = m_Poly.Iterate(); iter; iter++ )
125  {
126  RotatePoint( *iter, VECTOR2I(aRotCentre), aAngle);
127  }
128  break;
129 
130  case S_CURVE:
131  RotatePoint( &m_Start, aRotCentre, aAngle);
132  RotatePoint( &m_End, aRotCentre, aAngle);
133  RotatePoint( &m_BezierC1, aRotCentre, aAngle);
134  RotatePoint( &m_BezierC2, aRotCentre, aAngle);
135 
136  for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ )
137  {
138  RotatePoint( &m_BezierPoints[ii], aRotCentre, aAngle);
139  }
140  break;
141 
142  case S_RECT:
143  default:
144  // un-handled edge transform
145  wxASSERT_MSG( false, wxT( "DRAWSEGMENT::Rotate not implemented for "
146  + ShowShape( m_Shape ) ) );
147  break;
148  }
149 }
150 
151 void DRAWSEGMENT::Flip( const wxPoint& aCentre )
152 {
153  m_Start.y = aCentre.y - (m_Start.y - aCentre.y);
154  m_End.y = aCentre.y - (m_End.y - aCentre.y);
155 
156  switch ( m_Shape )
157  {
158  case S_ARC:
159  m_Angle = -m_Angle;
160  break;
161 
162  case S_POLYGON:
163  for( auto iter = m_Poly.Iterate(); iter; iter++ )
164  {
165  iter->y = aCentre.y - (iter->y - aCentre.y);
166  }
167  break;
168 
169  case S_CURVE:
170  {
171  m_BezierC1.y = aCentre.y - (m_BezierC1.y - aCentre.y);
172  m_BezierC2.y = aCentre.y - (m_BezierC2.y - aCentre.y);
173 
174  // Rebuild the poly points shape
175  std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
176  BEZIER_POLY converter( ctrlPoints );
177  converter.GetPoly( m_BezierPoints, m_Width );
178  }
179  break;
180 
181  default:
182  break;
183  }
184 
185  // DRAWSEGMENT items are not allowed on copper layers, so
186  // copper layers count is not taken in account in Flip transform
187  SetLayer( FlipLayer( GetLayer() ) );
188 }
189 
190 
192 {
193  // Has meaning only for S_CURVE DRAW_SEGMENT shape
194  if( m_Shape != S_CURVE )
195  {
196  m_BezierPoints.clear();
197  return;
198  }
199  // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
200  std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
201  BEZIER_POLY converter( ctrlPoints );
202  converter.GetPoly( m_BezierPoints, aMinSegLen );
203 }
204 
205 
206 const wxPoint DRAWSEGMENT::GetCenter() const
207 {
208  wxPoint c;
209 
210  switch( m_Shape )
211  {
212  case S_ARC:
213  case S_CIRCLE:
214  c = m_Start;
215  break;
216 
217  case S_SEGMENT:
218  // Midpoint of the line
219  c = ( GetStart() + GetEnd() ) / 2;
220  break;
221 
222  case S_POLYGON:
223  case S_RECT:
224  case S_CURVE:
225  c = GetBoundingBox().Centre();
226  break;
227 
228  default:
229  wxASSERT_MSG( false, "DRAWSEGMENT::GetCentre not implemented for shape"
230  + ShowShape( GetShape() ) );
231  break;
232  }
233 
234  return c;
235 }
236 
237 const wxPoint DRAWSEGMENT::GetArcEnd() const
238 {
239  wxPoint endPoint; // start of arc
240 
241  switch( m_Shape )
242  {
243  case S_ARC:
244  // rotate the starting point of the arc, given by m_End, through the
245  // angle m_Angle to get the ending point of the arc.
246  // m_Start is the arc centre
247  endPoint = m_End; // m_End = start point of arc
248  RotatePoint( &endPoint, m_Start, -m_Angle );
249  break;
250 
251  default:
252  break;
253  }
254 
255  return endPoint; // after rotation, the end of the arc.
256 }
257 
259 {
260  // due to the Y axis orient atan2 needs - y value
261  double angleStart = ArcTangente( GetArcStart().y - GetCenter().y,
262  GetArcStart().x - GetCenter().x );
263 
264  // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg
265  // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg.
266  // and this is not easy to handle in calculations
267  NORMALIZE_ANGLE_POS( angleStart );
268 
269  return angleStart;
270 }
271 
272 
273 void DRAWSEGMENT::SetAngle( double aAngle )
274 {
275  // m_Angle must be >= -360 and <= +360 degrees
276  m_Angle = NormalizeAngle360Max( aAngle );
277 }
278 
279 
281 {
282  if( !m_Parent || m_Parent->Type() != PCB_MODULE_T )
283  return NULL;
284 
285  return (MODULE*) m_Parent;
286 }
287 
288 
289 void DRAWSEGMENT::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode,
290  const wxPoint& aOffset )
291 {
292  int ux0, uy0, dx, dy;
293  int l_trace;
294  int radius;
295 
296  PCB_LAYER_ID curr_layer = ( (PCB_SCREEN*) panel->GetScreen() )->m_Active_Layer;
297 
298  BOARD * brd = GetBoard( );
299 
300  if( brd->IsLayerVisible( GetLayer() ) == false )
301  return;
302 
303  auto frame = static_cast<PCB_EDIT_FRAME*> ( panel->GetParent() );
304  auto color = frame->Settings().Colors().GetLayerColor( GetLayer() );
305 
306  auto displ_opts = (PCB_DISPLAY_OPTIONS*) panel->GetDisplayOptions();
307 
308  if( ( draw_mode & GR_ALLOW_HIGHCONTRAST ) && displ_opts && displ_opts->m_ContrastModeDisplay )
309  {
310  if( !IsOnLayer( curr_layer ) && !IsOnLayer( Edge_Cuts ) )
312  }
313 
314  GRSetDrawMode( DC, draw_mode );
315  l_trace = m_Width >> 1; // half trace width
316 
317  // Line start point or Circle and Arc center
318  ux0 = m_Start.x + aOffset.x;
319  uy0 = m_Start.y + aOffset.y;
320 
321  // Line end point or circle and arc start point
322  dx = m_End.x + aOffset.x;
323  dy = m_End.y + aOffset.y;
324 
325  bool filled = displ_opts ? displ_opts->m_DisplayDrawItemsFill : FILLED;
326 
327  if( m_Flags & FORCE_SKETCH )
328  filled = SKETCH;
329 
330  switch( m_Shape )
331  {
332  case S_CIRCLE:
333  radius = KiROUND( Distance( ux0, uy0, dx, dy ) );
334 
335  if( filled )
336  {
337  GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius, m_Width, color );
338  }
339  else
340  {
341  GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius - l_trace, color );
342  GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius + l_trace, color );
343  }
344 
345  break;
346 
347  case S_ARC:
348  double StAngle, EndAngle;
349  radius = KiROUND( Distance( ux0, uy0, dx, dy ) );
350  StAngle = ArcTangente( dy - uy0, dx - ux0 );
351  EndAngle = StAngle + m_Angle;
352 
353  if( !panel->GetPrintMirrored() )
354  {
355  if( StAngle > EndAngle )
356  std::swap( StAngle, EndAngle );
357  }
358  else // Mirrored mode: arc orientation is reversed
359  {
360 #ifdef __WXMAC__ // wxWidgets OSX print driver handles arc mirroring for us
361  if( StAngle > EndAngle )
362  std::swap( StAngle, EndAngle );
363 #else
364  if( StAngle < EndAngle )
365  std::swap( StAngle, EndAngle );
366 #endif
367  }
368 
369  if( filled )
370  {
371  GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle,
372  radius, m_Width, color );
373  }
374  else
375  {
376  GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle,
377  radius - l_trace, color );
378  GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle,
379  radius + l_trace, color );
380  }
381 
382  break;
383 
384  case S_CURVE:
385  {
387 
388  wxPoint& startp = m_BezierPoints[0];
389 
390  for( unsigned int i = 1; i < m_BezierPoints.size(); i++ )
391  {
392  wxPoint& endp = m_BezierPoints[i];
393 
394  if( filled )
395  GRFilledSegment( panel->GetClipBox(), DC,
396  startp+aOffset, endp+aOffset, m_Width, color );
397  else
398  GRCSegm( panel->GetClipBox(), DC,
399  startp+aOffset, endp+aOffset, m_Width, color );
400 
401  startp = m_BezierPoints[i];
402  }
403  }
404  break;
405 
406  case S_POLYGON:
407  {
408  SHAPE_POLY_SET& outline = GetPolyShape();
409  // Draw the polygon: only one polygon is expected
410  // However we provide a multi polygon shape drawing
411  // ( for the future or to show a non expected shape )
412  for( int jj = 0; jj < outline.OutlineCount(); ++jj )
413  {
414  SHAPE_LINE_CHAIN& poly = outline.Outline( jj );
415  GRClosedPoly( panel->GetClipBox(), DC, poly.PointCount(),
416  (wxPoint*)&poly.Point( 0 ), IsPolygonFilled(), GetWidth(),
417  color, color );
418  }
419  }
420  break;
421 
422  default:
423  if( filled )
424  {
425  GRFillCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color );
426  }
427  else
428  {
429  GRCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color );
430  }
431 
432  break;
433  }
434 }
435 
436 void DRAWSEGMENT::GetMsgPanelInfo( EDA_UNITS_T aUnits, std::vector< MSG_PANEL_ITEM >& aList )
437 {
438  wxString msg;
439 
440  msg = _( "Drawing" );
441 
442  aList.push_back( MSG_PANEL_ITEM( _( "Type" ), msg, DARKCYAN ) );
443 
444  wxString shape = _( "Shape" );
445 
446  switch( m_Shape )
447  {
448  case S_CIRCLE:
449  aList.push_back( MSG_PANEL_ITEM( shape, _( "Circle" ), RED ) );
450 
451  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
452  aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
453  break;
454 
455  case S_ARC:
456  aList.push_back( MSG_PANEL_ITEM( shape, _( "Arc" ), RED ) );
457  msg.Printf( wxT( "%.1f" ), m_Angle / 10.0 );
458  aList.push_back( MSG_PANEL_ITEM( _( "Angle" ), msg, RED ) );
459 
460  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
461  aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
462  break;
463 
464  case S_CURVE:
465  aList.push_back( MSG_PANEL_ITEM( shape, _( "Curve" ), RED ) );
466  break;
467 
468  default:
469  {
470  aList.push_back( MSG_PANEL_ITEM( shape, _( "Segment" ), RED ) );
471 
472  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
473  aList.push_back( MSG_PANEL_ITEM( _( "Length" ), msg, DARKGREEN ) );
474 
475  // angle counter-clockwise from 3'o-clock
476  const double deg = RAD2DEG( atan2( (double)( m_Start.y - m_End.y ),
477  (double)( m_End.x - m_Start.x ) ) );
478  msg.Printf( wxT( "%.1f" ), deg );
479  aList.push_back( MSG_PANEL_ITEM( _( "Angle" ), msg, DARKGREEN ) );
480  }
481  }
482 
483  wxString start = wxString::Format( "@(%s, %s)",
484  MessageTextFromValue( aUnits, GetStart().x ),
485  MessageTextFromValue( aUnits, GetStart().y ) );
486  wxString end = wxString::Format( "@(%s, %s)",
487  MessageTextFromValue( aUnits, GetEnd().x ),
488  MessageTextFromValue( aUnits, GetEnd().y ) );
489 
490  aList.push_back( MSG_PANEL_ITEM( start, end, DARKGREEN ) );
491  aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), GetLayerName(), DARKBROWN ) );
492 
493  msg = MessageTextFromValue( aUnits, m_Width, true );
494  aList.push_back( MSG_PANEL_ITEM( _( "Width" ), msg, DARKCYAN ) );
495 }
496 
497 
499 {
500  EDA_RECT bbox;
501 
502  bbox.SetOrigin( m_Start );
503 
504  switch( m_Shape )
505  {
506  case S_SEGMENT:
507  bbox.SetEnd( m_End );
508  break;
509 
510  case S_CIRCLE:
511  bbox.Inflate( GetRadius() );
512  break;
513 
514  case S_ARC:
515  computeArcBBox( bbox );
516  break;
517 
518  case S_POLYGON:
519  if( m_Poly.IsEmpty() )
520  break;
521  {
522  wxPoint p_end;
523  MODULE* module = GetParentModule();
524  bool first = true;
525 
526  for( auto iter = m_Poly.CIterate(); iter; iter++ )
527  {
528  wxPoint pt ( iter->x, iter->y );
529 
530  if( module ) // Transform, if we belong to a module
531  {
532  RotatePoint( &pt, module->GetOrientation() );
533  pt += module->GetPosition();
534  }
535 
536 
537  if( first )
538  {
539  p_end = pt;
540  bbox.SetX( pt.x );
541  bbox.SetY( pt.y );
542  first = false;
543  }
544  else
545  {
546 
547  bbox.SetX( std::min( bbox.GetX(), pt.x ) );
548  bbox.SetY( std::min( bbox.GetY(), pt.y ) );
549 
550  p_end.x = std::max( p_end.x, pt.x );
551  p_end.y = std::max( p_end.y, pt.y );
552  }
553  }
554 
555  bbox.SetEnd( p_end );
556  break;
557  }
558 
559  case S_CURVE:
560  // Rebuild the poly points shape
561  ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width );
562 
563  for( unsigned ii = 0; ii < m_BezierPoints.size(); ++ii )
564  bbox.Merge( m_BezierPoints[ii] );
565  break;
566 
567  default:
568  break;
569  }
570 
571  bbox.Inflate( ((m_Width+1) / 2) + 1 );
572  bbox.Normalize();
573 
574  return bbox;
575 }
576 
577 
578 bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) const
579 {
580  switch( m_Shape )
581  {
582  case S_CIRCLE:
583  case S_ARC:
584  {
585  wxPoint relPos = aPosition - GetCenter();
586  int radius = GetRadius();
587  int dist = KiROUND( EuclideanNorm( relPos ) );
588 
589  if( abs( radius - dist ) <= ( m_Width / 2 ) )
590  {
591  if( m_Shape == S_CIRCLE )
592  return true;
593 
594  // For arcs, the test point angle must be >= arc angle start
595  // and <= arc angle end
596  // However angle values > 360 deg are not easy to handle
597  // so we calculate the relative angle between arc start point and teast point
598  // this relative arc should be < arc angle if arc angle > 0 (CW arc)
599  // and > arc angle if arc angle < 0 (CCW arc)
600  double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
601 
602  double arc_hittest = ArcTangente( relPos.y, relPos.x );
603 
604  // Calculate relative angle between the starting point of the arc, and the test point
605  arc_hittest -= arc_angle_start;
606 
607  // Normalise arc_hittest between 0 ... 360 deg
608  NORMALIZE_ANGLE_POS( arc_hittest );
609 
610  // Check angle: inside the arc angle when it is > 0
611  // and outside the not drawn arc when it is < 0
612  if( GetAngle() >= 0.0 )
613  {
614  if( arc_hittest <= GetAngle() )
615  return true;
616  }
617  else
618  {
619  if( arc_hittest >= (3600.0 + GetAngle()) )
620  return true;
621  }
622  }
623  }
624  break;
625 
626  case S_CURVE:
627  ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width );
628 
629  for( unsigned int i= 1; i < m_BezierPoints.size(); i++)
630  {
631  if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i-1], m_Width / 2 ) )
632  return true;
633  }
634  break;
635 
636  case S_SEGMENT:
637  if( TestSegmentHit( aPosition, m_Start, m_End, m_Width / 2 ) )
638  return true;
639  break;
640 
641  case S_POLYGON:
642  {
643  if( !IsPolygonFilled() )
644  {
646  auto poly = m_Poly; //todo: Fix CollideEdge to be const
647  return poly.CollideEdge(VECTOR2I( aPosition ), i,
648  std::max( m_Width / 2, Millimeter2iu( 0.25 ) ) );
649  }
650  else
651  return m_Poly.Collide( VECTOR2I( aPosition ), m_Width / 2 );
652  }
653  break;
654 
655  default:
656  wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
657  break;
658  }
659 
660  return false;
661 }
662 
663 
664 bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
665 {
666  EDA_RECT arect = aRect;
667  arect.Normalize();
668  arect.Inflate( aAccuracy );
669 
670  EDA_RECT arcRect;
671  EDA_RECT bb = GetBoundingBox();
672 
673  switch( m_Shape )
674  {
675  case S_CIRCLE:
676  // Test if area intersects or contains the circle:
677  if( aContained )
678  return arect.Contains( bb );
679  else
680  {
681  // If the rectangle does not intersect the bounding box, this is a much quicker test
682  if( !aRect.Intersects( bb ) )
683  {
684  return false;
685  }
686  else
687  {
688  return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
689  }
690 
691  }
692  break;
693 
694  case S_ARC:
695  // Test for full containment of this arc in the rect
696  if( aContained )
697  {
698  return arect.Contains( bb );
699  }
700  // Test if the rect crosses the arc
701  else
702  {
703  arcRect = bb.Common( arect );
704 
705  /* All following tests must pass:
706  * 1. Rectangle must intersect arc BoundingBox
707  * 2. Rectangle must cross the outside of the arc
708  */
709  return arcRect.Intersects( arect ) &&
711  }
712  break;
713 
714  case S_SEGMENT:
715  if( aContained )
716  {
717  return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
718  }
719  else
720  {
721  // Account for the width of the line
722  arect.Inflate( GetWidth() / 2 );
723  return arect.Intersects( GetStart(), GetEnd() );
724  }
725 
726  break;
727 
728  case S_POLYGON:
729  if( aContained )
730  {
731  return arect.Contains( bb );
732  }
733  else
734  {
735  // Fast test: if aRect is outside the polygon bounding box,
736  // rectangles cannot intersect
737  if( !arect.Intersects( bb ) )
738  return false;
739 
740  // Account for the width of the line
741  arect.Inflate( GetWidth() / 2 );
742  int count = m_Poly.TotalVertices();
743 
744  for( int ii = 0; ii < count; ii++ )
745  {
746  auto vertex = m_Poly.CVertex( ii );
747  auto vertexNext = m_Poly.CVertex( ( ii + 1 ) % count );
748 
749  // Test if the point is within aRect
750  if( arect.Contains( ( wxPoint ) vertex ) )
751  return true;
752 
753  // Test if this edge intersects aRect
754  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
755  return true;
756  }
757  }
758  break;
759 
760  case S_CURVE: // not yet handled
761  if( aContained )
762  {
763  return arect.Contains( bb );
764  }
765  else
766  {
767  // Fast test: if aRect is outside the polygon bounding box,
768  // rectangles cannot intersect
769  if( !arect.Intersects( bb ) )
770  return false;
771 
772  // Account for the width of the line
773  arect.Inflate( GetWidth() / 2 );
774  unsigned count = m_BezierPoints.size();
775 
776  for( unsigned ii = 1; ii < count; ii++ )
777  {
778  wxPoint vertex = m_BezierPoints[ii-1];
779  wxPoint vertexNext = m_BezierPoints[ii];
780 
781  // Test if the point is within aRect
782  if( arect.Contains( ( wxPoint ) vertex ) )
783  return true;
784 
785  // Test if this edge intersects aRect
786  if( arect.Intersects( vertex, vertexNext ) )
787  return true;
788  }
789  }
790  break;
791 
792 
793  default:
794  wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
795  break;
796  }
797 
798  return false;
799 }
800 
801 
803 {
804  return wxString::Format( _( "Pcb Graphic %s, length %s on %s" ),
805  ShowShape( m_Shape ),
806  MessageTextFromValue( aUnits, GetLength() ),
807  GetLayerName() );
808 }
809 
810 
812 {
813  return add_dashed_line_xpm;
814 }
815 
816 
818 {
819  return new DRAWSEGMENT( *this );
820 }
821 
822 
824 {
825  // For arcs - do not include the center point in the bounding box,
826  // it is redundant for displaying an arc
827  if( m_Shape == S_ARC )
828  {
829  EDA_RECT bbox;
830  bbox.SetOrigin( m_End );
831  computeArcBBox( bbox );
832  return BOX2I( bbox.GetOrigin(), bbox.GetSize() );
833  }
834 
835  return EDA_ITEM::ViewBBox();
836 }
837 
838 
840 {
841  // Do not include the center, which is not necessarily
842  // inside the BB of a arc with a small angle
843  aBBox.SetOrigin( m_End );
844 
845  wxPoint end = m_End;
846  RotatePoint( &end, m_Start, -m_Angle );
847  aBBox.Merge( end );
848 
849  // Determine the starting quarter
850  // 0 right-bottom
851  // 1 left-bottom
852  // 2 left-top
853  // 3 right-top
854  unsigned int quarter = 0; // assume right-bottom
855 
856  if( m_End.x < m_Start.x )
857  {
858  if( m_End.y <= m_Start.y )
859  quarter = 2;
860  else // ( m_End.y > m_Start.y )
861  quarter = 1;
862  }
863  else if( m_End.x >= m_Start.x )
864  {
865  if( m_End.y < m_Start.y )
866  quarter = 3;
867  else if( m_End.x == m_Start.x )
868  quarter = 1;
869  }
870 
871  int radius = GetRadius();
872  int angle = (int) GetArcAngleStart() % 900 + m_Angle;
873  bool directionCW = ( m_Angle > 0 ); // Is the direction of arc clockwise?
874 
875  // Make the angle positive, so we go clockwise and merge points belonging to the arc
876  if( !directionCW )
877  {
878  angle = 900 - angle;
879  quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic
880  }
881 
882  while( angle > 900 )
883  {
884  switch( quarter )
885  {
886  case 0:
887  aBBox.Merge( wxPoint( m_Start.x, m_Start.y + radius ) ); // down
888  break;
889 
890  case 1:
891  aBBox.Merge( wxPoint( m_Start.x - radius, m_Start.y ) ); // left
892  break;
893 
894  case 2:
895  aBBox.Merge( wxPoint( m_Start.x, m_Start.y - radius ) ); // up
896  break;
897 
898  case 3:
899  aBBox.Merge( wxPoint( m_Start.x + radius, m_Start.y ) ); // right
900  break;
901  }
902 
903  if( directionCW )
904  ++quarter;
905  else
906  quarter += 3; // -1 modulo arithmetic
907 
908  quarter %= 4;
909  angle -= 900;
910  }
911 }
912 
913 void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
914 {
916  m_Poly.NewOutline();
917 
918  for ( auto p : aPoints )
919  {
920  m_Poly.Append( p.x, p.y );
921  }
922 }
923 
924 
925 const std::vector<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const
926 {
927  std::vector<wxPoint> rv;
928 
929  if( m_Poly.OutlineCount() )
930  {
931  if( m_Poly.COutline( 0 ).PointCount() )
932  {
933  for ( auto iter = m_Poly.CIterate(); iter; iter++ )
934  {
935  rv.push_back( wxPoint( iter->x, iter->y ) );
936  }
937  }
938  }
939 
940  return rv;
941 }
942 
943 
945 {
946  // return true if the polygonal shape is valid (has more than 2 points)
947  if( GetPolyShape().OutlineCount() == 0 )
948  return false;
949 
950  const SHAPE_LINE_CHAIN& outline = ((SHAPE_POLY_SET&)GetPolyShape()).Outline( 0 );
951 
952  return outline.PointCount() > 2;
953 }
954 
955 
957 {
958  // return the number of corners of the polygonal shape
959  // this shape is expected to be only one polygon without hole
960  if( GetPolyShape().OutlineCount() )
961  return GetPolyShape().VertexCount( 0 );
962 
963  return 0;
964 }
965 
966 
968 {
969  assert( aImage->Type() == PCB_LINE_T );
970 
971  std::swap( *((DRAWSEGMENT*) this), *((DRAWSEGMENT*) aImage) );
972 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:112
virtual BASE_SCREEN * GetScreen()=0
KICAD_T Type() const
Function Type()
Definition: base_struct.h:201
static wxString ShowShape(STROKE_T aShape)
Function ShowShape converts the enum STROKE_T integer value to a wxString.
virtual void Flip(const wxPoint &aCentre) override
Function Flip Flip this object, i.e.
BOX2< VECTOR2I > BOX2I
Definition: box2.h:520
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:171
double GetLength() const
Function GetLength returns the length of the track using the hypotenuse calculation.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Returns the index-th vertex in a given hole outline within a given outline
const wxPoint GetOrigin() const
Definition: eda_rect.h:112
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Function GetLineLength returns the length of a line segment defined by aPointA and aPointB...
Definition: trigo.h:191
void Merge(const EDA_RECT &aRect)
Function Merge modifies the position and size of the rectangle in order to contain aRect...
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...
PNG memory record (file in memory).
Definition: bitmap_types.h:43
virtual void Move(const wxPoint &aMoveVector) override
Function Move move this object.
static const int dist[10][10]
Definition: ar_matrix.cpp:320
void GRSetDrawMode(wxDC *DC, GR_DRAWMODE draw_mode)
Definition: gr_basic.cpp:223
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...
T NormalizeAngle360Max(T Angle)
Normalize angle to be >=-360.0 and <= 360.0 Angle can be equal to -360 or +360.
Definition: trigo.h:211
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:120
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Function IsOnLayer tests to see if this object is on the given layer.
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...
Class 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()
const wxSize GetSize() const
Definition: eda_rect.h:101
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
int PointCount() const
Function PointCount()
bool Contains(const wxPoint &aPoint) const
Function Contains.
void GetPoly(std::vector< wxPoint > &aOutput, int aMinSegLen=0)
Converts Bezier curve to a polygon.
Class BOARD to handle a board.
SHAPE_POLY_SET m_Poly
Stores the S_POLYGON shape.
int color
Definition: DXF_plotter.cpp:62
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:475
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...
virtual bool HitTest(const wxPoint &aPosition) const override
Function HitTest tests if aPosition is contained within or on the bounding area of an item...
double RAD2DEG(double rad)
Definition: trigo.h:200
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
double GetArcAngleStart() const
function GetArcAngleStart()
EDA_RECT Common(const EDA_RECT &aRect) const
Function Common returns the area that is common with another rectangle.
int TotalVertices() const
Returns total number of vertices stored in the set.
#define DEFAULT_LINE_WIDTH
Struct VERTEX_INDEX.
void SetOrigin(const wxPoint &pos)
Definition: eda_rect.h:124
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:415
const wxPoint GetPosition() const override
virtual wxString GetSelectMenuText(EDA_UNITS_T aUnits) const override
Function GetSelectMenuText returns the text to display to be used in the selection clarification cont...
usual segment : line with rounded ends
void SetPosition(const wxPoint &aPos) override
virtual EDA_RECT * GetClipBox()
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
double m_Angle
Used only for Arcs: Arc angle in 1/10 deg.
bool IsPolyShapeValid() const
ITERATOR Iterate(int aFirst, int aLast, bool aIterateHoles=false)
Function Iterate returns an object to iterate through the points of the polygons between aFirst and a...
int OutlineCount() const
Returns the number of outlines in the set
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:241
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
#define abs(a)
Definition: auxiliary.h:84
virtual void SwapData(BOARD_ITEM *aImage) override
Swap data between aItem and aImage.
wxString GetLayerName() const
Function GetLayerName returns the name of the PCB layer on which the item resides.
void GRClosedPoly(EDA_RECT *ClipBox, wxDC *DC, int n, 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:711
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
This file contains miscellaneous commonly used macros and functions.
wxPoint m_BezierC1
Bezier Control Point 1.
const wxPoint & GetArcStart() const
wxString MessageTextFromValue(EDA_UNITS_T aUnits, int aValue, bool aUseMils)
Definition: base_units.cpp:125
virtual EDA_DRAW_FRAME * GetParent() const =0
int GetPointCount() const
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.
Class PCB_DISPLAY_OPTIONS handles display options like enable/disable some optional drawings...
PCB_LAYER_ID
A quick note on layer IDs:
STROKE_T GetShape() const
wxPoint m_Start
Line start point or Circle and Arc center.
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:170
wxPoint m_End
Line end point or circle and arc start point.
double GetOrientation() const
Definition: class_module.h:188
GR_DRAWMODE
Drawmode. Compositing mode plus a flag or two.
Definition: gr_basic.h:37
Class 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:518
void SetEnd(int x, int y)
Definition: eda_rect.h:134
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:908
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
void SetX(int val)
Definition: eda_rect.h:130
STROKE_T m_Shape
Shape: line, Circle, Arc.
Definition: colors.h:60
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...
SHAPE_POLY_SET & GetPolyShape()
Bezier Curve.
wxPoint Centre() const
Definition: eda_rect.h:60
bool IsPolygonFilled() const
Polygonal shape is not always filled.
void computeArcBBox(EDA_RECT &aBBox) const
int NewOutline()
Creates a new empty polygon in the set and returns its index
void SetY(int val)
Definition: eda_rect.h:131
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
void Normalize()
Function Normalize ensures that the height ant width are positive.
bool Intersects(const EDA_RECT &aRect) const
Function Intersects tests for a common area between rectangles.
Bezier curves to polygon converter.
Definition: bezier_curves.h:35
virtual void Rotate(const wxPoint &aRotCentre, double aAngle) override
Function Rotate Rotate this object.
double GetAngle() const
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
#define max(a, b)
Definition: auxiliary.h:86
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:171
Class SHAPE_LINE_CHAIN.
void GRFilledSegment(EDA_RECT *aClipBox, wxDC *aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor)
Definition: gr_basic.cpp:526
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
int VertexCount(int aOutline=-1, int aHole=-1) const
Returns the number of vertices in a given outline/hole
void GRCircle(EDA_RECT *ClipBox, wxDC *DC, int xc, int yc, int r, int width, COLOR4D Color)
Definition: gr_basic.cpp:755
STATUS_FLAGS m_Flags
Flag bits for editing and other uses.
Definition: base_struct.h:178
size_t i
Definition: json11.cpp:597
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
const wxPoint GetArcEnd() const
Class EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
Class PCB_EDIT_FRAME is the main frame for Pcbnew.
int GetX() const
Definition: eda_rect.h:109
wxPoint m_BezierC2
Bezier Control Point 2.
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:154
int GetWidth() const
int GetY() const
Definition: eda_rect.h:110
virtual BOARD * GetBoard() const
Function GetBoard returns the BOARD in which this BOARD_ITEM resides, or NULL if none.
void SetAngle(double aAngle)
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees...
MODULE * GetParentModule() const
Function GetParentModule returns a pointer to the parent module, or NULL if DRAWSEGMENT does not belo...
VECTOR2I & Point(int aIndex)
Function Point()
virtual void GetMsgPanelInfo(EDA_UNITS_T aUnits, std::vector< MSG_PANEL_ITEM > &aList) override
Function GetMsgPanelInfo populates aList of MSG_PANEL_ITEM objects with it&#39;s internal state for displ...
int m_Width
thickness of lines ...
Module description (excepted pads)
bool IsEmpty() const
Returns true if the set is empty (no polygons at all)
virtual void * GetDisplayOptions()
Function GetDisplayOptions A way to pass info to draw functions.
DRAWSEGMENT(BOARD_ITEM *aParent=NULL, KICAD_T idtype=PCB_LINE_T)
Class EDA_MSG_ITEM is used EDA_MSG_PANEL as the item type for displaying messages.
Definition: msgpanel.h:53
bool TestSegmentHit(const wxPoint &aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist)
Function TestSegmentHit test for hit on line segment i.e.
Definition: trigo.cpp:122
double Distance(double x1, double y1, double x2, double y2)
void Draw(EDA_DRAW_PANEL *panel, wxDC *DC, GR_DRAWMODE aDrawMode, const wxPoint &aOffset=ZeroOffset) override
Function Draw BOARD_ITEMs have their own color information.
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
virtual const BOX2I ViewBBox() const override
Function ViewBBox() returns the bounding box of the item covering all its layers. ...
Message panel definition file.
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
int GetRadius() const
Function GetRadius returns the radius of this item Has meaning only for arc and circle.
#define FORCE_SKETCH
Definition: pcbnew.h:73
const wxPoint GetPosition() const override
Definition: class_module.h:183
EDA_UNITS_T
Definition: common.h:159
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
#define min(a, b)
Definition: auxiliary.h:85
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:458
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
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39
virtual const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes...