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