From 1e62af88cd746674ea879ef17cd9423ed94e0b6a Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 13 Jan 2025 00:01:48 -0800 Subject: [PATCH] Remove trailing zeros in rendered SVG path output --- .../messages/tool/tool_messages/path_tool.rs | 5 ++- libraries/bezier-rs/src/bezier/core.rs | 38 +++++++++++-------- libraries/bezier-rs/src/subpath/core.rs | 5 ++- libraries/bezier-rs/src/utils.rs | 13 +++++++ 4 files changed, 44 insertions(+), 17 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 3bb5cc76..ab582d5e 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -155,7 +155,10 @@ impl LayoutHolder for PathTool { }) .tooltip(colinear_handles_tooltip) .widget_holder(); - let colinear_handles_label = TextLabel::new("Colinear Handles").tooltip(colinear_handles_tooltip).widget_holder(); + let colinear_handles_label = TextLabel::new("Colinear Handles") + .disabled(self.tool_data.selection_status.is_none()) + .tooltip(colinear_handles_tooltip) + .widget_holder(); Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![ diff --git a/libraries/bezier-rs/src/bezier/core.rs b/libraries/bezier-rs/src/bezier/core.rs index 9124012a..7aeb07b4 100644 --- a/libraries/bezier-rs/src/bezier/core.rs +++ b/libraries/bezier-rs/src/bezier/core.rs @@ -1,4 +1,6 @@ use super::*; +use utils::format_point; + use std::fmt::Write; /// Functionality relating to core `Bezier` operations, such as constructors and `abs_diff_eq`. @@ -122,32 +124,38 @@ impl Bezier { pub fn write_curve_argument(&self, svg: &mut String) -> std::fmt::Result { match self.handles { BezierHandles::Linear => svg.push_str(SVG_ARG_LINEAR), - BezierHandles::Quadratic { handle } => write!(svg, "{SVG_ARG_QUADRATIC}{:.6},{:.6}", handle.x, handle.y)?, - BezierHandles::Cubic { handle_start, handle_end } => write!(svg, "{SVG_ARG_CUBIC}{:.6},{:.6} {:.6},{:.6}", handle_start.x, handle_start.y, handle_end.x, handle_end.y)?, + BezierHandles::Quadratic { handle } => { + format_point(svg, SVG_ARG_QUADRATIC, handle.x, handle.y)?; + } + BezierHandles::Cubic { handle_start, handle_end } => { + format_point(svg, SVG_ARG_CUBIC, handle_start.x, handle_start.y)?; + format_point(svg, " ", handle_end.x, handle_end.y)?; + } } - write!(svg, " {:.6},{:.6}", self.end.x, self.end.y) + format_point(svg, " ", self.end.x, self.end.y) } /// Return the string argument used to create the lines connecting handles to endpoints in an SVG `path` pub(crate) fn svg_handle_line_argument(&self) -> Option { + let mut result = String::new(); + match self.handles { - BezierHandles::Linear => None, + BezierHandles::Linear => {} BezierHandles::Quadratic { handle } => { - let handle_line = format!("{SVG_ARG_LINEAR}{:.6} {:.6}", handle.x, handle.y); - Some(format!( - "{SVG_ARG_MOVE}{:.6} {:.6} {handle_line} {SVG_ARG_MOVE}{:.6} {:.6} {handle_line}", - self.start.x, self.start.y, self.end.x, self.end.y - )) + let _ = format_point(&mut result, SVG_ARG_MOVE, self.start.x, self.start.y); + let _ = format_point(&mut result, SVG_ARG_LINEAR, handle.x, handle.y); + let _ = format_point(&mut result, SVG_ARG_MOVE, self.end.x, self.end.y); + let _ = format_point(&mut result, SVG_ARG_LINEAR, handle.x, handle.y); } BezierHandles::Cubic { handle_start, handle_end } => { - let handle_start_line = format!("{SVG_ARG_LINEAR}{:.6} {:.6}", handle_start.x, handle_start.y); - let handle_end_line = format!("{SVG_ARG_LINEAR}{} {}", handle_end.x, handle_end.y); - Some(format!( - "{SVG_ARG_MOVE}{:.6} {:.6} {handle_start_line} {SVG_ARG_MOVE}{:.6} {:.6} {handle_end_line}", - self.start.x, self.start.y, self.end.x, self.end.y - )) + let _ = format_point(&mut result, SVG_ARG_MOVE, self.start.x, self.start.y); + let _ = format_point(&mut result, SVG_ARG_LINEAR, handle_start.x, handle_start.y); + let _ = format_point(&mut result, SVG_ARG_MOVE, self.end.x, self.end.y); + let _ = format_point(&mut result, SVG_ARG_LINEAR, handle_end.x, handle_end.y); } } + + (!result.is_empty()).then_some(result) } /// Appends to the `svg` mutable string with an SVG shape representation of the curve. diff --git a/libraries/bezier-rs/src/subpath/core.rs b/libraries/bezier-rs/src/subpath/core.rs index 90f2818c..0164419a 100644 --- a/libraries/bezier-rs/src/subpath/core.rs +++ b/libraries/bezier-rs/src/subpath/core.rs @@ -1,5 +1,6 @@ use super::*; use crate::consts::*; +use crate::utils::format_point; use glam::DVec2; use std::fmt::Write; @@ -163,8 +164,10 @@ impl Subpath { if self.is_empty() { return Ok(()); } + let start = transform.transform_point2(self[0].anchor); - write!(svg, "{SVG_ARG_MOVE}{:.6},{:.6}", start.x, start.y)?; + format_point(svg, SVG_ARG_MOVE, start.x, start.y)?; + for bezier in self.iter() { bezier.apply_transformation(|pos| transform.transform_point2(pos)).write_curve_argument(svg)?; svg.push(' '); diff --git a/libraries/bezier-rs/src/utils.rs b/libraries/bezier-rs/src/utils.rs index 2cafd288..bccdb070 100644 --- a/libraries/bezier-rs/src/utils.rs +++ b/libraries/bezier-rs/src/utils.rs @@ -2,6 +2,7 @@ use crate::consts::{MAX_ABSOLUTE_DIFFERENCE, STRICT_MAX_ABSOLUTE_DIFFERENCE}; use crate::{ManipulatorGroup, Subpath}; use glam::{BVec2, DMat2, DVec2}; +use std::fmt::Write; #[derive(Copy, Clone, PartialEq)] /// A structure which can be used to reference a particular point along a `Bezier`. @@ -294,6 +295,18 @@ pub fn compute_circular_subpath_details(left: DVec2, ) } +pub fn format_point(svg: &mut String, prefix: &str, x: f64, y: f64) -> std::fmt::Result { + write!(svg, "{prefix}{:.6}", x)?; + let trimmed_length = svg.trim_end_matches('0').trim_end_matches('.').len(); + svg.truncate(trimmed_length); + + write!(svg, ",{:.6}", y)?; + let trimmed_length = svg.trim_end_matches('0').trim_end_matches('.').len(); + svg.truncate(trimmed_length); + + Ok(()) +} + #[cfg(test)] mod tests { use super::*;