diff --git a/src/Mod/Arch/exportIFCStructuralTools.py b/src/Mod/Arch/exportIFCStructuralTools.py index c4f68f81fc..b3f415c4e2 100644 --- a/src/Mod/Arch/exportIFCStructuralTools.py +++ b/src/Mod/Arch/exportIFCStructuralTools.py @@ -29,6 +29,7 @@ __url__ = "https://www.freecadweb.org" ALLOW_LINEAR_OBJECTS = True # allow non-solid objects (wires, etc) to become analytic objects? structural_nodes = {} # this keeps track of nodes during this session +structural_curves = {} # this keeps track of structural curves during this session scaling = 1.0 # this keeps track of scaling during this session @@ -103,6 +104,37 @@ def createStructuralNode(ifcfile, ifcbin, point): return structPntConn +def createStructuralCurve(ifcfile, ifcbin, curve): + + """Creates a structural connection for a curve""" + + import ifcopenshell + uid = ifcopenshell.guid.new + ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] + structContext = getStructuralContext(ifcfile) + + cartPnt1 = ifcbin.createIfcCartesianPoint(tuple(curve.Vertexes[ 0].Point.multiply(scaling))) + cartPnt2 = ifcbin.createIfcCartesianPoint(tuple(curve.Vertexes[-1].Point.multiply(scaling))) + vertPnt1 = ifcfile.createIfcVertexPoint(cartPnt1) + vertPnt2 = ifcfile.createIfcVertexPoint(cartPnt2) + edge = ifcfile.createIfcEdge(vertPnt1, vertPnt2) + topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, "Analysis", "Edge", [edge]) + prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None , [topologyRep]) + + # boundary conditions serve for ex. to create fixed edges + # for now we don't create any boundary condition + appliedCondition = None + localPlacement = ifcbin.createIfcLocalPlacement() + origin = ifcfile.createIfcCartesianPoint((0.0, 0.0, 0.0)) + orientation = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] + xAxis = ifcfile.createIfcDirection(tuple(orientation[0])) + zAxis = ifcfile.createIfcDirection(tuple(orientation[2])) + localAxes = ifcfile.createIfcAxis2Placement3D(origin, zAxis, xAxis) + structCrvConn = ifcfile.createIfcStructuralCurveConnection( + uid(), ownerHistory, "Line", None, None, localPlacement, prodDefShape, appliedCondition, localAxes) + return structCrvConn + + def createStructuralMember(ifcfile, ifcbin, obj): """Creates a structural member if possible. Returns the member""" @@ -112,6 +144,7 @@ def createStructuralMember(ifcfile, ifcbin, obj): import Draft import Part import ifcopenshell + import FreeCAD uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] structContext = getStructuralContext(ifcfile) @@ -140,42 +173,123 @@ def createStructuralMember(ifcfile, ifcbin, obj): edges = wire.Edges if not edges: return None - for edge in edges: - if len(edge.Vertexes) > 1: - # we don't care about curved edges just now... - vert0 = edge.Vertexes[ 0].Point.multiply(scaling) - vert1 = edge.Vertexes[-1].Point.multiply(scaling) - cartPoint1 = ifcbin.createIfcCartesianPoint(tuple(vert0)) - cartPoint2 = ifcbin.createIfcCartesianPoint(tuple(vert1)) - localPlacement = ifcbin.createIfcLocalPlacement() - vertPoint1 = ifcfile.createIfcVertexPoint(cartPoint1) - vertPoint2 = ifcfile.createIfcVertexPoint(cartPoint2) - newEdge = ifcfile.createIfcEdge(vertPoint1, vertPoint2) - topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, 'Analysis', 'Edge', [newEdge]) - prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None, [topologyRep]) - if ifcfile.wrapped_data.schema_name() == "IFC2X3": - structuralMember = ifcfile.createIfcStructuralCurveMember( - uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "RIGID_JOINED_MEMBER") - else: - localZAxis = ifcbin.createIfcDirection((0, 0, 1)) - structuralMember = ifcfile.createIfcStructuralCurveMember( - uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "RIGID_JOINED_MEMBER", localZAxis) - # check for existing connection nodes - for vert in [vert0, vert1]: - vertCoord = tuple(vert) - if vertCoord in structural_nodes: - if structural_nodes[vertCoord]: - # there is already another member using this point - structPntConn = structural_nodes[vertCoord] - else: - # there is another member with same point, create a new node - structPntConn = createStructuralNode(ifcfile, ifcbin, vert) - structural_nodes[vertCoord] = structPntConn - ifcfile.createIfcRelConnectsStructuralMember( - uid(), ownerHistory, None, None, structuralMember, structPntConn, None, None, None, None) + + # OBJECT CLASSIFICATION by edge number + # Linear elements for edge_number = 1, Surface elements for edge_number > 1 + # we don't care about curved edges just now... + + if len(edges) == 1: + # LINEAR OBJECTS: beams, columns + # ATM limitations: + # - no profile properties are taken into account + # - no materials properties are takein into account + # - + # create geometry + verts = [None for _ in range(len(edges)+1)] + verts[0] = tuple(edges[0].Vertexes[ 0].Point.multiply(scaling)) + verts[1] = tuple(edges[0].Vertexes[-1].Point.multiply(scaling)) + cartPnt1 = ifcfile.createIfcCartesianPoint(verts[0]) + cartPnt2 = ifcfile.createIfcCartesianPoint(verts[1]) + vertPnt1 = ifcfile.createIfcVertexPoint(cartPnt1) + vertPnt2 = ifcfile.createIfcVertexPoint(cartPnt2) + newEdge = ifcfile.createIfcEdge(vertPnt1, vertPnt2) + topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, "Analysis", "Edge", (newEdge,)) + prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None, (topologyRep,)) + # set local coordinate system + localPlacement = ifcbin.createIfcLocalPlacement() + localZAxis = ifcbin.createIfcDirection((0, 0, 1)) + # create structural member + if ifcfile.wrapped_data.schema_name() == "IFC2X3": + structuralMember = ifcfile.createIfcStructuralCurveMember( + uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "RIGID_JOINED_MEMBER") + else: + localZAxis = ifcbin.createIfcDirection((0, 0, 1)) + structuralMember = ifcfile.createIfcStructuralCurveMember( + uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "RIGID_JOINED_MEMBER", localZAxis) + + elif len(edges) > 1: + # SURFACE OBJECTS: slabs (horizontal, vertical, inclined) + # ATM limitations: + # - mo material properties are taken into account + # - walls don't work because they miss a node system + # - + # creates geometry + verts = [None for _ in range(len(edges))] + for i, edge in enumerate(edges): + verts[i] = tuple(edge.Vertexes[0].Point.multiply(scaling)) + cartPnt = ifcfile.createIfcCartesianPoint(verts[i]) + vertPnt = ifcfile.createIfcVertexPoint(cartPnt) + orientedEdges = [None for _ in range(len(edges))] + for i, vert in enumerate(verts): + v2Index = (i + 1) if i < len(verts) - 1 else 0 + cartPnt1 = ifcfile.createIfcCartesianPoint(vert) + cartPnt2 = ifcfile.createIfcCartesianPoint(verts[v2Index]) + vertPnt1 = ifcfile.createIfcVertexPoint(cartPnt1) + vertPnt2 = ifcfile.createIfcVertexPoint(cartPnt2) + edge = ifcfile.createIfcEdge(vertPnt1, vertPnt2) + orientedEdges[i] = ifcfile.createIfcOrientedEdge(None, None, edge, True) + edgeLoop = ifcfile.createIfcEdgeLoop(tuple(orientedEdges)) + # sets local coordinate system + localPlacement = ifcbin.createIfcLocalPlacement() + # sets face origin to the first vertex point of the planar surface + origin = cartPnt2 + # find crossVect that is perpendicular to the planar surface + vect0 = FreeCAD.Vector(verts[0]) + vect1 = FreeCAD.Vector(verts[1]) + vectn = FreeCAD.Vector(verts[-1]) + vect01 = vect1.sub(vect0) + vect0n = vectn.sub(vect0) + crossVect = vect01.cross(vect0n) + # normalize crossVect + normVect = crossVect.normalize() + xAxis = ifcfile.createIfcDirection(tuple([vect01.x, vect01.y, vect01.z])) + zAxis = ifcfile.createIfcDirection(tuple([normVect.x, normVect.y, normVect.z])) + localAxes = ifcfile.createIfcAxis2Placement3D(origin, zAxis, xAxis) + plane = ifcfile.createIfcPlane(localAxes) + faceBound = ifcfile.createIfcFaceBound(edgeLoop, True) + face = ifcfile.createIfcFaceSurface((faceBound,), plane, True) + topologyRep = ifcfile.createIfcTopologyRepresentation(structContext, "Analysis", "Face", (face,)) + prodDefShape = ifcfile.createIfcProductDefinitionShape(None, None, (topologyRep,)) + # sets surface thickness + # ATM limitations + # - for verical slabs (walls) or inclined slabs (ramps) the thickness is taken from the Height property + thickness = float(obj.Height)*scaling + # creates structural member + structuralMember = ifcfile.createIfcStructuralSurfaceMember( + uid(), ownerHistory, obj.Label, None, None, localPlacement, prodDefShape, "SHELL", thickness) + + # check for existing connection nodes + for vert in verts: + vertCoord = tuple(vert) + if vertCoord in structural_nodes: + if structural_nodes[vertCoord]: + # there is already another member using this point + structPntConn = structural_nodes[vertCoord] else: - # just add the point, no other member using it yet - structural_nodes[vertCoord] = None + # there is another member with same point, create a new node connection + structPntConn = createStructuralNode(ifcfile, ifcbin, vert) + structural_nodes[vertCoord] = structPntConn + ifcfile.createIfcRelConnectsStructuralMember( + uid(), ownerHistory, None, None, structuralMember, structPntConn, None, None, None, None) + else: + # just add the point, no other member using it yet + structural_nodes[vertCoord] = None + + # check for existing connection curves + for edge in edges: + if edge in structural_curves: + if structural_curves[edge]: + # there is already another member using this curve + strucCrvConn = structural_curves[edge] + else: + # there is another member with same edge, create a new curve connection + strucCrvConn = createStructuralCurve(ifcfile, ifcbin, edge) + structural_curves[edge] = strucCrvConn + ifcfile.createIfcRelConnectsStructuralMember( + uid(), None, None, None, structuralMember, strucCrvConn, None, None, None, None) + else: + # just add the curve, no other member using it yet + structural_curves[edge] = None return structuralMember @@ -186,11 +300,13 @@ def createStructuralGroup(ifcfile): import ifcopenshell uid = ifcopenshell.guid.new ownerHistory = ifcfile.by_type("IfcOwnerHistory")[0] - structCrvMember = ifcfile.by_type("IfcStructuralCurveMember") + structSrfMember = ifcfile.by_type("IfcStructuralSurfaceMember") + structCrvMember = ifcfile.by_type("IfcStructuralCurveMember") structPntConn = ifcfile.by_type("IfcStructuralPointConnection") + structCrvConn = ifcfile.by_type("IfcStructuralCurveConnection") structModel = ifcfile.by_type("IfcStructuralAnalysisModel")[0] if structModel: - members = structCrvMember + structPntConn + members = structSrfMember + structCrvMember + structPntConn + structCrvConn if members: ifcfile.createIfcRelAssignsToGroup(uid(), ownerHistory, None, None, members, "PRODUCT", structModel)