M4.1: Straighten tool (drag along the horizon or a vertical) + Auto straighten (crop.autoStraighten)

Auto uses Upright's consensus level angle as the crop angle; sign verified on a tilted test image
and pinned by a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-01 10:28:29 -04:00
co-authored by Claude Opus 5.5
parent 6015af99b7
commit ce89ab5375
6 changed files with 110 additions and 4 deletions
+22
View File
@@ -330,6 +330,28 @@ pub fn specs() -> Vec<CommandSpec> {
})
}
),
cmd!(
"crop.autoStraighten",
"Auto Straighten",
[],
None,
"{} — level the horizon (or plumb lines) with the crop angle",
has_active,
|s, _| {
let id = active(s, "crop.autoStraighten")?;
let src = s.source_now(id, SourceLevel::Preview).map_err(|e| bad("crop.autoStraighten", e))?;
let info = s.catalog.photo(id).map(|p| crate::media::source_info(p)).unwrap_or_default();
let d = s.develop_of(id).unwrap_or_default();
let Some(level) = lightcraft_pipeline::upright::level_degrees(&src, &info, &d) else {
return Ok(json!({"changed": false, "reason": "no dominant horizontal or vertical lines"}));
};
// Same convention as the crop angle (verified: a horizon descending 6° to the right
// comes out level with angle = level ≈ −6°).
let angle = level.clamp(-45.0, 45.0);
s.execute("crop.straighten", &json!({"angle": (angle * 100.0).round() / 100.0}))?;
Ok(json!({"changed": true, "angle": angle}))
}
),
cmd!(
"crop.aspect",
"Crop Aspect",