1 /*
2 * LibrePCB - Professional EDA for everyone!
3 * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4 * https://librepcb.org/
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 /*******************************************************************************
21 * Includes
22 ******************************************************************************/
23 #include "boarddesignrules.h"
24
25 #include <QtCore>
26
27 /*******************************************************************************
28 * Namespace
29 ******************************************************************************/
30 namespace librepcb {
31
32 /*******************************************************************************
33 * Constructors / Destructor
34 ******************************************************************************/
35
BoardDesignRules()36 BoardDesignRules::BoardDesignRules() noexcept
37 : // general attributes
38 mName(tr("LibrePCB Default Design Rules")),
39 mDescription(),
40 // stop mask
41 mStopMaskClearanceRatio(Ratio::percent0()), // 0%
42 mStopMaskClearanceMin(100000), // 0.1mm
43 mStopMaskClearanceMax(100000), // 0.1mm
44 mStopMaskMaxViaDrillDiameter(500000), // 0.5mm
45 // cream mask
46 mCreamMaskClearanceRatio(Ratio::percent100() / 10), // 10%
47 mCreamMaskClearanceMin(0), // 0.0mm
48 mCreamMaskClearanceMax(1000000), // 1.0mm
49 // restring
50 mRestringPadRatio(Ratio::percent100() / 4), // 25%
51 mRestringPadMin(250000), // 0.25mm
52 mRestringPadMax(2000000), // 2.0mm
53 mRestringViaRatio(Ratio::percent100() / 4), // 25%
54 mRestringViaMin(200000), // 0.2mm
55 mRestringViaMax(2000000) // 2.0mm
56 {
57 }
58
BoardDesignRules(const BoardDesignRules & other)59 BoardDesignRules::BoardDesignRules(const BoardDesignRules& other)
60 : BoardDesignRules() {
61 *this = other;
62 }
63
BoardDesignRules(const SExpression & node,const Version & fileFormat)64 BoardDesignRules::BoardDesignRules(const SExpression& node,
65 const Version& fileFormat)
66 : BoardDesignRules() // this loads all default values!
67 {
68 // general attributes
69 mName = deserialize<ElementName>(node.getChild("name/@0"), fileFormat);
70 mDescription = node.getChild("description/@0").getValue();
71 // stop mask
72 if (const SExpression* e = node.tryGetChild("stopmask_clearance_ratio")) {
73 mStopMaskClearanceRatio =
74 deserialize<UnsignedRatio>(e->getChild("@0"), fileFormat);
75 }
76 if (const SExpression* e = node.tryGetChild("stopmask_clearance_min")) {
77 mStopMaskClearanceMin =
78 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
79 }
80 if (const SExpression* e = node.tryGetChild("stopmask_clearance_max")) {
81 mStopMaskClearanceMax =
82 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
83 }
84 if (const SExpression* e =
85 node.tryGetChild("stopmask_max_via_drill_diameter")) {
86 mStopMaskMaxViaDrillDiameter =
87 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
88 }
89 // cream mask
90 if (const SExpression* e = node.tryGetChild("creammask_clearance_ratio")) {
91 mCreamMaskClearanceRatio =
92 deserialize<UnsignedRatio>(e->getChild("@0"), fileFormat);
93 }
94 if (const SExpression* e = node.tryGetChild("creammask_clearance_min")) {
95 mCreamMaskClearanceMin =
96 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
97 }
98 if (const SExpression* e = node.tryGetChild("creammask_clearance_max")) {
99 mCreamMaskClearanceMax =
100 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
101 }
102 // restring
103 if (const SExpression* e = node.tryGetChild("restring_pad_ratio")) {
104 mRestringPadRatio =
105 deserialize<UnsignedRatio>(e->getChild("@0"), fileFormat);
106 }
107 if (const SExpression* e = node.tryGetChild("restring_pad_min")) {
108 mRestringPadMin =
109 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
110 }
111 if (const SExpression* e = node.tryGetChild("restring_pad_max")) {
112 mRestringPadMax =
113 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
114 }
115 if (const SExpression* e = node.tryGetChild("restring_via_ratio")) {
116 mRestringViaRatio =
117 deserialize<UnsignedRatio>(e->getChild("@0"), fileFormat);
118 }
119 if (const SExpression* e = node.tryGetChild("restring_via_min")) {
120 mRestringViaMin =
121 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
122 }
123 if (const SExpression* e = node.tryGetChild("restring_via_max")) {
124 mRestringViaMax =
125 deserialize<UnsignedLength>(e->getChild("@0"), fileFormat);
126 }
127
128 // force validating properties, throw exception on error
129 try {
130 setStopMaskClearanceBounds(mStopMaskClearanceMin, mStopMaskClearanceMax);
131 setCreamMaskClearanceBounds(mCreamMaskClearanceMin, mCreamMaskClearanceMax);
132 setRestringPadBounds(mRestringPadMin, mRestringPadMax);
133 setRestringViaBounds(mRestringViaMin, mRestringViaMax);
134 } catch (const Exception& e) {
135 throw RuntimeError(__FILE__, __LINE__,
136 tr("Invalid design rules: %1").arg(e.getMsg()));
137 }
138 }
139
~BoardDesignRules()140 BoardDesignRules::~BoardDesignRules() noexcept {
141 }
142
143 /*******************************************************************************
144 * Setters
145 ******************************************************************************/
146
setStopMaskClearanceBounds(const UnsignedLength & min,const UnsignedLength & max)147 void BoardDesignRules::setStopMaskClearanceBounds(const UnsignedLength& min,
148 const UnsignedLength& max) {
149 if (max >= min) {
150 mStopMaskClearanceMin = min;
151 mStopMaskClearanceMax = max;
152 } else {
153 throw RuntimeError(__FILE__, __LINE__,
154 tr("Stop mask clearance: MAX must be >= MIN"));
155 }
156 }
157
setCreamMaskClearanceBounds(const UnsignedLength & min,const UnsignedLength & max)158 void BoardDesignRules::setCreamMaskClearanceBounds(const UnsignedLength& min,
159 const UnsignedLength& max) {
160 if (max >= min) {
161 mCreamMaskClearanceMin = min;
162 mCreamMaskClearanceMax = max;
163 } else {
164 throw RuntimeError(__FILE__, __LINE__,
165 tr("Cream mask clearance: MAX must be >= MIN"));
166 }
167 }
168
setRestringPadBounds(const UnsignedLength & min,const UnsignedLength & max)169 void BoardDesignRules::setRestringPadBounds(const UnsignedLength& min,
170 const UnsignedLength& max) {
171 if (max >= min) {
172 mRestringPadMin = min;
173 mRestringPadMax = max;
174 } else {
175 throw RuntimeError(__FILE__, __LINE__,
176 tr("Restring pads: MAX must be >= MIN"));
177 }
178 }
179
setRestringViaBounds(const UnsignedLength & min,const UnsignedLength & max)180 void BoardDesignRules::setRestringViaBounds(const UnsignedLength& min,
181 const UnsignedLength& max) {
182 if (max >= min) {
183 mRestringViaMin = min;
184 mRestringViaMax = max;
185 } else {
186 throw RuntimeError(__FILE__, __LINE__,
187 tr("Restring vias: MAX must be >= MIN"));
188 }
189 }
190
191 /*******************************************************************************
192 * General Methods
193 ******************************************************************************/
194
restoreDefaults()195 void BoardDesignRules::restoreDefaults() noexcept {
196 *this = BoardDesignRules();
197 }
198
serialize(SExpression & root) const199 void BoardDesignRules::serialize(SExpression& root) const {
200 // general attributes
201 root.appendChild("name", mName, true);
202 root.appendChild("description", mDescription, true);
203 // stop mask
204 root.appendChild("stopmask_clearance_ratio", mStopMaskClearanceRatio, true);
205 root.appendChild("stopmask_clearance_min", mStopMaskClearanceMin, true);
206 root.appendChild("stopmask_clearance_max", mStopMaskClearanceMax, true);
207 root.appendChild("stopmask_max_via_drill_diameter",
208 mStopMaskMaxViaDrillDiameter, true);
209 // cream mask
210 root.appendChild("creammask_clearance_ratio", mCreamMaskClearanceRatio, true);
211 root.appendChild("creammask_clearance_min", mCreamMaskClearanceMin, true);
212 root.appendChild("creammask_clearance_max", mCreamMaskClearanceMax, true);
213 // restring
214 root.appendChild("restring_pad_ratio", mRestringPadRatio, true);
215 root.appendChild("restring_pad_min", mRestringPadMin, true);
216 root.appendChild("restring_pad_max", mRestringPadMax, true);
217 root.appendChild("restring_via_ratio", mRestringViaRatio, true);
218 root.appendChild("restring_via_min", mRestringViaMin, true);
219 root.appendChild("restring_via_max", mRestringViaMax, true);
220 }
221
222 /*******************************************************************************
223 * Helper Methods
224 ******************************************************************************/
225
doesViaRequireStopMask(const Length & drillDia) const226 bool BoardDesignRules::doesViaRequireStopMask(const Length& drillDia) const
227 noexcept {
228 return (drillDia > *mStopMaskMaxViaDrillDiameter ? true : false);
229 }
230
calcStopMaskClearance(const Length & padSize) const231 UnsignedLength BoardDesignRules::calcStopMaskClearance(
232 const Length& padSize) const noexcept {
233 return UnsignedLength(
234 qBound(*mStopMaskClearanceMin,
235 padSize.scaled(mStopMaskClearanceRatio->toNormalized()),
236 *mStopMaskClearanceMax));
237 }
238
calcCreamMaskClearance(const Length & padSize) const239 UnsignedLength BoardDesignRules::calcCreamMaskClearance(
240 const Length& padSize) const noexcept {
241 return UnsignedLength(
242 qBound(*mCreamMaskClearanceMin,
243 padSize.scaled(mCreamMaskClearanceRatio->toNormalized()),
244 *mCreamMaskClearanceMax));
245 }
246
calcPadRestring(const Length & drillDia) const247 UnsignedLength BoardDesignRules::calcPadRestring(const Length& drillDia) const
248 noexcept {
249 return UnsignedLength(qBound(
250 *mRestringPadMin, drillDia.scaled(mRestringPadRatio->toNormalized()),
251 *mRestringPadMax));
252 }
253
calcViaRestring(const Length & drillDia) const254 UnsignedLength BoardDesignRules::calcViaRestring(const Length& drillDia) const
255 noexcept {
256 return UnsignedLength(qBound(
257 *mRestringViaMin, drillDia.scaled(mRestringViaRatio->toNormalized()),
258 *mRestringViaMax));
259 }
260
261 /*******************************************************************************
262 * Operator Overloadings
263 ******************************************************************************/
264
operator =(const BoardDesignRules & rhs)265 BoardDesignRules& BoardDesignRules::operator=(
266 const BoardDesignRules& rhs) noexcept {
267 // general attributes
268 mName = rhs.mName;
269 mDescription = rhs.mDescription;
270 // stop mask
271 mStopMaskClearanceRatio = rhs.mStopMaskClearanceRatio;
272 mStopMaskClearanceMin = rhs.mStopMaskClearanceMin;
273 mStopMaskClearanceMax = rhs.mStopMaskClearanceMax;
274 mStopMaskMaxViaDrillDiameter = rhs.mStopMaskMaxViaDrillDiameter;
275 // cream mask
276 mCreamMaskClearanceRatio = rhs.mCreamMaskClearanceRatio;
277 mCreamMaskClearanceMin = rhs.mCreamMaskClearanceMin;
278 mCreamMaskClearanceMax = rhs.mCreamMaskClearanceMax;
279 // restring
280 mRestringPadRatio = rhs.mRestringPadRatio;
281 mRestringPadMin = rhs.mRestringPadMin;
282 mRestringPadMax = rhs.mRestringPadMax;
283 mRestringViaRatio = rhs.mRestringViaRatio;
284 mRestringViaMin = rhs.mRestringViaMin;
285 mRestringViaMax = rhs.mRestringViaMax;
286 return *this;
287 }
288
289 /*******************************************************************************
290 * End of File
291 ******************************************************************************/
292
293 } // namespace librepcb
294