diff --git a/src/bonsai/bonsai/bim/gizmo.py b/src/bonsai/bonsai/bim/gizmo.py index 9c9816b03e..cdd5fe02a4 100644 --- a/src/bonsai/bonsai/bim/gizmo.py +++ b/src/bonsai/bonsai/bim/gizmo.py @@ -52,6 +52,7 @@ ARC_LINE_WIDTH = 0.015 PRECISION_MODE_MULTIPLIER = 0.1 + class SnapManager: """Manages snap point visualization and mesh snapping.""" @@ -64,16 +65,14 @@ class SnapManager: """Set snap point and register draw handler if needed.""" self._snap_point = point if self._draw_handler is None and point is not None: - self._draw_handler = bpy.types.SpaceView3D.draw_handler_add( - self._draw, (), 'WINDOW', 'POST_VIEW' - ) + self._draw_handler = bpy.types.SpaceView3D.draw_handler_add(self._draw, (), "WINDOW", "POST_VIEW") self._redraw_viewport() def clear(self) -> None: """Clear snap point and unregister handler.""" self._snap_point = None if self._draw_handler is not None: - bpy.types.SpaceView3D.draw_handler_remove(self._draw_handler, 'WINDOW') + bpy.types.SpaceView3D.draw_handler_remove(self._draw_handler, "WINDOW") self._draw_handler = None self._redraw_viewport() @@ -83,17 +82,15 @@ class SnapManager: return if self._shader is None: - self._shader = gpu.shader.from_builtin('UNIFORM_COLOR') + self._shader = gpu.shader.from_builtin("UNIFORM_COLOR") self._shader.bind() self._shader.uniform_float("color", SNAP_CIRCLE_COLOR) gpu.state.line_width_set(SNAP_LINE_WIDTH) - for plane in ('XY', 'XZ', 'YZ'): - vertices = generate_circle_vertices( - self._snap_point, SNAP_CIRCLE_RADIUS, SNAP_CIRCLE_SEGMENTS, plane - ) - batch = batch_for_shader(self._shader, 'LINE_STRIP', {"pos": vertices}) + for plane in ("XY", "XZ", "YZ"): + vertices = generate_circle_vertices(self._snap_point, SNAP_CIRCLE_RADIUS, SNAP_CIRCLE_SEGMENTS, plane) + batch = batch_for_shader(self._shader, "LINE_STRIP", {"pos": vertices}) batch.draw(self._shader) gpu.state.line_width_set(1.0) @@ -102,15 +99,12 @@ class SnapManager: def _redraw_viewport() -> None: """Force 3D viewport redraw.""" for area in bpy.context.screen.areas: - if area.type == 'VIEW_3D': + if area.type == "VIEW_3D": area.tag_redraw() @staticmethod def snap_to_mesh( - location: Vector, - context: bpy.types.Context, - axis_vector: Vector, - active_obj: bpy.types.Object + location: Vector, context: bpy.types.Context, axis_vector: Vector, active_obj: bpy.types.Object ) -> Vector: """Snap a location to the nearest mesh element if snapping is enabled. @@ -131,9 +125,7 @@ class SnapManager: snap_elements = tool_settings.snap_elements_base mesh_objects = [ - obj - for obj in context.visible_objects - if obj.type == "MESH" and obj != active_obj and obj.visible_get() + obj for obj in context.visible_objects if obj.type == "MESH" and obj != active_obj and obj.visible_get() ] if not mesh_objects: @@ -168,14 +160,14 @@ class SnapManager: world_vertices = [obj.matrix_world @ v.co for v in mesh.vertices] - if 'VERTEX' in snap_elements: + if "VERTEX" in snap_elements: for v_co in world_vertices: dist = (v_co - location).length if dist < closest_distance: closest_distance = dist closest_point = v_co - if 'EDGE' in snap_elements: + if "EDGE" in snap_elements: for edge in mesh.edges: v1 = world_vertices[edge.vertices[0]] v2 = world_vertices[edge.vertices[1]] @@ -192,10 +184,8 @@ class SnapManager: closest_distance = dist closest_point = closest_on_edge - if 'FACE' in snap_elements: - bvh = BVHTree.FromPolygons( - world_vertices, [p.vertices for p in mesh.polygons] - ) + if "FACE" in snap_elements: + bvh = BVHTree.FromPolygons(world_vertices, [p.vertices for p in mesh.polygons]) nearest_loc, normal, index, dist = bvh.find_nearest(location) @@ -223,19 +213,13 @@ def clear_snap_point() -> None: def snap_to_mesh( - location: Vector, - context: bpy.types.Context, - axis_vector: Vector, - active_obj: bpy.types.Object + location: Vector, context: bpy.types.Context, axis_vector: Vector, active_obj: bpy.types.Object ) -> Vector: return _snap_manager.snap_to_mesh(location, context, axis_vector, active_obj) def generate_circle_vertices( - center: Tuple[float, float, float], - radius: float, - segments: int, - plane: str = 'XY' + center: Tuple[float, float, float], radius: float, segments: int, plane: str = "XY" ) -> List[Tuple[float, float, float]]: """Generate circle vertices in specified plane. @@ -254,9 +238,9 @@ def generate_circle_vertices( cos_a = radius * math.cos(angle) sin_a = radius * math.sin(angle) - if plane == 'XY': + if plane == "XY": vertices.append((center[0] + cos_a, center[1] + sin_a, center[2])) - elif plane == 'XZ': + elif plane == "XZ": vertices.append((center[0] + cos_a, center[1], center[2] + sin_a)) else: vertices.append((center[0], center[1] + cos_a, center[2] + sin_a)) @@ -265,10 +249,7 @@ def generate_circle_vertices( def create_quarter_circle_arc( - radius: float = 1.0, - segments: int = ARC_SEGMENTS, - direction: str = "LEFT", - line_width: float = ARC_LINE_WIDTH + radius: float = 1.0, segments: int = ARC_SEGMENTS, direction: str = "LEFT", line_width: float = ARC_LINE_WIDTH ) -> Tuple[Tuple[float, float, float], ...]: """Create a quarter circle arc with cross-section thickness for visibility from all angles. @@ -307,27 +288,35 @@ def create_quarter_circle_arc( if length > 0: px, py = -dy / length * half_width, dx / length * half_width - arc_triangles.extend([ - (x1 + px, y1 + py, 0.0), - (x1 - px, y1 - py, 0.0), - (x2 + px, y2 + py, 0.0), - ]) - arc_triangles.extend([ - (x2 + px, y2 + py, 0.0), - (x1 - px, y1 - py, 0.0), - (x2 - px, y2 - py, 0.0), - ]) + arc_triangles.extend( + [ + (x1 + px, y1 + py, 0.0), + (x1 - px, y1 - py, 0.0), + (x2 + px, y2 + py, 0.0), + ] + ) + arc_triangles.extend( + [ + (x2 + px, y2 + py, 0.0), + (x1 - px, y1 - py, 0.0), + (x2 - px, y2 - py, 0.0), + ] + ) - arc_triangles.extend([ - (x1, y1, -half_width), - (x2, y2, -half_width), - (x1, y1, +half_width), - ]) - arc_triangles.extend([ - (x1, y1, +half_width), - (x2, y2, -half_width), - (x2, y2, +half_width), - ]) + arc_triangles.extend( + [ + (x1, y1, -half_width), + (x2, y2, -half_width), + (x1, y1, +half_width), + ] + ) + arc_triangles.extend( + [ + (x1, y1, +half_width), + (x2, y2, -half_width), + (x2, y2, +half_width), + ] + ) return tuple(arc_triangles) @@ -387,8 +376,10 @@ class GizmoMovable(bpy.types.Gizmo): axis_direction = self.get_axis_direction() result = intersect_line_line( - view_origin, view_origin + view_direction * 1000, - self.start_location, self.start_location + axis_direction * 1000 + view_origin, + view_origin + view_direction * 1000, + self.start_location, + self.start_location + axis_direction * 1000, ) current_3d = result[1] if result else self.start_location @@ -414,13 +405,7 @@ class GizmoMovable(bpy.types.Gizmo): return {"RUNNING_MODAL"} - def _update_header( - self, - context: bpy.types.Context, - value: float, - is_snapping: bool, - is_precision: bool - ) -> None: + def _update_header(self, context: bpy.types.Context, value: float, is_snapping: bool, is_precision: bool) -> None: header_text = f"Value: {value:.3f}m" hints = [] if is_snapping: @@ -446,25 +431,57 @@ class GizmoLock(bpy.types.Gizmo): ) tris_closed = ( - (0.16803650558, 0.18791499734, 0.0), (-0.07805634290, 0.18791499734, 0.0), (0.16803650558, 0.44701099396, 0.0), - (0.16803650558, 0.44701099396, 0.0), (-0.07805634290, 0.18791499734, 0.0), (-0.07805634290, 0.44701099396, 0.0), - (0.20165449381, 0.13603900373, 0.0), (-0.11167433113, 0.13603900373, 0.0), (0.20165449381, -0.44701099396, 0.0), - (0.20165449381, -0.44701099396, 0.0), (-0.11167433113, 0.13603900373, 0.0), (-0.11167433113, -0.44701099396, 0.0), - (-0.07805634290, 0.18791499734, 0.0), (-0.39353451133, 0.18791499734, 0.0), (-0.07805634290, 0.44701099396, 0.0), - (-0.07805634290, 0.44701099396, 0.0), (-0.39353451133, 0.18791499734, 0.0), (-0.39353451133, 0.30746498704, 0.0), - (-0.44701099396, 0.18791499734, 0.0), (-0.44701099396, -0.04477182776, 0.0), (-0.39353451133, 0.18791499734, 0.0), - (-0.39353451133, 0.18791499734, 0.0), (-0.44701099396, -0.04477182776, 0.0), (-0.39353451133, -0.04477182776, 0.0), + (0.16803650558, 0.18791499734, 0.0), + (-0.07805634290, 0.18791499734, 0.0), + (0.16803650558, 0.44701099396, 0.0), + (0.16803650558, 0.44701099396, 0.0), + (-0.07805634290, 0.18791499734, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (0.20165449381, 0.13603900373, 0.0), + (-0.11167433113, 0.13603900373, 0.0), + (0.20165449381, -0.44701099396, 0.0), + (0.20165449381, -0.44701099396, 0.0), + (-0.11167433113, 0.13603900373, 0.0), + (-0.11167433113, -0.44701099396, 0.0), + (-0.07805634290, 0.18791499734, 0.0), + (-0.39353451133, 0.18791499734, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (-0.39353451133, 0.18791499734, 0.0), + (-0.39353451133, 0.30746498704, 0.0), + (-0.44701099396, 0.18791499734, 0.0), + (-0.44701099396, -0.04477182776, 0.0), + (-0.39353451133, 0.18791499734, 0.0), + (-0.39353451133, 0.18791499734, 0.0), + (-0.44701099396, -0.04477182776, 0.0), + (-0.39353451133, -0.04477182776, 0.0), ) tris_open = ( - (0.16803650558, 0.18791499734, 0.0), (-0.07805634290, 0.18791499734, 0.0), (0.16803650558, 0.44701099396, 0.0), - (0.16803650558, 0.44701099396, 0.0), (-0.07805634290, 0.18791499734, 0.0), (-0.07805634290, 0.44701099396, 0.0), - (0.20165449381, 0.13603900373, 0.0), (-0.11167433113, 0.13603900373, 0.0), (0.20165449381, -0.44701099396, 0.0), - (0.20165449381, -0.44701099396, 0.0), (-0.11167433113, 0.13603900373, 0.0), (-0.11167433113, -0.44701099396, 0.0), - (0.16803650558, 0.44701099396, 0.0), (-0.07805634290, 0.44701099396, 0.0), (0.16803650558, 0.70610702038, 0.0), - (0.16803650558, 0.70610702038, 0.0), (-0.07805634290, 0.44701099396, 0.0), (-0.07805634290, 0.58656096458, 0.0), - (-0.11167433113, 0.44701099396, 0.0), (-0.11167433113, 0.73432201147, 0.0), (-0.07805634290, 0.44701099396, 0.0), - (-0.07805634290, 0.44701099396, 0.0), (-0.11167433113, 0.73432201147, 0.0), (-0.07805634290, 0.73432201147, 0.0), + (0.16803650558, 0.18791499734, 0.0), + (-0.07805634290, 0.18791499734, 0.0), + (0.16803650558, 0.44701099396, 0.0), + (0.16803650558, 0.44701099396, 0.0), + (-0.07805634290, 0.18791499734, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (0.20165449381, 0.13603900373, 0.0), + (-0.11167433113, 0.13603900373, 0.0), + (0.20165449381, -0.44701099396, 0.0), + (0.20165449381, -0.44701099396, 0.0), + (-0.11167433113, 0.13603900373, 0.0), + (-0.11167433113, -0.44701099396, 0.0), + (0.16803650558, 0.44701099396, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (0.16803650558, 0.70610702038, 0.0), + (0.16803650558, 0.70610702038, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (-0.07805634290, 0.58656096458, 0.0), + (-0.11167433113, 0.44701099396, 0.0), + (-0.11167433113, 0.73432201147, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (-0.07805634290, 0.44701099396, 0.0), + (-0.11167433113, 0.73432201147, 0.0), + (-0.07805634290, 0.73432201147, 0.0), ) def get_custom_shape(self, context: bpy.types.Context): @@ -656,40 +673,52 @@ class GizmoArrow(GizmoMovable): """Generate arrow geometry along +X axis.""" triangles = [] - triangles.extend([ - (0, -ARROW_WIDTH, 0), - (ARROW_SHAFT_LENGTH, -ARROW_WIDTH, 0), - (0, +ARROW_WIDTH, 0), - ]) - triangles.extend([ - (0, +ARROW_WIDTH, 0), - (ARROW_SHAFT_LENGTH, -ARROW_WIDTH, 0), - (ARROW_SHAFT_LENGTH, +ARROW_WIDTH, 0), - ]) + triangles.extend( + [ + (0, -ARROW_WIDTH, 0), + (ARROW_SHAFT_LENGTH, -ARROW_WIDTH, 0), + (0, +ARROW_WIDTH, 0), + ] + ) + triangles.extend( + [ + (0, +ARROW_WIDTH, 0), + (ARROW_SHAFT_LENGTH, -ARROW_WIDTH, 0), + (ARROW_SHAFT_LENGTH, +ARROW_WIDTH, 0), + ] + ) - triangles.extend([ - (0, 0, -ARROW_WIDTH), - (ARROW_SHAFT_LENGTH, 0, -ARROW_WIDTH), - (0, 0, +ARROW_WIDTH), - ]) - triangles.extend([ - (0, 0, +ARROW_WIDTH), - (ARROW_SHAFT_LENGTH, 0, -ARROW_WIDTH), - (ARROW_SHAFT_LENGTH, 0, +ARROW_WIDTH), - ]) + triangles.extend( + [ + (0, 0, -ARROW_WIDTH), + (ARROW_SHAFT_LENGTH, 0, -ARROW_WIDTH), + (0, 0, +ARROW_WIDTH), + ] + ) + triangles.extend( + [ + (0, 0, +ARROW_WIDTH), + (ARROW_SHAFT_LENGTH, 0, -ARROW_WIDTH), + (ARROW_SHAFT_LENGTH, 0, +ARROW_WIDTH), + ] + ) head_width = ARROW_WIDTH * ARROW_HEAD_WIDTH_MULTIPLIER - triangles.extend([ - (ARROW_SHAFT_LENGTH, -head_width, 0), - (ARROW_SHAFT_LENGTH + ARROW_HEAD_LENGTH, 0, 0), - (ARROW_SHAFT_LENGTH, +head_width, 0), - ]) + triangles.extend( + [ + (ARROW_SHAFT_LENGTH, -head_width, 0), + (ARROW_SHAFT_LENGTH + ARROW_HEAD_LENGTH, 0, 0), + (ARROW_SHAFT_LENGTH, +head_width, 0), + ] + ) - triangles.extend([ - (ARROW_SHAFT_LENGTH, 0, -head_width), - (ARROW_SHAFT_LENGTH + ARROW_HEAD_LENGTH, 0, 0), - (ARROW_SHAFT_LENGTH, 0, +head_width), - ]) + triangles.extend( + [ + (ARROW_SHAFT_LENGTH, 0, -head_width), + (ARROW_SHAFT_LENGTH + ARROW_HEAD_LENGTH, 0, 0), + (ARROW_SHAFT_LENGTH, 0, +head_width), + ] + ) for i in range(ARROW_CIRCLE_SEGMENTS): angle1 = (2 * math.pi * i) / ARROW_CIRCLE_SEGMENTS @@ -700,11 +729,13 @@ class GizmoArrow(GizmoMovable): y2 = head_width * math.cos(angle2) z2 = head_width * math.sin(angle2) - triangles.extend([ - (ARROW_SHAFT_LENGTH, 0, 0), - (ARROW_SHAFT_LENGTH, y1, z1), - (ARROW_SHAFT_LENGTH, y2, z2), - ]) + triangles.extend( + [ + (ARROW_SHAFT_LENGTH, 0, 0), + (ARROW_SHAFT_LENGTH, y1, z1), + (ARROW_SHAFT_LENGTH, y2, z2), + ] + ) return tuple(triangles) @@ -746,17 +777,21 @@ class GizmoCone(GizmoMovable): y2 = CONE_RADIUS * math.cos(angle2) z2 = CONE_RADIUS * math.sin(angle2) - triangles.extend([ - (cone_tip_x, 0, 0), - (0, y1, z1), - (0, y2, z2), - ]) + triangles.extend( + [ + (cone_tip_x, 0, 0), + (0, y1, z1), + (0, y2, z2), + ] + ) - triangles.extend([ - (0, 0, 0), - (0, y2, z2), - (0, y1, z1), - ]) + triangles.extend( + [ + (0, 0, 0), + (0, y2, z2), + (0, y1, z1), + ] + ) return tuple(triangles) diff --git a/src/bonsai/bonsai/bim/module/aggregate/operator.py b/src/bonsai/bonsai/bim/module/aggregate/operator.py index e94199b208..32d06dd94e 100644 --- a/src/bonsai/bonsai/bim/module/aggregate/operator.py +++ b/src/bonsai/bonsai/bim/module/aggregate/operator.py @@ -130,7 +130,7 @@ class BIM_OT_aggregate_unassign_object(bpy.types.Operator, tool.Ifc.Operator): # Skip spatial elements - they should not be deleted even if empty if aggregate.is_a("IfcSpatialElement"): continue - + related_objects = ifcopenshell.util.element.get_parts(aggregate) if len(related_objects) == 0: aggregate_name = aggregate.Name or f"{aggregate.is_a()} #{aggregate.id()}" diff --git a/src/bonsai/bonsai/bim/module/drawing/decoration.py b/src/bonsai/bonsai/bim/module/drawing/decoration.py index bf4972df2d..581b8225c0 100644 --- a/src/bonsai/bonsai/bim/module/drawing/decoration.py +++ b/src/bonsai/bonsai/bim/module/drawing/decoration.py @@ -745,10 +745,10 @@ class DimensionDecorator(BaseDecorator): if not show_description_only: length = (v1 - v0).length text = self.format_value( - context, - length, + context, + length, suppress_zero_inches=dimension_data["suppress_zero_inches"], - custom_unit=dimension_data["custom_unit"] + custom_unit=dimension_data["custom_unit"], ) if isinstance(self, DiameterDecorator): text = "D" + text @@ -1203,11 +1203,11 @@ class PlanLevelDecorator(BaseDecorator): def get_text(): abs_z = verts[0].z z = helper.get_relative_z(obj, element, abs_z) - + # Z is in meters (Blender world space), convert to project units (feet) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) z = z / unit_scale # Convert meters to feet - + rl = self.format_value(context, z, in_unit_length=True) text = "{}{}".format("" if z < 0 else "+", rl) return text @@ -1284,7 +1284,7 @@ class SectionLevelDecorator(BaseDecorator): # Z is in meters (Blender world space), convert to project units (feet) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) z = z / unit_scale # Convert meters to feet - + rl = self.format_value(context, z, in_unit_length=True) text = "RL {}{}".format("" if z < 0 else "+", rl) return text @@ -1300,6 +1300,7 @@ class SectionLevelDecorator(BaseDecorator): ) break # support only 1 label + class BreakDecorator(BaseDecorator): """Decorator for breakline objects - first edge of a mesh with zigzag thingy in the middle diff --git a/src/bonsai/bonsai/bim/module/drawing/helper.py b/src/bonsai/bonsai/bim/module/drawing/helper.py index 8f2246a636..3943e8fd2f 100644 --- a/src/bonsai/bonsai/bim/module/drawing/helper.py +++ b/src/bonsai/bonsai/bim/module/drawing/helper.py @@ -121,11 +121,12 @@ class BoundingBox: retVal = True return retVal + def find_best_precision(fractional_inches, max_precision=256, base_tolerance=0.0001): """ Find the simplest fraction denominator that accurately represents the value. Uses a sliding tolerance - smaller denominators get more tolerance. - + :param fractional_inches: The fractional part of inches (0.0 to 1.0) :param max_precision: Maximum denominator to try (256 for imperial) :param base_tolerance: Base tolerance in inches (very strict) @@ -133,29 +134,29 @@ def find_best_precision(fractional_inches, max_precision=256, base_tolerance=0.0 """ if abs(fractional_inches) < 0.00001: return 1 # No fraction needed - + # Try denominators from coarsest to finest: 2, 4, 8, 16, 32, 64, 128, 256 for exp in range(1, 9): # 2^1 to 2^8 - denom = 2 ** exp + denom = 2**exp if denom > max_precision: break - + # Find nearest numerator for this denominator numer = round(fractional_inches * denom) - + # Skip if numerator is 0 if numer == 0: continue - + # Check if this fraction is close enough fraction_value = numer / denom error = abs(fraction_value - fractional_inches) - + # Very strict tolerance - only simplify if it's a near-perfect match # This keeps 3/256 as 3/256, but allows floating point errors to be cleaned up if error < base_tolerance: return denom - + # If no simpler fraction works, use max precision return max_precision @@ -233,11 +234,10 @@ def format_distance( if isArea: toInches = 1550 inPerFoot = 144 - decInches = value * toInches decFeet = decInches / 12 - + if not precision: precision = 256 elif precision == "1": @@ -258,10 +258,10 @@ def format_distance( else: feet = 0 - # Separate Fractional Inches + # Separate Fractional Inches decInches = abs(decInches) # ignore the sign for inches inches = math.floor(decInches) # remove decimal - + # Clean up floating point errors fractional_inches = decInches - inches tolerance = 0.01 # About 1/100 of an inch @@ -280,11 +280,11 @@ def format_distance( else: frac = round(base * fractional_inches) - # Set proper numerator and denominator if frac != base: # Simplify using GCD from math import gcd + divisor = gcd(int(frac), int(base)) frac = int(frac / divisor) base = int(base / divisor) @@ -312,14 +312,14 @@ def format_distance( tx_dist += str(feet) + "'" if not feet and not add_inches: tx_dist += str(feet) + "'" - + # Add "0' - " when we have inches but no feet # But only add " - " separator if we actually have inches to show if not feet and add_inches: tx_dist += "0' - " elif feet and add_inches: tx_dist += " - " - + if not feet and value < 0: tx_dist += "-" if add_inches: @@ -341,13 +341,11 @@ def format_distance( # Only add inch symbol if we actually added inch content if inches > 0 or frac > 0 or feet == 0: tx_dist += '"' - - if precision == "12" and unit_system == "IMPERIAL": tx_dist = str(round(decFeet)) + "'" if tx_dist == '"': - tx_dist = "0' - 0\"" + tx_dist = "0' - 0\"" else: fmt = "%1.3f" sq_feet = round(value * toInches / inPerFoot, 4) diff --git a/src/bonsai/bonsai/bim/module/model/door.py b/src/bonsai/bonsai/bim/module/model/door.py index 945cb64687..7a12b724cf 100644 --- a/src/bonsai/bonsai/bim/module/model/door.py +++ b/src/bonsai/bonsai/bim/module/model/door.py @@ -731,6 +731,7 @@ class GizmoDoorEdition(bpy.types.GizmoGroup): return cls.COLOR_GREEN else: return cls.COLOR_BLUE + gizmo_props = [ {"attr_name": "overall_height", "axis": (0, 0, 1)}, {"attr_name": "overall_width", "axis": (1, 0, 0)}, diff --git a/src/bonsai/bonsai/bim/module/model/ui.py b/src/bonsai/bonsai/bim/module/model/ui.py index 5d89e7bfee..3cf37e91ca 100644 --- a/src/bonsai/bonsai/bim/module/model/ui.py +++ b/src/bonsai/bonsai/bim/module/model/ui.py @@ -777,9 +777,7 @@ def draw_stair_properties(layout: bpy.types.UILayout, props: bpy.types.PropertyG if prop_name == "custom_first_last_tread_run": # Draw the lock toggle row_lock = layout.row(align=True) - lock_text = ( - "Lock First/Last Treads" if not props.custom_tread_lock else "Unlock First/Last Treads" - ) + lock_text = "Lock First/Last Treads" if not props.custom_tread_lock else "Unlock First/Last Treads" row_lock.prop( props, "custom_tread_lock", diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 9e9bfcd43c..8fc500a223 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -1625,11 +1625,11 @@ class Drawing(bonsai.core.tool.Drawing): xmin, xmax, ymin, ymax = helper.ortho_view_frame(camera.data)[:4] rl = ifcopenshell.util.placement.get_local_placement(storey.ObjectPlacement)[2][3] - + # Convert RL from project units (feet) to meters for Blender world space unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) rl_meters = rl * unit_scale - + y = (camera.matrix_world.inverted() @ Vector((0.0, 0.0, rl_meters))).y if y < ymin or y > ymax: return