fix(viewport): adapt layout crosshair contrast
Choose the sheet or desk surface under the cursor instead of the active input space so MSPACE stays visible on light paper.\n\nRefs #611
This commit is contained in:
parent
1cf2b700ce
commit
9382a412df
4 changed files with 84 additions and 5 deletions
|
|
@ -39,6 +39,40 @@ const PAPER_SPACE_BACKGROUND: Color = Color {
|
|||
a: 1.0,
|
||||
};
|
||||
|
||||
/// Base surface directly under the crosshair. Paper content viewports render
|
||||
/// transparently over the sheet/desk, including while MSPACE input is active.
|
||||
fn crosshair_background(tab: &DocumentTab, is_paper: bool) -> [f32; 4] {
|
||||
if !is_paper {
|
||||
return tab.scene.bg_color;
|
||||
}
|
||||
|
||||
let desk = [
|
||||
PAPER_SPACE_BACKGROUND.r,
|
||||
PAPER_SPACE_BACKGROUND.g,
|
||||
PAPER_SPACE_BACKGROUND.b,
|
||||
PAPER_SPACE_BACKGROUND.a,
|
||||
];
|
||||
let (cursor, viewport_size) = {
|
||||
let selection = tab.scene.selection.borrow();
|
||||
(selection.last_move_pos, selection.vp_size)
|
||||
};
|
||||
let Some(cursor) = cursor else {
|
||||
return desk;
|
||||
};
|
||||
if viewport_size.0 <= 0.0 || viewport_size.1 <= 0.0 {
|
||||
return desk;
|
||||
}
|
||||
let on_sheet = tab
|
||||
.scene
|
||||
.paper_sheet_screen_rect(viewport_size)
|
||||
.is_some_and(|rect| rect.contains(cursor));
|
||||
if on_sheet {
|
||||
tab.scene.paper_bg_color
|
||||
} else {
|
||||
desk
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear gap (px) kept between the render-mode bar (top-left) and the ViewCube
|
||||
/// (top-right) before the cube is judged to collide and hides.
|
||||
const VIEWCUBE_GAP: f32 = 12.0;
|
||||
|
|
@ -592,11 +626,7 @@ impl OpenCADStudio {
|
|||
tab.pan_mode,
|
||||
self.ribbon.open_dropdown.is_some(),
|
||||
hover_locked,
|
||||
if tab.scene.input_uses_model_space() {
|
||||
tab.scene.bg_color
|
||||
} else {
|
||||
tab.scene.paper_bg_color
|
||||
},
|
||||
crosshair_background(tab, is_paper),
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,9 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
}
|
||||
self.paper_viewport_cache
|
||||
.borrow_mut()
|
||||
.remove(&self.current_layout);
|
||||
}
|
||||
|
||||
/// ZOOM All frames the configured drawing limits. Object-only framing
|
||||
|
|
|
|||
|
|
@ -458,6 +458,7 @@ struct PaperViewportCache {
|
|||
layout_block: Handle,
|
||||
sheet: Handle,
|
||||
content: Arc<Vec<Handle>>,
|
||||
paper_limits: Option<((f64, f64), (f64, f64))>,
|
||||
}
|
||||
|
||||
struct PaperSheetRenderCache {
|
||||
|
|
@ -3354,6 +3355,12 @@ impl Scene {
|
|||
return None;
|
||||
}
|
||||
|
||||
if let Some(cache) = self.paper_viewport_cache.borrow().get(&self.current_layout) {
|
||||
if cache.epoch == self.geometry_epoch && cache.layout == self.current_layout {
|
||||
return cache.paper_limits;
|
||||
}
|
||||
}
|
||||
|
||||
self.document
|
||||
.objects
|
||||
.values()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ impl Scene {
|
|||
|
||||
let layout_block = self.current_layout_block_handle();
|
||||
let sheet = self.current_layout_sheet_viewport_handle();
|
||||
let paper_limits = self.paper_limits();
|
||||
let is_content = |handle: Handle| {
|
||||
let Some(EntityType::Viewport(vp)) = self.document.get_entity(handle) else {
|
||||
return false;
|
||||
|
|
@ -62,6 +63,7 @@ impl Scene {
|
|||
layout_block,
|
||||
sheet,
|
||||
content: Arc::clone(&content),
|
||||
paper_limits,
|
||||
},
|
||||
);
|
||||
(layout_block, sheet, content)
|
||||
|
|
@ -324,6 +326,43 @@ impl Scene {
|
|||
height: h,
|
||||
})
|
||||
}
|
||||
|
||||
/// Physical sheet bounds in canvas pixels. Uses the same forced top-down
|
||||
/// paper transform as the GPU sheet viewport, ignoring stored camera twist.
|
||||
pub fn paper_sheet_screen_rect(
|
||||
&self,
|
||||
canvas_px: (f32, f32),
|
||||
) -> Option<iced::Rectangle> {
|
||||
let ((x0, y0), (x1, y1)) = self.paper_limits()?;
|
||||
let (canvas_w, canvas_h) = canvas_px;
|
||||
if canvas_w < 1.0 || canvas_h < 1.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let cam = self.camera.borrow();
|
||||
let half_h = cam.ortho_size();
|
||||
let half_w = half_h * canvas_w / canvas_h;
|
||||
let tx = cam.target.x as f32;
|
||||
let ty = cam.target.y as f32;
|
||||
drop(cam);
|
||||
let to_px = |wx: f32, wy: f32| -> (f32, f32) {
|
||||
let x = (wx - tx + half_w) / (2.0 * half_w) * canvas_w;
|
||||
let y = (ty + half_h - wy) / (2.0 * half_h) * canvas_h;
|
||||
(x, y)
|
||||
};
|
||||
let min_x = x0.min(x1) as f32;
|
||||
let max_x = x0.max(x1) as f32;
|
||||
let min_y = y0.min(y1) as f32;
|
||||
let max_y = y0.max(y1) as f32;
|
||||
let (left, top) = to_px(min_x, max_y);
|
||||
let (right, bottom) = to_px(max_x, min_y);
|
||||
Some(iced::Rectangle {
|
||||
x: left,
|
||||
y: top,
|
||||
width: (right - left).max(0.0),
|
||||
height: (bottom - top).max(0.0),
|
||||
})
|
||||
}
|
||||
// ── Paper-space helpers ───────────────────────────────────────────────
|
||||
|
||||
/// Paper-layout hatch fills, restricted to the active layout block (used by
|
||||
|
|
|
|||
Loading…
Reference in a new issue