1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6 * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
7 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27 #include <pcb_base_frame.h>
28 #include <connectivity/connectivity_data.h>
29 #include <board.h>
30 #include <board_design_settings.h>
31 #include <convert_basic_shapes_to_polygon.h>
32 #include <pcb_track.h>
33 #include <base_units.h>
34 #include <bitmaps.h>
35 #include <string_utils.h>
36 #include <view/view.h>
37 #include <settings/color_settings.h>
38 #include <settings/settings_manager.h>
39 #include <i18n_utility.h>
40 #include <geometry/seg.h>
41 #include <geometry/shape_segment.h>
42 #include <geometry/shape_circle.h>
43 #include <geometry/shape_arc.h>
44 #include <drc/drc_engine.h>
45 #include <pcb_painter.h>
46 #include <trigo.h>
47
48 using KIGFX::PCB_PAINTER;
49 using KIGFX::PCB_RENDER_SETTINGS;
50
PCB_TRACK(BOARD_ITEM * aParent,KICAD_T idtype)51 PCB_TRACK::PCB_TRACK( BOARD_ITEM* aParent, KICAD_T idtype ) :
52 BOARD_CONNECTED_ITEM( aParent, idtype )
53 {
54 m_Width = Millimeter2iu( 0.2 ); // Gives a reasonable default width
55 }
56
57
Clone() const58 EDA_ITEM* PCB_TRACK::Clone() const
59 {
60 return new PCB_TRACK( *this );
61 }
62
63
PCB_ARC(BOARD_ITEM * aParent,const SHAPE_ARC * aArc)64 PCB_ARC::PCB_ARC( BOARD_ITEM* aParent, const SHAPE_ARC* aArc ) :
65 PCB_TRACK( aParent, PCB_ARC_T )
66 {
67 m_Start = wxPoint( aArc->GetP0() );
68 m_End = wxPoint( aArc->GetP1() );
69 m_Mid = wxPoint( aArc->GetArcMid() );
70 }
71
72
Clone() const73 EDA_ITEM* PCB_ARC::Clone() const
74 {
75 return new PCB_ARC( *this );
76 }
77
78
PCB_VIA(BOARD_ITEM * aParent)79 PCB_VIA::PCB_VIA( BOARD_ITEM* aParent ) :
80 PCB_TRACK( aParent, PCB_VIA_T )
81 {
82 SetViaType( VIATYPE::THROUGH );
83 m_bottomLayer = B_Cu;
84 SetDrillDefault();
85 m_removeUnconnectedLayer = false;
86 m_keepTopBottomLayer = true;
87 m_isFree = false;
88 }
89
90
Clone() const91 EDA_ITEM* PCB_VIA::Clone() const
92 {
93 return new PCB_VIA( *this );
94 }
95
96
GetSelectMenuText(EDA_UNITS aUnits) const97 wxString PCB_VIA::GetSelectMenuText( EDA_UNITS aUnits ) const
98 {
99 wxString formatStr;
100
101 switch( GetViaType() )
102 {
103 case VIATYPE::BLIND_BURIED: formatStr = _( "Blind/Buried Via %s on %s" ); break;
104 case VIATYPE::MICROVIA: formatStr = _( "Micro Via %s on %s" ); break;
105 default: formatStr = _( "Via %s on %s" ); break;
106 }
107
108 return wxString::Format( formatStr,
109 GetNetnameMsg(),
110 layerMaskDescribe() );
111 }
112
113
GetMenuImage() const114 BITMAPS PCB_VIA::GetMenuImage() const
115 {
116 return BITMAPS::via;
117 }
118
119
ApproxCollinear(const PCB_TRACK & aTrack)120 bool PCB_TRACK::ApproxCollinear( const PCB_TRACK& aTrack )
121 {
122 SEG a( m_Start, m_End );
123 SEG b( aTrack.GetStart(), aTrack.GetEnd() );
124 return a.ApproxCollinear( b );
125 }
126
127
GetLocalClearance(wxString * aSource) const128 int PCB_TRACK::GetLocalClearance( wxString* aSource ) const
129 {
130 // Not currently implemented
131 return 0;
132 }
133
134
GetMinAnnulus(PCB_LAYER_ID aLayer,wxString * aSource) const135 int PCB_VIA::GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const
136 {
137 if( !FlashLayer( aLayer ) )
138 {
139 if( aSource )
140 *aSource = _( "removed annular ring" );
141
142 return 0;
143 }
144
145 DRC_CONSTRAINT constraint;
146
147 if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
148 {
149 BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
150
151 constraint = bds.m_DRCEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, this, nullptr, aLayer );
152 }
153
154 if( constraint.Value().HasMin() )
155 {
156 if( aSource )
157 *aSource = constraint.GetName();
158
159 return constraint.Value().Min();
160 }
161
162 return 0;
163 }
164
165
GetDrillValue() const166 int PCB_VIA::GetDrillValue() const
167 {
168 if( m_drill > 0 ) // Use the specific value.
169 return m_drill;
170
171 // Use the default value from the Netclass
172 NETCLASS* netclass = GetNetClass();
173
174 if( GetViaType() == VIATYPE::MICROVIA )
175 return netclass->GetuViaDrill();
176
177 return netclass->GetViaDrill();
178 }
179
180
IsPointOnEnds(const wxPoint & point,int min_dist) const181 EDA_ITEM_FLAGS PCB_TRACK::IsPointOnEnds( const wxPoint& point, int min_dist ) const
182 {
183 EDA_ITEM_FLAGS result = 0;
184
185 if( min_dist < 0 )
186 min_dist = m_Width / 2;
187
188 if( min_dist == 0 )
189 {
190 if( m_Start == point )
191 result |= STARTPOINT;
192
193 if( m_End == point )
194 result |= ENDPOINT;
195 }
196 else
197 {
198 double dist = GetLineLength( m_Start, point );
199
200 if( min_dist >= KiROUND( dist ) )
201 result |= STARTPOINT;
202
203 dist = GetLineLength( m_End, point );
204
205 if( min_dist >= KiROUND( dist ) )
206 result |= ENDPOINT;
207 }
208
209 return result;
210 }
211
212
GetBoundingBox() const213 const EDA_RECT PCB_TRACK::GetBoundingBox() const
214 {
215 // end of track is round, this is its radius, rounded up
216 int radius = ( m_Width + 1 ) / 2;
217 int ymax, xmax, ymin, xmin;
218
219 if( Type() == PCB_VIA_T )
220 {
221 ymax = m_Start.y;
222 xmax = m_Start.x;
223
224 ymin = m_Start.y;
225 xmin = m_Start.x;
226 }
227 else if( Type() == PCB_ARC_T )
228 {
229 std::shared_ptr<SHAPE> arc = GetEffectiveShape();
230 auto bbox = arc->BBox();
231
232 xmin = bbox.GetLeft();
233 xmax = bbox.GetRight();
234 ymin = bbox.GetTop();
235 ymax = bbox.GetBottom();
236 }
237 else
238 {
239 ymax = std::max( m_Start.y, m_End.y );
240 xmax = std::max( m_Start.x, m_End.x );
241
242 ymin = std::min( m_Start.y, m_End.y );
243 xmin = std::min( m_Start.x, m_End.x );
244 }
245
246 ymax += radius;
247 xmax += radius;
248
249 ymin -= radius;
250 xmin -= radius;
251
252 // return a rectangle which is [pos,dim) in nature. therefore the +1
253 EDA_RECT ret( wxPoint( xmin, ymin ), wxSize( xmax - xmin + 1, ymax - ymin + 1 ) );
254
255 return ret;
256 }
257
258
GetLength() const259 double PCB_TRACK::GetLength() const
260 {
261 return GetLineLength( m_Start, m_End );
262 }
263
264
Rotate(const wxPoint & aRotCentre,double aAngle)265 void PCB_TRACK::Rotate( const wxPoint& aRotCentre, double aAngle )
266 {
267 RotatePoint( &m_Start, aRotCentre, aAngle );
268 RotatePoint( &m_End, aRotCentre, aAngle );
269 }
270
271
Rotate(const wxPoint & aRotCentre,double aAngle)272 void PCB_ARC::Rotate( const wxPoint& aRotCentre, double aAngle )
273 {
274 RotatePoint( &m_Start, aRotCentre, aAngle );
275 RotatePoint( &m_End, aRotCentre, aAngle );
276 RotatePoint( &m_Mid, aRotCentre, aAngle );
277 }
278
279
Flip(const wxPoint & aCentre,bool aFlipLeftRight)280 void PCB_TRACK::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
281 {
282 if( aFlipLeftRight )
283 {
284 m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
285 m_End.x = aCentre.x - ( m_End.x - aCentre.x );
286 }
287 else
288 {
289 m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
290 m_End.y = aCentre.y - ( m_End.y - aCentre.y );
291 }
292
293 int copperLayerCount = GetBoard()->GetCopperLayerCount();
294 SetLayer( FlipLayer( GetLayer(), copperLayerCount ) );
295 }
296
297
Flip(const wxPoint & aCentre,bool aFlipLeftRight)298 void PCB_ARC::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
299 {
300 if( aFlipLeftRight )
301 {
302 m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
303 m_End.x = aCentre.x - ( m_End.x - aCentre.x );
304 m_Mid.x = aCentre.x - ( m_Mid.x - aCentre.x );
305 }
306 else
307 {
308 m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
309 m_End.y = aCentre.y - ( m_End.y - aCentre.y );
310 m_Mid.y = aCentre.y - ( m_Mid.y - aCentre.y );
311 }
312
313 int copperLayerCount = GetBoard()->GetCopperLayerCount();
314 SetLayer( FlipLayer( GetLayer(), copperLayerCount ) );
315 }
316
317
Flip(const wxPoint & aCentre,bool aFlipLeftRight)318 void PCB_VIA::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
319 {
320 if( aFlipLeftRight )
321 {
322 m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
323 m_End.x = aCentre.x - ( m_End.x - aCentre.x );
324 }
325 else
326 {
327 m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
328 m_End.y = aCentre.y - ( m_End.y - aCentre.y );
329 }
330
331 if( GetViaType() != VIATYPE::THROUGH )
332 {
333 int copperLayerCount = GetBoard()->GetCopperLayerCount();
334 PCB_LAYER_ID top_layer;
335 PCB_LAYER_ID bottom_layer;
336 LayerPair( &top_layer, &bottom_layer );
337 top_layer = FlipLayer( top_layer, copperLayerCount );
338 bottom_layer = FlipLayer( bottom_layer, copperLayerCount );
339 SetLayerPair( top_layer, bottom_layer );
340 }
341 }
342
343
344 // see class_track.h
Visit(INSPECTOR inspector,void * testData,const KICAD_T scanTypes[])345 SEARCH_RESULT PCB_TRACK::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
346 {
347 KICAD_T stype = *scanTypes;
348
349 // If caller wants to inspect my type
350 if( stype == Type() )
351 {
352 if( SEARCH_RESULT::QUIT == inspector( this, testData ) )
353 return SEARCH_RESULT::QUIT;
354 }
355
356 return SEARCH_RESULT::CONTINUE;
357 }
358
359
IsOnLayer(PCB_LAYER_ID layer_number) const360 bool PCB_VIA::IsOnLayer( PCB_LAYER_ID layer_number ) const
361 {
362 PCB_LAYER_ID bottom_layer, top_layer;
363
364 LayerPair( &top_layer, &bottom_layer );
365
366 wxASSERT( top_layer <= bottom_layer );
367
368 if( top_layer <= layer_number && layer_number <= bottom_layer )
369 return true;
370 else
371 return false;
372 }
373
374
GetLayerSet() const375 LSET PCB_VIA::GetLayerSet() const
376 {
377 if( GetViaType() == VIATYPE::THROUGH )
378 return LSET::AllCuMask();
379
380 // VIA_BLIND_BURIED or VIA_MICRVIA:
381
382 LSET layermask;
383
384 wxASSERT( m_layer <= m_bottomLayer );
385
386 // PCB_LAYER_IDs are numbered from front to back, this is top to bottom.
387 for( LAYER_NUM id = m_layer; id <= m_bottomLayer; ++id )
388 {
389 layermask.set( id );
390 }
391
392 return layermask;
393 }
394
395
SetLayerSet(LSET aLayerSet)396 void PCB_VIA::SetLayerSet( LSET aLayerSet )
397 {
398 bool first = true;
399
400 for( PCB_LAYER_ID layer : aLayerSet.Seq() )
401 {
402 if( first )
403 {
404 m_layer = layer;
405 first = false;
406 }
407
408 m_bottomLayer = layer;
409 }
410 }
411
412
SetLayerPair(PCB_LAYER_ID aTopLayer,PCB_LAYER_ID aBottomLayer)413 void PCB_VIA::SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer )
414 {
415
416 m_layer = aTopLayer;
417 m_bottomLayer = aBottomLayer;
418 SanitizeLayers();
419 }
420
421
SetTopLayer(PCB_LAYER_ID aLayer)422 void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer )
423 {
424 m_layer = aLayer;
425 }
426
427
SetBottomLayer(PCB_LAYER_ID aLayer)428 void PCB_VIA::SetBottomLayer( PCB_LAYER_ID aLayer )
429 {
430 m_bottomLayer = aLayer;
431 }
432
433
LayerPair(PCB_LAYER_ID * top_layer,PCB_LAYER_ID * bottom_layer) const434 void PCB_VIA::LayerPair( PCB_LAYER_ID* top_layer, PCB_LAYER_ID* bottom_layer ) const
435 {
436 PCB_LAYER_ID t_layer = F_Cu;
437 PCB_LAYER_ID b_layer = B_Cu;
438
439 if( GetViaType() != VIATYPE::THROUGH )
440 {
441 b_layer = m_bottomLayer;
442 t_layer = m_layer;
443
444 if( b_layer < t_layer )
445 std::swap( b_layer, t_layer );
446 }
447
448 if( top_layer )
449 *top_layer = t_layer;
450
451 if( bottom_layer )
452 *bottom_layer = b_layer;
453 }
454
455
TopLayer() const456 PCB_LAYER_ID PCB_VIA::TopLayer() const
457 {
458 return m_layer;
459 }
460
461
BottomLayer() const462 PCB_LAYER_ID PCB_VIA::BottomLayer() const
463 {
464 return m_bottomLayer;
465 }
466
467
SanitizeLayers()468 void PCB_VIA::SanitizeLayers()
469 {
470 if( GetViaType() == VIATYPE::THROUGH )
471 {
472 m_layer = F_Cu;
473 m_bottomLayer = B_Cu;
474 }
475
476 if( m_bottomLayer < m_layer )
477 std::swap( m_bottomLayer, m_layer );
478 }
479
480
FlashLayer(LSET aLayers) const481 bool PCB_VIA::FlashLayer( LSET aLayers ) const
482 {
483 for( auto layer : aLayers.Seq() )
484 {
485 if( FlashLayer( layer ) )
486 return true;
487 }
488
489 return false;
490 }
491
492
FlashLayer(int aLayer) const493 bool PCB_VIA::FlashLayer( int aLayer ) const
494 {
495 std::vector<KICAD_T> types
496 { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_ZONE_T, PCB_FP_ZONE_T };
497
498 // Return the "normal" shape if the caller doesn't specify a particular layer
499 if( aLayer == UNDEFINED_LAYER )
500 return true;
501
502 const BOARD* board = GetBoard();
503
504 if( !board )
505 return false;
506
507 if( !IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) ) )
508 return false;
509
510 if( !m_removeUnconnectedLayer )
511 return true;
512
513 if( m_keepTopBottomLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) )
514 return true;
515
516 return board->GetConnectivity()->IsConnectedOnLayer( this, static_cast<int>( aLayer ), types );
517 }
518
519
ViewGetLayers(int aLayers[],int & aCount) const520 void PCB_TRACK::ViewGetLayers( int aLayers[], int& aCount ) const
521 {
522 // Show the track and its netname on different layers
523 aLayers[0] = GetLayer();
524 aLayers[1] = GetNetnameLayer( aLayers[0] );
525 aCount = 2;
526 }
527
528
ViewGetLOD(int aLayer,KIGFX::VIEW * aView) const529 double PCB_TRACK::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
530 {
531 constexpr double HIDE = std::numeric_limits<double>::max();
532
533 PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
534 PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
535
536 if( !aView->IsLayerVisible( LAYER_TRACKS ) )
537 return HIDE;
538
539 if( IsNetnameLayer( aLayer ) )
540 {
541 // Hide netnames on dimmed tracks
542 if( renderSettings->GetHighContrast() )
543 {
544 if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
545 return HIDE;
546 }
547
548 // Netnames will be shown only if zoom is appropriate
549 return ( double ) Millimeter2iu( 4 ) / ( m_Width + 1 );
550 }
551
552 // Other layers are shown without any conditions
553 return 0.0;
554 }
555
556
ViewBBox() const557 const BOX2I PCB_TRACK::ViewBBox() const
558 {
559 BOX2I bbox = GetBoundingBox();
560 const BOARD* board = GetBoard();
561
562 if( board )
563 bbox.Inflate( 2 * board->GetDesignSettings().GetBiggestClearanceValue() );
564 else
565 bbox.Inflate( GetWidth() ); // Add a bit extra for safety
566
567 return bbox;
568 }
569
570
ViewGetLayers(int aLayers[],int & aCount) const571 void PCB_VIA::ViewGetLayers( int aLayers[], int& aCount ) const
572 {
573 aLayers[0] = LAYER_VIA_HOLES;
574 aLayers[1] = LAYER_VIA_HOLEWALLS;
575 aLayers[2] = LAYER_VIA_NETNAMES;
576
577 // Just show it on common via & via holes layers
578 switch( GetViaType() )
579 {
580 case VIATYPE::THROUGH: aLayers[3] = LAYER_VIA_THROUGH; break;
581 case VIATYPE::BLIND_BURIED: aLayers[3] = LAYER_VIA_BBLIND; break;
582 case VIATYPE::MICROVIA: aLayers[3] = LAYER_VIA_MICROVIA; break;
583 default: aLayers[3] = LAYER_GP_OVERLAY; break;
584 }
585
586 aCount = 4;
587 }
588
589
ViewGetLOD(int aLayer,KIGFX::VIEW * aView) const590 double PCB_VIA::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
591 {
592 constexpr double HIDE = (double)std::numeric_limits<double>::max();
593
594 PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
595 PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
596 const BOARD* board = GetBoard();
597 LSET visible = LSET::AllLayersMask();
598
599 // Meta control for hiding all vias
600 if( !aView->IsLayerVisible( LAYER_VIAS ) )
601 return HIDE;
602
603 // Handle board visibility
604 if( board )
605 visible = board->GetVisibleLayers() & board->GetEnabledLayers();
606
607 if( IsViaPadLayer( aLayer ) )
608 {
609 if( !FlashLayer( visible ) )
610 return HIDE;
611 }
612 else if( IsHoleLayer( aLayer ) )
613 {
614 if( m_viaType == VIATYPE::BLIND_BURIED || m_viaType == VIATYPE::MICROVIA )
615 {
616 // Show a blind or micro via's hole if it crosses a visible layer
617 if( !( visible & GetLayerSet() ).any() )
618 return HIDE;
619 }
620 else
621 {
622 // Show a through via's hole if any physical layer is shown
623 if( !( visible & LSET::PhysicalLayersMask() ).any() )
624 return HIDE;
625 }
626 }
627 else if( IsNetnameLayer( aLayer ) )
628 {
629 if( renderSettings->GetHighContrast() )
630 {
631 // Hide netnames unless via is flashed to a high-contrast layer
632 if( !FlashLayer( renderSettings->GetPrimaryHighContrastLayer() ) )
633 return HIDE;
634 }
635 else
636 {
637 // Hide netnames unless pad is flashed to a visible layer
638 if( !FlashLayer( visible ) )
639 return HIDE;
640 }
641
642 // Netnames will be shown only if zoom is appropriate
643 return m_Width == 0 ? HIDE : ( (double)Millimeter2iu( 10 ) / m_Width );
644 }
645
646 // Passed all tests; show.
647 return 0.0;
648 }
649
650
651 // see class_track.h
GetMsgPanelInfo(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList)652 void PCB_TRACK::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
653 {
654 EDA_UNITS units = aFrame->GetUserUnits();
655 wxString msg;
656 BOARD* board = GetBoard();
657
658 aList.emplace_back( _( "Type" ),
659 Type() == PCB_ARC_T ? ( "Track (arc)" ) : _( "Track" ) );
660
661
662 GetMsgPanelInfoBase_Common( aFrame, aList );
663
664 aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
665
666 aList.emplace_back( _( "Width" ), MessageTextFromValue( units, m_Width ) );
667
668 if( Type() == PCB_ARC_T )
669 {
670 double radius = static_cast<PCB_ARC*>( this )->GetRadius();
671 aList.emplace_back( _( "Radius" ), MessageTextFromValue( units, radius ) );
672 }
673
674 aList.emplace_back( _( "Segment Length" ), MessageTextFromValue( units, GetLength() ) );
675
676 // Display full track length (in Pcbnew)
677 if( board && GetNetCode() > 0 )
678 {
679 int count;
680 double trackLen;
681 double lenPadToDie;
682
683 std::tie( count, trackLen, lenPadToDie ) = board->GetTrackLength( *this );
684
685 aList.emplace_back( _( "Routed Length" ), MessageTextFromValue( units, trackLen ) );
686
687 if( lenPadToDie != 0 )
688 {
689 msg = MessageTextFromValue( units, lenPadToDie );
690 aList.emplace_back( _( "Pad To Die Length" ), msg );
691
692 msg = MessageTextFromValue( units, trackLen + lenPadToDie );
693 aList.emplace_back( _( "Full Length" ), msg );
694 }
695 }
696
697 wxString source;
698 int clearance = GetOwnClearance( GetLayer(), &source );
699
700 aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
701 MessageTextFromValue( units, clearance ) ),
702 wxString::Format( _( "(from %s)" ), source ) );
703 }
704
705
GetMsgPanelInfo(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList)706 void PCB_VIA::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
707 {
708 EDA_UNITS units = aFrame->GetUserUnits();
709 wxString msg;
710
711 switch( GetViaType() )
712 {
713 case VIATYPE::MICROVIA: msg = _( "Micro Via" ); break;
714 case VIATYPE::BLIND_BURIED: msg = _( "Blind/Buried Via" ); break;
715 case VIATYPE::THROUGH: msg = _( "Through Via" ); break;
716 default: msg = _( "Via" ); break;
717 }
718
719 aList.emplace_back( _( "Type" ), msg );
720
721 GetMsgPanelInfoBase_Common( aFrame, aList );
722
723 aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
724
725 msg = MessageTextFromValue( aFrame->GetUserUnits(), m_Width );
726
727 aList.emplace_back( _( "Diameter" ), msg );
728
729 msg = MessageTextFromValue( aFrame->GetUserUnits(), GetDrillValue() );
730
731 aList.emplace_back( _( "Drill" ), msg );
732
733 wxString source;
734 int clearance = GetOwnClearance( GetLayer(), &source );
735
736 aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
737 MessageTextFromValue( units, clearance ) ),
738 wxString::Format( _( "(from %s)" ), source ) );
739
740 int minAnnulus = GetMinAnnulus( GetLayer(), &source );
741
742 aList.emplace_back( wxString::Format( _( "Min Annular Width: %s" ),
743 MessageTextFromValue( units, minAnnulus ) ),
744 wxString::Format( _( "(from %s)" ), source ) );
745 }
746
747
GetMsgPanelInfoBase_Common(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList) const748 void PCB_TRACK::GetMsgPanelInfoBase_Common( EDA_DRAW_FRAME* aFrame,
749 std::vector<MSG_PANEL_ITEM>& aList ) const
750 {
751 wxString msg;
752
753 aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
754
755 aList.emplace_back( _( "Net Class" ), UnescapeString( GetNetClass()->GetName() ) );
756
757 #if 0 // Enable for debugging
758 if( GetBoard() )
759 aList.emplace_back( _( "NetCode" ), wxString::Format( wxT( "%d" ), GetNetCode() ) );
760
761 aList.emplace_back( wxT( "Flags" ), wxString::Format( wxT( "0x%08X" ), m_flags ) );
762
763 aList.emplace_back( wxT( "Start pos" ), wxString::Format( wxT( "%d %d" ),
764 m_Start.x,
765 m_Start.y ) );
766 aList.emplace_back( wxT( "End pos" ), wxString::Format( wxT( "%d %d" ),
767 m_End.x,
768 m_End.y ) );
769 #endif
770
771 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
772 aList.emplace_back( _( "Status" ), _( "Locked" ) );
773 }
774
775
layerMaskDescribe() const776 wxString PCB_VIA::layerMaskDescribe() const
777 {
778 const BOARD* board = GetBoard();
779 PCB_LAYER_ID top_layer;
780 PCB_LAYER_ID bottom_layer;
781
782 LayerPair( &top_layer, &bottom_layer );
783
784 return board->GetLayerName( top_layer ) + wxT( " - " ) + board->GetLayerName( bottom_layer );
785 }
786
787
HitTest(const wxPoint & aPosition,int aAccuracy) const788 bool PCB_TRACK::HitTest( const wxPoint& aPosition, int aAccuracy ) const
789 {
790 return TestSegmentHit( aPosition, m_Start, m_End, aAccuracy + ( m_Width / 2 ) );
791 }
792
793
HitTest(const wxPoint & aPosition,int aAccuracy) const794 bool PCB_ARC::HitTest( const wxPoint& aPosition, int aAccuracy ) const
795 {
796 int max_dist = aAccuracy + ( m_Width / 2 );
797
798 // Short-circuit common cases where the arc is connected to a track or via at an endpoint
799 if( EuclideanNorm( GetStart() - aPosition ) <= max_dist ||
800 EuclideanNorm( GetEnd() - aPosition ) <= max_dist )
801 {
802 return true;
803 }
804
805 wxPoint center = GetPosition();
806 wxPoint relpos = aPosition - center;
807 double dist = EuclideanNorm( relpos );
808 double radius = GetRadius();
809
810 if( std::abs( dist - radius ) > max_dist )
811 return false;
812
813 double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
814 double arc_hittest = ArcTangente( relpos.y, relpos.x );
815
816 // Calculate relative angle between the starting point of the arc, and the test point
817 arc_hittest -= arc_angle_start;
818
819 // Normalise arc_hittest between 0 ... 360 deg
820 NORMALIZE_ANGLE_POS( arc_hittest );
821 double arc_angle = GetAngle();
822
823 if( arc_angle < 0 )
824 return arc_hittest >= 3600 + arc_angle;
825
826 return arc_hittest <= arc_angle;
827 }
828
829
HitTest(const wxPoint & aPosition,int aAccuracy) const830 bool PCB_VIA::HitTest( const wxPoint& aPosition, int aAccuracy ) const
831 {
832 int max_dist = aAccuracy + ( m_Width / 2 );
833
834 // rel_pos is aPosition relative to m_Start (or the center of the via)
835 wxPoint rel_pos = aPosition - m_Start;
836 double dist = (double) rel_pos.x * rel_pos.x + (double) rel_pos.y * rel_pos.y;
837 return dist <= (double) max_dist * max_dist;
838 }
839
840
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const841 bool PCB_TRACK::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
842 {
843 EDA_RECT arect = aRect;
844 arect.Inflate( aAccuracy );
845
846 if( aContained )
847 return arect.Contains( GetStart() ) && arect.Contains( GetEnd() );
848 else
849 return arect.Intersects( GetStart(), GetEnd() );
850 }
851
852
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const853 bool PCB_ARC::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
854 {
855 EDA_RECT box;
856 EDA_RECT arect = aRect;
857 arect.Inflate( aAccuracy );
858
859 box.SetOrigin( GetStart() );
860 box.Merge( GetMid() );
861 box.Merge( GetEnd() );
862
863 box.Inflate( GetWidth() / 2 );
864
865 if( aContained )
866 return arect.Contains( box );
867 else
868 return arect.Intersects( box );
869 }
870
871
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const872 bool PCB_VIA::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
873 {
874 EDA_RECT box;
875 EDA_RECT arect = aRect;
876 arect.Inflate( aAccuracy );
877
878 box.SetOrigin( GetStart() );
879 box.Inflate( GetWidth() / 2 );
880
881 if( aContained )
882 return arect.Contains( box );
883 else
884 return arect.IntersectsCircle( GetStart(), GetWidth() / 2 );
885 }
886
887
GetSelectMenuText(EDA_UNITS aUnits) const888 wxString PCB_TRACK::GetSelectMenuText( EDA_UNITS aUnits ) const
889 {
890 return wxString::Format( Type() == PCB_ARC_T ? _("Track (arc) %s on %s, length %s" )
891 : _("Track %s on %s, length %s" ),
892 GetNetnameMsg(),
893 GetLayerName(),
894 MessageTextFromValue( aUnits, GetLength() ) );
895 }
896
897
GetMenuImage() const898 BITMAPS PCB_TRACK::GetMenuImage() const
899 {
900 return BITMAPS::add_tracks;
901 }
902
SwapData(BOARD_ITEM * aImage)903 void PCB_TRACK::SwapData( BOARD_ITEM* aImage )
904 {
905 assert( aImage->Type() == PCB_TRACE_T );
906
907 std::swap( *((PCB_TRACK*) this), *((PCB_TRACK*) aImage) );
908 }
909
SwapData(BOARD_ITEM * aImage)910 void PCB_ARC::SwapData( BOARD_ITEM* aImage )
911 {
912 assert( aImage->Type() == PCB_ARC_T );
913
914 std::swap( *this, *static_cast<PCB_ARC*>( aImage ) );
915 }
916
SwapData(BOARD_ITEM * aImage)917 void PCB_VIA::SwapData( BOARD_ITEM* aImage )
918 {
919 assert( aImage->Type() == PCB_VIA_T );
920
921 std::swap( *((PCB_VIA*) this), *((PCB_VIA*) aImage) );
922 }
923
924
GetPosition() const925 wxPoint PCB_ARC::GetPosition() const
926 {
927 auto center = CalcArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ));
928 return wxPoint( center.x, center.y );
929 }
930
GetRadius() const931 double PCB_ARC::GetRadius() const
932 {
933 auto center = CalcArcCenter( VECTOR2I( m_Start ), VECTOR2I( m_Mid ), VECTOR2I( m_End ));
934 return GetLineLength( wxPoint( center ), m_Start );
935 }
936
GetAngle() const937 double PCB_ARC::GetAngle() const
938 {
939 wxPoint center = GetPosition();
940 wxPoint p0 = m_Start - center;
941 wxPoint p1 = m_Mid - center;
942 wxPoint p2 = m_End - center;
943 double angle1 = ArcTangente( p1.y, p1.x ) - ArcTangente( p0.y, p0.x );
944 double angle2 = ArcTangente( p2.y, p2.x ) - ArcTangente( p1.y, p1.x );
945
946 return NormalizeAngle180( angle1 ) + NormalizeAngle180( angle2 );
947 }
948
GetArcAngleStart() const949 double PCB_ARC::GetArcAngleStart() const
950 {
951 wxPoint center = GetPosition();
952
953 double angleStart = ArcTangente( m_Start.y - center.y,
954 m_Start.x - center.x );
955 return NormalizeAnglePos( angleStart );
956 }
957
GetArcAngleEnd() const958 double PCB_ARC::GetArcAngleEnd() const
959 {
960 wxPoint center = GetPosition();
961
962 double angleEnd = ArcTangente( m_End.y - center.y,
963 m_End.x - center.x );
964 return NormalizeAnglePos( angleEnd );
965 }
966
967
operator ()(const PCB_TRACK * a,const PCB_TRACK * b) const968 bool PCB_TRACK::cmp_tracks::operator() ( const PCB_TRACK* a, const PCB_TRACK* b ) const
969 {
970 if( a->GetNetCode() != b->GetNetCode() )
971 return a->GetNetCode() < b->GetNetCode();
972
973 if( a->GetLayer() != b->GetLayer() )
974 return a->GetLayer() < b->GetLayer();
975
976 if( a->Type() != b->Type() )
977 return a->Type() < b->Type();
978
979 if( a->m_Uuid != b->m_Uuid )
980 return a->m_Uuid < b->m_Uuid;
981
982 return a < b;
983 }
984
985
GetEffectiveShape(PCB_LAYER_ID aLayer) const986 std::shared_ptr<SHAPE> PCB_TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
987 {
988 return std::make_shared<SHAPE_SEGMENT>( m_Start, m_End, m_Width );
989 }
990
991
GetEffectiveShape(PCB_LAYER_ID aLayer) const992 std::shared_ptr<SHAPE> PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
993 {
994 if( FlashLayer( aLayer ) )
995 {
996 return std::make_shared<SHAPE_CIRCLE>( m_Start, m_Width / 2 );
997 }
998 else
999 {
1000 int radius = GetDrillValue() / 2;
1001
1002 if( GetBoard() )
1003 radius += GetBoard()->GetDesignSettings().GetHolePlatingThickness();
1004
1005 return std::make_shared<SHAPE_CIRCLE>( m_Start, radius );
1006 }
1007 }
1008
1009
GetEffectiveShape(PCB_LAYER_ID aLayer) const1010 std::shared_ptr<SHAPE> PCB_ARC::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
1011 {
1012 return std::make_shared<SHAPE_ARC>( GetStart(), GetMid(), GetEnd(), GetWidth() );
1013 }
1014
1015
TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET & aCornerBuffer,PCB_LAYER_ID aLayer,int aClearanceValue,int aError,ERROR_LOC aErrorLoc,bool ignoreLineWidth) const1016 void PCB_TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
1017 PCB_LAYER_ID aLayer, int aClearanceValue,
1018 int aError, ERROR_LOC aErrorLoc,
1019 bool ignoreLineWidth ) const
1020 {
1021 wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for tracks." );
1022
1023
1024 switch( Type() )
1025 {
1026 case PCB_VIA_T:
1027 {
1028 int radius = ( m_Width / 2 ) + aClearanceValue;
1029 TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aError, aErrorLoc );
1030 break;
1031 }
1032
1033 case PCB_ARC_T:
1034 {
1035 const PCB_ARC* arc = static_cast<const PCB_ARC*>( this );
1036 int width = m_Width + ( 2 * aClearanceValue );
1037
1038 TransformArcToPolygon( aCornerBuffer, arc->GetStart(), arc->GetMid(),
1039 arc->GetEnd(), width, aError, aErrorLoc );
1040 break;
1041 }
1042
1043 default:
1044 {
1045 int width = m_Width + ( 2 * aClearanceValue );
1046
1047 TransformOvalToPolygon( aCornerBuffer, m_Start, m_End, width, aError, aErrorLoc );
1048 break;
1049 }
1050 }
1051 }
1052
1053
1054 #if defined(DEBUG)
1055
ShowState(int stateBits)1056 wxString PCB_TRACK::ShowState( int stateBits )
1057 {
1058 wxString ret;
1059
1060 if( stateBits & IS_LINKED )
1061 ret << wxT( " | IS_LINKED" );
1062
1063 if( stateBits & LOCKED )
1064 ret << wxT( " | LOCKED" );
1065
1066 if( stateBits & IN_EDIT )
1067 ret << wxT( " | IN_EDIT" );
1068
1069 if( stateBits & IS_DRAGGING )
1070 ret << wxT( " | IS_DRAGGING" );
1071
1072 if( stateBits & DO_NOT_DRAW )
1073 ret << wxT( " | DO_NOT_DRAW" );
1074
1075 if( stateBits & IS_DELETED )
1076 ret << wxT( " | IS_DELETED" );
1077
1078 if( stateBits & END_ONPAD )
1079 ret << wxT( " | END_ONPAD" );
1080
1081 if( stateBits & BEGIN_ONPAD )
1082 ret << wxT( " | BEGIN_ONPAD" );
1083
1084 return ret;
1085 }
1086
1087 #endif
1088
1089
1090 static struct TRACK_VIA_DESC
1091 {
TRACK_VIA_DESCTRACK_VIA_DESC1092 TRACK_VIA_DESC()
1093 {
1094 ENUM_MAP<VIATYPE>::Instance()
1095 .Undefined( VIATYPE::NOT_DEFINED )
1096 .Map( VIATYPE::THROUGH, _HKI( "Through" ) )
1097 .Map( VIATYPE::BLIND_BURIED, _HKI( "Blind/buried" ) )
1098 .Map( VIATYPE::MICROVIA, _HKI( "Micro" ) );
1099
1100 ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
1101
1102 if( layerEnum.Choices().GetCount() == 0 )
1103 {
1104 layerEnum.Undefined( UNDEFINED_LAYER );
1105
1106 for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq )
1107 layerEnum.Map( *seq, LSET::Name( *seq ) );
1108 }
1109
1110 PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
1111
1112 // Track
1113 REGISTER_TYPE( PCB_TRACK );
1114 propMgr.InheritsAfter( TYPE_HASH( PCB_TRACK ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
1115
1116 propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "Width" ),
1117 &PCB_TRACK::SetWidth, &PCB_TRACK::GetWidth, PROPERTY_DISPLAY::DISTANCE ) );
1118 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ),
1119 new PROPERTY<PCB_TRACK, int, BOARD_ITEM>( _HKI( "Origin X" ),
1120 &PCB_TRACK::SetX, &PCB_TRACK::GetX, PROPERTY_DISPLAY::DISTANCE ) );
1121 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ),
1122 new PROPERTY<PCB_TRACK, int, BOARD_ITEM>( _HKI( "Origin Y" ),
1123 &PCB_TRACK::SetY, &PCB_TRACK::GetY, PROPERTY_DISPLAY::DISTANCE ) );
1124 propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End X" ),
1125 &PCB_TRACK::SetEndX, &PCB_TRACK::GetEndX, PROPERTY_DISPLAY::DISTANCE ) );
1126 propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End Y" ),
1127 &PCB_TRACK::SetEndY, &PCB_TRACK::GetEndY, PROPERTY_DISPLAY::DISTANCE ) );
1128
1129 // Arc
1130 REGISTER_TYPE( PCB_ARC );
1131 propMgr.InheritsAfter( TYPE_HASH( PCB_ARC ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
1132
1133 propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "Width" ),
1134 &PCB_ARC::SetWidth, &PCB_ARC::GetWidth, PROPERTY_DISPLAY::DISTANCE ) );
1135 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ),
1136 new PROPERTY<PCB_ARC, int, BOARD_ITEM>( _HKI( "Origin X" ),
1137 &PCB_TRACK::SetX, &PCB_ARC::GetX, PROPERTY_DISPLAY::DISTANCE ) );
1138 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ),
1139 new PROPERTY<PCB_ARC, int, BOARD_ITEM>( _HKI( "Origin Y" ),
1140 &PCB_TRACK::SetY, &PCB_ARC::GetY, PROPERTY_DISPLAY::DISTANCE ) );
1141 propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End X" ),
1142 &PCB_TRACK::SetEndX, &PCB_ARC::GetEndX, PROPERTY_DISPLAY::DISTANCE ) );
1143 propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End Y" ),
1144 &PCB_TRACK::SetEndY, &PCB_ARC::GetEndY, PROPERTY_DISPLAY::DISTANCE ) );
1145
1146 // Via
1147 REGISTER_TYPE( PCB_VIA );
1148 propMgr.InheritsAfter( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
1149
1150 // TODO layerset for vias?
1151 // TODO test drill, use getdrillvalue?
1152 propMgr.ReplaceProperty( TYPE_HASH( PCB_TRACK ), _HKI( "Width" ),
1153 new PROPERTY<PCB_VIA, int, PCB_TRACK>( _HKI( "Diameter" ),
1154 &PCB_VIA::SetWidth, &PCB_VIA::GetWidth, PROPERTY_DISPLAY::DISTANCE ) );
1155 propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Drill" ),
1156 &PCB_VIA::SetDrill, &PCB_VIA::GetDrillValue, PROPERTY_DISPLAY::DISTANCE ) );
1157 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ),
1158 new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID, BOARD_ITEM>( _HKI( "Layer Top" ),
1159 &PCB_VIA::SetLayer, &PCB_VIA::GetLayer ) );
1160 propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Bottom" ),
1161 &PCB_VIA::SetBottomLayer, &PCB_VIA::BottomLayer ) );
1162 propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, VIATYPE>( _HKI( "Via Type" ),
1163 &PCB_VIA::SetViaType, &PCB_VIA::GetViaType ) );
1164 }
1165 } _TRACK_VIA_DESC;
1166
1167 ENUM_TO_WXANY( VIATYPE );
1168