Line caps on exported SVG lines and arcs are now configured to be round
(stroke-linecap="round"), matching the on-screen Lyon tessellation.
Summary of Changes
turtle-lib/src/export_svg.rs:
Added .set("stroke-linecap", "round") to SVG <line> elements.
Added .set("stroke-linecap", "round") to partial arc <path> elements.
Extracted SvgExporter::to_svg_document(&TurtleWorld) -> Document to
allow in-memory SVG inspection and testing.
Added unit tests verifying that both lines and arcs export with
stroke-linecap="round".
turtle-lib/src/state.rs:
Added #[allow(clippy::unused_self)] to SvgLog::clear for clean clippy
passes when compiling without the svg feature.
This commit is contained in:
@@ -9,11 +9,11 @@ fn draw(turtle: &mut TurtlePlan) {
|
||||
turtle.set_pen_width(0.5);
|
||||
|
||||
// Draw a 5-pointed star pattern repeatedly
|
||||
for _i in 0..50000 {
|
||||
for _i in 0..500 {
|
||||
turtle.forward(200.0);
|
||||
turtle.circle_left(10.0, 72.0, 1000);
|
||||
turtle.circle_right(5.0, 360.0, 1000);
|
||||
turtle.circle_left(10.0, 72.0, 1000);
|
||||
turtle.circle_left(10.0, 72.0, 100);
|
||||
turtle.circle_right(5.0, 360.0, 100);
|
||||
turtle.circle_left(10.0, 72.0, 100);
|
||||
}
|
||||
|
||||
// Set animation speed
|
||||
|
||||
@@ -27,9 +27,11 @@ pub mod svg_export {
|
||||
|
||||
pub struct SvgExporter;
|
||||
|
||||
impl DrawingExporter for SvgExporter {
|
||||
impl SvgExporter {
|
||||
/// Generate an SVG [`Document`] from the given [`TurtleWorld`].
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn export(&self, world: &TurtleWorld, filename: &str) -> Result<(), ExportError> {
|
||||
pub fn to_svg_document(world: &TurtleWorld) -> Document {
|
||||
let mut doc = Document::new();
|
||||
|
||||
let mut min_x = f32::INFINITY;
|
||||
@@ -58,7 +60,8 @@ pub mod svg_export {
|
||||
.set("x2", end.x)
|
||||
.set("y2", end.y)
|
||||
.set("stroke", color_to_svg(*color))
|
||||
.set("stroke-width", *pen_width);
|
||||
.set("stroke-width", *pen_width)
|
||||
.set("stroke-linecap", "round");
|
||||
doc = doc.add(line);
|
||||
}
|
||||
|
||||
@@ -132,6 +135,7 @@ pub mod svg_export {
|
||||
.set("d", d)
|
||||
.set("stroke", color_to_svg(*color))
|
||||
.set("stroke-width", *pen_width)
|
||||
.set("stroke-linecap", "round")
|
||||
.set("fill", "none");
|
||||
doc = doc.add(path);
|
||||
}
|
||||
@@ -218,6 +222,13 @@ pub mod svg_export {
|
||||
doc = doc.set("viewBox", "0 0 400 400");
|
||||
}
|
||||
|
||||
doc
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawingExporter for SvgExporter {
|
||||
fn export(&self, world: &TurtleWorld, filename: &str) -> Result<(), ExportError> {
|
||||
let doc = Self::to_svg_document(world);
|
||||
let mut file = File::create(filename).map_err(ExportError::Io)?;
|
||||
svg::write(&mut file, &doc).map_err(ExportError::Io)?;
|
||||
Ok(())
|
||||
@@ -234,4 +245,55 @@ pub mod svg_export {
|
||||
format!("rgb({r},{g},{b})")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::circle_geometry::CircleDirection;
|
||||
use crate::general::{Color, Coordinate, Degrees};
|
||||
use crate::state::{SvgRecord, Turtle};
|
||||
|
||||
#[test]
|
||||
fn test_svg_export_line_has_round_line_caps() {
|
||||
let mut world = TurtleWorld::new();
|
||||
let mut turtle = Turtle::default();
|
||||
turtle.svg_log.push(SvgRecord::Line {
|
||||
start: Coordinate::new(0.0, 0.0),
|
||||
end: Coordinate::new(100.0, 0.0),
|
||||
color: Color::new(0.0, 0.0, 0.0, 1.0),
|
||||
pen_width: 2.0,
|
||||
});
|
||||
world.turtles.push(turtle);
|
||||
|
||||
let doc = SvgExporter::to_svg_document(&world);
|
||||
let svg_string = doc.to_string();
|
||||
assert!(
|
||||
svg_string.contains(r#"stroke-linecap="round""#),
|
||||
"SVG export of lines should have round line caps: {svg_string}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_svg_export_arc_has_round_line_caps() {
|
||||
let mut world = TurtleWorld::new();
|
||||
let mut turtle = Turtle::default();
|
||||
turtle.svg_log.push(SvgRecord::Arc {
|
||||
start_position: Coordinate::new(0.0, 0.0),
|
||||
start_heading: 0.0,
|
||||
radius: 50.0,
|
||||
angle: Degrees::new(90.0),
|
||||
direction: CircleDirection::Right,
|
||||
color: Color::new(0.0, 0.0, 0.0, 1.0),
|
||||
pen_width: 2.0,
|
||||
});
|
||||
world.turtles.push(turtle);
|
||||
|
||||
let doc = SvgExporter::to_svg_document(&world);
|
||||
let svg_string = doc.to_string();
|
||||
assert!(
|
||||
svg_string.contains(r#"stroke-linecap="round""#),
|
||||
"SVG export of partial arcs should have round line caps: {svg_string}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ pub(crate) struct SvgLog {
|
||||
}
|
||||
|
||||
impl SvgLog {
|
||||
#[allow(clippy::unused_self)]
|
||||
pub(crate) fn clear(&mut self) {
|
||||
#[cfg(feature = "svg")]
|
||||
self.records.clear();
|
||||
|
||||
Reference in New Issue
Block a user