diff --git a/src/bonsai/bonsai/bim/module/context/data.py b/src/bonsai/bonsai/bim/module/context/data.py index c2717557a0..c0b2143801 100644 --- a/src/bonsai/bonsai/bim/module/context/data.py +++ b/src/bonsai/bonsai/bim/module/context/data.py @@ -52,12 +52,19 @@ class ContextData: def get_subcontexts(cls, context: ifcopenshell.entity_instance) -> list[dict[str, Any]]: results = [] for subcontext in context.HasSubContexts: + target_scale_denominator = None + if subcontext.TargetScale is not None: + scale_value = float(subcontext.TargetScale) + if scale_value > 0: + target_scale_denominator = 1.0 / scale_value + results.append( { "id": subcontext.id(), "context_type": subcontext.ContextType, "context_identifier": subcontext.ContextIdentifier, "target_view": subcontext.TargetView, + "target_scale_denominator": target_scale_denominator, } ) return results diff --git a/src/bonsai/bonsai/bim/module/context/ui.py b/src/bonsai/bonsai/bim/module/context/ui.py index 8cc3cf4799..e8d0d337b4 100644 --- a/src/bonsai/bonsai/bim/module/context/ui.py +++ b/src/bonsai/bonsai/bim/module/context/ui.py @@ -84,5 +84,26 @@ class BIM_PT_context(bpy.types.Panel): row.label(text=subcontext["context_type"]) row.label(text=subcontext["context_identifier"]) row.label(text=subcontext["target_view"]) + + scale_col = row.column() + scale_col.ui_units_x = 6 + + if subcontext["target_scale_denominator"] is not None: + scale_value = subcontext["target_scale_denominator"] + + if scale_value == int(scale_value): + scale_str = str(int(scale_value)) + if len(scale_str) > 4: + scale_str = f"{float(scale_value):.3g}" + else: + scale_str = f"{scale_value:.4g}" + + if len(scale_str) > 4: + scale_str = f"{float(scale_value):.3g}" + + scale_col.label(text=f"1:{scale_str}") + else: + scale_col.label(text="") + row.operator("bim.enable_editing_context", icon="GREASEPENCIL", text="").context = subcontext["id"] row.operator("bim.remove_context", icon="X", text="").context = subcontext["id"] diff --git a/src/bonsai/bonsai/core/context.py b/src/bonsai/bonsai/core/context.py index 626720fb33..0fb1ea6292 100644 --- a/src/bonsai/bonsai/core/context.py +++ b/src/bonsai/bonsai/core/context.py @@ -30,6 +30,7 @@ def add_context( context_type: Optional[str] = None, context_identifier: Optional[str] = None, target_view: Optional[str] = None, + target_scale: Optional[float] = None, parent: Optional[str] = None, ) -> ifcopenshell.entity_instance: return ifc.run( @@ -37,6 +38,7 @@ def add_context( context_type=context_type, context_identifier=context_identifier, target_view=target_view, + target_scale=target_scale, parent=parent, ) diff --git a/src/bonsai/bonsai/tool/context.py b/src/bonsai/bonsai/tool/context.py index c3ca9cb797..8c44d0ff26 100644 --- a/src/bonsai/bonsai/tool/context.py +++ b/src/bonsai/bonsai/tool/context.py @@ -54,6 +54,17 @@ class Context(bonsai.core.tool.Context): elif name == "CoordinateSpaceDimension": props.context_attributes.remove(props.context_attributes.find("CoordinateSpaceDimension")) return True + elif name == "TargetScale": + props.context_attributes.remove(props.context_attributes.find("TargetScale")) + scale_denominator = None + if data.get(name) is not None and data.get(name) != 0: + scale_denominator = 1.0 / data.get(name) + new_prop = props.context_attributes.add() + new_prop.name = "ScaleDenominator" + new_prop.data_type = "float" + if scale_denominator is not None: + new_prop.float_value = scale_denominator + return True else: # IfcGeometricRepresentationContext # Import precision as a string because Blender has problem displaying 1e-7 and smaller numbers in UI. if name == "Precision": @@ -79,6 +90,13 @@ class Context(bonsai.core.tool.Context): if blender_attribute.name == "Precision": attributes["Precision"] = float(blender_attribute.get_value()) return True + elif blender_attribute.name == "ScaleDenominator": + scale_denominator = blender_attribute.get_value() + if scale_denominator is not None and scale_denominator != 0: + attributes["TargetScale"] = 1.0 / scale_denominator + else: + attributes["TargetScale"] = None + return True return False return bonsai.bim.helper.export_attributes(props.context_attributes, callback=callback) diff --git a/src/bonsai/test/tool/test_context.py b/src/bonsai/test/tool/test_context.py index 7151952d79..f4035ecd97 100644 --- a/src/bonsai/test/tool/test_context.py +++ b/src/bonsai/test/tool/test_context.py @@ -65,7 +65,7 @@ class TestImportAttributes(test.bim.bootstrap.NewFile): subject.set_context(subcontext) subject.import_attributes() props = subject.get_context_props() - assert props.context_attributes["TargetScale"].float_value == 0.5 + assert props.context_attributes["ScaleDenominator"].float_value == 2.0 # ScaleDenominator = 1.0 / TargetScale assert props.context_attributes["TargetView"].enum_value == "NOTDEFINED" assert props.context_attributes["UserDefinedTargetView"].string_value == "UserDefinedTargetView" assert "Precision" not in props.context_attributes diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py index e26d051eef..f354713dbc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py @@ -26,6 +26,7 @@ def add_context( context_type: Optional[ifcopenshell.util.representation.CONTEXT_TYPE] = None, context_identifier: Optional[ifcopenshell.util.representation.REPRESENTATION_IDENTIFIER] = None, target_view: Optional[ifcopenshell.util.representation.TARGET_VIEW] = None, + target_scale: Optional[float] = None, parent: Optional[ifcopenshell.entity_instance] = None, ) -> ifcopenshell.entity_instance: """Adds a new geometric representation context @@ -107,6 +108,9 @@ def add_context( the common target views above or consult the IFC documentation (under the IfcShapeRepresentation page) for more details. Optional for contexts, but mandatory for subcontexts. + :param target_scale: It defines the intended scale at which the representation + is designed to be viewed or printed + :type target_scale: float, optional :param parent: the parent context. Must be left as None (the default) for contexts, and only set for subcontexts. Note that there are only contexts and subcontexts, a subcontext cannot have any children. @@ -186,6 +190,7 @@ def add_context( "parent": parent, "context_identifier": context_identifier, "target_view": target_view, + "target_scale": target_scale, } return usecase.execute() @@ -221,6 +226,7 @@ class Usecase: "ContextType": self.settings["context_type"], "ParentContext": self.settings["parent"], "TargetView": self.settings["target_view"], + "TargetScale": self.settings["target_scale"], } )