1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 #include <bitmaps.h>
26 #include <core/mirror.h>
27 #include <sch_painter.h>
28 #include <plotters/plotter.h>
29 #include <sch_line.h>
30 #include <sch_edit_frame.h>
31 #include <settings/color_settings.h>
32 #include <schematic.h>
33 #include <connection_graph.h>
34 #include <project/project_file.h>
35 #include <project/net_settings.h>
36 #include <trigo.h>
37 #include <board_item.h>
38 #include <advanced_config.h>
39 
SCH_LINE(const wxPoint & pos,int layer)40 SCH_LINE::SCH_LINE( const wxPoint& pos, int layer ) :
41     SCH_ITEM( nullptr, SCH_LINE_T )
42 {
43     m_start           = pos;
44     m_end             = pos;
45     m_stroke.SetWidth( 0 );
46     m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
47     m_stroke.SetColor( COLOR4D::UNSPECIFIED );
48 
49     switch( layer )
50     {
51     default:         m_layer = LAYER_NOTES; break;
52     case LAYER_WIRE: m_layer = LAYER_WIRE;  break;
53     case LAYER_BUS:  m_layer = LAYER_BUS;   break;
54     }
55 
56     if( layer == LAYER_NOTES )
57         m_startIsDangling = m_endIsDangling = true;
58     else
59         m_startIsDangling = m_endIsDangling = false;
60 
61     if( layer == LAYER_WIRE )
62         m_lastResolvedWidth = Mils2iu( DEFAULT_WIRE_WIDTH_MILS );
63     else if( layer == LAYER_BUS )
64         m_lastResolvedWidth = Mils2iu( DEFAULT_BUS_WIDTH_MILS );
65     else
66         m_lastResolvedWidth = Mils2iu( DEFAULT_LINE_WIDTH_MILS );
67 
68     m_lastResolvedLineStyle = GetDefaultStyle();
69     m_lastResolvedColor = COLOR4D::UNSPECIFIED;
70 }
71 
72 
SCH_LINE(const SCH_LINE & aLine)73 SCH_LINE::SCH_LINE( const SCH_LINE& aLine ) :
74     SCH_ITEM( aLine )
75 {
76     m_start = aLine.m_start;
77     m_end = aLine.m_end;
78     m_stroke = aLine.m_stroke;
79     m_startIsDangling = aLine.m_startIsDangling;
80     m_endIsDangling = aLine.m_endIsDangling;
81 
82     m_lastResolvedLineStyle = aLine.m_lastResolvedLineStyle;
83     m_lastResolvedWidth = aLine.m_lastResolvedWidth;
84     m_lastResolvedColor = aLine.m_lastResolvedColor;
85 }
86 
87 
Clone() const88 EDA_ITEM* SCH_LINE::Clone() const
89 {
90     return new SCH_LINE( *this );
91 }
92 
93 
94 /*
95  * Conversion between PLOT_DASH_TYPE values and style names displayed
96  */
97 const std::map<PLOT_DASH_TYPE, const char*> lineStyleNames{
98     { PLOT_DASH_TYPE::SOLID, "solid" },
99     { PLOT_DASH_TYPE::DASH, "dashed" },
100     { PLOT_DASH_TYPE::DASHDOT, "dash_dot" },
101     { PLOT_DASH_TYPE::DOT, "dotted" },
102 };
103 
104 
GetLineStyleName(PLOT_DASH_TYPE aStyle)105 const char* SCH_LINE::GetLineStyleName( PLOT_DASH_TYPE aStyle )
106 {
107     auto resultIt = lineStyleNames.find( aStyle );
108 
109     //legacy behavior is to default to dash if there is no name
110     return resultIt == lineStyleNames.end() ? lineStyleNames.find( PLOT_DASH_TYPE::DASH )->second :
111                                               resultIt->second;
112 }
113 
114 
GetLineStyleByName(const wxString & aStyleName)115 PLOT_DASH_TYPE SCH_LINE::GetLineStyleByName( const wxString& aStyleName )
116 {
117     PLOT_DASH_TYPE id = PLOT_DASH_TYPE::DEFAULT; // Default style id
118 
119     //find the name by value
120     auto resultIt = std::find_if( lineStyleNames.begin(), lineStyleNames.end(),
121                                   [aStyleName]( const auto& it )
122                                   {
123                                       return it.second == aStyleName;
124                                   } );
125 
126     if( resultIt != lineStyleNames.end() )
127         id = resultIt->first;
128 
129     return id;
130 }
131 
132 
Move(const wxPoint & aOffset)133 void SCH_LINE::Move( const wxPoint& aOffset )
134 {
135     if( aOffset != wxPoint( 0, 0 ) )
136     {
137         m_start += aOffset;
138         m_end += aOffset;
139         SetModified();
140     }
141 }
142 
143 
MoveStart(const wxPoint & aOffset)144 void SCH_LINE::MoveStart( const wxPoint& aOffset )
145 {
146     if( aOffset != wxPoint( 0, 0 ) )
147     {
148         m_start += aOffset;
149         SetModified();
150     }
151 }
152 
153 
MoveEnd(const wxPoint & aOffset)154 void SCH_LINE::MoveEnd( const wxPoint& aOffset )
155 {
156     if( aOffset != wxPoint( 0, 0 ) )
157     {
158         m_end += aOffset;
159         SetModified();
160     }
161 }
162 
163 
164 #if defined(DEBUG)
165 
Show(int nestLevel,std::ostream & os) const166 void SCH_LINE::Show( int nestLevel, std::ostream& os ) const
167 {
168     NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str()
169                                  << " layer=\"" << m_layer << '"'
170                                  << " startIsDangling=\"" << m_startIsDangling
171                                  << '"' << " endIsDangling=\""
172                                  << m_endIsDangling << '"' << ">"
173                                  << " <start" << m_start << "/>"
174                                  << " <end" << m_end << "/>" << "</"
175                                  << GetClass().Lower().mb_str() << ">\n";
176 }
177 
178 #endif
179 
180 
ViewGetLayers(int aLayers[],int & aCount) const181 void SCH_LINE::ViewGetLayers( int aLayers[], int& aCount ) const
182 {
183     aCount     = 3;
184     aLayers[0] = LAYER_DANGLING;
185     aLayers[1] = m_layer;
186     aLayers[2] = LAYER_SELECTION_SHADOWS;
187 }
188 
189 
GetBoundingBox() const190 const EDA_RECT SCH_LINE::GetBoundingBox() const
191 {
192     int      width = m_stroke.GetWidth() / 2;
193     int      extra = m_stroke.GetWidth() & 0x1;
194 
195     int      xmin = std::min( m_start.x, m_end.x ) - width;
196     int      ymin = std::min( m_start.y, m_end.y ) - width;
197 
198     int      xmax = std::max( m_start.x, m_end.x ) + width + extra;
199     int      ymax = std::max( m_start.y, m_end.y ) + width + extra;
200 
201     EDA_RECT ret( wxPoint( xmin, ymin ), wxSize( xmax - xmin, ymax - ymin ) );
202 
203     return ret;
204 }
205 
206 
GetLength() const207 double SCH_LINE::GetLength() const
208 {
209     return GetLineLength( m_start, m_end );
210 }
211 
212 
SetLineColor(const COLOR4D & aColor)213 void SCH_LINE::SetLineColor( const COLOR4D& aColor )
214 {
215     m_stroke.SetColor( aColor );
216 }
217 
218 
SetLineColor(const double r,const double g,const double b,const double a)219 void SCH_LINE::SetLineColor( const double r, const double g, const double b, const double a )
220 {
221     COLOR4D newColor(r, g, b, a);
222 
223     if( newColor == COLOR4D::UNSPECIFIED )
224     {
225         m_stroke.SetColor( COLOR4D::UNSPECIFIED );
226     }
227     else
228     {
229         // Eeschema does not allow alpha channel in colors
230         newColor.a = 1.0;
231         m_stroke.SetColor( newColor );
232     }
233 }
234 
235 
GetLineColor() const236 COLOR4D SCH_LINE::GetLineColor() const
237 {
238     if( m_stroke.GetColor() != COLOR4D::UNSPECIFIED )
239     {
240         m_lastResolvedColor = m_stroke.GetColor();
241     }
242     else if( IsConnectable() && !IsConnectivityDirty() )
243     {
244         NETCLASSPTR netclass = NetClass();
245 
246         if( netclass )
247             m_lastResolvedColor = netclass->GetSchematicColor();
248     }
249 
250     return m_lastResolvedColor;
251 }
252 
253 
GetDefaultStyle() const254 PLOT_DASH_TYPE SCH_LINE::GetDefaultStyle() const
255 {
256     if( IsGraphicLine() )
257         return PLOT_DASH_TYPE::DASH;
258 
259     return PLOT_DASH_TYPE::SOLID;
260 }
261 
262 
SetLineStyle(const int aStyleId)263 void SCH_LINE::SetLineStyle( const int aStyleId )
264 {
265     SetLineStyle( static_cast<PLOT_DASH_TYPE>( aStyleId ) );
266 }
267 
268 
SetLineStyle(const PLOT_DASH_TYPE aStyle)269 void SCH_LINE::SetLineStyle( const PLOT_DASH_TYPE aStyle )
270 {
271     if( aStyle == GetDefaultStyle() )
272         m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
273     else
274         m_stroke.SetPlotStyle( aStyle );
275 }
276 
277 
GetLineStyle() const278 PLOT_DASH_TYPE SCH_LINE::GetLineStyle() const
279 {
280     if( m_stroke.GetPlotStyle() != PLOT_DASH_TYPE::DEFAULT )
281         return m_stroke.GetPlotStyle();
282 
283     return GetDefaultStyle();
284 }
285 
286 
GetEffectiveLineStyle() const287 PLOT_DASH_TYPE SCH_LINE::GetEffectiveLineStyle() const
288 {
289     if( m_stroke.GetPlotStyle() != PLOT_DASH_TYPE::DEFAULT )
290     {
291         m_lastResolvedLineStyle = m_stroke.GetPlotStyle();
292     }
293     else if( IsConnectable() && !IsConnectivityDirty() )
294     {
295         NETCLASSPTR netclass = NetClass();
296 
297         if( netclass )
298             m_lastResolvedLineStyle = static_cast<PLOT_DASH_TYPE>( netclass->GetLineStyle() );
299     }
300 
301     return m_lastResolvedLineStyle;
302 }
303 
304 
SetLineWidth(const int aSize)305 void SCH_LINE::SetLineWidth( const int aSize )
306 {
307     m_stroke.SetWidth( aSize );
308 }
309 
310 
GetPenWidth() const311 int SCH_LINE::GetPenWidth() const
312 {
313     SCHEMATIC*  schematic = Schematic();
314     NETCLASSPTR netclass;
315 
316     switch ( m_layer )
317     {
318     default:
319         if( m_stroke.GetWidth() > 0 )
320             return m_stroke.GetWidth();
321 
322         if( schematic )
323             return schematic->Settings().m_DefaultLineWidth;
324 
325         return Mils2iu( DEFAULT_LINE_WIDTH_MILS );
326 
327     case LAYER_WIRE:
328         if( m_stroke.GetWidth() > 0 )
329         {
330             m_lastResolvedWidth = m_stroke.GetWidth();
331         }
332         else if( !IsConnectivityDirty() )
333         {
334             netclass = NetClass();
335 
336             if( !netclass && schematic  )
337                 netclass = schematic->Prj().GetProjectFile().NetSettings().m_NetClasses.GetDefault();
338 
339             if( netclass )
340                 m_lastResolvedWidth = netclass->GetWireWidth();
341         }
342 
343         return m_lastResolvedWidth;
344 
345     case LAYER_BUS:
346         if( m_stroke.GetWidth() > 0 )
347         {
348             m_lastResolvedWidth = m_stroke.GetWidth();
349         }
350         else if( !IsConnectivityDirty() )
351         {
352             netclass = NetClass();
353 
354             if( !netclass && schematic )
355                 netclass = schematic->Prj().GetProjectFile().NetSettings().m_NetClasses.GetDefault();
356 
357             if( netclass )
358                 m_lastResolvedWidth = netclass->GetBusWidth();
359         }
360 
361         return m_lastResolvedWidth;
362     }
363 }
364 
365 
Print(const RENDER_SETTINGS * aSettings,const wxPoint & offset)366 void SCH_LINE::Print( const RENDER_SETTINGS* aSettings, const wxPoint& offset )
367 {
368     wxDC*   DC = aSettings->GetPrintDC();
369     COLOR4D color = GetLineColor();
370 
371     if( color == COLOR4D::UNSPECIFIED )
372         color = aSettings->GetLayerColor( GetLayer() );
373 
374     wxPoint        start = m_start;
375     wxPoint        end = m_end;
376     PLOT_DASH_TYPE lineStyle = GetEffectiveLineStyle();
377     int            penWidth = std::max( GetPenWidth(), aSettings->GetDefaultPenWidth() );
378 
379     if( lineStyle <= PLOT_DASH_TYPE::FIRST_TYPE )
380     {
381         GRLine( nullptr, DC, start.x, start.y, end.x, end.y, penWidth, color );
382     }
383     else
384     {
385         EDA_RECT clip( (wxPoint) start, wxSize( end.x - start.x, end.y - start.y ) );
386         clip.Normalize();
387 
388         double theta = atan2( end.y - start.y, end.x - start.x );
389         double strokes[] = { 1.0, dash_gap_len( penWidth ), 1.0, dash_gap_len( penWidth ) };
390 
391         switch( lineStyle )
392         {
393         default:
394         case PLOT_DASH_TYPE::DASH:
395             strokes[0] = strokes[2] = dash_mark_len( penWidth );
396             break;
397         case PLOT_DASH_TYPE::DOT:
398             strokes[0] = strokes[2] = dot_mark_len( penWidth );
399             break;
400         case PLOT_DASH_TYPE::DASHDOT:
401             strokes[0] = dash_mark_len( penWidth );
402             strokes[2] = dot_mark_len( penWidth );
403             break;
404         }
405 
406         for( size_t i = 0; i < 10000; ++i )
407         {
408             // Calculations MUST be done in doubles to keep from accumulating rounding
409             // errors as we go.
410             wxPoint next( start.x + strokes[ i % 4 ] * cos( theta ),
411                           start.y + strokes[ i % 4 ] * sin( theta ) );
412 
413             // Drawing each segment can be done rounded to ints.
414             wxPoint segStart( KiROUND( start.x ), KiROUND( start.y ) );
415             wxPoint segEnd( KiROUND( next.x ), KiROUND( next.y ) );
416 
417             if( ClipLine( &clip, segStart.x, segStart.y, segEnd.x, segEnd.y ) )
418                 break;
419             else if( i % 2 == 0 )
420                 GRLine( nullptr, DC, segStart.x, segStart.y, segEnd.x, segEnd.y, penWidth, color );
421 
422             start = next;
423         }
424     }
425 }
426 
427 
MirrorVertically(int aCenter)428 void SCH_LINE::MirrorVertically( int aCenter )
429 {
430     if( m_flags & STARTPOINT )
431         MIRROR( m_start.y, aCenter );
432 
433     if( m_flags & ENDPOINT )
434         MIRROR( m_end.y,   aCenter );
435 }
436 
437 
MirrorHorizontally(int aCenter)438 void SCH_LINE::MirrorHorizontally( int aCenter )
439 {
440     if( m_flags & STARTPOINT )
441         MIRROR( m_start.x, aCenter );
442 
443     if( m_flags & ENDPOINT )
444         MIRROR( m_end.x,   aCenter );
445 }
446 
447 
Rotate(const wxPoint & aCenter)448 void SCH_LINE::Rotate( const wxPoint& aCenter )
449 {
450     if( m_flags & STARTPOINT )
451         RotatePoint( &m_start, aCenter, 900 );
452 
453     if( m_flags & ENDPOINT )
454         RotatePoint( &m_end, aCenter, 900 );
455 }
456 
457 
RotateStart(const wxPoint & aCenter)458 void SCH_LINE::RotateStart( const wxPoint& aCenter )
459 {
460     RotatePoint( &m_start, aCenter, 900 );
461 }
462 
463 
RotateEnd(const wxPoint & aCenter)464 void SCH_LINE::RotateEnd( const wxPoint& aCenter )
465 {
466     RotatePoint( &m_end, aCenter, 900 );
467 }
468 
469 
GetAngleFrom(const wxPoint & aPoint) const470 int SCH_LINE::GetAngleFrom( const wxPoint& aPoint ) const
471 {
472     wxPoint vec;
473 
474     if( aPoint == m_start )
475         vec = m_end - aPoint;
476     else
477         vec = m_start - aPoint;
478 
479     return KiROUND( ArcTangente( vec.y, vec.x ) );
480 }
481 
482 
GetReverseAngleFrom(const wxPoint & aPoint) const483 int SCH_LINE::GetReverseAngleFrom( const wxPoint& aPoint ) const
484 {
485     wxPoint vec;
486 
487     if( aPoint == m_end )
488         vec = m_start - aPoint;
489     else
490         vec = m_end - aPoint;
491 
492     return KiROUND( ArcTangente( vec.y, vec.x ) );
493 }
494 
495 
IsParallel(const SCH_LINE * aLine) const496 bool SCH_LINE::IsParallel( const SCH_LINE* aLine ) const
497 {
498     wxCHECK_MSG( aLine != nullptr && aLine->Type() == SCH_LINE_T, false,
499                  wxT( "Cannot test line segment for overlap." ) );
500 
501     wxPoint firstSeg   = m_end - m_start;
502     wxPoint secondSeg = aLine->m_end - aLine->m_start;
503 
504     // Use long long here to avoid overflow in calculations
505     return !( (long long) firstSeg.x * secondSeg.y - (long long) firstSeg.y * secondSeg.x );
506 }
507 
508 
MergeOverlap(SCH_SCREEN * aScreen,SCH_LINE * aLine,bool aCheckJunctions)509 SCH_LINE* SCH_LINE::MergeOverlap( SCH_SCREEN* aScreen, SCH_LINE* aLine, bool aCheckJunctions )
510 {
511     auto less =
512             []( const wxPoint& lhs, const wxPoint& rhs ) -> bool
513             {
514                 if( lhs.x == rhs.x )
515                     return lhs.y < rhs.y;
516 
517                 return lhs.x < rhs.x;
518             };
519 
520     wxCHECK_MSG( aLine != nullptr && aLine->Type() == SCH_LINE_T, nullptr,
521                  wxT( "Cannot test line segment for overlap." ) );
522 
523     if( this == aLine || GetLayer() != aLine->GetLayer() )
524         return nullptr;
525 
526     wxPoint leftmost_start = aLine->m_start;
527     wxPoint leftmost_end = aLine->m_end;
528 
529     wxPoint rightmost_start = m_start;
530     wxPoint rightmost_end = m_end;
531 
532     // We place the start to the left and below the end of both lines
533     if( leftmost_start != std::min( { leftmost_start, leftmost_end }, less ) )
534         std::swap( leftmost_start, leftmost_end );
535     if( rightmost_start != std::min( { rightmost_start, rightmost_end }, less ) )
536         std::swap( rightmost_start, rightmost_end );
537 
538     // - leftmost is the line that starts farthest to the left
539     // - other is the line that is _not_ leftmost
540     // - rightmost is the line that ends farthest to the right.  This may or may not be 'other'
541     //      as the second line may be completely covered by the first.
542     if( less( rightmost_start, leftmost_start ) )
543     {
544         std::swap( leftmost_start, rightmost_start );
545         std::swap( leftmost_end, rightmost_end );
546     }
547 
548     wxPoint other_start = rightmost_start;
549     wxPoint other_end = rightmost_end;
550 
551     if( less( rightmost_end, leftmost_end ) )
552     {
553         rightmost_start = leftmost_start;
554         rightmost_end = leftmost_end;
555     }
556 
557     // If we end one before the beginning of the other, no overlap is possible
558     if( less( leftmost_end, other_start ) )
559     {
560         return nullptr;
561     }
562 
563     // Search for a common end:
564     if( ( leftmost_start == other_start ) && ( leftmost_end == other_end ) )  // Trivial case
565     {
566         SCH_LINE* ret = new SCH_LINE( *aLine );
567         ret->SetStartPoint( leftmost_start );
568         ret->SetEndPoint( leftmost_end );
569         ret->SetConnectivityDirty( true );
570 
571         if( IsSelected() || aLine->IsSelected() )
572             ret->SetSelected();
573 
574         return ret;
575     }
576 
577     bool colinear = false;
578 
579     /* Test alignment: */
580     if( ( leftmost_start.y == leftmost_end.y ) &&
581         ( other_start.y == other_end.y ) )       // Horizontal segment
582     {
583         colinear = ( leftmost_start.y == other_start.y );
584     }
585     else if( ( leftmost_start.x == leftmost_end.x ) &&
586              ( other_start.x == other_end.x ) )  // Vertical segment
587     {
588         colinear = ( leftmost_start.x == other_start.x );
589     }
590     else
591     {
592         // We use long long here to avoid overflow -- it enforces promotion
593         // The slope of the left-most line is dy/dx.  Then we check that the slope from the
594         // left most start to the right most start is the same as well as the slope from the
595         // left most start to right most end.
596         long long dx = leftmost_end.x - leftmost_start.x;
597         long long dy = leftmost_end.y - leftmost_start.y;
598         colinear = ( ( ( other_start.y - leftmost_start.y ) * dx ==
599                        ( other_start.x - leftmost_start.x ) * dy ) &&
600                      ( ( other_end.y - leftmost_start.y ) * dx ==
601                        ( other_end.x - leftmost_start.x ) * dy ) );
602     }
603 
604     if( !colinear )
605         return nullptr;
606 
607     // We either have a true overlap or colinear touching segments.  We always want to merge
608     // the former, but the later only get merged if there no junction at the touch point.
609 
610     bool touching = leftmost_end == rightmost_start;
611 
612     if( touching && aCheckJunctions && aScreen->IsJunction( leftmost_end ) )
613         return nullptr;
614 
615     // Make a new segment that merges the 2 segments
616     leftmost_end = rightmost_end;
617 
618     SCH_LINE* ret = new SCH_LINE( *aLine );
619     ret->SetStartPoint( leftmost_start );
620     ret->SetEndPoint( leftmost_end );
621     ret->SetConnectivityDirty( true );
622 
623     if( IsSelected() || aLine->IsSelected() )
624         ret->SetSelected();
625 
626     return ret;
627 }
628 
629 
GetEndPoints(std::vector<DANGLING_END_ITEM> & aItemList)630 void SCH_LINE::GetEndPoints( std::vector <DANGLING_END_ITEM>& aItemList )
631 {
632     if( IsConnectable() )
633     {
634         aItemList.emplace_back( IsBus() ? BUS_END : WIRE_END, this, m_start );
635         aItemList.emplace_back( IsBus() ? BUS_END : WIRE_END, this, m_end );
636     }
637 }
638 
639 
UpdateDanglingState(std::vector<DANGLING_END_ITEM> & aItemList,const SCH_SHEET_PATH * aPath)640 bool SCH_LINE::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
641                                     const SCH_SHEET_PATH* aPath )
642 {
643     if( IsConnectable() )
644     {
645         bool previousStartState = m_startIsDangling;
646         bool previousEndState = m_endIsDangling;
647 
648         m_startIsDangling = m_endIsDangling = true;
649 
650         for( DANGLING_END_ITEM item : aItemList )
651         {
652             if( item.GetItem() == this )
653                 continue;
654 
655             if( ( IsWire() && item.GetType() != BUS_END && item.GetType() != BUS_ENTRY_END )
656                 || ( IsBus() && item.GetType() != WIRE_END && item.GetType() != PIN_END ) )
657             {
658                 if( m_start == item.GetPosition() )
659                     m_startIsDangling = false;
660 
661                 if( m_end == item.GetPosition() )
662                     m_endIsDangling = false;
663 
664                 if( !m_startIsDangling && !m_endIsDangling )
665                     break;
666             }
667         }
668 
669         // We only use the bus dangling state for automatic line starting, so we don't care if it
670         // has changed or not (and returning true will result in extra work)
671         if( IsBus() )
672             return false;
673 
674         return previousStartState != m_startIsDangling || previousEndState != m_endIsDangling;
675     }
676 
677     return false;
678 }
679 
680 
IsConnectable() const681 bool SCH_LINE::IsConnectable() const
682 {
683     if( m_layer == LAYER_WIRE || m_layer == LAYER_BUS )
684         return true;
685 
686     return false;
687 }
688 
689 
CanConnect(const SCH_ITEM * aItem) const690 bool SCH_LINE::CanConnect( const SCH_ITEM* aItem ) const
691 {
692     if( m_layer == LAYER_WIRE )
693     {
694         switch( aItem->Type() )
695         {
696         case SCH_JUNCTION_T:
697         case SCH_NO_CONNECT_T:
698         case SCH_LABEL_T:
699         case SCH_GLOBAL_LABEL_T:
700         case SCH_HIER_LABEL_T:
701         case SCH_BUS_WIRE_ENTRY_T:
702         case SCH_SYMBOL_T:
703         case SCH_SHEET_T:
704         case SCH_SHEET_PIN_T:
705             return true;
706         default:
707             break;
708         }
709     }
710     else if( m_layer == LAYER_BUS )
711     {
712         switch( aItem->Type() )
713         {
714         case SCH_JUNCTION_T:
715         case SCH_LABEL_T:
716         case SCH_GLOBAL_LABEL_T:
717         case SCH_HIER_LABEL_T:
718         case SCH_BUS_WIRE_ENTRY_T:
719         case SCH_SHEET_T:
720         case SCH_SHEET_PIN_T:
721             return true;
722         default:
723             break;
724         }
725     }
726 
727     return aItem->GetLayer() == m_layer;
728 }
729 
730 
GetConnectionPoints() const731 std::vector<wxPoint> SCH_LINE::GetConnectionPoints() const
732 {
733     return { m_start, m_end };
734 }
735 
736 
ConnectionPropagatesTo(const EDA_ITEM * aItem) const737 bool SCH_LINE::ConnectionPropagatesTo( const EDA_ITEM* aItem ) const
738 {
739     switch( aItem->Type() )
740     {
741     case SCH_LINE_T:
742         return IsBus() == static_cast<const SCH_LINE*>( aItem )->IsBus();
743 
744     default:
745         return true;
746     }
747 }
748 
749 
GetSelectedPoints(std::vector<wxPoint> & aPoints) const750 void SCH_LINE::GetSelectedPoints( std::vector< wxPoint >& aPoints ) const
751 {
752     if( m_flags & STARTPOINT )
753         aPoints.push_back( m_start );
754 
755     if( m_flags & ENDPOINT )
756         aPoints.push_back( m_end );
757 }
758 
759 
GetSelectMenuText(EDA_UNITS aUnits) const760 wxString SCH_LINE::GetSelectMenuText( EDA_UNITS aUnits ) const
761 {
762     wxString txtfmt, orient;
763 
764     if( m_start.x == m_end.x )
765     {
766         switch( m_layer )
767         {
768         case LAYER_WIRE:  txtfmt = _( "Vertical Wire, length %s" );         break;
769         case LAYER_BUS:   txtfmt = _( "Vertical Bus, length %s" );          break;
770         default:          txtfmt = _( "Vertical Graphic Line, length %s" ); break;
771         }
772     }
773     else if( m_start.y == m_end.y )
774     {
775         switch( m_layer )
776         {
777         case LAYER_WIRE:  txtfmt = _( "Horizontal Wire, length %s" );         break;
778         case LAYER_BUS:   txtfmt = _( "Horizontal Bus, length %s" );          break;
779         default:          txtfmt = _( "Horizontal Graphic Line, length %s" ); break;
780         }
781     }
782     else
783     {
784         switch( m_layer )
785         {
786         case LAYER_WIRE:  txtfmt = _( "Wire, length %s" );         break;
787         case LAYER_BUS:   txtfmt = _( "Bus, length %s" );          break;
788         default:          txtfmt = _( "Graphic Line, length %s" ); break;
789         }
790     }
791 
792     return wxString::Format( txtfmt,
793                              MessageTextFromValue( aUnits, EuclideanNorm( m_start - m_end ) ) );
794 }
795 
796 
GetMenuImage() const797 BITMAPS SCH_LINE::GetMenuImage() const
798 {
799     if( m_layer == LAYER_NOTES )
800         return BITMAPS::add_dashed_line;
801     else if( m_layer == LAYER_WIRE )
802         return BITMAPS::add_line;
803 
804     return BITMAPS::add_bus;
805 }
806 
807 
operator <(const SCH_ITEM & aItem) const808 bool SCH_LINE::operator <( const SCH_ITEM& aItem ) const
809 {
810     if( Type() != aItem.Type() )
811         return Type() < aItem.Type();
812 
813     const SCH_LINE* line = static_cast<const SCH_LINE*>( &aItem );
814 
815     if( GetLayer() != line->GetLayer() )
816         return GetLayer() < line->GetLayer();
817 
818     if( GetStartPoint().x != line->GetStartPoint().x )
819         return GetStartPoint().x < line->GetStartPoint().x;
820 
821     if( GetStartPoint().y != line->GetStartPoint().y )
822         return GetStartPoint().y < line->GetStartPoint().y;
823 
824     if( GetEndPoint().x != line->GetEndPoint().x )
825         return GetEndPoint().x < line->GetEndPoint().x;
826 
827     return GetEndPoint().y < line->GetEndPoint().y;
828 }
829 
830 
HitTest(const wxPoint & aPosition,int aAccuracy) const831 bool SCH_LINE::HitTest( const wxPoint& aPosition, int aAccuracy ) const
832 {
833     // Performance enhancement for connection-building
834     if( aPosition == m_start || aPosition == m_end )
835         return true;
836 
837     if( aAccuracy >= 0 )
838         aAccuracy += GetPenWidth() / 2;
839     else
840         aAccuracy = abs( aAccuracy );
841 
842     if( TestSegmentHit( aPosition, m_start, m_end, aAccuracy ) )
843         return true;
844 
845     aAccuracy += Mils2iu( DANGLING_SYMBOL_SIZE );
846 
847     return ( EuclideanNorm( aPosition - m_start ) < aAccuracy
848             || EuclideanNorm( aPosition - m_end ) < aAccuracy );
849 }
850 
851 
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const852 bool SCH_LINE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
853 {
854     if( m_flags & (STRUCT_DELETED | SKIP_STRUCT ) )
855         return false;
856 
857     EDA_RECT rect = aRect;
858 
859     if ( aAccuracy )
860         rect.Inflate( aAccuracy );
861 
862     if( aContained )
863         return rect.Contains( m_start ) && rect.Contains( m_end );
864 
865     return rect.Intersects( m_start, m_end );
866 }
867 
868 
SwapData(SCH_ITEM * aItem)869 void SCH_LINE::SwapData( SCH_ITEM* aItem )
870 {
871     SCH_LINE* item = (SCH_LINE*) aItem;
872 
873     std::swap( m_layer, item->m_layer );
874 
875     std::swap( m_start, item->m_start );
876     std::swap( m_end, item->m_end );
877     std::swap( m_startIsDangling, item->m_startIsDangling );
878     std::swap( m_endIsDangling, item->m_endIsDangling );
879     std::swap( m_stroke, item->m_stroke );
880 }
881 
882 
doIsConnected(const wxPoint & aPosition) const883 bool SCH_LINE::doIsConnected( const wxPoint& aPosition ) const
884 {
885     if( m_layer != LAYER_WIRE && m_layer != LAYER_BUS )
886         return false;
887 
888     return IsEndPoint( aPosition );
889 }
890 
891 
Plot(PLOTTER * aPlotter) const892 void SCH_LINE::Plot( PLOTTER* aPlotter ) const
893 {
894     auto*   settings = static_cast<KIGFX::SCH_RENDER_SETTINGS*>( aPlotter->RenderSettings() );
895     int     penWidth = std::max( GetPenWidth(), settings->GetMinPenWidth() );
896     COLOR4D color = GetLineColor();
897 
898     if( color == COLOR4D::UNSPECIFIED )
899         color = settings->GetLayerColor( GetLayer() );
900 
901     aPlotter->SetColor( color );
902 
903     aPlotter->SetCurrentLineWidth( penWidth );
904     aPlotter->SetDash( GetEffectiveLineStyle() );
905 
906     aPlotter->MoveTo( m_start );
907     aPlotter->FinishTo( m_end );
908 
909     aPlotter->SetDash( PLOT_DASH_TYPE::SOLID );
910 }
911 
912 
SetPosition(const wxPoint & aPosition)913 void SCH_LINE::SetPosition( const wxPoint& aPosition )
914 {
915     m_end = m_end - ( m_start - aPosition );
916     m_start = aPosition;
917 }
918 
919 
GetMsgPanelInfo(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList)920 void SCH_LINE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
921 {
922     wxString msg;
923 
924     switch( GetLayer() )
925     {
926     case LAYER_WIRE: msg = _( "Wire" );      break;
927     case LAYER_BUS:  msg = _( "Bus" );       break;
928     default:         msg = _( "Graphical" ); break;
929     }
930 
931     aList.emplace_back( _( "Line Type" ), msg );
932 
933     if( GetLineStyle() != GetEffectiveLineStyle() )
934         msg = _( "from netclass" );
935     else
936         msg = GetLineStyleName( GetLineStyle() );
937 
938     aList.emplace_back( _( "Line Style" ), msg );
939 
940     SCH_CONNECTION* conn = nullptr;
941 
942     if( !IsConnectivityDirty() && dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
943         conn = Connection();
944 
945     if( conn )
946     {
947         conn->AppendInfoToMsgPanel( aList );
948 
949         if( !conn->IsBus() )
950         {
951             NET_SETTINGS& netSettings = Schematic()->Prj().GetProjectFile().NetSettings();
952             wxString netname = conn->Name();
953             wxString netclassName = netSettings.m_NetClasses.GetDefaultPtr()->GetName();
954 
955             if( netSettings.m_NetClassAssignments.count( netname ) )
956                 netclassName = netSettings.m_NetClassAssignments[ netname ];
957 
958             aList.emplace_back( _( "Assigned Netclass" ), netclassName );
959         }
960     }
961 }
962 
963 
IsGraphicLine() const964 bool SCH_LINE::IsGraphicLine() const
965 {
966     return ( GetLayer() == LAYER_NOTES );
967 }
968 
969 
IsWire() const970 bool SCH_LINE::IsWire() const
971 {
972     return ( GetLayer() == LAYER_WIRE );
973 }
974 
IsBus() const975 bool SCH_LINE::IsBus() const
976 {
977     return ( GetLayer() == LAYER_BUS );
978 }
979 
980 
UsesDefaultStroke() const981 bool SCH_LINE::UsesDefaultStroke() const
982 {
983     return m_stroke.GetWidth() == 0 && m_stroke.GetColor() == COLOR4D::UNSPECIFIED
984             && ( m_stroke.GetPlotStyle() == GetDefaultStyle()
985             || m_stroke.GetPlotStyle() == PLOT_DASH_TYPE::DEFAULT );
986 }
987