Arch: Big cleanup - fixes #1524

* Removed redundant code in Arch objects
* Supports wandererfan's Units code in Draft module
* Cleanup of the IFC exporter
* Better support of extruded profiles in IFC export
This commit is contained in:
Yorik van Havre 2014-05-11 14:40:39 -03:00
parent 068452f0ac
commit 2798de1afa
12 changed files with 504 additions and 547 deletions

View File

@ -593,26 +593,14 @@ def check(objectslist,includehidden=False):
bad.append([o,translate("Arch","contains faces that are not part of any solid")])
return bad
def addFixture(fixture,baseobject):
'''addFixture(fixture,baseobject): adds the given object as a
fixture to the given base object'''
if hasattr(baseobject,"Fixtures"):
f = baseobject.Fixtures
f.append(fixture)
baseobject.Fixtures = f
if baseobject.ViewObject.DisplayMode != "Detailed":
fixture.ViewObject.hide()
else:
FreeCAD.Console.PrintMessage(translate("Arch","This object has no support for fixtures"))
def getTuples(data,scale=1,placement=None,normal=None,close=True,flatten=False):
"""getTuples(data,[scale,placement,normal,flatten]): returns a tuple or a list of tuples from a vector
def getTuples(data,scale=1,placement=None,normal=None,close=True):
"""getTuples(data,[scale,placement,normal,close]): returns a tuple or a list of tuples from a vector
or from the vertices of a shape. Scale can indicate a scale factor"""
import Part
if isinstance(data,FreeCAD.Vector):
if placement:
data = placement.multVec(data)
data = DraftVecUtils.rounded(data)
return (data.x*scale,data.y*scale,data.z*scale)
elif isinstance(data,Part.Shape):
t = []
@ -632,21 +620,21 @@ def getTuples(data,scale=1,placement=None,normal=None,close=True,flatten=False):
for v in verts:
pt = v.Point
if placement:
pt = placement.multVec(pt)
if flatten:
t.append((pt.x*scale,pt.y*scale,0))
else:
t.append((pt.x*scale,pt.y*scale,pt.z*scale))
if close:
t.append(t[0]) # for IFC verts lists must be closed
if not placement.isNull():
pt = placement.multVec(pt)
pt = DraftVecUtils.rounded(pt)
t.append((pt.x*scale,pt.y*scale,pt.z*scale))
if close: # faceloops must not be closed, but ifc profiles must.
t.append(t[0])
else:
print "Arch.getTuples(): Wrong profile data"
return t
def getExtrusionData(obj,scale=1,flatten=True):
"""getExtrusionData(obj,[scale,flatten]): returns a closed path (a list of tuples) and a tuple expressing an extrusion
vector, or None, if a base loop and an extrusion direction cannot be extracted. Scale can indicate a scale factor.
if flatten is True (default), the profile stays in the XY plane (IFC default)"""
def getExtrusionData(obj,scale=1):
"""getExtrusionData(obj,[scale]): returns a closed path (a list of tuples), a tuple expressing an extrusion
vector, and a list of 3 tuples for base position, x axis and z axis. Or returns None, if a base loop and
an extrusion direction cannot be extracted. Scale can indicate a scale factor."""
if hasattr(obj,"Additions"):
if obj.Additions:
# provisorily treat objs with additions as breps
@ -656,16 +644,24 @@ def getExtrusionData(obj,scale=1,flatten=True):
# provisorily treat objs with subtractions as breps
return None
if hasattr(obj,"Proxy"):
if hasattr(obj.Proxy,"BaseProfile") and hasattr(obj.Proxy,"ExtrusionVector"):
import Part
pl = FreeCAD.Placement(obj.Placement)
r = FreeCAD.Rotation(obj.Placement.Rotation)
if pl.isNull():
pl = r = None
if len(obj.Proxy.BaseProfile.Edges) == 1:
if isinstance(obj.Proxy.BaseProfile.Edges[0].Curve,Part.Circle):
return "circle", getTuples(obj.Proxy.BaseProfile.Edges[0].Curve.Center,scale,flatten=flatten), obj.Proxy.BaseProfile.Edges[0].Curve.Radius*scale, getTuples(obj.Proxy.ExtrusionVector,scale,r)
return "polyline", getTuples(obj.Proxy.BaseProfile,scale,pl,flatten=flatten), getTuples(obj.Proxy.ExtrusionVector,scale,r)
if hasattr(obj.Proxy,"getProfiles"):
p = obj.Proxy.getProfiles(obj,noplacement=True)
v = obj.Proxy.getExtrusionVector(obj,noplacement=True)
if (len(p) == 1) and v:
p = p[0]
r = FreeCAD.Placement()
#b = p.CenterOfMass
r = obj.Proxy.getPlacement(obj)
#b = obj.Placement.multVec(FreeCAD.Vector())
#r.Rotation = DraftVecUtils.getRotation(v,FreeCAD.Vector(0,0,1))
d = [r.Base,DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(1,0,0))),DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(0,0,1)))]
r = r.inverse()
#print "getExtrusionData: computed placement:",r
import Part
if len(p.Edges) == 1:
if isinstance(p.Edges[0].Curve,Part.Circle):
return "circle", [getTuples(p.Edges[0].Curve.Center,scale), p.Edges[0].Curve.Radius*scale], getTuples(v,scale), d
return "polyline", getTuples(p,scale), getTuples(v,scale), d
return None
def getBrepFacesData(obj,scale=1):
@ -719,7 +715,9 @@ def pruneIncluded(objectslist):
if not (Draft.getType(obj) in ["Window","Clone"]):
for parent in obj.InList:
if parent.isDerivedFrom("Part::Feature"):
toplevel = False
if not parent.isDerivedFrom("Part::Part2DObject"):
# don't consider 2D objects based on arch elements
toplevel = False
if toplevel:
newlist.append(obj)
return newlist
@ -784,8 +782,6 @@ def survey(callback=False):
if not found:
newsels.append(o)
if newsels:
from pivy import coin
pr = Draft.getParam("dimPrecision",2)
for o in newsels:
if o.Object.isDerivedFrom("Part::Feature"):
n = o.Object.Label
@ -796,15 +792,18 @@ def survey(callback=False):
FreeCAD.SurveyObserver.labels.append(anno.Name)
t = ""
if o.Object.Shape.Solids:
t = str(round(o.Object.Shape.Volume,pr))
t = FreeCAD.Units.Quantity(o.Object.Shape.Volume,FreeCAD.Units.Volume)
t = t.getUserPreferred()[0]
anno.LabelText = "v " + t
FreeCAD.Console.PrintMessage("Object: " + n + ", Element: Whole, Volume: " + t + "\n")
elif o.Object.Shape.Faces:
t = str(round(o.Object.Shape.Area,pr))
t = FreeCAD.Units.Quantity(o.Object.Shape.Area,FreeCAD.Units.Area)
t = t.getUserPreferred()[0]
anno.LabelText = "a " + t
FreeCAD.Console.PrintMessage("Object: " + n + ", Element: Whole, Area: " + t + "\n")
else:
t = str(round(o.Object.Shape.Length,pr))
t = FreeCAD.Units.Quantity(o.Object.Shape.Length,FreeCAD.Units.Length)
t = t.getUserPreferred()[0]
anno.LabelText = "l " + t
FreeCAD.Console.PrintMessage("Object: " + n + ", Element: Whole, Length: " + t + "\n")
if FreeCAD.GuiUp and t:
@ -821,15 +820,18 @@ def survey(callback=False):
FreeCAD.SurveyObserver.labels.append(anno.Name)
t = ""
if "Face" in el:
t = str(round(e.Area,pr))
t = FreeCAD.Units.Quantity(e.Area,FreeCAD.Units.Area)
t = t.getUserPreferred()[0]
anno.LabelText = "a " + t
FreeCAD.Console.PrintMessage("Object: " + n + ", Element: " + el + ", Area: "+ t + "\n")
elif "Edge" in el:
t = str(round(e.Length,pr))
t = FreeCAD.Units.Quantity(e.Length,FreeCAD.Units.Length)
t = t.getUserPreferred()[0]
anno.LabelText = "l " + t
FreeCAD.Console.PrintMessage("Object: " + n + ", Element: " + el + ", Length: " + t + "\n")
elif "Vertex" in el:
t = str(round(e.Z,pr))
t = FreeCAD.Units.Quantity(e.Z,FreeCAD.Units.Length)
t = t.getUserPreferred()[0]
anno.LabelText = "z " + t
FreeCAD.Console.PrintMessage("Object: " + n + ", Element: " + el + ", Zcoord: " + t + "\n")
if FreeCAD.GuiUp and t:
@ -1092,31 +1094,6 @@ class _CommandSurvey:
FreeCADGui.doCommand("Arch.survey()")
class _CommandFixture:
# OBSOLETE - To be removed
"the Arch Fixture command definition"
def GetResources(self):
return {'Pixmap' : 'Arch_Fixture',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Fixture","Add fixture"),
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Fixture","Adds the selected components as fixtures to the active object")}
def IsActive(self):
if len(FreeCADGui.Selection.getSelection()) > 1:
return True
else:
return False
def Activated(self):
sel = FreeCADGui.Selection.getSelection()
FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Grouping")))
host = sel.pop()
for o in sel:
FreeCADGui.doCommand("import Arch")
FreeCADGui.doCommand("Arch.addFixture(FreeCAD.ActiveDocument."+o.Name+",FreeCAD.ActiveDocument."+host.Name+")")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Arch_Add',_CommandAdd())
FreeCADGui.addCommand('Arch_Remove',_CommandRemove())
@ -1128,4 +1105,3 @@ if FreeCAD.GuiUp:
FreeCADGui.addCommand('Arch_Check',_CommandCheck())
FreeCADGui.addCommand('Arch_IfcExplorer',_CommandIfcExplorer())
FreeCADGui.addCommand('Arch_Survey',_CommandSurvey())
#FreeCADGui.addCommand('Arch_Fixture',_CommandFixture())

View File

@ -285,6 +285,8 @@ class Component:
obj.addProperty("App::PropertyLink","Base","Arch","The base object this component is built upon")
obj.addProperty("App::PropertyLinkList","Additions","Arch","Other shapes that are appended to this object")
obj.addProperty("App::PropertyLinkList","Subtractions","Arch","Other shapes that are subtracted from this object")
obj.addProperty("App::PropertyString","Description","Arch","An optional description for this component")
obj.addProperty("App::PropertyString","Tag","Arch","An optional tag for this component")
obj.Proxy = self
self.Type = "Component"
self.Subvolume = None
@ -317,6 +319,163 @@ class Component:
siblings.append(o)
return siblings
def getAxis(self,obj):
"Returns an open wire which is the axis of this component, if applicable"
if obj.Base:
if obj.Base.isDerivedFrom("Part::Feature"):
if obj.Base.Shape:
if (len(obj.Base.Shape.Wires) == 1) and not(obj.Base.Shape.Faces):
if not obj.Base.Shape.Wires.isClosed():
return obj.Base.Shape.copy()
elif not(obj.Base.Shape.Solids):
p1 = obj.Base.Shape.CenterOfMass
v = self.getExtrusionVector(obj)
if v:
p2 = p1.add(v)
import Part
return Part.Line(p1,p2).toShape()
else:
p1 = FreeCAD.Vector()
v = self.getExtrusionVector(obj)
if v:
p2 = p1.add(v)
import Part
return Part.Line(p1,p2).toShape()
return None
def getProfiles(self,obj,noplacement=False):
"Returns the base profile(s) of this component, if applicable"
wires = []
n,l,w,h = self.getDefaultValues(obj)
if obj.Base:
if obj.Base.isDerivedFrom("Part::Feature"):
if obj.Base.Shape:
base = obj.Base.Shape.copy()
if noplacement:
base.Placement = FreeCAD.Placement()
if not base.Solids:
if base.Faces:
return [base]
elif base.Wires:
import DraftGeomUtils,DraftVecUtils,Part
for wire in base.Wires:
dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(n)
if not DraftVecUtils.isNull(dvec):
dvec.normalize()
sh = None
if hasattr(obj,"Align"):
if obj.Align == "Left":
dvec.multiply(w)
if hasattr(obj,"Offset"):
if obj.Offset.Value:
dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value)
wire = DraftGeomUtils.offsetWire(wire,dvec2)
w2 = DraftGeomUtils.offsetWire(wire,dvec)
w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges))
sh = DraftGeomUtils.bind(w1,w2)
elif obj.Align == "Right":
dvec.multiply(w)
dvec = dvec.negative()
if hasattr(obj,"Offset"):
if obj.Offset.Value:
dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value)
wire = DraftGeomUtils.offsetWire(wire,dvec2)
w2 = DraftGeomUtils.offsetWire(wire,dvec)
w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges))
sh = DraftGeomUtils.bind(w1,w2)
elif obj.Align == "Center":
dvec.multiply(w/2)
w1 = DraftGeomUtils.offsetWire(wire,dvec)
dvec = dvec.negative()
w2 = DraftGeomUtils.offsetWire(wire,dvec)
sh = DraftGeomUtils.bind(w1,w2)
if sh:
wires.append(sh)
else:
if (Draft.getType(obj) == "Structure") and (l > h):
if noplacement:
h2 = h/2 or 0.5
w2 = w/2 or 0.5
v1 = Vector(-h2,-w2,0)
v2 = Vector(h2,-w2,0)
v3 = Vector(h2,w2,0)
v4 = Vector(-h2,w2,0)
else:
h2 = h/2 or 0.5
w2 = w/2 or 0.5
v1 = Vector(0,-w2,-h2)
v2 = Vector(0,-w2,h2)
v3 = Vector(0,w2,h2)
v4 = Vector(0,w2,-h2)
else:
l2 = l/2 or 0.5
w2 = w/2 or 0.5
v1 = Vector(-l2,-w2,0)
v2 = Vector(l2,-w2,0)
v3 = Vector(l2,w2,0)
v4 = Vector(-l2,w2,0)
import Part
base = Part.makePolygon([v1,v2,v3,v4,v1])
return [base]
return wires
def getExtrusionVector(self,obj,noplacement=False):
"Returns an extrusion vector of this component, if applicable"
n,l,w,h = self.getDefaultValues(obj)
if Draft.getType(obj) == "Structure":
if l > h:
v = n.multiply(l)
if noplacement:
import DraftVecUtils
v = DraftVecUtils.rounded(FreeCAD.Rotation(FreeCAD.Vector(0,1,0),-90).multVec(v))
return v
return n.multiply(h)
def getDefaultValues(self,obj):
"returns normal,length,width,height values from this component"
length = 0
if hasattr(obj,"Length"):
if obj.Length.Value:
length = obj.Length.Value
width = 0
if hasattr(obj,"Width"):
if obj.Width.Value:
width = obj.Width.Value
height = 0
if hasattr(obj,"Height"):
if obj.Height.Value:
height = obj.Height.Value
else:
for p in obj.InList:
if Draft.getType(p) == "Floor":
if p.Height.Value:
height = p.Height.Value
default = Vector(0,0,1)
if Draft.getType(obj) == "Structure":
if length > height:
default = Vector(1,0,0)
if hasattr(obj,"Normal"):
if obj.Normal == Vector(0,0,0):
normal = default
else:
normal = Vector(obj.Normal)
else:
normal = default
return normal,length,width,height
def getPlacement(self,obj):
"returns a total placement for the profile of this component"
p = FreeCAD.Placement()
if obj.Base:
p = obj.Base.Placement.multiply(p)
else:
if Draft.getType(obj) == "Structure":
n,l,w,h = self.getDefaultValues(obj)
if l > h:
p.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90)
p = obj.Placement.multiply(p)
return p
def hideSubobjects(self,obj,prop):
"Hides subobjects when a subobject lists change"
if prop in ["Additions","Subtractions"]:
@ -328,17 +487,17 @@ class Component:
continue
o.ViewObject.hide()
def processSubShapes(self,obj,base,pl=None):
def processSubShapes(self,obj,base,placement=None):
"Adds additions and subtractions to a base shape"
import Draft,Part
#print "Processing subshapes of ",obj.Label, " : ",obj.Additions
if pl:
if pl.isNull():
pl = None
if placement:
if placement.isNull():
placement = None
else:
pl = FreeCAD.Placement(pl)
pl = pl.inverse()
placement = FreeCAD.Placement(placement)
placement = placement.inverse()
# treat additions
for o in obj.Additions:
@ -356,16 +515,16 @@ class Component:
js = ArchWall.mergeShapes(o,obj)
if js:
add = js.cut(base)
if pl:
add.Placement = add.Placement.multiply(pl)
if placement:
add.Placement = add.Placement.multiply(placement)
base = base.fuse(add)
elif (Draft.getType(o) == "Window") or (Draft.isClone(o,"Window")):
f = o.Proxy.getSubVolume(o)
if f:
if base.Solids and f.Solids:
if pl:
f.Placement = f.Placement.multiply(pl)
if placemen:
f.Placement = f.Placement.multiply(placement)
base = base.cut(f)
elif o.isDerivedFrom("Part::Feature"):
@ -373,8 +532,8 @@ class Component:
if not o.Shape.isNull():
if o.Shape.Solids:
s = o.Shape.copy()
if pl:
s.Placement = s.Placement.multiply(pl)
if placement:
s.Placement = s.Placement.multiply(placement)
if base:
if base.Solids:
try:
@ -397,8 +556,8 @@ class Component:
f = o.Proxy.getSubVolume(o)
if f:
if base.Solids and f.Solids:
if pl:
f.Placement = f.Placement.multiply(pl)
if placement:
f.Placement = f.Placement.multiply(placement)
base = base.cut(f)
elif (Draft.getType(o) == "Roof") or (Draft.isClone(o,"Roof")):
@ -413,13 +572,29 @@ class Component:
if not o.Shape.isNull():
if o.Shape.Solids and base.Solids:
s = o.Shape.copy()
if pl:
s.Placement = s.Placement.multiply(pl)
if placement:
s.Placement = s.Placement.multiply(placement)
try:
base = base.cut(s)
except:
print "Arch: unable to cut object ",o.Name, " from ", obj.Name
return base
def applyShape(self,obj,shape,placement):
"checks and cleans the given shape, and apply it to the object"
if shape:
if not shape.isNull():
if shape.isValid() and shape.Solids:
if shape.Volume < 0:
shape.reverse()
if shape.Volume < 0:
FreeCAD.Console.PrintError(translate("Arch","Error computing the shape of this object"))
return
shape = shape.removeSplitter()
obj.Shape = shape
if not placement.isNull():
obj.Placement = placement
class ViewProviderComponent:
"A default View Provider for Component objects"

View File

@ -35,6 +35,8 @@ __title__="FreeCAD Arch Frame"
__author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
# Possible roles for frames
Roles = ['Covering','Member','Railing','Shading Device','Tendon']
def makeFrame(base,profile,name=translate("Arch","Frame")):
"""makeFrame(base,profile,[name]): creates a frame object from a base sketch (or any other object
@ -75,7 +77,9 @@ class _Frame(ArchComponent.Component):
obj.addProperty("App::PropertyBool","Align","Arch","Specifies if the profile must be aligned with the extrusion wires")
obj.addProperty("App::PropertyVector","Offset","Arch","An offset vector between the base sketch and the frame")
obj.addProperty("App::PropertyAngle","Rotation","Arch","The rotation of the profile around its extrusion axis")
obj.addProperty("App::PropertyEnumeration","Role","Arch","The role of this wall")
self.Type = "Frame"
obj.Role = Roles
def execute(self,obj):
if not obj.Base:

View File

@ -41,7 +41,7 @@ if FreeCAD.GuiUp:
QtCore.QT_TRANSLATE_NOOP("Arch","Steel")
# Possible roles for structural elements
Roles = ["Beam","Column","Slab","Wall","Containment wall","Roof","Foundation"]
Roles = ["Beam","Column","Slab","Wall","Curtain Wall","Roof","Foundation","Pile","Tendon"]
# Presets in the form: Class, Name, Width, Height, [Web thickness, Flange thickness]
Presets = [None,
@ -287,11 +287,12 @@ Presets = [None,
]
def makeStructure(baseobj=None,length=0,width=0,height=0,name=translate("Arch","Structure")):
def makeStructure(baseobj=None,length=None,width=None,height=None,name=translate("Arch","Structure")):
'''makeStructure([obj],[length],[width],[heigth],[swap]): creates a
structure element based on the given profile object and the given
extrusion height. If no base object is given, you can also specify
length and width for a cubic object.'''
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
_Structure(obj)
_ViewProviderStructure(obj.ViewObject)
@ -300,10 +301,19 @@ def makeStructure(baseobj=None,length=0,width=0,height=0,name=translate("Arch","
obj.Base.ViewObject.hide()
if width:
obj.Width = width
else:
obj.Width = p.GetFloat("StructureWidth",100)
if height:
obj.Height = height
else:
obj.Height = p.GetFloat("StructureHeight",1000)
if length:
obj.Length = length
else:
if not baseobj:
# don't set the length if we have a base object, otherwise the lenght X height calc
# gets wrong
obj.Length = p.GetFloat("StructureLength",100)
if height > length:
obj.Role = "Column"
obj.ViewObject.ShapeColor = ArchCommands.getDefaultColor("Structure")
@ -362,7 +372,8 @@ class _CommandStructure:
self.Profile = 0
self.continueCmd = False
self.DECIMALS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2)
self.FORMAT = "%." + str(self.DECIMALS) + "f mm"
import DraftGui
self.FORMAT = DraftGui.makeFormatSpec(self.DECIMALS,'Length')
sel = FreeCADGui.Selection.getSelection()
if sel:
st = Draft.getObjectsOfType(sel,"Structure")
@ -412,6 +423,7 @@ class _CommandStructure:
# horizontal
FreeCADGui.doCommand('s = Arch.makeStructure(p,height='+str(self.Length)+')')
FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)')
FreeCADGui.doCommand('s.Profile = "'+pr[1]+'"')
else:
FreeCADGui.doCommand('s = Arch.makeStructure(length='+str(self.Length)+',width='+str(self.Width)+',height='+str(self.Height)+')')
FreeCADGui.doCommand('s.Placement.Base = '+DraftVecUtils.toString(point))
@ -537,10 +549,8 @@ class _Structure(ArchComponent.Component):
obj.addProperty("App::PropertyVector","Normal","Arch",translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))
obj.addProperty("App::PropertyEnumeration","Role","Arch",translate("Arch","The role of this structural element"))
obj.addProperty("App::PropertyVectorList","Nodes","Arch",translate("Arch","The structural nodes of this element"))
obj.addProperty("App::PropertyString","Profile","Arch","A description of the standard profile this element is based upon")
self.Type = "Structure"
obj.Length = 1
obj.Width = 1
obj.Height = 1
obj.Role = Roles
def execute(self,obj):
@ -548,24 +558,7 @@ class _Structure(ArchComponent.Component):
import Part, DraftGeomUtils
# getting default values
length = 1
width = 1
height = 1
if hasattr(obj,"Length"):
if obj.Length.Value:
length = obj.Length.Value
if hasattr(obj,"Width"):
if obj.Width.Value:
width = obj.Width.Value
if hasattr(obj,"Height"):
if obj.Height.Value:
height = obj.Height.Value
else:
for p in obj.InList:
if Draft.getType(p) == "Floor":
if p.Height.Value:
height = p.Height.Value
normal,length,width,height = self.getDefaultValues(obj)
# creating base shape
pl = obj.Placement
@ -574,6 +567,8 @@ class _Structure(ArchComponent.Component):
if obj.Base.isDerivedFrom("Part::Feature"):
if obj.Base.Shape.isNull():
return
if not obj.Base.Shape.isValid():
return
if hasattr(obj,"Tool"):
if obj.Tool:
try:
@ -582,9 +577,11 @@ class _Structure(ArchComponent.Component):
FreeCAD.Console.PrintError(translate("Arch","Error: The base shape couldn't be extruded along this tool object"))
return
if not base:
if not height:
return
if obj.Normal == Vector(0,0,0):
p = FreeCAD.Placement(obj.Base.Placement)
normal = p.Rotation.multVec(Vector(0,0,1))
normal = p.Rotation.multVec(normal)
else:
normal = Vector(obj.Normal)
normal = normal.multiply(height)
@ -592,64 +589,33 @@ class _Structure(ArchComponent.Component):
if base.Solids:
pass
elif base.Faces:
self.BaseProfile = base
self.ExtrusionVector = normal
base = base.extrude(normal)
elif (len(base.Wires) == 1):
if base.Wires[0].isClosed():
base = Part.Face(base.Wires[0])
self.BaseProfile = base
self.ExtrusionVector = normal
base = base.extrude(normal)
elif obj.Base.isDerivedFrom("Mesh::Feature"):
if obj.Base.Mesh.isSolid():
if obj.Base.Mesh.countComponents() == 1:
sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh)
if sh.isClosed() and sh.isValid() and sh.Solids:
if sh.isClosed() and sh.isValid() and sh.Solids and (not sh.isNull()):
base = sh
else:
FreeCAD.Console.PrintWarning(str(translate("Arch","This mesh is an invalid solid")))
obj.Base.ViewObject.show()
else:
if obj.Normal == Vector(0,0,0):
base = self.getProfiles(obj)
if base:
if length > height:
normal = Vector(1,0,0).multiply(length)
normal = normal.multiply(length)
else:
normal = Vector(0,0,1).multiply(height)
else:
normal = Vector(obj.Normal).multiply(height)
self.ExtrusionVector = normal
if length > height:
h2 = height/2 or 0.5
w2 = width/2 or 0.5
v1 = Vector(0,-w2,-h2)
v2 = Vector(0,-w2,h2)
v3 = Vector(0,w2,h2)
v4 = Vector(0,w2,-h2)
else:
l2 = length/2 or 0.5
w2 = width/2 or 0.5
v1 = Vector(-l2,-w2,0)
v2 = Vector(l2,-w2,0)
v3 = Vector(l2,w2,0)
v4 = Vector(-l2,w2,0)
base = Part.makePolygon([v1,v2,v3,v4,v1])
base = Part.Face(base)
self.BaseProfile = base
base = base.extrude(self.ExtrusionVector)
normal = normal.multiply(height)
base = Part.Face(base[0])
base = base.extrude(normal)
base = self.processSubShapes(obj,base,pl)
if base:
if not base.isNull():
if base.isValid() and base.Solids:
if base.Volume < 0:
base.reverse()
if base.Volume < 0:
FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape"))
return
base = base.removeSplitter()
obj.Shape = base
if not pl.isNull():
obj.Placement = pl
self.applyShape(obj,base,pl)
def onChanged(self,obj,prop):
self.hideSubobjects(obj,prop)
@ -665,16 +631,14 @@ class _Structure(ArchComponent.Component):
else:
# nodes haven't been calculated yet, but are set (file load)
# we calculate the nodes now but don't change the property
if hasattr(self,"BaseProfile") and hasattr(self,"ExtrusionVector"):
p1 = self.BaseProfile.CenterOfMass
p2 = p1.add(self.ExtrusionVector)
self.nodes = [p1,p2]
axis = self.getAxis(obj)
if axis:
self.nodes = [v.Point for v in axis.Vertexes]
return
if hasattr(self,"BaseProfile") and hasattr(self,"ExtrusionVector"):
p1 = self.BaseProfile.CenterOfMass
p2 = p1.add(self.ExtrusionVector)
self.nodes = [p1,p2]
#print "calculating nodes: ",self.nodes
# we calculate and set the nodes
axis = self.getAxis(obj)
if axis:
self.nodes = [v.Point for v in axis.Vertexes]
obj.Nodes = self.nodes

