mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-30 08:33:10 +00:00
Apply black formatting and ruff isort fixes
Pre-commit checklist: black + ruff check. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -124,14 +124,16 @@ def class_items(self, context) -> EnumItems:
|
||||
|
||||
def type_items(self, context) -> EnumItems:
|
||||
return _build_items(
|
||||
"TYPE", "No types defined",
|
||||
"TYPE",
|
||||
"No types defined",
|
||||
lambda ifc: [(str(e.id()), _label(e, e.is_a()), "") for e in ifc.by_type("IfcTypeProduct")],
|
||||
)
|
||||
|
||||
|
||||
def material_items(self, context) -> EnumItems:
|
||||
return _build_items(
|
||||
"MATERIAL", "No materials defined",
|
||||
"MATERIAL",
|
||||
"No materials defined",
|
||||
lambda ifc: [(str(e.id()), _label(e), "") for e in ifc.by_type("IfcMaterial")],
|
||||
)
|
||||
|
||||
@@ -140,7 +142,8 @@ def profile_items(self, context) -> EnumItems:
|
||||
# ProfileName is optional. Skip unnamed profiles — they can't be
|
||||
# meaningfully picked from a flat list.
|
||||
return _build_items(
|
||||
"PROFILE", "No named profiles",
|
||||
"PROFILE",
|
||||
"No named profiles",
|
||||
lambda ifc: [
|
||||
(str(e.id()), f"{e.is_a()}: {e.ProfileName}", "")
|
||||
for e in ifc.by_type("IfcProfileDef")
|
||||
@@ -151,12 +154,9 @@ def profile_items(self, context) -> EnumItems:
|
||||
|
||||
def drawing_items(self, context) -> EnumItems:
|
||||
return _build_items(
|
||||
"DRAWING", "No drawings defined",
|
||||
lambda ifc: [
|
||||
(str(e.id()), _label(e), "")
|
||||
for e in ifc.by_type("IfcAnnotation")
|
||||
if e.ObjectType == "DRAWING"
|
||||
],
|
||||
"DRAWING",
|
||||
"No drawings defined",
|
||||
lambda ifc: [(str(e.id()), _label(e), "") for e in ifc.by_type("IfcAnnotation") if e.ObjectType == "DRAWING"],
|
||||
)
|
||||
|
||||
|
||||
@@ -184,7 +184,8 @@ def system_items(self, context) -> EnumItems:
|
||||
# IfcStructuralAnalysisModel is a structural-grouping container, not a
|
||||
# distribution system — excluded to match Bonsai's other system pickers.
|
||||
return _build_items(
|
||||
"SYSTEM", "No systems defined",
|
||||
"SYSTEM",
|
||||
"No systems defined",
|
||||
lambda ifc: [
|
||||
(str(e.id()), _label(e, e.is_a()), "")
|
||||
for e in ifc.by_type("IfcSystem")
|
||||
@@ -197,16 +198,15 @@ def group_items(self, context) -> EnumItems:
|
||||
# include_subtypes=False so IfcSystem and IfcZone instances don't appear
|
||||
# under Group as well — those get their own picker entries.
|
||||
return _build_items(
|
||||
"GROUP", "No groups defined",
|
||||
lambda ifc: [
|
||||
(str(e.id()), _label(e), "")
|
||||
for e in ifc.by_type("IfcGroup", include_subtypes=False)
|
||||
],
|
||||
"GROUP",
|
||||
"No groups defined",
|
||||
lambda ifc: [(str(e.id()), _label(e), "") for e in ifc.by_type("IfcGroup", include_subtypes=False)],
|
||||
)
|
||||
|
||||
|
||||
def zone_items(self, context) -> EnumItems:
|
||||
return _build_items(
|
||||
"ZONE", "No zones defined",
|
||||
"ZONE",
|
||||
"No zones defined",
|
||||
lambda ifc: [(str(e.id()), _label(e), "") for e in ifc.by_type("IfcZone")],
|
||||
)
|
||||
|
||||
@@ -57,7 +57,6 @@ import bpy
|
||||
from bpy_extras.view3d_utils import location_3d_to_region_2d, region_2d_to_location_3d
|
||||
from mathutils import Matrix, Vector
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public iteration order
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -236,9 +235,7 @@ def view_axis_parallel_face_mask(
|
||||
msg = f"expected 6 face normals, got {len(face_normals_world)}"
|
||||
raise ValueError(msg)
|
||||
vx, vy, vz = view_dir_world
|
||||
return tuple(
|
||||
abs(n[0] * vx + n[1] * vy + n[2] * vz) >= threshold for n in face_normals_world
|
||||
)
|
||||
return tuple(abs(n[0] * vx + n[1] * vy + n[2] * vz) >= threshold for n in face_normals_world)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -290,9 +287,7 @@ def compute_face_resize(
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _compute_face_quad_scale(
|
||||
bmin: Any, bmax: Any, axis: int, is_max: bool
|
||||
) -> tuple[float, float]:
|
||||
def _compute_face_quad_scale(bmin: Any, bmax: Any, axis: int, is_max: bool) -> tuple[float, float]:
|
||||
"""Return ``(w, h)`` for the face quad's scale matrix."""
|
||||
w_axis, h_axis = _QUAD_PERP_AXES[(axis, is_max)]
|
||||
w = float(bmax[w_axis] - bmin[w_axis])
|
||||
@@ -327,9 +322,7 @@ def _shared_edge_corner_keys(
|
||||
)
|
||||
|
||||
|
||||
def _face_corner_keys(
|
||||
axis: int, is_max: bool
|
||||
) -> tuple[
|
||||
def _face_corner_keys(axis: int, is_max: bool) -> tuple[
|
||||
tuple[int, int, int],
|
||||
tuple[int, int, int],
|
||||
tuple[int, int, int],
|
||||
@@ -406,13 +399,9 @@ def _compute_face_basis(
|
||||
orient: Any,
|
||||
) -> tuple[Any, Any]:
|
||||
"""World-space (translation, outward-normal-direction) for one face."""
|
||||
rotated_face_local = (
|
||||
cage_rotation.to_3x3() @ (face_local - pivot_local) + pivot_local
|
||||
)
|
||||
rotated_face_local = cage_rotation.to_3x3() @ (face_local - pivot_local) + pivot_local
|
||||
face_world = mw @ rotated_face_local
|
||||
world_axis = (
|
||||
mw_rot @ cage_rotation.to_3x3() @ (orient.to_3x3() @ Vector((0.0, 0.0, 1.0)))
|
||||
).normalized()
|
||||
world_axis = (mw_rot @ cage_rotation.to_3x3() @ (orient.to_3x3() @ Vector((0.0, 0.0, 1.0)))).normalized()
|
||||
return face_world, world_axis
|
||||
|
||||
|
||||
@@ -431,13 +420,7 @@ def _compose_face_matrix_basis(
|
||||
world-space face rectangle, including the host's scale.
|
||||
"""
|
||||
quad_scale = Matrix.Diagonal((w, h, 1.0, 1.0))
|
||||
return (
|
||||
Matrix.Translation(face_world)
|
||||
@ mw_rot_scale.to_4x4()
|
||||
@ cage_rotation
|
||||
@ orient
|
||||
@ quad_scale
|
||||
)
|
||||
return Matrix.Translation(face_world) @ mw_rot_scale.to_4x4() @ cage_rotation @ orient @ quad_scale
|
||||
|
||||
|
||||
def _compute_box_corners_world(
|
||||
@@ -499,9 +482,7 @@ def _world_radius_to_screen_pixels(
|
||||
except (AttributeError, ValueError):
|
||||
right = Vector((1.0, 0.0, 0.0))
|
||||
sample_world = center_world + right * world_radius
|
||||
return _world_segment_to_screen_pixels(
|
||||
region, rv3d, center_world, sample_world, min_pixels=min_pixels
|
||||
)
|
||||
return _world_segment_to_screen_pixels(region, rv3d, center_world, sample_world, min_pixels=min_pixels)
|
||||
|
||||
|
||||
def _world_segment_to_screen_pixels(
|
||||
@@ -601,9 +582,7 @@ class BIM_GT_box_face_quad(bpy.types.Gizmo): # noqa: N801 — Blender bl_idname
|
||||
# Freeze the projection plane at invoke — projection-plane
|
||||
# drift on tilted axes causes exponential delta runaway.
|
||||
self.depth_point = self.matrix_basis.translation.copy()
|
||||
self.start_location = region_2d_to_location_3d(
|
||||
region, rv3d, (event.mouse_x, event.mouse_y), self.depth_point
|
||||
)
|
||||
self.start_location = region_2d_to_location_3d(region, rv3d, (event.mouse_x, event.mouse_y), self.depth_point)
|
||||
|
||||
if getattr(self, "_group", None) is not None:
|
||||
self._group._lock_for(self)
|
||||
@@ -629,9 +608,7 @@ class BIM_GT_box_face_quad(bpy.types.Gizmo): # noqa: N801 — Blender bl_idname
|
||||
rv3d = context.region_data
|
||||
if region is None or rv3d is None:
|
||||
return {"CANCELLED"}
|
||||
end_location = region_2d_to_location_3d(
|
||||
region, rv3d, (event.mouse_x, event.mouse_y), self.depth_point
|
||||
)
|
||||
end_location = region_2d_to_location_3d(region, rv3d, (event.mouse_x, event.mouse_y), self.depth_point)
|
||||
delta = (end_location - self.start_location).dot(self.axis)
|
||||
if "SNAP" in tweak:
|
||||
delta = round(delta, 1)
|
||||
@@ -639,9 +616,7 @@ class BIM_GT_box_face_quad(bpy.types.Gizmo): # noqa: N801 — Blender bl_idname
|
||||
delta /= 10.0
|
||||
self.move_set_cb(self.init_value + delta)
|
||||
if context.area:
|
||||
context.area.header_text_set(
|
||||
f"Value: {self.move_get_cb():.3f} ({delta:.3f})"
|
||||
)
|
||||
context.area.header_text_set(f"Value: {self.move_get_cb():.3f} ({delta:.3f})")
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
@@ -740,15 +715,11 @@ def apply_face_quad_layout(
|
||||
for route_axis, route_is_max in FACE_ROUTES:
|
||||
axis_local = Vector(face_outward_axis_local(route_axis, route_is_max))
|
||||
n_world = (mw_rot @ cage_rotation_3x3 @ axis_local).normalized()
|
||||
face_normals_world.append(
|
||||
(float(n_world.x), float(n_world.y), float(n_world.z))
|
||||
)
|
||||
face_normals_world.append((float(n_world.x), float(n_world.y), float(n_world.z)))
|
||||
front = front_facing_face_mask(tuple(face_normals_world), view_dir_tuple)
|
||||
|
||||
box_center_world = mw @ pivot_local
|
||||
corners_world = _compute_box_corners_world(
|
||||
bmin, bmax, pivot_local, cage_rotation_3x3, mw
|
||||
)
|
||||
corners_world = _compute_box_corners_world(bmin, bmax, pivot_local, cage_rotation_3x3, mw)
|
||||
route_to_index = {route: i for i, route in enumerate(FACE_ROUTES)}
|
||||
|
||||
for i, route in enumerate(FACE_ROUTES):
|
||||
@@ -787,9 +758,7 @@ def apply_face_quad_layout(
|
||||
orient,
|
||||
)
|
||||
w, h = _compute_face_quad_scale(bmin, bmax, axis_b, is_max_b)
|
||||
quad_gz.matrix_basis = _compose_face_matrix_basis(
|
||||
face_world, mw_rot_scale, cage_rotation, orient, w, h
|
||||
)
|
||||
quad_gz.matrix_basis = _compose_face_matrix_basis(face_world, mw_rot_scale, cage_rotation, orient, w, h)
|
||||
quad_gz.axis = world_axis
|
||||
if getattr(quad_gz, "_last_geometry_state", None) != "solid":
|
||||
quad_gz.custom_shape = quad_gz.new_custom_shape("TRIS", _QUAD_TRIS)
|
||||
@@ -800,16 +769,9 @@ def apply_face_quad_layout(
|
||||
# Back-facing: anchor at the back face centre; build halo strips
|
||||
# in the planes of the adjacent FRONT faces, extruded outside
|
||||
# the silhouette toward this face's outward normal.
|
||||
face_world = mw @ (
|
||||
cage_rotation_3x3 @ (face_midpoints_local[route] - pivot_local)
|
||||
+ pivot_local
|
||||
)
|
||||
face_world = mw @ (cage_rotation_3x3 @ (face_midpoints_local[route] - pivot_local) + pivot_local)
|
||||
quad_gz.matrix_basis = Matrix.Translation(face_world)
|
||||
quad_gz.axis = (
|
||||
mw_rot
|
||||
@ cage_rotation_3x3
|
||||
@ Vector(face_outward_axis_local(axis_b, is_max_b))
|
||||
).normalized()
|
||||
quad_gz.axis = (mw_rot @ cage_rotation_3x3 @ Vector(face_outward_axis_local(axis_b, is_max_b))).normalized()
|
||||
|
||||
adjacent_front_routes = [
|
||||
(axis_a, is_max_a)
|
||||
@@ -827,9 +789,7 @@ def apply_face_quad_layout(
|
||||
face_world_margin = 0.0
|
||||
if region is not None:
|
||||
sample_end = box_center_world + quad_gz.axis * 1.0
|
||||
screen_step = _world_segment_to_screen_pixels(
|
||||
region, rv3d, box_center_world, sample_end, min_pixels=0.0
|
||||
)
|
||||
screen_step = _world_segment_to_screen_pixels(region, rv3d, box_center_world, sample_end, min_pixels=0.0)
|
||||
if screen_step > 0.0:
|
||||
face_world_margin = _FACE_QUAD_HALO_TARGET_PIXELS / screen_step
|
||||
if face_world_margin <= 0.0 or not adjacent_front_routes:
|
||||
@@ -866,9 +826,7 @@ def apply_face_quad_layout(
|
||||
float(wp1.y - face_world.y),
|
||||
float(wp1.z - face_world.z),
|
||||
)
|
||||
all_tris.extend(
|
||||
_build_strip_tris_relative(local_p0, local_p1, extrusion_local)
|
||||
)
|
||||
all_tris.extend(_build_strip_tris_relative(local_p0, local_p1, extrusion_local))
|
||||
|
||||
if not locked:
|
||||
quad_gz.hide = False
|
||||
@@ -907,12 +865,12 @@ def apply_face_quad_layout(
|
||||
|
||||
__all__ = [
|
||||
"AXIS_COLOR",
|
||||
"BIM_GT_box_face_outline",
|
||||
"BIM_GT_box_face_quad",
|
||||
"FACE_QUAD_ALPHA",
|
||||
"FACE_QUAD_ALPHA_HIGHLIGHT",
|
||||
"FACE_QUAD_SELECT_BIAS",
|
||||
"FACE_ROUTES",
|
||||
"BIM_GT_box_face_outline",
|
||||
"BIM_GT_box_face_quad",
|
||||
"apply_face_quad_layout",
|
||||
"compute_face_resize",
|
||||
"face_outward_axis_local",
|
||||
|
||||
@@ -40,7 +40,6 @@ import bonsai.tool as tool
|
||||
|
||||
from . import face_quad
|
||||
|
||||
|
||||
# Local-frame bounds of the empty's CUBE display. The display spans
|
||||
# ``[-empty_display_size, +empty_display_size]^3``; Bonsai always sets
|
||||
# ``empty_display_size = 1.0`` on clip-box hosts, so the local box is
|
||||
@@ -91,9 +90,7 @@ def _make_face_get_cb(gz: Any, group: Any, axis: int, is_max: bool):
|
||||
if empty is None:
|
||||
return 0.0
|
||||
existing = getattr(gz, "_drag_snapshot", None)
|
||||
if existing is not None and existing.get("empty_name") == getattr(
|
||||
empty, "name", None
|
||||
):
|
||||
if existing is not None and existing.get("empty_name") == getattr(empty, "name", None):
|
||||
return float(existing["world_half"])
|
||||
|
||||
world_half = _world_half_extent(empty, axis)
|
||||
@@ -119,9 +116,7 @@ def _make_ctrl_click_cb(axis: int, is_max: bool):
|
||||
"""
|
||||
|
||||
def _callback(_context: Any, _event: Any) -> None:
|
||||
bpy.ops.bim.align_view_to_clip_face(
|
||||
"INVOKE_DEFAULT", axis=axis, is_max=is_max
|
||||
)
|
||||
bpy.ops.bim.align_view_to_clip_face("INVOKE_DEFAULT", axis=axis, is_max=is_max)
|
||||
|
||||
return _callback
|
||||
|
||||
@@ -206,12 +201,8 @@ class OBJECT_GGT_bim_clip_box(bpy.types.GizmoGroup): # noqa: N801 — Blender b
|
||||
space_type=cls.bl_space_type,
|
||||
region_type=cls.bl_region_type,
|
||||
)
|
||||
km.keymap_items.new(
|
||||
"gizmogroup.gizmo_tweak", type="LEFTMOUSE", value="CLICK_DRAG"
|
||||
)
|
||||
km.keymap_items.new(
|
||||
"gizmogroup.gizmo_tweak", type="LEFTMOUSE", value="PRESS", ctrl=True
|
||||
)
|
||||
km.keymap_items.new("gizmogroup.gizmo_tweak", type="LEFTMOUSE", value="CLICK_DRAG")
|
||||
km.keymap_items.new("gizmogroup.gizmo_tweak", type="LEFTMOUSE", value="PRESS", ctrl=True)
|
||||
return km
|
||||
|
||||
def setup(self, context: Any) -> None:
|
||||
@@ -230,9 +221,7 @@ class OBJECT_GGT_bim_clip_box(bpy.types.GizmoGroup): # noqa: N801 — Blender b
|
||||
gz._last_geometry_state = "solid"
|
||||
gz._strips_cache_key = None
|
||||
gz.color = face_quad.AXIS_COLOR[axis]
|
||||
gz.color_highlight = tuple(
|
||||
min(1.0, c + 0.3) for c in face_quad.AXIS_COLOR[axis]
|
||||
)
|
||||
gz.color_highlight = tuple(min(1.0, c + 0.3) for c in face_quad.AXIS_COLOR[axis])
|
||||
gz.alpha = face_quad.FACE_QUAD_ALPHA
|
||||
gz.alpha_highlight = face_quad.FACE_QUAD_ALPHA_HIGHLIGHT
|
||||
gz.use_draw_modal = True
|
||||
|
||||
@@ -159,9 +159,7 @@ class BIM_OT_add_clip_box(bpy.types.Operator):
|
||||
# Default to a 20m cube (scale 10 around [-1, +1] local cube) so
|
||||
# the volume covers a typical building storey or two rather than
|
||||
# the meaningless 2m unit cube. The user resizes with S.
|
||||
matrix = Matrix.Translation(context.scene.cursor.location.copy()) @ Matrix.Diagonal(
|
||||
(10.0, 10.0, 10.0, 1.0)
|
||||
)
|
||||
matrix = Matrix.Translation(context.scene.cursor.location.copy()) @ Matrix.Diagonal((10.0, 10.0, 10.0, 1.0))
|
||||
tool.ClipBox.create_clip_box_empty(context, matrix, name=CLIP_BOX_NAME)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ from bpy.types import Menu, Panel, UIList
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
# Per-kind icon for the source-picker menu. Picked from Blender's built-in
|
||||
# icon set; semantically close to the kind so users can scan the menu visually.
|
||||
_SOURCE_MENU_ENTRIES: tuple[tuple[str, str, str], ...] = (
|
||||
|
||||
@@ -2464,9 +2464,7 @@ class Blender(bonsai.core.tool.Blender):
|
||||
shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
shader.bind()
|
||||
shader.uniform_float("color", fill_color)
|
||||
batch = batch_for_shader(
|
||||
shader, "TRIS", {"pos": verts}, indices=tri_indices
|
||||
)
|
||||
batch = batch_for_shader(shader, "TRIS", {"pos": verts}, indices=tri_indices)
|
||||
batch.draw(shader)
|
||||
if outline_color is not None and line_indices:
|
||||
shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
@@ -2477,9 +2475,7 @@ class Blender(bonsai.core.tool.Blender):
|
||||
prev_width = gpu.state.line_width_get()
|
||||
gpu.state.line_width_set(outline_width)
|
||||
try:
|
||||
batch = batch_for_shader(
|
||||
shader, "LINES", {"pos": verts}, indices=line_indices
|
||||
)
|
||||
batch = batch_for_shader(shader, "LINES", {"pos": verts}, indices=line_indices)
|
||||
batch.draw(shader)
|
||||
finally:
|
||||
gpu.state.line_width_set(prev_width)
|
||||
|
||||
@@ -44,13 +44,9 @@ class TestAddClipBoxForSourceSpatial(NewFile):
|
||||
storey = ifc.create_entity("IfcBuildingStorey")
|
||||
wall_a, _ = _make_ifc_cube(ifc, "IfcWall", location=(0.0, 0.0, 0.0), size=2.0)
|
||||
wall_b, _ = _make_ifc_cube(ifc, "IfcWall", location=(4.0, 0.0, 0.0), size=2.0)
|
||||
ifcopenshell.api.spatial.assign_container(
|
||||
ifc, products=[wall_a, wall_b], relating_structure=storey
|
||||
)
|
||||
ifcopenshell.api.spatial.assign_container(ifc, products=[wall_a, wall_b], relating_structure=storey)
|
||||
|
||||
result = bpy.ops.bim.add_clip_box_for_source(
|
||||
source_kind="SPATIAL", source_id=str(storey.id())
|
||||
)
|
||||
result = bpy.ops.bim.add_clip_box_for_source(source_kind="SPATIAL", source_id=str(storey.id()))
|
||||
|
||||
assert result == {"FINISHED"}
|
||||
scene_props = tool.ClipBox.get_scene_props()
|
||||
@@ -71,9 +67,7 @@ class TestAddClipBoxForSourceClass(NewFile):
|
||||
_make_ifc_cube(ifc, "IfcWall", location=(4.0, 0.0, 0.0), size=2.0)
|
||||
_make_ifc_cube(ifc, "IfcWindow", location=(20.0, 0.0, 0.0), size=2.0)
|
||||
|
||||
result = bpy.ops.bim.add_clip_box_for_source(
|
||||
source_kind="CLASS", source_id="IfcWall"
|
||||
)
|
||||
result = bpy.ops.bim.add_clip_box_for_source(source_kind="CLASS", source_id="IfcWall")
|
||||
|
||||
assert result == {"FINISHED"}
|
||||
scene_props = tool.ClipBox.get_scene_props()
|
||||
@@ -93,9 +87,7 @@ class TestAddClipBoxForSourceEmpty(NewFile):
|
||||
walltype = ifc.create_entity("IfcWallType")
|
||||
# No occurrences linked — TYPE source resolves to 0 elements.
|
||||
with pytest.raises(RuntimeError, match="No elements found"):
|
||||
bpy.ops.bim.add_clip_box_for_source(
|
||||
source_kind="TYPE", source_id=str(walltype.id())
|
||||
)
|
||||
bpy.ops.bim.add_clip_box_for_source(source_kind="TYPE", source_id=str(walltype.id()))
|
||||
scene_props = tool.ClipBox.get_scene_props()
|
||||
assert len(scene_props.clip_boxes) == 0
|
||||
|
||||
@@ -105,9 +97,7 @@ class TestAddClipBoxForSourceEmpty(NewFile):
|
||||
from bonsai.bim.module.clip_box import data as clip_data
|
||||
|
||||
with pytest.raises(RuntimeError, match="No source selected"):
|
||||
bpy.ops.bim.add_clip_box_for_source(
|
||||
source_kind="SPATIAL", source_id=clip_data.NO_OPTIONS_ID
|
||||
)
|
||||
bpy.ops.bim.add_clip_box_for_source(source_kind="SPATIAL", source_id=clip_data.NO_OPTIONS_ID)
|
||||
scene_props = tool.ClipBox.get_scene_props()
|
||||
assert len(scene_props.clip_boxes) == 0
|
||||
|
||||
|
||||
@@ -153,9 +153,7 @@ def _exec_align_view(axis: int, is_max: bool):
|
||||
if region is None:
|
||||
continue
|
||||
with bpy.context.temp_override(area=area, region=region):
|
||||
result = bpy.ops.bim.align_view_to_clip_face(
|
||||
"EXEC_DEFAULT", axis=axis, is_max=is_max
|
||||
)
|
||||
result = bpy.ops.bim.align_view_to_clip_face("EXEC_DEFAULT", axis=axis, is_max=is_max)
|
||||
assert result == {"FINISHED"}
|
||||
return bpy.context.space_data.region_3d
|
||||
pytest.skip("No VIEW_3D area available")
|
||||
@@ -190,7 +188,9 @@ class TestAlignViewToClipFace(NewFile):
|
||||
|
||||
expected_up = (clip_box.matrix_world.to_quaternion() @ Vector((0.0, 0.0, 1.0))).normalized()
|
||||
up_world = rv3d.view_rotation @ Vector((0.0, 1.0, 0.0))
|
||||
assert (up_world - expected_up).length < 1e-3, (
|
||||
assert (
|
||||
up_world - expected_up
|
||||
).length < 1e-3, (
|
||||
f"Side-face view must have box-local +Z as up; expected {tuple(expected_up)}, got {tuple(up_world)}"
|
||||
)
|
||||
|
||||
@@ -217,9 +217,9 @@ class TestAlignViewToClipFace(NewFile):
|
||||
|
||||
expected_up = (clip_box.matrix_world.to_quaternion() @ Vector((0.0, 0.0, 1.0))).normalized()
|
||||
up_world = rv3d.view_rotation @ Vector((0.0, 1.0, 0.0))
|
||||
assert (up_world - expected_up).length < 1e-3, (
|
||||
f"X-rotated box must use box-local Z; expected {tuple(expected_up)}, got {tuple(up_world)}"
|
||||
)
|
||||
assert (
|
||||
up_world - expected_up
|
||||
).length < 1e-3, f"X-rotated box must use box-local Z; expected {tuple(expected_up)}, got {tuple(up_world)}"
|
||||
|
||||
def test_align_view_uses_box_local_y_up_for_top_face(self):
|
||||
# Top face (local +Z outward) follows Blender's numpad-7
|
||||
@@ -257,9 +257,9 @@ class TestNotPersistedToProjectPset(NewFile):
|
||||
|
||||
# Whatever the pset stores, it must not carry this scene-only toggle.
|
||||
for key in pset:
|
||||
assert "clip_only_ifc" not in key.lower(), (
|
||||
f"Project pset unexpectedly carries the scene-only toggle (key {key!r})"
|
||||
)
|
||||
assert (
|
||||
"clip_only_ifc" not in key.lower()
|
||||
), f"Project pset unexpectedly carries the scene-only toggle (key {key!r})"
|
||||
|
||||
def test_load_from_pset_does_not_touch_clip_only_ifc_products(self):
|
||||
# Round-trip: set the toggle on the Scene, simulate a pset load, and
|
||||
|
||||
@@ -154,9 +154,7 @@ class TestFrontFacingFaceMask:
|
||||
|
||||
def test_wrong_length_raises(self):
|
||||
with pytest.raises(ValueError, match="expected 6 face normals"):
|
||||
face_quad.front_facing_face_mask(
|
||||
[(1.0, 0.0, 0.0), (-1.0, 0.0, 0.0)], (0.0, 0.0, -1.0)
|
||||
)
|
||||
face_quad.front_facing_face_mask([(1.0, 0.0, 0.0), (-1.0, 0.0, 0.0)], (0.0, 0.0, -1.0))
|
||||
|
||||
|
||||
# ----------------------------------------------------- apply_face_quad_layout (front/back) ---
|
||||
@@ -259,9 +257,7 @@ class TestApplyFaceQuadLayout:
|
||||
quads = [_FakeQuad() for _ in range(6)]
|
||||
outlines = [_FakeOutline() for _ in range(6)]
|
||||
# View toward +X: the +X face is at world +X for a standard box.
|
||||
view_rotation = Vector((0.0, 0.0, -1.0)).rotation_difference(
|
||||
Vector((-1.0, 0.0, 0.0))
|
||||
)
|
||||
view_rotation = Vector((0.0, 0.0, -1.0)).rotation_difference(Vector((-1.0, 0.0, 0.0)))
|
||||
rv3d = _FakeRV3D(view_rotation, Matrix.Identity(4))
|
||||
# Negative X scale (mirroring the cube along world X).
|
||||
mw = Matrix.Diagonal((-1.0, 1.0, 1.0, 1.0))
|
||||
|
||||
@@ -48,16 +48,14 @@ def test_every_dispatch_target_lives_in_data_module():
|
||||
# against typos in the dispatch table that would otherwise only surface
|
||||
# at the first dialog open.
|
||||
for kind, fn in operator._SOURCE_ID_DISPATCH.items():
|
||||
assert getattr(data, fn.__name__, None) is fn, (
|
||||
f"Dispatch target for {kind} ({fn.__name__}) is not exported from data.py"
|
||||
)
|
||||
assert (
|
||||
getattr(data, fn.__name__, None) is fn
|
||||
), f"Dispatch target for {kind} ({fn.__name__}) is not exported from data.py"
|
||||
|
||||
|
||||
def test_every_menu_entry_is_a_known_kind():
|
||||
for kind, label, icon in ui._SOURCE_MENU_ENTRIES:
|
||||
assert kind in operator.SOURCE_KIND_LABELS, (
|
||||
f"Menu kind {kind!r} (label={label!r}) is not in SOURCE_KIND_LABELS"
|
||||
)
|
||||
assert kind in operator.SOURCE_KIND_LABELS, f"Menu kind {kind!r} (label={label!r}) is not in SOURCE_KIND_LABELS"
|
||||
|
||||
|
||||
def test_every_label_has_a_menu_entry():
|
||||
|
||||
@@ -201,9 +201,7 @@ class TestIterElementsForSource(NewFile):
|
||||
storey = ifc.create_entity("IfcBuildingStorey")
|
||||
wall_a = ifc.create_entity("IfcWall")
|
||||
wall_b = ifc.create_entity("IfcWall")
|
||||
ifcopenshell.api.spatial.assign_container(
|
||||
ifc, products=[wall_a, wall_b], relating_structure=storey
|
||||
)
|
||||
ifcopenshell.api.spatial.assign_container(ifc, products=[wall_a, wall_b], relating_structure=storey)
|
||||
|
||||
result = tool.ClipBox.iter_elements_for_source("SPATIAL", str(storey.id()))
|
||||
|
||||
@@ -215,9 +213,7 @@ class TestIterElementsForSource(NewFile):
|
||||
wall_type = ifc.create_entity("IfcWallType")
|
||||
wall_a = ifc.create_entity("IfcWall")
|
||||
wall_b = ifc.create_entity("IfcWall")
|
||||
ifcopenshell.api.type.assign_type(
|
||||
ifc, related_objects=[wall_a, wall_b], relating_type=wall_type
|
||||
)
|
||||
ifcopenshell.api.type.assign_type(ifc, related_objects=[wall_a, wall_b], relating_type=wall_type)
|
||||
|
||||
result = tool.ClipBox.iter_elements_for_source("TYPE", str(wall_type.id()))
|
||||
|
||||
@@ -263,9 +259,7 @@ class TestComputeMatrixForSource(NewFile):
|
||||
storey = ifc.create_entity("IfcBuildingStorey")
|
||||
wall_a, _ = _make_ifc_cube(ifc, "IfcWall", location=(0.0, 0.0, 0.0), size=2.0)
|
||||
wall_b, _ = _make_ifc_cube(ifc, "IfcWall", location=(4.0, 0.0, 0.0), size=2.0)
|
||||
ifcopenshell.api.spatial.assign_container(
|
||||
ifc, products=[wall_a, wall_b], relating_structure=storey
|
||||
)
|
||||
ifcopenshell.api.spatial.assign_container(ifc, products=[wall_a, wall_b], relating_structure=storey)
|
||||
|
||||
matrix = tool.ClipBox.compute_matrix_for_source("SPATIAL", str(storey.id()))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user