From 94a5fd7408a40111595b6bc6ca6728cf7e14285a Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 9 Nov 2018 17:59:17 -0200 Subject: [PATCH] Arch: Fixed buggy import/export of objects based on rectangles to IFC --- src/Mod/Arch/ArchCommands.py | 14 +++++++++----- src/Mod/Arch/ArchWall.py | 5 ++++- src/Mod/Arch/importIFC.py | 28 +++++++++++++++++++--------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index 5f9b1db9f5..71387ac6f1 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -1222,9 +1222,10 @@ def rebuildArchShape(objects=None): FreeCAD.ActiveDocument.recompute() -def getExtrusionData(shape): - """getExtrusionData(shape): returns a base face and an extrusion vector - if this shape can be described as a perpendicular extrusion, or None if not.""" +def getExtrusionData(shape,sortmethod="area"): + """getExtrusionData(shape,sortmethod): returns a base face and an extrusion vector + if this shape can be described as a perpendicular extrusion, or None if not. + sortmethod can be "area" (default) or "z".""" if shape.isNull(): return None if not shape.Solids: @@ -1264,8 +1265,11 @@ def getExtrusionData(shape): else: valids.append([faces[pair[1]][0],faces[pair[0]][0].CenterOfMass.sub(faces[pair[1]][0].CenterOfMass)]) if valids: - # sort by smallest area - valids.sort(key=lambda v: v[0].Area) + if sortmethod == "z": + valids.sort(key=lambda v: v[0].CenterOfMass.z) + else: + # sort by smallest area + valids.sort(key=lambda v: v[0].Area) return valids[0] return None diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 9794dca921..be8130c5d9 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -787,7 +787,8 @@ class _Wall(ArchComponent.Component): if len(obj.Base.Shape.Faces) >= obj.Face: face = obj.Base.Shape.Faces[obj.Face-1] # this wall is based on a specific face of its base object - normal = face.normalAt(0,0) + if obj.Normal != Vector(0,0,0): + normal = face.normalAt(0,0) if normal.getAngle(Vector(0,0,1)) > math.pi/4: normal.multiply(width) base = face.extrude(normal) @@ -917,6 +918,8 @@ class _Wall(ArchComponent.Component): placement = FreeCAD.Placement() if base and placement: extrusion = normal.multiply(height) + if placement.Rotation.Angle > 0: + extrusion = placement.inverse().Rotation.multVec(extrusion) return (base,extrusion,placement) return None diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index fd97fd3141..9dd4848bd4 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -668,7 +668,11 @@ def insert(filename,docname,skip=[],only=[],root=None): if DEBUG: print("clone ",end="") else: if GET_EXTRUSIONS: - ex = Arch.getExtrusionData(shape) # is this an extrusion? + if ptype in ["IfcWall","IfcWallStandardCase"]: + sortmethod = "z" + else: + sortmethod = "area" + ex = Arch.getExtrusionData(shape,sortmethod) # is this an extrusion? if ex: # check for extrusion profile baseface = None @@ -701,7 +705,7 @@ def insert(filename,docname,skip=[],only=[],root=None): if DraftGeomUtils.hasCurves(ex[0]) or len(ex[0].Wires) != 1: # curves or holes? We just make a Part face baseface = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_footprint") - # bug in ifcopenshell? Some faces of a shell may have non-null placement + # bug/feature in ifcopenshell? Some faces of a shell may have non-null placement # workaround to remove the bad placement: exporting/reimporting as step if not ex[0].Placement.isNull(): import tempfile @@ -2521,12 +2525,13 @@ def checkRectangle(edges): """ checkRectangle(edges=[]): This function checks whether the given form rectangle or not. It will return True when edges form rectangular shape or return False when edges not form a rectangular.""" + if len(edges) != 4: + return False angles = [round(getEdgesAngle(edges[0], edges[1])), round(getEdgesAngle(edges[0], edges[2])), round(getEdgesAngle(edges[0], edges[3]))] if angles.count(90) == 2 and (angles.count(180) == 1 or angles.count(0) == 1): return True - else: - return False + return False def getProfile(ifcfile,p): @@ -2545,13 +2550,18 @@ def getProfile(ifcfile,p): # extruded ellipse profile = ifcfile.createIfcEllipseProfileDef("AREA",None,pt,p.Edges[0].Curve.MajorRadius,p.Edges[0].Curve.MinorRadius) elif (checkRectangle(p.Edges)): - pxvc = ifcbin.createIfcDirection((1.0,0.0)) + # arbitrarily use the first edge as the rectangle orientation + d = vec(p.Edges[0]) + d.normalize() + pxvc = ifcbin.createIfcDirection(tuple(d)) povc = ifcbin.createIfcCartesianPoint((0.0,0.0)) pt = ifcbin.createIfcAxis2Placement2D(povc,pxvc) - semiPerimeter = p.Length/2 - diff = math.sqrt(semiPerimeter**2 - 4*p.Area) - b = max(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) - h = min(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) + #semiPerimeter = p.Length/2 + #diff = math.sqrt(semiPerimeter**2 - 4*p.Area) + #b = max(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) + #h = min(abs((semiPerimeter + diff)/2),abs((semiPerimeter - diff)/2)) + b = p.Edges[0].Length + h = p.Edges[1].Length profile = ifcfile.createIfcRectangleProfileDef("AREA",'rectangular',pt,b,h) elif (len(p.Faces) == 1) and (len(p.Wires) > 1): # face with holes