BIM: NativeIFC 2D support - texts

This commit is contained in:
Yorik van Havre
2024-09-26 13:18:03 +02:00
committed by Yorik van Havre
parent 29ce0ad1d8
commit 1ba6bead9d
4 changed files with 104 additions and 40 deletions

View File

@@ -2443,11 +2443,9 @@ def create_annotation(anno, ifcfile, context, history, preferences):
# 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)
ovc = None
zvc = None
xvc = None
if anno.isDerivedFrom("Part::Feature"):
if Draft.getType(anno) == "Hatch":
objectType = "HATCH"
@@ -2477,18 +2475,20 @@ def create_annotation(anno, ifcfile, context, history, preferences):
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))
pos = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0))
tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None)
ovc = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
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)
pos = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0))
tpl = ifcbin.createIfcAxis2Placement3D(pos,None,None)
ovc = ifcbin.createIfcCartesianPoint((l.x,l.y,l.z))
zvc = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))))
xvc = ifcbin.createIfcDirection(tuple(anno.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0))))
alg = "LEFT"
if FreeCAD.GuiUp and hasattr(anno.ViewObject,"Justification"):
if anno.ViewObject.Justification == "Right":
@@ -2546,7 +2546,14 @@ def create_annotation(anno, ifcfile, context, history, preferences):
for rep in reps:
isi = ifcfile.createIfcStyledItem(rep,[psa],None)
break
if not xvc:
xvc = ifcbin.createIfcDirection((1.0,0.0,0.0))
if not zvc:
zvc = ifcbin.createIfcDirection((0.0,0.0,1.0))
if not ovc:
ovc = ifcbin.createIfcCartesianPoint((0.0,0.0,0.0))
gpl = ifcbin.createIfcAxis2Placement3D(ovc,zvc,xvc)
placement = ifcbin.createIfcLocalPlacement(gpl)
shp = ifcfile.createIfcShapeRepresentation(context,'Annotation','Annotation2D',reps)
rep = ifcfile.createIfcProductDefinitionShape(None,None,[shp])
label = anno.Label

View File

@@ -26,6 +26,7 @@ import ifcopenshell
from importers import exportIFC
from importers import exportIFCHelper
from importers import importIFCHelper
from nativeifc import ifc_tools
@@ -116,6 +117,17 @@ def is_annotation(obj):
return False
def get_text(annotation):
"""Determines if an IfcAnnotation contains an IfcTextLiteral.
Returns the IfcTextLiteral or None"""
for rep in annotation.Representation.Representations:
for item in rep.Items:
if item.is_a("IfcTextLiteral"):
return item
return None
def create_annotation(obj, ifcfile):
"""Adds an IfcAnnotation from the given object to the given IFC file"""
@@ -144,3 +156,10 @@ def get_history(ifcfile):
# IFC4 allows to not write any history
history = None
return history
def get_placement(ifcelement, ifcfile):
"""Returns a FreeCAD placement from an IFC placement"""
s = 0.001 / ifcopenshell.util.unit.calculate_unit_scale(ifcfile)
return importIFCHelper.getPlacement(ifcelement, scaling=s)

View File

@@ -64,6 +64,8 @@ class ifc_object:
self.edit_attribute(obj, prop)
elif prop == "Label":
self.edit_attribute(obj, "Name", obj.Label)
elif prop == "Text":
self.edit_annotation(obj, "Text", "\n".join(obj.Text))
elif prop == "Placement":
if getattr(self, "virgin_placement", False):
self.virgin_placement = False
@@ -169,6 +171,22 @@ class ifc_object:
if hasattr(result, "id") and (result.id() != obj.StepId):
obj.StepId = result.id()
def edit_annotation(self, obj, attribute, value=None):
"""Edits an attribute of an underlying IFC annotation"""
from nativeifc import ifc_tools # lazy import
from nativeifc import ifc_export
if not value:
value = obj.getPropertyByName(attribute)
ifcfile = ifc_tools.get_ifcfile(obj)
elt = ifc_tools.get_ifc_element(obj, ifcfile)
if elt:
if attribute == "Text":
text = ifc_export.get_text(elt)
if text:
result = ifc_tools.set_attribute(ifcfile, text, "Literal", value)
def edit_geometry(self, obj, prop):
"""Edits a geometry property of an object"""

View File

@@ -47,6 +47,8 @@ from nativeifc import ifc_layers
from nativeifc import ifc_status
from nativeifc import ifc_export
from draftviewproviders import view_layer
SCALE = 1000.0 # IfcOpenShell works in meters, FreeCAD works in mm
SHORT = False # If True, only Step ID attribute is created
ROUND = 8 # rounding value for placements
@@ -243,7 +245,7 @@ def api_run(*args, **kwargs):
return result
def create_object(ifcentity, document, ifcfile, shapemode=0):
def create_object(ifcentity, document, ifcfile, shapemode=0, objecttype=None):
"""Creates a FreeCAD object from an IFC entity"""
exobj = get_object(ifcentity, document)
@@ -253,7 +255,7 @@ def create_object(ifcentity, document, ifcfile, shapemode=0):
ifcentity.id(), ifcentity.is_a(), ifcentity.Name
)
FreeCAD.Console.PrintLog(s)
obj = add_object(document)
obj = add_object(document, otype=objecttype)
add_properties(obj, ifcfile, ifcentity, shapemode=shapemode)
ifc_layers.add_layers(obj, ifcentity, ifcfile)
if FreeCAD.GuiUp:
@@ -460,37 +462,41 @@ def can_expand(obj, ifcfile=None):
def add_object(document, otype=None, oname="IfcObject"):
"""adds a new object to a FreeCAD document.
otype can be 'project', 'group', 'material', 'layer' or None (normal object)"""
otype can be:
'project',
'group',
'material',
'layer',
'text',
or anything else for a standard IFC object"""
if not document:
return None
proxy = ifc_objects.ifc_object(otype)
if otype == "group":
proxy = None
ftype = "App::DocumentObjectGroupPython"
elif otype == "material":
ftype = "App::MaterialObjectPython"
if otype == "text":
obj = Draft.make_text("")
obj.Proxy = ifc_objects.ifc_object(otype)
elif otype == "layer":
ftype = "App::FeaturePython"
else:
ftype = "Part::FeaturePython"
if otype == "project":
vp = ifc_viewproviders.ifc_vp_document()
proxy = ifc_objects.ifc_object(otype)
obj = document.addObject("App::FeaturePython", oname, proxy, None, False)
if obj.ViewObject:
view_layer.ViewProviderLayer(obj.ViewObject)
obj.ViewObject.addProperty("App::PropertyBool", "HideChildren", "Layer")
obj.ViewObject.HideChildren = True
elif otype == "group":
vp = ifc_viewproviders.ifc_vp_group()
vproxy = ifc_viewproviders.ifc_vp_group()
obj = document.addObject("App::DocumentObjectGroupPython", oname, None, vproxy, False)
elif otype == "material":
vp = ifc_viewproviders.ifc_vp_material()
elif otype == "layer":
vp = None
else:
vp = ifc_viewproviders.ifc_vp_object()
obj = document.addObject(ftype, oname, proxy, vp, False)
if obj.ViewObject and otype == "layer":
from draftviewproviders import view_layer # lazy import
view_layer.ViewProviderLayer(obj.ViewObject)
obj.ViewObject.addProperty("App::PropertyBool", "HideChildren", "Layer")
obj.ViewObject.HideChildren = True
proxy = ifc_objects.ifc_object(otype)
vproxy = ifc_viewproviders.ifc_vp_material()
obj = document.addObject("App::MaterialObjectPython", oname, proxy, vproxy, False)
elif otype == "project":
proxy = ifc_objects.ifc_object(otype)
vproxy = ifc_viewproviders.ifc_vp_document()
obj = document.addObject("Part::FeaturePython", oname, proxy, vproxy, False)
else: # default case, standard IFC object
proxy = ifc_objects.ifc_object(otype)
vproxy = ifc_viewproviders.ifc_vp_object()
obj = document.addObject("Part::FeaturePython", oname, proxy, vproxy, False)
return obj
@@ -621,6 +627,17 @@ def add_properties(
obj.addProperty("App::PropertyString", attr, "IFC")
if value is not None:
setattr(obj, attr, str(value))
# annotation properties
if ifcentity.is_a("IfcAnnotation"):
text = ifc_export.get_text(ifcentity)
if text:
# the two props below are already taken care of, normally
if "Placement" not in obj.PropertiesList:
obj.addProperty("App::PropertyPlacement", "Placement", "Base")
if "Text" not in obj.PropertiesList:
obj.addProperty("App::PropertyStringList", "Text", "Base")
obj.Text = [text.Literal]
obj.Placement = ifc_export.get_placement(ifcentity.ObjectPlacement, ifcfile)
# link Label2 and Description
if "Description" in obj.PropertiesList and hasattr(obj, "setExpression"):
obj.setExpression("Label2", "Description")
@@ -1008,12 +1025,15 @@ def aggregate(obj, parent, mode=None):
ifcclass = None
if mode == "opening":
ifcclass = "IfcOpeningElement"
objecttype = None
if ifc_export.is_annotation(obj):
product = ifc_export.create_annotation(obj, ifcfile)
if Draft.get_type(obj) in ["DraftText","Text"]:
objecttype = "text"
else:
product = ifc_export.create_product(obj, parent, ifcfile, ifcclass)
shapemode = getattr(parent, "ShapeMode", DEFAULT_SHAPEMODE)
newobj = create_object(product, obj.Document, ifcfile, shapemode)
newobj = create_object(product, obj.Document, ifcfile, shapemode, objecttype)
new = True
create_relationship(obj, newobj, parent, product, ifcfile, mode)
base = getattr(obj, "Base", None)
@@ -1317,7 +1337,7 @@ def remove_ifc_element(obj,delete_obj=False):
def get_orphan_elements(ifcfile):
"""returns a list of orphan products in an ifcfile"""
products = ifcfile.by_type("IfcElement")
products = ifcfile.by_type("IfcProduct")
products = [p for p in products if not p.Decomposes]
products = [p for p in products if not p.ContainedInStructure]
products = [