BIM: NativeIFC 2D support - basic import/export + linework annotations
This commit is contained in:
committed by
Yorik van Havre
parent
7913280486
commit
9c9d451ac6
@@ -189,6 +189,7 @@ SET(nativeifc_SRCS
|
||||
nativeifc/__init__.py
|
||||
nativeifc/ifc_openshell.py
|
||||
nativeifc/ifc_types.py
|
||||
nativeifc/ifc_export.py
|
||||
)
|
||||
|
||||
SOURCE_GROUP("" FILES ${Arch_SRCS})
|
||||
|
||||
@@ -1289,125 +1289,11 @@ def export(exportList, filename, colors=None, preferences=None):
|
||||
|
||||
annos = {}
|
||||
if preferences['EXPORT_2D']:
|
||||
global curvestyles
|
||||
curvestyles = {}
|
||||
if annotations and preferences['DEBUG']: print("exporting 2D objects...")
|
||||
for anno in annotations:
|
||||
objectType = None
|
||||
xvc = ifcbin.createIfcDirection((1.0,0.0,0.0))
|
||||
zvc = ifcbin.createIfcDirection((0.0,0.0,1.0))
|
||||
ovc = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0))
|
||||
gpl = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc)
|
||||
placement = ifcbin.createIfcLocalPlacement(gpl)
|
||||
if anno.isDerivedFrom("Part::Feature"):
|
||||
if Draft.getType(anno) == "Hatch":
|
||||
objectType = "HATCH"
|
||||
elif getattr(anno.ViewObject,"EndArrow",False):
|
||||
objectType = "LEADER"
|
||||
elif anno.Shape.Faces:
|
||||
objectType = "AREA"
|
||||
else:
|
||||
objectType = "LINEWORK"
|
||||
reps = []
|
||||
sh = anno.Shape.copy()
|
||||
sh.scale(preferences['SCALE_FACTOR']) # to meters
|
||||
ehc = []
|
||||
curves = []
|
||||
for w in sh.Wires:
|
||||
curves.append(createCurve(ifcfile,w))
|
||||
for e in w.Edges:
|
||||
ehc.append(e.hashCode())
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
curves = []
|
||||
for e in sh.Edges:
|
||||
if e.hashCode not in ehc:
|
||||
curves.append(createCurve(ifcfile,e))
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
elif anno.isDerivedFrom("App::Annotation"):
|
||||
objectType = "TEXT"
|
||||
l = FreeCAD.Vector(anno.Position).multiply(preferences['SCALE_FACTOR'])
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None)
|
||||
s = ";".join(anno.LabelText)
|
||||
txt = ifcfile.createIfcTextLiteral(s,tpl,"LEFT")
|
||||
reps = [txt]
|
||||
elif Draft.getType(anno) in ["DraftText","Text"]:
|
||||
objectType = "TEXT"
|
||||
l = FreeCAD.Vector(anno.Placement.Base).multiply(preferences['SCALE_FACTOR'])
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
zdir = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))))
|
||||
xdir = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0))))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,zdir,xdir)
|
||||
alg = "LEFT"
|
||||
if FreeCAD.GuiUp and hasattr(anno.ViewObject,"Justification"):
|
||||
if anno.ViewObject.Justification == "Right":
|
||||
alg = "RIGHT"
|
||||
s = ";".join(anno.Text)
|
||||
txt = ifcfile.createIfcTextLiteral(s,tpl,alg)
|
||||
reps = [txt]
|
||||
elif Draft.getType(anno) in ["Dimension","LinearDimension","AngularDimension"]:
|
||||
if FreeCAD.GuiUp:
|
||||
objectType = "DIMENSION"
|
||||
vp = anno.ViewObject.Proxy
|
||||
reps = []
|
||||
sh = Part.makePolygon([vp.p1,vp.p2,vp.p3,vp.p4])
|
||||
sh.scale(preferences['SCALE_FACTOR']) # to meters
|
||||
ehc = []
|
||||
curves = []
|
||||
for w in sh.Wires:
|
||||
curves.append(createCurve(ifcfile,w))
|
||||
for e in w.Edges:
|
||||
ehc.append(e.hashCode())
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
curves = []
|
||||
for e in sh.Edges:
|
||||
if e.hashCode not in ehc:
|
||||
curves.append(createCurve(ifcfile,e))
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
l = FreeCAD.Vector(vp.tbase).multiply(preferences['SCALE_FACTOR'])
|
||||
zdir = None
|
||||
xdir = None
|
||||
if hasattr(vp,"trot"):
|
||||
r = FreeCAD.Rotation(vp.trot[0],vp.trot[1],vp.trot[2],vp.trot[3])
|
||||
zdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(0,0,1))))
|
||||
xdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(1,0,0))))
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,zdir,xdir)
|
||||
txt = ifcfile.createIfcTextLiteral(vp.string,tpl,"LEFT")
|
||||
reps.append(txt)
|
||||
else:
|
||||
print("Unable to handle object",anno.Label)
|
||||
continue
|
||||
|
||||
for coldef in ["LineColor","TextColor","ShapeColor"]:
|
||||
if hasattr(obj.ViewObject,coldef):
|
||||
rgb = getattr(obj.ViewObject,coldef)[:3]
|
||||
if rgb in curvestyles:
|
||||
psa = curvestyles[rgb]
|
||||
else:
|
||||
col = ifcbin.createIfcColourRgb(rgb[0],rgb[1],rgb[2])
|
||||
cvf = ifcfile.createIfcDraughtingPredefinedCurveFont("continuous")
|
||||
ics = ifcfile.createIfcCurveStyle('Line',cvf,None,col)
|
||||
psa = ifcfile.createIfcPresentationStyleAssignment([ics])
|
||||
curvestyles[rgb] = psa
|
||||
for rep in reps:
|
||||
isi = ifcfile.createIfcStyledItem(rep,[psa],None)
|
||||
break
|
||||
|
||||
shp = ifcfile.createIfcShapeRepresentation(context,'Annotation','Annotation2D',reps)
|
||||
rep = ifcfile.createIfcProductDefinitionShape(None,None,[shp])
|
||||
l = anno.Label
|
||||
ann = ifcfile.createIfcAnnotation(
|
||||
ifcopenshell.guid.new(),
|
||||
history,l,
|
||||
'',
|
||||
objectType,
|
||||
placement,
|
||||
rep
|
||||
)
|
||||
ann = create_annotation(anno, ifcfile, context, history, preferences)
|
||||
annos[anno.Name] = ann
|
||||
|
||||
# groups
|
||||
@@ -2550,3 +2436,128 @@ def writeJson(filename,ifcfile):
|
||||
#print("json:",s)
|
||||
f.write(s)
|
||||
f.close()
|
||||
|
||||
|
||||
def create_annotation(anno, ifcfile, context, history, preferences):
|
||||
"""Creates an annotation object"""
|
||||
|
||||
# uses global ifcbin, curvestyles
|
||||
objectType = None
|
||||
xvc = ifcbin.createIfcDirection((1.0,0.0,0.0))
|
||||
zvc = ifcbin.createIfcDirection((0.0,0.0,1.0))
|
||||
ovc = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0))
|
||||
gpl = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc)
|
||||
placement = ifcbin.createIfcLocalPlacement(gpl)
|
||||
if anno.isDerivedFrom("Part::Feature"):
|
||||
if Draft.getType(anno) == "Hatch":
|
||||
objectType = "HATCH"
|
||||
elif getattr(anno.ViewObject,"EndArrow",False):
|
||||
objectType = "LEADER"
|
||||
elif anno.Shape.Faces:
|
||||
objectType = "AREA"
|
||||
else:
|
||||
objectType = "LINEWORK"
|
||||
reps = []
|
||||
sh = anno.Shape.copy()
|
||||
sh.scale(preferences['SCALE_FACTOR']) # to meters
|
||||
ehc = []
|
||||
curves = []
|
||||
for w in sh.Wires:
|
||||
curves.append(createCurve(ifcfile,w))
|
||||
for e in w.Edges:
|
||||
ehc.append(e.hashCode())
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
curves = []
|
||||
for e in sh.Edges:
|
||||
if e.hashCode not in ehc:
|
||||
curves.append(createCurve(ifcfile,e))
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
elif anno.isDerivedFrom("App::Annotation"):
|
||||
objectType = "TEXT"
|
||||
l = FreeCAD.Vector(anno.Position).multiply(preferences['SCALE_FACTOR'])
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None)
|
||||
s = ";".join(anno.LabelText)
|
||||
txt = ifcfile.createIfcTextLiteral(s,tpl,"LEFT")
|
||||
reps = [txt]
|
||||
elif Draft.getType(anno) in ["DraftText","Text"]:
|
||||
objectType = "TEXT"
|
||||
l = FreeCAD.Vector(anno.Placement.Base).multiply(preferences['SCALE_FACTOR'])
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
zdir = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))))
|
||||
xdir = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0))))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,zdir,xdir)
|
||||
alg = "LEFT"
|
||||
if FreeCAD.GuiUp and hasattr(anno.ViewObject,"Justification"):
|
||||
if anno.ViewObject.Justification == "Right":
|
||||
alg = "RIGHT"
|
||||
s = ";".join(anno.Text)
|
||||
txt = ifcfile.createIfcTextLiteral(s,tpl,alg)
|
||||
reps = [txt]
|
||||
elif Draft.getType(anno) in ["Dimension","LinearDimension","AngularDimension"]:
|
||||
if FreeCAD.GuiUp:
|
||||
objectType = "DIMENSION"
|
||||
vp = anno.ViewObject.Proxy
|
||||
reps = []
|
||||
sh = Part.makePolygon([vp.p1,vp.p2,vp.p3,vp.p4])
|
||||
sh.scale(preferences['SCALE_FACTOR']) # to meters
|
||||
ehc = []
|
||||
curves = []
|
||||
for w in sh.Wires:
|
||||
curves.append(createCurve(ifcfile,w))
|
||||
for e in w.Edges:
|
||||
ehc.append(e.hashCode())
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
curves = []
|
||||
for e in sh.Edges:
|
||||
if e.hashCode not in ehc:
|
||||
curves.append(createCurve(ifcfile,e))
|
||||
if curves:
|
||||
reps.append(ifcfile.createIfcGeometricCurveSet(curves))
|
||||
l = FreeCAD.Vector(vp.tbase).multiply(preferences['SCALE_FACTOR'])
|
||||
zdir = None
|
||||
xdir = None
|
||||
if hasattr(vp,"trot"):
|
||||
r = FreeCAD.Rotation(vp.trot[0],vp.trot[1],vp.trot[2],vp.trot[3])
|
||||
zdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(0,0,1))))
|
||||
xdir = ifcbin.createIfcDirection(tuple(r.multVec(FreeCAD.Vector(1,0,0))))
|
||||
pos = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
|
||||
tpl = ifcbin.createIfcAxis2Placement3D(pos,zdir,xdir)
|
||||
txt = ifcfile.createIfcTextLiteral(vp.string,tpl,"LEFT")
|
||||
reps.append(txt)
|
||||
else:
|
||||
print("Unable to handle object",anno.Label)
|
||||
return None
|
||||
|
||||
for coldef in ["LineColor","TextColor","ShapeColor"]:
|
||||
if hasattr(anno.ViewObject,coldef):
|
||||
rgb = getattr(anno.ViewObject,coldef)[:3]
|
||||
if rgb in curvestyles:
|
||||
psa = curvestyles[rgb]
|
||||
else:
|
||||
col = ifcbin.createIfcColourRgb(rgb[0],rgb[1],rgb[2])
|
||||
cvf = ifcfile.createIfcDraughtingPredefinedCurveFont("continuous")
|
||||
ics = ifcfile.createIfcCurveStyle('Line',cvf,None,col)
|
||||
psa = ifcfile.createIfcPresentationStyleAssignment([ics])
|
||||
curvestyles[rgb] = psa
|
||||
for rep in reps:
|
||||
isi = ifcfile.createIfcStyledItem(rep,[psa],None)
|
||||
break
|
||||
|
||||
shp = ifcfile.createIfcShapeRepresentation(context,'Annotation','Annotation2D',reps)
|
||||
rep = ifcfile.createIfcProductDefinitionShape(None,None,[shp])
|
||||
label = anno.Label
|
||||
description = getattr(anno, "Description", "")
|
||||
ann = ifcfile.createIfcAnnotation(
|
||||
ifcopenshell.guid.new(),
|
||||
history,
|
||||
label,
|
||||
description,
|
||||
objectType,
|
||||
placement,
|
||||
rep
|
||||
)
|
||||
return ann
|
||||
|
||||
146
src/Mod/BIM/nativeifc/ifc_export.py
Normal file
146
src/Mod/BIM/nativeifc/ifc_export.py
Normal file
@@ -0,0 +1,146 @@
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2024 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU General Public License (GPL) *
|
||||
# * as published by the Free Software Foundation; either version 3 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import Draft
|
||||
import ifcopenshell
|
||||
|
||||
from importers import exportIFC
|
||||
from importers import exportIFCHelper
|
||||
|
||||
from nativeifc import ifc_tools
|
||||
|
||||
|
||||
def get_export_preferences(ifcfile):
|
||||
"""returns a preferences dict for exportIFC"""
|
||||
|
||||
prefs = exportIFC.getPreferences()
|
||||
prefs["SCHEMA"] = ifcfile.wrapped_data.schema_name()
|
||||
s = ifcopenshell.util.unit.calculate_unit_scale(ifcfile)
|
||||
# the above lines yields meter -> file unit scale factor. We need mm
|
||||
prefs["SCALE_FACTOR"] = 0.001 / s
|
||||
context = ifcfile[
|
||||
ifc_tools.get_body_context_ids(ifcfile)[0]
|
||||
] # we take the first one (first found subcontext)
|
||||
return prefs, context
|
||||
|
||||
|
||||
def create_product(obj, parent, ifcfile, ifcclass=None):
|
||||
"""Creates an IFC product out of a FreeCAD object"""
|
||||
|
||||
name = obj.Label
|
||||
description = getattr(obj, "Description", None)
|
||||
if not ifcclass:
|
||||
ifcclass = ifc_tools.get_ifctype(obj)
|
||||
representation, placement = create_representation(obj, ifcfile)
|
||||
product = ifc_tools.api_run("root.create_entity", ifcfile, ifc_class=ifcclass, name=name)
|
||||
ifc_tools.set_attribute(ifcfile, product, "Description", description)
|
||||
ifc_tools.set_attribute(ifcfile, product, "ObjectPlacement", placement)
|
||||
# TODO below cannot be used at the moment because the ArchIFC exporter returns an
|
||||
# IfcProductDefinitionShape already and not an IfcShapeRepresentation
|
||||
# ifc_tools.api_run("geometry.assign_representation", ifcfile, product=product, representation=representation)
|
||||
ifc_tools.set_attribute(ifcfile, product, "Representation", representation)
|
||||
# TODO treat subtractions/additions
|
||||
return product
|
||||
|
||||
|
||||
def create_representation(obj, ifcfile):
|
||||
"""Creates a geometry representation for the given object"""
|
||||
|
||||
# TEMPORARY use the Arch exporter
|
||||
# TODO this is temporary. We should rely on ifcopenshell for this with:
|
||||
# https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/root/create_entity/index.html
|
||||
# a new FreeCAD 'engine' should be added to:
|
||||
# https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/geometry/index.html
|
||||
# that should contain all typical use cases one could have to convert FreeCAD geometry
|
||||
# to IFC.
|
||||
|
||||
# setup exporter - TODO do that in the module init
|
||||
exportIFC.clones = {}
|
||||
exportIFC.profiledefs = {}
|
||||
exportIFC.surfstyles = {}
|
||||
exportIFC.shapedefs = {}
|
||||
exportIFC.ifcopenshell = ifcopenshell
|
||||
exportIFC.ifcbin = exportIFCHelper.recycler(ifcfile, template=False)
|
||||
prefs, context = get_export_preferences(ifcfile)
|
||||
representation, placement, shapetype = exportIFC.getRepresentation(
|
||||
ifcfile, context, obj, preferences=prefs
|
||||
)
|
||||
return representation, placement
|
||||
|
||||
|
||||
def is_annotation(obj):
|
||||
"""Determines if the given FreeCAD object should be saved as an IfcAnnotation"""
|
||||
|
||||
if getattr(obj, "IfcClass", None) == "IfcAnnotation":
|
||||
return True
|
||||
if getattr(obj, "IfcType", None) == "Annotation":
|
||||
return True
|
||||
if obj.isDerivedFrom("Part::Part2DObject"):
|
||||
return True
|
||||
elif obj.isDerivedFrom("App::Annotation"):
|
||||
return True
|
||||
elif Draft.getType(obj) in ["DraftText",
|
||||
"Text",
|
||||
"Dimension",
|
||||
"LinearDimension",
|
||||
"AngularDimension"]:
|
||||
return True
|
||||
elif obj.isDerivedFrom("Part::Feature"):
|
||||
if obj.Shape and (not obj.Shape.Solids) and obj.Shape.Edges:
|
||||
if not obj.Shape.Faces:
|
||||
return True
|
||||
elif (obj.Shape.BoundBox.XLength < 0.0001) \
|
||||
or (obj.Shape.BoundBox.YLength < 0.0001) \
|
||||
or (obj.Shape.BoundBox.ZLength < 0.0001):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def create_annotation(obj, ifcfile):
|
||||
"""Adds an IfcAnnotation from the given object to the given IFC file"""
|
||||
|
||||
exportIFC.clones = {}
|
||||
exportIFC.profiledefs = {}
|
||||
exportIFC.surfstyles = {}
|
||||
exportIFC.shapedefs = {}
|
||||
exportIFC.curvestyles = {}
|
||||
exportIFC.ifcopenshell = ifcopenshell
|
||||
exportIFC.ifcbin = exportIFCHelper.recycler(ifcfile, template=False)
|
||||
prefs, context = get_export_preferences(ifcfile)
|
||||
history = get_history(ifcfile)
|
||||
# TODO The following prints each edge as a separate IfcGeometricCurveSet
|
||||
# It should be refined to create polylines instead
|
||||
anno = exportIFC.create_annotation(obj, ifcfile, context, history, prefs)
|
||||
return anno
|
||||
|
||||
|
||||
def get_history(ifcfile):
|
||||
"""Returns the owner history or None"""
|
||||
|
||||
history = ifcfile.by_type("IfcOwnerHistory")
|
||||
if history:
|
||||
history = history[0]
|
||||
else:
|
||||
# IFC4 allows to not write any history
|
||||
history = None
|
||||
return history
|
||||
@@ -26,12 +26,14 @@ used by the execute() method of ifc_objects"""
|
||||
|
||||
|
||||
import time
|
||||
import re
|
||||
import FreeCAD
|
||||
from FreeCAD import Base
|
||||
import Part
|
||||
import ifcopenshell
|
||||
from ifcopenshell.util import element
|
||||
from nativeifc import ifc_tools
|
||||
from nativeifc import ifc_export
|
||||
import multiprocessing
|
||||
import FreeCADGui
|
||||
from pivy import coin
|
||||
@@ -57,13 +59,32 @@ def generate_geometry(obj, cached=False):
|
||||
return
|
||||
colors = None
|
||||
|
||||
# workaround for Group property bug: Avoid having a null shape, otherwise
|
||||
# a default representation will be created from the object's Group contents
|
||||
# obj.Shape = Part.makeBox(1, 1, 1)
|
||||
# fixed in FreeCAD 0.22 - uncomment the line above for earlier versions
|
||||
ifcfile = ifc_tools.get_ifcfile(obj)
|
||||
|
||||
# annotations
|
||||
if ifc_export.is_annotation(obj):
|
||||
element = ifc_tools.get_ifc_element(obj)
|
||||
if not element:
|
||||
return
|
||||
if obj.ShapeMode == "Shape":
|
||||
shape, placement = get_annotation_shape(element, ifcfile)
|
||||
if shape:
|
||||
obj.Shape = shape
|
||||
if placement:
|
||||
obj.Placement = placement
|
||||
elif obj.ViewObject and obj.ShapeMode == "Coin":
|
||||
node, placement = get_annotation_shape(element, ifcfile, coin=True)
|
||||
if node:
|
||||
set_representation(obj.ViewObject, node)
|
||||
colors = node[0]
|
||||
else:
|
||||
set_representation(obj.ViewObject, None)
|
||||
print_debug(obj)
|
||||
if placement:
|
||||
obj.Placement = placement
|
||||
return
|
||||
|
||||
# generate the shape or coin node
|
||||
ifcfile = ifc_tools.get_ifcfile(obj)
|
||||
elements = get_decomposition(obj)
|
||||
if obj.ShapeMode == "Shape":
|
||||
shape, colors = generate_shape(ifcfile, elements, cached)
|
||||
@@ -77,6 +98,7 @@ def generate_geometry(obj, cached=False):
|
||||
elif obj.ViewObject and obj.ShapeMode == "Coin":
|
||||
node, placement = generate_coin(ifcfile, elements, cached)
|
||||
if node:
|
||||
# TODO this still needs to be fixed
|
||||
#QtCore.QTimer.singleShot(0, lambda: set_representation(obj.ViewObject, node))
|
||||
set_representation(obj.ViewObject, node)
|
||||
colors = node[0]
|
||||
@@ -339,7 +361,6 @@ def filter_types(elements, obj_ids=[]):
|
||||
elements = [e for e in elements if not e.is_a("IfcOpeningElement")]
|
||||
elements = [e for e in elements if not e.is_a("IfcSpace")]
|
||||
elements = [e for e in elements if not e.is_a("IfcFurnishingElement")]
|
||||
elements = [e for e in elements if not e.is_a("IfcAnnotation")]
|
||||
elements = [e for e in elements if not e.id() in obj_ids]
|
||||
return elements
|
||||
|
||||
@@ -451,11 +472,12 @@ def set_representation(vobj, node):
|
||||
coords.point.deleteValues(0)
|
||||
if not node:
|
||||
return
|
||||
if node[1] and node[2] and node[3] and node[4]:
|
||||
if node[1] and node[3]:
|
||||
coords.point.setValues(node[1])
|
||||
fset.coordIndex.setValues(node[2])
|
||||
fset.partIndex.setValues(node[4])
|
||||
eset.coordIndex.setValues(node[3])
|
||||
if node[2] and node[4]:
|
||||
fset.coordIndex.setValues(node[2])
|
||||
fset.partIndex.setValues(node[4])
|
||||
|
||||
|
||||
def print_debug(obj):
|
||||
@@ -524,3 +546,45 @@ def delete_ghost(document):
|
||||
sg = FreeCADGui.getDocument(document.Name).ActiveView.getSceneGraph()
|
||||
sg.removeChild(document.Proxy.ghost)
|
||||
del document.Proxy.ghost
|
||||
|
||||
|
||||
def get_annotation_shape(annotation, ifcfile, coin=False):
|
||||
"""Returns a shape or a coin node form an IFC annotation"""
|
||||
|
||||
import Part
|
||||
from importers import importIFCHelper
|
||||
|
||||
shape = None
|
||||
placement = None
|
||||
ifcscale = importIFCHelper.getScaling(ifcfile)
|
||||
shapes2d = []
|
||||
for rep in annotation.Representation.Representations:
|
||||
if rep.RepresentationIdentifier in ["Annotation", "FootPrint", "Axis"]:
|
||||
sh = importIFCHelper.get2DShape(rep, ifcscale)
|
||||
if sh:
|
||||
shapes2d.extend(sh)
|
||||
if shapes2d:
|
||||
shape = Part.makeCompound(shapes2d)
|
||||
placement = importIFCHelper.getPlacement(annotation.ObjectPlacement, ifcscale)
|
||||
if coin:
|
||||
iv = shape.writeInventor()
|
||||
iv = iv.replace("\n", "")
|
||||
segs = re.findall(r"point \[.*?\]",iv)
|
||||
segs = [s.replace("point [","").replace("]","").strip() for s in segs]
|
||||
segs = [s.split(" ") for s in segs]
|
||||
verts = []
|
||||
edges = []
|
||||
for pair in segs:
|
||||
v1 = tuple([float(v) for v in pair[0].split()])
|
||||
v2 = tuple([float(v) for v in pair[1].split()])
|
||||
if not v1 in verts:
|
||||
verts.append(v1)
|
||||
edges.append(verts.index(v1))
|
||||
if not v2 in verts:
|
||||
verts.append(v2)
|
||||
edges.append(verts.index(v2))
|
||||
edges.append(-1)
|
||||
shape = [[None, verts, [], edges]]
|
||||
# unify nodes
|
||||
shape = unify(shape)
|
||||
return shape, placement
|
||||
|
||||
@@ -355,6 +355,7 @@ def lock_document():
|
||||
from nativeifc import ifc_tools # lazy loading
|
||||
from importers import exportIFC
|
||||
from nativeifc import ifc_geometry
|
||||
from nativeifc import ifc_export
|
||||
from PySide import QtCore
|
||||
|
||||
doc = FreeCAD.ActiveDocument
|
||||
@@ -379,7 +380,7 @@ def lock_document():
|
||||
if rest:
|
||||
# 1b some objects are outside
|
||||
objs = find_toplevel(rest)
|
||||
prefs, context = ifc_tools.get_export_preferences(ifcfile)
|
||||
prefs, context = ifc_export.get_export_preferences(ifcfile)
|
||||
products = exportIFC.export(objs, ifcfile, preferences=prefs)
|
||||
for product in products.values():
|
||||
if not getattr(product, "ContainedInStructure", None):
|
||||
@@ -416,7 +417,7 @@ def lock_document():
|
||||
ifc_tools.convert_document(doc, silent=True)
|
||||
ifcfile = doc.Proxy.ifcfile
|
||||
objs = find_toplevel(doc.Objects)
|
||||
prefs, context = ifc_tools.get_export_preferences(ifcfile)
|
||||
prefs, context = ifc_export.get_export_preferences(ifcfile)
|
||||
exportIFC.export(objs, ifcfile, preferences=prefs)
|
||||
for n in [o.Name for o in doc.Objects]:
|
||||
if doc.getObject(n):
|
||||
|
||||
@@ -29,8 +29,6 @@ import os
|
||||
import FreeCAD
|
||||
import Draft
|
||||
import Arch
|
||||
from importers import exportIFC
|
||||
from importers import exportIFCHelper
|
||||
|
||||
import ifcopenshell
|
||||
from ifcopenshell import geom
|
||||
@@ -47,6 +45,7 @@ from nativeifc import ifc_viewproviders
|
||||
from nativeifc import ifc_import
|
||||
from nativeifc import ifc_layers
|
||||
from nativeifc import ifc_status
|
||||
from nativeifc import ifc_export
|
||||
|
||||
SCALE = 1000.0 # IfcOpenShell works in meters, FreeCAD works in mm
|
||||
SHORT = False # If True, only Step ID attribute is created
|
||||
@@ -739,8 +738,6 @@ def filter_elements(elements, ifcfile, expand=True, spaces=False, assemblies=Tru
|
||||
elements = [e for e in elements if not e.is_a("IfcProject")]
|
||||
# skip furniture for now, they can be lazy loaded probably
|
||||
elements = [e for e in elements if not e.is_a("IfcFurnishingElement")]
|
||||
# skip annotations for now
|
||||
elements = [e for e in elements if not e.is_a("IfcAnnotation")]
|
||||
return elements
|
||||
|
||||
|
||||
@@ -1011,7 +1008,10 @@ def aggregate(obj, parent, mode=None):
|
||||
ifcclass = None
|
||||
if mode == "opening":
|
||||
ifcclass = "IfcOpeningElement"
|
||||
product = create_product(obj, parent, ifcfile, ifcclass)
|
||||
if ifc_export.is_annotation(obj):
|
||||
product = ifc_export.create_annotation(obj, ifcfile)
|
||||
else:
|
||||
product = ifc_export.create_product(obj, parent, ifcfile, ifcclass)
|
||||
shapemode = getattr(parent, "ShapeMode", DEFAULT_SHAPEMODE)
|
||||
newobj = create_object(product, obj.Document, ifcfile, shapemode)
|
||||
new = True
|
||||
@@ -1065,69 +1065,6 @@ def deaggregate(obj, parent):
|
||||
parent.Proxy.removeObject(parent, obj)
|
||||
|
||||
|
||||
def create_product(obj, parent, ifcfile, ifcclass=None):
|
||||
"""Creates an IFC product out of a FreeCAD object"""
|
||||
|
||||
name = obj.Label
|
||||
description = getattr(obj, "Description", None)
|
||||
if not ifcclass:
|
||||
ifcclass = get_ifctype(obj)
|
||||
representation, placement, shapetype = create_representation(obj, ifcfile)
|
||||
product = api_run("root.create_entity", ifcfile, ifc_class=ifcclass, name=name)
|
||||
set_attribute(ifcfile, product, "Description", description)
|
||||
set_attribute(ifcfile, product, "ObjectPlacement", placement)
|
||||
# TODO below cannot be used at the moment because the ArchIFC exporter returns an
|
||||
# IfcProductDefinitionShape already and not an IfcShapeRepresentation
|
||||
# api_run("geometry.assign_representation", ifcfile, product=product, representation=representation)
|
||||
set_attribute(ifcfile, product, "Representation", representation)
|
||||
# additions
|
||||
if hasattr(obj,"Additions") and shapetype in ["extrusion","no shape"]:
|
||||
for addobj in obj.Additions:
|
||||
r2,p2,c2 = create_representation(addobj, ifcfile)
|
||||
cl2 = get_ifctype(addobj)
|
||||
addprod = api_run("root.create_entity", ifcfile, ifc_class=cl2, name=addobj.Label)
|
||||
set_attribute(ifcfile, addprod, "Description", getattr(addobj, "Description", ""))
|
||||
set_attribute(ifcfile, addprod, "ObjectPlacement", p2)
|
||||
set_attribute(ifcfile, addprod, "Representation", r2)
|
||||
create_relationship(None, addobj, product, addprod, ifcfile)
|
||||
# subtractions
|
||||
if hasattr(obj,"Subtractions") and shapetype in ["extrusion","no shape"]:
|
||||
for subobj in obj.Subtractions:
|
||||
r3,p3,c3 = create_representation(subobj, ifcfile)
|
||||
cl3 = "IfcOpeningElement"
|
||||
subprod = api_run("root.create_entity", ifcfile, ifc_class=cl3, name=subobj.Label)
|
||||
set_attribute(ifcfile, subprod, "Description", getattr(subobj, "Description", ""))
|
||||
set_attribute(ifcfile, subprod, "ObjectPlacement", p3)
|
||||
set_attribute(ifcfile, subprod, "Representation", r3)
|
||||
create_relationship(None, subobj, product, subprod, ifcfile)
|
||||
return product
|
||||
|
||||
|
||||
def create_representation(obj, ifcfile):
|
||||
"""Creates a geometry representation for the given object"""
|
||||
|
||||
# TEMPORARY use the Arch exporter
|
||||
# TODO this is temporary. We should rely on ifcopenshell for this with:
|
||||
# https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/root/create_entity/index.html
|
||||
# a new FreeCAD 'engine' should be added to:
|
||||
# https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/geometry/index.html
|
||||
# that should contain all typical use cases one could have to convert FreeCAD geometry
|
||||
# to IFC.
|
||||
|
||||
# setup exporter - TODO do that in the module init
|
||||
exportIFC.clones = {}
|
||||
exportIFC.profiledefs = {}
|
||||
exportIFC.surfstyles = {}
|
||||
exportIFC.shapedefs = {}
|
||||
exportIFC.ifcopenshell = ifcopenshell
|
||||
exportIFC.ifcbin = exportIFCHelper.recycler(ifcfile, template=False)
|
||||
prefs, context = get_export_preferences(ifcfile)
|
||||
representation, placement, shapetype = exportIFC.getRepresentation(
|
||||
ifcfile, context, obj, preferences=prefs
|
||||
)
|
||||
return representation, placement, shapetype
|
||||
|
||||
|
||||
def get_ifctype(obj):
|
||||
"""Returns a valid IFC type from an object"""
|
||||
|
||||
@@ -1144,20 +1081,6 @@ def get_ifctype(obj):
|
||||
return "IfcBuildingElementProxy"
|
||||
|
||||
|
||||
def get_export_preferences(ifcfile):
|
||||
"""returns a preferences dict for exportIFC"""
|
||||
|
||||
prefs = exportIFC.getPreferences()
|
||||
prefs["SCHEMA"] = ifcfile.wrapped_data.schema_name()
|
||||
s = ifcopenshell.util.unit.calculate_unit_scale(ifcfile)
|
||||
# the above lines yields meter -> file unit scale factor. We need mm
|
||||
prefs["SCALE_FACTOR"] = 0.001 / s
|
||||
context = ifcfile[
|
||||
get_body_context_ids(ifcfile)[0]
|
||||
] # we take the first one (first found subcontext)
|
||||
return prefs, context
|
||||
|
||||
|
||||
def get_subvolume(obj):
|
||||
"""returns a subface + subvolume from a window object"""
|
||||
|
||||
@@ -1240,7 +1163,7 @@ def create_relationship(old_obj, obj, parent, element, ifcfile, mode=None):
|
||||
if old_obj:
|
||||
tempface, tempobj = get_subvolume(old_obj)
|
||||
if tempobj:
|
||||
opening = create_product(tempobj, parent, ifcfile, "IfcOpeningElement")
|
||||
opening = ifc_export.create_product(tempobj, parent, ifcfile, "IfcOpeningElement")
|
||||
set_attribute(ifcfile, product, "Name", "Opening")
|
||||
old_obj.Document.removeObject(tempobj.Name)
|
||||
if tempface:
|
||||
|
||||
Reference in New Issue
Block a user