add a viewbox to the svg that has the whole drawing in frame
This commit is contained in:
parent
e6bc79ea7b
commit
ea5bb85e88
@ -10,6 +10,7 @@ async fn main() {
|
||||
.right(90.0)
|
||||
.forward(100.0)
|
||||
.set_pen_color(macroquad::color::GRAY)
|
||||
.set_pen_width(8.0)
|
||||
.circle_right(50.0, 90.0, 4)
|
||||
.begin_fill()
|
||||
.forward(100.0)
|
||||
|
||||
@ -5,6 +5,7 @@ pub mod svg_export {
|
||||
use crate::commands::TurtleCommand;
|
||||
use crate::export::{DrawingExporter, ExportError};
|
||||
use crate::state::{DrawCommand, TurtleWorld};
|
||||
use macroquad::prelude::Vec2;
|
||||
use std::fs::File;
|
||||
use svg::{
|
||||
node::element::{Circle, Line, Polygon, Text as SvgText},
|
||||
@ -17,6 +18,25 @@ pub mod svg_export {
|
||||
fn export(&self, world: &TurtleWorld, filename: &str) -> Result<(), ExportError> {
|
||||
let mut doc = Document::new();
|
||||
|
||||
let mut min_x = f32::INFINITY;
|
||||
let mut max_x = f32::NEG_INFINITY;
|
||||
let mut min_y = f32::INFINITY;
|
||||
let mut max_y = f32::NEG_INFINITY;
|
||||
|
||||
fn update_bounds(
|
||||
min_x: &mut f32,
|
||||
max_x: &mut f32,
|
||||
min_y: &mut f32,
|
||||
max_y: &mut f32,
|
||||
x: f32,
|
||||
y: f32,
|
||||
) {
|
||||
*min_x = min_x.min(x);
|
||||
*max_x = max_x.max(x);
|
||||
*min_y = min_y.min(y);
|
||||
*max_y = max_y.max(y);
|
||||
}
|
||||
|
||||
for turtle in &world.turtles {
|
||||
for cmd in &turtle.commands {
|
||||
match cmd {
|
||||
@ -26,6 +46,14 @@ pub mod svg_export {
|
||||
// Linie als <line>
|
||||
let start = source.start_position;
|
||||
let end = source.end_position;
|
||||
update_bounds(
|
||||
&mut min_x, &mut max_x, &mut min_y, &mut max_y, start.x,
|
||||
start.y,
|
||||
);
|
||||
update_bounds(
|
||||
&mut min_x, &mut max_x, &mut min_y, &mut max_y, end.x,
|
||||
end.y,
|
||||
);
|
||||
let line = Line::new()
|
||||
.set("x1", start.x)
|
||||
.set("y1", start.y)
|
||||
@ -51,6 +79,22 @@ pub mod svg_export {
|
||||
let center = geom.center;
|
||||
if (*angle - 360.0).abs() < 1e-3 {
|
||||
// Voller Kreis
|
||||
update_bounds(
|
||||
&mut min_x,
|
||||
&mut max_x,
|
||||
&mut min_y,
|
||||
&mut max_y,
|
||||
center.x - radius,
|
||||
center.y - radius,
|
||||
);
|
||||
update_bounds(
|
||||
&mut min_x,
|
||||
&mut max_x,
|
||||
&mut min_y,
|
||||
&mut max_y,
|
||||
center.x + radius,
|
||||
center.y + radius,
|
||||
);
|
||||
let circle = Circle::new()
|
||||
.set("cx", center.x)
|
||||
.set("cy", center.y)
|
||||
@ -63,6 +107,23 @@ pub mod svg_export {
|
||||
// Kreisbogen als <path>
|
||||
let start = source.start_position;
|
||||
let end = source.end_position;
|
||||
// For arcs, include the full circle bounds to ensure complete visibility
|
||||
update_bounds(
|
||||
&mut min_x,
|
||||
&mut max_x,
|
||||
&mut min_y,
|
||||
&mut max_y,
|
||||
center.x - radius,
|
||||
center.y - radius,
|
||||
);
|
||||
update_bounds(
|
||||
&mut min_x,
|
||||
&mut max_x,
|
||||
&mut min_y,
|
||||
&mut max_y,
|
||||
center.x + radius,
|
||||
center.y + radius,
|
||||
);
|
||||
let large_arc = if *angle > 180.0 { 1 } else { 0 };
|
||||
let sweep = match direction {
|
||||
crate::circle_geometry::CircleDirection::Left => 0,
|
||||
@ -90,6 +151,14 @@ pub mod svg_export {
|
||||
TurtleCommand::EndFill => {
|
||||
// Fills werden als <path> mit Konturen ausgegeben
|
||||
if let Some(contours) = &source.contours {
|
||||
for contour in contours {
|
||||
for point in contour {
|
||||
update_bounds(
|
||||
&mut min_x, &mut max_x, &mut min_y, &mut max_y,
|
||||
point.x, point.y,
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut d = String::new();
|
||||
for (i, contour) in contours.iter().enumerate() {
|
||||
if !contour.is_empty() {
|
||||
@ -119,6 +188,30 @@ pub mod svg_export {
|
||||
}
|
||||
} else {
|
||||
// Fallback: Dummy-Polygon
|
||||
update_bounds(
|
||||
&mut min_x,
|
||||
&mut max_x,
|
||||
&mut min_y,
|
||||
&mut max_y,
|
||||
source.start_position.x,
|
||||
source.start_position.y,
|
||||
);
|
||||
update_bounds(
|
||||
&mut min_x,
|
||||
&mut max_x,
|
||||
&mut min_y,
|
||||
&mut max_y,
|
||||
source.start_position.x + 10.0,
|
||||
source.start_position.y + 10.0,
|
||||
);
|
||||
update_bounds(
|
||||
&mut min_x,
|
||||
&mut max_x,
|
||||
&mut min_y,
|
||||
&mut max_y,
|
||||
source.start_position.x + 5.0,
|
||||
source.start_position.y + 15.0,
|
||||
);
|
||||
let poly = Polygon::new()
|
||||
.set(
|
||||
"points",
|
||||
@ -146,6 +239,10 @@ pub mod svg_export {
|
||||
source,
|
||||
..
|
||||
} => {
|
||||
update_bounds(
|
||||
&mut min_x, &mut max_x, &mut min_y, &mut max_y, position.x,
|
||||
position.y,
|
||||
);
|
||||
let txt = SvgText::new()
|
||||
.set("x", position.x)
|
||||
.set("y", position.y)
|
||||
@ -157,6 +254,17 @@ pub mod svg_export {
|
||||
}
|
||||
}
|
||||
|
||||
// Set viewBox with 20px padding
|
||||
if min_x.is_finite() && max_x.is_finite() && min_y.is_finite() && max_y.is_finite() {
|
||||
let width = (max_x - min_x) + 40.0;
|
||||
let height = (max_y - min_y) + 40.0;
|
||||
let view_box = format!("{} {} {} {}", min_x - 20.0, min_y - 20.0, width, height);
|
||||
doc = doc.set("viewBox", view_box);
|
||||
} else {
|
||||
// Default viewBox if no elements
|
||||
doc = doc.set("viewBox", "0 0 400 400");
|
||||
}
|
||||
|
||||
let mut file = File::create(filename).map_err(ExportError::Io)?;
|
||||
svg::write(&mut file, &doc).map_err(ExportError::Io)?;
|
||||
Ok(())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user