now svg lines, circles and fills are exported
This commit is contained in:
parent
6e6aa8b27e
commit
e6bc79ea7b
@ -9,11 +9,31 @@ async fn main() {
|
|||||||
plan.forward(100.0)
|
plan.forward(100.0)
|
||||||
.right(90.0)
|
.right(90.0)
|
||||||
.forward(100.0)
|
.forward(100.0)
|
||||||
.circle_left(50.0, 90.0, 4)
|
.set_pen_color(macroquad::color::GRAY)
|
||||||
|
.circle_right(50.0, 90.0, 4)
|
||||||
.begin_fill()
|
.begin_fill()
|
||||||
.forward(100.0)
|
.forward(100.0)
|
||||||
.right(90.0)
|
.right(90.0)
|
||||||
.forward(200.0)
|
.forward(200.0)
|
||||||
|
.end_fill()
|
||||||
|
.circle_left(200., 180., 24)
|
||||||
|
.circle_left(90.0, 180.0, 36)
|
||||||
|
.begin_fill()
|
||||||
|
.circle_left(90.0, 180.0, 36)
|
||||||
|
.circle_left(45.0, 180.0, 26)
|
||||||
|
.circle_right(45.0, 180.0, 26)
|
||||||
|
.pen_up()
|
||||||
|
.right(90.0)
|
||||||
|
.forward(37.0)
|
||||||
|
.left(90.0)
|
||||||
|
.pen_down()
|
||||||
|
.circle_right(8.0, 360.0, 12)
|
||||||
|
.pen_up()
|
||||||
|
.right(90.0)
|
||||||
|
.forward(90.0)
|
||||||
|
.left(90.0)
|
||||||
|
.pen_down()
|
||||||
|
.circle_right(8.0, 360.0, 12)
|
||||||
.end_fill();
|
.end_fill();
|
||||||
let mut app = TurtleApp::new().with_commands(plan.build());
|
let mut app = TurtleApp::new().with_commands(plan.build());
|
||||||
use macroquad::{
|
use macroquad::{
|
||||||
|
|||||||
@ -72,6 +72,8 @@ pub fn execute_command_side_effects(command: &TurtleCommand, state: &mut Turtle)
|
|||||||
pen_width: state.params.pen_width,
|
pen_width: state.params.pen_width,
|
||||||
start_position: fill_state.start_position,
|
start_position: fill_state.start_position,
|
||||||
end_position: fill_state.start_position,
|
end_position: fill_state.start_position,
|
||||||
|
start_heading: state.params.heading,
|
||||||
|
contours: Some(fill_state.contours.clone()),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -133,6 +135,8 @@ pub fn execute_command_side_effects(command: &TurtleCommand, state: &mut Turtle)
|
|||||||
pen_width: state.params.pen_width,
|
pen_width: state.params.pen_width,
|
||||||
start_position: state.params.position,
|
start_position: state.params.position,
|
||||||
end_position: state.params.position,
|
end_position: state.params.position,
|
||||||
|
start_heading: state.params.heading,
|
||||||
|
contours: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
@ -230,6 +234,8 @@ pub fn execute_command(command: &TurtleCommand, state: &mut Turtle) {
|
|||||||
pen_width: state.params.pen_width,
|
pen_width: state.params.pen_width,
|
||||||
start_position: start,
|
start_position: start,
|
||||||
end_position: state.params.position,
|
end_position: state.params.position,
|
||||||
|
start_heading: state.params.heading,
|
||||||
|
contours: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -270,7 +276,9 @@ pub fn execute_command(command: &TurtleCommand, state: &mut Turtle) {
|
|||||||
fill_color: state.params.fill_color.unwrap_or(BLACK),
|
fill_color: state.params.fill_color.unwrap_or(BLACK),
|
||||||
pen_width: state.params.pen_width,
|
pen_width: state.params.pen_width,
|
||||||
start_position: state.params.position,
|
start_position: state.params.position,
|
||||||
end_position: state.params.position,
|
end_position: geom.position_at_angle(angle.to_radians()),
|
||||||
|
start_heading: start_heading,
|
||||||
|
contours: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -306,6 +314,8 @@ pub fn execute_command(command: &TurtleCommand, state: &mut Turtle) {
|
|||||||
pen_width: state.params.pen_width,
|
pen_width: state.params.pen_width,
|
||||||
start_position: start,
|
start_position: start,
|
||||||
end_position: state.params.position,
|
end_position: state.params.position,
|
||||||
|
start_heading: state.params.heading,
|
||||||
|
contours: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -371,6 +381,8 @@ pub fn add_draw_for_completed_tween(
|
|||||||
pen_width: start_state.pen_width,
|
pen_width: start_state.pen_width,
|
||||||
start_position: start_state.position,
|
start_position: start_state.position,
|
||||||
end_position: end_state.position,
|
end_position: end_state.position,
|
||||||
|
start_heading: start_state.heading,
|
||||||
|
contours: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -408,6 +420,8 @@ pub fn add_draw_for_completed_tween(
|
|||||||
pen_width: start_state.pen_width,
|
pen_width: start_state.pen_width,
|
||||||
start_position: start_state.position,
|
start_position: start_state.position,
|
||||||
end_position: end_state.position,
|
end_position: end_state.position,
|
||||||
|
start_heading: start_state.heading,
|
||||||
|
contours: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
pub mod svg_export {
|
pub mod svg_export {
|
||||||
use crate::commands::TurtleCommand;
|
use crate::commands::TurtleCommand;
|
||||||
use crate::export::{DrawingExporter, ExportError};
|
use crate::export::{DrawingExporter, ExportError};
|
||||||
use crate::state::{DrawCommand, TurtleSource, TurtleWorld};
|
use crate::state::{DrawCommand, TurtleWorld};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use svg::{
|
use svg::{
|
||||||
node::element::{Circle, Line, Polygon, Text as SvgText},
|
node::element::{Circle, Line, Polygon, Text as SvgText},
|
||||||
@ -35,9 +35,22 @@ pub mod svg_export {
|
|||||||
.set("stroke-width", source.pen_width);
|
.set("stroke-width", source.pen_width);
|
||||||
doc = doc.add(line);
|
doc = doc.add(line);
|
||||||
}
|
}
|
||||||
TurtleCommand::Circle { radius, .. } => {
|
TurtleCommand::Circle {
|
||||||
// Kreis als <circle>
|
radius,
|
||||||
let center = source.start_position;
|
angle,
|
||||||
|
direction,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
use crate::circle_geometry::CircleGeometry;
|
||||||
|
let geom = CircleGeometry::new(
|
||||||
|
source.start_position,
|
||||||
|
source.start_heading,
|
||||||
|
*radius,
|
||||||
|
*direction,
|
||||||
|
);
|
||||||
|
let center = geom.center;
|
||||||
|
if (*angle - 360.0).abs() < 1e-3 {
|
||||||
|
// Voller Kreis
|
||||||
let circle = Circle::new()
|
let circle = Circle::new()
|
||||||
.set("cx", center.x)
|
.set("cx", center.x)
|
||||||
.set("cy", center.y)
|
.set("cy", center.y)
|
||||||
@ -46,11 +59,66 @@ pub mod svg_export {
|
|||||||
.set("stroke-width", source.pen_width)
|
.set("stroke-width", source.pen_width)
|
||||||
.set("fill", "none");
|
.set("fill", "none");
|
||||||
doc = doc.add(circle);
|
doc = doc.add(circle);
|
||||||
|
} else {
|
||||||
|
// Kreisbogen als <path>
|
||||||
|
let start = source.start_position;
|
||||||
|
let end = source.end_position;
|
||||||
|
let large_arc = if *angle > 180.0 { 1 } else { 0 };
|
||||||
|
let sweep = match direction {
|
||||||
|
crate::circle_geometry::CircleDirection::Left => 0,
|
||||||
|
crate::circle_geometry::CircleDirection::Right => 1,
|
||||||
|
};
|
||||||
|
let d = format!(
|
||||||
|
"M {} {} A {} {} 0 {} {} {} {}",
|
||||||
|
start.x,
|
||||||
|
start.y,
|
||||||
|
radius,
|
||||||
|
radius,
|
||||||
|
large_arc,
|
||||||
|
sweep,
|
||||||
|
end.x,
|
||||||
|
end.y
|
||||||
|
);
|
||||||
|
let path = svg::node::element::Path::new()
|
||||||
|
.set("d", d)
|
||||||
|
.set("stroke", color_to_svg(source.color))
|
||||||
|
.set("stroke-width", source.pen_width)
|
||||||
|
.set("fill", "none");
|
||||||
|
doc = doc.add(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TurtleCommand::EndFill => {
|
TurtleCommand::EndFill => {
|
||||||
// Fills werden als Polygon ausgegeben
|
// Fills werden als <path> mit Konturen ausgegeben
|
||||||
// (Vereinfachung: Startposition als Dummy, echte Konturen müssten separat gespeichert werden)
|
if let Some(contours) = &source.contours {
|
||||||
// Hier nur ein Dummy-Polygon
|
let mut d = String::new();
|
||||||
|
for (i, contour) in contours.iter().enumerate() {
|
||||||
|
if !contour.is_empty() {
|
||||||
|
if i > 0 {
|
||||||
|
d.push(' ');
|
||||||
|
}
|
||||||
|
d.push_str(&format!(
|
||||||
|
"M {} {}",
|
||||||
|
contour[0].x, contour[0].y
|
||||||
|
));
|
||||||
|
for point in contour.iter().skip(1) {
|
||||||
|
d.push_str(&format!(
|
||||||
|
" L {} {}",
|
||||||
|
point.x, point.y
|
||||||
|
));
|
||||||
|
}
|
||||||
|
d.push_str(" Z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !d.is_empty() {
|
||||||
|
let path = svg::node::element::Path::new()
|
||||||
|
.set("d", d)
|
||||||
|
.set("fill", color_to_svg(source.fill_color))
|
||||||
|
.set("fill-rule", "evenodd")
|
||||||
|
.set("stroke", color_to_svg(source.color));
|
||||||
|
doc = doc.add(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: Dummy-Polygon
|
||||||
let poly = Polygon::new()
|
let poly = Polygon::new()
|
||||||
.set(
|
.set(
|
||||||
"points",
|
"points",
|
||||||
@ -68,6 +136,7 @@ pub mod svg_export {
|
|||||||
.set("stroke", color_to_svg(source.color));
|
.set("stroke", color_to_svg(source.color));
|
||||||
doc = doc.add(poly);
|
doc = doc.add(poly);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -285,7 +285,8 @@ pub struct TurtleSource {
|
|||||||
pub pen_width: f32,
|
pub pen_width: f32,
|
||||||
pub start_position: Vec2,
|
pub start_position: Vec2,
|
||||||
pub end_position: Vec2,
|
pub end_position: Vec2,
|
||||||
// ggf. weitere Metadaten
|
pub start_heading: f32,
|
||||||
|
pub contours: Option<Vec<Vec<crate::general::Coordinate>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user