1
#***************************************************************************
2
#* Copyright (c) 2011 Yorik van Havre <yorik@uncreated.net> *
4
#* This program is free software; you can redistribute it and/or modify *
5
#* it under the terms of the GNU Lesser General Public License (LGPL) *
6
#* as published by the Free Software Foundation; either version 2 of *
7
#* the License, or (at your option) any later version. *
8
#* for detail see the LICENCE text file. *
10
#* This program is distributed in the hope that it will be useful, *
11
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
#* GNU Library General Public License for more details. *
15
#* You should have received a copy of the GNU Library General Public *
16
#* License along with this program; if not, write to the Free Software *
17
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
20
#***************************************************************************
21
#Modified 2016-01-03 JAndersM
23
import FreeCAD,Draft,ArchComponent,DraftVecUtils,ArchCommands
24
from FreeCAD import Vector
26
from draftutils import params
27
from draftutils import gui_utils
31
from PySide import QtCore, QtGui
32
from draftutils.translate import translate
33
from PySide.QtCore import QT_TRANSLATE_NOOP
35
import draftguitools.gui_trackers as DraftTrackers
38
def translate(ctxt,txt):
40
def QT_TRANSLATE_NOOP(ctxt,txt):
44
## @package ArchStructure
46
# \brief The Structure object and tools
48
# This module provides tools to build Structure objects.
49
# Structure elements are beams, columns, slabs, and other
50
# elements that have a structural function, that is, that
51
# support other parts of the building.
53
__title__= "FreeCAD Structure"
54
__author__ = "Yorik van Havre"
55
__url__ = "https://www.freecad.org"
58
#Reads preset profiles and categorizes them
60
Presets=ArchProfile.readPresets()
62
if pre[1] not in Categories:
63
Categories.append(pre[1])
66
def makeStructure(baseobj=None,length=None,width=None,height=None,name=None):
68
'''makeStructure([baseobj],[length],[width],[height],[name]): creates a
69
structure element based on the given profile object and the given
70
extrusion height. If no base object is given, you can also specify
71
length and width for a cubic object.'''
73
if not FreeCAD.ActiveDocument:
74
FreeCAD.Console.PrintError("No active document. Aborting\n")
76
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Structure")
79
_ViewProviderStructure(obj.ViewObject)
83
obj.Base.ViewObject.hide()
87
obj.Width = params.get_param_arch("StructureWidth")
92
obj.Height = params.get_param_arch("StructureHeight")
97
# don't set the length if we have a base object, otherwise the length X height calc
99
obj.Length = params.get_param_arch("StructureLength")
103
if hasattr(baseobj,"Width") and hasattr(baseobj,"Height"):
104
w = baseobj.Width.Value
105
h = baseobj.Height.Value
106
elif hasattr(baseobj,"Length") and hasattr(baseobj,"Width"):
107
w = baseobj.Length.Value
108
h = baseobj.Width.Value
109
elif hasattr(baseobj,"Length") and hasattr(baseobj,"Height"):
110
w = baseobj.Length.Value
111
h = baseobj.Height.Value
113
if length and not height:
116
elif height and not length:
120
if not height and not length:
121
obj.IfcType = "Building Element Proxy"
122
obj.Label = name if name else translate("Arch","Structure")
123
elif obj.Length > obj.Height:
125
obj.Label = name if name else translate("Arch","Beam")
126
elif obj.Height > obj.Length:
127
obj.IfcType = "Column"
128
obj.Label = name if name else translate("Arch","Column")
131
def makeStructuralSystem(objects=[],axes=[],name=None):
133
'''makeStructuralSystem([objects],[axes],[name]): makes a structural system
134
based on the given objects and axes'''
136
if not FreeCAD.ActiveDocument:
137
FreeCAD.Console.PrintError("No active document. Aborting\n")
141
print("At least one axis must be given")
144
if not isinstance(objects,list):
149
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","StructuralSystem")
150
obj.Label = name if name else translate("Arch","StructuralSystem")
151
_StructuralSystem(obj)
153
_ViewProviderStructuralSystem(obj.ViewObject)
158
if FreeCAD.GuiUp and o:
160
Draft.formatObject(obj,o)
161
FreeCAD.ActiveDocument.recompute()
167
def placeAlongEdge(p1,p2,horizontal=False):
169
"""placeAlongEdge(p1,p2,[horizontal]): returns a Placement positioned at p1, with Z axis oriented towards p2.
170
If horizontal is True, then the X axis is oriented towards p2, not the Z axis"""
172
pl = FreeCAD.Placement()
175
up = WorkingPlane.get_working_plane(update=False).axis
177
yaxis = up.cross(zaxis)
179
xaxis = zaxis.cross(yaxis)
181
pl.Rotation = FreeCAD.Rotation(zaxis,yaxis,xaxis,"ZXY")
183
pl.Rotation = FreeCAD.Rotation(xaxis,yaxis,zaxis,"ZXY")
184
pl.Rotation = FreeCAD.Rotation(pl.Rotation.multVec(FreeCAD.Vector(0,0,1)),90).multiply(pl.Rotation)
188
class CommandStructuresFromSelection:
189
""" The Arch Structures from selection command definition. """
194
def GetResources(self):
195
return {'Pixmap': 'Arch_MultipleStructures',
196
'MenuText': QT_TRANSLATE_NOOP("Arch_StructuresFromSelection", "Multiple Structures"),
197
'ToolTip': QT_TRANSLATE_NOOP("Arch_StructuresFromSelection", "Create multiple BIM Structures from a selected base, using each selected edge as an extrusion path")}
200
v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph")
204
selex = FreeCADGui.Selection.getSelectionEx()
206
FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Structures From Selection"))
207
FreeCADGui.addModule("Arch")
208
FreeCADGui.addModule("Draft")
209
base = selex[0].Object # The first selected object is the base for the Structure objects
210
for selexi in selex[1:]: # All the edges from the other objects are used as a Tool (extrusion paths)
211
if len(selexi.SubElementNames) == 0:
212
subelement_names = ["Edge" + str(i) for i in range(1, len(selexi.Object.Shape.Edges) + 1)]
214
subelement_names = [sub for sub in selexi.SubElementNames if sub.startswith("Edge")]
215
for sub in subelement_names:
216
FreeCADGui.doCommand("structure = Arch.makeStructure(FreeCAD.ActiveDocument." + base.Name + ")")
217
FreeCADGui.doCommand("structure.Tool = (FreeCAD.ActiveDocument." + selexi.Object.Name + ", '" + sub + "')")
218
FreeCADGui.doCommand("structure.BasePerpendicularToTool = True")
219
FreeCADGui.doCommand("Draft.autogroup(structure)")
220
FreeCAD.ActiveDocument.commitTransaction()
221
FreeCAD.ActiveDocument.recompute()
223
FreeCAD.Console.PrintError(translate("Arch", "Please select the base object first and then the edges to use as extrusion paths") + "\n")
226
class CommandStructuralSystem:
227
""" The Arch Structural System command definition. """
232
def GetResources(self):
233
return {'Pixmap': 'Arch_StructuralSystem',
234
'MenuText': QT_TRANSLATE_NOOP("Arch_StructuralSystem", "Structural System"),
235
'ToolTip': QT_TRANSLATE_NOOP("Arch_StructuralSystem", "Create a structural system from a selected structure and axis")}
238
return not FreeCAD.ActiveDocument is None
241
sel = FreeCADGui.Selection.getSelection()
243
st = Draft.getObjectsOfType(sel, "Structure")
244
ax = Draft.getObjectsOfType(sel, "Axis")
246
FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Structural System"))
247
FreeCADGui.addModule("Arch")
249
FreeCADGui.doCommand("obj = Arch.makeStructuralSystem(" + ArchCommands.getStringList(st) + ", " + ArchCommands.getStringList(ax) + ")")
251
FreeCADGui.doCommand("obj = Arch.makeStructuralSystem(axes = " + ArchCommands.getStringList(ax) + ")")
252
FreeCADGui.addModule("Draft")
253
FreeCADGui.doCommand("Draft.autogroup(obj)")
254
FreeCAD.ActiveDocument.commitTransaction()
255
FreeCAD.ActiveDocument.recompute()
257
FreeCAD.Console.PrintError(translate("Arch", "Please select at least an axis object") + "\n")
260
class _CommandStructure:
262
"the Arch Structure command definition"
266
self.beammode = False
268
def GetResources(self):
270
return {'Pixmap' : 'Arch_Structure',
271
'MenuText': QT_TRANSLATE_NOOP("Arch_Structure","Structure"),
273
'ToolTip': QT_TRANSLATE_NOOP("Arch_Structure","Creates a structure from scratch or from a selected object (sketch, wire, face or solid)")}
277
return not FreeCAD.ActiveDocument is None
281
self.Width = params.get_param_arch("StructureWidth")
283
self.Height = params.get_param_arch("StructureLength")
284
self.Length = params.get_param_arch("StructureHeight")
286
self.Length = params.get_param_arch("StructureLength")
287
self.Height = params.get_param_arch("StructureHeight")
289
self.continueCmd = False
292
self.precastvalues = None
294
sel = FreeCADGui.Selection.getSelection()
296
st = Draft.getObjectsOfType(sel,"Structure")
297
ax = Draft.getObjectsOfType(sel,"Axis")
299
FreeCADGui.runCommand("Arch_StructuralSystem")
301
elif not(ax) and not(st):
302
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Structure"))
303
FreeCADGui.addModule("Arch")
305
FreeCADGui.doCommand("obj = Arch.makeStructure(FreeCAD.ActiveDocument." + obj.Name + ")")
306
FreeCADGui.addModule("Draft")
307
FreeCADGui.doCommand("Draft.autogroup(obj)")
308
FreeCAD.ActiveDocument.commitTransaction()
309
FreeCAD.ActiveDocument.recompute()
314
self.wp = WorkingPlane.get_working_plane()
317
self.tracker = DraftTrackers.boxTracker()
318
self.tracker.width(self.Width)
319
self.tracker.height(self.Height)
320
self.tracker.length(self.Length)
321
self.tracker.setRotation(self.wp.get_placement().Rotation)
323
self.precast = ArchPrecast._PrecastTaskPanel()
324
self.dents = ArchPrecast._DentsTaskPanel()
325
self.precast.Dents = self.dents
327
title=translate("Arch","First point of the beam")+":"
329
title=translate("Arch","Base point of column")+":"
330
FreeCAD.activeDraftCommand = self # register as a Draft command for auto grid on/off
331
FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=[self.taskbox(),self.precast.form,self.dents.form],title=title)
333
def getPoint(self,point=None,obj=None):
335
"this function is called by the snapper when it has a 3D point"
337
self.bmode = self.modeb.isChecked()
339
self.tracker.finalize()
340
FreeCAD.activeDraftCommand = None
341
FreeCADGui.Snapper.off()
343
if self.bmode and (self.bpoint is None):
345
FreeCADGui.Snapper.getPoint(last=point,callback=self.getPoint,movecallback=self.update,extradlg=[self.taskbox(),self.precast.form,self.dents.form],title=translate("Arch","Next point")+":",mode="line")
348
FreeCAD.activeDraftCommand = None
349
FreeCADGui.Snapper.off()
350
horiz = True # determines the type of rotation to apply to the final object
351
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Structure"))
352
FreeCADGui.addModule("Arch")
353
FreeCADGui.addModule("WorkingPlane")
355
self.Length = point.sub(self.bpoint).Length
356
params.set_param_arch("StructureHeight",self.Length)
357
if self.Profile is not None:
358
try: # try to update latest precast values - fails if dialog has been destroyed already
359
self.precastvalues = self.precast.getValues()
362
if ("Precast" in self.Profile) and self.precastvalues:
364
self.precastvalues["PrecastType"] = self.Profile.split("_")[1]
365
self.precastvalues["Length"] = self.Length
366
self.precastvalues["Width"] = self.Width
367
self.precastvalues["Height"] = self.Height
369
# fix for precast placement, since their (0,0) point is the lower left corner
371
delta = FreeCAD.Vector(0,0-self.Width/2,0)
373
delta = FreeCAD.Vector(-self.Length/2,-self.Width/2,0)
374
delta = self.wp.get_global_coords(delta,as_vector=True)
375
point = point.add(delta)
377
self.bpoint = self.bpoint.add(delta)
378
# build the string definition
379
for pair in self.precastvalues.items():
380
argstring += pair[0].lower() + "="
381
if isinstance(pair[1],str):
382
argstring += '"' + pair[1] + '",'
384
argstring += str(pair[1]) + ","
385
FreeCADGui.addModule("ArchPrecast")
386
FreeCADGui.doCommand("s = ArchPrecast.makePrecast("+argstring+")")
389
FreeCADGui.doCommand('p = Arch.makeProfile('+str(self.Profile)+')')
390
if (abs(self.Length - self.Profile[4]) >= 0.1) or self.bmode: # forgive rounding errors
392
FreeCADGui.doCommand('s = Arch.makeStructure(p,length='+str(self.Length)+')')
396
FreeCADGui.doCommand('s = Arch.makeStructure(p,height='+str(self.Height)+')')
398
# FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)')
399
FreeCADGui.doCommand('s.Profile = "'+self.Profile[2]+'"')
401
FreeCADGui.doCommand('s = Arch.makeStructure(length='+str(self.Length)+',width='+str(self.Width)+',height='+str(self.Height)+')')
404
if self.bmode and self.bpoint:
405
FreeCADGui.doCommand('s.Placement = Arch.placeAlongEdge('+DraftVecUtils.toString(self.bpoint)+","+DraftVecUtils.toString(point)+","+str(horiz)+")")
407
FreeCADGui.doCommand('s.Placement.Base = '+DraftVecUtils.toString(point))
408
FreeCADGui.doCommand('wp = WorkingPlane.get_working_plane()')
409
FreeCADGui.doCommand('s.Placement.Rotation = s.Placement.Rotation.multiply(wp.get_placement().Rotation)')
411
FreeCADGui.addModule("Draft")
412
FreeCADGui.doCommand("Draft.autogroup(s)")
413
FreeCAD.ActiveDocument.commitTransaction()
414
FreeCAD.ActiveDocument.recompute()
415
gui_utils.end_all_events()
416
self.tracker.finalize()
420
def _createItemlist(self, baselist):
422
"create nice labels for presets in the task panel"
426
f = FreeCAD.Units.Quantity(p[4],FreeCAD.Units.Length).getUserPreferred()
427
d = params.get_param("Decimals",path="Units")
428
s1 = str(round(p[4]/f[1],d))
429
s2 = str(round(p[5]/f[1],d))
431
ilist.append(p[2]+" ("+s1+"x"+s2+s3+")")
436
"sets up a taskbox widget"
439
ui = FreeCADGui.UiLoader()
440
w.setWindowTitle(translate("Arch","Structure options"))
441
grid = QtGui.QGridLayout(w)
444
labelmode = QtGui.QLabel(translate("Arch","Parameters of the structure")+":")
445
self.modeb = QtGui.QRadioButton(translate("Arch","Beam"))
446
self.modec = QtGui.QRadioButton(translate("Arch","Column"))
447
if self.bpoint or self.beammode:
448
self.modeb.setChecked(True)
450
self.modec.setChecked(True)
451
grid.addWidget(labelmode,0,0,1,2)
452
grid.addWidget(self.modeb,1,0,1,1)
453
grid.addWidget(self.modec,1,1,1,1)
456
labelc = QtGui.QLabel(translate("Arch","Category"))
457
self.valuec = QtGui.QComboBox()
458
self.valuec.addItems([" ","Precast concrete"]+Categories)
459
grid.addWidget(labelc,2,0,1,1)
460
grid.addWidget(self.valuec,2,1,1,1)
463
labelp = QtGui.QLabel(translate("Arch","Preset"))
464
self.vPresets = QtGui.QComboBox()
465
self.pSelect = [None]
467
self.vPresets.addItems(fpresets)
468
grid.addWidget(labelp,3,0,1,1)
469
grid.addWidget(self.vPresets,3,1,1,1)
472
label1 = QtGui.QLabel(translate("Arch","Length"))
473
self.vLength = ui.createWidget("Gui::InputField")
474
if self.modeb.isChecked():
475
self.vLength.setText(FreeCAD.Units.Quantity(self.Height,FreeCAD.Units.Length).UserString)
477
self.vLength.setText(FreeCAD.Units.Quantity(self.Length,FreeCAD.Units.Length).UserString)
478
grid.addWidget(label1,4,0,1,1)
479
grid.addWidget(self.vLength,4,1,1,1)
482
label2 = QtGui.QLabel(translate("Arch","Width"))
483
self.vWidth = ui.createWidget("Gui::InputField")
484
self.vWidth.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString)
485
grid.addWidget(label2,5,0,1,1)
486
grid.addWidget(self.vWidth,5,1,1,1)
489
label3 = QtGui.QLabel(translate("Arch","Height"))
490
self.vHeight = ui.createWidget("Gui::InputField")
491
if self.modeb.isChecked():
492
self.vHeight.setText(FreeCAD.Units.Quantity(self.Length,FreeCAD.Units.Length).UserString)
494
self.vHeight.setText(FreeCAD.Units.Quantity(self.Height,FreeCAD.Units.Length).UserString)
495
grid.addWidget(label3,6,0,1,1)
496
grid.addWidget(self.vHeight,6,1,1,1)
499
value5 = QtGui.QPushButton(translate("Arch","Switch Length/Height"))
500
grid.addWidget(value5,7,0,1,1)
501
value6 = QtGui.QPushButton(translate("Arch","Switch Length/Width"))
502
grid.addWidget(value6,7,1,1,1)
505
label4 = QtGui.QLabel(translate("Arch","Con&tinue"))
506
value4 = QtGui.QCheckBox()
507
value4.setObjectName("ContinueCmd")
508
value4.setLayoutDirection(QtCore.Qt.RightToLeft)
509
label4.setBuddy(value4)
510
self.continueCmd = params.get_param("ContinueMode")
511
value4.setChecked(self.continueCmd)
512
grid.addWidget(label4,8,0,1,1)
513
grid.addWidget(value4,8,1,1,1)
516
QtCore.QObject.connect(self.valuec,QtCore.SIGNAL("currentIndexChanged(int)"),self.setCategory)
517
QtCore.QObject.connect(self.vPresets,QtCore.SIGNAL("currentIndexChanged(int)"),self.setPreset)
518
QtCore.QObject.connect(self.vLength,QtCore.SIGNAL("valueChanged(double)"),self.setLength)
519
QtCore.QObject.connect(self.vWidth,QtCore.SIGNAL("valueChanged(double)"),self.setWidth)
520
QtCore.QObject.connect(self.vHeight,QtCore.SIGNAL("valueChanged(double)"),self.setHeight)
521
QtCore.QObject.connect(value4,QtCore.SIGNAL("stateChanged(int)"),self.setContinue)
522
QtCore.QObject.connect(value5,QtCore.SIGNAL("pressed()"),self.rotateLH)
523
QtCore.QObject.connect(value6,QtCore.SIGNAL("pressed()"),self.rotateLW)
524
QtCore.QObject.connect(self.modeb,QtCore.SIGNAL("toggled(bool)"),self.switchLH)
527
stored = params.get_param_arch("StructurePreset")
529
if stored.lower().startswith("precast_"):
530
self.valuec.setCurrentIndex(1)
531
tp = stored.split("_")[1]
532
if tp and (tp in self.precast.PrecastTypes):
533
self.vPresets.setCurrentIndex(self.precast.PrecastTypes.index(tp))
535
stored = stored.split(";")
537
if stored[1] in Categories:
538
self.valuec.setCurrentIndex(2+Categories.index(stored[1]))
539
ps = [p[2] for p in self.pSelect]
541
self.vPresets.setCurrentIndex(ps.index(stored[2]))
544
def update(self,point,info):
546
"this function is called by the Snapper when the mouse is moved"
548
if FreeCADGui.Control.activeDialog():
549
try: # try to update latest precast values - fails if dialog has been destroyed already
550
self.precastvalues = self.precast.getValues()
553
if self.Height >= self.Length:
554
delta = Vector(0,0,self.Height/2)
556
delta = Vector(self.Length/2,0,0)
557
delta = self.wp.get_global_coords(delta,as_vector=True)
558
if self.modec.isChecked():
559
self.tracker.pos(point.add(delta))
563
delta = Vector(0,0,-self.Height/2)
564
delta = self.wp.get_global_coords(delta,as_vector=True)
565
self.tracker.update([self.bpoint.add(delta),point.add(delta)])
567
l = (point.sub(self.bpoint)).Length
568
self.vLength.setText(FreeCAD.Units.Quantity(l,FreeCAD.Units.Length).UserString)
572
def setWidth(self,d):
575
self.tracker.width(d)
576
params.set_param_arch("StructureWidth",d)
578
def setHeight(self,d):
581
self.tracker.height(d)
582
if self.modeb.isChecked():
583
params.set_param_arch("StructureLength",d)
585
params.set_param_arch("StructureHeight",d)
587
def setLength(self,d):
590
self.tracker.length(d)
591
if self.modeb.isChecked():
592
params.set_param_arch("StructureHeight",d)
594
params.set_param_arch("StructureLength",d)
596
def setContinue(self,i):
598
self.continueCmd = bool(i)
599
params.set_param("ContinueMode", bool(i))
601
def setCategory(self,i):
603
self.vPresets.clear()
605
self.precast.form.hide()
606
self.pSelect = [p for p in Presets if p[1] == Categories[i-2]]
607
fpresets = self._createItemlist(self.pSelect)
608
self.vPresets.addItems(fpresets)
611
self.precast.form.show()
612
self.pSelect = self.precast.PrecastTypes
613
fpresets = self.precast.PrecastTypes
614
self.vPresets.addItems(fpresets)
617
self.precast.form.hide()
618
self.pSelect = [None]
620
self.vPresets.addItems(fpresets)
621
params.set_param_arch("StructurePreset","")
623
def setPreset(self,i):
626
elt = self.pSelect[i]
628
if elt in self.precast.PrecastTypes:
629
self.precast.setPreset(elt)
630
self.Profile = "Precast_" + elt
631
if elt in ["Pillar","Beam"]:
632
self.dents.form.show()
634
self.dents.form.hide()
635
params.set_param_arch("StructurePreset",self.Profile)
637
p=elt[0]-1 # Presets indexes are 1-based
638
self.vLength.setText(FreeCAD.Units.Quantity(float(Presets[p][4]),FreeCAD.Units.Length).UserString)
639
self.vWidth.setText(FreeCAD.Units.Quantity(float(Presets[p][5]),FreeCAD.Units.Length).UserString)
640
self.Profile = Presets[p]
641
params.set_param_arch("StructurePreset",";".join([str(i) for i in self.Profile]))
643
def switchLH(self,bmode):
647
if self.Height > self.Length:
651
if self.Length > self.Height:
653
self.tracker.setRotation(FreeCAD.Rotation())
657
l = self.vLength.text()
658
h = self.vHeight.text()
659
self.vLength.setText(h)
660
self.vHeight.setText(l)
664
l = self.vLength.text()
665
w = self.vWidth.text()
666
self.vLength.setText(w)
667
self.vWidth.setText(l)
670
class _Structure(ArchComponent.Component):
672
"The Structure object"
674
def __init__(self,obj):
676
ArchComponent.Component.__init__(self,obj)
677
self.setProperties(obj)
680
def setProperties(self,obj):
682
pl = obj.PropertiesList
684
obj.addProperty("App::PropertyLinkSubList", "Tool", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "An optional extrusion path for this element"))
685
if not "ComputedLength" in pl:
686
obj.addProperty("App::PropertyDistance", "ComputedLength", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "The computed length of the extrusion path"), 1)
687
if not "ToolOffsetFirst" in pl:
688
obj.addProperty("App::PropertyDistance", "ToolOffsetFirst", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Start offset distance along the extrusion path (positive: extend, negative: trim)"))
689
if not "ToolOffsetLast" in pl:
690
obj.addProperty("App::PropertyDistance", "ToolOffsetLast", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "End offset distance along the extrusion path (positive: extend, negative: trim)"))
691
if not "BasePerpendicularToTool" in pl:
692
obj.addProperty("App::PropertyBool", "BasePerpendicularToTool", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Automatically align the Base of the Structure perpendicular to the Tool axis"))
693
if not "BaseOffsetX" in pl:
694
obj.addProperty("App::PropertyDistance", "BaseOffsetX", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "X offset between the Base origin and the Tool axis (only used if BasePerpendicularToTool is True)"))
695
if not "BaseOffsetY" in pl:
696
obj.addProperty("App::PropertyDistance", "BaseOffsetY", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Y offset between the Base origin and the Tool axis (only used if BasePerpendicularToTool is True)"))
697
if not "BaseMirror" in pl:
698
obj.addProperty("App::PropertyBool", "BaseMirror", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Mirror the Base along its Y axis (only used if BasePerpendicularToTool is True)"))
699
if not "BaseRotation" in pl:
700
obj.addProperty("App::PropertyAngle", "BaseRotation", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Base rotation around the Tool axis (only used if BasePerpendicularToTool is True)"))
701
if not "Length" in pl:
702
obj.addProperty("App::PropertyLength","Length","Structure",QT_TRANSLATE_NOOP("App::Property","The length of this element, if not based on a profile"))
703
if not "Width" in pl:
704
obj.addProperty("App::PropertyLength","Width","Structure",QT_TRANSLATE_NOOP("App::Property","The width of this element, if not based on a profile"))
705
if not "Height" in pl:
706
obj.addProperty("App::PropertyLength","Height","Structure",QT_TRANSLATE_NOOP("App::Property","The height or extrusion depth of this element. Keep 0 for automatic"))
707
if not "Normal" in pl:
708
obj.addProperty("App::PropertyVector","Normal","Structure",QT_TRANSLATE_NOOP("App::Property","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))
709
if not "Nodes" in pl:
710
obj.addProperty("App::PropertyVectorList","Nodes","Structure",QT_TRANSLATE_NOOP("App::Property","The structural nodes of this element"))
711
if not "Profile" in pl:
712
obj.addProperty("App::PropertyString","Profile","Structure",QT_TRANSLATE_NOOP("App::Property","A description of the standard profile this element is based upon"))
713
if not "NodesOffset" in pl:
714
obj.addProperty("App::PropertyDistance","NodesOffset","Structure",QT_TRANSLATE_NOOP("App::Property","Offset distance between the centerline and the nodes line"))
715
if not "FaceMaker" in pl:
716
obj.addProperty("App::PropertyEnumeration","FaceMaker","Structure",QT_TRANSLATE_NOOP("App::Property","The facemaker type to use to build the profile of this object"))
717
obj.FaceMaker = ["None","Simple","Cheese","Bullseye"]
718
if not "ArchSketchEdges" in pl: # PropertyStringList
719
obj.addProperty("App::PropertyStringList","ArchSketchEdges","Structure",QT_TRANSLATE_NOOP("App::Property","Selected edges (or group of edges) of the base ArchSketch, to use in creating the shape of this BIM Structure (instead of using all the Base shape's edges by default). Input are index numbers of edges or groups."))
721
# test if the property was added but as IntegerList, then update;
722
type = obj.getTypeIdOfProperty('ArchSketchEdges')
723
if type == "App::PropertyIntegerList":
724
oldIntValue = obj.ArchSketchEdges
725
newStrValue = [str(x) for x in oldIntValue]
726
obj.removeProperty("ArchSketchEdges")
727
obj.addProperty("App::PropertyStringList","ArchSketchEdges","Structure",QT_TRANSLATE_NOOP("App::Property","Selected edges (or group of edges) of the base ArchSketch, to use in creating the shape of this BIM Structure (instead of using all the Base shape's edges by default). Input are index numbers of edges or groups."))
728
obj.ArchSketchEdges = newStrValue
729
self.Type = "Structure"
731
def onDocumentRestored(self,obj):
733
ArchComponent.Component.onDocumentRestored(self,obj)
734
self.setProperties(obj)
736
def execute(self,obj):
738
"creates the structure shape"
740
import Part, DraftGeomUtils
747
extdata = self.getExtrusionData(obj)
751
if not isinstance(sh,list):
754
if not isinstance(ev,list):
757
if not isinstance(pla,list):
760
extrusion_length = 0.0
761
for i in range(len(sh)):
767
if isinstance(evi, FreeCAD.Vector):
768
evi = FreeCAD.Vector(evi)
775
shi.Placement = pli.multiply(shi.Placement)
776
if isinstance(evi, FreeCAD.Vector):
777
extv = pla[0].Rotation.multVec(evi)
778
shi = shi.extrude(extv)
781
shi = evi.makePipe(shi)
782
except Part.OCCError:
783
FreeCAD.Console.PrintError(translate("Arch","Error: The base shape couldn't be extruded along this tool object")+"\n")
786
extrusion_length += evi.Length
790
base = Part.makeCompound(base)
791
obj.ComputedLength = FreeCAD.Units.Quantity(extrusion_length, FreeCAD.Units.Length)
793
if hasattr(obj.Base,'Shape'):
794
if obj.Base.Shape.isNull():
796
if not obj.Base.Shape.isValid():
797
if not obj.Base.Shape.Solids:
798
# let pass invalid objects if they have solids...
800
elif obj.Base.Shape.Solids:
801
base = obj.Base.Shape.copy()
802
elif obj.Base.isDerivedFrom("Mesh::Feature"):
803
if obj.Base.Mesh.isSolid():
804
if obj.Base.Mesh.countComponents() == 1:
805
sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh)
806
if sh.isClosed() and sh.isValid() and sh.Solids and (not sh.isNull()):
809
FreeCAD.Console.PrintWarning(translate("Arch","This mesh is an invalid solid")+"\n")
810
obj.Base.ViewObject.show()
811
if (not base) and (not obj.Additions):
812
#FreeCAD.Console.PrintError(translate("Arch","Error: Invalid base object")+"\n")
815
base = self.processSubShapes(obj,base,pl)
816
self.applyShape(obj,base,pl)
818
def getExtrusionData(self,obj):
819
"""returns (shape,extrusion vector or path,placement) or None"""
820
if hasattr(obj,"IfcType"):
821
IfcType = obj.IfcType
824
import Part,DraftGeomUtils
825
data = ArchComponent.Component.getExtrusionData(self,obj)
827
if not isinstance(data[0],list):
828
# multifuses not considered here
830
length = obj.Length.Value
831
width = obj.Width.Value
832
height = obj.Height.Value
834
height = self.getParentHeight(obj)
839
if hasattr(obj.Base,'Shape'):
841
if obj.Base.Shape.Solids:
843
elif obj.Base.Shape.Faces:
844
if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces,tol=0.01):
847
baseface = obj.Base.Shape.copy()
848
elif obj.Base.Shape.Wires:
849
# ArchSketch feature :
850
# Get base shape wires, and faceMaker, for Structure (slab. etc.) from Base Objects if they store and provide by getStructureBaseShapeWires()
851
# (thickness, normal/extrusion, length, width, baseface maybe for later) of structure (slab etc.)
852
structureBaseShapeWires = None
853
baseShapeWires = None #baseSlabWires / baseSlabOpeningWires = None
855
if hasattr(obj.Base, 'Proxy'):
856
if hasattr(obj.Base.Proxy, 'getStructureBaseShapeWires'):
857
structureBaseShapeWires = obj.Base.Proxy.getStructureBaseShapeWires(obj.Base, archsketchEdges=obj.ArchSketchEdges)
858
# provide selected edges, or groups, in obj.ArchSketchEdges for processing in getStructureBaseShapeWires() (getSortedClusters) as override
859
# returned a {dict} ( or a [list] )
860
# get slab wires; use original wires if structureBaseShapeWires() provided none
861
if structureBaseShapeWires: # would be false (none) if both base ArchSketch and obj do not have the edges stored / inputted by user
862
# if structureBaseShapeWires is {dict}
863
baseShapeWires = structureBaseShapeWires.get('slabWires')
864
faceMaker = structureBaseShapeWires.get('faceMaker')
865
if not baseShapeWires:
866
baseShapeWires = obj.Base.Shape.Wires
867
if faceMaker or (obj.FaceMaker != "None"):
869
faceMaker = obj.FaceMaker
871
baseface = Part.makeFace(baseShapeWires,"Part::FaceMaker"+str(faceMaker))
873
FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n")
874
# Not returning even Part.makeFace fails, fall back to 'non-Part.makeFace' method
876
for w in baseShapeWires:
878
p0 = w.OrderedVertexes[0].Point
879
p1 = w.OrderedVertexes[-1].Point
881
e = Part.LineSegment(p0,p1).toShape()
883
w.fix(0.1,0,1) # fixes self-intersecting wires
885
# check if it is 1st face (f) created from w in baseShapeWires; if not, fuse()
887
baseface = baseface.fuse(f)
889
# TODO use Part.Shape() rather than shape.copy() ... ?
891
elif length and width and height:
892
if (length > height) and (IfcType != "Slab"):
895
v1 = Vector(0,-w2,-h2)
896
v4 = Vector(0,-w2,h2)
898
v2 = Vector(0,w2,-h2)
902
v1 = Vector(-l2,-w2,0)
903
v2 = Vector(l2,-w2,0)
905
v4 = Vector(-l2,w2,0)
907
baseface = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1]))
909
if hasattr(obj, "Tool") and obj.Tool:
911
edges = DraftGeomUtils.get_referenced_edges(tool)
913
extrusion = Part.Wire(Part.__sortEdges__(edges))
914
if hasattr(obj, "ToolOffsetFirst"):
915
offset_start = float(obj.ToolOffsetFirst.getValueAs("mm"))
918
if hasattr(obj, "ToolOffsetLast"):
919
offset_end = float(obj.ToolOffsetLast.getValueAs("mm"))
922
if offset_start != 0.0 or offset_end != 0.0:
923
extrusion = DraftGeomUtils.get_extended_wire(extrusion, offset_start, offset_end)
924
if hasattr(obj, "BasePerpendicularToTool") and obj.BasePerpendicularToTool:
925
pl = FreeCAD.Placement()
926
if hasattr(obj, "BaseRotation"):
927
pl.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), -obj.BaseRotation)
928
if hasattr(obj, "BaseOffsetX") and hasattr(obj, "BaseOffsetY"):
929
pl.translate(FreeCAD.Vector(obj.BaseOffsetX, obj.BaseOffsetY, 0))
930
if hasattr(obj, "BaseMirror") and obj.BaseMirror:
931
pl.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 1, 0), 180)
932
baseface.Placement = DraftGeomUtils.get_placement_perpendicular_to_wire(extrusion).multiply(pl)
934
if obj.Normal.Length:
935
normal = Vector(obj.Normal).normalize()
937
normal = baseface.Faces[0].normalAt(0, 0) ## TODO to use ArchSketch's 'normal' for consistency
940
inverse_placement = None
941
if len(baseface.Faces) > 1:
944
hint = baseface.Faces[0].normalAt(0, 0) ## TODO anything to do ?
945
for f in baseface.Faces:
946
bf, pf = self.rebase(f, hint)
949
inverse_placement = placement[0].inverse()
951
base, placement = self.rebase(baseface)
952
inverse_placement = placement.inverse()
954
if len(extrusion.Edges) == 1 and DraftGeomUtils.geomType(extrusion.Edges[0]) == "Line":
955
extrusion = DraftGeomUtils.vec(extrusion.Edges[0], True)
956
if isinstance(extrusion, FreeCAD.Vector):
957
extrusion = inverse_placement.Rotation.multVec(extrusion)
959
normal = inverse_placement.Rotation.multVec(normal)
961
normal = Vector(0,0,1)
962
if not normal.Length:
963
normal = Vector(0,0,1)
965
if (length > height) and (IfcType != "Slab"):
967
extrusion = normal.multiply(length)
970
extrusion = normal.multiply(height)
972
return (base, extrusion, placement)
975
def onChanged(self,obj,prop):
977
if hasattr(obj,"IfcType"):
978
IfcType = obj.IfcType
981
self.hideSubobjects(obj,prop)
982
if prop in ["Shape","ResetNodes","NodesOffset"]:
983
# ResetNodes is not a property but it allows us to use this function to force reset the nodes
985
extdata = self.getExtrusionData(obj)
986
if extdata and not isinstance(extdata[0],list):
988
if IfcType not in ["Slab"]:
989
if not isinstance(extdata[1], FreeCAD.Vector):
991
elif extdata[1].Length > 0:
992
if hasattr(nodes,"CenterOfMass"):
994
nodes = Part.LineSegment(nodes.CenterOfMass,nodes.CenterOfMass.add(extdata[1])).toShape()
995
if isinstance(extdata[1], FreeCAD.Vector):
996
nodes.Placement = nodes.Placement.multiply(extdata[2])
997
offset = FreeCAD.Vector()
998
if hasattr(obj,"NodesOffset"):
999
offset = FreeCAD.Vector(0,0,obj.NodesOffset.Value)
1000
if obj.Nodes and (prop != "ResetNodes"):
1001
if hasattr(self,"nodes"):
1003
if obj.Nodes != self.nodes:
1004
# nodes are set manually: don't touch them
1007
# nodes haven't been calculated yet, but are set (file load)
1008
# we set the nodes now but don't change the property
1010
self.nodes = [v.Point.add(offset) for v in nodes.Vertexes]
1014
self.nodes = [v.Point.add(offset) for v in nodes.Vertexes]
1015
obj.Nodes = self.nodes
1016
ArchComponent.Component.onChanged(self,obj,prop)
1018
def getNodeEdges(self,obj):
1020
"returns a list of edges from structural nodes"
1025
for i in range(len(obj.Nodes)-1):
1026
edges.append(Part.LineSegment(obj.Placement.multVec(obj.Nodes[i]),obj.Placement.multVec(obj.Nodes[i+1])).toShape())
1027
if hasattr(obj.ViewObject,"NodeType"):
1028
if (obj.ViewObject.NodeType == "Area") and (len(obj.Nodes) > 2):
1029
edges.append(Part.LineSegment(obj.Placement.multVec(obj.Nodes[-1]),obj.Placement.multVec(obj.Nodes[0])).toShape())
1033
class _ViewProviderStructure(ArchComponent.ViewProviderComponent):
1035
"A View Provider for the Structure object"
1037
def __init__(self,vobj):
1039
ArchComponent.ViewProviderComponent.__init__(self,vobj)
1041
# setProperties of ArchComponent will be overwritten
1042
# thus setProperties from ArchComponent will be explicit called to get the properties
1043
ArchComponent.ViewProviderComponent.setProperties(self, vobj)
1045
self.setProperties(vobj)
1046
vobj.ShapeColor = ArchCommands.getDefaultColor("Structure")
1048
def setProperties(self,vobj):
1050
pl = vobj.PropertiesList
1051
if not "ShowNodes" in pl:
1052
vobj.addProperty("App::PropertyBool","ShowNodes","Nodes",QT_TRANSLATE_NOOP("App::Property","If the nodes are visible or not")).ShowNodes = False
1053
if not "NodeLine" in pl:
1054
vobj.addProperty("App::PropertyFloat","NodeLine","Nodes",QT_TRANSLATE_NOOP("App::Property","The width of the nodes line"))
1055
if not "NodeSize" in pl:
1056
vobj.addProperty("App::PropertyFloat","NodeSize","Nodes",QT_TRANSLATE_NOOP("App::Property","The size of the node points"))
1058
if not "NodeColor" in pl:
1059
vobj.addProperty("App::PropertyColor","NodeColor","Nodes",QT_TRANSLATE_NOOP("App::Property","The color of the nodes line"))
1060
vobj.NodeColor = (1.0,1.0,1.0,1.0)
1061
if not "NodeType" in pl:
1062
vobj.addProperty("App::PropertyEnumeration","NodeType","Nodes",QT_TRANSLATE_NOOP("App::Property","The type of structural node"))
1063
vobj.NodeType = ["Linear","Area"]
1065
def onDocumentRestored(self,vobj):
1067
self.setProperties(vobj)
1072
if hasattr(self,"Object"):
1073
if hasattr(self.Object,"CloneOf"):
1074
if self.Object.CloneOf:
1075
return ":/icons/Arch_Structure_Clone.svg"
1076
return ":/icons/Arch_Structure_Tree.svg"
1078
def updateData(self,obj,prop):
1082
if hasattr(self,"nodes"):
1084
self.pointset.numPoints.setValue(0)
1085
self.lineset.coordIndex.deleteValues(0)
1086
self.faceset.coordIndex.deleteValues(0)
1088
p.append([n.x,n.y,n.z])
1089
self.coords.point.setValues(0,len(p),p)
1090
self.pointset.numPoints.setValue(len(p))
1091
self.lineset.coordIndex.setValues(0,len(p)+1,list(range(len(p)))+[-1])
1092
if hasattr(obj.ViewObject,"NodeType"):
1093
if (obj.ViewObject.NodeType == "Area") and (len(p) > 2):
1094
self.coords.point.set1Value(len(p),p[0][0],p[0][1],p[0][2])
1095
self.lineset.coordIndex.setValues(0,len(p)+2,list(range(len(p)+1))+[-1])
1096
self.faceset.coordIndex.setValues(0,len(p)+1,list(range(len(p)))+[-1])
1098
elif prop in ["IfcType"]:
1099
if hasattr(obj.ViewObject,"NodeType"):
1100
if hasattr(obj,"IfcType"):
1101
IfcType = obj.IfcType
1104
if IfcType == "Slab":
1105
obj.ViewObject.NodeType = "Area"
1107
obj.ViewObject.NodeType = "Linear"
1109
ArchComponent.ViewProviderComponent.updateData(self,obj,prop)
1111
def onChanged(self,vobj,prop):
1113
if prop == "ShowNodes":
1114
if hasattr(self,"nodes"):
1115
vobj.Annotation.removeChild(self.nodes)
1118
from pivy import coin
1119
self.nodes = coin.SoAnnotation()
1120
self.coords = coin.SoCoordinate3()
1121
self.mat = coin.SoMaterial()
1122
self.pointstyle = coin.SoDrawStyle()
1123
self.pointstyle.style = coin.SoDrawStyle.POINTS
1124
self.pointset = coin.SoType.fromName("SoBrepPointSet").createInstance()
1125
self.linestyle = coin.SoDrawStyle()
1126
self.linestyle.style = coin.SoDrawStyle.LINES
1127
self.lineset = coin.SoType.fromName("SoBrepEdgeSet").createInstance()
1128
self.facestyle = coin.SoDrawStyle()
1129
self.facestyle.style = coin.SoDrawStyle.FILLED
1130
self.shapehints = coin.SoShapeHints()
1131
self.shapehints.faceType = coin.SoShapeHints.UNKNOWN_FACE_TYPE
1132
self.fmat = coin.SoMaterial()
1133
self.fmat.transparency.setValue(0.75)
1134
self.faceset = coin.SoIndexedFaceSet()
1135
self.nodes.addChild(self.coords)
1136
self.nodes.addChild(self.mat)
1137
self.nodes.addChild(self.pointstyle)
1138
self.nodes.addChild(self.pointset)
1139
self.nodes.addChild(self.linestyle)
1140
self.nodes.addChild(self.lineset)
1141
self.nodes.addChild(self.facestyle)
1142
self.nodes.addChild(self.shapehints)
1143
self.nodes.addChild(self.fmat)
1144
self.nodes.addChild(self.faceset)
1145
vobj.Annotation.addChild(self.nodes)
1146
self.updateData(vobj.Object,"Nodes")
1147
self.onChanged(vobj,"NodeColor")
1148
self.onChanged(vobj,"NodeLine")
1149
self.onChanged(vobj,"NodeSize")
1151
elif prop == "NodeColor":
1152
if hasattr(self,"mat"):
1154
self.mat.diffuseColor.setValue([l[0],l[1],l[2]])
1155
self.fmat.diffuseColor.setValue([l[0],l[1],l[2]])
1157
elif prop == "NodeLine":
1158
if hasattr(self,"linestyle"):
1159
self.linestyle.lineWidth = vobj.NodeLine
1161
elif prop == "NodeSize":
1162
if hasattr(self,"pointstyle"):
1163
self.pointstyle.pointSize = vobj.NodeSize
1165
elif prop == "NodeType":
1166
self.updateData(vobj.Object,"Nodes")
1169
ArchComponent.ViewProviderComponent.onChanged(self,vobj,prop)
1171
def setEdit(self,vobj,mode):
1175
taskd = StructureTaskPanel(vobj.Object)
1176
taskd.obj = self.Object
1178
FreeCADGui.Control.showDialog(taskd)
1182
class StructureTaskPanel(ArchComponent.ComponentTaskPanel):
1184
def __init__(self,obj):
1186
ArchComponent.ComponentTaskPanel.__init__(self)
1187
self.nodes_widget = QtGui.QWidget()
1188
self.nodes_widget.setWindowTitle(QtGui.QApplication.translate("Arch", "Node Tools", None))
1189
lay = QtGui.QVBoxLayout(self.nodes_widget)
1191
self.resetButton = QtGui.QPushButton(self.nodes_widget)
1192
self.resetButton.setIcon(QtGui.QIcon(":/icons/edit-undo.svg"))
1193
self.resetButton.setText(QtGui.QApplication.translate("Arch", "Reset nodes", None))
1195
lay.addWidget(self.resetButton)
1196
QtCore.QObject.connect(self.resetButton, QtCore.SIGNAL("clicked()"), self.resetNodes)
1198
self.editButton = QtGui.QPushButton(self.nodes_widget)
1199
self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg"))
1200
self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit nodes", None))
1201
lay.addWidget(self.editButton)
1202
QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editNodes)
1204
self.extendButton = QtGui.QPushButton(self.nodes_widget)
1205
self.extendButton.setIcon(QtGui.QIcon(":/icons/Snap_Perpendicular.svg"))
1206
self.extendButton.setText(QtGui.QApplication.translate("Arch", "Extend nodes", None))
1207
self.extendButton.setToolTip(QtGui.QApplication.translate("Arch", "Extends the nodes of this element to reach the nodes of another element", None))
1208
lay.addWidget(self.extendButton)
1209
QtCore.QObject.connect(self.extendButton, QtCore.SIGNAL("clicked()"), self.extendNodes)
1211
self.connectButton = QtGui.QPushButton(self.nodes_widget)
1212
self.connectButton.setIcon(QtGui.QIcon(":/icons/Snap_Intersection.svg"))
1213
self.connectButton.setText(QtGui.QApplication.translate("Arch", "Connect nodes", None))
1214
self.connectButton.setToolTip(QtGui.QApplication.translate("Arch", "Connects nodes of this element with the nodes of another element", None))
1215
lay.addWidget(self.connectButton)
1216
QtCore.QObject.connect(self.connectButton, QtCore.SIGNAL("clicked()"), self.connectNodes)
1218
self.toggleButton = QtGui.QPushButton(self.nodes_widget)
1219
self.toggleButton.setIcon(QtGui.QIcon(":/icons/dagViewVisible.svg"))
1220
self.toggleButton.setText(QtGui.QApplication.translate("Arch", "Toggle all nodes", None))
1221
self.toggleButton.setToolTip(QtGui.QApplication.translate("Arch", "Toggles all structural nodes of the document on/off", None))
1222
lay.addWidget(self.toggleButton)
1223
QtCore.QObject.connect(self.toggleButton, QtCore.SIGNAL("clicked()"), self.toggleNodes)
1225
self.extrusion_widget = QtGui.QWidget()
1226
self.extrusion_widget.setWindowTitle(QtGui.QApplication.translate("Arch", "Extrusion Tools", None))
1227
lay = QtGui.QVBoxLayout(self.extrusion_widget)
1229
self.selectToolButton = QtGui.QPushButton(self.extrusion_widget)
1230
self.selectToolButton.setIcon(QtGui.QIcon())
1231
self.selectToolButton.setText(QtGui.QApplication.translate("Arch", "Select tool...", None))
1232
self.selectToolButton.setToolTip(QtGui.QApplication.translate("Arch", "Select object or edges to be used as a Tool (extrusion path)", None))
1233
lay.addWidget(self.selectToolButton)
1234
QtCore.QObject.connect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setSelectionFromTool)
1236
self.form = [self.form, self.nodes_widget, self.extrusion_widget]
1238
self.observer = None
1241
def editNodes(self):
1243
FreeCADGui.Control.closeDialog()
1244
FreeCADGui.runCommand("Draft_Edit")
1246
def resetNodes(self):
1248
self.Object.Proxy.onChanged(self.Object,"ResetNodes")
1250
def extendNodes(self,other=None):
1253
self.observer = StructSelectionObserver(self.extendNodes)
1254
FreeCADGui.Selection.addObserver(self.observer)
1255
FreeCAD.Console.PrintMessage(translate("Arch","Choose another Structure object:"))
1257
FreeCADGui.Selection.removeObserver(self.observer)
1258
self.observer = None
1259
if Draft.getType(other) != "Structure":
1260
FreeCAD.Console.PrintError(translate("Arch","The chosen object is not a Structure")+"\n")
1263
FreeCAD.Console.PrintError(translate("Arch","The chosen object has no structural nodes")+"\n")
1265
if (len(self.Object.Nodes) != 2) or (len(other.Nodes) != 2):
1266
FreeCAD.Console.PrintError(translate("Arch","One of these objects has more than 2 nodes")+"\n")
1268
import DraftGeomUtils
1269
nodes1 = [self.Object.Placement.multVec(v) for v in self.Object.Nodes]
1270
nodes2 = [other.Placement.multVec(v) for v in other.Nodes]
1271
intersect = DraftGeomUtils.findIntersection(nodes1[0],nodes1[1],nodes2[0],nodes2[1],True,True)
1273
FreeCAD.Console.PrintError(translate("Arch","Unable to find a suitable intersection point")+"\n")
1275
intersect = intersect[0]
1276
FreeCAD.Console.PrintMessage(translate("Arch","Intersection found.\n"))
1277
if DraftGeomUtils.findClosest(intersect,nodes1) == 0:
1278
self.Object.Nodes = [self.Object.Placement.inverse().multVec(intersect),self.Object.Nodes[1]]
1280
self.Object.Nodes = [self.Object.Nodes[0],self.Object.Placement.inverse().multVec(intersect)]
1282
def connectNodes(self,other=None):
1285
self.observer = StructSelectionObserver(self.connectNodes)
1286
FreeCADGui.Selection.addObserver(self.observer)
1287
FreeCAD.Console.PrintMessage(translate("Arch","Choose another Structure object:"))
1289
FreeCADGui.Selection.removeObserver(self.observer)
1290
self.observer = None
1291
if Draft.getType(other) != "Structure":
1292
FreeCAD.Console.PrintError(translate("Arch","The chosen object is not a Structure")+"\n")
1295
FreeCAD.Console.PrintError(translate("Arch","The chosen object has no structural nodes")+"\n")
1297
if (len(self.Object.Nodes) != 2) or (len(other.Nodes) != 2):
1298
FreeCAD.Console.PrintError(translate("Arch","One of these objects has more than 2 nodes")+"\n")
1300
import DraftGeomUtils
1301
nodes1 = [self.Object.Placement.multVec(v) for v in self.Object.Nodes]
1302
nodes2 = [other.Placement.multVec(v) for v in other.Nodes]
1303
intersect = DraftGeomUtils.findIntersection(nodes1[0],nodes1[1],nodes2[0],nodes2[1],True,True)
1305
FreeCAD.Console.PrintError(translate("Arch","Unable to find a suitable intersection point")+"\n")
1307
intersect = intersect[0]
1308
FreeCAD.Console.PrintMessage(translate("Arch","Intersection found.")+"\n")
1309
if DraftGeomUtils.findClosest(intersect,nodes1) == 0:
1310
self.Object.Nodes = [self.Object.Placement.inverse().multVec(intersect),self.Object.Nodes[1]]
1312
self.Object.Nodes = [self.Object.Nodes[0],self.Object.Placement.inverse().multVec(intersect)]
1313
if DraftGeomUtils.findClosest(intersect,nodes2) == 0:
1314
other.Nodes = [other.Placement.inverse().multVec(intersect),other.Nodes[1]]
1316
other.Nodes = [other.Nodes[0],other.Placement.inverse().multVec(intersect)]
1318
def toggleNodes(self):
1321
for obj in self.nodevis:
1322
obj[0].ViewObject.ShowNodes = obj[1]
1326
for obj in FreeCAD.ActiveDocument.Objects:
1327
if hasattr(obj.ViewObject,"ShowNodes"):
1328
self.nodevis.append([obj,obj.ViewObject.ShowNodes])
1329
obj.ViewObject.ShowNodes = True
1331
def setSelectionFromTool(self):
1332
FreeCADGui.Selection.clearSelection()
1333
if hasattr(self.Object, "Tool"):
1334
tool = self.Object.Tool
1335
if hasattr(tool, "Shape") and tool.Shape:
1336
FreeCADGui.Selection.addSelection(tool)
1338
if not isinstance(tool, list):
1340
for o, subs in tool:
1341
FreeCADGui.Selection.addSelection(o, subs)
1342
QtCore.QObject.disconnect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setSelectionFromTool)
1343
QtCore.QObject.connect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setToolFromSelection)
1344
self.selectToolButton.setText(QtGui.QApplication.translate("Arch", "Done", None))
1346
def setToolFromSelection(self):
1348
selEx = FreeCADGui.Selection.getSelectionEx()
1349
for selExi in selEx:
1350
if len(selExi.SubElementNames) == 0:
1351
# Add entirely selected objects
1352
objectList.append(selExi.Object)
1354
subElementsNames = [subElementName for subElementName in selExi.SubElementNames if subElementName.startswith("Edge")]
1355
# Check that at least an edge is selected from the object's shape
1356
if len(subElementsNames) > 0:
1357
objectList.append((selExi.Object, subElementsNames))
1358
if self.Object.getTypeIdOfProperty("Tool") != "App::PropertyLinkSubList":
1359
# Upgrade property Tool from App::PropertyLink to App::PropertyLinkSubList (note: Undo/Redo fails)
1360
self.Object.removeProperty("Tool")
1361
self.Object.addProperty("App::PropertyLinkSubList", "Tool", "Structure", QT_TRANSLATE_NOOP("App::Property", "An optional extrusion path for this element"))
1362
self.Object.Tool = objectList
1363
QtCore.QObject.disconnect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setToolFromSelection)
1364
QtCore.QObject.connect(self.selectToolButton, QtCore.SIGNAL("clicked()"), self.setSelectionFromTool)
1365
self.selectToolButton.setText(QtGui.QApplication.translate("Arch", "Select tool...", None))
1370
FreeCADGui.Selection.removeObserver(self.observer)
1373
FreeCAD.ActiveDocument.recompute()
1374
FreeCADGui.ActiveDocument.resetEdit()
1378
class StructSelectionObserver:
1380
def __init__(self,callback):
1381
self.callback = callback
1383
def addSelection(self, docName, objName, sub, pos):
1384
print("got ",objName)
1385
obj = FreeCAD.getDocument(docName).getObject(objName)
1389
class _StructuralSystem(ArchComponent.Component): # OBSOLETE - All Arch objects can now be based on axes
1391
"The Structural System object"
1393
def __init__(self,obj):
1395
ArchComponent.Component.__init__(self,obj)
1396
obj.addProperty("App::PropertyLinkList","Axes","Arch",QT_TRANSLATE_NOOP("App::Property","Axes systems this structure is built on"))
1397
obj.addProperty("App::PropertyIntegerList","Exclude","Arch",QT_TRANSLATE_NOOP("App::Property","The element numbers to exclude when this structure is based on axes"))
1398
obj.addProperty("App::PropertyBool","Align","Arch",QT_TRANSLATE_NOOP("App::Property","If true the element are aligned with axes")).Align = False
1399
self.Type = "StructuralSystem"
1401
def execute(self,obj):
1402
"creates the structure shape"
1404
import Part, DraftGeomUtils
1406
# creating base shape
1409
if hasattr(obj.Base,'Shape'):
1410
if obj.Base.Shape.isNull():
1412
if not obj.Base.Shape.Solids:
1418
pts = self.getAxisPoints(obj)
1419
if hasattr(obj,"Align"):
1420
if obj.Align == False :
1421
apl = self.getAxisPlacement(obj)
1425
apl = self.getAxisPlacement(obj)
1429
for i in range(len(pts)):
1430
sh = obj.Base.Shape.copy()
1431
if hasattr(obj,"Exclude"):
1432
if i in obj.Exclude:
1435
sh.Placement.Rotation = sh.Placement.Rotation.multiply(apl.Rotation)
1436
sh.translate(pts[i])
1440
base = Part.makeCompound(fsh)
1441
base = self.processSubShapes(obj,base,pl)
1444
if not base.isNull():
1445
if base.isValid() and base.Solids:
1449
FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape"))
1451
base = base.removeSplitter()
1456
def getAxisPoints(self,obj):
1457
"returns the gridpoints of linked axes"
1458
import DraftGeomUtils
1460
if len(obj.Axes) == 1:
1461
if hasattr(obj,"Align"):
1463
p0 = obj.Axes[0].Shape.Edges[0].Vertexes[1].Point
1464
for e in obj.Axes[0].Shape.Edges:
1465
p = e.Vertexes[1].Point
1469
for e in obj.Axes[0].Shape.Edges:
1470
pts.append(e.Vertexes[0].Point)
1472
for e in obj.Axes[0].Shape.Edges:
1473
pts.append(e.Vertexes[0].Point)
1474
elif len(obj.Axes) >= 2:
1475
set1 = obj.Axes[0].Shape.Edges
1476
set2 = obj.Axes[1].Shape.Edges
1479
pts.extend(DraftGeomUtils.findIntersection(e1,e2))
1482
def getAxisPlacement(self,obj):
1483
"returns an axis placement"
1485
return obj.Axes[0].Placement
1489
class _ViewProviderStructuralSystem(ArchComponent.ViewProviderComponent):
1491
"A View Provider for the Structural System object"
1496
return ":/icons/Arch_StructuralSystem_Tree.svg"
1500
FreeCADGui.addCommand("Arch_Structure", _CommandStructure())
1501
FreeCADGui.addCommand("Arch_StructuralSystem", CommandStructuralSystem())
1502
FreeCADGui.addCommand("Arch_StructuresFromSelection", CommandStructuresFromSelection())
1504
class _ArchStructureGroupCommand:
1506
def GetCommands(self):
1507
return ("Arch_Structure", "Arch_StructuralSystem", "Arch_StructuresFromSelection")
1508
def GetResources(self):
1509
return { "MenuText": QT_TRANSLATE_NOOP("Arch_StructureTools", "Structure tools"),
1510
"ToolTip": QT_TRANSLATE_NOOP("Arch_StructureTools", "Structure tools")
1513
return not FreeCAD.ActiveDocument is None
1515
FreeCADGui.addCommand("Arch_StructureTools", _ArchStructureGroupCommand())