1 /*
2 	VeroRoute - Qt based Veroboard/Perfboard/PCB layout & routing application.
3 
4 	Copyright (C) 2017  Alex Lawrow    ( dralx@users.sourceforge.net )
5 
6 	This program is free software: you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation, either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #pragma once
21 
22 #include "FootPrint.h"
23 #include "RectManager.h"
24 #include "CompDefiner.h"
25 
26 // For 2-layer boards, the following says which is the prefered layer for a pin to make connections.
27 // This only affects rendered connections between adjacent pins..
28 // It does not affect connectivity or routing.
29 // The joint preference of adjacent pins determines which layers will show the connection.
30 static const uchar	LAYER_X = 0;	// No preference
31 static const uchar	LAYER_B	= 1;	// Prefer bottom layer
32 static const uchar	LAYER_T	= 2;	// Prefer top layer
33 
34 static const int MAX_PAD_OFFSET_MIL = 50;
35 
36 class CompManager;
37 
38 // Class to describe a component.
39 // Wires (i.e. jumpers) and markers also use this class even though they are not true components.
40 
41 class Component : public FootPrint
42 {
43 public:
Component()44 	Component()	: FootPrint() { Clear(); }
~Component()45 	virtual ~Component() override {}
Component(const Component & o)46 	Component(const Component& o) : FootPrint() { *this = o; }
Clear()47 	void Clear()
48 	{
49 		FootPrint::DeAllocate();
50 		SetType(COMP::INVALID);
51 		m_id		= 0;
52 		m_nameStr	= m_valueStr = m_prefixStr = m_typeStr = m_importStr = "";
53 		m_lyr = m_row = m_col = m_iLabelOffsetRow = m_iLabelOffsetCol = 0;
54 		m_direction		= 'W';
55 		m_bIsPlaced		= false;
56 		m_iPinFlags		= 0;
57 		m_iPadWidth		= 70;
58 		m_iHoleWidth	= 40;
59 		m_bAllowFlyWire	= false;
60 		m_nodeIdPins.clear();
61 		m_origIdPins1.clear();
62 		m_origIdPins2.clear();
63 		m_layerPrefs.clear();
64 		m_pinOffsetRow.clear();
65 		m_pinOffsetCol.clear();
66 		m_pinLabels.clear();
67 		m_pinAligns.clear();
68 		m_shapes.clear();
69 	}
Component(const CompDefiner & definer)70 	Component(const CompDefiner& definer)	// This method is for building a custom component
71 	{
72 		Clear();
73 
74 		definer.Build(*this);		// Use component definer to make the footprint and shapes
75 	}
Component(CompManager * pCompMgr,const RectManager & rectMgr,const ElementGrid & grid,const int & nLyr,const int & nRowMin,const int & nRowMax,const int & nColMin,const int & nColMax)76 	Component(CompManager* pCompMgr, const RectManager& rectMgr, const ElementGrid& grid, const int& nLyr, const int& nRowMin, const int& nRowMax, const int& nColMin, const int& nColMax)
77 	{
78 		Clear();
79 
80 		BuildTrax(pCompMgr, rectMgr, grid, nLyr, nRowMin, nRowMax, nColMin, nColMax);	// Build method for "tracks" component
81 
82 		SetDefaultStrings();
83 
84 		m_lyr = nLyr;
85 		m_row = nRowMin;
86 		m_col = nColMin;
87 		m_bIsPlaced = false;	// Trax are always created from the board
88 	}
Component(const std::string & name,const std::string & value,const COMP & eType,std::vector<int> & nodeIdPins)89 	Component(const std::string& name, const std::string& value, const COMP& eType, std::vector<int>& nodeIdPins)
90 	{
91 		Clear();
92 
93 		BuildDefault(eType);	// Build method for default component
94 
95 		SetAllowFlyWire(eType == COMP::PAD_FLYINGWIRE);
96 		SetDefaultPinFlags();
97 		SetDefaultStrings();
98 		SetDefaultLabelOffsets();
99 
100 		const size_t numPins = nodeIdPins.size();
101 		if ( eType == COMP::DIP || eType == COMP::SIP || eType == COMP::SWITCH_ST_DIP )	// Resize DIP/SIP/SWITCH_DIP as needed
102 		{
103 			const int reqLength = static_cast<int>( ( eType == COMP::SIP ) ? numPins : numPins / 2 );
104 			while ( GetCols() < reqLength )	Stretch(true);	// true  ==> grow
105 			while ( GetCols() > reqLength )	Stretch(false);	// false ==> shrink
106 		}
107 		if ( eType == COMP::STRIP_100 || eType == COMP::BLOCK_100 || eType == COMP::BLOCK_200 )
108 		{
109 			const int reqLength = static_cast<int>( ( eType == COMP::BLOCK_200 ) ? 1 + 2 * numPins : numPins );
110 			while ( GetCols() < reqLength )	Stretch(true);	// true  ==> grow
111 			while ( GetCols() > reqLength )	Stretch(false);	// false ==> shrink
112 		}
113 		if ( eType == COMP::SWITCH_ST || eType == COMP::SWITCH_DT )	// Resize non-DIP switches
114 		{
115 			const int numPoles  = static_cast<int>( ( eType == COMP::SWITCH_ST ) ? numPins / 2 : numPins / 3 );
116 			const int reqLength = ( 2 * numPoles ) - 1;
117 			while ( GetCols() < reqLength )	Stretch(true);	// true  ==> grow
118 			while ( GetCols() > reqLength )	Stretch(false);	// false ==> shrink
119 		}
120 		m_nameStr	= name;
121 		m_valueStr	= value;
122 		AllocatePins( numPins );
123 		std::copy(nodeIdPins.begin(), nodeIdPins.end(), m_nodeIdPins.begin());
124 
125 		SetDefaultShapes();
126 	}
127 	Component& operator=(const Component& o)
128 	{
129 		FootPrint::operator=(o);	// Call operator= in base class
130 		m_id				= o.m_id;
131 		m_nameStr			= o.m_nameStr;
132 		m_valueStr			= o.m_valueStr;
133 		m_prefixStr			= o.m_prefixStr;
134 		m_typeStr			= o.m_typeStr;
135 		m_importStr			= o.m_importStr;
136 		m_lyr				= o.m_lyr;
137 		m_row				= o.m_row;
138 		m_col				= o.m_col;
139 		m_iLabelOffsetRow	= o.m_iLabelOffsetRow;
140 		m_iLabelOffsetCol	= o.m_iLabelOffsetCol;
141 		m_direction			= o.m_direction;
142 		m_bIsPlaced			= o.m_bIsPlaced;
143 		m_iPinFlags			= o.m_iPinFlags;
144 		m_iPadWidth			= o.m_iPadWidth;
145 		m_iHoleWidth		= o.m_iHoleWidth;
146 		m_bAllowFlyWire		= o.m_bAllowFlyWire;
147 		AllocatePins( o.GetNumPins() );
148 		std::copy(o.m_nodeIdPins.begin(),	o.m_nodeIdPins.end(),	m_nodeIdPins.begin());
149 		std::copy(o.m_origIdPins1.begin(),	o.m_origIdPins1.end(),	m_origIdPins1.begin());
150 		std::copy(o.m_origIdPins2.begin(),	o.m_origIdPins2.end(),	m_origIdPins2.begin());
151 		std::copy(o.m_layerPrefs.begin(),	o.m_layerPrefs.end(),	m_layerPrefs.begin());
152 		std::copy(o.m_pinOffsetRow.begin(),	o.m_pinOffsetRow.end(),	m_pinOffsetRow.begin());
153 		std::copy(o.m_pinOffsetCol.begin(),	o.m_pinOffsetCol.end(),	m_pinOffsetCol.begin());
154 		std::copy(o.m_pinLabels.begin(),	o.m_pinLabels.end(),	m_pinLabels.begin());
155 		std::copy(o.m_pinAligns.begin(),	o.m_pinAligns.end(),	m_pinAligns.begin());
156 		CopyShapes( o );
157 		return *this;
158 	}
ClearNodeIds()159 	void ClearNodeIds()
160 	{
161 		for (auto& o : m_nodeIdPins)  o = BAD_NODEID;
162 		for (auto& o : m_origIdPins1) o = BAD_NODEID;
163 		for (auto& o : m_origIdPins2) o = BAD_NODEID;
164 		for (auto& o : m_layerPrefs)  o = LAYER_X;
165 	}
IsEqual(const Component & o)166 	bool IsEqual(const Component& o) const	// Compare persisted info
167 	{
168 		bool bOK = FootPrint::operator==(o)
169 				&& m_id					== o.m_id
170 				&& m_nameStr			== o.m_nameStr
171 				&& m_valueStr			== o.m_valueStr
172 				&& m_prefixStr			== o.m_prefixStr
173 				&& m_typeStr			== o.m_typeStr
174 				&& m_importStr			== o.m_importStr
175 				&& m_lyr				== o.m_lyr
176 				&& m_row				== o.m_row
177 				&& m_col				== o.m_col
178 				&& m_iLabelOffsetRow	== o.m_iLabelOffsetRow
179 				&& m_iLabelOffsetCol	== o.m_iLabelOffsetCol
180 				&& m_direction			== o.m_direction
181 				&& m_bIsPlaced			== o.m_bIsPlaced
182 				&& m_iPinFlags			== o.m_iPinFlags
183 				&& m_iPadWidth			== o.m_iPadWidth
184 				&& m_iHoleWidth			== o.m_iHoleWidth
185 				&& m_bAllowFlyWire		== o.m_bAllowFlyWire
186 				&& GetNumPins()			== o.GetNumPins()
187 				&& GetNumShapes()		== o.GetNumShapes();
188 		for (size_t i = 0; i < GetNumPins() && bOK; i++)
189 		{
190 			bOK =  m_nodeIdPins[i]		== o.m_nodeIdPins[i]
191 				&& m_origIdPins1[i]		== o.m_origIdPins1[i]
192 				&& m_origIdPins2[i]		== o.m_origIdPins2[i]
193 				&& m_layerPrefs[i]		== o.m_layerPrefs[i]
194 				&& m_pinOffsetRow[i]	== o.m_pinOffsetRow[i]
195 				&& m_pinOffsetCol[i]	== o.m_pinOffsetCol[i]
196 				&& m_pinLabels[i]		== o.m_pinLabels[i]
197 				&& m_pinAligns[i]		== o.m_pinAligns[i];
198 		}
199 		for (size_t i = 0; i < GetNumShapes() && bOK; i++)
200 		{
201 			bOK = m_shapes[i] == o.m_shapes[i];
202 		}
203 		return bOK;
204 	}
205 	bool operator<(const Component& o) const	{ return m_id < o.m_id; }
206 	bool operator==(const Component& o) const	{ return m_id == o.m_id; }
SetId(const int & i)207 	void SetId(const int& i)					{ m_id = i; }
SetNameStr(const std::string & s)208 	void SetNameStr(const std::string& s)		{ m_nameStr = s; }
SetValueStr(const std::string & s)209 	void SetValueStr(const std::string& s)		{ m_valueStr = s; }
SetPrefixStr(const std::string & s)210 	void SetPrefixStr(const std::string& s)		{ m_prefixStr = s; }
SetTypeStr(const std::string & s)211 	void SetTypeStr(const std::string& s)		{ m_typeStr = s; }
SetImportStr(const std::string & s)212 	void SetImportStr(const std::string& s)		{ m_importStr = s; }
SetNodeId(const size_t & iPinIndex,const int & i)213 	void SetNodeId(const size_t& iPinIndex, const int& i)
214 	{
215 		if ( iPinIndex < m_nodeIdPins.size() ) m_nodeIdPins[iPinIndex] = i;
216 	}
SetOrigId(const int & lyr,const size_t & iPinIndex,const int & i)217 	void SetOrigId(const int& lyr, const size_t& iPinIndex, const int& i)
218 	{
219 		assert( lyr == 0 || lyr == 1 );
220 		if ( lyr == 0 )
221 		{
222 			if ( iPinIndex < m_origIdPins1.size() ) m_origIdPins1[iPinIndex] = i;
223 		}
224 		else
225 		{
226 			if ( iPinIndex < m_origIdPins2.size() ) m_origIdPins2[iPinIndex] = i;
227 		}
228 	}
SetLayerPref(const size_t & iPinIndex,const uchar & iPref)229 	void SetLayerPref(const size_t& iPinIndex, const uchar& iPref)
230 	{
231 		if ( iPinIndex < m_layerPrefs.size() ) m_layerPrefs[iPinIndex] = iPref;
232 	}
SetPinOffsetRow(const size_t & iPinIndex,const int & i)233 	void SetPinOffsetRow(const size_t& iPinIndex, const int& i)
234 	{
235 		if ( iPinIndex < m_pinOffsetRow.size() ) m_pinOffsetRow[iPinIndex] = std::max(-MAX_PAD_OFFSET_MIL, std::min(MAX_PAD_OFFSET_MIL, i));
236 	}
SetPinOffsetCol(const size_t & iPinIndex,const int & i)237 	void SetPinOffsetCol(const size_t& iPinIndex, const int& i)
238 	{
239 		if ( iPinIndex < m_pinOffsetCol.size() ) m_pinOffsetCol[iPinIndex] = std::max(-MAX_PAD_OFFSET_MIL, std::min(MAX_PAD_OFFSET_MIL, i));
240 	}
SetPinLabel(const size_t & iPinIndex,const std::string & s)241 	void SetPinLabel(const size_t& iPinIndex, const std::string& s)
242 	{
243 		if ( iPinIndex < m_pinLabels.size() ) m_pinLabels[iPinIndex] = s;
244 	}
SetPinAlign(const size_t & iPinIndex,const int & i)245 	void SetPinAlign(const size_t& iPinIndex, const int& i)
246 	{
247 		if ( iPinIndex < m_pinAligns.size() ) m_pinAligns[iPinIndex] = i;
248 	}
SetShape(const size_t & iShapeIndex,const Shape & o)249 	void SetShape(const size_t& iShapeIndex, const Shape& o)
250 	{
251 		if ( iShapeIndex < m_shapes.size() ) m_shapes[iShapeIndex] = o;
252 	}
CopyPinLabels(const Component & o)253 	void CopyPinLabels(const Component& o)
254 	{
255 		assert( m_pinLabels.size() == o.m_pinLabels.size() );
256 		assert( m_pinAligns.size() == o.m_pinAligns.size() );
257 		std::copy(o.m_pinLabels.begin(), o.m_pinLabels.end(), m_pinLabels.begin());
258 		std::copy(o.m_pinAligns.begin(), o.m_pinAligns.end(), m_pinAligns.begin());
259 	}
CopyShapes(const Component & o)260 	void CopyShapes(const Component& o)
261 	{
262 		AllocateShapes( o.GetNumShapes() );
263 		std::copy(o.m_shapes.begin(), o.m_shapes.end(), m_shapes.begin());
264 	}
AllocatePins(const size_t numPins)265 	void AllocatePins(const size_t numPins)
266 	{
267 		m_nodeIdPins.clear();	m_nodeIdPins.resize(numPins, BAD_NODEID);
268 		m_origIdPins1.clear();	m_origIdPins1.resize(numPins, BAD_NODEID);
269 		m_origIdPins2.clear();	m_origIdPins2.resize(numPins, BAD_NODEID);
270 		m_layerPrefs.clear();	m_layerPrefs.resize(numPins, LAYER_X);
271 		m_pinOffsetRow.clear();	m_pinOffsetRow.resize(numPins, 0);
272 		m_pinOffsetCol.clear();	m_pinOffsetCol.resize(numPins, 0);
273 		m_pinLabels.clear();	m_pinLabels.resize(numPins, "");
274 		m_pinAligns.clear();	m_pinAligns.resize(numPins, Qt::AlignHCenter);
275 		SetDefaultPinLabels();
276 	}
AllocateShapes(const size_t numShapes)277 	void AllocateShapes(const size_t numShapes)
278 	{
279 		m_shapes.clear();	m_shapes.resize(numShapes, Shape());
280 	}
SetLyr(const int & i)281 	void SetLyr(const int& i)					{ m_lyr = i; }
SetRow(const int & i)282 	void SetRow(const int& i)					{ m_row = i; }
SetCol(const int & i)283 	void SetCol(const int& i)					{ m_col = i; }
284 //	void SetLabelOffsetRow(const int& i)		{ m_iLabelOffsetRow = i; }
285 //	void SetLabelOffsetCol(const int& i)		{ m_iLabelOffsetCol = i; }
SetDirection(const char & d)286 	void SetDirection(const char& d)			{ m_direction = d; }
SetIsPlaced(const bool & b)287 	void SetIsPlaced(const bool& b)				{ m_bIsPlaced = b; }
SetPinFlags(const uchar & i)288 	void SetPinFlags(const uchar& i)			{ m_iPinFlags = i; }
SetPadWidth(const int & i)289 	void SetPadWidth(const int& i)				{ m_iPadWidth = i; }
SetHoleWidth(const int & i)290 	void SetHoleWidth(const int& i)				{ m_iHoleWidth = i; }
SetAllowFlyWire(const bool & b)291 	void SetAllowFlyWire(const bool& b)			{ m_bAllowFlyWire = b; }
AddOne(const Shape & s)292 	void AddOne(const Shape& s)					{ m_shapes.push_back(s); }
AddTwo(const Shape & s)293 	void AddTwo(const Shape& s)	// Adds the shape twice.  Once with fill only, and once with line only
294 	{
295 		Shape tmp(s);
296 		tmp.SetDrawFill(true);	tmp.SetDrawLine(false);	m_shapes.push_back(tmp);
297 		tmp.SetDrawFill(false);	tmp.SetDrawLine(true);	m_shapes.push_back(tmp);
298 	}
GetIsTemplate()299 	bool				GetIsTemplate() const	{ return GetId() == BAD_COMPID; }
GetId()300 	const int&			GetId() const			{ return m_id; }
GetNameStr()301 	const std::string&	GetNameStr() const		{ return m_nameStr; }
GetValueStr()302 	const std::string&	GetValueStr() const		{ return m_valueStr; }
GetPrefixStr()303 	const std::string&	GetPrefixStr() const	{ return m_prefixStr; }
GetTypeStr()304 	const std::string&	GetTypeStr() const		{ return m_typeStr; }
GetImportStr()305 	const std::string&	GetImportStr() const	{ return m_importStr; }
GetNumPins()306 	size_t				GetNumPins() const		{ return m_nodeIdPins.size(); }
GetNumShapes()307 	size_t				GetNumShapes() const	{ return m_shapes.size(); }
GetNodeId(const size_t & iPinIndex)308 	const int&			GetNodeId(const size_t& iPinIndex) const
309 	{
310 		static int badNodeId(BAD_NODEID);
311 		return ( iPinIndex < m_nodeIdPins.size() ) ? m_nodeIdPins[iPinIndex] : badNodeId;
312 	}
GetOrigId(const int & lyr,const size_t & iPinIndex)313 	const int&			GetOrigId(const int& lyr, const size_t& iPinIndex) const
314 	{
315 		static int badNodeId(BAD_NODEID);
316 		if ( lyr == 0 )
317 			return ( iPinIndex < m_origIdPins1.size() ) ? m_origIdPins1[iPinIndex] : badNodeId;
318 		else
319 			return ( iPinIndex < m_origIdPins2.size() ) ? m_origIdPins2[iPinIndex] : badNodeId;
320 	}
GetLayerPref(const size_t & iPinIndex)321 	const uchar&		GetLayerPref(const size_t& iPinIndex) const
322 	{
323 		static uchar noPref(LAYER_X);
324 		return ( iPinIndex < m_layerPrefs.size() ) ? m_layerPrefs[iPinIndex] : noPref;
325 	}
GetPinOffsetRow(const size_t & iPinIndex)326 	const int&			GetPinOffsetRow(const size_t& iPinIndex) const
327 	{
328 		static int defaultOffset(0);
329 		return ( iPinIndex < m_pinOffsetRow.size() ) ? m_pinOffsetRow[iPinIndex] : defaultOffset;
330 	}
GetPinOffsetCol(const size_t & iPinIndex)331 	const int&			GetPinOffsetCol(const size_t& iPinIndex) const
332 	{
333 		static int defaultOffset(0);
334 		return ( iPinIndex < m_pinOffsetCol.size() ) ? m_pinOffsetCol[iPinIndex] : defaultOffset;
335 	}
GetPinLabel(const size_t & iPinIndex)336 	const std::string&	GetPinLabel(const size_t& iPinIndex) const
337 	{
338 		static const std::string emptyStr("");
339 		return ( iPinIndex < m_pinLabels.size() ) ? m_pinLabels[iPinIndex] : emptyStr;
340 	}
GetPinAlign(const size_t & iPinIndex)341 	const int&			GetPinAlign(const size_t& iPinIndex) const
342 	{
343 		static int defaultAlign(Qt::AlignHCenter);
344 		return ( iPinIndex < m_pinAligns.size() ) ? m_pinAligns[iPinIndex] : defaultAlign;
345 	}
GetShape(const size_t & iShapeIndex)346 	const Shape&		GetShape(const size_t& iShapeIndex) const
347 	{
348 		static Shape	defaultShape;
349 		return ( iShapeIndex < m_shapes.size() ) ? m_shapes[iShapeIndex] : defaultShape;
350 	}
GetLyr()351 	const int&			GetLyr() const				{ return m_lyr; }
GetRow()352 	const int&			GetRow() const				{ return m_row; }
GetCol()353 	const int&			GetCol() const				{ return m_col; }
354 //	const int&			GetLabelOffsetRow() const	{ return m_iLabelOffsetRow; }
355 //	const int&			GetLabelOffsetCol() const	{ return m_iLabelOffsetCol; }
GetDirection()356 	const char&			GetDirection() const		{ return m_direction; }
GetIsPlaced()357 	const bool&			GetIsPlaced() const			{ return m_bIsPlaced; }
GetPinFlags()358 	const uchar&		GetPinFlags() const			{ return m_iPinFlags; }
GetPadWidth()359 	const int&			GetPadWidth() const			{ return m_iPadWidth; }
GetHoleWidth()360 	const int&			GetHoleWidth() const		{ return m_iHoleWidth; }
GetAllowFlyWire()361 	const bool&			GetAllowFlyWire() const		{ return m_bAllowFlyWire; }
GetShapes()362 	const std::vector<Shape>& GetShapes() const		{ return m_shapes; }
363 
364 	// Helpers for labels
365 	void SetDefaultLabelOffsets();
366 	void GetLabelOffsets(int& offsetRow, int& offsetCol) const;			// w.r.t. screen, not comp rotation
367 	void MoveLabelOffsets(const int& deltaRow, const int& deltaCol);	// w.r.t. screen, not comp rotation
368 	void HandleLegacyLabelOffsets();	// For old VRT files
369 
370 	// Helpers for custom pads
SetCustomPads(const bool & b)371 	void SetCustomPads(const bool& b)	{ if ( b )	SetPinFlags( m_iPinFlags |  PIN_CUSTOM );
372 										  else		SetPinFlags( m_iPinFlags & ~PIN_CUSTOM ); }
GetCustomPads()373 	bool GetCustomPads() const			{ return ( GetPinFlags() & PIN_CUSTOM ) != 0; }
374 
375 	void GetSafeBounds(double& L, double& R, double& T, double& B, bool bFill = true) const
376 	{
377 		// First consider footprint bounds (for direction 'W')
378 		const double dW( 0.5 * GetCols() ), dH( 0.5 * GetRows() );
379 		L = -dW;	R = dW;		T = -dH;	B = dH;
380 		// Then consider the list of shapes (for direction 'W')
381 		double l,r,t,b;	// Working variables
382 		for (const auto& o : m_shapes)
383 		{
384 			if ( !bFill && o.GetDrawFill() ) continue;	// If view is not drawing filled shapes, skip them
385 			o.GetSafeBounds(l, r, t, b);
386 			L = std::min(L,l);	T = std::min(T,t);
387 			R = std::max(R,r);	B = std::max(B,b);
388 		}
389 		// Handle other component directions
390 		switch ( GetDirection() )
391 		{
392 			case 'E':	l = -R;	t = -B;	r = -L;	b = -T;	break;
393 			case 'N':	l = -B;	t =  L;	r = -T;	b =  R;	break;
394 			case 'S':	l =  T;	t = -R;	r =  B;	b = -L;	break;
395 			default:	return;
396 		}
397 		L = l;	T = t;	R = r;	B = b;
398 	}
GetFullTypeStr()399 	std::string GetFullTypeStr() const		// For SIP/DIP types, append the number of pins
400 	{
401 		std::string str = GetTypeStr();
402 		if ( GetType() == COMP::DIP || GetType() == COMP::SIP )
403 		{
404 			char buffer[32] = {'\0'};
405 			sprintf(buffer, "%d", static_cast<int>(GetNumPins()));
406 			str += std::string(buffer);		// e.g. "DIP16"
407 		}
408 		return str;
409 	}
GetFullImportStr()410 	std::string GetFullImportStr() const	// For SIP/DIP/SWITCH/STRIP/BLOCK types, append the number of pins
411 	{
412 		std::string str = GetImportStr();
413 		if ( GetType() == COMP::DIP || GetType() == COMP::SIP ||
414 			 GetType() == COMP::SWITCH_DT || GetType() == COMP::SWITCH_ST || GetType() == COMP::SWITCH_ST_DIP ||
415 			 GetType() == COMP::STRIP_100 || GetType() == COMP::BLOCK_100 || GetType() == COMP::BLOCK_200 )
416 		{
417 			char buffer[32] = {'\0'};
418 			sprintf(buffer, "%d", static_cast<int>(GetNumPins()));
419 			str += std::string(buffer);		// e.g. "DIP16"
420 		}
421 		return str;
422 	}
423 	// Helpers (account for component direction)
GetCompRows()424 	const int&	GetCompRows() const	{ return GetRows( GetDirection() ); }
GetCompCols()425 	const int&	GetCompCols() const	{ return GetCols( GetDirection() ); }
GetLastRow()426 	int			GetLastRow() const	{ return GetRow() + GetCompRows() - 1; }
GetLastCol()427 	int			GetLastCol() const	{ return GetCol() + GetCompCols() - 1; }
GetCompPinOffsets(const size_t & iPinIndex,int & Xmil,int & Ymil)428 	void GetCompPinOffsets(const size_t& iPinIndex, int& Xmil, int& Ymil) const
429 	{
430 		switch ( GetDirection() )
431 		{
432 			case 'E':	Xmil = -GetPinOffsetCol(iPinIndex);	Ymil = -GetPinOffsetRow(iPinIndex); return;
433 			case 'N':	Xmil = -GetPinOffsetRow(iPinIndex);	Ymil =  GetPinOffsetCol(iPinIndex); return;
434 			case 'S':	Xmil =  GetPinOffsetRow(iPinIndex);	Ymil = -GetPinOffsetCol(iPinIndex); return;
435 			default:	Xmil =  GetPinOffsetCol(iPinIndex);	Ymil =  GetPinOffsetRow(iPinIndex); return;
436 		}
437 	}
SetCompPinOffsets(const size_t & iPinIndex,const int & Xmil,const int & Ymil)438 	void SetCompPinOffsets(const size_t& iPinIndex, const int& Xmil, const int& Ymil)
439 	{
440 		switch ( GetDirection() )
441 		{
442 			case 'E':	SetPinOffsetCol(iPinIndex, -Xmil);	SetPinOffsetRow(iPinIndex, -Ymil); return;
443 			case 'N':	SetPinOffsetRow(iPinIndex, -Xmil);	SetPinOffsetCol(iPinIndex,  Ymil); return;
444 			case 'S':	SetPinOffsetRow(iPinIndex,  Xmil);	SetPinOffsetCol(iPinIndex, -Ymil); return;
445 			default:	SetPinOffsetCol(iPinIndex,  Xmil);	SetPinOffsetRow(iPinIndex,  Ymil); return;
446 		}
447 	}
IncCompPinOffsets(const size_t & iPinIndex,const int & dX,const int & dY)448 	void IncCompPinOffsets(const size_t& iPinIndex, const int& dX, const int& dY)
449 	{
450 		int Xmil, Ymil;
451 		GetCompPinOffsets(iPinIndex, Xmil, Ymil);
452 		SetCompPinOffsets(iPinIndex, Xmil + dX, Ymil + dY);
453 	}
GetUniformPinOffsets()454 	bool GetUniformPinOffsets() const	// Check if all pins have the same offsets
455 	{
456 		const size_t iSize = GetNumPins();
457 		if ( iSize < 2 ) return iSize == 1;
458 		bool bAllSame(true);
459 		for (size_t i = 1; i < iSize && bAllSame; i++)
460 			bAllSame = ( m_pinOffsetRow[i] == m_pinOffsetRow[0] ) && ( m_pinOffsetCol[i] == m_pinOffsetCol[0] );
461 		return bAllSame;
462 	}
GetCompShapeOffsets(int & Xmil,int & Ymil)463 	void GetCompShapeOffsets(int& Xmil, int& Ymil) const
464 	{
465 		if ( GetUniformPinOffsets() )
466 		{
467 			switch ( GetDirection() )
468 			{
469 				case 'E':	Xmil = -m_pinOffsetCol[0];	Ymil = -m_pinOffsetRow[0]; return;
470 				case 'N':	Xmil = -m_pinOffsetRow[0];	Ymil =  m_pinOffsetCol[0]; return;
471 				case 'S':	Xmil =  m_pinOffsetRow[0];	Ymil = -m_pinOffsetCol[0]; return;
472 				default:	Xmil =  m_pinOffsetCol[0];	Ymil =  m_pinOffsetRow[0]; return;
473 			}
474 		}
475 		Xmil = Ymil = 0;	// Default to 0 offsets
476 	}
GetCompElement(const int & compRow,const int & compCol)477 	const CompElement*	GetCompElement(const int& compRow, const int& compCol) const
478 	{
479 		return FootPrint::Get(0, compRow, compCol, GetDirection());
480 	}
GetHasNodeId(int nodeId)481 	bool GetHasNodeId(int nodeId) const
482 	{
483 		for (const auto& i : m_nodeIdPins) if ( i == nodeId ) return true;
484 		return false;
485 	}
Rotate(const bool & bClockWise)486 	void Rotate(const bool& bClockWise)
487 	{
488 		switch( GetDirection() )	// Component direction: 'W','E','N','S'
489 		{
490 			case 'W': return SetDirection( bClockWise ? 'N' : 'S' );
491 			case 'E': return SetDirection( bClockWise ? 'S' : 'N' );
492 			case 'N': return SetDirection( bClockWise ? 'E' : 'W' );
493 			case 'S': return SetDirection( bClockWise ? 'W' : 'E' );
494 		}
495 	}
GetHasAssignedPins()496 	bool GetHasAssignedPins() const	// Check if any pins have a nodeId or pin label set
497 	{
498 		for (size_t i = 0; i < GetNumPins(); i++)
499 		{
500 			if ( m_nodeIdPins[i] != BAD_NODEID ) return true;
501 			if ( m_pinLabels[i]  != CompTypes::GetDefaultPinLabel(i) ) return true;
502 			if ( m_pinAligns[i]  != CompTypes::GetDefaultPinAlign(i, GetNumPins(), GetType()) ) return true;
503 		}
504 		return false;
505 	}
GetAllowCustomPads()506 	bool GetAllowCustomPads() const	// true ==> allow custom pad and hole widths
507 	{
508 		switch( GetType()  )
509 		{
510 			case COMP::TRACKS:	return false;
511 			case COMP::WIRE:	return false;	// Not allowed since 2 wires can share a hole
512 			default:			return GetNumPins() > 0;
513 		}
514 	}
CanStretch(const bool & bGrow)515 	bool CanStretch(const bool& bGrow) const
516 	{
517 		if ( !FootPrint::CanStretch(bGrow) ) return false;
518 		switch( GetType() )
519 		{
520 			case COMP::SIP:
521 			case COMP::DIP:
522 			case COMP::STRIP_100:
523 			case COMP::BLOCK_100:
524 			case COMP::BLOCK_200:
525 			case COMP::SWITCH_ST:
526 			case COMP::SWITCH_DT:
527 			case COMP::SWITCH_ST_DIP:	return !GetHasAssignedPins();
528 			default:					return true;
529 		}
530 	}
531 	void Stretch(const bool& bGrow, const bool& bUsePCBshapes = false)
532 	{
533 		FootPrint::Stretch(bGrow);
534 		SetDefaultShapes(bUsePCBshapes);	// Rebuild the shapes list
535 
536 		switch( GetType() )
537 		{
538 			case COMP::SIP:				return AllocatePins( static_cast<size_t>( GetCols() ) );
539 			case COMP::DIP:				return AllocatePins( static_cast<size_t>( 2 * GetCols() ) );
540 			case COMP::STRIP_100:		return AllocatePins( static_cast<size_t>( GetCols() ) );
541 			case COMP::BLOCK_100:		return AllocatePins( static_cast<size_t>( GetCols() ) );
542 			case COMP::BLOCK_200:		return AllocatePins( static_cast<size_t>( ( GetCols() - 1 ) / 2 ) );
543 			case COMP::SWITCH_ST:		return AllocatePins( static_cast<size_t>( GetCols() + 1 ) );
544 			case COMP::SWITCH_DT:		return AllocatePins( static_cast<size_t>( 3 * ( GetCols() + 1 ) / 2 ) );
545 			case COMP::SWITCH_ST_DIP:	return AllocatePins( static_cast<size_t>( 2 * GetCols() ) );
546 			default:					return;
547 		}
548 	}
549 	void StretchWidth(const bool& bGrow, const bool& bUsePCBshapes = false)
550 	{
551 		FootPrint::StretchWidth(bGrow);
552 		SetDefaultShapes(bUsePCBshapes);	// Rebuild the shapes list
553 	}
SetDefaultPinLabels()554 	void SetDefaultPinLabels()
555 	{
556 		for (size_t i = 0; i < GetNumPins(); i++)
557 		{
558 			m_pinLabels[i] = CompTypes::GetDefaultPinLabel(i);
559 			m_pinAligns[i] = CompTypes::GetDefaultPinAlign(i, GetNumPins(), GetType());
560 		}
561 	}
GetFootprintRect()562 	Rect GetFootprintRect() const
563 	{
564 		return Rect(GetRow(), GetLastRow(), GetCol(), GetLastCol());
565 	}
GetIsTrueComp()566 	bool GetIsTrueComp() const	// A true component has pins and "owns" the nodeIds on them
567 	{
568 		switch( GetType() )
569 		{
570 			case COMP::INVALID:
571 			case COMP::VERO_NUMBER:
572 			case COMP::VERO_LETTER:
573 			case COMP::MARK:
574 			case COMP::WIRE:
575 			case COMP::TRACKS:	return false;
576 			default:			return true;
577 		}
578 	}
SetFillColor(const MyRGB & r)579 	void SetFillColor(const MyRGB& r)	// Gives all shapes the same fill color
580 	{
581 		for (auto& o : m_shapes) o.SetFillColor(r);
582 	}
GetNewColor()583 	MyRGB GetNewColor() const	// returns an un-used color
584 	{
585 		for (int iColor = 1; iColor <= 0xFFFFFF; iColor++)	// Black is used for outlines so start at 1
586 		{
587 			bool bOK(true);
588 			MyRGB tmp(iColor);
589 			for (const auto& o : m_shapes)
590 				if ( o.GetFillColor() == tmp ) { bOK = false; break; }
591 			if ( bOK ) return tmp;
592 		}
593 		assert(0);
594 		return MyRGB(0x000000);
595 	}
596 	// Merge interface functions
UpdateMergeOffsets(MergeOffsets & o)597 	virtual void UpdateMergeOffsets(MergeOffsets& o) override
598 	{
599 		FootPrint::UpdateMergeOffsets(o);	// Call UpdateMergeOffsets in base class
600 
601 		if ( m_id != BAD_COMPID && m_id != TRAX_COMPID ) o.deltaCompId  = std::max(o.deltaCompId, m_id + 1);
602 //		o.deltaLyr = std::max(o.deltaLyr, m_lyr + GetCompLyrs() + 1);
603 		o.deltaRow = std::max(o.deltaRow, m_row + GetCompRows() + 1);
604 //		o.deltaCol = std::max(o.deltaCol, m_col + GetCompCols() + 1);
605 		for (size_t i = 0; i < GetNumPins(); i++)
606 		{
607 			if ( m_nodeIdPins[i]  != BAD_NODEID ) o.deltaNodeId = std::max(o.deltaNodeId, m_nodeIdPins[i]  + 1);
608 			if ( m_origIdPins1[i] != BAD_NODEID ) o.deltaNodeId = std::max(o.deltaNodeId, m_origIdPins1[i] + 1);
609 			if ( m_origIdPins2[i] != BAD_NODEID ) o.deltaNodeId = std::max(o.deltaNodeId, m_origIdPins2[i] + 1);
610 		}
611 	}
ApplyMergeOffsets(const MergeOffsets & o)612 	virtual void ApplyMergeOffsets(const MergeOffsets& o) override
613 	{
614 		FootPrint::ApplyMergeOffsets(o);	// Call ApplyMergeOffsets in base class
615 
616 		if ( m_id != BAD_COMPID && m_id != TRAX_COMPID) m_id += o.deltaCompId;
617 		m_lyr += o.deltaLyr;
618 		m_row += o.deltaRow;
619 		m_col += o.deltaCol;
620 		for (size_t i = 0; i < GetNumPins(); i++)
621 		{
622 			if ( m_nodeIdPins[i]  != BAD_NODEID ) m_nodeIdPins[i]  += o.deltaNodeId;
623 			if ( m_origIdPins1[i] != BAD_NODEID ) m_origIdPins1[i] += o.deltaNodeId;
624 			if ( m_origIdPins2[i] != BAD_NODEID ) m_origIdPins2[i] += o.deltaNodeId;
625 		}
626 	}
627 	void SetDefaultPinFlags();
628 	void SetDefaultStrings(bool bForce = true)
629 	{
630 		if ( GetPrefixStr().empty() || bForce )
631 			SetPrefixStr( CompTypes::GetDefaultPrefixStr( GetType() ) );
632 		if ( GetTypeStr().empty() || bForce )
633 			SetTypeStr( CompTypes::GetDefaultTypeStr( GetType() ) );
634 		if ( GetImportStr().empty() || bForce || GetType() != COMP::CUSTOM )
635 			SetImportStr( CompTypes::GetDefaultImportStr( GetType() ) );
636 	}
637 	void SetDefaultShapes(const bool& bUsePCBshapes = false);
638 	void SetDefaultColor();
639 	// Persist interface functions
Load(DataStream & inStream)640 	virtual void Load(DataStream& inStream) override
641 	{
642 		FootPrint::Load(inStream);	// Load() base class
643 		inStream.Load(m_id);
644 		if ( m_id == -2 ) SetType(COMP::TRACKS);	// Needed to stop old VRTs from crashing
645 		inStream.Load(m_nameStr);
646 		inStream.Load(m_valueStr);
647 		if ( inStream.GetVersion() >= VRT_VERSION_19 )
648 			inStream.Load(m_prefixStr);				// Added in VRT_VERSION_19
649 		if ( inStream.GetVersion() >= VRT_VERSION_18 )
650 		{
651 			inStream.Load(m_typeStr);				// Added in VRT_VERSION_18
652 			inStream.Load(m_importStr);				// Added in VRT_VERSION_18
653 		}
654 		m_lyr = 0;
655 		if ( inStream.GetVersion() >= VRT_VERSION_34 )
656 			inStream.Load(m_lyr);					// Added in VRT_VERSION_34
657 		inStream.Load(m_row);
658 		inStream.Load(m_col);
659 		inStream.Load(m_iLabelOffsetRow);
660 		inStream.Load(m_iLabelOffsetCol);
661 		if ( inStream.GetVersion() < VRT_VERSION_22 )	// Units changed from 1/4 square to 1/16 square in VRT_VERSION_22
662 		{
663 			m_iLabelOffsetRow *= 4;
664 			m_iLabelOffsetCol *= 4;
665 		}
666 		inStream.Load(m_direction);
667 		inStream.Load(m_bIsPlaced);
668 		if ( inStream.GetVersion() >= VRT_VERSION_19 )
669 			inStream.Load(m_iPinFlags);				// Added in VRT_VERSION_19
670 		m_iPadWidth  = 70;
671 		m_iHoleWidth = 40;
672 		if ( inStream.GetVersion() >= VRT_VERSION_39 )
673 		{
674 			inStream.Load(m_iPadWidth);				// Added in VRT_VERSION_39
675 			inStream.Load(m_iHoleWidth);			// Added in VRT_VERSION_39
676 		}
677 		m_bAllowFlyWire = false;
678 		if ( inStream.GetVersion() >= VRT_VERSION_47 )
679 			inStream.Load(m_bAllowFlyWire);			// Added in VRT_VERSION_47
680 		unsigned int numPins(0);
681 		inStream.Load(numPins);
682 		AllocatePins(numPins);
683 		for (unsigned int i = 0; i < numPins; i++)
684 		{
685 			inStream.Load(m_nodeIdPins[i]);
686 			inStream.Load(m_origIdPins1[i]);
687 			if ( inStream.GetVersion() >= VRT_VERSION_34 )
688 				inStream.Load(m_origIdPins2[i]);	// Added in VRT_VERSION_34
689 			if ( inStream.GetVersion() >= VRT_VERSION_45 )
690 				inStream.Load(m_layerPrefs[i]);		// Added in VRT_VERSION_45
691 			if ( inStream.GetVersion() >= VRT_VERSION_46 )
692 			{
693 				inStream.Load(m_pinOffsetRow[i]);	// Added in VRT_VERSION_46
694 				inStream.Load(m_pinOffsetCol[i]);	// Added in VRT_VERSION_46
695 			}
696 			if ( inStream.GetVersion() >= VRT_VERSION_7 )
697 				inStream.Load(m_pinLabels[i]);		// Added in VRT_VERSION_7
698 			if ( inStream.GetVersion() >= VRT_VERSION_30 )
699 				inStream.Load(m_pinAligns[i]);		// Added in VRT_VERSION_30
700 		}
701 		if ( inStream.GetVersion() >= VRT_VERSION_18 )
702 		{
703 			unsigned int numShapes(0);
704 			inStream.Load(numShapes);				// Added in VRT_VERSION_18
705 			AllocateShapes(numShapes);
706 			for (unsigned int i = 0; i < numShapes; i++)
707 				m_shapes[i].Load(inStream);			// Added in VRT_VERSION_18
708 		}
709 		if ( inStream.GetVersion() < VRT_VERSION_31 )
710 			HandleLegacyLabelOffsets();
711 		// Try to fix any missing definitions
712 		SetDefaultPinFlags();
713 		SetDefaultStrings(false);	// false ==> only set empty (m_prefixStr, m_guiStr, m_importStr)
714 		SetDefaultShapes();
715 	}
Save(DataStream & outStream)716 	virtual void Save(DataStream& outStream) override
717 	{
718 		FootPrint::Save(outStream);	// Save() base class
719 		outStream.Save(m_id);
720 		outStream.Save(m_nameStr);
721 		outStream.Save(m_valueStr);
722 		outStream.Save(m_prefixStr);			// Added in VRT_VERSION_19
723 		outStream.Save(m_typeStr);				// Added in VRT_VERSION_18
724 		outStream.Save(m_importStr);			// Added in VRT_VERSION_18
725 		outStream.Save(m_lyr);					// Added in VRT_VERSION_34
726 		outStream.Save(m_row);
727 		outStream.Save(m_col);
728 		outStream.Save(m_iLabelOffsetRow);
729 		outStream.Save(m_iLabelOffsetCol);
730 		outStream.Save(m_direction);
731 		outStream.Save(m_bIsPlaced);
732 		outStream.Save(m_iPinFlags);			// Added in VRT_VERSION_19
733 		outStream.Save(m_iPadWidth);			// Added in VRT_VERSION_39
734 		outStream.Save(m_iHoleWidth);			// Added in VRT_VERSION_39
735 		outStream.Save(m_bAllowFlyWire);		// Added in VRT_VERSION_47
736 		const unsigned int numPins = static_cast<unsigned int>( GetNumPins() );
737 		outStream.Save(numPins);
738 		for (unsigned int i = 0; i < numPins; i++)
739 		{
740 			outStream.Save(m_nodeIdPins[i]);
741 			outStream.Save(m_origIdPins1[i]);
742 			outStream.Save(m_origIdPins2[i]);	// Added in VRT_VERSION_34
743 			outStream.Save(m_layerPrefs[i]);	// Added in VRT_VERSION_45
744 			outStream.Save(m_pinOffsetRow[i]);	// Added in VRT_VERSION_46
745 			outStream.Save(m_pinOffsetCol[i]);	// Added in VRT_VERSION_46
746 			outStream.Save(m_pinLabels[i]);		// Added in VRT_VERSION_7
747 			outStream.Save(m_pinAligns[i]);		// Added in VRT_VERSION_30
748 		}
749 		const unsigned int numShapes = static_cast<unsigned int>( GetNumShapes() );
750 		outStream.Save(numShapes);				// Added in VRT_VERSION_18
751 		for (unsigned int i = 0; i < numShapes; i++)
752 			m_shapes[i].Save(outStream);		// Added in VRT_VERSION_18
753 	}
754 private:
755 	int							m_id;				// Component ID.  (BAD_COMPID ==> a component template)
756 	std::string					m_nameStr;			// Name label
757 	std::string					m_valueStr;			// Value label
758 	std::string					m_prefixStr;		// The prefix for new components (overridden for CUSTOM components).
759 	std::string					m_typeStr;			// The footprint type (overridden for CUSTOM components).
760 	std::string					m_importStr;		// Protel/Tango/OrcadPCB2 footprint name. Only for CUSTOM components !!!
761 	std::vector<int>			m_nodeIdPins;		// NodeIds of the pins
762 	std::vector<int>			m_origIdPins1;		// NodeIds under the pins BEFORE placement (1st layer)
763 	std::vector<int>			m_origIdPins2;		// NodeIds under the pins BEFORE placement (2nd Layer)
764 	std::vector<uchar>			m_layerPrefs;		// Prefered layers of the pins
765 	std::vector<int>			m_pinOffsetRow;		// Pin row offset (-50 mil to +50 mil) to allow pad shifts in PCB mode
766 	std::vector<int>			m_pinOffsetCol;		// Pin row offset (-50 mil to +50 mil) to allow pad shifts in PCB mode
767 	std::vector<std::string>	m_pinLabels;		// Pin labels
768 	std::vector<int>			m_pinAligns;		// Pin label alignments (Qt::AlignLeft,Qt::AlignRight,Qt::AlignHCenter)
769 	std::vector<Shape>			m_shapes;			// For rendering components. Coordinates are RELATIVE to footprint centre.
770 	uchar						m_iPinFlags;		// 1 ==> PIN_RECT, 2 ==> PIN_LABELS, 4 ==> PIN_CUSTOM
771 	int							m_iPadWidth;		// Used if the PIN_CUSTOM flag is set
772 	int							m_iHoleWidth;		// Used if the PIN_CUSTOM flag is set
773 	bool						m_bAllowFlyWire;	// For single pin parts only
774 	// Current placement in board
775 	int							m_lyr;				// Board layer for component
776 	int							m_row;				// Board row for top-left element of footprint
777 	int							m_col;				// Board col for top-left element of footprint
778 	int							m_iLabelOffsetRow;	// Label offset in units of 1/16 of a grid square
779 	int							m_iLabelOffsetCol;	// Label offset in units of 1/16 of a grid square
780 	char						m_direction;		// Component orienation:  'W', 'E', 'N', 'S'
781 	bool						m_bIsPlaced;		// true ==> placed on board,  false ==> floating
782 };
783