Render: presets PowerPoint mitres by default get sharp corners; miter limit 8
An outline naming no join was always rounded. PowerPoint rounds most presets but mitres some by default: classified per preset by comparing its export (8 pt outline, no join) with round- and miter-joined renders: the plus, right/up/down block arrows (not the left one), chevrons, stars from five points up, the equation shapes, curved ribbons and the action buttons. Miter joins also take PowerPoint's default limit of 8 widths (kurbo's default was 4), so sharp star points no longer bevel off. Wild synthetic set: joins slide 0.43 -> 0.21, outline-on-pattern slide 3.95 -> 3.76, preset galleries slightly better. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bbba07cb39
commit
5d5fa7d1dc
|
|
@ -339,6 +339,45 @@ pub fn info(name: &str) -> Option<&'static PresetInfo> {
|
|||
CATALOG.iter().find(|p| p.name == name)
|
||||
}
|
||||
|
||||
/// Whether PowerPoint joins this preset's outline with sharp (miter) corners when the line names
|
||||
/// no join (it rounds them otherwise). Observed per preset on its export: the plus, the right,
|
||||
/// up and down block arrows (not the left one), chevrons, stars from five points up, the
|
||||
/// equation shapes, curved ribbons and the action buttons are mitred.
|
||||
pub fn mitred_by_default(name: &str) -> bool {
|
||||
matches!(
|
||||
name,
|
||||
"plus"
|
||||
| "rightArrow"
|
||||
| "upArrow"
|
||||
| "downArrow"
|
||||
| "upDownArrow"
|
||||
| "leftUpArrow"
|
||||
| "bentUpArrow"
|
||||
| "stripedRightArrow"
|
||||
| "notchedRightArrow"
|
||||
| "chevron"
|
||||
| "leftArrowCallout"
|
||||
| "leftRightArrowCallout"
|
||||
| "upDownArrowCallout"
|
||||
| "quadArrowCallout"
|
||||
| "mathPlus"
|
||||
| "mathMinus"
|
||||
| "mathMultiply"
|
||||
| "mathEqual"
|
||||
| "mathNotEqual"
|
||||
| "star5"
|
||||
| "star6"
|
||||
| "star8"
|
||||
| "star10"
|
||||
| "star12"
|
||||
| "star16"
|
||||
| "star24"
|
||||
| "star32"
|
||||
| "ellipseRibbon"
|
||||
| "ellipseRibbon2"
|
||||
) || name.starts_with("actionButton")
|
||||
}
|
||||
|
||||
pub fn is_line_like(name: &str) -> bool {
|
||||
matches!(name, "line" | "straightConnector1" | "bentConnector2" | "bentConnector3" | "curvedConnector3" | "arc")
|
||||
|| name.starts_with("bentConnector")
|
||||
|
|
|
|||
|
|
@ -998,6 +998,13 @@ impl Renderer {
|
|||
ctx.pop_layer();
|
||||
}
|
||||
}
|
||||
// Some presets' outlines default to sharp corners (PowerPoint's own per-shape default).
|
||||
let line = match (&s.geom, line.join) {
|
||||
(Geom::Preset { name, .. }, None) if preset::mitred_by_default(name) => {
|
||||
Line { join: Some(deckcraft_model::style::LineJoin::Miter), ..line }
|
||||
}
|
||||
_ => line,
|
||||
};
|
||||
// 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 solid = match &fill {
|
||||
|
|
@ -1404,7 +1411,8 @@ pub(crate) fn stroke(
|
|||
// Hairlines stay visible at any zoom.
|
||||
let min_w = 0.5 / scale_of(m).max(1e-6);
|
||||
let sw = lw.max(min_w);
|
||||
let mut sk = kurbo::Stroke::new(sw).with_caps(cap).with_join(join);
|
||||
// PowerPoint's default miter limit is 8 widths (`a:miter lim="800000"`).
|
||||
let mut sk = kurbo::Stroke::new(sw).with_caps(cap).with_join(join).with_miter_limit(8.0);
|
||||
// Square caps on a dashed line square off only the line's own two ends (where a dash is
|
||||
// drawn): each dash itself ends flat, as PowerPoint draws them.
|
||||
let mut square_ends = None;
|
||||
|
|
|
|||
|
|
@ -662,3 +662,24 @@ fn a_gradient_not_rotating_with_its_shape_keeps_to_the_slide() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stars_and_right_arrows_have_sharp_corners_by_default() {
|
||||
let shape = |name: &str, join: Option<deckcraft_model::style::LineJoin>| Shape {
|
||||
id: ShapeId(29),
|
||||
xfrm: Some(Xfrm::new(100.0, 100.0, 100.0, 100.0)),
|
||||
geom: Geom::preset(name),
|
||||
fill: solid(WHITE),
|
||||
line: Some(Line { join, ..Line::solid(ColorRef::rgb(RED), 10.0) }),
|
||||
..Default::default()
|
||||
};
|
||||
// A star5's top point mitred reaches about 16 pt above the box; rounded only 5.
|
||||
let img = render(vec![shape("star5", None)]);
|
||||
assert!(close(img.pixel(150, 90), RED, 40), "mitred point {:?}", img.pixel(150, 90));
|
||||
let img = render(vec![shape("star5", Some(deckcraft_model::style::LineJoin::Round))]);
|
||||
assert!(close(img.pixel(150, 90), WHITE, 10), "an explicit round join stays round");
|
||||
// A rectangle keeps PowerPoint's round default: its corner is cut round.
|
||||
let img = render(vec![shape("rect", None)]);
|
||||
assert!(close(img.pixel(95, 95), WHITE, 30), "{:?}", img.pixel(95, 95));
|
||||
assert!(deckcraft_geom::preset::mitred_by_default("rightArrow") && !deckcraft_geom::preset::mitred_by_default("leftArrow"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue