Arch: Support for extruded curves in IFC export

This commit is contained in:
Yorik van Havre 2014-06-01 21:45:48 -03:00
parent 2724242f38
commit 5424ad1d6a
6 changed files with 136 additions and 59 deletions

View File

@ -35,6 +35,8 @@ __title__="FreeCAD Arch Commands"
__author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
CURVEMODE = "PARAMETER" # For trimmed curves. CARTESIAN or PARAMETER
# module functions ###############################################
def getStringList(objects):
@ -600,7 +602,7 @@ def getTuples(data,scale=1,placement=None,normal=None,close=True):
if isinstance(data,FreeCAD.Vector):
if placement:
data = placement.multVec(data)
data = DraftVecUtils.rounded(data)
data = DraftVecUtils.rounded(data)
return (data.x*scale,data.y*scale,data.z*scale)
elif isinstance(data,Part.Shape):
t = []
@ -622,7 +624,7 @@ def getTuples(data,scale=1,placement=None,normal=None,close=True):
if placement:
if not placement.isNull():
pt = placement.multVec(pt)
pt = DraftVecUtils.rounded(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.
@ -677,13 +679,46 @@ def getIfcExtrusionData(obj,scale=1):
if curves:
# Composite profile
ecurves = []
for e in p.Edges:
last = None
import DraftGeomUtils
edges = DraftGeomUtils.sortEdges(p.Edges)
for e in edges:
if isinstance(e.Curve,Part.Circle):
p1 = e.FirstParameter
p2 = e.LastParameter
ecurves.append(["arc",getTuples(e.Curve.Center,scale),e.Curve.Radius*scale,[p1,p2]])
import math
follow = True
if last:
if not DraftVecUtils.equals(last,e.Vertexes[0].Point):
follow = False
last = e.Vertexes[0].Point
else:
last = e.Vertexes[-1].Point
else:
last = e.Vertexes[-1].Point
p1 = math.degrees(-DraftVecUtils.angle(e.Vertexes[0].Point.sub(e.Curve.Center)))
p2 = math.degrees(-DraftVecUtils.angle(e.Vertexes[-1].Point.sub(e.Curve.Center)))
da = DraftVecUtils.angle(e.valueAt(e.FirstParameter+0.1).sub(e.Curve.Center),e.Vertexes[0].Point.sub(e.Curve.Center))
if p1 < 0:
p1 = 360 + p1
if p2 < 0:
p2 = 360 + p2
if da > 0:
follow = not(follow)
if CURVEMODE == "CARTESIAN":
# BUGGY
p1 = getTuples(e.Vertexes[0].Point,scale)
p2 = getTuples(e.Vertexes[-1].Point,scale)
ecurves.append(["arc",getTuples(e.Curve.Center,scale),e.Curve.Radius*scale,[p1,p2],follow,CURVEMODE])
else:
ecurves.append(["line",[getTuples(vt.Point,scale) for vt in e.Vertexes]])
verts = [vertex.Point for vertex in e.Vertexes]
if last:
if not DraftVecUtils.equals(last,verts[0]):
verts.reverse()
last = e.Vertexes[0].Point
else:
last = e.Vertexes[-1].Point
else:
last = e.Vertexes[-1].Point
ecurves.append(["line",[getTuples(vert,scale) for vert in verts]])
return "composite", ecurves, getTuples(v,scale), d
else:
# Polyline profile

View File

@ -356,10 +356,21 @@ class Component:
if not base.Solids:
if base.Faces:
return [base]
elif base.Wires:
basewires = []
if not base.Wires:
if len(base.Edges) == 1:
import Part
basewires = [Part.Wire(base.Edges)]
else:
basewires = base.Wires
if basewires:
import DraftGeomUtils,DraftVecUtils,Part
for wire in base.Wires:
dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(n)
for wire in basewires:
e = wire.Edges[0]
if isinstance(e.Curve,Part.Circle):
dvec = e.Vertexes[0].Point.sub(e.Curve.Center)
else:
dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(n)
if not DraftVecUtils.isNull(dvec):
dvec.normalize()
sh = None

View File

@ -440,14 +440,17 @@ class _Wall(ArchComponent.Component):
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)
if profiles:
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
else:
base = None
FreeCAD.Console.PrintError(str(translate("Arch","Error: Invalid base object")))

View File

