mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 10:11:46 +00:00
Stack cursor-anchored wall gizmos along screen-up in top view
The extend-X / extend-Z / split icons share the cursor's projected X on the wall axis, separated only by world Z (floor / cursor / wall top). World Z collapses to a single screen point in plan view, so every icon piled onto extend-X's hit target and only the topmost was clickable. Two refinements ported from gizmos-8088: * When ``tool.Blender.is_view_top_down(context)`` reports the camera is near plan-view, swap world-Z stacking for screen-up stacking: anchor all icons at the floor world position and offset each by ``index * CURSOR_STACK_OFFSET`` along ``tool.Blender.get_screen_up_world(context)``. Each icon lands in its own screen-space slot regardless of view rotation. * In the same top-down branch, drop ``extend_z_gizmo`` entirely. A vertical-intent gizmo has no readable cue when looking down +Z — clicking it would mutate the wall in a direction the user can't see change. * Bonus: split's local Z now goes through ``core.extrusion_depth_from_vertical_height(props.height, props.x_angle)`` so the icon lands on the slanted top edge of sloped walls (x_angle != 0) instead of the vertical-height target the wall isn't at. All three helpers (``is_view_top_down``, ``get_screen_up_world``, ``extrusion_depth_from_vertical_height``) already on HEAD from PR2/PR3. Non-top views unchanged — same world-Z stacking + cascading bumps as before. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -2090,12 +2090,19 @@ class GizmoWallEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
|
|||||||
|
|
||||||
def _update_cursor_gizmos(self, context: bpy.types.Context, mw: Matrix, props: "BIMWallProperties") -> None:
|
def _update_cursor_gizmos(self, context: bpy.types.Context, mw: Matrix, props: "BIMWallProperties") -> None:
|
||||||
"""Position the cursor-anchored icons (extend-X / extend-Z / split) on the wall
|
"""Position the cursor-anchored icons (extend-X / extend-Z / split) on the wall
|
||||||
axis at the cursor's projected X, each at the Z its action would land at.
|
axis at the cursor's projected X.
|
||||||
|
|
||||||
When two icons want the same Z (within ``CURSOR_STACK_OFFSET``), bump the
|
Two branches by view orientation:
|
||||||
lower-priority one upward so both stay clickable. Priority low → high:
|
|
||||||
extend-X, extend-Z, split. Bumps cascade — bumping extend-Z up can in turn
|
- **Non-top-down**: world-Z stacking. Each icon sits at the Z its
|
||||||
collide with split, so extend-Z gets bumped further to clear it."""
|
action would land at (extend-X at floor, extend-Z at cursor Z,
|
||||||
|
split at wall top). Colliding icons stack at ``CURSOR_STACK_OFFSET``
|
||||||
|
increments; priority low → high: extend-X, extend-Z, split.
|
||||||
|
- **Top-down (plan view)**: world-Z collapses to one screen point,
|
||||||
|
so the world-Z stack would invisibly pile every icon on top of
|
||||||
|
``extend_x``. Drop ``extend_z`` (vertical intent has no readable
|
||||||
|
cue when looking down +Z) and stack the rest along screen-up at
|
||||||
|
the floor anchor."""
|
||||||
if not hasattr(self, "split_gizmo"):
|
if not hasattr(self, "split_gizmo"):
|
||||||
return
|
return
|
||||||
all_gizmos = (self.extend_x_gizmo, self.extend_z_gizmo, self.split_gizmo)
|
all_gizmos = (self.extend_x_gizmo, self.extend_z_gizmo, self.split_gizmo)
|
||||||
@@ -2107,16 +2114,17 @@ class GizmoWallEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
|
|||||||
cursor_local = mw.inverted() @ cursor_world
|
cursor_local = mw.inverted() @ cursor_world
|
||||||
in_range = props.anchor_x < cursor_local.x < props.anchor_x + props.length
|
in_range = props.anchor_x < cursor_local.x < props.anchor_x + props.length
|
||||||
billboard_rot = self._frame_billboard_rot
|
billboard_rot = self._frame_billboard_rot
|
||||||
|
top_down = tool.Blender.is_view_top_down(context)
|
||||||
|
|
||||||
# Candidates ordered by priority (lowest first). Each is (gizmo, local_z).
|
# Candidates ordered by priority (lowest first). Each is (gizmo, local_z).
|
||||||
# The local X and Y are common: at the cursor's projected X on the axis.
|
candidates: list[tuple[bpy.types.Gizmo, float]] = [(self.extend_x_gizmo, 0.0)]
|
||||||
# Split only joins when the cursor sits inside the wall's length range.
|
if not top_down:
|
||||||
candidates: list[tuple[bpy.types.Gizmo, float]] = [
|
candidates.append((self.extend_z_gizmo, cursor_local.z))
|
||||||
(self.extend_x_gizmo, 0.0),
|
|
||||||
(self.extend_z_gizmo, cursor_local.z),
|
|
||||||
]
|
|
||||||
if in_range:
|
if in_range:
|
||||||
candidates.append((self.split_gizmo, props.height))
|
# Vertical (world-Z) height → wall-local Z so the icon lands on
|
||||||
|
# the slanted top edge for sloped walls (x_angle != 0).
|
||||||
|
split_local_z = core.extrusion_depth_from_vertical_height(props.height, props.x_angle)
|
||||||
|
candidates.append((self.split_gizmo, split_local_z))
|
||||||
|
|
||||||
# Resolve collisions: walk in priority order and ensure each gizmo's
|
# Resolve collisions: walk in priority order and ensure each gizmo's
|
||||||
# final Z is at least CURSOR_STACK_OFFSET above the previous one (when
|
# final Z is at least CURSOR_STACK_OFFSET above the previous one (when
|
||||||
@@ -2126,12 +2134,22 @@ class GizmoWallEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
|
|||||||
final_z = desired_z
|
final_z = desired_z
|
||||||
for _, prev_z in resolved:
|
for _, prev_z in resolved:
|
||||||
if abs(final_z - prev_z) < self.CURSOR_STACK_OFFSET:
|
if abs(final_z - prev_z) < self.CURSOR_STACK_OFFSET:
|
||||||
# Bump up to clear the previous gizmo's slot.
|
|
||||||
final_z = prev_z + self.CURSOR_STACK_OFFSET
|
final_z = prev_z + self.CURSOR_STACK_OFFSET
|
||||||
resolved.append((gz, final_z))
|
resolved.append((gz, final_z))
|
||||||
|
|
||||||
for gz in all_gizmos:
|
for gz in all_gizmos:
|
||||||
gz.hide = True
|
gz.hide = True
|
||||||
|
if top_down:
|
||||||
|
# Swap world-Z stacking for screen-up stacking so each icon stays
|
||||||
|
# individually clickable when the camera projects world Z to zero.
|
||||||
|
screen_up = tool.Blender.get_screen_up_world(context)
|
||||||
|
base_world = mw @ Vector((cursor_local.x, 0.0, 0.0))
|
||||||
|
for index, (gz, _local_z) in enumerate(resolved):
|
||||||
|
gz.hide = self.is_gizmo_hidden_by_modal(gz)
|
||||||
|
world_pos = base_world + screen_up * (index * self.CURSOR_STACK_OFFSET)
|
||||||
|
gz.matrix_basis = gizmo.billboarded_at(world_pos, billboard_rot)
|
||||||
|
_apply_wall_extend_flips(gz, self, world_pos, mw, cursor_local, props, billboard_rot)
|
||||||
|
return
|
||||||
for gz, local_z in resolved:
|
for gz, local_z in resolved:
|
||||||
gz.hide = self.is_gizmo_hidden_by_modal(gz)
|
gz.hide = self.is_gizmo_hidden_by_modal(gz)
|
||||||
world_pos = mw @ Vector((cursor_local.x, 0.0, local_z))
|
world_pos = mw @ Vector((cursor_local.x, 0.0, local_z))
|
||||||
|
|||||||
Reference in New Issue
Block a user