diff --git a/src/bonsai/bonsai/bim/data/pset/Psets_BBIM_Annotation.ifc b/src/bonsai/bonsai/bim/data/pset/Psets_BBIM_Annotation.ifc index dd4851f9c5..451c890536 100644 --- a/src/bonsai/bonsai/bim/data/pset/Psets_BBIM_Annotation.ifc +++ b/src/bonsai/bonsai/bim/data/pset/Psets_BBIM_Annotation.ifc @@ -5,10 +5,10 @@ FILE_NAME('Psets_BBIM_Annotation.ifc','2020-01-01T00:00:00',$,$,'Psets_BBIM_Anno FILE_SCHEMA(('IFC4')); ENDSEC; DATA; -#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation,IfcTypeProduct',(#2,#3,#4,#29)); +#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation,IfcTypeProduct',(#4,#33,#29,#32,#3,#2)); #2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.); #3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); -#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','Classes separarated by spaces that end up in classes for this element in svg. Can be used to specify the text font size: small - 1.8mm; regular - 2.5mm; large - 3.5mm; header - 5mm; title - 7mm. By default regular size is used.',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); +#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','Classes separated by spaces that end up in classes for this element in svg. Can be used to specify the text font size: small - 1.8mm; regular - 2.5mm; large - 3.5mm; header - 5mm; title - 7mm. By default regular size is used.',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); #5=IFCPROPERTYSETTEMPLATE('0iKwujnQL9IevVQato8f7Z',$,'BBIM_Batting','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation/BATTING,IfcTypeProduct',(#6,#7)); #6=IFCSIMPLEPROPERTYTEMPLATE('0t2LEesGT1QRQtrIZUAR8L',$,'Thickness','Batting thickness',.P_SINGLEVALUE.,'IfcPositiveLengthMeasure',$,$,$,$,$,.READWRITE.); #7=IFCSIMPLEPROPERTYTEMPLATE('082PndS6v2kBOiJoSboMnh',$,'Reverse pattern direction','Reverse batting pattern (swap starting and ending points)',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); @@ -36,5 +36,7 @@ DATA; #29=IFCSIMPLEPROPERTYTEMPLATE('2pJmUDpB50VBdCOib1zcJJ',$,'Newline_At','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.); #30=IFCSIMPLEPROPERTYTEMPLATE('2TJn72t_v2cvBUG916Dpev',$,'CustomUnit','Dimension''s custom unit',.P_ENUMERATEDVALUE.,'IfcText',$,#31,$,$,$,.READWRITE.); #31=IFCPROPERTYENUMERATION('CustomUnit',(IFCTEXT('Feet and Inches - Fractional'),IFCTEXT('Feet - Decimal'),IFCTEXT('Inches - Fractional'),IFCTEXT('Inches - Decimal'),IFCTEXT('Meters'),IFCTEXT('Decimeters'),IFCTEXT('Centimeters'),IFCTEXT('Millimeters')),$); +#32=IFCSIMPLEPROPERTYTEMPLATE('0gjJzDYBX8P85qn1xcAOOo',$,'Reverse_List','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); +#33=IFCSIMPLEPROPERTYTEMPLATE('22TrcxF8jFNB4buSmzjGEF',$,'List_Separator','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); ENDSEC; END-ISO-10303-21; diff --git a/src/bonsai/bonsai/bim/module/drawing/data.py b/src/bonsai/bonsai/bim/module/drawing/data.py index c4b6c1c7c3..b31b1371f4 100644 --- a/src/bonsai/bonsai/bim/module/drawing/data.py +++ b/src/bonsai/bonsai/bim/module/drawing/data.py @@ -347,12 +347,10 @@ class DecoratorData: (font_size_type for font_size_type in FONT_SIZES if font_size_type in classes_split), "regular" ) font_size = FONT_SIZES[font_size_type] - - # get symbol symbol = tool.Drawing.get_annotation_symbol(element) - - # get newline_at newline_at = pset_data.get("Newline_At", 0) + reverse_list = pset_data.get("Reverse_List", False) + list_separator = pset_data.get("List_Separator") or ", " # other attributes literals = tool.Drawing.get_text_literal(obj, return_list=True) @@ -364,11 +362,20 @@ class DecoratorData: literal_data = { "Literal": literal_value, "BoxAlignment": literal.BoxAlignment, - "CurrentValue": tool.Drawing.replace_text_literal_variables(literal_value, product), + "CurrentValue": tool.Drawing.replace_text_literal_variables( + literal_value, product, reverse_list, list_separator + ), } literals_data.append(literal_data) - return {"Literals": literals_data, "FontSize": font_size, "Symbol": symbol, "Newline_At": newline_at} + return { + "Literals": literals_data, + "FontSize": font_size, + "Symbol": symbol, + "Newline_At": newline_at, + "Reverse_List": reverse_list, + "List_Separator": list_separator, + } @classmethod def get_dimension_data(cls, obj: bpy.types.Object) -> dict[str, Any]: diff --git a/src/bonsai/bonsai/bim/module/drawing/prop.py b/src/bonsai/bonsai/bim/module/drawing/prop.py index 49e39551eb..fb5c05679a 100644 --- a/src/bonsai/bonsai/bim/module/drawing/prop.py +++ b/src/bonsai/bonsai/bim/module/drawing/prop.py @@ -744,6 +744,11 @@ class BIMTextProperties(PropertyGroup): name="Font Size", ) newline_at: IntProperty(name="Newline At") + reverse_list: BoolProperty(name="Reverse List", description="Reverses the order of any list.", default=False) + list_separator: StringProperty( # pyright: ignore[reportRedeclaration] + name="List Separator", + description="Text used to separate lists. Uses a comma (, ) if empty.", + ) symbol: EnumProperty( # pyright: ignore[reportRedeclaration] name="Symbol", description="Symbol from symbols.svg to use for this text.", @@ -760,6 +765,8 @@ class BIMTextProperties(PropertyGroup): literals: bpy.types.bpy_prop_collection_idprop[LiteralProps] font_size: str newline_at: int + reverse_list: bool + list_separator: str symbol: Union[str, Literal["NO SYMBOL", "CUSTOM SYMBOL"]] custom_symbol: str @@ -796,6 +803,8 @@ class BIMTextProperties(PropertyGroup): "FontSize": float(self.font_size), "Newline_At": int(self.newline_at), "Symbol": self.get_symbol(), + "Reverse_List": self.reverse_list, + "List_Separator": self.list_separator or ", ", } return text_data diff --git a/src/bonsai/bonsai/bim/module/drawing/ui.py b/src/bonsai/bonsai/bim/module/drawing/ui.py index 8fb0c5743e..905bc8e287 100644 --- a/src/bonsai/bonsai/bim/module/drawing/ui.py +++ b/src/bonsai/bonsai/bim/module/drawing/ui.py @@ -590,6 +590,10 @@ class BIM_PT_text(Panel): row.prop(props, "font_size") row = self.layout.row(align=True) row.prop(props, "newline_at") + row = self.layout.row(align=True) + row.prop(props, "reverse_list") + row = self.layout.row(align=True) + row.prop(props, "list_separator") row = self.layout.row(align=True) row.prop(props, "symbol") @@ -648,6 +652,12 @@ class BIM_PT_text(Panel): row = self.layout.row(align=True) row.label(text="Newline_At") row.label(text=str(text_data["Newline_At"])) + row = self.layout.row(align=True) + row.label(text="Reverse_List") + row.label(text=str(text_data["Reverse_List"])) + row = self.layout.row(align=True) + row.label(text="List_Separator") + row.label(text=str(text_data["List_Separator"])) for literal_data in text_data["Literals"]: box = self.layout.box() diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index fd3f679270..2763f72ea0 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -39,7 +39,7 @@ def disable_editing_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> def edit_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: drawing.synchronise_ifc_and_text_attributes(obj) drawing.update_text_size_pset(obj) - drawing.update_newline_at_and_symbol(obj) + drawing.update_text_annotation_properties(obj) drawing.disable_editing_text(obj) diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py index abef38836e..0124ec1430 100644 --- a/src/bonsai/bonsai/core/tool.py +++ b/src/bonsai/bonsai/core/tool.py @@ -394,7 +394,7 @@ class Drawing: def sync_object_placement(cls, obj): pass def synchronise_ifc_and_text_attributes(cls, obj): pass def update_embedded_svg_location(cls, uri, old_location, new_location): pass - def update_newline_at_and_symbol(cls, obj): pass + def update_text_annotation_properties(cls, obj): pass def update_text_size_pset(cls, obj): pass diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 8227b9dbfd..5a01923103 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -1098,6 +1098,8 @@ class Drawing(bonsai.core.tool.Drawing): props.font_size = str(text_data["FontSize"]) props.newline_at = text_data["Newline_At"] props.set_symbol(text_data["Symbol"]) + props.reverse_list = text_data["Reverse_List"] + props.list_separator = text_data["List_Separator"] @classmethod def import_assigned_product(cls, obj: bpy.types.Object) -> None: @@ -1235,25 +1237,29 @@ class Drawing(bonsai.core.tool.Drawing): ) @classmethod - def update_newline_at_and_symbol(cls, obj: bpy.types.Object) -> None: + def update_text_annotation_properties(cls, obj: bpy.types.Object) -> None: + """Update all EPset_Annotation properties from the text props""" props = cls.get_text_props(obj) element = tool.Ifc.get_entity(obj) assert element - newline_at = int(props.newline_at) - symbol = props.get_symbol() + ifc_file = tool.Ifc.get() pset = tool.Pset.get_element_pset(element, "EPset_Annotation") if not pset: pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="EPset_Annotation") + ifcopenshell.api.pset.edit_pset( ifc_file, pset=pset, properties={ - "Newline_At": newline_at, - "Symbol": symbol, + "Newline_At": int(props.newline_at), + "Symbol": props.get_symbol(), + "Reverse_List": props.reverse_list, + "List_Separator": props.list_separator or "", }, ) + # TODO below this point is highly experimental prototype code with no tests class SheetWarningType(NamedTuple): @@ -1834,10 +1840,18 @@ class Drawing(bonsai.core.tool.Drawing): return bool(a_tree.overlap(b_tree)) @classmethod - def replace_text_literal_variables(cls, text: str, product: Optional[ifcopenshell.entity_instance] = None) -> str: + def replace_text_literal_variables( + cls, + text: str, + product: Optional[ifcopenshell.entity_instance] = None, + reverse_list: bool = False, + list_separator: str = ", " + ) -> str: if not product: return text - + if list_separator: + list_separator = list_separator.encode().decode('unicode_escape') + for command in re.findall("``.*?``", text): original_command = command for variable in re.findall("{{.*?}}", command): @@ -1845,13 +1859,14 @@ class Drawing(bonsai.core.tool.Drawing): value = '"' + str(value).replace('"', '\\"') + '"' command = command.replace(variable, value) text = text.replace(original_command, ifcopenshell.util.selector.format(command[2:-2])) - for variable in re.findall("{{.*?}}", text): value = ifcopenshell.util.selector.get_element_value(product, variable[2:-2]) if isinstance(value, (list, tuple)): - value = ", ".join(str(v) for v in value) + if reverse_list: + value = list_separator.join(str(v) for v in reversed(value)) + else: + value = list_separator.join(str(v) for v in value) text = text.replace(variable, str(value)) - return text @classmethod diff --git a/src/bonsai/test/core/test_drawing.py b/src/bonsai/test/core/test_drawing.py index 875efba93c..e12c7dd0f8 100644 --- a/src/bonsai/test/core/test_drawing.py +++ b/src/bonsai/test/core/test_drawing.py @@ -37,7 +37,7 @@ class TestEditText: def test_run(self, drawing): drawing.synchronise_ifc_and_text_attributes("obj").should_be_called() drawing.update_text_size_pset("obj").should_be_called() - drawing.update_newline_at_and_symbol("obj").should_be_called() + drawing.update_text_annotation_properties("obj").should_be_called() drawing.disable_editing_text("obj").should_be_called() subject.edit_text(drawing, obj="obj")