Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ struct Opt {
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Selectors>
#[arg(long)]
selector_filter: Option<String>,
/// Starting point , usefull only if try optimize path
#[arg(long)]
starting_point: Option<String>,
}

fn main() -> io::Result<()> {
Expand Down Expand Up @@ -168,6 +171,24 @@ fn main() -> io::Result<()> {
}
}
}
{
if let Some(starting_point) = opt.starting_point {
for (i, dimension_starting_point) in starting_point
.split(',')
.map(|point| {
if point.is_empty() {
Default::default()
} else {
point.parse::<f64>().expect("could not parse coordinate")
}
})
.take(2)
.enumerate()
{
settings.conversion.inner.starting_point[i] = Some(dimension_starting_point);
}
}
}

if let Some(line_numbers) = opt.line_numbers {
settings.postprocess.line_numbers = line_numbers;
Expand Down
11 changes: 10 additions & 1 deletion star/src/lower/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub struct ConversionConfig {
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Selectors>
#[cfg_attr(feature = "serde", serde(default))]
pub selector_filter: Option<String>,
pub starting_point: [Option<f64>; 2],
}

const fn zero_origin() -> [Option<f64>; 2] {
Expand All @@ -63,6 +64,7 @@ impl Default for ConversionConfig {
extra_attribute_name: None,
optimize_path_order: false,
selector_filter: None,
starting_point: zero_origin() ,
}
}
}
Expand Down Expand Up @@ -177,6 +179,11 @@ pub fn svg_to_turtle<T: Turtle>(
.origin
.map(|dim| dim.map(|d| UomLength::new::<millimeter>(d).get::<inch>() * CSS_DEFAULT_DPI));

// Convert from millimeters to user units
let starting_point = config
.starting_point
.map(|dim| dim.map(|d| UomLength::new::<millimeter>(d).get::<inch>() * CSS_DEFAULT_DPI));

let origin_transform = match origin {
[None, Some(origin_y)] => {
let bb = bounding_box_generator();
Expand Down Expand Up @@ -219,6 +226,7 @@ pub fn svg_to_turtle<T: Turtle>(
origin_transform,
selector_filter,
coordinate_system,
starting_point,
);
let turtle = &mut conversion_visitor.terrarium.turtle;
for stroke in strokes {
Expand All @@ -244,6 +252,7 @@ fn svg_to_optimized_strokes(
origin_transform: Transform2D<f64>,
selector_filter: Option<SelectorList>,
coordinate_system: CoordinateSystem,
starting_point: [Option<f64>; 2]
) -> Vec<Stroke> {
let mut collect_visitor = ConversionVisitor {
terrarium: Terrarium::new(StrokeCollectingTurtle::default()),
Expand All @@ -260,7 +269,7 @@ fn svg_to_optimized_strokes(
collect_visitor.end();
collect_visitor.terrarium.pop_transform();
let strokes = collect_visitor.terrarium.turtle.into_strokes();
minimize_travel_time(strokes)
minimize_travel_time(strokes,starting_point)
}

fn node_name(node: &Node, attr_to_print: &Option<String>) -> String {
Expand Down
31 changes: 13 additions & 18 deletions star/src/turtle/elements/tsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,22 @@ fn dist(a: Point<f64>, b: Point<f64>) -> f64 {
///
/// <https://github.com/sameer/raster2svg>
/// <https://www.mdpi.com/2076-3417/9/19/3985/pdf>
pub fn minimize_travel_time(strokes: Vec<Stroke>) -> Vec<Stroke> {
pub fn minimize_travel_time(strokes: Vec<Stroke>,starting_point: [Option<f64>; 2] ) -> Vec<Stroke> {
if strokes.len() <= 1 {
return strokes;
}
let path = nearest_neighbor_greedy(strokes);
local_improvement_with_tabu_search(&path)
let the_starting_point : Point<f64> = Point::new(starting_point[0].expect("No starting point Y"),starting_point[1].expect("No starting point Y"));

let path = nearest_neighbor_greedy(strokes,the_starting_point);
local_improvement_with_tabu_search(&path,the_starting_point)
}

/// Greedy nearest-neighbour ordering with flips.
///
/// Repeatedly chooses the [Stroke] or [Stroke::reversed] closest to the current point until none remain.
fn nearest_neighbor_greedy(mut remaining: Vec<Stroke>) -> Vec<Stroke> {
fn nearest_neighbor_greedy(mut remaining: Vec<Stroke>,the_starting_point: Point<f64> ) -> Vec<Stroke> {
let mut result = Vec::with_capacity(remaining.len());
// TODO: this assumption may be incorrect? depends on the GCode begin sequence, which this can't account for.
let mut pos = Point::zero();
let mut pos : Point<f64> = the_starting_point ;

while !remaining.is_empty() {
let mut best_idx = 0;
Expand Down Expand Up @@ -132,13 +133,13 @@ fn reverse_and_flip(strokes: &mut [Stroke]) {
/// - TwoOpt and LinkSwap reversals also flip each stroke in the reversed range.
/// - Relocate tries both the normal and reversed orientation of the moved stroke.
/// - Distances are `f64` Euclidean rather than squared integers.
fn local_improvement_with_tabu_search(path: &[Stroke]) -> Vec<Stroke> {
fn local_improvement_with_tabu_search(path: &[Stroke],the_starting_point: Point<f64> ) -> Vec<Stroke> {
let mut best = path.to_owned();
let mut best_sum: f64 = stroke_distances(&best).iter().sum();
let mut best_sum: f64 = stroke_distances(&best).iter().sum::<f64>() + dist(the_starting_point,best[0].start_point()) ;

let mut current = best.clone();
let mut current_distances = stroke_distances(&current);
let mut current_sum = best_sum;
let mut current_sum ;

const ITERATIONS: usize = 20000;
let mut rng = rand::rng();
Expand Down Expand Up @@ -282,8 +283,8 @@ fn local_improvement_with_tabu_search(path: &[Stroke]) -> Vec<Stroke> {
// 2 = [first_start, last_end]: both
let candidates = [
(0usize, dist(from, last_end)),
(1usize, dist(first_start, to)),
(2usize, dist(first_start, last_end)),
(1usize, dist(first_start, to) +dist(the_starting_point,to)-dist(the_starting_point,first_start)),
(2usize, dist(first_start, last_end)+dist(the_starting_point,last_end)-dist(the_starting_point,first_start)),
];
let (opt, best_new_dist) = candidates
.into_iter()
Expand Down Expand Up @@ -322,14 +323,8 @@ fn local_improvement_with_tabu_search(path: &[Stroke]) -> Vec<Stroke> {
}
}

let prev_sum = current_sum;
current_distances = stroke_distances(&current);
current_sum = current_distances.iter().sum::<f64>();

debug_assert!(
prev_sum > current_sum - f64::EPSILON,
"operator={operator:?} prev={prev_sum} current={current_sum}"
);
current_sum = current_distances.iter().sum::<f64>() +dist(the_starting_point,current[0].start_point()) ;

if current_sum < best_sum {
best = current.clone();
Expand Down
9 changes: 9 additions & 0 deletions web/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct FormState {
pub checksums: bool,
pub line_numbers: bool,
pub newline_before_comment: bool,
pub starting_point: [Option<Result<f64, ParseFloatError>>; 2],
}

impl Default for FormState {
Expand Down Expand Up @@ -56,6 +57,10 @@ impl TryInto<Settings> for &FormState {
extra_attribute_name: None,
optimize_path_order: self.optimize_path_order,
selector_filter: None,
starting_point: [
self.starting_point[0].clone().transpose()?,
self.starting_point[1].clone().transpose()?,
],
},
tolerance: self.tolerance.clone()?,
feedrate: self.feedrate.clone()?,
Expand Down Expand Up @@ -117,6 +122,10 @@ impl From<&Settings> for FormState {
checksums: settings.postprocess.checksums,
line_numbers: settings.postprocess.line_numbers,
newline_before_comment: settings.postprocess.newline_before_comment,
starting_point: [
settings.conversion.inner.starting_point[0].map(Ok),
settings.conversion.inner.starting_point[1].map(Ok),
],
}
}
}
Expand Down