@ -89,14 +89,18 @@ def getPropertyNames(entity):
def getTuple(vec):
"""getTuple(vec): returns a tuple from other coordinate
structures: tuple, list, 3d vector, or occ vertex"""
def fmt(t):
t = float(t)
t = round(t,PRECISION)
return t
if isinstance(vec,tuple):
return tuple([float(v) for v in vec])
return tuple([fmt(v) for v in vec])
elif isinstance(vec,list):
return tuple([float(v) for v in vec])
return tuple([fmt(v) for v in vec])
elif hasattr(vec,"x") and hasattr(vec,"y") and hasattr(vec,"z"):
return (float(vec.x),float(vec.y),float(vec.z))
return (fmt(vec.x),fmt(vec.y),fmt(vec.z))
elif hasattr(vec,"X") and hasattr(vec,"Y") and hasattr(vec,"Z"):
return (float(vec.X),float(vec.Y),float(vec.Z))
return (fmt(vec.X),fmt(vec.Y),fmt(vec.Z))
def getValueAndDirection(vec):
"""getValueAndDirection(vec): returns a length and a tuple
@ -364,9 +368,9 @@ class IfcDocument(object):
f = open(path,"rb")
lines = []
for l in f.readlines():
if "IFCPLANEANGLEMEASURE" in l:
# bug 1: adding ifcPlaneAngleMeasure in a ifcMeasureWithUnit adds an unwanted = sign
l = l.replace("=IFCPLANEANGLEMEASURE","IFCPLANEANGLEMEASURE")
if "(=IFC" in l:
# bug 1: adding an ifc entity without ID adds an unwanted = sign
l = l.replace("(=IFC","(IFC")
#elif ("FACEBOUND" in l) or ("FACEOUTERBOUND" in l): # FIXED
# bug 2: booleans are exported as ints
#l = l.replace(",1);",",.T.);")
@ -511,6 +515,15 @@ class IfcDocument(object):
def addProfile(self,ifctype,data,curvetype="AREA"):
"""addProfile(ifctype,data): creates a 2D profile of the given type, with the given
data as arguments, which must be formatted correctly according to the type."""
# Expected ifctype and corresponding data formatting:
# IfcPolyLine: [ (0,0,0), (2,1,0), (3,3,0) ] # list of points
# IfcCompositeCurve: [ ["line",[ (0,0,0), (2,1,0) ] ], # list of points
# ["arc", (0,0,0), 15, [0.76, 3.1416], True, "PARAMETER"] # center, radius, [trim1, trim2], SameSense, trimtype
# ... ]
# IfcCircleProfileDef: [ (0,0,0), 15 ] # center, radius
# IfcEllipseProfileDef: [ (0,0,0), 15, 7 ] # center, radiusX, radiusY
if ifctype == "IfcPolyline":
pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in data]
pol = create(self._fileobject,"IfcPolyline",[pts])
@ -520,17 +533,24 @@ class IfcDocument(object):
for curve in data:
cur = None
if curve[0] == "line":
cur = self.addProfile("IfcPolyline",curve[1])
pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in curve[1]]
cur = create(self._fileobject,"IfcPolyline",[pts])
elif curve[0] == "arc":
pla = self.addPlacement(origin=curve[1],local=False,flat=True)
cir = create(self._fileobject,"IfcCircle",[pla,curve[2]])
trim1 = create(None,"IfcParameterValue",[curve[3][0]])
trim2 = create(None,"IfcParameterValue",[curve[3][1]])
cur = create(self._fileobject,"IfcTrimmedCurve",[cir,[trim1],[trim2]])
if curve[5] == "CARTESIAN":
# BUGGY! Impossible to add cartesian points as "embedded" entity
trim1 = create(None,"IfcCartesianPoint",getTuple(curve[3][0])[:2])
trim2 = create(None,"IfcCartesianPoint",getTuple(curve[3][1])[:2])
else:
trim1 = create(None,"IfcParameterValue",[curve[3][0]])
trim2 = create(None,"IfcParameterValue",[curve[3][1]])
cur = create(self._fileobject,"IfcTrimmedCurve",[cir,[trim1],[trim2],curve[4],curve[5]])
if cur:
seg = create(self._fileobject,"IfcCompositeCurveSegment",["CONTINUOUS",True,cur])
curves.append(seg)
profile = create(self._fileobject,"IfcCompositeCurve",[curves,False])
ccu = create(self._fileobject,"IfcCompositeCurve",[curves,False])
profile = create(self._fileobject,"IfcArbitraryClosedProfileDef",[curvetype,None,ccu])
else:
if not isinstance(data,list):
data = [data]
@ -559,23 +579,23 @@ class IfcDocument(object):
self.addColor(color,exp)
return exp
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.addProfile("IfcCircleProfileDef",radius)
def addExtrudedCircle(self,data,extrusion,placement=None,color=None):
"""addExtrudedCircle(data,extrusion,[placement,color]): makes an extruded circle
from the given data (center,radius) and the given extrusion vector"""
cir = self.addProfile("IfcCircleProfileDef",data[1])
if not placement:
placement = self.addPlacement(origin=center,local=False)
placement = self.addPlacement(origin=data[0],local=False)
exp = self.addExtrusion(cir,extrusion,placement)
if color:
self.addColor(color,exp)
return exp
def addExtrudedEllipse(self,center,radiusx,radiusy,extrusion,placement=None,color=None):
"""addExtrudedEllipse(radiusx,radiusy,extrusion,[placement,color]): makes an extruded ellipse
from the given radii and the given extrusion vector"""
cir = self.addProfile("IfcEllipseProfileDef",[radiusx,radiusy])
def addExtrudedEllipse(self,data,extrusion,placement=None,color=None):
"""addExtrudedEllipse(data,extrusion,[placement,color]): makes an extruded ellipse
from the given data (center,radiusx,radiusy) and the given extrusion vector"""
cir = self.addProfile("IfcEllipseProfileDef",[data[1],data[2]])
if not placement:
placement = self.addPlacement(origin=center,local=False)
placement = self.addPlacement(origin=data[0],local=False)
exp = self.addExtrusion(cir,extrusion,placement)
if color:
self.addColor(color,exp)
@ -584,9 +604,9 @@ class IfcDocument(object):
def addExtrudedCompositeCurve(self,curves,extrusion,placement=None,color=None):
"""addExtrudedCompositeCurve(curves,extrusion,[placement,color]): makes an extruded polyline
from the given curves and the given extrusion vector"""
ccu = self.addProfile("IfcCompositeCurve",curves)
if not placement:
placement = self.addPlacement(origin=center,local=False)
placement = self.addPlacement(local=False)
ccu = self.addProfile("IfcCompositeCurve",curves)
exp = self.addExtrusion(ccu,extrusion,placement)
if color:
self.addColor(color,exp)

View File

@ -719,7 +719,10 @@ def getShape(obj,objid):
# if DEBUG: print "getting Shape from ",obj
#print "getting shape: ",sh,sh.Solids,sh.Volume,sh.isValid(),sh.isNull()
#for v in sh.Vertexes: print v.Point
return sh
if sh:
if not sh.isNull():
return sh
return None
def getPlacement(entity):
"returns a placement from the given entity"
@ -1041,13 +1044,14 @@ def export(exportList,filename):
else:
if DEBUG: print " Extrusion"
if gdata:
# gdata = [ type, profile data, extrusion data, placement data ]
placement = ifc.addPlacement(origin=gdata[3][0],xaxis=gdata[3][1],zaxis=gdata[3][2])
if gdata[0] == "polyline":
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)
representation = ifc.addExtrudedCircle(gdata[1], gdata[2], color=color)
elif gdata[0] == "ellipse":
representation = ifc.addExtrudedEllipse(gdata[1][0], gdata[1][1], gdata[1][2], gdata[2], color=color)
representation = ifc.addExtrudedEllipse(gdata[1], gdata[2], color=color)
elif gdata[0] == "composite":
representation = ifc.addExtrudedCompositeCurve(gdata[1], gdata[2], color=color)
else:

View File

@ -1253,21 +1253,25 @@ def getTangent(edge,frompoint=None):
return None
def bind(w1,w2):
'''bind(wire1,wire2): binds 2 wires by their endpoints and
returns a face'''
if w1.isClosed() and w2.isClosed():
d1 = w1.BoundBox.DiagonalLength
d2 = w2.BoundBox.DiagonalLength
if d1 > d2:
#w2.reverse()
return Part.Face([w1,w2])
else:
#w1.reverse()
return Part.Face([w2,w1])
'''bind(wire1,wire2): binds 2 wires by their endpoints and
returns a face'''
if w1.isClosed() and w2.isClosed():
d1 = w1.BoundBox.DiagonalLength
d2 = w2.BoundBox.DiagonalLength
if d1 > d2:
#w2.reverse()
return Part.Face([w1,w2])
else:
w3 = Part.Line(w1.Vertexes[0].Point,w2.Vertexes[0].Point).toShape()
w4 = Part.Line(w1.Vertexes[-1].Point,w2.Vertexes[-1].Point).toShape()
return Part.Face(Part.Wire(w1.Edges+[w3]+w2.Edges+[w4]))
#w1.reverse()
return Part.Face([w2,w1])
else:
try:
w3 = Part.Line(w1.Vertexes[0].Point,w2.Vertexes[0].Point).toShape()
w4 = Part.Line(w1.Vertexes[-1].Point,w2.Vertexes[-1].Point).toShape()
return Part.Face(Part.Wire(w1.Edges+[w3]+w2.Edges+[w4]))
except:
print "DraftGeomUtils: unable to bind wires"
return None
def cleanFaces(shape):
"removes inner edges from coplanar faces"