Fix 'Regular Polygon' and 'Star' shapes incorrectly having cubic handles, not linear (#3606)

Fix shape nodes creating cubic-curved polylines
This commit is contained in:
Keavon Chambers
2026-01-06 20:58:08 -08:00
committed by GitHub
parent bd847034f3
commit 1b91198b28
13 changed files with 40 additions and 49 deletions

View File

@@ -470,7 +470,7 @@ impl Render for Artboard {
fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option<NodeId>) {
if let Some(element_id) = element_id {
let subpath = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2());
let subpath = Subpath::new_rectangle(DVec2::ZERO, self.dimensions.as_dvec2());
metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]);
metadata.upstream_footprints.insert(element_id, footprint);
metadata.local_transforms.insert(element_id, DAffine2::from_translation(self.location.as_dvec2()));
@@ -483,7 +483,7 @@ impl Render for Artboard {
}
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
let subpath_rectangle = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2());
let subpath_rectangle = Subpath::new_rectangle(DVec2::ZERO, self.dimensions.as_dvec2());
click_targets.push(ClickTarget::new_with_subpath(subpath_rectangle, 0.));
}
@@ -1363,7 +1363,7 @@ impl Render for Table<Raster<CPU>> {
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
let Some(element_id) = element_id else { return };
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]);
metadata.upstream_footprints.insert(element_id, footprint);
@@ -1374,7 +1374,7 @@ impl Render for Table<Raster<CPU>> {
}
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
click_targets.push(ClickTarget::new_with_subpath(subpath, 0.));
}
}
@@ -1423,7 +1423,7 @@ impl Render for Table<Raster<GPU>> {
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
let Some(element_id) = element_id else { return };
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]);
metadata.upstream_footprints.insert(element_id, footprint);
@@ -1434,7 +1434,7 @@ impl Render for Table<Raster<GPU>> {
}
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
click_targets.push(ClickTarget::new_with_subpath(subpath, 0.));
}
}

View File

