File: Element.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 (527 lines) | stat: -rw-r--r-- 21,742 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
/*
	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 "Pin.h"
#include "TrackElement.h"

// The board is basically a Grid of "Element" objects.
// "Element" derives from "Pin" and therefore has a description
// of the surface at a location, and the pin index there (if any).
// Each Element in the grid is "glued" to (i.e. has pointers to) it's neighbours.
// There are always 8 "same-layer" neighbours.
// For 2-layer boards, there is an additional "other layer" neighbour.
// Wires (jumpers) "glue" remote elements together within layer 0 (i.e. the base layer).

// This makes all the routing/connectivity code tidy because
// each Element knows what it can be connected to without having
// to go through the parent Grid object.

Q_DECL_CONSTEXPR static const int			TRAX_COMPID	= -2;		// The component manager member m_trax has this ID
Q_DECL_CONSTEXPR static const int			BAD_COMPID	= -1;		// Invalid component ID
Q_DECL_CONSTEXPR static const unsigned int	BAD_ROUTEID	= UINT_MAX;	// Invalid route (i.e. track section) ID
Q_DECL_CONSTEXPR static const unsigned int	BAD_MH		= UINT_MAX;	// "Infinite" MH distance

class Element;

// Quicker to use struct than a std::pair
struct ElementInt
{
	ElementInt(const Element* p, unsigned int i) : first(p), second(i) {}
	const Element*	first;
	unsigned int	second;
};

// Quicker to use a list than an unordered_map since list is typically small
typedef std::list<ElementInt> WIRELIST;	// Helper for chains of wires

class Element : public Pin, public TrackElement
{
public:
	// Debug methods that avoid tunneling through layers
	size_t	GetPinIndexRaw() const	{ return Pin::GetPinIndex(); }
	size_t	GetPinIndex2Raw() const	{ return ( m_pinChar2 == BAD_PINCHAR ) ? BAD_PININDEX : m_pinChar2; }
	int		GetCompIdRaw() const	{ return m_compId; }
	int		GetCompId2Raw() const	{ return m_compId2; }
	uchar	GetSurfaceRaw() const	{ return Pin::GetSurface(); }
	uchar	GetHoleUseRaw() const	{ return Pin::GetHoleUse(); }
	uchar	GetSoicCharRaw() const	{ return Pin::GetSoicChar(); }
	int		GetNodeIdRaw() const	{ return TrackElement::GetNodeId(); }
	void		 SetSurface(uchar c)		{ return GetBase()->Pin::SetSurface(c); }
	void		 SetHoleUse(uchar c)		{ return GetBase()->Pin::SetHoleUse(c); }
	void		 SetSoicChar(uchar c)		{ return GetBase()->Pin::SetSoicChar(c); }
	void		 SetOccupancyTH(bool bWire)	{ return GetBase()->Pin::SetOccupancyTH(bWire); }
	const uchar& GetSurface() const			{ return GetBaseConst()->Pin::GetSurface(); }
	const uchar& GetHoleUse() const			{ return GetBaseConst()->Pin::GetHoleUse(); }
	bool		 GetIsHole() const			{ return GetBaseConst()->Pin::GetIsHole(); }
	uchar		 GetSoicChar() const		{ return GetBaseConst()->Pin::GetSoicChar(); }
	bool		 GetIsBotLyr() const		{ const Element* const p = GetNbr(NBR_X);	return p == nullptr || p > this; }
	bool		 GetIsTopLyr() const		{ const Element* const p = GetNbr(NBR_X);	return p != nullptr && p < this; }
	bool		 GetSoicProtected() const	{ return GetSoicChar() & ( GetIsTopLyr() ? SOIC_TRACKS_TOP : SOIC_TRACKS_BOT ); }
	const int&	 GetNodeId() const
	{
		auto pBase = GetBaseConst();	return ( pBase != this && GetHasPinTH() ) ? pBase->TrackElement::GetNodeId() : TrackElement::GetNodeId();
	}
	void		 SetNodeId(int i)	// Only called via the parent board method Board::SetNodeId()
	{
		TrackElement::SetNodeId(i);

		// Update usage flags for connections emanating from "this" element.
		for (int iNbr = 0; iNbr < NUM_NBRS; iNbr++)
			if ( GetNbr(iNbr) ) UpdateUsed(iNbr);

		// Update usage flags for diagonals that cut across the LT,RT,LB,RB diagonals.
		// Call these LTX,RTX,LBX,RBX respectively.
		// The point of doing this is that if "this" element has a diagonal connection that
		// we've just cleared, then previously blocked diagonals may now be usable.
		GetNbr(NBR_L)->UpdateUsed(NBR_RT);	GetNbr(NBR_R)->UpdateUsed(NBR_LT);	// LTX, RTX
		GetNbr(NBR_L)->UpdateUsed(NBR_RB);	GetNbr(NBR_R)->UpdateUsed(NBR_LB);	// LBX, RBX

		auto pBase = GetBase();	if ( pBase != this && GetHasPinTH() ) pBase->SetNodeId(i);
	}

	Element() : Pin(), TrackElement() { ZeroConnectionPointers(); }
	Element(const Element& o) : Pin(o), TrackElement(o)	{ *this = o; }	// This is never used
	virtual ~Element() override {}
	void ZeroConnectionPointers()
	{
		memset(m_pNbr,	0, NUM_NBRS * sizeof(Element*));
		memset(m_pW,	0, 2 * sizeof(Element*));
	}
	Element& operator=(const Element& o)
	{
		Pin::operator=(o);			// Call operator= in base class
		TrackElement::operator=(o);	// Call operator= in base class
		m_bIsMark	= o.m_bIsMark;
		m_compId	= o.m_compId;
		m_compId2	= o.m_compId2;
		m_pinChar2	= o.m_pinChar2;
		m_bSolderR	= o.m_bSolderR;
		m_bIsVia	= o.m_bIsVia;
//		m_iRoutable	= o.m_iRoutable;	// This should only be set by the Board::Glue() method
		m_MH		= o.m_MH;
		m_maxMH		= o.m_maxMH;
		m_routeId	= o.m_routeId;
		// Zero the connection pointers m_pNbr[] and m_pW[].
		// These should only be set by Board::GlueNbrs() and Board::GlueWires().
		// m_pW can also be modified by the methods Board::PutDown() and Board::TakeOff().
		ZeroConnectionPointers();
		return *this;
	}
	bool operator==(const Element& o) const	// Compare persisted info only
	{
		return	Pin::operator==(o)
			&&	TrackElement::operator==(o)
			&&	m_bIsMark	== o.m_bIsMark
			&&	m_compId	== o.m_compId
			&&	m_compId2	== o.m_compId2
			&&	m_pinChar2	== o.m_pinChar2;
	}
	bool operator!=(const Element& o) const
	{
		return !(*this == o);
	}
	void SetIsMark(bool b)			{ GetBase()->m_bIsMark = b; }
	void SetSolderR(bool b)			{ GetBase()->m_bSolderR = b; }
	void SetIsVia(bool b)			{ GetBase()->m_bIsVia	= b; }
	void SetRoutable(int i)			{ m_iRoutable	= i; }
	void SetRouteId(unsigned int i)	{ m_routeId = i; }
	void ResetMH()
	{
		m_routeId	= BAD_ROUTEID;	// Wipe RouteId
		m_MH		= BAD_MH;		// Set "infinite" MH distance.
		m_maxMH		= 0;			// Zero max MH parameter
	}
	void UpdateMH(unsigned int iRouteID, unsigned int iMH, unsigned int& iMaxMH)
	{
		assert( m_MH == BAD_MH );	// Should only ever write the MH once
		iMaxMH		= std::max(iMaxMH, iMH);	// Update iMaxMH for output before storing it
		m_routeId	= iRouteID;
		m_MH		= iMH;
		m_maxMH		= iMaxMH;
	}
	void SetNbr(int iNbr, Element* p)	{ m_pNbr[iNbr]	= p; }
	void ClearWires()			{ SetW(0, nullptr);	SetW(1, nullptr); }
	bool GetHasWire() const		{ return GetW(0) != nullptr || GetW(1) != nullptr; }
	int GetNumWires() const
	{
		int nCount(0);
		for (int iSlot = 0; iSlot < 2; iSlot++) if ( GetW(iSlot) != nullptr ) nCount++;
		return nCount;
	}
	int GetNumUsedSlots() const
	{
		int nCount(0);
		for (int iSlot = 0; iSlot < 2; iSlot++) if ( GetSlotCompId(iSlot) != BAD_COMPID ) nCount++;
		return nCount;
	}
	int GetFirstUsedSlot() const
	{
		for (int iSlot = 0; iSlot < 2; iSlot++) if ( GetSlotCompId(iSlot) != BAD_COMPID ) return iSlot;
		assert(0);
		return -1;
	}
	int GetFreeSlot() const
	{
		for (int iSlot = 0; iSlot < 2; iSlot++) if ( GetSlotCompId(iSlot) == BAD_COMPID ) return iSlot;
		assert(0);
		return -1;
	}
	int GetSlotFromCompId(int compId) const
	{
		assert( GetCompId() != GetCompId2() || GetCompId() == BAD_COMPID );
		for (int iSlot = 0; iSlot < 2; iSlot++) if ( GetSlotCompId(iSlot) == compId ) return iSlot;
		return -1;
	}
	void SetSlotInfo(int iSlot, size_t pinIndex, int compId)
	{
		SetSlotPinIndex(iSlot, pinIndex);
		SetSlotCompId(iSlot, compId);
	}
	void SetSlotPinIndex(int iSlot, size_t pinIndex)
	{
		switch( iSlot )
		{
			case 0:		return SetPinIndex(pinIndex);
			case 1:		return SetPinIndex2(pinIndex);
			default:	assert(0);
		}
	}
	void SetSlotCompId(int iSlot, int compId)
	{
		switch( iSlot )
		{
			case 0:		return SetCompId(compId);
			case 1:		return SetCompId2(compId);
			default:	assert(0);
		}
	}
	void GetSlotInfo(int iSlot, size_t& pinIndex, int& compId) const
	{
		switch( iSlot )
		{
			case 0:		pinIndex = GetPinIndex();	compId = GetCompId();	return;
			case 1:		pinIndex = GetPinIndex2();	compId = GetCompId2();	return;
			default:	pinIndex = BAD_PININDEX;	compId = BAD_COMPID;	assert(0);
		}
	}
	size_t GetSlotPinIndex(int iSlot) const
	{
		switch( iSlot )
		{
			case 0:		return GetPinIndex();
			case 1:		return GetPinIndex2();
			default:	assert(0);	return BAD_PININDEX;
		}
	}
	int GetSlotCompId(int iSlot) const
	{
		switch( iSlot )
		{
			case 0:		return GetCompId();
			case 1:		return GetCompId2();
			default:	assert(0);	return BAD_COMPID;
		}
	}
	bool GetWireExists(const Element* p) const
	{
		return p != nullptr && ( GetW(0) == p || GetW(1) == p );
	}
	bool GetCompExists(int compId) const
	{
		return compId != BAD_COMPID && ( GetCompId() == compId || GetCompId2() == compId );
	}
	void SetW(int iSlot, Element* p)
	{
		assert( !GetWireExists(p) );	// No duplicates allowed
		assert( iSlot == 0 || iSlot == 1 );
		GetBase()->m_pW[iSlot] = p;
	}
	void				SetMH(unsigned int iMH)	{ m_MH = iMH; }
	const bool&			GetIsMark() const		{ return GetBaseConst()->m_bIsMark; }
	int					GetNumCompIds() const	{ int i(0); if ( GetCompId() != BAD_COMPID ) i++; if ( GetCompId2() != BAD_COMPID ) i++; return i; }
	bool				GetHasComp() const		{ return GetCompId() != BAD_COMPID || GetCompId2() != BAD_COMPID; }
	bool				GetHasPinLegacy() const	{ return GetIsPin() || GetIsPin2(); }	// Only kept for legacy purposes (e.g. old VRTs don't have SOIC codes)
	bool				GetHasPin() const		{ return GetSoicChar() & (SOIC_PAD|SOIC_THL); }	// true ==> Have TH pin/SOIC pad on either layer
	bool				GetHasPinTH() const		{ return GetSoicChar() & SOIC_THL; }			// true ==> Have TH pin on either layer
	bool				GetHasPinSOIC() const	{ return GetSoicChar() & SOIC_PAD; }			// true ==> Have SOIC pad on either layer
	bool				GetLyrHasPin() const	{ return GetHasPinTH() || ( GetHasPinSOIC() && GetIsTopLyr() ); }
	const bool&			GetSolderR() const		{ return GetBaseConst()->m_bSolderR; }
	const bool&			GetIsVia() const		{ return GetBaseConst()->m_bIsVia; }
	const int&			GetRoutable() const		{ return m_iRoutable; }
	const unsigned int&	GetRouteId() const		{ return m_routeId; }
	const unsigned int&	GetMH() const			{ return m_MH; }
	const unsigned int&	GetMaxMH() const		{ return m_maxMH; }
	Element*			GetNbr(int iNbr) const	{ return m_pNbr[iNbr]; }
	Element*			GetW(int iSlot) const	{ return GetBaseConst()->m_pW[iSlot]; }
	bool				GetPinSupportsOffsetPads() const	{ return GetHasPinTH() && !GetHasWire(); }
	bool				GetPinSupportsLayerPref() const		{ return GetHasPinTH() && !GetHasWire(); }
	// Helpers
	bool HaveNoBlankPins(int iNbr) const
	{
		const Element* pLyr = this;
		const Element* pNbr = pLyr->GetNbr(iNbr);
		return	( !pLyr->GetLyrHasPin() || pLyr->GetNodeId() != BAD_NODEID || pLyr->GetHasWire() ) &&	// Only allow routing FROM blank pins if they are on wires
				( !pNbr->GetLyrHasPin() || pNbr->GetNodeId() != BAD_NODEID || pNbr->GetHasWire() );	// Only allow routing  TO  blank pins if they are on wires
	}
	void GetWireList(WIRELIST& wireList) const
	{
		wireList.clear();
		return UpdateWireList(wireList, 0);
	}
	// Connectivity helpers
	void UpdateUsed(int iNbr)
	{
		const bool bUsed = GetNodeId() != BAD_NODEID
						&& GetNodeId() == GetNbr(iNbr)->GetNodeId()
						&& !IsBlocked(iNbr, GetNodeId());
		SetUsed(iNbr, bUsed);
		m_pNbr[iNbr]->SetUsed(Opposite(iNbr), bUsed);	// Keep consistent with nbr
	}
	void ToggleUsed(int iNbr)
	{
		ToggleCodeBit(iNbr, m_iCode);
		ToggleCodeBit(Opposite(iNbr), m_pNbr[iNbr]->m_iCode);	// Keep consistent with nbr
		// Toggles are only done by the user so set the flag accordingly
		Element* pNbr = GetNbr(iNbr);
		WipeFlagBits(AUTOSET|VEROSET);			MarkFlagBits(USERSET);
		pNbr->WipeFlagBits(AUTOSET|VEROSET);	pNbr->MarkFlagBits(USERSET);
		// Handle wire ends
		Element* pW = GetW(0);	if ( pW ) { pW->WipeFlagBits(AUTOSET|VEROSET);	pW->MarkFlagBits(USERSET); }
		pW = GetW(1);			if ( pW ) { pW->WipeFlagBits(AUTOSET|VEROSET);	pW->MarkFlagBits(USERSET); }
		pW = pNbr->GetW(0);		if ( pW ) { pW->WipeFlagBits(AUTOSET|VEROSET);	pW->MarkFlagBits(USERSET); }
		pW = pNbr->GetW(1);		if ( pW ) { pW->WipeFlagBits(AUTOSET|VEROSET);	pW->MarkFlagBits(USERSET); }
	}
	bool CanSwapDiagLinks()
	{
		// Take "this" to be the bottom right element in group of 4 squares
		// "LT"  is the diagonal from "this" to m_pLT
		// "LTX" is the diagonal that cuts across it (from m_pL to m_pT)
		return	GetNodeId() == GetNbr(NBR_LT)->GetNodeId()					&&	// LT:  "this" and LT must have same nodeId
				GetNbr(NBR_L)->GetNodeId() == GetNbr(NBR_T)->GetNodeId()	&&	// LTX: L and T must have same nodeId
				GetNbr(NBR_L)->IsClash( GetNodeId() );							// L and "this" must have clashing nodeIds
	}
	bool SwapDiagLinks()
	{
		if ( !CanSwapDiagLinks() ) return false;

		// Swap (by inverting flags) if we have competing diagonals
		ToggleUsed(NBR_LT);
		GetNbr(NBR_L)->ToggleUsed(NBR_RT);
		return true;
	}
	bool IsDiagNbr(const Element* p) const
	{
		assert( p != nullptr );	// Sanity check
		for (int iNbr = 1; iNbr < 8; iNbr += 2)
			if ( GetNbr(iNbr) == p ) return true;
		return false;
	}
	bool IsNbr(const Element* p) const
	{
		assert( p != nullptr );	// Sanity check
		for (int iNbr = 0; iNbr < NUM_NBRS; iNbr++)
			if ( GetNbr(iNbr) == p ) return true;
		return false;
	}
	bool IsUselessWire(int iNbr, int nodeId) const	// Helper: true ==> painting nbr with nodeId is wasteful
	{
		const Element* pWA = GetNbr(iNbr);
		if ( pWA->GetHasWire() )
		{
			const Element* pWB0 = pWA->GetW(0);
			const Element* pWB1 = pWA->GetW(1);
			// pWA and pWB are opposite ends of a wire.
			// If these ends both neighbour a common element with the specified nodeId,
			// then it is wasteful to paint the wire with that nodeId too, since the
			// common element already provides a connection.
			for (int iNbr = 0; iNbr < NUM_NBRS; iNbr++)
			{
				const Element* p = pWA->GetNbr(iNbr);
				if ( p == nullptr || p->GetNodeId() != nodeId ) continue;
				if ( pWB0 != nullptr && pWB0->IsNbr(p) ) return true;
				if ( pWB1 != nullptr && pWB1->IsNbr(p) ) return true;
			}
		}
		return false;
	}
	bool IsBlocked(int iNbr, int nodeId) const	// Helper: true ==> assiging nodeId to "this" blocks the iNbr direction
	{
		assert(nodeId != BAD_NODEID);
		if ( !ReadCodeBit(iNbr, GetRoutable() ) ) return true;	// Block toroidal connections at board edges
		auto pNbr = GetNbr(iNbr);
		if ( pNbr->IsClash(nodeId) ) return true;		// Check if nbr has a clashing nodeId assigned to it
		if ( pNbr->GetIsHole() ) return true;			// Block connections to holes
		if ( pNbr->GetSoicProtected() ) return true;	// Block connections to SOIC area

		switch( iNbr )	// Block diagonals crossing the SOIC area
		{
			case NBR_LT: if ( GetNbr(NBR_L)->GetSoicProtected() || GetNbr(NBR_T)->GetSoicProtected() ) return true;	break;
			case NBR_RT: if ( GetNbr(NBR_R)->GetSoicProtected() || GetNbr(NBR_T)->GetSoicProtected() ) return true;	break;
			case NBR_LB: if ( GetNbr(NBR_L)->GetSoicProtected() || GetNbr(NBR_B)->GetSoicProtected() ) return true;	break;
			case NBR_RB: if ( GetNbr(NBR_R)->GetSoicProtected() || GetNbr(NBR_B)->GetSoicProtected() ) return true;	break;
		}

		switch( iNbr )	// Then do additional checks for competing diagonals
		{
			case NBR_LT: return GetNbr(NBR_L)->IsClash(nodeId) && GetNbr(NBR_L)->GetUsed(NBR_RT);
			case NBR_RT: return GetNbr(NBR_R)->IsClash(nodeId) && GetNbr(NBR_R)->GetUsed(NBR_LT);
			case NBR_LB: return GetNbr(NBR_L)->IsClash(nodeId) && GetNbr(NBR_L)->GetUsed(NBR_RB);
			case NBR_RB: return GetNbr(NBR_R)->IsClash(nodeId) && GetNbr(NBR_R)->GetUsed(NBR_LB);
			default:	 return false;
		}
	}
	// Merge interface functions
	virtual void UpdateMergeOffsets(MergeOffsets& o) override
	{
		Pin::UpdateMergeOffsets(o);	// Does nothing
		TrackElement::UpdateMergeOffsets(o);
		if ( m_compId != BAD_COMPID && m_compId != TRAX_COMPID )
			o.deltaCompId = std::max(o.deltaCompId,  m_compId + 1);
		assert( m_compId2 != TRAX_COMPID );
		if ( m_compId2 != BAD_COMPID && m_compId2 != TRAX_COMPID )
			o.deltaCompId = std::max(o.deltaCompId,  m_compId2 + 1);
	}
	void FixCorruption()
	{
		const bool bOK = GetCompId()	== BAD_COMPID	&&
						 GetCompId2()	== BAD_COMPID	&&
						 GetPinIndex()	== BAD_PININDEX	&&
						 GetPinIndex2()	== BAD_PININDEX	&&
						 GetSurface()	== SURFACE_FREE	&&
						 GetHoleUse()	== HOLE_FREE	&&
						 GetSoicChar()	== SOIC_FREE	&&
						 GetIsMark()	== false;
		if ( !bOK )
		{
			SetCompId(BAD_COMPID);
			SetCompId2(BAD_COMPID);
			SetPinIndex(BAD_PININDEX);
			SetPinIndex2(BAD_PININDEX);
			SetSurface(SURFACE_FREE);
			SetHoleUse(HOLE_FREE);
			SetSoicChar(SOIC_FREE);
			SetIsMark(false);
		}
	}
	virtual void ApplyMergeOffsets(const MergeOffsets& o) override
	{
		Pin::ApplyMergeOffsets(o);	// Does nothing
		TrackElement::ApplyMergeOffsets(o);
		if ( m_compId != BAD_COMPID	&& m_compId != TRAX_COMPID)
			m_compId += o.deltaCompId;
		assert( m_compId2 != TRAX_COMPID );
		if ( m_compId2 != BAD_COMPID && m_compId2 != TRAX_COMPID)
			m_compId2 += o.deltaCompId;
	}
	void Merge(const Element& o)
	{
		Pin::Merge(o);
		TrackElement::Merge(o);
		m_bIsMark	= o.m_bIsMark;
		m_compId	= o.m_compId;
		m_compId2	= o.m_compId2;
		m_pinChar2	= o.m_pinChar2;
	}
	// Persist interface functions
	virtual void Load(DataStream& inStream) override
	{
		if ( inStream.GetVersion() < VRT_VERSION_25 )
		{
			Pin::Load(inStream);			// Load() base class
			inStream.Load(m_compId);
			TrackElement::Load(inStream);	// Load() base class
			inStream.Load(m_bIsMark);
		}
		else
		{
			Pin::Load(inStream);			// Load() base class
			TrackElement::Load(inStream);	// Load() base class
			inStream.Load(m_bIsMark);
			inStream.Load(m_compId);
		}
		m_compId2	= BAD_COMPID;
		m_pinChar2	= BAD_PINCHAR;
		if ( inStream.GetVersion() >= VRT_VERSION_27 )
		{
			inStream.Load(m_compId2);	// Added in VRT_VERSION_27
			inStream.Load(m_pinChar2);	// Added in VRT_VERSION_27
		}
	}
	virtual void Save(DataStream& outStream) override
	{
		Pin::Save(outStream);				// Save() base class
		TrackElement::Save(outStream);		// Save() base class
		outStream.Save(m_bIsMark);
		outStream.Save(m_compId);
		outStream.Save(m_compId2);		// Added in VRT_VERSION_27
		outStream.Save(m_pinChar2);		// Added in VRT_VERSION_27
	}
private:
	Element*		GetBase()				{ Element*		 p = GetNbr(NBR_X);	return p == nullptr || p > this ? this : p; }
	const Element*	GetBaseConst() const	{ const Element* p = GetNbr(NBR_X);	return p == nullptr || p > this ? this : p; }
	const int&		GetCompId() const		{ return GetBaseConst()->m_compId; }
	const int&		GetCompId2() const		{ return GetBaseConst()->m_compId2; }
	const uchar&	GetPinChar2() const		{ return GetBaseConst()->m_pinChar2; }
	bool			GetIsPin() const		{ return GetBaseConst()->Pin::GetIsPin(); }
	bool			GetIsPin2() const		{ return GetPinChar2() != BAD_PINCHAR; }
	size_t			GetPinIndex() const		{ return GetBaseConst()->Pin::GetPinIndex(); }
	size_t			GetPinIndex2() const	{ return ( GetPinChar2() == BAD_PINCHAR ) ? BAD_PININDEX : GetPinChar2(); }
	void			SetCompId(int i)		{ GetBase()->m_compId  = i; }
	void			SetCompId2(int i)		{ GetBase()->m_compId2 = i; }
	void			SetPinIndex(size_t i)	{ return GetBase()->Pin::SetPinIndex(i); }
	void			SetPinIndex2(size_t i)	{ GetBase()->m_pinChar2 = ( i >= BAD_PINCHAR ) ? BAD_PINCHAR : static_cast<uchar> (i); }
	bool WireListHelper(WIRELIST& wireList, const Element* p, unsigned int iStep) const
	{
		for (auto& o : wireList)
		{
			if ( o.first != p ) continue;
			if ( iStep < o.second )	{ o.second = iStep; return true; } else return false;
		}
		wireList.push_back(ElementInt(p, iStep));
		return true;
	}
	void UpdateWireList(WIRELIST& wireList, unsigned int iStep) const
	{
		WireListHelper(wireList, this, iStep);
		const bool bOK_0 = GetW(0) != nullptr && WireListHelper(wireList, GetW(0), iStep + 1);
		const bool bOK_1 = GetW(1) != nullptr && WireListHelper(wireList, GetW(1), iStep + 1);

		if ( bOK_0 ) GetW(0)->UpdateWireList(wireList, iStep + 1);
		if ( bOK_1 ) GetW(1)->UpdateWireList(wireList, iStep + 1);
	}
private:
	// Persist info
	bool			m_bIsMark	= false;
	int				m_compId	= BAD_COMPID;	// For elements with a valid pinindex, this is the ID of the parent component
	int				m_compId2	= BAD_COMPID;	// Only used when we have 2 wires sharing a hole
	uchar			m_pinChar2	= BAD_PINCHAR;	// Only used when we have 2 wires sharing a hole

	// Working variables.	Don't persist.
	bool			m_bSolderR	= false;		// true ==> have blob of solder to right (for joining vero tracks)
	bool			m_bIsVia	= false;		// true ==> have a (candidate) via between layers
	int				m_iRoutable	= 0;			// Set by Board::GlueNbrs().  Code bits used to enable/disable connections to neighbours
	unsigned int	m_routeId	= BAD_ROUTEID;	// For the routing algorithm.
	unsigned int	m_MH		= BAD_MH;		// Manhatten distance to another element.  For the routing/connectivity algorithm.
	unsigned int	m_maxMH		= 0;			// For the routing algorithm.
	// Connection pointers. Set by Board::GlueNbrs() and Board::GlueWires().	Don't persist.
	Element*		m_pNbr[static_cast<size_t>(NUM_NBRS)];	// 0 to 7 <==> NBR_L to NBR_LB,	  8 ==> NBR_X
	Element*		m_pW[2];								// Up to 2 wires per element. These point to the other end of the wire(s).
};