mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Render: measure 3-D point sizes without the camera's turn
shape_at folds a 3-D camera's turn into the shape's transform, so per_point() measured its foreshortening as if it were a group's child-coordinate scale: in a 1:1 group, a shape under a turned camera got a factor other than 1 and drew its extrusion depth, contour and bevel at the wrong size. The factor is now measured on the transform before the turn (m with the camera's affine taken back out), and the turned transform still draws the geometry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
48dbdffe07
commit
7919429652
@@ -1056,6 +1056,17 @@ impl Renderer {
|
||||
ctx.push_layer(None, None, None, None, None);
|
||||
let _ = r;
|
||||
}
|
||||
// Shape-space units per point for the 3-D sizes, measured without the camera's turn (which
|
||||
// `shape_at` puts in `m`): its foreshortening is not a child-coordinate scale.
|
||||
let pt = {
|
||||
let turn = effects
|
||||
.as_ref()
|
||||
.and_then(|e| e.raw3d.as_deref())
|
||||
.and_then(camera::from_raw)
|
||||
.and_then(|c| camera::affine(&c, w, h))
|
||||
.filter(|t| t.determinant().abs() > 1e-9);
|
||||
self.per_point(turn.map_or(m, |t| m * t.inverse()), depth)
|
||||
};
|
||||
// A 3-D extrusion under a turned camera: the sides, swept behind the face.
|
||||
if let Some(raw) = effects.as_ref().and_then(|e| e.raw3d.as_deref())
|
||||
&& let Some(ex) = extrude::from_raw(raw)
|
||||
@@ -1069,7 +1080,7 @@ impl Renderer {
|
||||
};
|
||||
let pre = m * turn.inverse();
|
||||
let lit = light::Lighting::from_raw(raw);
|
||||
let d = ex.depth * self.per_point(m, depth);
|
||||
let d = ex.depth * pt;
|
||||
extrude::draw(ctx, &outline, w, h, &cam, d, pre, turn, extrude::side_color(rctx, &ex, face), &lit);
|
||||
}
|
||||
// A 3-D contour: a band around the outline, behind the face (and in front of any sides).
|
||||
@@ -1081,7 +1092,7 @@ impl Renderer {
|
||||
_ => None,
|
||||
};
|
||||
let col = c.color.as_ref().map(|k| rctx.color(k, face)).or(face).unwrap_or(Rgba::rgb(0xff, 0xff, 0xff));
|
||||
extrude::draw_contour(ctx, &outline, m, c.width * self.per_point(m, depth), col, &light::Lighting::from_raw(raw));
|
||||
extrude::draw_contour(ctx, &outline, m, c.width * pt, col, &light::Lighting::from_raw(raw));
|
||||
}
|
||||
// Fill.
|
||||
match &s.kind {
|
||||
@@ -1176,8 +1187,7 @@ impl Renderer {
|
||||
};
|
||||
// A 3-D bevel: the face and a band inside the outline lit as surfaces.
|
||||
if let Some(bv) = effects.as_ref().and_then(|e| e.raw3d.as_deref()).and_then(bevel::from_raw) {
|
||||
let k = self.per_point(m, depth);
|
||||
let bv = bevel::Bevel { w: bv.w * k, h: bv.h * k, ..bv };
|
||||
let bv = bevel::Bevel { w: bv.w * pt, h: bv.h * pt, ..bv };
|
||||
let solid = match &fill {
|
||||
Some(Fill::Solid { color: c }) => Some(rctx.color(c, fill_ph)),
|
||||
_ => None,
|
||||
|
||||
@@ -2310,6 +2310,36 @@ fn group_child_scale_does_not_scale_3d_depth_contours_or_bevels() {
|
||||
assert!(d * 20 < drawn, "{d} differing values against {drawn} inked pixels");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_turned_camera_in_a_one_to_one_group_draws_its_3d_sizes_as_outside_one() {
|
||||
// The camera's turn is part of the shape's transform; its foreshortening is not a group scale,
|
||||
// so a 1:1 group must not change extrusion depths, contours or bevels under a turned camera.
|
||||
let mut deck = extruded_deck();
|
||||
let bevelled = Shape {
|
||||
id: ShapeId(705),
|
||||
xfrm: Some(Xfrm::new(40.0, 340.0, 170.0, 130.0)),
|
||||
geom: Geom::preset("rect"),
|
||||
style: Some(ShapeStyle::accent(deckcraft_color::SchemeSlot::Accent1)),
|
||||
effects: Some(deckcraft_model::Effects {
|
||||
raw3d: Some(r#"<a:scene3d><a:camera prst="isometricOffAxis1Right"/><a:lightRig rig="threePt" dir="t"/></a:scene3d><a:sp3d extrusionH="254000" contourW="38100"><a:bevelT w="254000" h="254000"/></a:sp3d>"#.into()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
std::sync::Arc::make_mut(&mut deck.slides[0]).shapes.push(bevelled);
|
||||
let opts = RenderOpts { scale: 1.0, ..Default::default() };
|
||||
let alone = render_slide(&deck, 0, &opts);
|
||||
let group = Shape {
|
||||
id: ShapeId(10),
|
||||
xfrm: Some(Xfrm::new(0.0, 0.0, 960.0, 540.0)),
|
||||
kind: ShapeKind::Group { children: deck.slides[0].shapes.clone(), child: Xfrm::new(0.0, 0.0, 960.0, 540.0) },
|
||||
..Default::default()
|
||||
};
|
||||
let grouped = render_slide(&deck_with(vec![group]), 0, &opts);
|
||||
let d = differing(&alone, &grouped);
|
||||
assert!(d < 200, "{d} values differ between the shapes alone and in a 1:1 group");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_rows_below_the_slide_are_not_laid_out_and_the_slide_looks_the_same() {
|
||||
// A fuzzed deck's 200 x 200 table of long text took 70 s to draw, most of it off the slide.
|
||||
|
||||
Reference in New Issue
Block a user