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 
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 
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 
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 
416  GRClosedPoly( panel->GetClipBox(), DC, poly.PointCount(),
417  (wxPoint*)&poly.Point( 0 ), FILLED, GetWidth(),
418  color, color );
419  }
420  }
421  break;
422 
423  default:
424  if( filled )
425  {
426  GRFillCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color );
427  }
428  else
429  {
430  GRCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color );
431  }
432 
433  break;
434  }
435 }
436 
437 void DRAWSEGMENT::GetMsgPanelInfo( EDA_UNITS_T aUnits, std::vector< MSG_PANEL_ITEM >& aList )
438 {
439  wxString msg;
440 
441  msg = _( "Drawing" );
442 
443  aList.push_back( MSG_PANEL_ITEM( _( "Type" ), msg, DARKCYAN ) );
444 
445  wxString shape = _( "Shape" );
446 
447  switch( m_Shape )
448  {
449  case S_CIRCLE:
450  aList.push_back( MSG_PANEL_ITEM( shape, _( "Circle" ), RED ) );
451 
452  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
453  aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
454  break;
455 
456  case S_ARC:
457  aList.push_back( MSG_PANEL_ITEM( shape, _( "Arc" ), RED ) );
458  msg.Printf( wxT( "%.1f" ), m_Angle / 10.0 );
459  aList.push_back( MSG_PANEL_ITEM( _( "Angle" ), msg, RED ) );
460 
461  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
462  aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
463  break;
464 
465  case S_CURVE:
466  aList.push_back( MSG_PANEL_ITEM( shape, _( "Curve" ), RED ) );
467  break;
468 
469  default:
470  {
471  aList.push_back( MSG_PANEL_ITEM( shape, _( "Segment" ), RED ) );
472 
473  msg = MessageTextFromValue( aUnits, GetLineLength( m_Start, m_End ) );
474  aList.push_back( MSG_PANEL_ITEM( _( "Length" ), msg, DARKGREEN ) );
475 
476  // angle counter-clockwise from 3'o-clock
477  const double deg = RAD2DEG( atan2( (double)( m_Start.y - m_End.y ),
478  (double)( m_End.x - m_Start.x ) ) );
479  msg.Printf( wxT( "%.1f" ), deg );
480  aList.push_back( MSG_PANEL_ITEM( _( "Angle" ), msg, DARKGREEN ) );
481  }
482  }
483 
484  wxString start = wxString::Format( "@(%s, %s)",
485  MessageTextFromValue( aUnits, GetStart().x ),
486  MessageTextFromValue( aUnits, GetStart().y ) );
487  wxString end = wxString::Format( "@(%s, %s)",
488  MessageTextFromValue( aUnits, GetEnd().x ),
489  MessageTextFromValue( aUnits, GetEnd().y ) );
490 
491  aList.push_back( MSG_PANEL_ITEM( start, end, DARKGREEN ) );
492  aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), GetLayerName(), DARKBROWN ) );
493 
494  msg = MessageTextFromValue( aUnits, m_Width, true );
495  aList.push_back( MSG_PANEL_ITEM( _( "Width" ), msg, DARKCYAN ) );
496 }
497 
498 
500 {
501  EDA_RECT bbox;
502 
503  bbox.SetOrigin( m_Start );
504 
505  switch( m_Shape )
506  {
507  case S_SEGMENT:
508  bbox.SetEnd( m_End );
509  break;
510 
511  case S_CIRCLE:
512  bbox.Inflate( GetRadius() );
513  break;
514 
515  case S_ARC:
516  computeArcBBox( bbox );
517  break;
518 
519  case S_POLYGON:
520  if( m_Poly.IsEmpty() )
521  break;
522  {
523  wxPoint p_end;
524  MODULE* module = GetParentModule();
525  bool first = true;
526 
527  for( auto iter = m_Poly.CIterate(); iter; iter++ )
528  {
529  wxPoint pt ( iter->x, iter->y );
530 
531  if( module ) // Transform, if we belong to a module
532  {
533  RotatePoint( &pt, module->GetOrientation() );
534  pt += module->GetPosition();
535  }
536 
537 
538  if( first )
539  {
540  p_end = pt;
541  bbox.SetX( pt.x );
542  bbox.SetY( pt.y );
543  first = false;
544  }
545  else
546  {
547 
548  bbox.SetX( std::min( bbox.GetX(), pt.x ) );
549  bbox.SetY( std::min( bbox.GetY(), pt.y ) );
550 
551  p_end.x = std::max( p_end.x, pt.x );
552  p_end.y = std::max( p_end.y, pt.y );
553  }
554  }
555 
556  bbox.SetEnd( p_end );
557  break;
558  }
559 
560  case S_CURVE:
561  // Rebuild the poly points shape
562  ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width );
563 
564  for( unsigned ii = 0; ii < m_BezierPoints.size(); ++ii )
565  bbox.Merge( m_BezierPoints[ii] );
566  break;
567 
568  default:
569  break;
570  }
571 
572  bbox.Inflate( ((m_Width+1) / 2) + 1 );
573  bbox.Normalize();
574 
575  return bbox;
576 }
577 
578 
579 bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) const
580 {
581  switch( m_Shape )
582  {
583  case S_CIRCLE:
584  case S_ARC:
585  {
586  wxPoint relPos = aPosition - GetCenter();
587  int radius = GetRadius();
588  int dist = KiROUND( EuclideanNorm( relPos ) );
589 
590  if( abs( radius - dist ) <= ( m_Width / 2 ) )
591  {
592  if( m_Shape == S_CIRCLE )
593  return true;
594 
595  // For arcs, the test point angle must be >= arc angle start
596  // and <= arc angle end
597  // However angle values > 360 deg are not easy to handle
598  // so we calculate the relative angle between arc start point and teast point
599  // this relative arc should be < arc angle if arc angle > 0 (CW arc)
600  // and > arc angle if arc angle < 0 (CCW arc)
601  double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
602 
603  double arc_hittest = ArcTangente( relPos.y, relPos.x );
604 
605  // Calculate relative angle between the starting point of the arc, and the test point
606  arc_hittest -= arc_angle_start;
607 
608  // Normalise arc_hittest between 0 ... 360 deg
609  NORMALIZE_ANGLE_POS( arc_hittest );
610 
611  // Check angle: inside the arc angle when it is > 0
612  // and outside the not drawn arc when it is < 0
613  if( GetAngle() >= 0.0 )
614  {
615  if( arc_hittest <= GetAngle() )
616  return true;
617  }
618  else
619  {
620  if( arc_hittest >= (3600.0 + GetAngle()) )
621  return true;
622  }
623  }
624  }
625  break;
626 
627  case S_CURVE:
628  ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width );
629 
630  for( unsigned int i= 1; i < m_BezierPoints.size(); i++)
631  {
632  if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i-1], m_Width / 2 ) )
633  return true;
634  }
635  break;
636 
637  case S_SEGMENT:
638  if( TestSegmentHit( aPosition, m_Start, m_End, m_Width / 2 ) )
639  return true;
640  break;
641 
642  case S_POLYGON: // not yet handled
643  {
644  #define MAX_DIST_IN_MM 0.25
645  int distmax = std::max( m_Width, Millimeter2iu( MAX_DIST_IN_MM ) );
646 
647  if( m_Poly.Collide( VECTOR2I( aPosition ), distmax ) )
648  return true;
649  }
650  break;
651 
652  default:
653  wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
654  break;
655  }
656 
657  return false;
658 }
659 
660 
661 bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
662 {
663  EDA_RECT arect = aRect;
664  arect.Normalize();
665  arect.Inflate( aAccuracy );
666 
667  EDA_RECT arcRect;
668  EDA_RECT bb = GetBoundingBox();
669 
670  switch( m_Shape )
671  {
672  case S_CIRCLE:
673  // Test if area intersects or contains the circle:
674  if( aContained )
675  return arect.Contains( bb );
676  else
677  {
678  // If the rectangle does not intersect the bounding box, this is a much quicker test
679  if( !aRect.Intersects( bb ) )
680  {
681  return false;
682  }
683  else
684  {
685  return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
686  }
687 
688  }
689  break;
690 
691  case S_ARC:
692  // Test for full containment of this arc in the rect
693  if( aContained )
694  {
695  return arect.Contains( bb );
696  }
697  // Test if the rect crosses the arc
698  else
699  {
700  arcRect = bb.Common( arect );
701 
702  /* All following tests must pass:
703  * 1. Rectangle must intersect arc BoundingBox
704  * 2. Rectangle must cross the outside of the arc
705  */
706  return arcRect.Intersects( arect ) &&
708  }
709  break;
710 
711  case S_SEGMENT:
712  if( aContained )
713  {
714  return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
715  }
716  else
717  {
718  // Account for the width of the line
719  arect.Inflate( GetWidth() / 2 );
720  return arect.Intersects( GetStart(), GetEnd() );
721  }
722 
723  break;
724 
725  case S_POLYGON:
726  if( aContained )
727  {
728  return arect.Contains( bb );
729  }
730  else
731  {
732  // Fast test: if aRect is outside the polygon bounding box,
733  // rectangles cannot intersect
734  if( !arect.Intersects( bb ) )
735  return false;
736 
737  // Account for the width of the line
738  arect.Inflate( GetWidth() / 2 );
739  int count = m_Poly.TotalVertices();
740 
741  for( int ii = 0; ii < count; ii++ )
742  {
743  auto vertex = m_Poly.CVertex( ii );
744  auto vertexNext = m_Poly.CVertex( ( ii + 1 ) % count );
745 
746  // Test if the point is within aRect
747  if( arect.Contains( ( wxPoint ) vertex ) )
748  return true;
749 
750  // Test if this edge intersects aRect
751  if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
752  return true;
753  }
754  }
755  break;
756 
757  case S_CURVE: // not yet handled
758  if( aContained )
759  {
760  return arect.Contains( bb );
761  }
762  else
763  {
764  // Fast test: if aRect is outside the polygon bounding box,
765  // rectangles cannot intersect
766  if( !arect.Intersects( bb ) )
767  return false;
768 
769  // Account for the width of the line
770  arect.Inflate( GetWidth() / 2 );
771  unsigned count = m_BezierPoints.size();
772 
773  for( unsigned ii = 1; ii < count; ii++ )
774  {
775  wxPoint vertex = m_BezierPoints[ii-1];
776  wxPoint vertexNext = m_BezierPoints[ii];
777 
778  // Test if the point is within aRect
779  if( arect.Contains( ( wxPoint ) vertex ) )
780  return true;
781 
782  // Test if this edge intersects aRect
783  if( arect.Intersects( vertex, vertexNext ) )
784  return true;
785  }
786  }
787  break;
788 
789 
790  default:
791  wxASSERT_MSG( 0, wxString::Format( "unknown DRAWSEGMENT shape: %d", m_Shape ) );
792  break;
793  }
794 
795  return false;
796 }
797 
798 
800 {
801  return wxString::Format( _( "Pcb Graphic %s, length %s on %s" ),
802  ShowShape( m_Shape ),
803  MessageTextFromValue( aUnits, GetLength() ),
804  GetLayerName() );
805 }
806 
807 
809 {
810  return add_dashed_line_xpm;
811 }
812 
813 
815 {
816  return new DRAWSEGMENT( *this );
817 }
818 
819 
821 {
822  // For arcs - do not include the center point in the bounding box,
823  // it is redundant for displaying an arc
824  if( m_Shape == S_ARC )
825  {
826  EDA_RECT bbox;
827  bbox.SetOrigin( m_End );
828  computeArcBBox( bbox );
829  return BOX2I( bbox.GetOrigin(), bbox.GetSize() );
830  }
831 
832  return EDA_ITEM::ViewBBox();
833 }
834 
835 
837 {
838  // Do not include the center, which is not necessarily
839  // inside the BB of a arc with a small angle
840  aBBox.SetOrigin( m_End );
841 
842  wxPoint end = m_End;
843  RotatePoint( &end, m_Start, -m_Angle );
844  aBBox.Merge( end );
845 
846  // Determine the starting quarter
847  // 0 right-bottom
848  // 1 left-bottom
849  // 2 left-top
850  // 3 right-top
851  unsigned int quarter = 0; // assume right-bottom
852 
853  if( m_End.x < m_Start.x )
854  {
855  if( m_End.y <= m_Start.y )
856  quarter = 2;
857  else // ( m_End.y > m_Start.y )
858  quarter = 1;
859  }
860  else if( m_End.x >= m_Start.x )
861  {
862  if( m_End.y < m_Start.y )
863  quarter = 3;
864  else if( m_End.x == m_Start.x )
865  quarter = 1;
866  }
867 
868  int radius = GetRadius();
869  int angle = (int) GetArcAngleStart() % 900 + m_Angle;
870  bool directionCW = ( m_Angle > 0 ); // Is the direction of arc clockwise?
871 
872  // Make the angle positive, so we go clockwise and merge points belonging to the arc
873  if( !directionCW )
874  {
875  angle = 900 - angle;
876  quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic
877  }
878 
879  while( angle > 900 )
880  {
881  switch( quarter )
882  {
883  case 0:
884  aBBox.Merge( wxPoint( m_Start.x, m_Start.y + radius ) ); // down
885  break;
886 
887  case 1:
888  aBBox.Merge( wxPoint( m_Start.x - radius, m_Start.y ) ); // left
889  break;
890 
891  case 2:
892  aBBox.Merge( wxPoint( m_Start.x, m_Start.y - radius ) ); // up
893  break;
894 
895  case 3:
896  aBBox.Merge( wxPoint( m_Start.x + radius, m_Start.y ) ); // right
897  break;
898  }
899 
900  if( directionCW )
901  ++quarter;
902  else
903  quarter += 3; // -1 modulo arithmetic
904 
905  quarter %= 4;
906  angle -= 900;
907  }
908 }
909 
910 void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
911 {
913  m_Poly.NewOutline();
914 
915  for ( auto p : aPoints )
916  {
917  m_Poly.Append( p.x, p.y );
918  }
919 }
920 
921 
922 const std::vector<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const
923 {
924  std::vector<wxPoint> rv;
925 
926  if( m_Poly.OutlineCount() )
927  {
928  if( m_Poly.COutline( 0 ).PointCount() )
929  {
930  for ( auto iter = m_Poly.CIterate(); iter; iter++ )
931  {
932  rv.push_back( wxPoint( iter->x, iter->y ) );
933  }
934  }
935  }
936 
937  return rv;
938 }
939 
940 
942 {
943  // return true if the polygonal shape is valid (has more than 2 points)
944  if( GetPolyShape().OutlineCount() == 0 )
945  return false;
946 
947  const SHAPE_LINE_CHAIN& outline = ((SHAPE_POLY_SET&)GetPolyShape()).Outline( 0 );
948 
949  return outline.PointCount() > 2;
950 }
951 
952 
954 {
955  // return the number of corners of the polygonal shape
956  // this shape is expected to be only one polygon without hole
957  if( GetPolyShape().OutlineCount() )
958  return GetPolyShape().VertexCount( 0 );
959 
960  return 0;
961 }
962 
963 
965 {
966  assert( aImage->Type() == PCB_LINE_T );
967 
968  std::swap( *((DRAWSEGMENT*) this), *((DRAWSEGMENT*) aImage) );
969 }
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:198
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:168
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:41
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.
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:472
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
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
#define MAX_DIST_IN_MM
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:189
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
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:170
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:175
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:151
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:184
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:457
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...