fix: ViewCube hover, glow, centroids, corner geometry and snap angles

- Hide OS crosshair and drawn crosshair when cursor is over ViewCube area
- Enhanced hover glow in WGSL shader (blue tint + brightness boost)
- Fixed region_centroids() Y-axis sign for all FRONT/BACK-related regions
- Swapped corner geometry assignments (TPF↔TBK, BTF↔BBK) to match
  intended FRONT/BACK semantics (sy=-1 = FRONT, sy=+1 = BACK)
- Corrected all snap_angles: yaw_pitch_to_quat(0,0) places eye at -Y
  (FRONT view), so FRONT=0 and BACK=PI — cascaded through edges/corners

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-04-23 19:11:19 +03:00
commit a6592cc9bc
4 changed files with 87 additions and 59 deletions

View file

@ -189,7 +189,7 @@ impl H7CAD {
vec![]
};
overlay::selection_overlay(sel, snap_info, grips, grid, ucs_icon, ost_points, tab.last_cursor_screen)
overlay::selection_overlay(sel, snap_info, grips, grid, ucs_icon, ost_points, tab.last_cursor_screen, !is_paper)
};
let nav = container(overlay::nav_toolbar())

View file

@ -78,30 +78,30 @@ impl CubeRegion {
match self.id() {
0 => (0.0, FRAC_PI_2),
1 => (0.0, -FRAC_PI_2),
2 => (PI, 0.0),
3 => (0.0, 0.0),
4 => (FRAC_PI_2, 0.0),
5 => (-FRAC_PI_2, 0.0),
6 => (PI, FRAC_PI_4),
7 => (0.0, FRAC_PI_4),
8 => (FRAC_PI_2, FRAC_PI_4),
9 => (-FRAC_PI_2, FRAC_PI_4),
10 => (PI, -FRAC_PI_4),
11 => (0.0, -FRAC_PI_4),
2 => (0.0, 0.0), // FRONT: yaw=0 (eye at -Y)
3 => (PI, 0.0), // BACK: yaw=PI (eye at +Y)
4 => (FRAC_PI_2, 0.0), // RIGHT
5 => (-FRAC_PI_2, 0.0), // LEFT
6 => (0.0, FRAC_PI_4), // EDGE_TOP_FRONT
7 => (PI, FRAC_PI_4), // EDGE_TOP_BACK
8 => (FRAC_PI_2, FRAC_PI_4), // EDGE_TOP_RIGHT
9 => (-FRAC_PI_2, FRAC_PI_4), // EDGE_TOP_LEFT
10 => (0.0, -FRAC_PI_4), // EDGE_BOT_FRONT
11 => (PI, -FRAC_PI_4), // EDGE_BOT_BACK
12 => (FRAC_PI_2, -FRAC_PI_4),
13 => (-FRAC_PI_2, -FRAC_PI_4),
14 => (FRAC_PI_4, 0.0),
15 => (-FRAC_PI_4, 0.0),
16 => (PI * 0.75, 0.0),
17 => (-PI * 0.75, 0.0),
18 => (PI * 0.75, iso),
19 => (-PI * 0.75, iso),
20 => (FRAC_PI_4, iso),
21 => (-FRAC_PI_4, iso),
22 => (PI * 0.75, -iso),
23 => (-PI * 0.75, -iso),
24 => (FRAC_PI_4, -iso),
25 => (-FRAC_PI_4, -iso),
14 => (FRAC_PI_4, 0.0), // EDGE_FRONT_RIGHT (between 0 and PI/2)
15 => (-FRAC_PI_4, 0.0), // EDGE_FRONT_LEFT
16 => (PI * 0.75, 0.0), // EDGE_BACK_RIGHT (between PI and PI/2)
17 => (-PI * 0.75, 0.0), // EDGE_BACK_LEFT
18 => (FRAC_PI_4, iso), // CORNER_TPF_R (FRONT+RIGHT)
19 => (-FRAC_PI_4, iso), // CORNER_TPF_L (FRONT+LEFT)
20 => (PI * 0.75, iso), // CORNER_TBK_R (BACK+RIGHT)
21 => (-PI * 0.75, iso), // CORNER_TBK_L (BACK+LEFT)
22 => (FRAC_PI_4, -iso), // CORNER_BTF_R
23 => (-FRAC_PI_4, -iso), // CORNER_BTF_L
24 => (PI * 0.75, -iso), // CORNER_BBK_R
25 => (-PI * 0.75, -iso), // CORNER_BBK_L
_ => (0.0, 0.0),
}
}
@ -909,14 +909,14 @@ pub fn build_geometry() -> (Vec<CubeVertex>, Vec<u32>) {
&mut is,
);
for &([sx, sy, sz], region) in &[
([1.0f32, 1.0, 1.0], CORNER_TPF_R),
([-1.0, 1.0, 1.0], CORNER_TPF_L),
([1.0, 1.0, -1.0], CORNER_BTF_R),
([-1.0, 1.0, -1.0], CORNER_BTF_L),
([1.0, -1.0, 1.0], CORNER_TBK_R),
([-1.0, -1.0, 1.0], CORNER_TBK_L),
([1.0, -1.0, -1.0], CORNER_BBK_R),
([-1.0, -1.0, -1.0], CORNER_BBK_L),
([1.0f32, 1.0, 1.0], CORNER_TBK_R), // sy=+1 = BACK yönü
([-1.0, 1.0, 1.0], CORNER_TBK_L),
([1.0, 1.0, -1.0], CORNER_BBK_R),
([-1.0, 1.0, -1.0], CORNER_BBK_L),
([1.0, -1.0, 1.0], CORNER_TPF_R), // sy=-1 = FRONT yönü
([-1.0, -1.0, 1.0], CORNER_TPF_L),
([1.0, -1.0, -1.0], CORNER_BTF_R),
([-1.0, -1.0, -1.0], CORNER_BTF_L),
] {
push_tri(
[sx * F, sy * F, sz * E],
@ -934,32 +934,32 @@ pub fn build_geometry() -> (Vec<CubeVertex>, Vec<u32>) {
pub fn region_centroids() -> [[f32; 3]; NUM_REGIONS] {
let m = (F + E) * 0.5;
[
[0.0, 0.0, E],
[0.0, 0.0, -E],
[0.0, E, 0.0],
[0.0, -E, 0.0],
[E, 0.0, 0.0],
[-E, 0.0, 0.0],
[0.0, m, m],
[0.0, -m, m],
[m, 0.0, m],
[-m, 0.0, m],
[0.0, m, -m],
[0.0, -m, -m],
[m, 0.0, -m],
[-m, 0.0, -m],
[m, m, 0.0],
[-m, m, 0.0],
[m, -m, 0.0],
[-m, -m, 0.0],
[m, m, m],
[-m, m, m],
[m, -m, m],
[-m, -m, m],
[m, m, -m],
[-m, m, -m],
[m, -m, -m],
[-m, -m, -m],
[0.0, 0.0, E], // FACE_TOP
[0.0, 0.0, -E], // FACE_BOTTOM
[0.0, -E, 0.0], // FACE_FRONT (geometry Y=-E)
[0.0, E, 0.0], // FACE_BACK (geometry Y=+E)
[E, 0.0, 0.0], // FACE_RIGHT
[-E, 0.0, 0.0], // FACE_LEFT
[0.0, -m, m], // EDGE_TOP_FRONT
[0.0, m, m], // EDGE_TOP_BACK
[m, 0.0, m], // EDGE_TOP_RIGHT
[-m, 0.0, m], // EDGE_TOP_LEFT
[0.0, -m, -m], // EDGE_BOT_FRONT
[0.0, m, -m], // EDGE_BOT_BACK
[m, 0.0, -m], // EDGE_BOT_RIGHT
[-m, 0.0, -m], // EDGE_BOT_LEFT
[m, -m, 0.0], // EDGE_FRONT_RIGHT
[-m, -m, 0.0], // EDGE_FRONT_LEFT
[m, m, 0.0], // EDGE_BACK_RIGHT
[-m, m, 0.0], // EDGE_BACK_LEFT
[m, -m, m], // CORNER_TPF_R (geometry sy=-1 = FRONT)
[-m, -m, m], // CORNER_TPF_L
[m, m, m], // CORNER_TBK_R (geometry sy=+1 = BACK)
[-m, m, m], // CORNER_TBK_L
[m, -m, -m], // CORNER_BTF_R (geometry sy=-1 = FRONT)
[-m, -m, -m], // CORNER_BTF_L
[m, m, -m], // CORNER_BBK_R (geometry sy=+1 = BACK)
[-m, m, -m], // CORNER_BBK_L
]
}

View file

@ -26,7 +26,9 @@ fn fs_main(in: VOut) -> @location(0) vec4<f32> {
let is_hovered = abs(in.region_f - u.hover_region.x) < 0.001 && u.hover_region.x >= 0.0;
var final_rgb = in.color;
if is_hovered {
final_rgb = mix(final_rgb, vec3<f32>(0.55, 0.78, 1.0), 0.45);
let glow = vec3<f32>(0.68, 0.88, 1.0);
final_rgb = mix(final_rgb, glow, 0.60);
final_rgb = clamp(final_rgb * 1.30, vec3<f32>(0.0), vec3<f32>(1.0));
}
return vec4<f32>(final_rgb, 1.0);
}

View file

@ -204,6 +204,7 @@ pub fn selection_overlay<'a>(
ucs_icon: Option<UcsIconParams>,
ost_points: Vec<OstTrackPoint>,
cursor_screen: Point,
show_viewcube: bool,
) -> Element<'a, Message> {
canvas(SelectionCanvas {
selection,
@ -213,6 +214,7 @@ pub fn selection_overlay<'a>(
ucs_icon,
ost_points,
cursor_screen,
show_viewcube,
})
.width(Length::Fill)
.height(Length::Fill)
@ -227,6 +229,7 @@ struct SelectionCanvas {
ucs_icon: Option<UcsIconParams>,
ost_points: Vec<OstTrackPoint>,
cursor_screen: Point,
show_viewcube: bool,
}
impl canvas::Program<Message> for SelectionCanvas {
@ -238,6 +241,18 @@ impl canvas::Program<Message> for SelectionCanvas {
bounds: iced::Rectangle,
cursor: mouse::Cursor,
) -> mouse::Interaction {
if self.show_viewcube {
if let Some(pos) = cursor.position_in(bounds) {
use crate::scene::{VIEWCUBE_DRAW_PX, VIEWCUBE_PAD};
let vc_x = bounds.width - VIEWCUBE_DRAW_PX - VIEWCUBE_PAD;
let vc_y = VIEWCUBE_PAD;
if pos.x >= vc_x && pos.x <= vc_x + VIEWCUBE_DRAW_PX
&& pos.y >= vc_y && pos.y <= vc_y + VIEWCUBE_DRAW_PX
{
return mouse::Interaction::None;
}
}
}
if cursor.is_over(bounds) {
mouse::Interaction::Crosshair
} else {
@ -251,7 +266,7 @@ impl canvas::Program<Message> for SelectionCanvas {
renderer: &iced::Renderer,
_theme: &Theme,
bounds: iced::Rectangle,
_cursor: mouse::Cursor,
cursor: mouse::Cursor,
) -> Vec<canvas::Geometry> {
let mut frame = canvas::Frame::new(renderer, bounds.size());
@ -506,6 +521,16 @@ impl canvas::Program<Message> for SelectionCanvas {
}
// ── CAD crosshair cursor ──────────────────────────────────────────────
let over_viewcube = self.show_viewcube && {
use crate::scene::{VIEWCUBE_DRAW_PX, VIEWCUBE_PAD};
cursor.position_in(bounds).map_or(false, |pos| {
let vc_x = bounds.width - VIEWCUBE_DRAW_PX - VIEWCUBE_PAD;
let vc_y = VIEWCUBE_PAD;
pos.x >= vc_x && pos.x <= vc_x + VIEWCUBE_DRAW_PX
&& pos.y >= vc_y && pos.y <= vc_y + VIEWCUBE_DRAW_PX
})
};
if !over_viewcube {
if let Some(cp) = self.selection.last_move_pos {
let color = Color {
r: 0.85,
@ -551,6 +576,7 @@ impl canvas::Program<Message> for SelectionCanvas {
frame.stroke(&v_bot, stroke.clone());
frame.stroke(&square, stroke);
}
} // end !over_viewcube
// ── UCS icon ──────────────────────────────────────────────────────
if let Some(ref ucs) = self.ucs_icon {