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