mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-02 22:41:47 -06:00
fix(ui): rotate cropped image previews around the committed pivot
Project the visible user-space bounds centre for the drag preview, matching the RotateBy engine operation. The full image centre differs for cropped artwork and caused the preview to jump on release. The regression compares actual painted preview corners with saved/reopened image corners and verifies a requested 90-degree turn on cropped and uncropped controls, including page rotation. Linux scoped clippy, 106 tests, wasm, formatting, layering and assets pass. Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
This commit is contained in:
co-authored by
Codex GPT-6.1
parent
c0a5a5cbf8
commit
fdb6a6f33e
@@ -761,7 +761,8 @@ pub(crate) fn image_input(cx: &ImageCx<'_>, images: &[pdfcraft_engine::PageImage
|
||||
|
||||
if view.image_selection.as_ref().is_some_and(|s| s.turning) {
|
||||
let shift = ui.input(|i| i.modifiers.shift);
|
||||
let centre = obb.screen_center;
|
||||
let visible = image.visible_rect.unwrap_or(image.rect);
|
||||
let centre = user_to_screen(xf, info, page, visible[0] * 0.5 + visible[2] * 0.5, visible[1] * 0.5 + visible[3] * 0.5);
|
||||
let rot_knob = shown_knob;
|
||||
let drag_start = view.image_selection.as_ref().and_then(|s| s.drag).map_or(rot_knob, |d| d.start);
|
||||
let degrees = hover_pos.map_or(0.0, |p| turn_degrees_delta(centre, drag_start, p, shift));
|
||||
|
||||
@@ -9549,3 +9549,69 @@ fn affine_review_resize_uses_press_delta_and_affine_frame() {
|
||||
assert!(placements[0].into_iter().zip(placements[1]).all(|(a, b)| (a - b).abs() < 1e-3), "press offset changed placement: {placements:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn affine_review_cropped_rotation_preview_matches_saved_corners() {
|
||||
let matrix = [70.710678, 70.710678, -70.710678, 70.710678, 170.710678, 100.0];
|
||||
for (clip, rotation) in [(None, 0), (Some([100.0, 170.0, 241.421356, 241.421356]), 0), (Some([100.0, 170.0, 241.421356, 241.421356]), 90)] {
|
||||
let mut h = affine_review_harness(affine_review_image_fixture(matrix, clip, rotation));
|
||||
let view = &h.state().views[0];
|
||||
let id = view.id;
|
||||
let doc = h.state().session.get(id).unwrap();
|
||||
let image = doc.page_images(0)[0].clone();
|
||||
let xf = view.page_xform(0).unwrap();
|
||||
let obb = pdfcraft_ui_egui::canvas::ImageObb::from_image(&image, &xf, &doc.info, 0);
|
||||
let visible = image.visible_rect.unwrap_or(image.rect);
|
||||
let p = doc.info.pages[0].user_to_view(((visible[0] + visible[2]) * 0.5) as f32, ((visible[1] + visible[3]) * 0.5) as f32);
|
||||
let centre = xf.norm_to_screen(p[0] / xf.pw, p[1] / xf.ph);
|
||||
let start = obb.s_rot;
|
||||
let v = start - centre;
|
||||
let finish = centre + egui::vec2(-v.y, v.x);
|
||||
h.hover_at(start);
|
||||
h.run_steps(1);
|
||||
h.drag_at(start);
|
||||
h.run_steps(1);
|
||||
h.hover_at(finish);
|
||||
h.run_steps(2);
|
||||
assert!(h.state().views[0].image_selection.as_ref().unwrap().turning);
|
||||
let preview = h
|
||||
.output()
|
||||
.shapes
|
||||
.iter()
|
||||
.find_map(|shape| match &shape.shape {
|
||||
egui::Shape::Path(path) if path.closed && path.points.len() == 4 && path.stroke.width == 1.5 => Some(path.points.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.expect("rotation preview outline");
|
||||
if clip.is_some()
|
||||
&& rotation == 0
|
||||
&& let Ok(directory) = std::env::var("PDFCRAFT_AFFINE_SHOTS")
|
||||
{
|
||||
h.render().unwrap().save(std::path::Path::new(&directory).join("affine-rotation-preview.png")).unwrap();
|
||||
}
|
||||
h.drop_at(finish);
|
||||
h.run_steps(3);
|
||||
if clip.is_some()
|
||||
&& rotation == 0
|
||||
&& let Ok(directory) = std::env::var("PDFCRAFT_AFFINE_SHOTS")
|
||||
{
|
||||
h.render().unwrap().save(std::path::Path::new(&directory).join("affine-rotation-committed.png")).unwrap();
|
||||
}
|
||||
let saved = h.state().session.save_bytes(id).unwrap();
|
||||
let mut reopened = pdfcraft_engine::Session::new();
|
||||
let reopened_id = reopened.open("saved.pdf", None, saved, None).unwrap();
|
||||
let doc = reopened.get(reopened_id).unwrap();
|
||||
let after = doc.page_images(0)[0].clone();
|
||||
let committed = pdfcraft_ui_egui::canvas::ImageObb::from_matrix(after.matrix, &xf, &doc.info, 0).corners();
|
||||
let (cx, cy) = ((visible[0] + visible[2]) * 0.5, (visible[1] + visible[3]) * 0.5);
|
||||
let expected = pdfcraft_content::Matrix(image.matrix).then(&pdfcraft_content::Matrix([0.0, -1.0, 1.0, 0.0, cx - cy, cy + cx])).0;
|
||||
assert!(
|
||||
after.matrix.into_iter().zip(expected).all(|(a, b)| (a - b).abs() < 1e-3),
|
||||
"knob must commit the requested 90-degree turn around visible bounds"
|
||||
);
|
||||
assert!(
|
||||
preview.iter().zip(committed).all(|(a, b)| a.distance(b) < 1.0),
|
||||
"preview jumped after commit/save: clip={clip:?} rotation={rotation}: {preview:?} != {committed:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user