From e6238a1f7558c1390b571f079ce62c929f2e0682 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 24 Jun 2024 23:39:25 +1000 Subject: [PATCH] Georeference UI now lets you edit either a decimal degrees angle or an abscissa ordinate and calculates on the fly Seriously no normal person uses abscissa and ordinate --- .../bim/module/georeference/prop.py | 83 ++++++++++++++----- .../blenderbim/bim/module/georeference/ui.py | 33 ++++++-- .../blenderbim/tool/georeference.py | 40 ++++++++- 3 files changed, 126 insertions(+), 30 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/georeference/prop.py b/src/blenderbim/blenderbim/bim/module/georeference/prop.py index 0082981c3a..870d7b32eb 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/prop.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/prop.py @@ -17,6 +17,7 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import ifcopenshell.util.geolocation from blenderbim.bim.prop import Attribute from bpy.types import PropertyGroup from bpy.props import ( @@ -38,10 +39,65 @@ def get_coordinate_operation_class(self, context): return GeoreferenceData.data["coordinate_operation_class"] +def update_true_north_angle(self, context): + if self.is_changing_angle: + return + self.is_changing_angle = True + try: + x, y = ifcopenshell.util.geolocation.angle2yaxis(float(self.true_north_angle)) + self.true_north_abscissa = str(x) + self.true_north_ordinate = str(y) + except: + pass + self.is_changing_angle = False + + +def update_true_north_vector(self, context): + if self.is_changing_angle: + return + self.is_changing_angle = True + try: + x = float(self.true_north_abscissa) + y = float(self.true_north_ordinate) + self.true_north_angle = str(round(ifcopenshell.util.geolocation.yaxis2angle(x, y), 7)) + except: + pass + self.is_changing_angle = False + + +def update_grid_north_angle(self, context): + if self.is_changing_angle: + return + self.is_changing_angle = True + try: + x, y = ifcopenshell.util.geolocation.angle2xaxis(float(self.grid_north_angle)) + self.x_axis_abscissa = str(x) + self.x_axis_ordinate = str(y) + self.x_axis_is_null = False + except: + pass + self.is_changing_angle = False + + +def update_grid_north_vector(self, context): + if self.is_changing_angle: + return + self.is_changing_angle = True + try: + x = float(self.x_axis_abscissa) + y = float(self.x_axis_ordinate) + self.grid_north_angle = str(round(ifcopenshell.util.geolocation.xaxis2angle(x, y), 7)) + self.x_axis_is_null = False + except: + pass + self.is_changing_angle = False + + class BIMGeoreferenceProperties(PropertyGroup): coordinate_operation_class: bpy.props.EnumProperty( items=get_coordinate_operation_class, name="Coordinate Operation Class" ) + is_changing_angle: BoolProperty(name="Is Changing Angle", default=False) is_editing: BoolProperty(name="Is Editing") is_editing_wcs: BoolProperty(name="Is Editing WCS") is_editing_true_north: BoolProperty(name="Is Editing True North") @@ -53,24 +109,10 @@ class BIMGeoreferenceProperties(PropertyGroup): map_coordinates: StringProperty( name="Map Coordinates", description='Formatted "x,y,z" (without quotes)', default="0,0,0" ) - angle_degree_input_x: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to Easting") - angle_degree_input_y: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to +Y") - x_axis_abscissa_output: StringProperty( - name="X Axis Abscissa Ordinate Output", - description="X axis abscissa and ordinate", - ) - x_axis_ordinate_output: StringProperty( - name="X Axis Abscissa Ordinate Output", - description="X axis abscissa and ordinate", - ) - y_axis_abscissa_output: StringProperty( - name="Y Axis Abscissa Ordinate Output", - description="Y axis abscissa and ordinate", - ) - y_axis_ordinate_output: StringProperty( - name="Y Axis Abscissa Ordinate Output", - description="Y axis abscissa and ordinate", - ) + grid_north_angle: StringProperty(name="Grid North Angle", update=update_grid_north_angle) + x_axis_abscissa: StringProperty(name="X Axis Abscissa", update=update_grid_north_vector) + x_axis_ordinate: StringProperty(name="X Axis Ordinate", update=update_grid_north_vector) + x_axis_is_null: BoolProperty(name="X Axis Is Null") host_model_origin: StringProperty(name="Host Model Origin") model_origin: StringProperty(name="Model Origin") has_blender_offset: BoolProperty(name="Has Blender Offset") @@ -82,8 +124,9 @@ class BIMGeoreferenceProperties(PropertyGroup): blender_offset_x: StringProperty(name="Blender Offset X", default="0") blender_offset_y: StringProperty(name="Blender Offset Y", default="0") blender_offset_z: StringProperty(name="Blender Offset Z", default="0") - true_north_abscissa: StringProperty(name="True North Abscissa") - true_north_ordinate: StringProperty(name="True North Ordinate") + true_north_angle: StringProperty(name="True North Angle", update=update_true_north_angle) + true_north_abscissa: StringProperty(name="True North Abscissa", update=update_true_north_vector) + true_north_ordinate: StringProperty(name="True North Ordinate", update=update_true_north_vector) wcs_x: StringProperty(name="WCS X", default="0") wcs_y: StringProperty(name="WCS Y", default="0") wcs_z: StringProperty(name="WCS Z", default="0") diff --git a/src/blenderbim/blenderbim/bim/module/georeference/ui.py b/src/blenderbim/blenderbim/bim/module/georeference/ui.py index a45dc4e65c..66d3f35f8f 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/ui.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/ui.py @@ -57,11 +57,23 @@ class BIM_PT_gis(Panel): row.label(text="Coordinate Operation", icon="GRID") for attribute in props.coordinate_operation: - if attribute.name == "Scale" and hasattr(context.scene, "sun_pos_properties"): + if attribute.name == "XAxisAbscissa": row = self.layout.row(align=True) - row.operator("bim.set_ifc_grid_north", text="Set IFC North") - row.operator("bim.set_blender_grid_north", text="Set Blender North") - draw_attribute(attribute, self.layout.row()) + row.prop(props, "grid_north_angle", text="Angle") + row.prop(props, "x_axis_is_null", icon="RADIOBUT_OFF" if props.x_axis_is_null else "RADIOBUT_ON", text="") + row = self.layout.row(align=True) + row.prop(props, "x_axis_abscissa", text="XAxis Abscissa") + row.prop(props, "x_axis_is_null", icon="RADIOBUT_OFF" if props.x_axis_is_null else "RADIOBUT_ON", text="") + elif attribute.name == "XAxisOrdinate": + row = self.layout.row(align=True) + row.prop(props, "x_axis_ordinate", text="XAxis Ordinate") + row.prop(props, "x_axis_is_null", icon="RADIOBUT_OFF" if props.x_axis_is_null else "RADIOBUT_ON", text="") + if hasattr(context.scene, "sun_pos_properties"): + row = self.layout.row(align=True) + row.operator("bim.set_ifc_grid_north", text="Set IFC North") + row.operator("bim.set_blender_grid_north", text="Set Blender North") + else: + draw_attribute(attribute, self.layout.row()) def draw_ui(self, context): props = context.scene.BIMGeoreferenceProperties @@ -158,9 +170,11 @@ class BIM_PT_gis_true_north(Panel): def draw_editable_ui(self, context): row = self.layout.row() - row.prop(self.props, "true_north_abscissa") + row.prop(self.props, "true_north_angle", text="Angle") row = self.layout.row() - row.prop(self.props, "true_north_ordinate") + row.prop(self.props, "true_north_abscissa", text="Abscissa") + row = self.layout.row() + row.prop(self.props, "true_north_ordinate", text="Ordinate") if hasattr(context.scene, "sun_pos_properties"): row = self.layout.row(align=True) row.operator("bim.set_ifc_true_north", text="Set IFC North") @@ -173,8 +187,11 @@ class BIM_PT_gis_true_north(Panel): def draw_ui(self): if GeoreferenceData.data["true_north"]: row = self.layout.row(align=True) - row.label(text="Vector") - row.label(text=str(GeoreferenceData.data["true_north"][0:2])[1:-1]) + row.label(text="Abscissa") + row.label(text=str(GeoreferenceData.data["true_north"][0])) + row = self.layout.row(align=True) + row.label(text="Ordinate") + row.label(text=str(GeoreferenceData.data["true_north"][1])) row.operator("bim.enable_editing_true_north", icon="GREASEPENCIL", text="") row.operator("bim.remove_true_north", icon="X", text="") row = self.layout.row(align=True) diff --git a/src/blenderbim/blenderbim/tool/georeference.py b/src/blenderbim/blenderbim/tool/georeference.py index dd9c21fffa..677d2a89fb 100644 --- a/src/blenderbim/blenderbim/tool/georeference.py +++ b/src/blenderbim/blenderbim/tool/georeference.py @@ -88,6 +88,23 @@ class Georeference(blenderbim.core.tool.Georeference): prop.data_type = "string" prop.string_value = "" if prop.is_null else str(data[name].wrappedValue) return True + elif name == "XAxisAbscissa": + props = bpy.context.scene.BIMGeoreferenceProperties + props.is_changing_angle = True + if data["XAxisAbscissa"] is None or data["XAxisOrdinate"]: + props.x_axis_is_null = True + else: + props.grid_north_angle = str( + round( + ifcopenshell.util.geolocation.xaxis2angle(data["XAxisAbscissa"], data["XAxisOrdinate"]), 7 + ) + ) + props.x_axis_abscissa = str(data["XAxisAbscissa"]) + props.x_axis_ordinate = str(data["XAxisOrdinate"]) + props.is_changing_angle = False + return True + elif name == "XAxisOrdinate": + return True elif name not in ("SourceCRS", "TargetCRS"): # Enforce a string data type to prevent data loss in single-precision Blender props prop.data_type = "string" @@ -114,11 +131,20 @@ class Georeference(blenderbim.core.tool.Georeference): return props = bpy.context.scene.BIMGeoreferenceProperties + props.is_changing_angle = True + props.true_north_abscissa = "0" + props.true_north_ordinate = "1" + props.true_north_angle = "0" + props.is_changing_angle = False + for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False): if context.TrueNorth: true_north = context.TrueNorth.DirectionRatios + props.is_changing_angle = True props.true_north_abscissa = str(true_north[0]) props.true_north_ordinate = str(true_north[1]) + props.true_north_angle = str(round(ifcopenshell.util.geolocation.yaxis2angle(*true_north[:2]), 7)) + props.is_changing_angle = False return @classmethod @@ -143,6 +169,17 @@ class Georeference(blenderbim.core.tool.Georeference): elif prop.name in ("FirstCoordinate", "SecondCoordinate"): attributes[prop.name] = tool.Ifc.get().create_entity(measure_type, float(prop.string_value)) return True + elif prop.name == "XAxisAbscissa": + props = bpy.context.scene.BIMGeoreferenceProperties + if props.x_axis_is_null: + attributes["XAxisAbscissa"] = None + attributes["XAxisOrdinate"] = None + else: + attributes["XAxisAbscissa"] = float(props.x_axis_abscissa) + attributes["XAxisOrdinate"] = float(props.x_axis_ordinate) + return True + elif prop.name == "XAxisOrdinate": + return True elif not prop.is_null and prop.data_type == "string": # We store our floats as string to prevent single precision data loss attributes[prop.name] = float(prop.string_value) @@ -183,7 +220,6 @@ class Georeference(blenderbim.core.tool.Georeference): def disable_editing_true_north(cls): bpy.context.scene.BIMGeoreferenceProperties.is_editing_true_north = False - @classmethod def set_coordinates(cls, io, coordinates): if io == "local": @@ -345,7 +381,7 @@ class Georeference(blenderbim.core.tool.Georeference): if np.allclose(placement, np.eye(4)): props.wcs_x = props.wcs_y = props.wcs_z = props.wcs_rotation = "0" else: - props.wcs_rotation = str(round(ifcopenshell.util.geolocation.yaxis2angle(*placement[:, 1][:2]), 3)) + props.wcs_rotation = str(round(ifcopenshell.util.geolocation.yaxis2angle(*placement[:, 1][:2]), 7)) props.wcs_x, props.wcs_y, props.wcs_z = map(str, placement[:, 3][:3]) @classmethod