View File

@ -35,6 +35,9 @@ __title__="FreeCAD Wall"
__author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
# Possible roles for walls
Roles = ['Wall','Wall Layer','Beam','Column','Curtain Wall']
def makeWall(baseobj=None,length=None,width=None,height=None,align="Center",face=None,name=translate("Arch","Wall")):
'''makeWall([obj],[length],[width],[height],[align],[face],[name]): creates a wall based on the
given object, which can be a sketch, a draft object, a face or a solid, or no object at
@ -157,7 +160,8 @@ class _CommandWall:
self.JOIN_WALLS_SKETCHES = p.GetBool("joinWallSketches",False)
self.AUTOJOIN = p.GetBool("autoJoinWalls",True)
self.DECIMALS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2)
self.FORMAT = "%." + str(self.DECIMALS) + "f mm"
import DraftGui
self.FORMAT = DraftGui.makeFormatSpec(self.DECIMALS,'Length')
sel = FreeCADGui.Selection.getSelectionEx()
done = False
self.existing = []
@ -387,15 +391,12 @@ class _Wall(ArchComponent.Component):
obj.addProperty("App::PropertyLength","Height","Arch",translate("Arch","The height of this wall. Keep 0 for automatic. Not used if this wall is based on a solid"))
obj.addProperty("App::PropertyEnumeration","Align","Arch",translate("Arch","The alignment of this wall on its base object, if applicable"))
obj.addProperty("App::PropertyVector","Normal","Arch",translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))
obj.addProperty("App::PropertyBool","ForceWire","Arch",translate("Arch","If True, if this wall is based on a face, it will use its border wire as trace, and disconsider the face."))
obj.addProperty("App::PropertyInteger","Face","Arch",translate("Arch","The face number of the base object used to build this wall"))
obj.addProperty("App::PropertyLength","Offset","Arch",translate("Arch","The offset between this wall and its baseline (only for left and right alignments)"))
obj.addProperty("App::PropertyEnumeration","Role","Arch",translate("Arch","The role of this wall"))
obj.Align = ['Left','Right','Center']
obj.ForceWire = False
obj.Role = Roles
self.Type = "Wall"
obj.Width = 1
obj.Height = 1
obj.Length = 1
def execute(self,obj):
"builds the wall shape"
@ -404,62 +405,50 @@ class _Wall(ArchComponent.Component):
pl = obj.Placement
normal,length,width,height = self.getDefaultValues(obj)
base = None
# computing a shape from scratch
if not obj.Base:
if length and width and height:
base = Part.makeBox(length,width,height)
else:
FreeCAD.Console.PrintError(str(translate("Arch","Error: Unable to compute a base shape")))
return
else:
face = None
if obj.Base:
# computing a shape from a base object
if obj.Base.isDerivedFrom("Part::Feature"):
if not obj.Base.Shape.isNull():
if obj.Base.Shape.isValid():
base = obj.Base.Shape.copy()
face = None
if hasattr(obj,"Face"):
if obj.Face:
if len(base.Faces) >= obj.Face:
face = base.Faces[obj.Face-1]
if face:
# wall is based on a face
normal = face.normalAt(0,0)
if normal.getAngle(Vector(0,0,1)) > math.pi/4:
normal.multiply(width)
base = face.extrude(normal)
if obj.Align == "Center":
base.translate(normal.negative().multiply(0.5))
elif obj.Align == "Right":
base.translate(normal.negative())
else:
normal.multiply(height)
base = face.extrude(normal)
elif base.Solids:
pass
elif (len(base.Faces) == 1) and (not obj.ForceWire):
if height:
normal.multiply(height)
base = base.extrude(normal)
elif len(base.Wires) >= 1:
temp = None
for wire in obj.Base.Shape.Wires:
sh = self.getBase(obj,wire,normal,width,height)
if temp:
temp = temp.fuse(sh)
else:
temp = sh
base = temp
elif base.Edges:
wire = Part.Wire(base.Edges)
if wire:
sh = self.getBase(obj,wire,normal,width,height)
if sh:
base = sh
else:
base = None
FreeCAD.Console.PrintError(str(translate("Arch","Error: Invalid base object")))
if obj.Base.Shape.isNull():
return
if not obj.Base.Shape.isValid():
return
if hasattr(obj,"Face"):
if obj.Face > 0:
if len(obj.Base.Shape.Faces) >= obj.Face:
face = obj.Base.Shape.Faces[obj.Face-1]
if face:
# case 1: this wall is based on a specific face of its base object
normal = face.normalAt(0,0)
if normal.getAngle(Vector(0,0,1)) > math.pi/4:
normal.multiply(width)
base = face.extrude(normal)
if obj.Align == "Center":
base.translate(normal.negative().multiply(0.5))
elif obj.Align == "Right":
base.translate(normal.negative())
else:
normal.multiply(height)
base = face.extrude(normal)
elif obj.Base.Shape.Solids:
# case 2: the base is already a solid
base = obj.Base.Shape.copy()
elif obj.Base.Shape.Edges:
# case 3: the base is flat, we need to extrude it
profiles = self.getProfiles(obj)
normal.multiply(height)
base = profiles.pop()
base.fix(0.1,0,1)
base = base.extrude(normal)
for p in profiles:
p.fix(0.1,0,1)
p = p.extrude(normal)
base = base.fuse(p)
else:
base = None
FreeCAD.Console.PrintError(str(translate("Arch","Error: Invalid base object")))
elif obj.Base.isDerivedFrom("Mesh::Feature"):
if obj.Base.Mesh.isSolid():
@ -470,24 +459,13 @@ class _Wall(ArchComponent.Component):
else:
FreeCAD.Console.PrintWarning(str(translate("Arch","This mesh is an invalid solid")))
obj.Base.ViewObject.show()
else:
# computing a shape from scratch
if length and width and height:
base = Part.makeBox(length,width,height)
base = self.processSubShapes(obj,base,pl)
if base:
if not base.isNull():
if base.isValid() and base.Solids:
if base.Volume < 0:
base.reverse()
if base.Volume < 0:
FreeCAD.Console.PrintError(str(translate("Arch","Couldn't compute the wall shape")))
return
try:
base = base.removeSplitter()
except:
FreeCAD.Console.PrintError(str(translate("Arch","Error removing splitter from wall shape")))
obj.Shape = base
if not pl.isNull():
obj.Placement = pl
self.applyShape(obj,base,pl)
def onChanged(self,obj,prop):
self.hideSubobjects(obj,prop)
@ -503,77 +481,6 @@ class _Wall(ArchComponent.Component):
if (Draft.getType(o) == "Window") or Draft.isClone(o,"Window"):
o.Placement.move(delta)
ArchComponent.Component.onChanged(self,obj,prop)
def getDefaultValues(self,obj):
"returns normal,width,height values from this wall"
length = 1
if hasattr(obj,"Length"):
if obj.Length.Value:
length = obj.Length.Value
width = 1
if hasattr(obj,"Width"):
if obj.Width.Value:
width = obj.Width.Value
height = 1
if hasattr(obj,"Height"):
if obj.Height.Value:
height = obj.Height.Value
else:
for p in obj.InList:
if Draft.getType(p) == "Floor":
if p.Height.Value:
height = p.Height.Value
normal = None
if hasattr(obj,"Normal"):
if obj.Normal == Vector(0,0,0):
normal = Vector(0,0,1)
else:
normal = Vector(obj.Normal)
else:
normal = Vector(0,0,1)
return normal,length,width,height
def getBase(self,obj,wire,normal,width,height):
"returns a full shape from a base wire"
import DraftGeomUtils,Part
flat = False
if hasattr(obj.ViewObject,"DisplayMode"):
flat = (obj.ViewObject.DisplayMode == "Flat 2D")
dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal)
if not DraftVecUtils.isNull(dvec):
dvec.normalize()
if obj.Align == "Left":
dvec.multiply(width)
if hasattr(obj,"Offset"):
if obj.Offset.Value:
dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value)
wire = DraftGeomUtils.offsetWire(wire,dvec2)
w2 = DraftGeomUtils.offsetWire(wire,dvec)
w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges))
sh = DraftGeomUtils.bind(w1,w2)
elif obj.Align == "Right":
dvec.multiply(width)
dvec = dvec.negative()
if hasattr(obj,"Offset"):
if obj.Offset.Value:
dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value)
wire = DraftGeomUtils.offsetWire(wire,dvec2)
w2 = DraftGeomUtils.offsetWire(wire,dvec)
w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges))
sh = DraftGeomUtils.bind(w1,w2)
elif obj.Align == "Center":
dvec.multiply(width/2)
w1 = DraftGeomUtils.offsetWire(wire,dvec)
dvec = dvec.negative()
w2 = DraftGeomUtils.offsetWire(wire,dvec)
sh = DraftGeomUtils.bind(w1,w2)
# fixing self-intersections
sh.fix(0.1,0,1)
self.BaseProfile = sh
if height and (not flat):
self.ExtrusionVector = Vector(normal).multiply(height)
sh = sh.extrude(self.ExtrusionVector)
return sh
class _ViewProviderWall(ArchComponent.ViewProviderComponent):

View File

@ -383,7 +383,8 @@ class _CommandWindow:
self.baseFace = None
self.wparams = ["Width","Height","H1","H2","H3","W1","W2","O1","O2"]
self.DECIMALS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2)
self.FORMAT = "%." + str(self.DECIMALS) + "f mm"
import DraftGui
self.FORMAT = DraftGui.makeFormatSpec(self.DECIMALS,'Length')
# auto mode
if sel:
@ -810,7 +811,8 @@ class _ArchWindowTaskPanel:
self.obj = None
self.DECIMALS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2)
self.FORMAT = "%." + str(self.DECIMALS) + "f mm"
import DraftGui
self.FORMAT = DraftGui.makeFormatSpec(self.DECIMALS,'Length')
self.form = QtGui.QWidget()
self.form.setObjectName("TaskPanel")
self.grid = QtGui.QGridLayout(self.form)

File diff suppressed because one or more lines are too long

View File

@ -299,23 +299,6 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_4">
<property name="text">
<string>Aggregate windows to their host objects</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>ifcAggregateWindows</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Arch</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@ -397,14 +397,17 @@ class IfcDocument(object):
base = create(self._fileobject,"IfcBooleanResult",["UNION",base,s])
return base
def addPlacement(self,reference=None,origin=(0,0,0),xaxis=(1,0,0),zaxis=(0,0,1),local=True):
def addPlacement(self,reference=None,origin=(0,0,0),xaxis=(1,0,0),zaxis=(0,0,1),local=True,flat=False):
"""addPlacement([reference,origin,xaxis,zaxis,local]): adds a placement. origin,
xaxis and zaxis can be either tuples or 3d vectors. If local is False, a global
placement is returned, otherwise a local one."""
xvc = create(self._fileobject,"IfcDirection",getTuple(xaxis))
zvc = create(self._fileobject,"IfcDirection",getTuple(zaxis))
ovc = create(self._fileobject,"IfcCartesianPoint",getTuple(origin))
gpl = create(self._fileobject,"IfcAxis2Placement3D",[ovc,zvc,xvc])
if flat:
gpl = create(self._fileobject,"IfcAxis2Placement2D",[ovc,xvc])
else:
gpl = create(self._fileobject,"IfcAxis2Placement3D",[ovc,zvc,xvc])
if local:
lpl = create(self._fileobject,"IfcLocalPlacement",[reference,gpl])
return lpl
@ -450,72 +453,53 @@ class IfcDocument(object):
else:
create(self._fileobject,"IfcRelAggregates",[uid(),self._owner,'Relationship','',container,entities])
def addWall(self,shapes,storey=None,placement=None,name="Default wall",description=None,standard=False):
"""addWall(shapes,[storey,placement,name,description]): creates a wall from the given representation shape(s)"""
def addProduct(self,elttype,shapes,storey=None,placement=None,name="Unnamed element",description=None,extra=None):
"""addProduct(elttype,representations,[storey,placement,name,description,extra]): creates an element of the given type
(IfcWall, IfcBeam, etc...) with the given attributes, plus the given extra attributes."""
if not extra:
extra = []
if not description:
description = None
if not placement:
placement = self.addPlacement()
if not isinstance(shapes,list):
shapes = [shapes]
if standard:
solidType = "SweptSolid"
else:
solidType = "Brep"
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body',solidType,[shape]]) for shape in shapes]
prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps])
if standard:
wal = create(self._fileobject,"IfcWallStandardCase",[uid(),self._owner,name,description,None,placement,prd,None])
else:
wal = create(self._fileobject,"IfcWall",[uid(),self._owner,name,description,None,placement,prd,None])
self.BuildingProducts.append(wal)
representations = self.addRepresentations(shapes)
prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,representations])
try:
elt = create(self._fileobject,elttype,[uid(),self._owner,name,description,None,placement,prd,None]+extra)
except:
print "unable to create an ",elttype, " with attributes: ",[uid(),self._owner,name,description,None,placement,prd,None]+extra
print "supported attributes are: "
o = IfcImport.Entity(elttype)
print getPropertyNames(o)
raise
self.BuildingProducts.append(elt)
if not storey:
if self.Storeys:
storey = self.Storeys[0]
else:
storey = self.addStorey()
self._relate(storey,wal)
return wal
def addStructure(self,ifctype,shapes,storey=None,placement=None,name="Default Structure",description=None,extrusion=False):
"""addStructure(ifctype,shapes,[storey,placement,name,description]): creates a structure
from the given representation shape(s). Ifctype is the type of structural object (IfcBeam, IfcColumn, etc)"""
if not placement:
placement = self.addPlacement()
self._relate(storey,elt)
return elt
def addRepresentations(self,shapes):
"""addRepresentations(shapes,[solidType]): creates a representation from the given shape"""
solidType = "Brep"
if not isinstance(shapes,list):
if shapes.is_a("IfcExtrudedAreaSolid"):
solidType = "SweptSolid"
shapes = [shapes]
if extrusion:
solidType = "SweptSolid"
else:
solidType = "Brep"
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body',solidType,[shape]]) for shape in shapes]
prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps])
if ifctype in ["IfcSlab","IfcFooting"]:
stt = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,"NOTDEFINED"])
else:
stt = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None])
self.BuildingProducts.append(stt)
if not storey:
if self.Storeys:
storey = self.Storeys[0]
else:
storey = self.addStorey()
self._relate(storey,stt)
return stt
def addWindow(self,ifctype,width,height,shapes,host=None,placement=None,name="Default Window",description=None):
"""addWindow(ifctype,width,height,shapes,[host,placement,name,description]): creates a window
from the given representation shape(s). Ifctype is the type of window object (IfcWindow, IfcDoor, etc)"""
if not placement:
placement = self.addPlacement()
if not isinstance(shapes,list):
shapes = [shapes]
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body','SolidModel',[shape]]) for shape in shapes]
prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps])
win = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,float(height),float(width)])
self.BuildingProducts.append(win)
if host:
self._relate(host,win)
return win
return reps
def addColor(self,rgb,rep):
"""addColor(rgb,rep): adds a RGB color definition tuple (float,float,float) to a given representation"""
col = create(self._fileobject,"IfcColourRgb",[None]+list(rgb))
ssr = create(self._fileobject,"IfcSurfaceStyleRendering",[col,None,None,None,None,None,None,None,"FLAT"])
iss = create(self._fileobject,"IfcSurfaceStyle",[None,"BOTH",[ssr]])
psa = create(self._fileobject,"IfcPresentationStyleAssignment",[[iss]])
isi = create(self._fileobject,"IfcStyledItem",[rep,[psa],None])
return isi
def addPolyline(self,points):
"""addPolyline(points): creates a polyline from the given points"""
pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)) for p in points]
@ -525,7 +509,7 @@ class IfcDocument(object):
def addCircle(self,radius):
"""addCircle(radius): creates a polyline from the given points"""
lpl = self.addPlacement()
lpl = self.addPlacement(flat=True)
cir = create(self._fileobject,"IfcCircleProfileDef",["AREA",None,lpl,float(radius)])
return cir
@ -539,20 +523,24 @@ class IfcDocument(object):
solid = create(self._fileobject,"IfcExtrudedAreaSolid",[profile,placement,edir,value])
return solid
def addExtrudedPolyline(self,points,extrusion,placement=None):
"""addExtrudedPolyline(points,extrusion,[placement]): makes an extruded polyline
def addExtrudedPolyline(self,points,extrusion,placement=None,color=None):
"""addExtrudedPolyline(points,extrusion,[placement,color]): makes an extruded polyline
from the given points and the given extrusion vector"""
pol = self.addPolyline(points)
exp = self.addExtrusion(pol,extrusion,placement)
if color:
self.addColor(color,exp)
return exp
def addExtrudedCircle(self,center,radius,extrusion,placement=None):
"""addExtrudedCircle(radius,extrusion,[placement]): makes an extruded circle
def addExtrudedCircle(self,center,radius,extrusion,placement=None,color=None):
"""addExtrudedCircle(radius,extrusion,[placement,color]): makes an extruded circle
from the given radius and the given extrusion vector"""
cir = self.addCircle(radius)
if not placement:
placement = self.addPlacement(origin=center)
exp = self.addExtrusion(cir,extrusion,placement)
if color:
self.addColor(color,exp)
return exp
def addFace(self,face):
@ -584,8 +572,8 @@ class IfcDocument(object):
iface = create(self._fileobject,"IfcFace",[ifb])
return iface
def addFacetedBrep(self,faces):
"""addFacetedBrep(self,faces): creates a faceted brep object from the given list
def addFacetedBrep(self,faces,color=None):
"""addFacetedBrep(self,faces,[color]): creates a faceted brep object from the given list
of faces (each face is a list of lists of points, inner wires are reversed)"""
self.fpoints = []
self.frefs = []
@ -594,6 +582,8 @@ class IfcDocument(object):
ifaces = [self.addFace(face) for face in faces]
sh = create(self._fileobject,"IfcClosedShell",[ifaces])
brp = create(self._fileobject,"IfcFacetedBrep",[sh])
if color:
self.addColor(color,brp)
return brp
@ -641,18 +631,18 @@ def example2():
ifc.Name = "Test Project"
ifc.Owner = "Yorik van Havre"
ifc.Organization = "FreeCAD"
w1 = ifc.addWall( ifc.addExtrudedPolyline([(0,0,0),(0,200,0),(5000,200,0),(5000,0,0),(0,0,0)], (0,0,3500)) )
ifc.addWall( ifc.addExtrudedPolyline([(0,200,0),(0,2000,0),(200,2000,0),(200,200,0),(0,200,0)],(0,0,3500)) )
ifc.addWall( ifc.addExtrudedPolyline([(0,2000,0),(0,2200,0),(5000,2200,0),(5000,2000,0),(0,2000,0)],(0,0,3500)) )
ifc.addWall( ifc.addExtrudedPolyline([(5000,200,0),(5000,2000,0),(4800,2000,0),(4800,200,0),(5000,200,0)],(0,0,3500)) )
ifc.addWall( ifc.addFacetedBrep([[[(0,0,0),(100,0,0),(100,-1000,0),(0,-1000,0)]],
w1 = ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(0,0,0),(0,200,0),(5000,200,0),(5000,0,0),(0,0,0)], (0,0,3500)) )
ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(0,200,0),(0,2000,0),(200,2000,0),(200,200,0),(0,200,0)],(0,0,3500)) )
ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(0,2000,0),(0,2200,0),(5000,2200,0),(5000,2000,0),(0,2000,0)],(0,0,3500)) )
ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(5000,200,0),(5000,2000,0),(4800,2000,0),(4800,200,0),(5000,200,0)],(0,0,3500)) )
ifc.addProduct( "IfcWall", ifc.addFacetedBrep([[[(0,0,0),(100,0,0),(100,-1000,0),(0,-1000,0)]],
[[(0,0,0),(100,0,0),(100,0,1000),(0,0,1000)]],
[[(0,0,0),(0,0,1000),(0,-1000,1000),(0,-1000,0)]],
[[(0,-1000,0),(0,-1000,1000),(100,-1000,1000),(100,-1000,0)]],
[[(100,-1000,0),(100,-1000,1000),(100,0,1000),(100,0,0)]],
[[(0,0,1000),(0,-1000,1000),(100,-1000,1000),(100,0,1000)]]]) )
ifc.addStructure( "IfcColumn", ifc.addExtrudedPolyline([(0,0,0),(0,-200,0),(-500,-200,0),(-500,0,0),(0,0,0)], (0,0,3500)) )
ifc.addWindow( "IfcDoor", 200, 200, ifc.addExtrudedPolyline([(200,200,0),(200,400,0),(400,400,0),(400,200,0),(200,200,0)], (0,0,200)), w1 )
ifc.addProduct( "IfcColumn", ifc.addExtrudedPolyline([(0,0,0),(0,-200,0),(-500,-200,0),(-500,0,0),(0,0,0)], (0,0,3500)) )
ifc.addProduct( "IfcDoor", ifc.addExtrudedPolyline([(200,200,0),(200,400,0),(400,400,0),(400,200,0),(200,200,0)], (0,0,200)), w1, [200, 200] )
ifc.write()
print dir(ifc._fileobject)

View File

@ -30,13 +30,23 @@ __url__ = "http://www.freecadweb.org"
# config
subtractiveTypes = ["IfcOpeningElement"] # elements that must be subtracted from their parents
SCHEMA = "http://www.steptools.com/support/stdev_docs/express/ifc2x3/ifc2x3_tc1.exp" # only for internal prser
SCHEMA = "http://www.steptools.com/support/stdev_docs/ifcbim/ifc4.exp" # only for internal prser
MAKETEMPFILES = False # if True, shapes are passed from ifcopenshell to freecad through temp files
DEBUG = True # this is only for the python console, this value is overridden when importing through the GUI
SKIP = ["IfcBuildingElementProxy","IfcFlowTerminal","IfcFurnishingElement"] # default. overwritten by the GUI options
TOUCH = True # arch objects based on profiles need to be reexecuted after loading the file (this is temporary)
# end config
# supported ifc products (export only):
supportedIfcTypes = ["IfcSite", "IfcBuilding", "IfcBuildingStorey", "IfcBeam", "IfcBeamStandardCase",
"IfcChimney", "IfcColumn", "IfcColumnStandardCase", "IfcCovering", "IfcCurtainWall",
"IfcDoor", "IfcDoorStandardCase", "IfcMember", "IfcMemberStandardCase", "IfcPlate",
"IfcPlateStandardCase", "IfcRailing", "IfcRamp", "IfcRampFlight", "IfcRoof",
"IfcSlab", "IfcStair", "IfcStairFlight", "IfcWall",
"IfcWallStandardCase", "IfcWindow", "IfcWindowStandardCase", "IfcBuildingElementProxy",
"IfcPile", "IfcFooting", "IfcReinforcingBar", "IfcTendon"]
# TODO : shading device not supported?
if open.__module__ == '__builtin__':
pyopen = open # because we'll redefine open below
@ -915,12 +925,14 @@ def export(exportList,filename):
try:
import IfcImport
except:
FreeCAD.Console.PrintError(translate("Arch","Error: IfcOpenShell is not installed\n"))
print """importIFC: ifcOpenShell is not installed. IFC export is unavailable.
Note: IFC export currently requires an experimental version of IfcOpenShell
available from https://github.com/aothms/IfcOpenShell"""
return
else:
if not hasattr(IfcImport,"IfcFile"):
FreeCAD.Console.PrintError(translate("Arch","Error: your IfcOpenShell version is too old\n"))
print """importIFC: The version of ifcOpenShell installed on this system doesn't
have IFC export capabilities. IFC export currently requires an experimental
version of IfcOpenShell available from https://github.com/aothms/IfcOpenShell"""
@ -948,12 +960,6 @@ def export(exportList,filename):
objectslist = Draft.getGroupContents(exportList,walls=True,addgroups=True)
objectslist = Arch.pruneIncluded(objectslist)
# workaround: loaded objects loose their profile info - needs to recompute...
if TOUCH:
for o in objectslist:
if Draft.getType(o) in ["Wall", "Structure"]:
o.Proxy.execute(o)
buildings = []
floors = []
others = []
@ -970,164 +976,112 @@ def export(exportList,filename):
# process objects
for obj in objectslist:
otype = Draft.getType(obj)
name = str(obj.Label)
parent = Arch.getHost(obj)
gdata = None
if otype in ["Group"]:
# unsupported objects. TODO: support
continue
if DEBUG: print "adding ",obj.Label
# getting geometry
if not forcebrep:
gdata = Arch.getExtrusionData(obj,scaling)
#if DEBUG: print "extrusion data for ",obj.Label," : ",gdata
if not gdata:
fdata = Arch.getBrepFacesData(obj,scaling)
#if DEBUG: print "brep data for ",obj.Label," : ",fdata
if not fdata:
if obj.isDerivedFrom("Part::Feature"):
print "IFC export: error retrieving the shape of object ", obj.Name
continue
else:
if DEBUG: print " No geometry"
else:
if DEBUG: print " Brep"
fdata = None
placement = None
color = None
representation = None
descr = None
extra = None
# setting the IFC type
if hasattr(obj,"Role"):
ifctype = obj.Role.replace(" ","")
else:
if DEBUG: print " Extrusion"
ifctype = otype
if ifctype == "Foundation":
ifctype = "Footing"
elif ifctype == "Rebar":
ifctype = "ReinforcingBar"
# compute final placement
basepoint = None
if obj.isDerivedFrom("Part::Feature"):
b1 = None
b2 = None
if hasattr(obj,"Base"):
if obj.Base:
b1 = FreeCAD.Vector(obj.Base.Placement.Base).multiply(scaling)
if hasattr(obj,"Placement"):
b2 = FreeCAD.Vector(obj.Placement.Base).multiply(scaling)
if b2:
if b1:
basepoint = b2.add(b1)
else:
basepoint = b2
elif b1:
basepoint = b1
if basepoint:
basepoint = Arch.getTuples(basepoint)
if DEBUG: print "adding " + obj.Label + " as Ifc" + ifctype
# writing text log
spacer = ""
for i in range(36-len(obj.Label)):
spacer += " "
if otype in ["Structure","Window"]:
if hasattr(obj,"Role"):
tp = obj.Role
else:
tp = otype
else:
tp = otype
txt.append(obj.Label + spacer + tp)
txt.append(obj.Label + spacer + ifctype)
# writing IFC data
if otype == "Building":
ifc.addBuilding( name=name )
# writing IFC data
if obj.isDerivedFrom("App::DocumentObjectGroup"):
elif otype == "Floor":
# getting parent building
if parent:
parent = ifc.findByName("IfcBuilding",str(parent.Label))
ifc.addStorey( building=parent, name=name )
elif otype == "Wall":
if parent:
parent = ifc.findByName("IfcBuildingStorey",str(parent.Label))
if gdata:
if gdata[0] == "polyline":
ifc.addWall( ifc.addExtrudedPolyline(gdata[1], gdata[2]), storey=parent, name=name, standard=True )
elif gdata[0] == "circle":
ifc.addWall( ifc.addExtrudedCircle(gdata[1], gdata[2], gdata[3]), storey=parent, name=name )
elif fdata:
if JOINSOLIDS:
ifc.addWall( ifc.join([ifc.addFacetedBrep(f) for f in fdata]), storey=parent, name=name )
else:
ifc.addWall( [ifc.addFacetedBrep(f) for f in fdata], storey=parent, name=name )
elif otype == "Structure":
placement = None
if parent:
parent = ifc.findByName("IfcBuildingStorey",str(parent.Label))
role = "IfcBeam"
if hasattr(obj,"Role"):
if obj.Role == "Column":
role = "IfcColumn"
elif obj.Role == "Slab":
role = "IfcSlab"
elif obj.Role == "Foundation":
role = "IfcFooting"
if gdata:
if gdata[0] == "polyline":
#ifc.addStructure( role, ifc.addExtrudedPolyline(gdata[0],gdata[1]), storey=parent, name=name )
if FreeCAD.Vector(gdata[2]).getAngle(FreeCAD.Vector(0,0,1)) < .01:
# the placement of polylines is weird... The Z values from the polyline info is not used,
# so we need to give it now as a placement.
if basepoint:
if round(basepoint[2],Draft.precision()) != 0:
placement = ifc.addPlacement(origin=(0.0,0.0,basepoint[2]))
ifc.addStructure( role, ifc.addExtrudedPolyline(gdata[1],gdata[2]), storey=parent, placement=placement, name=name, extrusion=True )
else:
# Workaround for non-Z extrusions, apparently not supported by ifc++ TODO: fix this
print " switching to Brep"
fdata = Arch.getBrepFacesData(obj,scaling)
ifc.addStructure( role, [ifc.addFacetedBrep(f) for f in fdata], storey=parent, name=name )
elif gdata[0] == "circle":
if basepoint:
placement = ifc.addPlacement(origin=basepoint)
ifc.addStructure( role, ifc.addExtrudedCircle(gdata[1], gdata[2], gdata[3]), storey=parent, placement=placement, name=name, extrusion=True )
elif fdata:
if JOINSOLIDS:
ifc.addStructure( role, ifc.join([ifc.addFacetedBrep(f) for f in fdata]), storey=parent, name=name )
else:
ifc.addStructure( role, [ifc.addFacetedBrep(f) for f in fdata], storey=parent, name=name )
elif otype == "Window":
if AGGREGATE_WINDOWS:
if parent:
p = ifc.findByName("IfcWallStandardCase",str(parent.Label))
if not p:
p = ifc.findByName("IfcWall",str(parent.Label))
if not p:
p = ifc.findByName("IfcColumn",str(parent.Label))
if not p:
p = ifc.findByName("IfcBeam",str(parent.Label))
if not p:
p = ifc.findByName("IfcSlab",str(parent.Label))
parent = p
if otype == "Building":
ifc.addBuilding( name=name )
elif otype == "Floor":
ifc.addStorey( building=parent, name=name )
else:
if parent:
p = Arch.getHost(parent)
if p:
parent = ifc.findByName("IfcBuildingStorey",str(p.Label))
else:
parent = None
print " Skipping (not implemented yet)"
role = "IfcWindow"
if hasattr(obj,"Role"):
if obj.Role == "Door":
role = "IfcDoor"
elif obj.isDerivedFrom("Part::Feature"):
# get color
if FreeCAD.GuiUp:
color = obj.ViewObject.ShapeColor[:3]
# get parent floor
if parent:
parent = ifc.findByName("IfcBuildingStorey",str(parent.Label))
# get representation
if not forcebrep:
gdata = Arch.getExtrusionData(obj,scaling)
#if DEBUG: print " extrusion data for ",obj.Label," : ",gdata
if not gdata:
fdata = Arch.getBrepFacesData(obj,scaling)
#if DEBUG: print " brep data for ",obj.Label," : ",fdata
if not fdata:
if obj.isDerivedFrom("Part::Feature"):
print "IFC export: error retrieving the shape of object ", obj.Name
continue
else:
if DEBUG: print " No geometry"
else:
if DEBUG: print " Brep"
else:
if DEBUG: print " Extrusion"
if gdata:
placement = ifc.addPlacement(origin=gdata[3][0],xaxis=gdata[3][1],zaxis=gdata[3][2])
if gdata[0] == "polyline":
ifc.addWindow( role, obj.Width*scaling, obj.Height*scaling, ifc.addExtrudedPolyline(gdata[1], gdata[2]), host=parent, name=name )
representation = ifc.addExtrudedPolyline(gdata[1], gdata[2], color=color)
elif gdata[0] == "circle":
representation = ifc.addExtrudedCircle(gdata[1][0], gdata[1][1], gdata[2], color=color)
else:
print "debug: unknow extrusion type"
elif fdata:
if JOINSOLIDS:
ifc.addWindow( role, obj.Width*scaling, obj.Height*scaling, ifc.union([ifc.addFacetedBrep(f) for f in fdata]), host=parent, name=name )
representation = ifc.join([ifc.addFacetedBrep(f, color=color) for f in fdata])
else:
ifc.addWindow( role, obj.Width*scaling, obj.Height*scaling, [ifc.addFacetedBrep(f) for f in fdata], host=parent, name=name )
representation = [ifc.addFacetedBrep(f, color=color) for f in fdata]
# create ifc object
ifctype = "Ifc" + ifctype
if hasattr(obj,"Description"):
descr = obj.Description
if otype == "Wall":
if gdata:
if gdata[0] == "polyline":
ifctype = "IfcWallStandardCase"
elif otype == "Structure":
if ifctype in ["IfcSlab","IfcFooting"]:
extra = ["NOTDEFINED"]
elif otype == "Window":
extra = [obj.Width.Value*scaling, obj.Height.Value*scaling]
if not ifctype in supportedIfcTypes:
if DEBUG: print " Type ",ifctype," is not supported by the current version of IfcOpenShell. Exporting as IfcBuildingElementProxy instead"
ifctype = "IfcBuildingElementProxy"
extra = ["ELEMENT"]
ifc.addProduct( ifctype, representation, storey=parent, placement=placement, name=name, description=descr, extra=extra )
else:
print "IFC export: object type ", otype, " is not supported yet."
if DEBUG: print "IFC export: object type ", otype, " is not supported yet."
ifc.write()
@ -1169,4 +1123,4 @@ def explore(filename=None):
ifcReader.DEBUG = DEBUG
d = ifcReader.explorer(filename,schema)
d.show()
return d
return d

View File

@ -326,7 +326,7 @@ def getGroupContents(objectslist,walls=False,addgroups=False):
#print "adding ",obj.Name
newlist.append(obj)
if walls:
if getType(obj) == "Wall":
if getType(obj) in ["Wall","Structure"]:
for o in obj.OutList:
if (getType(o) == "Window") or isClone(o,"Window"):
newlist.append(o)

View File

@ -168,6 +168,8 @@ def getRotation(vector,reference=Vector(1,0,0)):
representing a quaternion rotation between the reference
(or X axis if omitted) and the vector'''
c = vector.cross(reference)
if isNull(c):
return (0,0,0,1.0)
c.normalize()
return (c.x,c.y,c.z,math.sqrt((vector.Length ** 2) * (reference.Length ** 2)) + vector.dot(reference))