File: Component.h

package info (click to toggle)
veroroute 2.38-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,044 kB
  • sloc: cpp: 21,512; xml: 89; sh: 65; lisp: 20; makefile: 5
file content (803 lines) | stat: -rw-r--r-- 32,301 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
/*
	VeroRoute - Qt based Veroboard/Perfboard/PCB layout & routing application.

	Copyright (C) 2017  Alex Lawrow    ( dralx@users.sourceforge.net )

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "FootPrint.h"
#include "RectManager.h"
#include "CompDefiner.h"

// For 2-layer boards, the following says which is the prefered layer for a pin to make connections.
// This only affects rendered connections between adjacent pins..
// It does not affect connectivity or routing.
// The joint preference of adjacent pins determines which layers will show the connection.
Q_DECL_CONSTEXPR static const uchar	LAYER_X = 0;	// No preference
Q_DECL_CONSTEXPR static const uchar	LAYER_B	= 1;	// Prefer bottom layer
Q_DECL_CONSTEXPR static const uchar	LAYER_T	= 2;	// Prefer top layer

Q_DECL_CONSTEXPR static const int	MAX_PAD_OFFSET_MIL = 50;

class CompManager;
class TemplateManager;

struct CompStrings	// Mainly for handling netlist import
{
	CompStrings() {}
	CompStrings(const std::string& nameStr, const std::string& valueStr, const std::string& importStr)
	: m_nameStr(nameStr), m_valueStr(valueStr), m_importStr(importStr) {}
	~CompStrings() {}
	CompStrings(const CompStrings& o) { *this = o; }
	CompStrings& operator=(const CompStrings& o)
	{
		m_nameStr	= o.m_nameStr;
		m_valueStr	= o.m_valueStr;
		m_importStr	= o.m_importStr;
		return *this;
	}
	void Clear() { m_nameStr.clear(); m_valueStr.clear(); m_importStr.clear(); }
	std::string	m_nameStr;		// e.q. "U1"
	std::string	m_valueStr;		// e.g. "TL072"
	std::string	m_importStr;	// e.g. "DIP8"
};

// Class to describe a component.
// Wires (i.e. jumpers) and markers also use this class even though they are not true components.

class Component : public FootPrint
{
public:
	Component() : FootPrint() { Clear(); }
	virtual ~Component() override {}
	Component(const Component& o) : FootPrint() { *this = o; }
	void Clear()
	{
		FootPrint::DeAllocate();
		SetType(COMP::INVALID);
		m_id		= 0;
		m_nameStr	= m_valueStr = m_prefixStr = m_typeStr = m_importStr = "";
		m_lyr = m_row = m_col = m_iLabelOffsetRow = m_iLabelOffsetCol = 0;
		m_direction		= 'W';
		m_bIsPlaced		= false;
		m_iPinFlags		= 0;
		m_iPadWidth		= 70;
		m_iHoleWidth	= 40;
		m_bAllowFlyWire	= false;
		m_nodeIdPins.clear();
		m_origIdPins1.clear();
		m_origIdPins2.clear();
		m_layerPrefs.clear();
		m_pinOffsetRow.clear();
		m_pinOffsetCol.clear();
		m_pinLabels.clear();
		m_pinAligns.clear();
		m_shapes.clear();
	}
	Component(const TemplateManager& templateMgr, const CompDefiner& definer);	// This method is for building a custom component
	Component(CompManager* pCompMgr, const RectManager& rectMgr, const ElementGrid& grid, int nLyr, int nRowMin, int nRowMax, int nColMin, int nColMax)
	{
		Clear();

		BuildTrax(pCompMgr, rectMgr, grid, nLyr, nRowMin, nRowMax, nColMin, nColMax);	// Build method for "tracks" component

		SetDefaultStrings();

		m_lyr = nLyr;
		m_row = nRowMin;
		m_col = nColMin;
		m_bIsPlaced = false;	// Trax are always created from the board
	}
	Component(const std::string& name, const std::string& value, COMP eType, std::vector<int>& nodeIdPins)
	{
		Clear();

		BuildDefault(eType);	// Build method for default component

		SetAllowFlyWire(eType == COMP::PAD_FLYINGWIRE);
		SetDefaultPinFlags();
		SetDefaultStrings();
		SetDefaultLabelOffsets();

		const size_t numPins = nodeIdPins.size();
		if ( eType == COMP::DIP || eType == COMP::SIP || eType == COMP::SWITCH_ST_DIP )	// Resize DIP/SIP/SWITCH_DIP as needed
		{
			const int reqLength = static_cast<int>( ( eType == COMP::SIP ) ? numPins : numPins / 2 );
			while ( GetCols() < reqLength )	Stretch(true);	// true  ==> grow
			while ( GetCols() > reqLength )	Stretch(false);	// false ==> shrink
		}
		if ( eType == COMP::STRIP_100 || eType == COMP::BLOCK_100 || eType == COMP::BLOCK_200 )
		{
			const int reqLength = static_cast<int>( ( eType == COMP::BLOCK_200 ) ? 1 + 2 * numPins : numPins );
			while ( GetCols() < reqLength )	Stretch(true);	// true  ==> grow
			while ( GetCols() > reqLength )	Stretch(false);	// false ==> shrink
		}
		if ( eType == COMP::SWITCH_ST || eType == COMP::SWITCH_DT )	// Resize non-DIP switches
		{
			const int numPoles  = static_cast<int>( ( eType == COMP::SWITCH_ST ) ? numPins / 2 : numPins / 3 );
			const int reqLength = ( 2 * numPoles ) - 1;
			while ( GetCols() < reqLength )	Stretch(true);	// true  ==> grow
			while ( GetCols() > reqLength )	Stretch(false);	// false ==> shrink
		}
		m_nameStr	= name;
		m_valueStr	= value;
		AllocatePins( numPins );
		std::copy(nodeIdPins.begin(), nodeIdPins.end(), m_nodeIdPins.begin());

		SetDefaultShapes();
	}
	Component& operator=(const Component& o)
	{
		FootPrint::operator=(o);	// Call operator= in base class
		m_id				= o.m_id;
		m_nameStr			= o.m_nameStr;
		m_valueStr			= o.m_valueStr;
		m_prefixStr			= o.m_prefixStr;
		m_typeStr			= o.m_typeStr;
		m_importStr			= o.m_importStr;
		m_lyr				= o.m_lyr;
		m_row				= o.m_row;
		m_col				= o.m_col;
		m_iLabelOffsetRow	= o.m_iLabelOffsetRow;
		m_iLabelOffsetCol	= o.m_iLabelOffsetCol;
		m_direction			= o.m_direction;
		m_bIsPlaced			= o.m_bIsPlaced;
		m_iPinFlags			= o.m_iPinFlags;
		m_iPadWidth			= o.m_iPadWidth;
		m_iHoleWidth		= o.m_iHoleWidth;
		m_bAllowFlyWire		= o.m_bAllowFlyWire;
		AllocatePins( o.GetNumPins() );
		std::copy(o.m_nodeIdPins.begin(),	o.m_nodeIdPins.end(),	m_nodeIdPins.begin());
		std::copy(o.m_origIdPins1.begin(),	o.m_origIdPins1.end(),	m_origIdPins1.begin());
		std::copy(o.m_origIdPins2.begin(),	o.m_origIdPins2.end(),	m_origIdPins2.begin());
		std::copy(o.m_layerPrefs.begin(),	o.m_layerPrefs.end(),	m_layerPrefs.begin());
		std::copy(o.m_pinOffsetRow.begin(),	o.m_pinOffsetRow.end(),	m_pinOffsetRow.begin());
		std::copy(o.m_pinOffsetCol.begin(),	o.m_pinOffsetCol.end(),	m_pinOffsetCol.begin());
		std::copy(o.m_pinLabels.begin(),	o.m_pinLabels.end(),	m_pinLabels.begin());
		std::copy(o.m_pinAligns.begin(),	o.m_pinAligns.end(),	m_pinAligns.begin());
		CopyShapes( o );
		return *this;
	}
	void ClearNodeIds()
	{
		for (auto& o : m_nodeIdPins)  o = BAD_NODEID;
		for (auto& o : m_origIdPins1) o = BAD_NODEID;
		for (auto& o : m_origIdPins2) o = BAD_NODEID;
		for (auto& o : m_layerPrefs)  o = LAYER_X;
	}
	bool IsEqual(const Component& o) const	// Compare persisted info
	{
		bool bOK = FootPrint::operator==(o)
				&& m_id					== o.m_id
				&& m_nameStr			== o.m_nameStr
				&& m_valueStr			== o.m_valueStr
				&& m_prefixStr			== o.m_prefixStr
				&& m_typeStr			== o.m_typeStr
				&& m_importStr			== o.m_importStr
				&& m_lyr				== o.m_lyr
				&& m_row				== o.m_row
				&& m_col				== o.m_col
				&& m_iLabelOffsetRow	== o.m_iLabelOffsetRow
				&& m_iLabelOffsetCol	== o.m_iLabelOffsetCol
				&& m_direction			== o.m_direction
				&& m_bIsPlaced			== o.m_bIsPlaced
				&& m_iPinFlags			== o.m_iPinFlags
				&& m_iPadWidth			== o.m_iPadWidth
				&& m_iHoleWidth			== o.m_iHoleWidth
				&& m_bAllowFlyWire		== o.m_bAllowFlyWire
				&& GetNumPins()			== o.GetNumPins()
				&& GetNumShapes()		== o.GetNumShapes();
		for (size_t i = 0, iSize = GetNumPins(); i < iSize && bOK; i++)
		{
			bOK =  m_nodeIdPins[i]		== o.m_nodeIdPins[i]
				&& m_origIdPins1[i]		== o.m_origIdPins1[i]
				&& m_origIdPins2[i]		== o.m_origIdPins2[i]
				&& m_layerPrefs[i]		== o.m_layerPrefs[i]
				&& m_pinOffsetRow[i]	== o.m_pinOffsetRow[i]
				&& m_pinOffsetCol[i]	== o.m_pinOffsetCol[i]
				&& m_pinLabels[i]		== o.m_pinLabels[i]
				&& m_pinAligns[i]		== o.m_pinAligns[i];
		}
		for (size_t i = 0, iSize = GetNumShapes(); i < iSize && bOK; i++)
		{
			bOK = m_shapes[i] == o.m_shapes[i];
		}
		return bOK;
	}
	bool operator<(const Component& o) const	{ return m_id < o.m_id; }
	bool operator==(const Component& o) const	{ return m_id == o.m_id; }
	void SetId(int i)							{ m_id = i; }
	void SetNameStr(const std::string& s)		{ m_nameStr = s; }
	void SetValueStr(const std::string& s)		{ m_valueStr = s; }
	void SetPrefixStr(const std::string& s)		{ m_prefixStr = s; }
	void SetTypeStr(const std::string& s)		{ m_typeStr = s; }
	void SetImportStr(const std::string& s)		{ m_importStr = s; }
	void SetNodeId(size_t iPinIndex, int i)
	{
		if ( iPinIndex < m_nodeIdPins.size() ) m_nodeIdPins[iPinIndex] = i;
	}
	void SetOrigId(int lyr, size_t iPinIndex, int i)
	{
		assert( lyr == 0 || lyr == 1 );
		if ( lyr == 0 )
		{
			if ( iPinIndex < m_origIdPins1.size() ) m_origIdPins1[iPinIndex] = i;
		}
		else
		{
			if ( iPinIndex < m_origIdPins2.size() ) m_origIdPins2[iPinIndex] = i;
		}
	}
	void SetLayerPref(size_t iPinIndex, uchar iPref)
	{
		if ( iPinIndex < m_layerPrefs.size() ) m_layerPrefs[iPinIndex] = iPref;
	}
	void SetPinOffsetRow(size_t iPinIndex, int i)
	{
		if ( iPinIndex < m_pinOffsetRow.size() ) m_pinOffsetRow[iPinIndex] = std::max(-MAX_PAD_OFFSET_MIL, std::min(MAX_PAD_OFFSET_MIL, i));
	}
	void SetPinOffsetCol(size_t iPinIndex, int i)
	{
		if ( iPinIndex < m_pinOffsetCol.size() ) m_pinOffsetCol[iPinIndex] = std::max(-MAX_PAD_OFFSET_MIL, std::min(MAX_PAD_OFFSET_MIL, i));
	}
	void SetPinLabel(size_t iPinIndex, const std::string& s)
	{
		if ( iPinIndex < m_pinLabels.size() ) m_pinLabels[iPinIndex] = s;
	}
	void SetPinAlign(size_t iPinIndex, int i)
	{
		if ( iPinIndex < m_pinAligns.size() ) m_pinAligns[iPinIndex] = i;
	}
	void SetShape(size_t iShapeIndex, const Shape& o)
	{
		if ( iShapeIndex < m_shapes.size() ) m_shapes[iShapeIndex] = o;
	}
	void CopyPinLabels(const Component& o)
	{
		assert( m_pinLabels.size() == o.m_pinLabels.size() );
		assert( m_pinAligns.size() == o.m_pinAligns.size() );
		std::copy(o.m_pinLabels.begin(), o.m_pinLabels.end(), m_pinLabels.begin());
		std::copy(o.m_pinAligns.begin(), o.m_pinAligns.end(), m_pinAligns.begin());
	}
	void CopyShapes(const Component& o)
	{
		AllocateShapes( o.GetNumShapes() );
		std::copy(o.m_shapes.begin(), o.m_shapes.end(), m_shapes.begin());
	}
	void AllocatePins(size_t numPins)
	{
		m_nodeIdPins.clear();	m_nodeIdPins.resize(numPins, BAD_NODEID);
		m_origIdPins1.clear();	m_origIdPins1.resize(numPins, BAD_NODEID);
		m_origIdPins2.clear();	m_origIdPins2.resize(numPins, BAD_NODEID);
		m_layerPrefs.clear();	m_layerPrefs.resize(numPins, LAYER_X);
		m_pinOffsetRow.clear();	m_pinOffsetRow.resize(numPins, 0);
		m_pinOffsetCol.clear();	m_pinOffsetCol.resize(numPins, 0);
		m_pinLabels.clear();	m_pinLabels.resize(numPins, "");
		m_pinAligns.clear();	m_pinAligns.resize(numPins, Qt::AlignHCenter);
		SetDefaultPinLabels();
	}
	void AllocateShapes(size_t numShapes)
	{
		m_shapes.clear();	m_shapes.resize(numShapes, Shape());
	}
	void SetLyr(int i)				{ m_lyr = i; }
	void SetRow(int i)				{ m_row = i; }
	void SetCol(int i)				{ m_col = i; }
	void SetLabelOffsetRow(int i)	{ m_iLabelOffsetRow = i; }
	void SetLabelOffsetCol(int i)	{ m_iLabelOffsetCol = i; }
	void SetDirection(char d)		{ m_direction = d; }
	void SetIsPlaced(bool b)		{ m_bIsPlaced = b; }
	void SetPinFlags(uchar i)		{ m_iPinFlags = i; }
	void SetPadWidth(int i)			{ m_iPadWidth = i; }
	void SetHoleWidth(int i)		{ m_iHoleWidth = i; }
	void SetAllowFlyWire(bool b)	{ m_bAllowFlyWire = b; }
	void AddOne(const Shape& s)		{ m_shapes.push_back(s); }
	void AddTwo(const Shape& s)	// Adds the shape twice.  Once with fill only, and once with line only
	{
		Shape tmp(s);
		tmp.SetDrawFill(true);	tmp.SetDrawLine(false);	m_shapes.push_back(tmp);
		tmp.SetDrawFill(false);	tmp.SetDrawLine(true);	m_shapes.push_back(tmp);
	}
	bool				GetIsTemplate() const	{ return GetId() == BAD_COMPID; }
	const int&			GetId() const			{ return m_id; }
	const std::string&	GetNameStr() const		{ return m_nameStr; }
	const std::string&	GetValueStr() const		{ return m_valueStr; }
	const std::string&	GetPrefixStr() const	{ return m_prefixStr; }
	const std::string&	GetTypeStr() const		{ return m_typeStr; }
	const std::string&	GetImportStr() const	{ return m_importStr; }
	size_t				GetNumPins() const		{ return m_nodeIdPins.size(); }
	size_t				GetNumShapes() const	{ return m_shapes.size(); }
	const int&			GetNodeId(size_t iPinIndex) const
	{
		static int badNodeId(BAD_NODEID);
		return ( iPinIndex < m_nodeIdPins.size() ) ? m_nodeIdPins[iPinIndex] : badNodeId;
	}
	const int&			GetOrigId(int lyr, size_t iPinIndex) const
	{
		static int badNodeId(BAD_NODEID);
		if ( lyr == 0 )
			return ( iPinIndex < m_origIdPins1.size() ) ? m_origIdPins1[iPinIndex] : badNodeId;
		else
			return ( iPinIndex < m_origIdPins2.size() ) ? m_origIdPins2[iPinIndex] : badNodeId;
	}
	const uchar&		GetLayerPref(size_t iPinIndex) const
	{
		static uchar noPref(LAYER_X);
		return ( iPinIndex < m_layerPrefs.size() ) ? m_layerPrefs[iPinIndex] : noPref;
	}
	const int&			GetPinOffsetRow(size_t iPinIndex) const
	{
		static int defaultOffset(0);
		return ( iPinIndex < m_pinOffsetRow.size() ) ? m_pinOffsetRow[iPinIndex] : defaultOffset;
	}
	const int&			GetPinOffsetCol(size_t iPinIndex) const
	{
		static int defaultOffset(0);
		return ( iPinIndex < m_pinOffsetCol.size() ) ? m_pinOffsetCol[iPinIndex] : defaultOffset;
	}
	const std::string&	GetPinLabel(size_t iPinIndex) const
	{
		static const std::string emptyStr("");
		return ( iPinIndex < m_pinLabels.size() ) ? m_pinLabels[iPinIndex] : emptyStr;
	}
	const int&			GetPinAlign(size_t iPinIndex) const
	{
		static int defaultAlign(Qt::AlignHCenter);
		return ( iPinIndex < m_pinAligns.size() ) ? m_pinAligns[iPinIndex] : defaultAlign;
	}
	const Shape&		GetShape(size_t iShapeIndex) const
	{
		static Shape	defaultShape;
		return ( iShapeIndex < m_shapes.size() ) ? m_shapes[iShapeIndex] : defaultShape;
	}
	const int&			GetLyr() const				{ return m_lyr; }
	const int&			GetRow() const				{ return m_row; }
	const int&			GetCol() const				{ return m_col; }
	const int&			GetLabelOffsetRow() const	{ return m_iLabelOffsetRow; }
	const int&			GetLabelOffsetCol() const	{ return m_iLabelOffsetCol; }
	const char&			GetDirection() const		{ return m_direction; }
	const bool&			GetIsPlaced() const			{ return m_bIsPlaced; }
	const uchar&		GetPinFlags() const			{ return m_iPinFlags; }
	const int&			GetPadWidth() const			{ return m_iPadWidth; }
	const int&			GetHoleWidth() const		{ return m_iHoleWidth; }
	const bool&			GetAllowFlyWire() const		{ return m_bAllowFlyWire; }
	const std::vector<Shape>& GetShapes() const		{ return m_shapes; }
	bool				GetIsSOIC() const
	{
#ifdef _TEST_SOIC		
		if ( GetValueStr() == "SOIC28_TEST" ) return true;	// So we can make test SOICs using component editor
#endif			
		return CompTypes::GetIsSOIC( GetType() );
	}
	// Helpers for labels
	void SetDefaultLabelOffsets();
	void GetLabelOffsets(int& offsetRow, int& offsetCol) const;	// w.r.t. screen, not comp rotation
	void MoveLabelOffsets(int deltaRow, int deltaCol);			// w.r.t. screen, not comp rotation
	void HandleLegacyLabelOffsets();	// For old VRT files

	// Helpers for custom pads
	void SetCustomPads(bool b)	{ if ( b )	SetPinFlags( m_iPinFlags |  PIN_CUSTOM );
								  else		SetPinFlags( m_iPinFlags & ~PIN_CUSTOM ); }
	bool GetCustomPads() const	{ return ( GetPinFlags() & PIN_CUSTOM ) != 0; }

	void GetSafeBounds(double& L, double& R, double& T, double& B, bool bFill = true) const
	{
		// First consider footprint bounds (for direction 'W')
		const double dW( 0.5 * GetCols() ), dH( 0.5 * GetRows() );
		L = -dW;	R = dW;		T = -dH;	B = dH;
		// Then consider the list of shapes (for direction 'W')
		double l,r,t,b;	// Working variables
		for (const auto& o : m_shapes)
		{
			if ( !bFill && o.GetDrawFill() ) continue;	// If view is not drawing filled shapes, skip them
			o.GetSafeBounds(l, r, t, b);
			L = std::min(L,l);	T = std::min(T,t);
			R = std::max(R,r);	B = std::max(B,b);
		}
		// Handle other component directions
		switch ( GetDirection() )
		{
			case 'E':	l = -R;	t = -B;	r = -L;	b = -T;	break;
			case 'N':	l = -B;	t =  L;	r = -T;	b =  R;	break;
			case 'S':	l =  T;	t = -R;	r =  B;	b = -L;	break;
			default:	return;
		}
		L = l;	T = t;	R = r;	B = b;
	}
	std::string GetFullTypeStr() const		// For SIP/DIP types, append the number of pins
	{
		if ( GetType() == COMP::DIP || GetType() == COMP::SIP )
			return CompTypes::GetDefaultTypeStr( GetType() ) + std::to_string(GetNumPins());	// e.g. "DIP16"
		if ( GetType() == COMP::STRIP_100 || GetType() == COMP::BLOCK_100 || GetType() == COMP::BLOCK_200 )
			return CompTypes::GetDefaultTypeStr( GetType() ) + std::string(" (") + std::to_string(GetNumPins()) + std::string(" pins)");
		return GetTypeStr();
	}
	std::string GetFullImportStr() const	// For SIP/DIP/SWITCH/STRIP/BLOCK types, append the number of pins
	{
		if ( GetType() == COMP::DIP || GetType() == COMP::SIP ||
			 GetType() == COMP::SWITCH_DT || GetType() == COMP::SWITCH_ST || GetType() == COMP::SWITCH_ST_DIP ||
			 GetType() == COMP::STRIP_100 || GetType() == COMP::BLOCK_100 || GetType() == COMP::BLOCK_200 )
			return CompTypes::GetDefaultImportStr( GetType() ) + std::to_string(GetNumPins());	// e.g. "DIP16"
		return GetImportStr();
	}
	// Helpers (account for component direction)
	const int&	GetCompRows() const	{ return GetRows( GetDirection() ); }
	const int&	GetCompCols() const	{ return GetCols( GetDirection() ); }
	int			GetLastRow() const	{ return GetRow() + GetCompRows() - 1; }
	int			GetLastCol() const	{ return GetCol() + GetCompCols() - 1; }
	void GetCompPinOffsets(size_t iPinIndex, int& Xmil, int& Ymil) const
	{
		switch ( GetDirection() )
		{
			case 'E':	Xmil = -GetPinOffsetCol(iPinIndex);	Ymil = -GetPinOffsetRow(iPinIndex); return;
			case 'N':	Xmil = -GetPinOffsetRow(iPinIndex);	Ymil =  GetPinOffsetCol(iPinIndex); return;
			case 'S':	Xmil =  GetPinOffsetRow(iPinIndex);	Ymil = -GetPinOffsetCol(iPinIndex); return;
			default:	Xmil =  GetPinOffsetCol(iPinIndex);	Ymil =  GetPinOffsetRow(iPinIndex); return;
		}
	}
	void SetCompPinOffsets(size_t iPinIndex, int Xmil, int Ymil)
	{
		switch ( GetDirection() )
		{
			case 'E':	SetPinOffsetCol(iPinIndex, -Xmil);	SetPinOffsetRow(iPinIndex, -Ymil); return;
			case 'N':	SetPinOffsetRow(iPinIndex, -Xmil);	SetPinOffsetCol(iPinIndex,  Ymil); return;
			case 'S':	SetPinOffsetRow(iPinIndex,  Xmil);	SetPinOffsetCol(iPinIndex, -Ymil); return;
			default:	SetPinOffsetCol(iPinIndex,  Xmil);	SetPinOffsetRow(iPinIndex,  Ymil); return;
		}
	}
	void IncCompPinOffsets(size_t iPinIndex, int dX, int dY)
	{
		if ( dX == 0 && dY == 0 )
			return SetCompPinOffsets(iPinIndex, 0, 0);	// Reset
		int Xmil, Ymil;
		GetCompPinOffsets(iPinIndex, Xmil, Ymil);
		SetCompPinOffsets(iPinIndex, Xmil + dX, Ymil + dY);
	}
	bool GetUniformPinOffsets() const	// Check if all pins have the same offsets
	{
		const size_t iSize = GetNumPins();
		if ( iSize < 2 ) return iSize == 1;
		bool bAllSame(true);
		for (size_t i = 1; i < iSize && bAllSame; i++)
			bAllSame = ( m_pinOffsetRow[i] == m_pinOffsetRow[0] ) && ( m_pinOffsetCol[i] == m_pinOffsetCol[0] );
		return bAllSame;
	}
	void GetCompShapeOffsets(int& Xmil, int& Ymil) const
	{
		if ( GetUniformPinOffsets() )
		{
			switch ( GetDirection() )
			{
				case 'E':	Xmil = -m_pinOffsetCol[0];	Ymil = -m_pinOffsetRow[0]; return;
				case 'N':	Xmil = -m_pinOffsetRow[0];	Ymil =  m_pinOffsetCol[0]; return;
				case 'S':	Xmil =  m_pinOffsetRow[0];	Ymil = -m_pinOffsetCol[0]; return;
				default:	Xmil =  m_pinOffsetCol[0];	Ymil =  m_pinOffsetRow[0]; return;
			}
		}
		Xmil = Ymil = 0;	// Default to 0 offsets
	}
	const CompElement*	GetCompElement(int compRow, int compCol) const
	{
		return FootPrint::Get(0, compRow, compCol, GetDirection());
	}
	bool GetHasNodeId(int nodeId) const
	{
		for (const auto& i : m_nodeIdPins) if ( i == nodeId ) return true;
		return false;
	}
	void Rotate(bool bClockWise)
	{
		switch( GetDirection() )	// Component direction: 'W','E','N','S'
		{
			case 'W': return SetDirection( bClockWise ? 'N' : 'S' );
			case 'E': return SetDirection( bClockWise ? 'S' : 'N' );
			case 'N': return SetDirection( bClockWise ? 'E' : 'W' );
			case 'S': return SetDirection( bClockWise ? 'W' : 'E' );
		}
	}
	bool GetHasAssignedPins() const	// Check if any pins have a nodeId or pin label set
	{
		for (size_t i = 0; i < GetNumPins(); i++)
		{
			if ( m_nodeIdPins[i] != BAD_NODEID ) return true;
			if ( m_pinLabels[i]  != CompTypes::GetDefaultPinLabel(i) ) return true;
			if ( m_pinAligns[i]  != CompTypes::GetDefaultPinAlign(i, GetNumPins(), GetType()) ) return true;
		}
		return false;
	}
	bool GetAllowCustomPads() const	// true ==> allow custom pad and hole widths
	{
		if ( GetIsSOIC() ) return false;
		switch( GetType()  )
		{
			case COMP::TRACKS:	return false;
			case COMP::WIRE:	return false;	// Not allowed since 2 wires can share a hole
			default:			return GetNumPins() > 0;
		}
	}
	bool CanStretch(bool bGrow) const
	{
		if ( !FootPrint::CanStretch(bGrow) ) return false;
		switch( GetType() )
		{
			case COMP::SIP:
			case COMP::DIP:
			case COMP::STRIP_100:
			case COMP::BLOCK_100:
			case COMP::BLOCK_200:
			case COMP::SWITCH_ST:
			case COMP::SWITCH_DT:
			case COMP::SWITCH_ST_DIP:	return !GetHasAssignedPins();
			default:					return true;
		}
	}
	void Stretch(bool bGrow, bool bUsePCBshapes = false)
	{
		FootPrint::Stretch(bGrow);
		SetDefaultShapes(bUsePCBshapes);	// Rebuild the shapes list

		switch( GetType() )
		{
			case COMP::SIP:				return AllocatePins( static_cast<size_t>( GetCols() ) );
			case COMP::DIP:				return AllocatePins( static_cast<size_t>( 2 * GetCols() ) );
			case COMP::STRIP_100:		return AllocatePins( static_cast<size_t>( GetCols() ) );
			case COMP::BLOCK_100:		return AllocatePins( static_cast<size_t>( GetCols() ) );
			case COMP::BLOCK_200:		return AllocatePins( static_cast<size_t>( ( GetCols() - 1 ) / 2 ) );
			case COMP::SWITCH_ST:		return AllocatePins( static_cast<size_t>( GetCols() + 1 ) );
			case COMP::SWITCH_DT:		return AllocatePins( static_cast<size_t>( 3 * ( GetCols() + 1 ) / 2 ) );
			case COMP::SWITCH_ST_DIP:	return AllocatePins( static_cast<size_t>( 2 * GetCols() ) );
			default:					return;
		}
	}
	void StretchWidth(bool bGrow, bool bUsePCBshapes = false)
	{
		FootPrint::StretchWidth(bGrow);
		SetDefaultShapes(bUsePCBshapes);	// Rebuild the shapes list
	}
	void SetDefaultPinLabels()
	{
		for (size_t i = 0, iSize = GetNumPins(); i < iSize; i++)
		{
			m_pinLabels[i] = CompTypes::GetDefaultPinLabel(i);
			m_pinAligns[i] = CompTypes::GetDefaultPinAlign(i, GetNumPins(), GetType());
		}
	}
	Rect GetFootprintRect() const
	{
		return Rect(GetRow(), GetLastRow(), GetCol(), GetLastCol());
	}
	bool GetIsTrueComp() const	// A true component has pins and "owns" the nodeIds on them
	{
		switch( GetType() )
		{
			case COMP::INVALID:
			case COMP::VERO_NUMBER:
			case COMP::VERO_LETTER:
			case COMP::MARK:
			case COMP::WIRE:
			case COMP::TRACKS:	return false;
			default:			return true;
		}
	}
	void SetFillColor(const MyRGB& r)	// Gives all shapes the same fill color
	{
		for (auto& o : m_shapes) o.SetFillColor(r);
	}
	MyRGB GetNewColor() const	// returns an un-used color
	{
		for (int iColor = 1; iColor <= 0xFFFFFF; iColor++)	// Black is used for outlines so start at 1
		{
			bool bOK(true);
			MyRGB tmp(iColor);
			for (const auto& o : m_shapes)
				if ( o.GetFillColor() == tmp ) { bOK = false; break; }
			if ( bOK ) return tmp;
		}
		assert(0);
		return MyRGB(0x000000);
	}
	// Merge interface functions
	virtual void UpdateMergeOffsets(MergeOffsets& o) override
	{
		FootPrint::UpdateMergeOffsets(o);	// Call UpdateMergeOffsets in base class

		if ( m_id != BAD_COMPID && m_id != TRAX_COMPID ) o.deltaCompId  = std::max(o.deltaCompId, m_id + 1);
//		o.deltaLyr = std::max(o.deltaLyr, m_lyr + GetCompLyrs() + 1);
		o.deltaRow = std::max(o.deltaRow, m_row + GetCompRows() + 1);
//		o.deltaCol = std::max(o.deltaCol, m_col + GetCompCols() + 1);
		for (size_t i = 0; i < GetNumPins(); i++)
		{
			if ( m_nodeIdPins[i]  != BAD_NODEID ) o.deltaNodeId = std::max(o.deltaNodeId, m_nodeIdPins[i]  + 1);
			if ( m_origIdPins1[i] != BAD_NODEID ) o.deltaNodeId = std::max(o.deltaNodeId, m_origIdPins1[i] + 1);
			if ( m_origIdPins2[i] != BAD_NODEID ) o.deltaNodeId = std::max(o.deltaNodeId, m_origIdPins2[i] + 1);
		}
	}
	virtual void ApplyMergeOffsets(const MergeOffsets& o) override
	{
		FootPrint::ApplyMergeOffsets(o);	// Call ApplyMergeOffsets in base class

		if ( m_id != BAD_COMPID && m_id != TRAX_COMPID) m_id += o.deltaCompId;
		m_lyr += o.deltaLyr;
		m_row += o.deltaRow;
		m_col += o.deltaCol;
		for (size_t i = 0; i < GetNumPins(); i++)
		{
			if ( m_nodeIdPins[i]  != BAD_NODEID ) m_nodeIdPins[i]  += o.deltaNodeId;
			if ( m_origIdPins1[i] != BAD_NODEID ) m_origIdPins1[i] += o.deltaNodeId;
			if ( m_origIdPins2[i] != BAD_NODEID ) m_origIdPins2[i] += o.deltaNodeId;
		}
	}
	void SetDefaultPinFlags();
	void SetDefaultStrings(bool bForce = true)
	{
		if ( GetPrefixStr().empty() || bForce )
			SetPrefixStr( CompTypes::GetDefaultPrefixStr( GetType() ) );
		if ( GetTypeStr().empty() || bForce )
			SetTypeStr( CompTypes::GetDefaultTypeStr( GetType() ) );
		if ( GetImportStr().empty() || bForce || GetType() != COMP::CUSTOM )
			SetImportStr( CompTypes::GetDefaultImportStr( GetType() ) );
	}
	void SetDefaultShapes(bool bUsePCBshapes = false);
	void SetDefaultColor();
	// Persist interface functions
	virtual void Load(DataStream& inStream) override
	{
		FootPrint::Load(inStream);	// Load() base class
		inStream.Load(m_id);
		if ( m_id == -2 ) SetType(COMP::TRACKS);	// Needed to stop old VRTs from crashing
		inStream.Load(m_nameStr);
		inStream.Load(m_valueStr);
		if ( inStream.GetVersion() >= VRT_VERSION_19 )
			inStream.Load(m_prefixStr);				// Added in VRT_VERSION_19
		if ( inStream.GetVersion() >= VRT_VERSION_18 )
		{
			inStream.Load(m_typeStr);				// Added in VRT_VERSION_18
			inStream.Load(m_importStr);				// Added in VRT_VERSION_18
		}
		m_lyr = 0;
		if ( inStream.GetVersion() >= VRT_VERSION_34 )
			inStream.Load(m_lyr);					// Added in VRT_VERSION_34
		inStream.Load(m_row);
		inStream.Load(m_col);
		inStream.Load(m_iLabelOffsetRow);
		inStream.Load(m_iLabelOffsetCol);
		if ( inStream.GetVersion() < VRT_VERSION_22 )	// Units changed from 1/4 square to 1/16 square in VRT_VERSION_22
		{
			m_iLabelOffsetRow *= 4;
			m_iLabelOffsetCol *= 4;
		}
		inStream.Load(m_direction);
		inStream.Load(m_bIsPlaced);
		if ( inStream.GetVersion() >= VRT_VERSION_19 )
			inStream.Load(m_iPinFlags);				// Added in VRT_VERSION_19
		m_iPadWidth  = 70;
		m_iHoleWidth = 40;
		if ( inStream.GetVersion() >= VRT_VERSION_39 )
		{
			inStream.Load(m_iPadWidth);				// Added in VRT_VERSION_39
			inStream.Load(m_iHoleWidth);			// Added in VRT_VERSION_39
		}
		m_bAllowFlyWire = false;
		if ( inStream.GetVersion() >= VRT_VERSION_47 )
			inStream.Load(m_bAllowFlyWire);			// Added in VRT_VERSION_47
		unsigned int numPins(0);
		inStream.Load(numPins);
		AllocatePins(numPins);
		for (unsigned int i = 0; i < numPins; i++)
		{
			inStream.Load(m_nodeIdPins[i]);
			inStream.Load(m_origIdPins1[i]);
			if ( inStream.GetVersion() >= VRT_VERSION_34 )
				inStream.Load(m_origIdPins2[i]);	// Added in VRT_VERSION_34
			if ( inStream.GetVersion() >= VRT_VERSION_45 )
				inStream.Load(m_layerPrefs[i]);		// Added in VRT_VERSION_45
			if ( inStream.GetVersion() >= VRT_VERSION_46 )
			{
				inStream.Load(m_pinOffsetRow[i]);	// Added in VRT_VERSION_46
				inStream.Load(m_pinOffsetCol[i]);	// Added in VRT_VERSION_46
			}
			if ( inStream.GetVersion() >= VRT_VERSION_7 )
				inStream.Load(m_pinLabels[i]);		// Added in VRT_VERSION_7
			if ( inStream.GetVersion() >= VRT_VERSION_30 )
				inStream.Load(m_pinAligns[i]);		// Added in VRT_VERSION_30
		}
		if ( inStream.GetVersion() >= VRT_VERSION_18 )
		{
			unsigned int numShapes(0);
			inStream.Load(numShapes);				// Added in VRT_VERSION_18
			AllocateShapes(numShapes);
			for (unsigned int i = 0; i < numShapes; i++)
				m_shapes[i].Load(inStream);			// Added in VRT_VERSION_18
		}
		if ( inStream.GetVersion() < VRT_VERSION_31 )
			HandleLegacyLabelOffsets();

		if ( inStream.GetVersion() < VRT_VERSION_55 )
			SetupOccupanciesTH();

		// Try to fix any missing definitions
		SetDefaultPinFlags();
		SetDefaultStrings(false);	// false ==> only set empty (m_prefixStr, m_guiStr, m_importStr)
		SetDefaultShapes();
	}
	virtual void Save(DataStream& outStream) override
	{
		FootPrint::Save(outStream);	// Save() base class
		outStream.Save(m_id);
		outStream.Save(m_nameStr);
		outStream.Save(m_valueStr);
		outStream.Save(m_prefixStr);			// Added in VRT_VERSION_19
		outStream.Save(m_typeStr);				// Added in VRT_VERSION_18
		outStream.Save(m_importStr);			// Added in VRT_VERSION_18
		outStream.Save(m_lyr);					// Added in VRT_VERSION_34
		outStream.Save(m_row);
		outStream.Save(m_col);
		outStream.Save(m_iLabelOffsetRow);
		outStream.Save(m_iLabelOffsetCol);
		outStream.Save(m_direction);
		outStream.Save(m_bIsPlaced);
		outStream.Save(m_iPinFlags);			// Added in VRT_VERSION_19
		outStream.Save(m_iPadWidth);			// Added in VRT_VERSION_39
		outStream.Save(m_iHoleWidth);			// Added in VRT_VERSION_39
		outStream.Save(m_bAllowFlyWire);		// Added in VRT_VERSION_47
		const unsigned int numPins = static_cast<unsigned int>( GetNumPins() );
		outStream.Save(numPins);
		for (unsigned int i = 0; i < numPins; i++)
		{
			outStream.Save(m_nodeIdPins[i]);
			outStream.Save(m_origIdPins1[i]);
			outStream.Save(m_origIdPins2[i]);	// Added in VRT_VERSION_34
			outStream.Save(m_layerPrefs[i]);	// Added in VRT_VERSION_45
			outStream.Save(m_pinOffsetRow[i]);	// Added in VRT_VERSION_46
			outStream.Save(m_pinOffsetCol[i]);	// Added in VRT_VERSION_46
			outStream.Save(m_pinLabels[i]);		// Added in VRT_VERSION_7
			outStream.Save(m_pinAligns[i]);		// Added in VRT_VERSION_30
		}
		const unsigned int numShapes = static_cast<unsigned int>( GetNumShapes() );
		outStream.Save(numShapes);				// Added in VRT_VERSION_18
		for (unsigned int i = 0; i < numShapes; i++)
			m_shapes[i].Save(outStream);		// Added in VRT_VERSION_18
	}
private:
	int							m_id;				// Component ID.  (BAD_COMPID ==> a component template)
	std::string					m_nameStr;			// Name label
	std::string					m_valueStr;			// Value label
	std::string					m_prefixStr;		// The prefix for new components (overridden for CUSTOM components).
	std::string					m_typeStr;			// The footprint type (overridden for CUSTOM components).
	std::string					m_importStr;		// Protel/Tango/OrcadPCB2 footprint name. Only for CUSTOM components !!!
	std::vector<int>			m_nodeIdPins;		// NodeIds of the pins
	std::vector<int>			m_origIdPins1;		// NodeIds under the pins BEFORE placement (1st layer)
	std::vector<int>			m_origIdPins2;		// NodeIds under the pins BEFORE placement (2nd Layer)
	std::vector<uchar>			m_layerPrefs;		// Prefered layers of the pins
	std::vector<int>			m_pinOffsetRow;		// Pin row offset (-50 mil to +50 mil) to allow pad shifts in PCB mode
	std::vector<int>			m_pinOffsetCol;		// Pin row offset (-50 mil to +50 mil) to allow pad shifts in PCB mode
	std::vector<std::string>	m_pinLabels;		// Pin labels
	std::vector<int>			m_pinAligns;		// Pin label alignments (Qt::AlignLeft,Qt::AlignRight,Qt::AlignHCenter)
	std::vector<Shape>			m_shapes;			// For rendering components. Coordinates are RELATIVE to footprint centre.
	uchar						m_iPinFlags;		// 1 ==> PIN_RECT, 2 ==> PIN_LABELS, 4 ==> PIN_CUSTOM
	int							m_iPadWidth;		// Used if the PIN_CUSTOM flag is set
	int							m_iHoleWidth;		// Used if the PIN_CUSTOM flag is set
	bool						m_bAllowFlyWire;	// For single pin parts only
	// Current placement in board
	int							m_lyr;				// Board layer for component
	int							m_row;				// Board row for top-left element of footprint
	int							m_col;				// Board col for top-left element of footprint
	int							m_iLabelOffsetRow;	// Label offset in units of 1/16 of a grid square
	int							m_iLabelOffsetCol;	// Label offset in units of 1/16 of a grid square
	char						m_direction;		// Component orientation:  'W', 'E', 'N', 'S'
	bool						m_bIsPlaced;		// true ==> placed on board,  false ==> floating
};