Skip to content

Commit

Permalink
Remove trailing zeros in rendered SVG path output
Browse files Browse the repository at this point in the history
  • Loading branch information
Keavon committed Jan 13, 2025
1 parent 8dfdc2b commit 1e62af8
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 17 deletions.
5 changes: 4 additions & 1 deletion editor/src/messages/tool/tool_messages/path_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![
Expand Down
38 changes: 23 additions & 15 deletions libraries/bezier-rs/src/bezier/core.rs
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down Expand Up @@ -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<String> {
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.
Expand Down
5 changes: 4 additions & 1 deletion libraries/bezier-rs/src/subpath/core.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::*;
use crate::consts::*;
use crate::utils::format_point;

use glam::DVec2;
use std::fmt::Write;
Expand Down Expand Up @@ -163,8 +164,10 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
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(' ');
Expand Down
13 changes: 13 additions & 0 deletions libraries/bezier-rs/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -294,6 +295,18 @@ pub fn compute_circular_subpath_details<PointId: crate::Identifier>(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::*;
Expand Down

0 comments on commit 1e62af8

Please sign in to comment.