Section: leave the origin and construction planes and axes whole (#1286)
Section Analysis clipped the origin and construction planes and axes along with the model. They now draw from their own unclipped lists, with the same depth and blending; bodies, edges, sketches and highlights are clipped as before. Fixes #1229. Co-authored-by: DuBearte <[email protected]>
This commit is contained in:
1 parent
1ec33fcdc7
commit
7391e8ed75
3 files changed
+61
-5
No files matched your search
@@ -200,6 +200,12 @@ fn fs_tri(i: TOut) -> @location(0) vec4<f32> {
|
||||
return shade(i);
|
||||
}
|
||||
|
||||
/// Reference planes: shaded like `fs_tri`, but never cut by the section plane.
|
||||
@fragment
|
||||
fn fs_tri_all(i: TOut) -> @location(0) vec4<f32> {
|
||||
return shade(i);
|
||||
}
|
||||
|
||||
fn shade(i: TOut) -> vec4<f32> {
|
||||
// A zero normal marks flat colour (selection fills, translucent planes).
|
||||
if (length(i.n) < 0.5) {
|
||||
|
||||
@@ -56,6 +56,10 @@ pub struct GpuScene {
|
||||
/// Lines drawn first, under everything (the ground grid: the model hides it, it never
|
||||
/// hides the model).
|
||||
pub under: Vec<u8>,
|
||||
/// Origin and construction planes (translucent, like `trans`) and their axes (lines, like
|
||||
/// `lines`): reference geometry, never cut away by a section (#1229).
|
||||
pub guide_trans: Vec<u8>,
|
||||
pub guide_lines: Vec<u8>,
|
||||
/// Opaque bodies drawn from their plans on the GPU, like `tris`.
|
||||
pub planned: Vec<PlannedBody>,
|
||||
/// The model's bodies, a few at a time, drawn before the loose geometry above (`tris`,
|
||||
@@ -157,6 +161,21 @@ impl GpuScene {
|
||||
self.under.extend_from_slice(&c);
|
||||
self.under.extend_from_slice(&width.to_le_bytes());
|
||||
}
|
||||
/// A translucent triangle of a plane a section does not cut (see [`GpuScene::guide_trans`]).
|
||||
pub fn guide_tri(&mut self, p: [f32; 3], c: [u8; 4]) {
|
||||
for v in p.iter().chain(&[0.0; 3]) {
|
||||
self.guide_trans.extend_from_slice(&v.to_le_bytes());
|
||||
}
|
||||
self.guide_trans.extend_from_slice(&c);
|
||||
}
|
||||
/// A depth-tested line a section does not cut (see [`GpuScene::guide_lines`]).
|
||||
pub fn guide_line(&mut self, a: [f32; 3], b: [f32; 3], c: [u8; 4], width: f32) {
|
||||
for v in a.iter().chain(&b) {
|
||||
self.guide_lines.extend_from_slice(&v.to_le_bytes());
|
||||
}
|
||||
self.guide_lines.extend_from_slice(&c);
|
||||
self.guide_lines.extend_from_slice(&width.to_le_bytes());
|
||||
}
|
||||
pub fn line(&mut self, a: [f32; 3], b: [f32; 3], c: [u8; 4], width: f32, on_top: bool) {
|
||||
let out = if on_top { &mut self.overlay } else { &mut self.lines };
|
||||
for v in a.iter().chain(&b) {
|
||||
@@ -291,6 +310,8 @@ struct Batches {
|
||||
ghost: Option<Batch>,
|
||||
xray: Option<Batch>,
|
||||
under: Option<Batch>,
|
||||
guide_trans: Option<Batch>,
|
||||
guide_lines: Option<Batch>,
|
||||
planned: Vec<PlannedDraw>,
|
||||
/// Evaluated chunks by their plans' addresses, holding the plans so the addresses stay
|
||||
/// theirs: a chunk whose plans are unchanged is not evaluated again when the scene is rebuilt.
|
||||
@@ -341,6 +362,8 @@ impl Batches {
|
||||
self.ghost = upload(device, "sc_ghost", &sc.ghost, TRI_SIZE);
|
||||
self.xray = upload(device, "sc_xray", &sc.xray, TRI_SIZE);
|
||||
self.under = upload(device, "sc_under", &sc.under, LINE_SIZE);
|
||||
self.guide_trans = upload(device, "sc_guide_trans", &sc.guide_trans, TRI_SIZE);
|
||||
self.guide_lines = upload(device, "sc_guide_lines", &sc.guide_lines, LINE_SIZE);
|
||||
self.key = Some(key);
|
||||
}
|
||||
}
|
||||
@@ -428,6 +451,9 @@ struct Resources {
|
||||
/// Lines behind the model (depth test >).
|
||||
line_hidden: wgpu::RenderPipeline,
|
||||
overlay: wgpu::RenderPipeline,
|
||||
/// Reference planes and axes: as `trans` and `line`, but not cut by a section.
|
||||
guide_trans: wgpu::RenderPipeline,
|
||||
guide_line: wgpu::RenderPipeline,
|
||||
uniform: wgpu::Buffer,
|
||||
bind: wgpu::BindGroup,
|
||||
linear_out: bool,
|
||||
@@ -764,6 +790,8 @@ impl Resources {
|
||||
line: pipeline("sc_lines", "vs_line", "fs_line", line_layout(), depth(wgpu::CompareFunction::LessEqual, false), alpha),
|
||||
line_hidden: pipeline("sc_lines_hidden", "vs_line", "fs_line", line_layout(), depth(wgpu::CompareFunction::Greater, false), alpha),
|
||||
overlay: pipeline("sc_overlay", "vs_line", "fs_line_all", line_layout(), depth(wgpu::CompareFunction::Always, false), alpha),
|
||||
guide_trans: pipeline("sc_guide_trans", "vs_tri", "fs_tri_all", tri_layout(), depth(wgpu::CompareFunction::LessEqual, false), alpha),
|
||||
guide_line: pipeline("sc_guide_lines", "vs_line", "fs_line_all", line_layout(), depth(wgpu::CompareFunction::LessEqual, false), alpha),
|
||||
uniform,
|
||||
bind,
|
||||
linear_out: t.format.is_srgb(),
|
||||
@@ -1038,6 +1066,7 @@ impl egui_wgpu::CallbackTrait for ViewportCallback {
|
||||
lines(pass, &res.line_hidden, &h.hidden);
|
||||
lines(pass, &res.line, &pv.lines);
|
||||
lines(pass, &res.line, &h.lines);
|
||||
lines(pass, &res.guide_line, &h.guide_lines);
|
||||
for (id, q) in res.quads.iter().filter(|_| !self.product) {
|
||||
if let Some((_, bind)) = res.textures.get(id) {
|
||||
pass.set_pipeline(&res.image);
|
||||
@@ -1054,6 +1083,7 @@ impl egui_wgpu::CallbackTrait for ViewportCallback {
|
||||
tris(pass, &res.trans, &m.trans);
|
||||
tris(pass, &res.trans, &sk.trans);
|
||||
tris(pass, &res.trans, &h.trans);
|
||||
tris(pass, &res.guide_trans, &h.guide_trans);
|
||||
tris(pass, &res.xray, &pv.xray);
|
||||
lines(pass, &res.overlay, &m.overlays);
|
||||
lines(pass, &res.overlay, &sk.overlays);
|
||||
|
||||
@@ -1544,15 +1544,16 @@ pub(crate) fn profile_fill(sc: &mut GpuScene, app: &SolveApp, sketch: u64, index
|
||||
}
|
||||
}
|
||||
|
||||
/// An origin or construction plane: reference geometry, which a section does not cut (#1229).
|
||||
fn quad(sc: &mut GpuScene, q: &[Vec3; 4], fill: Color32, edge: Color32) {
|
||||
let col = c4(fill);
|
||||
for i in [0, 1, 2, 0, 2, 3] {
|
||||
if let Some(p) = q.get(i) {
|
||||
sc.trans_tri(p.to_f32(), [0.0; 3], col);
|
||||
sc.guide_tri(p.to_f32(), col);
|
||||
}
|
||||
}
|
||||
for i in 0..4 {
|
||||
sc.line(q[i].to_f32(), q[(i + 1) % 4].to_f32(), c4(edge), 1.2, false);
|
||||
sc.guide_line(q[i].to_f32(), q[(i + 1) % 4].to_f32(), c4(edge), 1.2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1628,11 +1629,11 @@ fn build_highlight(app: &SolveApp) -> GpuScene {
|
||||
};
|
||||
let on = sel.iter().any(|x| matches!(x, Sel::Axis { name: n } if n == name)) || matches!(hover, Some(Hit::Axis { name: n }) if n == name);
|
||||
let w = if on { 4.0 } else { 2.0 };
|
||||
sc.line([0.0; 3], (dir * size).to_f32(), c4(col), w, false);
|
||||
sc.guide_line([0.0; 3], (dir * size).to_f32(), c4(col), w);
|
||||
let dash = size * 0.08;
|
||||
let mut a = size + dash;
|
||||
while a < size * AXIS_LEN {
|
||||
sc.line((dir * a).to_f32(), (dir * (a + dash)).to_f32(), c4(col), w * 0.75, false);
|
||||
sc.guide_line((dir * a).to_f32(), (dir * (a + dash)).to_f32(), c4(col), w * 0.75);
|
||||
a += dash * 2.0;
|
||||
}
|
||||
}
|
||||
@@ -1647,7 +1648,7 @@ fn build_highlight(app: &SolveApp) -> GpuScene {
|
||||
let mut s = 0.0;
|
||||
while s < len {
|
||||
let e = (s + dash).min(len);
|
||||
sc.line(a.lerp(b, s / len).to_f32(), a.lerp(b, e / len).to_f32(), col, w, false);
|
||||
sc.guide_line(a.lerp(b, s / len).to_f32(), a.lerp(b, e / len).to_f32(), col, w);
|
||||
s += dash * 1.6;
|
||||
}
|
||||
}
|
||||
@@ -4590,6 +4591,25 @@ mod tests {
|
||||
assert_eq!(d.items().len(), 1, "the other half toggles the same wall");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_section_leaves_the_origin_planes_and_axes_whole() {
|
||||
// #1229: the origin planes and axes are reference geometry; the section plane cuts the
|
||||
// model, not them, so they go to the lists the GPU draws without the section clip.
|
||||
let mut h = crate::scenario::Harness::new();
|
||||
h.app.run("solid.box", json!({"length":20,"width":20,"height":20,"corner":[-10,-10,-10]})).unwrap();
|
||||
h.app.run("inspect.section", json!({"plane": "XZ", "offset": 0})).unwrap();
|
||||
h.app.ui.show_origin = true;
|
||||
h.frames(2);
|
||||
assert!(section_plane(&h.app).is_some());
|
||||
let sc = build_highlight(&h.app);
|
||||
let planes = origin_planes(&h.app).len();
|
||||
assert!(planes > 0);
|
||||
assert_eq!(sc.guide_trans.len(), planes * 6 * crate::gpu::TRI_SIZE);
|
||||
assert!(sc.trans.is_empty(), "no origin plane is drawn where the section clips");
|
||||
// Each origin axis: a solid part and dashes beyond, plus each plane's four edges.
|
||||
assert!(sc.guide_lines.len() >= (planes * 4 + 3) * crate::gpu::LINE_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn circular_rim_hover_click_and_dialog_pick_include_both_halves() {
|
||||
let mut app = cylinder_app();
|
||||
|
||||
Reference in new issue
Block a user