Arch: Support for extruded curves in IFC export
This commit is contained in:
parent
2724242f38
commit
5424ad1d6a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue
Block a user