46
42
CLSSizeCodes = ['00', '0', '1', '2']
47
43
CLSDiamCodes = ['Auto', 'M2', 'M2.5', 'M3', 'M3.5', 'M4', 'M5', 'M6', 'M8', 'M10', 'M12']
49
# (00, 0, 1, 2 ) C, E, T, d
50
'M2': ((0, 0.77, 0.97, 1.38), 4.2, 6.35, 1.5, 1.6),
51
'M2.5': ((0, 0.77, 0.97, 1.38), 4.2, 6.35, 1.5, 2.05),
52
'M3': ((0, 0.77, 0.97, 1.38), 4.2, 6.35, 1.5, 2.5),
53
'M3.5': ((0, 0.77, 0.97, 1.38), 4.73, 7.11, 1.5, 2.9),
54
'M4': ((0, 0.77, 0.97, 1.38), 5.38, 7.87, 2.0, 3.3),
55
'M5': ((0, 0.77, 0.97, 1.38), 6.33, 8.64, 2.0, 4.2),
56
'M6': ((0.89, 1.15, 1.38, 2.21), 8.73, 11.18, 4.08, 5.0),
57
'M8': ((0, 0, 1.38, 2.21), 10.47, 12.7, 5.47, 6.8),
58
'M10': ((0, 0, 2.21, 3.05), 13.97, 17.35, 7.48, 8.5),
59
'M12': ((0, 0, 3.05, 0), 16.95, 20.57, 8.5, 10.2)
45
# (00, 0, 1, 2 ) C, E, T, d
46
'M2': ((0, 0.77, 0.97, 1.38), 4.2, 6.35, 1.5, 1.6),
47
'M2.5':((0, 0.77, 0.97, 1.38), 4.2, 6.35, 1.5, 2.05),
48
'M3': ((0, 0.77, 0.97, 1.38), 4.2, 6.35, 1.5, 2.5),
49
'M3.5':((0, 0.77, 0.97, 1.38), 4.73, 7.11, 1.5, 2.9),
50
'M4': ((0, 0.77, 0.97, 1.38), 5.38, 7.87, 2.0, 3.3),
51
'M5': ((0, 0.77, 0.97, 1.38), 6.33, 8.64, 2.0, 4.2),
52
'M6': ((0.89, 1.15, 1.38, 2.21), 8.73, 11.18, 4.08, 5.0),
53
'M8': ((0, 0, 1.38, 2.21), 10.47, 12.7, 5.47, 6.8),
54
'M10': ((0, 0, 2.21, 3.05), 13.97, 17.35, 7.48, 8.5),
55
'M12': ((0, 0, 3.05, 0), 16.95, 20.57, 8.5, 10.2)
63
59
def clMakeWire(do, di, a, c, e, t):
76
fm = FastenerBase.FSFaceMaker()
77
fm.AddPoint(di, -a + ch1)
80
fm.AddPoint(c, -a * 0.75, )
81
fm.AddPoint(c - sl, -a2)
85
fm.AddPoint(e, t - ch2)
86
fm.AddPoint(e - ch2, t)
88
fm.AddPoint(di, t - ch1)
72
fm = FastenerBase.FSFaceMaker()
73
fm.AddPoint(di, -a + ch1)
76
fm.AddPoint(c, -a * 0.75,)
77
fm.AddPoint(c - sl, -a2)
81
fm.AddPoint(e, t - ch2)
82
fm.AddPoint(e - ch2, t)
84
fm.AddPoint(di, t - ch1)
92
87
def clMakePressNut(diam, code):
93
if code not in CLSSizeCodes:
95
i = CLSSizeCodes.index(code)
97
if not (diam in CLSPEMTable):
100
(key, shape) = FastenerBase.FSGetKey('PressNut', diam, code)
101
if shape is not None:
104
ls, c, e, t, di = CLSPEMTable[diam]
108
do = FastenerBase.MToFloat(diam)
109
f = clMakeWire(do, di, a, c, e, t)
110
p = f.revolve(Base.Vector(0.0, 0.0, 0.0), Base.Vector(0.0, 0.0, 1.0), 360)
111
FastenerBase.FSCache[key] = p
88
if code not in CLSSizeCodes:
90
i = CLSSizeCodes.index(code)
92
if not(diam in CLSPEMTable):
95
(key, shape) = FastenerBase.FSGetKey('PressNut', diam, code)
99
ls, c, e, t, di = CLSPEMTable[diam]
103
do = FastenerBase.MToFloat(diam)
104
f = clMakeWire(do, di, a, c, e, t)
105
p = f.revolve(Base.Vector(0.0,0.0,0.0),Base.Vector(0.0,0.0,1.0),360)
106
FastenerBase.FSCache[key] = p
115
109
def clFindClosest(diam, code):
116
''' Find closest standard screw to given parameters '''
117
if code not in CLSSizeCodes:
119
i = CLSSizeCodes.index(code)
120
lens = CLSPEMTable[diam][0]
124
max = len(CLSSizeCodes)
127
if c != 0 and min == 999:
129
if c == 0 and min != 999:
134
return CLSSizeCodes[min]
135
# i is probably > max
136
return CLSSizeCodes[max]
110
''' Find closest standard screw to given parameters '''
111
if code not in CLSSizeCodes:
113
i = CLSSizeCodes.index(code)
114
lens = CLSPEMTable[diam][0]
118
max = len(CLSSizeCodes)
121
if c != 0 and min == 999:
123
if c == 0 and min != 999:
128
return CLSSizeCodes[min]
129
# i is probably > max
130
return CLSSizeCodes[max]
139
133
# h = clMakePressNut('M5','1')
141
135
class FSPressNutObject(FSBaseObject):
142
def __init__(self, obj, attachTo):
143
'''"Add Press nut (self clinching) type fastener" '''
144
FSBaseObject.__init__(self, obj, attachTo)
145
self.itemText = "PressNut"
146
# self.Proxy = obj.Name
148
obj.addProperty("App::PropertyEnumeration", "tcode", "Parameters", "Thickness code").tcode = CLSSizeCodes
149
obj.addProperty("App::PropertyEnumeration", "diameter", "Parameters", "Press nut thread diameter").diameter = CLSDiamCodes
150
obj.invert = FastenerBase.FSLastInvert
154
def execute(self, fp):
155
'''"Print a short message when doing a recomputation, this method is mandatory" '''
158
baseobj = fp.baseObject[0]
159
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
164
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter or self.tcode != fp.tcode:
165
if fp.diameter == 'Auto':
166
d = FastenerBase.FSAutoDiameterM(shape, CLSPEMTable, 1)
170
l = clFindClosest(d, fp.tcode)
175
s = clMakePressNut(d, l)
176
self.diameter = fp.diameter
177
self.tcode = fp.tcode
178
FastenerBase.FSLastInvert = fp.invert
179
fp.Label = fp.diameter + '-PressNut'
182
FreeCAD.Console.PrintLog("Using cached object\n")
183
if shape is not None:
184
# feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
185
# fp.Placement = FreeCAD.Placement() # reset placement
186
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
189
FastenerBase.FSClassIcons[FSPressNutObject] = 'PEMPressNut.svg'
136
def __init__(self, obj, attachTo):
137
'''"Add Press nut (self clinching) type fastener" '''
138
FSBaseObject.__init__(self, obj, attachTo)
139
self.itemText = "PressNut"
140
#self.Proxy = obj.Name
142
obj.addProperty("App::PropertyEnumeration","tcode","Parameters","Thickness code").tcode = CLSSizeCodes
143
obj.addProperty("App::PropertyEnumeration","diameter","Parameters","Press nut thread diameter").diameter = CLSDiamCodes
144
obj.invert = FastenerBase.FSLastInvert
148
def execute(self, fp):
149
'''"Print a short message when doing a recomputation, this method is mandatory" '''
152
baseobj = fp.baseObject[0]
153
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
158
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter or self.tcode != fp.tcode:
159
if fp.diameter == 'Auto':
160
d = FastenerBase.FSAutoDiameterM(shape, CLSPEMTable, 1)
164
l = clFindClosest(d, fp.tcode)
169
s = clMakePressNut(d, l)
170
self.diameter = fp.diameter
171
self.tcode = fp.tcode
172
FastenerBase.FSLastInvert = fp.invert
173
fp.Label = fp.diameter + '-PressNut'
176
FreeCAD.Console.PrintLog("Using cached object\n")
177
if shape is not None:
178
#feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
179
#fp.Placement = FreeCAD.Placement() # reset placement
180
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
183
FastenerBase.FSClassIcons[FSPressNutObject] = 'PEMPressNut.svg'
192
185
class FSPressnutCommand:
193
"""Add Preass-nut command"""
195
def GetResources(self):
196
icon = os.path.join(iconPath, 'PEMPressNut.svg')
198
'Pixmap': icon, # the name of a svg file available in the resources
199
'MenuText': "Add Press-Nut",
200
'ToolTip': "Add PEM Self Clinching Metric Nut"
204
FastenerBase.FSGenerateObjects(FSPressNutObject, "PressNut")
208
return Gui.ActiveDocument is not None
186
"""Add Preass-nut command"""
188
def GetResources(self):
189
icon = os.path.join( iconPath , 'PEMPressNut.svg')
190
return {'Pixmap' : icon , # the name of a svg file available in the resources
191
'MenuText': "Add Press-Nut" ,
192
'ToolTip' : "Add PEM Self Clinching Metric Nut"}
195
FastenerBase.FSGenerateObjects(FSPressNutObject, "PressNut")
199
return Gui.ActiveDocument is not None
211
201
Gui.addCommand("FSPressNut", FSPressnutCommand())
212
202
FastenerBase.FSCommands.append("FSPressNut", "screws", "PEM Inserts")
214
205
###################################################################################
215
206
# PEM Self Clinching standoffs types: SO/SOS/SOA/SO4
216
SOLengths = {'3': 0, '4': 0, '6': 0, '8': 0, '10': 4, '12': 4, '14': 4, '16': 8, '18': 8, '20': 8, '22': 11, '25': 11}
217
# BSLengths = {'6':3.2, '8':4, '10':4, '12':5, '14':6.5, '16':6.5, '18':9.5, '20':9.5, '22':9.5, '25':9.5}
218
SODiameters = ['Auto', 'M3', '3.5M3', 'M3.5', 'M4', 'M5']
207
SOLengths = {'3':0, '4':0, '6':0, '8':0, '10':4, '12':4, '14':4, '16':8, '18':8, '20':8, '22':11, '25':11}
208
#BSLengths = {'6':3.2, '8':4, '10':4, '12':5, '14':6.5, '16':6.5, '18':9.5, '20':9.5, '22':9.5, '25':9.5}
209
SODiameters = ['Auto', 'M3', '3.5M3', 'M3.5', 'M4', 'M5' ]
220
# B, C, H, d, Lmin, Lmax
221
'M3': (3.2, 4.2, 4.8, 2.5, 3, 18),
222
'3.5M3': (3.2, 5.39, 6.4, 2.5, 3, 25),
223
'M3.5': (3.9, 5.39, 6.4, 2.9, 3, 25),
224
'M4': (4.8, 7.12, 7.9, 3.3, 3, 25),
225
'M5': (5.36, 7.12, 7.9, 4.2, 3, 25)
211
# B, C, H, d, Lmin, Lmax
212
'M3': (3.2, 4.2, 4.8, 2.5, 3, 18),
213
'3.5M3':(3.2, 5.39, 6.4, 2.5, 3, 25),
214
'M3.5': (3.9, 5.39, 6.4, 2.9, 3, 25),
215
'M4': (4.8, 7.12, 7.9, 3.3, 3, 25),
216
'M5': (5.36, 7.12, 7.9, 4.2, 3, 25)
229
220
def soMakeFace(b, c, h, d, l):
239
l2 = l1 - SOLengths[l]
242
l3 = h10 * 2 + (c12 + c20) * 2
244
fm = FastenerBase.FSFaceMaker()
247
fm.AddPoint(d, -(l2 - ch1))
255
fm.AddPoint(c1, -(l3 - c20))
256
fm.AddPoint(c, -(l3 - c20))
257
fm.AddPoint(c, -(h10 * 2 + c12 + c20))
258
fm.AddPoint(c1, -(h10 * 2 + c12 + c20))
259
fm.AddPoint(c1, -(h10 * 2 + c12))
260
fm.AddPoint(c, -(h10 * 2 + c12))
261
fm.AddPoint(c, -h10 * 2)
262
fm.AddPoint(c2, -h10 * 2)
263
fm.AddPoint(c2, -h10)
264
fm.AddPoint(h * 0.6, -h10)
265
fm.AddPoint(h * 0.6, 0)
230
l2 = l1 - SOLengths[l]
233
l3 = h10 * 2 + (c12 + c20) * 2
235
fm = FastenerBase.FSFaceMaker()
238
fm.AddPoint(d, -(l2 - ch1))
246
fm.AddPoint(c1, -(l3 - c20))
247
fm.AddPoint(c, -(l3 - c20))
248
fm.AddPoint(c, -(h10 * 2 + c12 + c20))
249
fm.AddPoint(c1, -(h10 * 2 + c12 + c20))
250
fm.AddPoint(c1, -(h10 * 2 + c12))
251
fm.AddPoint(c, -(h10 * 2 + c12))
252
fm.AddPoint(c, -h10 * 2)
253
fm.AddPoint(c2, -h10 * 2)
254
fm.AddPoint(c2, -h10)
255
fm.AddPoint(h * 0.6, -h10)
256
fm.AddPoint(h * 0.6, 0)
269
259
def bsMakeFace(b, c, h, d, l):
281
# l2 = l1 - SOLengths[l]
284
l3 = h10 * 2 + (c12 + c20) * 2
286
fm = FastenerBase.FSFaceMaker()
288
fm.AddPoint(0, -h102)
289
fm.AddPoint(d, -(h102 + ch2))
290
fm.AddPoint(d, -(l1 - ch1))
296
fm.AddPoint(c1, -(l3 - c20))
297
fm.AddPoint(c, -(l3 - c20))
298
fm.AddPoint(c, -(h10 * 2 + c12 + c20))
299
fm.AddPoint(c1, -(h10 * 2 + c12 + c20))
300
fm.AddPoint(c1, -(h10 * 2 + c12))
301
fm.AddPoint(c, -(h10 * 2 + c12))
302
fm.AddPoint(c, -h10 * 2)
303
fm.AddPoint(c2, -h10 * 2)
304
fm.AddPoint(c2, -h10)
305
fm.AddPoint(h * 0.6, -h10)
306
fm.AddPoint(h * 0.6, 0)
271
#l2 = l1 - SOLengths[l]
274
l3 = h10 * 2 + (c12 + c20) * 2
276
fm = FastenerBase.FSFaceMaker()
278
fm.AddPoint(0, -h102)
279
fm.AddPoint(d, -(h102 + ch2))
280
fm.AddPoint(d, -(l1 - ch1))
286
fm.AddPoint(c1, -(l3 - c20))
287
fm.AddPoint(c, -(l3 - c20))
288
fm.AddPoint(c, -(h10 * 2 + c12 + c20))
289
fm.AddPoint(c1, -(h10 * 2 + c12 + c20))
290
fm.AddPoint(c1, -(h10 * 2 + c12))
291
fm.AddPoint(c, -(h10 * 2 + c12))
292
fm.AddPoint(c, -h10 * 2)
293
fm.AddPoint(c2, -h10 * 2)
294
fm.AddPoint(c2, -h10)
295
fm.AddPoint(h * 0.6, -h10)
296
fm.AddPoint(h * 0.6, 0)
310
299
def soMakeStandOff(diam, len, blind):
311
if not (len in SOLengths):
313
if not (diam in SOPEMTable):
316
(key, shape) = FastenerBase.FSGetKey('StandOff', diam, len, blind)
317
if shape is not None:
321
b, c, h, d, lmin, lmax = SOPEMTable[diam]
324
if l < lmin or l > lmax:
328
f = bsMakeFace(b, c, h, d, len)
330
f = soMakeFace(b, c, h, d, len)
331
p = f.revolve(Base.Vector(0.0, 0.0, 0.0), Base.Vector(0.0, 0.0, 1.0), 360)
332
htool = screwMaker.makeHextool(h, 3, h * 2)
333
htool.translate(Base.Vector(0.0, 0.0, -2.0))
335
FastenerBase.FSCache[key] = shape
300
if not(len in SOLengths):
302
if not(diam in SOPEMTable):
305
(key, shape) = FastenerBase.FSGetKey('StandOff', diam, len, blind)
306
if shape is not None:
310
b, c, h, d, lmin, lmax = SOPEMTable[diam]
313
if l < lmin or l > lmax:
317
f = bsMakeFace(b, c, h, d, len)
319
f = soMakeFace(b, c, h, d, len)
320
p = f.revolve(Base.Vector(0.0,0.0,0.0),Base.Vector(0.0,0.0,1.0),360)
321
htool = screwMaker.makeHextool(h, 3, h * 2)
322
htool.translate(Base.Vector(0.0,0.0,-2.0))
324
FastenerBase.FSCache[key] = shape
339
327
def soFindClosest(diam, len):
340
''' Find closest standard screw to given parameters '''
341
if not (diam in SOPEMTable):
343
if float(len) > SOPEMTable[diam][5]:
344
return str(SOPEMTable[diam][5])
345
if float(len) < SOPEMTable[diam][4]:
346
return str(SOPEMTable[diam][4])
328
''' Find closest standard screw to given parameters '''
329
if not(diam in SOPEMTable):
331
if float(len) > SOPEMTable[diam][5]:
332
return str(SOPEMTable[diam][5])
333
if float(len) < SOPEMTable[diam][4]:
334
return str(SOPEMTable[diam][4])
350
337
def soGetAllLengths(diam, blind):
354
b, c, h, d, lmin, lmax = SOPEMTable[diam]
356
for len in SOLengths:
358
if l >= lmin and l <= lmax:
362
sorted(list, key=functools.cmp_to_key(FastenerBase.NumCompare))
364
list.sort(cmp=FastenerBase.NumCompare)
341
b, c, h, d, lmin, lmax = SOPEMTable[diam]
343
for len in SOLengths:
345
if l >= lmin and l <= lmax:
349
sorted(list, key = functools.cmp_to_key(FastenerBase.NumCompare))
351
list.sort(cmp = FastenerBase.NumCompare)
368
354
# h = clMakePressNut('M5','1')
370
356
class FSStandOffObject(FSBaseObject):
371
def __init__(self, obj, attachTo):
372
'''"Add StandOff (self clinching) type fastener" '''
373
FSBaseObject.__init__(self, obj, attachTo)
374
self.itemText = "StandOff"
375
# self.Proxy = obj.Name
377
obj.addProperty("App::PropertyEnumeration", "diameter", "Parameters", "Standoff thread diameter").diameter = SODiameters
378
obj.addProperty("App::PropertyBool", "blind", "Parameters", "Blind Standoff type").blind = False
379
obj.addProperty("App::PropertyEnumeration", "length", "Parameters", "Standoff length").length = soGetAllLengths(SODiameters[1], False)
380
obj.invert = FastenerBase.FSLastInvert
383
def execute(self, fp):
384
'''"Print a short message when doing a recomputation, this method is mandatory" '''
387
baseobj = fp.baseObject[0]
388
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
393
if not (hasattr(self,
394
'diameter')) or self.diameter != fp.diameter or self.length != fp.length or self.blind != fp.blind:
395
diameterchange = False
396
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
397
diameterchange = True
398
if fp.diameter == 'Auto':
399
d = FastenerBase.FSAutoDiameterM(shape, SOPEMTable, 1)
400
diameterchange = True
405
if not (hasattr(self, 'blind')) or self.blind != fp.blind:
408
l = soFindClosest(d, fp.length)
410
diameterchange = True
413
if l != fp.length or diameterchange or blindchange:
414
if diameterchange or blindchange:
415
fp.length = soGetAllLengths(fp.diameter, fp.blind)
418
s = soMakeStandOff(d, l, fp.blind)
419
self.diameter = fp.diameter
420
self.length = fp.length
421
self.blind = fp.blind
422
FastenerBase.FSLastInvert = fp.invert
423
fp.Label = fp.diameter + 'x' + fp.length + '-Standoff'
426
FreeCAD.Console.PrintLog("Using cached object\n")
427
if shape is not None:
428
# feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
429
# fp.Placement = FreeCAD.Placement() # reset placement
430
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
433
FastenerBase.FSClassIcons[FSStandOffObject] = 'PEMTHStandoff.svg'
357
def __init__(self, obj, attachTo):
358
'''"Add StandOff (self clinching) type fastener" '''
359
FSBaseObject.__init__(self, obj, attachTo)
360
self.itemText = "StandOff"
361
#self.Proxy = obj.Name
363
obj.addProperty("App::PropertyEnumeration","diameter","Parameters","Standoff thread diameter").diameter = SODiameters
364
obj.addProperty("App::PropertyBool", "blind", "Parameters", "Blind Standoff type").blind = False
365
obj.addProperty("App::PropertyEnumeration","length","Parameters","Standoff length").length = soGetAllLengths(SODiameters[1], False)
366
obj.invert = FastenerBase.FSLastInvert
369
def execute(self, fp):
370
'''"Print a short message when doing a recomputation, this method is mandatory" '''
373
baseobj = fp.baseObject[0]
374
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
379
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter or self.length != fp.length or self.blind != fp.blind:
380
diameterchange = False
381
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
382
diameterchange = True
383
if fp.diameter == 'Auto':
384
d = FastenerBase.FSAutoDiameterM(shape, SOPEMTable, 1)
385
diameterchange = True
390
if not(hasattr(self, 'blind')) or self.blind != fp.blind:
393
l = soFindClosest(d, fp.length)
395
diameterchange = True
398
if l != fp.length or diameterchange or blindchange:
399
if diameterchange or blindchange:
400
fp.length = soGetAllLengths(fp.diameter, fp.blind)
403
s = soMakeStandOff(d, l, fp.blind)
404
self.diameter = fp.diameter
405
self.length = fp.length
406
self.blind = fp.blind
407
FastenerBase.FSLastInvert = fp.invert
408
fp.Label = fp.diameter + 'x' + fp.length + '-Standoff'
411
FreeCAD.Console.PrintLog("Using cached object\n")
412
if shape is not None:
413
#feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
414
#fp.Placement = FreeCAD.Placement() # reset placement
415
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
418
FastenerBase.FSClassIcons[FSStandOffObject] = 'PEMTHStandoff.svg'
436
421
class FSStandOffCommand:
437
"""Add Standoff command"""
439
def GetResources(self):
440
icon = os.path.join(iconPath, 'PEMTHStandoff.svg')
442
'Pixmap': icon, # the name of a svg file available in the resources
443
'MenuText': "Add Standoff",
444
'ToolTip': "Add PEM Self Clinching Metric Standoff"
448
FastenerBase.FSGenerateObjects(FSStandOffObject, "Standoff")
452
return Gui.ActiveDocument is not None
422
"""Add Standoff command"""
424
def GetResources(self):
425
icon = os.path.join( iconPath , 'PEMTHStandoff.svg')
426
return {'Pixmap' : icon , # the name of a svg file available in the resources
427
'MenuText': "Add Standoff" ,
428
'ToolTip' : "Add PEM Self Clinching Metric Standoff"}
431
FastenerBase.FSGenerateObjects(FSStandOffObject, "Standoff")
435
return Gui.ActiveDocument is not None
455
437
Gui.addCommand("FSStandOff", FSStandOffCommand())
456
438
FastenerBase.FSCommands.append("FSStandOff", "screws", "PEM Inserts")
458
440
###################################################################################
459
441
# PEM Self Clinching studs types: FH/FHS/FHA
460
442
FHLengths = ['6', '8', '10', '12', '15', '18', '20', '25', '30', '35']
461
# BSLengths = {'6':3.2, '8':4, '10':4, '12':5, '14':6.5, '16':6.5, '18':9.5, '20':9.5, '22':9.5, '25':9.5}
462
FHDiameters = ['Auto', 'M2.5', 'M3', 'M3.5', 'M4', 'M5', 'M6', 'M8']
443
#BSLengths = {'6':3.2, '8':4, '10':4, '12':5, '14':6.5, '16':6.5, '18':9.5, '20':9.5, '22':9.5, '25':9.5}
444
FHDiameters = ['Auto', 'M2.5', 'M3', 'M3.5', 'M4', 'M5', 'M6', 'M8' ]
464
# H, S, d, Lmin, Lmax
465
'M2.5': (4.1, 1.95, 2.05, 6, 18),
466
'M3': (4.6, 2.1, 2.5, 6, 25),
467
'M3.5': (5.3, 2.25, 2.9, 6, 30),
468
'M4': (5.9, 2.4, 3.3, 6, 35),
469
'M5': (6.5, 2.7, 4.2, 8, 35),
470
'M6': (8.2, 3.0, 5.0, 10, 35),
471
'M8': (9.6, 3.7, 6.75, 12, 35)
446
# H, S, d, Lmin, Lmax
447
'M2.5': (4.1, 1.95, 2.05, 6, 18),
448
'M3': (4.6, 2.1, 2.5, 6, 25),
449
'M3.5': (5.3, 2.25, 2.9, 6, 30),
450
'M4': (5.9, 2.4, 3.3, 6, 35),
451
'M5': (6.5, 2.7, 4.2, 8, 35),
452
'M6': (8.2, 3.0, 5.0, 10, 35),
453
'M8': (9.6, 3.7, 6.75, 12, 35)
475
456
def fhMakeFace(m, h, d, l):
486
mr = m9 - m25 * (1.0 - cos30)
489
fm = FastenerBase.FSFaceMaker()
491
fm.AddPoint(h - h20, 0)
492
fm.AddArc(h, - h20, h - h20, -h10)
493
fm.AddPoint(h - h20, -(h10 + h20))
494
fm.AddPoint(m, -(h10 + h20))
496
fm.AddPoint(m9, -(hs + m25))
497
fm.AddArc(mr, -(hs + m25 * 1.5), m9, -(hs + m25 * 2))
499
fm.AddPoint(m, -(l - ch1))
500
fm.AddPoint(m - ch1, -l)
467
mr = m9 - m25 * (1.0 - cos30)
470
fm = FastenerBase.FSFaceMaker()
472
fm.AddPoint(h - h20, 0)
473
fm.AddArc(h, - h20, h - h20, -h10)
474
fm.AddPoint(h - h20, -(h10 + h20))
475
fm.AddPoint(m , -(h10 + h20))
477
fm.AddPoint(m9, -(hs + m25))
478
fm.AddArc(mr, -(hs + m25 * 1.5), m9, -(hs + m25 * 2))
480
fm.AddPoint(m, -(l - ch1))
481
fm.AddPoint(m - ch1, -l)
505
485
def fhMakeStud(diam, len):
506
if not (len in FHLengths):
508
if not (diam in FHPEMTable):
511
key, shape = FastenerBase.FSGetKey('Stud', diam, len)
512
if shape is not None:
516
m = FastenerBase.MToFloat(diam)
517
h, s, d, lmin, lmax = FHPEMTable[diam]
518
if l < lmin or l > lmax:
521
f = fhMakeFace(m, h, d, l)
522
p = f.revolve(Base.Vector(0.0, 0.0, 0.0), Base.Vector(0.0, 0.0, 1.0), 360)
523
FastenerBase.FSCache[key] = p
486
if not(len in FHLengths):
488
if not(diam in FHPEMTable):
491
key, shape = FastenerBase.FSGetKey('Stud', diam, len)
492
if shape is not None:
496
m = FastenerBase.MToFloat(diam)
497
h, s, d, lmin, lmax = FHPEMTable[diam]
498
if l < lmin or l > lmax:
501
f = fhMakeFace(m, h, d, l)
502
p = f.revolve(Base.Vector(0.0,0.0,0.0),Base.Vector(0.0,0.0,1.0),360)
503
FastenerBase.FSCache[key] = p
527
507
def fhFindClosest(diam, len):
528
''' Find closest standard screw to given parameters '''
529
if not (diam in FHPEMTable):
531
if float(len) > FHPEMTable[diam][4]:
532
return str(FHPEMTable[diam][4])
533
if float(len) < FHPEMTable[diam][3]:
534
return str(FHPEMTable[diam][3])
508
''' Find closest standard screw to given parameters '''
509
if not(diam in FHPEMTable):
511
if float(len) > FHPEMTable[diam][4]:
512
return str(FHPEMTable[diam][4])
513
if float(len) < FHPEMTable[diam][3]:
514
return str(FHPEMTable[diam][3])
538
518
def fhGetAllLengths(diam):
539
h, s, d, lmin, lmax = FHPEMTable[diam]
541
for len in FHLengths:
543
if l >= lmin and l <= lmax:
547
sorted(list, key=functools.cmp_to_key(FastenerBase.NumCompare))
549
list.sort(cmp=FastenerBase.NumCompare)
519
h, s, d, lmin, lmax = FHPEMTable[diam]
521
for len in FHLengths:
523
if l >= lmin and l <= lmax:
527
sorted(list, key=functools.cmp_to_key(FastenerBase.NumCompare))
529
list.sort(cmp=FastenerBase.NumCompare)
553
533
class FSStudObject(FSBaseObject):
554
def __init__(self, obj, attachTo):
555
'''"Add Stud (self clinching) type fastener" '''
556
FSBaseObject.__init__(self, obj, attachTo)
557
self.itemText = "Stud"
558
# self.Proxy = obj.Name
560
obj.addProperty("App::PropertyEnumeration", "diameter", "Parameters",
561
"Standoff thread diameter").diameter = FHDiameters
562
obj.addProperty("App::PropertyEnumeration", "length", "Parameters", "Standoff length").length = fhGetAllLengths(
564
obj.invert = FastenerBase.FSLastInvert
567
def execute(self, fp):
568
'''"Print a short message when doing a recomputation, this method is mandatory" '''
571
baseobj = fp.baseObject[0]
572
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
577
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter or self.length != fp.length:
578
diameterchange = False
579
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
580
diameterchange = True
581
if fp.diameter == 'Auto':
582
d = FastenerBase.FSAutoDiameterM(shape, FHPEMTable, -1)
583
diameterchange = True
587
l = fhFindClosest(d, fp.length)
589
diameterchange = True
592
if l != fp.length or diameterchange:
594
fp.length = fhGetAllLengths(fp.diameter)
598
self.diameter = fp.diameter
599
self.length = fp.length
600
FastenerBase.FSLastInvert = fp.invert
601
fp.Label = fp.diameter + 'x' + fp.length + '-Stud'
604
FreeCAD.Console.PrintLog("Using cached object\n")
605
if shape is not None:
606
# feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
607
# fp.Placement = FreeCAD.Placement() # reset placement
608
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
611
FastenerBase.FSClassIcons[FSStudObject] = 'PEMStud.svg'
534
def __init__(self, obj, attachTo):
535
'''"Add Stud (self clinching) type fastener" '''
536
FSBaseObject.__init__(self, obj, attachTo)
537
self.itemText = "Stud"
538
#self.Proxy = obj.Name
540
obj.addProperty("App::PropertyEnumeration","diameter","Parameters","Standoff thread diameter").diameter = FHDiameters
541
obj.addProperty("App::PropertyEnumeration","length","Parameters","Standoff length").length = fhGetAllLengths(FHDiameters[1])
542
obj.invert = FastenerBase.FSLastInvert
545
def execute(self, fp):
546
'''"Print a short message when doing a recomputation, this method is mandatory" '''
549
baseobj = fp.baseObject[0]
550
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
555
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter or self.length != fp.length:
556
diameterchange = False
557
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
558
diameterchange = True
559
if fp.diameter == 'Auto':
560
d = FastenerBase.FSAutoDiameterM(shape, FHPEMTable, -1)
561
diameterchange = True
565
l = fhFindClosest(d, fp.length)
567
diameterchange = True
570
if l != fp.length or diameterchange:
572
fp.length = fhGetAllLengths(fp.diameter)
576
self.diameter = fp.diameter
577
self.length = fp.length
578
FastenerBase.FSLastInvert = fp.invert
579
fp.Label = fp.diameter + 'x' + fp.length + '-Stud'
582
FreeCAD.Console.PrintLog("Using cached object\n")
583
if shape is not None:
584
#feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
585
#fp.Placement = FreeCAD.Placement() # reset placement
586
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
589
FastenerBase.FSClassIcons[FSStudObject] = 'PEMStud.svg'
614
592
class FSStudCommand:
615
"""Add Standoff command"""
617
def GetResources(self):
618
icon = os.path.join(iconPath, 'PEMStud.svg')
620
'Pixmap': icon, # the name of a svg file available in the resources
621
'MenuText': "Add Stud",
622
'ToolTip': "Add PEM Self Clinching Metric Stud"
626
FastenerBase.FSGenerateObjects(FSStudObject, "Stud")
630
return Gui.ActiveDocument is not None
593
"""Add Standoff command"""
595
def GetResources(self):
596
icon = os.path.join( iconPath , 'PEMStud.svg')
597
return {'Pixmap' : icon , # the name of a svg file available in the resources
598
'MenuText': "Add Stud" ,
599
'ToolTip' : "Add PEM Self Clinching Metric Stud"}
602
FastenerBase.FSGenerateObjects(FSStudObject, "Stud")
606
return Gui.ActiveDocument is not None
633
608
Gui.addCommand("FSStud", FSStudCommand())
634
609
FastenerBase.FSCommands.append("FSStud", "screws", "PEM Inserts")
643
618
FastenerBase.FSAddFastenerType("HeatSet", False)
644
619
FastenerBase.FSAddItemsToType("HeatSet", "HeatSet")
647
621
def FSPIGetAllDiameters(type):
648
if type == "PressNut":
649
diams = list(CLSDiamCodes)
650
elif type == "StandOff":
651
diams = list(SODiameters)
653
diams = list(FHDiameters)
622
if type == "PressNut":
623
diams = list(CLSDiamCodes)
624
elif type == "StandOff":
625
diams = list(SODiameters)
627
diams = list(FHDiameters)
658
631
def FSPIGetAllLengths(type, diam):
659
# if type == "PressNut":
661
if type == "StandOff":
662
return soGetAllLengths(diam, False)
664
return fhGetAllLengths(diam)
632
#if type == "PressNut":
634
if type == "StandOff":
635
return soGetAllLengths(diam, False)
637
return fhGetAllLengths(diam)
668
641
###################################################################################
669
642
# PCB standoffs / Wurth standard WA-SSTIE
671
'M2.5x5': ('5', '6', '7', '8', '9', '10', '12', '15', '17', '18', '20', '25', '30'),
672
'M3x5': ('10', '15', '20', '25'),
674
'5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '22', '23', '25', '27',
675
'28', '30', '35', '40', '45', '50', '55', '60'),
676
'M3x6': ('8', '10', '12', '15', '20', '25', '30', '35', '40'),
677
'M4x7': ('5', '6', '8', '10', '12', '15', '20', '25', '30', '35', '40'),
678
'M5x8': ('8', '10', '15', '20', '25', '30', '40', '50', '60', '70'),
679
'M6x10': ('15', '20', '25', '30', '35', '40', '45', '50', '60')
644
'M2.5x5': ('5', '6', '7', '8', '9', '10', '12', '15', '17', '18', '20', '25', '30'),
645
'M3x5': ('10', '15', '20', '25'),
646
'M3x5.5': ('5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '22', '23', '25', '27', '28', '30', '35', '40', '45', '50', '55', '60'),
647
'M3x6': ('8', '10', '12', '15', '20', '25', '30', '35', '40'),
648
'M4x7': ('5', '6', '8', '10', '12', '15', '20', '25', '30', '35', '40'),
649
'M5x8': ('8', '10', '15', '20', '25', '30', '40', '50', '60', '70'),
650
'M6x10': ('15', '20', '25', '30', '35', '40', '45', '50', '60')
681
PSDiameters = ['Auto', 'M2.5', 'M3', 'M4', 'M5', 'M6']
652
PSDiameters = ['Auto', 'M2.5', 'M3', 'M4', 'M5', 'M6' ]
684
'M2.5': (6, 2.05, ('5',)),
685
'M3': (6, 2.5, ('5', '5.5', '6')),
686
'M4': (8, 3.3, ('7',)),
687
'M5': (10, 4.2, ('8',)),
688
'M6': (10, 5, ('10',))
655
'M2.5': (6, 2.05, ('5',)),
656
'M3': (6, 2.5, ('5', '5.5', '6')),
657
'M4': (8, 3.3, ('7',)),
658
'M5': (10, 4.2, ('8',)),
659
'M6': (10, 5, ('10',))
692
663
def psMakeFace(m, sw, lo, l, id):
695
sw2 = float(sw) / 2.0
697
d2 = 0.95 * sw2 / cos30
698
l1 = l - (d2 - sw2) / 2.0
708
fm = FastenerBase.FSFaceMaker()
711
fm.AddPoint(id2, l - dd)
712
fm.AddPoint(id2 + dd, l)
717
fm.AddPoint(id2, lo1)
720
fm.AddPoint(id2, -lo)
666
sw2 = float(sw) / 2.0
668
d2 = 0.95 * sw2 / cos30
669
l1 = l - (d2 - sw2) / 2.0
679
fm = FastenerBase.FSFaceMaker()
682
fm.AddPoint(id2, l - dd)
683
fm.AddPoint(id2 + dd, l)
688
fm.AddPoint(id2, lo1)
691
fm.AddPoint(id2, -lo)
725
695
def psMakeStandOff(diam, len, width, screwlen):
726
FreeCAD.Console.PrintLog("Making PCB standof" + diam + "x" + len + "x" + width + "x" + str(screwlen) + "\n")
727
if not (diam in PSMTable):
729
lenKey = diam + "x" + width
730
if not (lenKey in PSLengths):
733
(key, shape) = FastenerBase.FSGetKey('PcbStandOff', diam, width, len, screwlen)
734
if shape is not None:
737
tlo, id, sw = PSMTable[diam]
739
m = FastenerBase.MToFloat(diam)
740
f = psMakeFace(m, width, screwlen, len, id)
741
p = f.revolve(Base.Vector(0.0, 0.0, 0.0), Base.Vector(0.0, 0.0, 1.0), 360)
744
htool = screwMaker.makeHextool(w, l + screwlen, w * 2)
745
htool.translate(Base.Vector(0.0, 0.0, -screwlen - 0.1))
747
FastenerBase.FSCache[key] = shape
696
FreeCAD.Console.PrintLog("Making PCB standof" + diam + "x" + len + "x" + width + "x" + str(screwlen) + "\n")
697
if not(diam in PSMTable):
699
lenKey = diam + "x" + width
700
if not(lenKey in PSLengths):
703
(key, shape) = FastenerBase.FSGetKey('PcbStandOff', diam, width, len, screwlen)
704
if shape is not None:
707
tlo, id, sw = PSMTable[diam]
709
m = FastenerBase.MToFloat(diam)
710
f = psMakeFace(m, width, screwlen, len, id)
711
p = f.revolve(Base.Vector(0.0,0.0,0.0),Base.Vector(0.0,0.0,1.0),360)
714
htool = screwMaker.makeHextool(w, l + screwlen, w * 2)
715
htool.translate(Base.Vector(0.0,0.0,-screwlen - 0.1))
717
FastenerBase.FSCache[key] = shape
751
720
def psFindClosest(mtable, ltable, diam, width, len):
752
''' Find closest standard standoff to given parameters '''
753
if not (diam in mtable):
755
lenKey = diam + "x" + width
756
if not (lenKey in ltable):
758
lens = ltable[lenKey]
760
if float(len) <= float(l):
762
return lens[len(lens) - 1]
721
''' Find closest standard standoff to given parameters '''
722
if not(diam in mtable):
724
lenKey = diam + "x" + width
725
if not(lenKey in ltable):
727
lens = ltable[lenKey]
729
if float(len) <= float(l):
731
return lens[len(lens) - 1]
765
733
def psGetAllWidths(mtable, diam):
766
if not (diam in mtable):
768
list = mtable[diam][2]
771
sorted(list, key=functools.cmp_to_key(FastenerBase.NumCompare))
773
list.sort(cmp=FastenerBase.NumCompare)
734
if not(diam in mtable):
736
list = mtable[diam][2]
739
sorted(list, key = functools.cmp_to_key(FastenerBase.NumCompare))
741
list.sort(cmp = FastenerBase.NumCompare)
777
745
def psGetAllLengths(mtable, ltable, diam, width):
778
if not (diam in mtable):
780
if width not in mtable[diam][2]:
781
width = mtable[diam][2][0]
782
lenKey = diam + "x" + width
783
if not (lenKey in ltable):
786
for len in ltable[lenKey]:
790
sorted(list, key=functools.cmp_to_key(FastenerBase.NumCompare))
792
list.sort(cmp=FastenerBase.NumCompare)
793
list.append("Custom")
746
if not(diam in mtable):
748
if width not in mtable[diam][2]:
749
width = mtable[diam][2][0]
750
lenKey = diam + "x" + width
751
if not(lenKey in ltable):
754
for len in ltable[lenKey]:
758
sorted(list, key=functools.cmp_to_key(FastenerBase.NumCompare))
760
list.sort(cmp=FastenerBase.NumCompare)
761
list.append("Custom")
797
764
# h = clMakePressNut('M5','1')
799
766
class FSPcbStandOffObject(FSBaseObject):
800
def __init__(self, obj, attachTo):
801
'''"Add PCB StandOff type fastener" '''
802
FSBaseObject.__init__(self, obj, attachTo)
803
self.itemText = "PcbStandOff"
804
# self.Proxy = obj.Name
806
obj.addProperty("App::PropertyEnumeration", "diameter", "Parameters",
807
"Standoff thread diameter").diameter = PSDiameters
808
widths = psGetAllWidths(PSMTable, PSDiameters[1])
809
obj.addProperty("App::PropertyEnumeration", "width", "Parameters", "Standoff body width").width = widths
810
self.VerifyMissingAttrs(obj, PSDiameters[1], widths[0])
811
# obj.addProperty("App::PropertyEnumeration","length","Parameters","Standoff length").length = psGetAllLengths(PSMTable, PSLengths ,PSDiameters[1], widths[0])
812
obj.invert = FastenerBase.FSLastInvert
815
def VerifyMissingAttrs(self, obj, diam, width):
816
self.updateProps(obj)
817
if not hasattr(obj, 'lengthCustom'):
818
slens = psGetAllLengths(PSMTable, PSLengths, diam, width)
819
if hasattr(obj, 'length'):
822
if origLen not in slens:
823
obj.length = slens[0]
827
obj.addProperty("App::PropertyEnumeration", "length", "Parameters", "Standoff length").length = slens
828
obj.addProperty("App::PropertyLength", "lengthCustom", "Parameters", "Custom length").lengthCustom = slens[
830
if not hasattr(obj, 'screwLength'):
831
obj.addProperty("App::PropertyLength", "screwLength", "Parameters", "Thread length").screwLength = \
834
def ActiveLength(self, obj):
835
if not hasattr(obj, 'length'):
837
if obj.length == 'Custom':
838
return str(float(obj.lengthCustom)).rstrip("0").rstrip('.')
841
def execute(self, fp):
842
'''"Print a short message when doing a recomputation, this method is mandatory" '''
845
baseobj = fp.baseObject[0]
846
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
851
# for backward compatibility: add missing attribute if needed
852
self.VerifyMissingAttrs(fp, fp.diameter, fp.width)
854
diameterchange = not (hasattr(self, 'diameter')) or self.diameter != fp.diameter
855
widthchange = not (hasattr(self, 'width')) or self.width != fp.width
856
lengthchange = not (hasattr(self, 'length')) or self.length != fp.length
857
cutstlenchange = not (hasattr(self, 'lengthCustom')) or self.lengthCustom != fp.lengthCustom
858
screwlenchange = not (hasattr(self, 'screwLength')) or self.screwLength != fp.screwLength
859
if diameterchange or widthchange or lengthchange or cutstlenchange or screwlenchange:
860
if fp.diameter == 'Auto':
861
d = FastenerBase.FSAutoDiameterM(shape, PSMTable, 1)
862
diameterchange = True
867
diameterchange = True
870
if widthchange or diameterchange:
873
allwidth = psGetAllWidths(PSMTable, d)
875
if len(allwidth) > 1:
876
fp.width = allwidth[1]
878
fp.width = allwidth[0]
880
if not lengthchange and hasattr(self, 'lengthCustom') and self.lengthCustom != fp.lengthCustom.Value:
883
if fp.length == 'Custom':
884
l = str(float(fp.lengthCustom)).rstrip("0").rstrip('.')
886
l = psFindClosest(PSMTable, PSLengths, d, fp.width, fp.length)
888
if l != fp.length or diameterchange or widthchange:
889
if diameterchange or widthchange:
890
fp.length = psGetAllLengths(PSMTable, PSLengths, fp.diameter, fp.width)
895
fp.screwLength = PSMTable[fp.diameter][0]
896
elif fp.screwLength < 2:
899
s = psMakeStandOff(d, l, fp.width, float(fp.screwLength))
901
self.diameter = fp.diameter
902
self.length = fp.length
903
self.width = fp.width
904
self.lengthCustom = fp.lengthCustom.Value
905
self.screwLength = fp.screwLength.Value
906
FastenerBase.FSLastInvert = fp.invert
907
fp.Label = fp.diameter + 'x' + fp.width + 'x' + l + '-Standoff'
767
def __init__(self, obj, attachTo):
768
'''"Add PCB StandOff type fastener" '''
769
FSBaseObject.__init__(self, obj, attachTo)
770
self.itemText = "PcbStandOff"
771
#self.Proxy = obj.Name
773
obj.addProperty("App::PropertyEnumeration","diameter","Parameters","Standoff thread diameter").diameter = PSDiameters
774
widths = psGetAllWidths(PSMTable ,PSDiameters[1])
775
obj.addProperty("App::PropertyEnumeration", "width", "Parameters", "Standoff body width").width = widths
776
self.VerifyMissingAttrs(obj, PSDiameters[1], widths[0])
777
#obj.addProperty("App::PropertyEnumeration","length","Parameters","Standoff length").length = psGetAllLengths(PSMTable, PSLengths ,PSDiameters[1], widths[0])
778
obj.invert = FastenerBase.FSLastInvert
781
def VerifyMissingAttrs(self, obj, diam, width):
782
self.updateProps(obj)
783
if not hasattr(obj, 'lengthCustom'):
784
slens = psGetAllLengths(PSMTable, PSLengths ,diam, width)
785
if hasattr(obj, 'length'):
788
if origLen not in slens:
789
obj.length = slens[0]
910
FreeCAD.Console.PrintLog("Using cached object\n")
911
if shape is not None:
912
# feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
913
# fp.Placement = FreeCAD.Placement() # reset placement
914
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
917
FastenerBase.FSClassIcons[FSPcbStandOffObject] = 'PCBStandoff.svg'
793
obj.addProperty("App::PropertyEnumeration", "length", "Parameters", "Standoff length").length = slens
794
obj.addProperty("App::PropertyLength", "lengthCustom", "Parameters", "Custom length").lengthCustom = slens[0]
795
if not hasattr(obj, 'screwLength'):
796
obj.addProperty("App::PropertyLength", "screwLength", "Parameters", "Thread length").screwLength = PSMTable[diam][0]
798
def ActiveLength(self, obj):
799
if not hasattr(obj,'length'):
801
if obj.length == 'Custom':
802
return str(float(obj.lengthCustom)).rstrip("0").rstrip('.')
805
def execute(self, fp):
806
'''"Print a short message when doing a recomputation, this method is mandatory" '''
809
baseobj = fp.baseObject[0]
810
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
815
# for backward compatibility: add missing attribute if needed
816
self.VerifyMissingAttrs(fp, fp.diameter, fp.width)
818
diameterchange = not (hasattr(self, 'diameter')) or self.diameter != fp.diameter
819
widthchange = not(hasattr(self, 'width')) or self.width != fp.width
820
lengthchange = not(hasattr(self, 'length')) or self.length != fp.length
821
cutstlenchange = not(hasattr(self, 'lengthCustom')) or self.lengthCustom != fp.lengthCustom
822
screwlenchange = not(hasattr(self, 'screwLength')) or self.screwLength != fp.screwLength
823
if diameterchange or widthchange or lengthchange or cutstlenchange or screwlenchange:
824
if fp.diameter == 'Auto':
825
d = FastenerBase.FSAutoDiameterM(shape, PSMTable, 1)
826
diameterchange = True
831
diameterchange = True
834
if widthchange or diameterchange:
837
allwidth = psGetAllWidths(PSMTable, d)
839
if len(allwidth) > 1:
840
fp.width = allwidth[1]
842
fp.width = allwidth[0]
844
if not lengthchange and hasattr(self,'lengthCustom') and self.lengthCustom != fp.lengthCustom.Value:
847
if fp.length == 'Custom':
848
l = str(float(fp.lengthCustom)).rstrip("0").rstrip('.')
850
l = psFindClosest(PSMTable, PSLengths ,d, fp.width, fp.length)
852
if l != fp.length or diameterchange or widthchange:
853
if diameterchange or widthchange:
854
fp.length = psGetAllLengths(PSMTable, PSLengths ,fp.diameter, fp.width)
859
fp.screwLength = PSMTable[fp.diameter][0]
860
elif fp.screwLength < 2:
863
s = psMakeStandOff(d, l, fp.width, float(fp.screwLength))
865
self.diameter = fp.diameter
866
self.length = fp.length
867
self.width = fp.width
868
self.lengthCustom = fp.lengthCustom.Value
869
self.screwLength = fp.screwLength.Value
870
FastenerBase.FSLastInvert = fp.invert
871
fp.Label = fp.diameter + 'x' + fp.width + 'x' + l + '-Standoff'
874
FreeCAD.Console.PrintLog("Using cached object\n")
875
if shape is not None:
876
#feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
877
#fp.Placement = FreeCAD.Placement() # reset placement
878
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
881
FastenerBase.FSClassIcons[FSPcbStandOffObject] = 'PCBStandoff.svg'
920
883
class FSPcbStandOffCommand:
921
"""Add PCB Standoff command"""
923
def GetResources(self):
924
icon = os.path.join(iconPath, 'PCBStandoff.svg')
926
'Pixmap': icon, # the name of a svg file available in the resources
927
'MenuText': "Add PCB Standoff",
928
'ToolTip': "Add PCB Metric Standoff"
932
FastenerBase.FSGenerateObjects(FSPcbStandOffObject, "PcbStandoff")
936
return Gui.ActiveDocument is not None
884
"""Add PCB Standoff command"""
886
def GetResources(self):
887
icon = os.path.join( iconPath , 'PCBStandoff.svg')
888
return {'Pixmap' : icon , # the name of a svg file available in the resources
889
'MenuText': "Add PCB Standoff" ,
890
'ToolTip' : "Add PCB Metric Standoff"}
893
FastenerBase.FSGenerateObjects(FSPcbStandOffObject, "PcbStandoff")
897
return Gui.ActiveDocument is not None
939
899
Gui.addCommand("FSPcbStandOff", FSPcbStandOffCommand())
940
900
FastenerBase.FSCommands.append("FSPcbStandOff", "screws", "PEM Inserts")
942
902
###################################################################################
943
903
# PCB Spacers / Wurth standard WA-SSTII
945
'M2.5x5': ('5', '10', '11', '12', '15', '17', '18', '20', '22'),
946
'M3x5': ('5', '10', '15', '20'),
948
'5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '25',
949
'27', '30', '35', '40'),
950
'M4x7': ('5', '8', '10', '12', '15', '20', '25', '30', '35', '40', '45', '50', '60', '70', '80'),
951
'M5x8': ('10', '12', '15', '20', '25', '30', '35', '40', '50'),
952
'M6x10': ('10', '15', '20', '25', '30', '35', '40', '45', '50', '60')
905
'M2.5x5' : ('5', '10', '11', '12', '15', '17', '18', '20', '22'),
906
'M3x5' : ('5', '10', '15', '20'),
907
'M3x5.5' : ('5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '25', '27', '30', '35', '40'),
908
'M4x7' : ('5', '8', '10', '12', '15', '20', '25', '30', '35', '40', '45', '50', '60', '70', '80'),
909
'M5x8' : ('10', '12', '15', '20', '25', '30', '35', '40', '50'),
910
'M6x10' : ('10', '15', '20', '25', '30', '35', '40', '45', '50', '60')
954
PSPDiameters = ['Auto', 'M2.5', 'M3', 'M4', 'M5', 'M6']
912
PSPDiameters = ['Auto', 'M2.5', 'M3', 'M4', 'M5', 'M6' ]
957
'M2.5': (18, 2.05, ('5',)),
958
'M3': (20, 2.5, ('5', '5.5')),
959
'M4': (20, 3.3, ('7',)),
960
'M5': (20, 4.2, ('8',)),
961
'M6': (20, 5, ('10',))
915
'M2.5': (18, 2.05, ('5',)),
916
'M3': (20, 2.5, ('5', '5.5')),
917
'M4': (20, 3.3, ('7',)),
918
'M5': (20, 4.2, ('8',)),
919
'M6': (20, 5, ('10',))
965
923
def pspMakeFace(m, sw, l, id, th):
968
sw2 = float(sw) / 2.0
970
d2 = 0.95 * sw2 / cos30
971
l1 = l - (d2 - sw2) / 2.0
974
if p + 0.5 > l / 2.0:
978
fm = FastenerBase.FSFaceMaker()
979
fm.AddPoint(id2, l - dd)
980
fm.AddPoint(id2 + dd, l)
985
fm.AddPoint(id2 + dd, 0)
991
fm.AddPoint(0, l - p)
992
fm.AddPoint(id2, l - p1)
926
sw2 = float(sw) / 2.0
928
d2 = 0.95 * sw2 / cos30
929
l1 = l - (d2 - sw2) / 2.0
932
if p + 0.5 > l / 2.0:
936
fm = FastenerBase.FSFaceMaker()
937
fm.AddPoint(id2, l - dd)
938
fm.AddPoint(id2 + dd, l)
943
fm.AddPoint(id2 + dd, 0)
949
fm.AddPoint(0, l - p)
950
fm.AddPoint(id2, l - p1)
996
953
def pspMakeSpacer(diam, len, width):
997
FreeCAD.Console.PrintLog("Making PCB spacer" + diam + "x" + len + "x" + width + "\n")
998
if not (diam in PSPMTable):
1000
lenKey = diam + "x" + width
1001
if not (lenKey in PSPLengths):
1004
(key, shape) = FastenerBase.FSGetKey('PcbSpacer', diam, width, len)
1005
if shape is not None:
1008
th, id, sw = PSPMTable[diam]
1010
m = FastenerBase.MToFloat(diam)
1011
f = pspMakeFace(m, width, len, id, th)
1012
p = f.revolve(Base.Vector(0.0, 0.0, 0.0), Base.Vector(0.0, 0.0, 1.0), 360)
1015
htool = screwMaker.makeHextool(w, l, w * 2)
1016
htool.translate(Base.Vector(0.0, 0.0, - 0.1))
1017
shape = p.cut(htool)
1018
FastenerBase.FSCache[key] = shape
954
FreeCAD.Console.PrintLog("Making PCB spacer" + diam + "x" + len + "x" + width + "\n")
955
if not(diam in PSPMTable):
957
lenKey = diam + "x" + width
958
if not(lenKey in PSPLengths):
961
(key, shape) = FastenerBase.FSGetKey('PcbSpacer', diam, width, len)
962
if shape is not None:
965
th, id, sw = PSPMTable[diam]
967
m = FastenerBase.MToFloat(diam)
968
f = pspMakeFace(m, width, len, id, th)
969
p = f.revolve(Base.Vector(0.0,0.0,0.0),Base.Vector(0.0,0.0,1.0),360)
972
htool = screwMaker.makeHextool(w, l, w * 2)
973
htool.translate(Base.Vector(0.0,0.0,- 0.1))
975
FastenerBase.FSCache[key] = shape
1022
978
# h = clMakePressNut('M5','1')
1024
980
class FSPcbSpacerObject(FSBaseObject):
1025
def __init__(self, obj, attachTo):
1026
'''"Add PCB Spacer type fastener" '''
1027
FSBaseObject.__init__(self, obj, attachTo)
1028
self.itemText = "PcbSpacer"
1029
# self.Proxy = obj.Name
1031
obj.addProperty("App::PropertyEnumeration", "diameter", "Parameters",
1032
"Standoff thread diameter").diameter = PSDiameters
1033
widths = psGetAllWidths(PSPMTable, PSDiameters[1])
1034
obj.addProperty("App::PropertyEnumeration", "width", "Parameters", "Standoff body width").width = widths
1035
obj.addProperty("App::PropertyEnumeration", "length", "Parameters", "Standoff length").length = psGetAllLengths(
1036
PSPMTable, PSPLengths, PSDiameters[1], widths[0])
1037
obj.invert = FastenerBase.FSLastInvert
1040
def execute(self, fp):
1041
'''"Print a short message when doing a recomputation, this method is mandatory" '''
1043
baseobj = fp.baseObject[0]
1044
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
1048
self.updateProps(fp)
1049
if not hasattr(self, 'diameter') or self.diameter != fp.diameter or self.width != fp.width or self.length != fp.length:
1050
diameterchange = False
1051
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
1052
diameterchange = True
1053
if fp.diameter == 'Auto':
1054
d = FastenerBase.FSAutoDiameterM(shape, PSMTable, 1)
1055
diameterchange = True
1059
if d != fp.diameter:
1060
diameterchange = True
1064
if diameterchange or not (hasattr(self, 'width')) or self.width != fp.width:
1067
allwidth = psGetAllWidths(PSPMTable, d)
1069
if len(allwidth) > 1:
1070
fp.width = allwidth[1]
1072
fp.width = allwidth[0]
1074
l = psFindClosest(PSPMTable, PSPLengths, d, fp.width, fp.length)
1076
if l != fp.length or diameterchange or widthchange:
1077
if diameterchange or widthchange:
1078
fp.length = psGetAllLengths(PSPMTable, PSPLengths, fp.diameter, fp.width)
1081
s = pspMakeSpacer(d, l, fp.width)
1083
self.diameter = fp.diameter
1084
self.length = fp.length
1085
self.width = fp.width
1086
FastenerBase.FSLastInvert = fp.invert
1087
fp.Label = fp.diameter + 'x' + fp.width + 'x' + fp.length + '-Spacer'
1090
FreeCAD.Console.PrintLog("Using cached object\n")
1091
if shape is not None:
1092
# feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
1093
# fp.Placement = FreeCAD.Placement() # reset placement
1094
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
1097
FastenerBase.FSClassIcons[FSPcbSpacerObject] = 'PCBSpacer.svg'
981
def __init__(self, obj, attachTo):
982
'''"Add PCB Spacer type fastener" '''
983
FSBaseObject.__init__(self, obj, attachTo)
984
self.itemText = "PcbSpacer"
985
#self.Proxy = obj.Name
987
obj.addProperty("App::PropertyEnumeration","diameter","Parameters","Standoff thread diameter").diameter = PSDiameters
988
widths = psGetAllWidths(PSPMTable ,PSDiameters[1])
989
obj.addProperty("App::PropertyEnumeration", "width", "Parameters", "Standoff body width").width = widths
990
obj.addProperty("App::PropertyEnumeration","length","Parameters","Standoff length").length = psGetAllLengths(PSPMTable, PSPLengths ,PSDiameters[1], widths[0])
991
obj.invert = FastenerBase.FSLastInvert
994
def execute(self, fp):
995
'''"Print a short message when doing a recomputation, this method is mandatory" '''
997
baseobj = fp.baseObject[0]
998
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
1002
self.updateProps(fp)
1003
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter or self.width != fp.width or self.length != fp.length:
1004
diameterchange = False
1005
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
1006
diameterchange = True
1007
if fp.diameter == 'Auto':
1008
d = FastenerBase.FSAutoDiameterM(shape, PSMTable, 1)
1009
diameterchange = True
1013
if d != fp.diameter:
1014
diameterchange = True
1018
if diameterchange or not(hasattr(self, 'width')) or self.width != fp.width:
1021
allwidth = psGetAllWidths(PSPMTable, d)
1023
if len(allwidth) > 1:
1024
fp.width = allwidth[1]
1026
fp.width = allwidth[0]
1028
l = psFindClosest(PSPMTable, PSPLengths ,d, fp.width, fp.length)
1030
if l != fp.length or diameterchange or widthchange:
1031
if diameterchange or widthchange:
1032
fp.length = psGetAllLengths(PSPMTable, PSPLengths, fp.diameter, fp.width)
1035
s = pspMakeSpacer(d, l, fp.width)
1037
self.diameter = fp.diameter
1038
self.length = fp.length
1039
self.width = fp.width
1040
FastenerBase.FSLastInvert = fp.invert
1041
fp.Label = fp.diameter + 'x' + fp.width + 'x' + fp.length + '-Spacer'
1044
FreeCAD.Console.PrintLog("Using cached object\n")
1045
if shape is not None:
1046
#feature = FreeCAD.ActiveDocument.getObject(self.Proxy)
1047
#fp.Placement = FreeCAD.Placement() # reset placement
1048
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
1051
FastenerBase.FSClassIcons[FSPcbSpacerObject] = 'PCBSpacer.svg'
1100
1053
class FSPcbSpacerCommand:
1101
"""Add PCB Spacer command"""
1103
def GetResources(self):
1104
icon = os.path.join(iconPath, 'PCBSpacer.svg')
1106
'Pixmap': icon, # the name of a svg file available in the resources
1107
'MenuText': "Add PCB Spacer",
1108
'ToolTip': "Add PCB Metric Spacer"
1111
def Activated(self):
1112
FastenerBase.FSGenerateObjects(FSPcbSpacerObject, "PcbSpacer")
1116
return Gui.ActiveDocument is not None
1054
"""Add PCB Spacer command"""
1056
def GetResources(self):
1057
icon = os.path.join( iconPath , 'PCBSpacer.svg')
1058
return {'Pixmap' : icon , # the name of a svg file available in the resources
1059
'MenuText': "Add PCB Spacer" ,
1060
'ToolTip' : "Add PCB Metric Spacer"}
1062
def Activated(self):
1063
FastenerBase.FSGenerateObjects(FSPcbSpacerObject, "PcbSpacer")
1067
return Gui.ActiveDocument is not None
1119
1069
Gui.addCommand("FSPcbSpacer", FSPcbSpacerCommand())
1120
1070
FastenerBase.FSCommands.append("FSPcbSpacer", "screws", "PEM Inserts")
1122
1072
###################################################################################
1123
1073
# Heat Staked Threaded Insert types: IUT
1125
IUTDiamCodes = ['Auto', 'M2', 'M2.5', 'M3', 'M3.5', 'M4', 'M5', 'M6']
1075
IUTDiamCodes = ['Auto','M2', 'M2.5', 'M3', 'M3.5', 'M4', 'M5', 'M6']
1127
1077
IUTPEMTable = {
1128
# D, A, E, C, S1, S2
1129
'M2': (3.23, 4.00, 3.73, 3.07, 0.79, 0.79),
1130
'M2.5': (4.00, 5.74, 4.55, 3.86, 0.79, 0.79),
1131
'M3': (4.00, 5.74, 4.55, 3.86, 0.79, 0.79),
1132
'M3.5': (4.80, 7.14, 5.33, 4.65, 0.79, 0.79),
1133
'M4': (5.67, 8.15, 6.17, 5.51, 0.79, 0.79),
1134
'M5': (6.43, 9.52, 6.93, 6.27, 1.17, 1.17),
1135
'M6': (8.00, 12.7, 8.69, 7.87, 1.17, 1.58),
1078
# D, A, E, C, S1, S2
1079
'M2': (3.23, 4.00, 3.73, 3.07, 0.79, 0.79),
1080
'M2.5':(4.00, 5.74, 4.55, 3.86, 0.79, 0.79),
1081
'M3': (4.00, 5.74, 4.55, 3.86, 0.79, 0.79),
1082
'M3.5':(4.80, 7.14, 5.33, 4.65, 0.79, 0.79),
1083
'M4': (5.67, 8.15, 6.17, 5.51, 0.79, 0.79),
1084
'M5': (6.43, 9.52, 6.93, 6.27, 1.17, 1.17),
1085
'M6': (8.00, 12.7, 8.69, 7.87, 1.17, 1.58),
1139
1088
def iutMakeWire(D, a, E, C, s1, s2):
1144
k1 = (a - ch - s1 - s2) / 2
1148
fm = FastenerBase.FSFaceMaker()
1152
fm.AddPoint(e - sd, -k1)
1153
fm.AddPoint(e - sd, -k1 - s1)
1154
fm.AddPoint(e, -k1 - s1)
1156
fm.AddPoint(e - sd, -k2)
1157
fm.AddPoint(e - sd, -k2 - s2)
1158
fm.AddPoint(c, -k2 - s2)
1093
k1 = (a - ch - s1 - s2)/2
1097
fm = FastenerBase.FSFaceMaker()
1101
fm.AddPoint(e - sd, -k1)
1102
fm.AddPoint(e - sd, -k1 - s1)
1103
fm.AddPoint(e, -k1 - s1)
1105
fm.AddPoint(e - sd, -k2)
1106
fm.AddPoint(e - sd, -k2 - s2)
1107
fm.AddPoint(c, -k2 - s2)
1164
1112
def iutMakeHeatSet(diam):
1165
if not (diam in IUTPEMTable):
1168
(key, shape) = FastenerBase.FSGetKey('HeatSet', diam)
1169
if shape is not None:
1172
D, A, E, C, s1, s2 = IUTPEMTable[diam]
1173
D = FastenerBase.MToFloat(diam)
1174
f = iutMakeWire(D, A, E, C, s1, s2)
1175
p = f.revolve(Base.Vector(0.0, 0.0, 0.0), Base.Vector(0.0, 0.0, 1.0), 360)
1176
FastenerBase.FSCache[key] = p
1113
if not(diam in IUTPEMTable):
1116
(key, shape) = FastenerBase.FSGetKey('HeatSet', diam)
1117
if shape is not None:
1120
D, A, E, C, s1, s2 = IUTPEMTable[diam]
1121
D = FastenerBase.MToFloat(diam)
1122
f = iutMakeWire(D, A, E, C, s1, s2)
1123
p = f.revolve(Base.Vector(0.0,0.0,0.0),Base.Vector(0.0,0.0,1.0),360)
1124
FastenerBase.FSCache[key] = p
1180
1127
def iutFindClosest(diam):
1181
''' Find closest standard screw to given parameters '''
1182
i = IUTDiamCodes.index(diam)
1183
lens = IUTPEMTable[diam][0]
1185
max = len(IUTDiamCodes)
1188
if c != 0 and min == 999:
1190
if c == 0 and min != 999:
1195
return IUTDiamCodes[min]
1196
return IUTDiamCodes[max]
1128
''' Find closest standard screw to given parameters '''
1129
i = IUTDiamCodes.index(diam)
1130
lens = IUTPEMTable[diam][0]
1132
max = len(IUTDiamCodes)
1135
if c != 0 and min == 999:
1137
if c == 0 and min != 999:
1142
return IUTDiamCodes[min]
1143
return IUTDiamCodes[max]
1199
1145
class FSHeatSetObject(FSBaseObject):
1200
def __init__(self, obj, attachTo):
1201
'''"Add IUT[A/B/C] Heat Set Insert fastener" '''
1202
FSBaseObject.__init__(self, obj, attachTo)
1203
self.itemText = "HeatSet"
1205
obj.addProperty("App::PropertyEnumeration", "diameter", "Parameters",
1206
"Heat set thread diameter").diameter = IUTDiamCodes
1207
obj.invert = FastenerBase.FSLastInvert
1210
def execute(self, fp):
1211
'''"Print a short message when doing a recomputation, this method is mandatory" '''
1214
baseobj = fp.baseObject[0]
1215
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
1219
self.updateProps(fp)
1220
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
1221
if fp.diameter == 'Auto':
1222
d = FastenerBase.FSAutoDiameterM(shape, IUTPEMTable, 0)
1226
s = iutMakeHeatSet(d)
1227
self.diameter = fp.diameter
1228
FastenerBase.FSLastInvert = fp.invert
1229
fp.Label = fp.diameter + '-HeatSet'
1232
FreeCAD.Console.PrintLog("Using cached object\n")
1233
if shape is not None:
1234
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
1237
FastenerBase.FSClassIcons[FSHeatSetObject] = 'IUTHeatInsert.svg'
1146
def __init__(self, obj, attachTo):
1147
'''"Add IUT[A/B/C] Heat Set Insert fastener" '''
1148
FSBaseObject.__init__(self, obj, attachTo)
1149
self.itemText = "HeatSet"
1151
obj.addProperty("App::PropertyEnumeration","diameter","Parameters","Heat set thread diameter").diameter = IUTDiamCodes
1152
obj.invert = FastenerBase.FSLastInvert
1155
def execute(self, fp):
1156
'''"Print a short message when doing a recomputation, this method is mandatory" '''
1159
baseobj = fp.baseObject[0]
1160
shape = baseobj.Shape.getElement(fp.baseObject[1][0])
1164
self.updateProps(fp)
1165
if not (hasattr(self, 'diameter')) or self.diameter != fp.diameter:
1166
if fp.diameter == 'Auto':
1167
d = FastenerBase.FSAutoDiameterM(shape, IUTPEMTable, 0)
1171
s = iutMakeHeatSet(d)
1172
self.diameter = fp.diameter
1173
FastenerBase.FSLastInvert = fp.invert
1174
fp.Label = fp.diameter + '-HeatSet'
1177
FreeCAD.Console.PrintLog("Using cached object\n")
1178
if shape is not None:
1179
FastenerBase.FSMoveToObject(fp, shape, fp.invert, fp.offset.Value)
1182
FastenerBase.FSClassIcons[FSHeatSetObject] = 'IUTHeatInsert.svg'
1240
1184
class FSHeatSetCommand:
1241
"""Add Heat Set Insert command"""
1243
def GetResources(self):
1244
icon = os.path.join(iconPath, 'IUTHeatInsert.svg')
1246
'Pixmap': icon, # the name of a svg file available in the resources
1247
'MenuText': "Add Heatset Insert",
1248
'ToolTip': "Add IUT[A/B/C] Heat Staked Metric Insert"
1251
def Activated(self):
1252
FastenerBase.FSGenerateObjects(FSHeatSetObject, "HeatSet")
1256
return Gui.ActiveDocument is not None
1185
"""Add Heat Set Insert command"""
1187
def GetResources(self):
1188
icon = os.path.join( iconPath , 'IUTHeatInsert.svg')
1189
return {'Pixmap' : icon , # the name of a svg file available in the resources
1190
'MenuText': "Add Heatset Insert" ,
1191
'ToolTip' : "Add IUT[A/B/C] Heat Staked Metric Insert"}
1193
def Activated(self):
1194
FastenerBase.FSGenerateObjects(FSHeatSetObject, "HeatSet")
1198
return Gui.ActiveDocument is not None
1259
1200
Gui.addCommand("FSHeatSet", FSHeatSetCommand())
1260
1201
FastenerBase.FSCommands.append("FSHeatSet", "screws", "PEM Inserts")