@@ -156,24 +156,19 @@ impl<PointId: Identifier> Subpath<PointId> {
.all(|manipulator_group| manipulator_group.anchor.abs_diff_eq(point, MAX_ABSOLUTE_DIFFERENCE))
}
/// Construct a [Subpath] from an iter of anchor positions.
pub fn from_anchors(anchor_positions: impl IntoIterator<Item = DVec2>, closed: bool) -> Self {
Self::new(anchor_positions.into_iter().map(|anchor| ManipulatorGroup::new_anchor(anchor)).collect(), closed)
}
pub fn from_anchors_linear(anchor_positions: impl IntoIterator<Item = DVec2>, closed: bool) -> Self {
Self::new(anchor_positions.into_iter().map(|anchor| ManipulatorGroup::new_anchor_linear(anchor)).collect(), closed)
}
/// Constructs a rectangle with `corner1` and `corner2` as the two corners.
pub fn new_rect(corner1: DVec2, corner2: DVec2) -> Self {
Self::from_anchors_linear([corner1, DVec2::new(corner2.x, corner1.y), corner2, DVec2::new(corner1.x, corner2.y)], true)
pub fn new_rectangle(corner1: DVec2, corner2: DVec2) -> Self {
Self::from_anchors([corner1, DVec2::new(corner2.x, corner1.y), corner2, DVec2::new(corner1.x, corner2.y)], true)
}
/// Constructs a rounded rectangle with `corner1` and `corner2` as the two corners and `corner_radii` as the radii of the corners: `[top_left, top_right, bottom_right, bottom_left]`.
pub fn new_rounded_rect(corner1: DVec2, corner2: DVec2, corner_radii: [f64; 4]) -> Self {
pub fn new_rounded_rectangle(corner1: DVec2, corner2: DVec2, corner_radii: [f64; 4]) -> Self {
if corner_radii.iter().all(|radii| radii.abs() < f64::EPSILON * 100.) {
return Self::new_rect(corner1, corner2);
return Self::new_rectangle(corner1, corner2);
}
use std::f64::consts::{FRAC_1_SQRT_2, PI};
@@ -185,7 +180,7 @@ impl<PointId: Identifier> Subpath<PointId> {
return vec![ManipulatorGroup::new_anchor(point1), ManipulatorGroup::new_anchor(point2)];
}
// Based on https://pomax.github.io/bezierinfo/#circles_cubic
// Constant from https://pomax.github.io/bezierinfo/#circles_cubic
const HANDLE_OFFSET_FACTOR: f64 = 0.551784777779014;
let handle_offset = radius * HANDLE_OFFSET_FACTOR;
vec![

View File

@@ -120,7 +120,7 @@ mod test_centroid {
use super::*;
#[test]
fn centroid_rect() {
let rect = Subpath::<PointId>::new_rect(DVec2::new(100., 100.), DVec2::new(300., 200.));
let rect = Subpath::<PointId>::new_rectangle(DVec2::new(100., 100.), DVec2::new(300., 200.));
let (centre, area) = rect.area_centroid_and_area(Some(1e-3), Some(1e-3)).unwrap();
assert_eq!(area, 200. * 100.);
assert_eq!(centre, DVec2::new(200., 150.))

View File

@@ -55,10 +55,6 @@ impl<PointId: Identifier> ManipulatorGroup<PointId> {
/// Construct a new manipulator point with just an anchor position
pub fn new_anchor(anchor: DVec2) -> Self {
Self::new(anchor, Some(anchor), Some(anchor))
}
pub fn new_anchor_linear(anchor: DVec2) -> Self {
Self::new(anchor, None, None)
}

View File

@@ -330,7 +330,7 @@ mod tests {
let mut cache = BoundingBoxCache::default();
// Create a simple rectangle subpath for testing
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::new(100.0, 50.0));
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::new(100.0, 50.0));
let rotation = PI / 4.0;
let scale = DVec2::new(2.0, 2.0);
@@ -353,7 +353,7 @@ mod tests {
#[test]
fn test_bounding_box_cache_ring_buffer_behavior() {
let mut cache = BoundingBoxCache::default();
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::new(10.0, 10.0));
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::new(10.0, 10.0));
let scale = DVec2::ONE;
let translation = DVec2::ZERO;
@@ -378,7 +378,7 @@ mod tests {
#[test]
fn test_click_target_bounding_box_caching() {
// Create a click target with a simple rectangle
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::new(100.0, 50.0));
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::new(100.0, 50.0));
let click_target = ClickTarget::new_with_subpath(subpath, 1.0);
let rotation = PI / 6.0;
@@ -415,7 +415,7 @@ mod tests {
#[test]
fn test_click_target_skew_bypass_cache() {
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::new(100.0, 50.0));
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::new(100.0, 50.0));
let click_target = ClickTarget::new_with_subpath(subpath.clone(), 1.0);
// Create a transform with skew (non-uniform scaling in different directions)
@@ -431,7 +431,7 @@ mod tests {
#[test]
fn test_cache_fingerprint_collision_handling() {
let mut cache = BoundingBoxCache::default();
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::new(10.0, 10.0));
let subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::new(10.0, 10.0));
let scale = DVec2::ONE;
let translation = DVec2::ZERO;

View File

@@ -654,7 +654,7 @@ mod tests {
#[test]
fn modify_new() {
let vector: Vector<()> = Vector::from_subpaths([Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE), Subpath::new_rect(DVec2::NEG_ONE, DVec2::ZERO)], false);
let vector: Vector<()> = Vector::from_subpaths([Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE), Subpath::new_rectangle(DVec2::NEG_ONE, DVec2::ZERO)], false);
let modify = VectorModification::create_from_vector(&vector);
@@ -667,7 +667,7 @@ mod tests {
fn modify_existing() {
let subpaths = [
Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE),
Subpath::new_rect(DVec2::NEG_ONE, DVec2::ZERO),
Subpath::new_rectangle(DVec2::NEG_ONE, DVec2::ZERO),
Subpath::from_beziers(
&[
PathSeg::Quad(QuadBez::new(Point::new(0., 0.), Point::new(5., 10.), Point::new(10., 0.))),