mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Fix #2352. Text styling is now possible with custom classes.
This commit is contained in:
@@ -5,9 +5,9 @@ FILE_NAME('EPset_Annotation.ifc','2020-01-01T00:00:00',(),(),'EPset_Annotation',
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3));
|
||||
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4));
|
||||
#2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_ENUMERATEDVALUE.,'IfcLabel',$,#4,$,$,$,.READWRITE.);
|
||||
#4=IFCPROPERTYENUMERATION('Symbol',(IFCLABEL('rectangle-tag'), IFCLABEL('door-tag')),$);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
* { stroke-linecap: round; stroke-linejoin: round; }
|
||||
text { fill: black; stroke: none; }
|
||||
text { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT'; font-size: 4.13px; dominant-baseline: baseline; text-anchor: start; }
|
||||
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; }
|
||||
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||
@@ -44,6 +44,11 @@ text { fill: black; stroke: none; }
|
||||
.PredefinedType-TEXT { fill: black; stroke: none; }
|
||||
path.PredefinedType-TEXTLEADER { marker-end: url(#leader-marker); }
|
||||
text.PredefinedType-TEXTLEADER { fill: black; stroke: none; }
|
||||
text.title { /* 7mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT'; font-size: 11.55px; }
|
||||
text.header { /* 5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT'; font-size: 8.25px; }
|
||||
text.large { /* 3.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT'; font-size: 5.78px; }
|
||||
text.regular { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT'; font-size: 4.13px; }
|
||||
text.small { /* 1.8mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT'; font-size: 2.97px; }
|
||||
.material-blank { fill: white; }
|
||||
.material-diagonal1 { fill: url(#diagonal1); }
|
||||
.material-diagonal2 { fill: url(#diagonal2); }
|
||||
|
||||
@@ -23,17 +23,6 @@ from mathutils import Vector
|
||||
|
||||
|
||||
class Annotator:
|
||||
@staticmethod
|
||||
def get_svg_text_size(size):
|
||||
sizes = {
|
||||
"1.8": "2.97",
|
||||
"2.5": "4.13",
|
||||
"3.5": "5.78",
|
||||
"5.0": "8.25",
|
||||
"7.0": "11.55",
|
||||
}
|
||||
return float(sizes[str(size)])
|
||||
|
||||
@staticmethod
|
||||
def add_text(related_element=None):
|
||||
curve = bpy.data.curves.new(type="FONT", name="Text")
|
||||
|
||||
@@ -202,8 +202,6 @@ class SvgWriter:
|
||||
rl *= unit_scale
|
||||
rl = "{:.3f}m".format(rl)
|
||||
text_style = {
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "start",
|
||||
"alignment-baseline": "baseline",
|
||||
"dominant-baseline": "baseline",
|
||||
@@ -236,8 +234,6 @@ class SvgWriter:
|
||||
"UP",
|
||||
insert=tuple(text_position),
|
||||
**{
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
"alignment-baseline": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
@@ -270,8 +266,6 @@ class SvgWriter:
|
||||
axis_tag,
|
||||
insert=tuple(start * self.svg_scale),
|
||||
**{
|
||||
"font-size": annotation.Annotator.get_svg_text_size(5.0),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
"alignment-baseline": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
@@ -283,8 +277,6 @@ class SvgWriter:
|
||||
axis_tag,
|
||||
insert=tuple(end * self.svg_scale),
|
||||
**{
|
||||
"font-size": annotation.Annotator.get_svg_text_size(5.0),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
"alignment-baseline": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
@@ -325,6 +317,9 @@ class SvgWriter:
|
||||
str(ifcopenshell.util.element.get_predefined_type(element))
|
||||
)
|
||||
classes = [global_id, element.is_a(), predefined_type]
|
||||
custom_classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
|
||||
if custom_classes:
|
||||
classes.extend(custom_classes.split())
|
||||
for key in self.metadata:
|
||||
value = ifcopenshell.util.selector.Selector.get_element_value(element, key)
|
||||
if value:
|
||||
@@ -413,8 +408,6 @@ class SvgWriter:
|
||||
reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
|
||||
text_position = list(symbol_position * self.svg_scale)
|
||||
text_style = {
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
"alignment-baseline": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
@@ -446,8 +439,6 @@ class SvgWriter:
|
||||
reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
|
||||
text_position = list(symbol_position * self.svg_scale)
|
||||
text_style = {
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
"alignment-baseline": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
@@ -544,8 +535,6 @@ class SvgWriter:
|
||||
insert=tuple((text_position * self.svg_scale) + Vector((0, 3.5 * line_number))),
|
||||
class_=" ".join(self.get_attribute_classes(text_obj)),
|
||||
**{
|
||||
"font-size": annotation.Annotator.get_svg_text_size(text_obj.BIMTextProperties.font_size),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": text_anchor,
|
||||
"alignment-baseline": alignment_baseline,
|
||||
"dominant-baseline": alignment_baseline,
|
||||
@@ -618,8 +607,6 @@ class SvgWriter:
|
||||
"RL +{}".format(rl),
|
||||
insert=tuple(text_position),
|
||||
**{
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": text_anchor,
|
||||
"alignment-baseline": "baseline",
|
||||
"dominant-baseline": "baseline",
|
||||
@@ -740,8 +727,6 @@ class SvgWriter:
|
||||
text_position += text_offset
|
||||
|
||||
text_style = {
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
"alignment-baseline": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
@@ -808,8 +793,6 @@ class SvgWriter:
|
||||
text_position += text_offset
|
||||
|
||||
text_style = {
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
"alignment-baseline": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
@@ -898,8 +881,6 @@ class SvgWriter:
|
||||
insert=tuple(text_position),
|
||||
**{
|
||||
"transform": "rotate({} {} {})".format(rotation, text_position.x, text_position.y),
|
||||
"font-size": annotation.Annotator.get_svg_text_size(2.5),
|
||||
"font-family": "OpenGost Type B TT",
|
||||
"text-anchor": "middle",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -355,11 +355,6 @@ class BIM_PT_text(Panel):
|
||||
row.label(text=attribute["name"])
|
||||
row.label(text=attribute["value"])
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(props, "font_size")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "symbol")
|
||||
|
||||
|
||||
class BIM_PT_annotation_utilities(Panel):
|
||||
bl_idname = "BIM_PT_annotation_utilities"
|
||||
|
||||
@@ -160,6 +160,8 @@ class OpenUpstream(bpy.types.Operator):
|
||||
webbrowser.open("https://wiki.osarch.org/index.php?title=Category:BlenderBIM_Add-on")
|
||||
elif self.page == "community":
|
||||
webbrowser.open("https://community.osarch.org/")
|
||||
elif self.page == "fund":
|
||||
webbrowser.open("https://opencollective.com/opensourcebim")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -962,11 +962,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def get_annotation_z_index(cls, drawing):
|
||||
return ifcopenshell.util.element.get_psets(drawing).get("EPset_Annotation", {}).get("ZIndex", 0)
|
||||
return ifcopenshell.util.element.get_pset(drawing, "EPset_Annotation", "ZIndex") or 0
|
||||
|
||||
@classmethod
|
||||
def get_annotation_symbol(cls, drawing):
|
||||
return ifcopenshell.util.element.get_psets(drawing).get("EPset_Annotation", {}).get("Symbol", None)
|
||||
return ifcopenshell.util.element.get_pset(drawing, "EPset_Annotation", "Symbol")
|
||||
|
||||
@classmethod
|
||||
def has_linework(cls, drawing):
|
||||
|
||||
@@ -19,20 +19,95 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True):
|
||||
"""Retrieve property sets, their related properties' names & values and ids.
|
||||
def get_pset(element, name, prop=None, should_inherit=True):
|
||||
"""Retrieve a single property set or single property
|
||||
|
||||
This is more efficient than ifcopenshell.util.element.get_psets if you know
|
||||
exactly which property set and property you are after.
|
||||
|
||||
:param element: The IFC Element entity
|
||||
:param psets_only: Default as False. Set to true if only property sets are needed.
|
||||
:param qtos_only: Default as False. Set to true if only quantities are needed.
|
||||
:type element: ifcopenshell.entity_instance.entity_instance
|
||||
:param name: The name of the pset
|
||||
:type name: str
|
||||
:param prop: The name of the property
|
||||
:type name: str,optional
|
||||
:param should_inherit: Default as True. Set to false if you don't want to inherit property sets from the Type.
|
||||
:return: dictionnary: key, value pair of psets' names and their properties' names & values
|
||||
:type should_inherit: bool,optional
|
||||
:return: A dictionary of property names and values, or a single value if a
|
||||
property is specified.
|
||||
:rtype: dict
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
element = ifcopenshell.by_type("IfcBuildingElement")[0]
|
||||
element = ifcopenshell.by_type("IfcWall")[0]
|
||||
psets_and_qtos = ifcopenshell.util.element.get_pset(element, "Pset_WallCommon")
|
||||
"""
|
||||
pset = None
|
||||
if element.is_a("IfcTypeObject"):
|
||||
for definition in element.HasPropertySets or []:
|
||||
if definition.Name == name:
|
||||
pset = definition
|
||||
break
|
||||
elif element.is_a("IfcMaterialDefinition") or element.is_a("IfcProfileDef"):
|
||||
for definition in element.HasProperties or []:
|
||||
if definition.Name == name:
|
||||
pset = definition
|
||||
break
|
||||
elif hasattr(element, "IsDefinedBy"):
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type and should_inherit:
|
||||
result = get_pset(element_type, name, prop, should_inherit=False)
|
||||
if result:
|
||||
return result
|
||||
for relationship in element.IsDefinedBy:
|
||||
if relationship.is_a("IfcRelDefinesByProperties"):
|
||||
definition = relationship.RelatingPropertyDefinition
|
||||
if definition.Name == name:
|
||||
pset = definition
|
||||
break
|
||||
|
||||
if not pset:
|
||||
return
|
||||
|
||||
if not prop:
|
||||
return get_property_definition(pset)
|
||||
|
||||
if definition.is_a("IfcElementQuantity"):
|
||||
return get_quantity(definition.Quantities, prop)
|
||||
elif definition.is_a("IfcPropertySet"):
|
||||
return get_property(definition.HasProperties, prop)
|
||||
elif definition.is_a("IfcMaterialProperties") or definition.is_a("IfcProfileProperties"):
|
||||
return get_property(definition.Properties, prop)
|
||||
else:
|
||||
# Entity introduced in IFC4
|
||||
# definition.is_a('IfcPreDefinedPropertySet'):
|
||||
for i in range(4, len(definition)):
|
||||
if definition[i] is not None:
|
||||
if definition.attribute_name(i) == prop:
|
||||
return definition[i]
|
||||
|
||||
|
||||
def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True):
|
||||
"""Retrieve property sets, their related properties' names & values and ids.
|
||||
|
||||
:param element: The IFC Element entity
|
||||
:type element: ifcopenshell.entity_instance.entity_instance
|
||||
:param psets_only: Default as False. Set to true if only property sets are needed.
|
||||
:type psets_only: bool,optional
|
||||
:param qtos_only: Default as False. Set to true if only quantities are needed.
|
||||
:type qtos_only: bool,optional
|
||||
:param should_inherit: Default as True. Set to false if you don't want to inherit property sets from the Type.
|
||||
:type should_inherit: bool,optional
|
||||
:return: Key, value pair of psets' names and their properties' names & values
|
||||
:rtype: dict
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
element = ifcopenshell.by_type("IfcWall")[0]
|
||||
psets = ifcopenshell.util.element.get_psets(element, psets_only=True)
|
||||
qsets = ifcopenshell.util.element.get_psets(element, qtos_only=True)
|
||||
psets_and_qtos = ifcopenshell.util.element.get_psets(element)
|
||||
@@ -84,7 +159,21 @@ def get_property_definition(definition):
|
||||
return props
|
||||
|
||||
|
||||
def get_quantities(quantities):
|
||||
def get_quantity(quantities, name):
|
||||
for quantity in quantities or []:
|
||||
if quantity.Name != name:
|
||||
continue
|
||||
if quantity.is_a("IfcPhysicalSimpleQuantity"):
|
||||
return quantity[3]
|
||||
results[quantity.Name] = quantity[3]
|
||||
elif quantity.is_a("IfcPhysicalComplexQuantity"):
|
||||
data = {k: v for k, v in quantity.get_info().items() if v is not None and k != "Name"}
|
||||
data["properties"] = get_quantities(quantity.HasQuantities)
|
||||
del data["HasQuantities"]
|
||||
return data
|
||||
|
||||
|
||||
def get_quantities(quantities, name=None):
|
||||
results = {}
|
||||
for quantity in quantities or []:
|
||||
if quantity.is_a("IfcPhysicalSimpleQuantity"):
|
||||
@@ -97,6 +186,29 @@ def get_quantities(quantities):
|
||||
return results
|
||||
|
||||
|
||||
def get_property(properties, name):
|
||||
for prop in properties or []:
|
||||
if prop.Name != name:
|
||||
continue
|
||||
if prop.is_a("IfcPropertySingleValue"):
|
||||
return prop.NominalValue.wrappedValue if prop.NominalValue else None
|
||||
elif prop.is_a("IfcPropertyEnumeratedValue"):
|
||||
return [v.wrappedValue for v in prop.EnumerationValues] if prop.EnumerationValues else None
|
||||
elif prop.is_a("IfcPropertyListValue"):
|
||||
return [v.wrappedValue for v in prop.ListValues] or None
|
||||
elif prop.is_a("IfcPropertyBoundedValue"):
|
||||
data = prop.get_info()
|
||||
del data["Unit"]
|
||||
return data
|
||||
elif prop.is_a("IfcPropertyTableValue"):
|
||||
return prop.get_info()
|
||||
elif prop.is_a("IfcComplexProperty"):
|
||||
data = {k: v for k, v in prop.get_info().items() if v is not None and k != "Name"}
|
||||
data["properties"] = get_properties(prop.HasProperties)
|
||||
del data["HasProperties"]
|
||||
return data
|
||||
|
||||
|
||||
def get_properties(properties):
|
||||
results = {}
|
||||
for prop in properties or []:
|
||||
|
||||
Reference in New Issue
Block a user