| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| __title__ = "FreeCAD Arch Truss" |
| __author__ = "Yorik van Havre" |
| __url__ = "https://www.freecad.org" |
|
|
| |
| |
| |
| |
| |
|
|
| import math |
|
|
| import FreeCAD |
| import ArchComponent |
|
|
| if FreeCAD.GuiUp: |
| from PySide.QtCore import QT_TRANSLATE_NOOP |
| import FreeCADGui |
| from draftutils.translate import translate |
| else: |
| |
| def translate(ctxt, txt): |
| return txt |
|
|
| def QT_TRANSLATE_NOOP(ctxt, txt): |
| return txt |
|
|
| |
|
|
| rodmodes = ( |
| "/|/|/|", |
| "/\\/\\/\\", |
| "/|\\|/|\\", |
| ) |
|
|
|
|
| class Truss(ArchComponent.Component): |
| "The truss object" |
|
|
| def __init__(self, obj): |
|
|
| ArchComponent.Component.__init__(self, obj) |
| self.Type = "Truss" |
| self.setProperties(obj) |
| obj.IfcType = "Beam" |
|
|
| def setProperties(self, obj): |
|
|
| pl = obj.PropertiesList |
| if not "TrussAngle" in pl: |
| obj.addProperty( |
| "App::PropertyAngle", |
| "TrussAngle", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The angle of the truss"), |
| locked=True, |
| ) |
| obj.setEditorMode("TrussAngle", 1) |
| if not "SlantType" in pl: |
| obj.addProperty( |
| "App::PropertyEnumeration", |
| "SlantType", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The slant type of this truss"), |
| locked=True, |
| ) |
| obj.SlantType = ["Simple", "Double"] |
| if not "Normal" in pl: |
| obj.addProperty( |
| "App::PropertyVector", |
| "Normal", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The normal direction of this truss"), |
| locked=True, |
| ) |
| obj.Normal = FreeCAD.Vector(0, 0, 1) |
| if not "HeightStart" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "HeightStart", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The height of the truss at the start position"), |
| locked=True, |
| ) |
| obj.HeightStart = 100 |
| if not "HeightEnd" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "HeightEnd", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The height of the truss at the end position"), |
| locked=True, |
| ) |
| obj.HeightEnd = 200 |
| if not "StrutStartOffset" in pl: |
| obj.addProperty( |
| "App::PropertyDistance", |
| "StrutStartOffset", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "An optional start offset for the top strut"), |
| locked=True, |
| ) |
| if not "StrutEndOffset" in pl: |
| obj.addProperty( |
| "App::PropertyDistance", |
| "StrutEndOffset", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "An optional end offset for the top strut"), |
| locked=True, |
| ) |
| if not "StrutHeight" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "StrutHeight", |
| "Truss", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The height of the main top and bottom elements of the truss" |
| ), |
| locked=True, |
| ) |
| obj.StrutHeight = 10 |
| if not "StrutWidth" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "StrutWidth", |
| "Truss", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The width of the main top and bottom elements of the truss" |
| ), |
| locked=True, |
| ) |
| obj.StrutWidth = 5 |
| if not "RodType" in pl: |
| obj.addProperty( |
| "App::PropertyEnumeration", |
| "RodType", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The type of the middle element of the truss"), |
| locked=True, |
| ) |
| obj.RodType = ["Round", "Square"] |
| if not "RodDirection" in pl: |
| obj.addProperty( |
| "App::PropertyEnumeration", |
| "RodDirection", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The direction of the rods"), |
| locked=True, |
| ) |
| obj.RodDirection = ["Forward", "Backward"] |
| if not "RodSize" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "RodSize", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The diameter or side of the rods"), |
| locked=True, |
| ) |
| obj.RodSize = 2 |
| if not "RodSections" in pl: |
| obj.addProperty( |
| "App::PropertyInteger", |
| "RodSections", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "The number of rod sections"), |
| locked=True, |
| ) |
| obj.RodSections = 3 |
| if not "RodEnd" in pl: |
| obj.addProperty( |
| "App::PropertyBool", |
| "RodEnd", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "If the truss has a rod at its endpoint or not"), |
| locked=True, |
| ) |
| if not "RodMode" in pl: |
| obj.addProperty( |
| "App::PropertyEnumeration", |
| "RodMode", |
| "Truss", |
| QT_TRANSLATE_NOOP("App::Property", "How to draw the rods"), |
| locked=True, |
| ) |
| obj.RodMode = rodmodes |
|
|
| def onDocumentRestored(self, obj): |
|
|
| ArchComponent.Component.onDocumentRestored(self, obj) |
| self.setProperties(obj) |
|
|
| def loads(self, state): |
|
|
| self.Type = "Truss" |
|
|
| def onChanged(self, obj, prop): |
|
|
| ArchComponent.Component.onChanged(self, obj, prop) |
|
|
| def execute(self, obj): |
|
|
| if self.clone(obj): |
| return |
| if not self.ensureBase(obj): |
| return |
|
|
| import Part |
|
|
| pl = obj.Placement |
|
|
| |
| if not obj.StrutWidth.Value or not obj.StrutHeight.Value: |
| FreeCAD.Console.PrintLog(obj.Label + ": Invalid strut size\n") |
| return |
| if not obj.HeightStart.Value or not obj.HeightEnd.Value: |
| FreeCAD.Console.PrintLog(obj.Label + ": Invalid truss heights\n") |
| return |
| if not obj.RodSections or not obj.RodSize.Value: |
| FreeCAD.Console.PrintLog(obj.Label + ": Invalid rod config\n") |
| return |
| if not obj.Base: |
| FreeCAD.Console.PrintLog(obj.Label + ": No base\n") |
| return |
| if (not hasattr(obj.Base, "Shape")) or (len(obj.Base.Shape.Vertexes) > 2): |
| FreeCAD.Console.PrintLog(obj.Label + ": No base edge\n") |
| return |
|
|
| |
| v0 = obj.Base.Shape.Vertexes[0].Point |
| v1 = obj.Base.Shape.Vertexes[-1].Point |
|
|
| if obj.SlantType == "Simple": |
| topstrut, bottomstrut, rods, angle = self.makeTruss(obj, v0, v1) |
| else: |
| v3 = v0.add((v1.sub(v0)).multiply(0.5)) |
| topstrut1, bottomstrut1, rods1, angle = self.makeTruss(obj, v0, v3) |
| topstrut2, bottomstrut2, rods2, angle = self.makeTruss(obj, v1, v3) |
| topstrut = topstrut1.fuse(topstrut2) |
| topstrut = topstrut.removeSplitter() |
| bottomstrut = bottomstrut1.fuse(bottomstrut2) |
| bottomstrut = bottomstrut.removeSplitter() |
| if obj.RodEnd: |
| rods2 = rods2[:-1] |
| rods = rods1 + rods2 |
|
|
| |
| shape = Part.makeCompound([topstrut, bottomstrut] + rods) |
| shape = self.processSubShapes(obj, shape, pl) |
| self.applyShape(obj, shape, pl) |
| obj.TrussAngle = angle |
|
|
| def makeTruss(self, obj, v0, v1): |
|
|
| import Part |
|
|
| |
| normal = obj.Normal |
| if not normal.Length: |
| normal = FreeCAD.Vector(0, 0, 1) |
|
|
| |
| maindir = v1.sub(v0) |
| sidedir = normal.cross(maindir) |
| if not sidedir.Length: |
| FreeCAD.Console.PrintLog(obj.Label + ": normal and base are parallel\n") |
| return |
| sidedir.normalize() |
| p0 = v0.add(FreeCAD.Vector(sidedir).negative().multiply(obj.StrutWidth.Value / 2)) |
| p1 = p0.add(FreeCAD.Vector(sidedir).multiply(obj.StrutWidth.Value / 2).multiply(2)) |
| p2 = p1.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight)) |
| p3 = p0.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight)) |
| trussprofile = Part.Face(Part.makePolygon([p0, p1, p2, p3, p0])) |
|
|
| |
| bottomstrut = trussprofile.extrude(maindir) |
|
|
| |
| v2 = v0.add(FreeCAD.Vector(normal).multiply(obj.HeightStart.Value)) |
| v3 = v1.add(FreeCAD.Vector(normal).multiply(obj.HeightEnd.Value)) |
| topdir = v3.sub(v2) |
| if obj.StrutStartOffset.Value: |
| v2f = v2.add((v2.sub(v3)).normalize().multiply(obj.StrutStartOffset.Value)) |
| else: |
| v2f = v2 |
| if obj.StrutEndOffset.Value: |
| v3f = v3.add((v3.sub(v2)).normalize().multiply(obj.StrutEndOffset.Value)) |
| else: |
| v3f = v3 |
| offtopdir = v3f.sub(v2f) |
| topstrut = trussprofile.extrude(offtopdir) |
| topstrut.translate(v2f.sub(v0)) |
| topstrut.translate(FreeCAD.Vector(normal).multiply(-obj.StrutHeight.Value)) |
| angle = math.degrees(topdir.getAngle(maindir)) |
|
|
| |
| if obj.RodType == "Round": |
| rodprofile = Part.Face(Part.Wire([Part.makeCircle(obj.RodSize / 2)])) |
| else: |
| rodprofile = Part.Face( |
| Part.makePlane( |
| obj.RodSize, obj.RodSize, FreeCAD.Vector(-obj.RodSize / 2, -obj.RodSize / 2, 0) |
| ) |
| ) |
|
|
| |
| rods = [] |
| bottomrodstart = v0.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value / 2)) |
| toprodstart = v2.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value / 2).negative()) |
| bottomrodvec = FreeCAD.Vector(maindir).multiply(1 / obj.RodSections) |
| toprodvec = FreeCAD.Vector(topdir).multiply(1 / obj.RodSections) |
| bottomrodpos = [bottomrodstart] |
| toprodpos = [toprodstart] |
|
|
| if obj.RodMode == rodmodes[0]: |
| |
| for i in range(1, obj.RodSections + 1): |
| if (i > 1) or (obj.StrutStartOffset.Value >= 0): |
| |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-1])) |
| bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec)) |
| toprodpos.append(toprodpos[-1].add(toprodvec)) |
| if obj.RodDirection == "Forward": |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) |
| else: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) |
|
|
| elif obj.RodMode == rodmodes[1]: |
| |
| fw = True |
| for i in range(1, obj.RodSections + 1): |
| bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec)) |
| toprodpos.append(toprodpos[-1].add(toprodvec)) |
| if obj.RodDirection == "Forward": |
| if fw: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) |
| else: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) |
| else: |
| if fw: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) |
| else: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) |
| fw = not fw |
|
|
| elif obj.RodMode == rodmodes[2]: |
| |
| fw = True |
| for i in range(1, obj.RodSections + 1): |
| if (i > 1) or (obj.StrutStartOffset.Value >= 0): |
| |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-1])) |
| bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec)) |
| toprodpos.append(toprodpos[-1].add(toprodvec)) |
| if obj.RodDirection == "Forward": |
| if fw: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) |
| else: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) |
| else: |
| if fw: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-2])) |
| else: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-2], toprodpos[-1])) |
| fw = not fw |
|
|
| |
| if obj.RodEnd: |
| rods.append(self.makeRod(rodprofile, bottomrodpos[-1], toprodpos[-1])) |
|
|
| |
| rods = [rod.cut(topstrut).cut(bottomstrut) for rod in rods] |
|
|
| return topstrut, bottomstrut, rods, angle |
|
|
| def makeRod(self, profile, p1, p2): |
| """makes a rod by extruding profile between p1 and p2""" |
|
|
| rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), p2.sub(p1)) |
| rod = profile.copy().translate(p1) |
| rod = rod.rotate(p1, rot.Axis, math.degrees(rot.Angle)) |
| rod = rod.extrude(p2.sub(p1)) |
| return rod |
|
|
|
|
| class ViewProviderTruss(ArchComponent.ViewProviderComponent): |
| "A View Provider for the Truss object" |
|
|
| def __init__(self, vobj): |
|
|
| ArchComponent.ViewProviderComponent.__init__(self, vobj) |
|
|
| def getIcon(self): |
|
|
| import Arch_rc |
|
|
| return ":/icons/Arch_Truss_Tree.svg" |
|
|