From 056ec894d9933c206a0fca125812f59a35836eec Mon Sep 17 00:00:00 2001 From: antbern <40672068+antbern@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:13:19 +0200 Subject: [PATCH 1/4] replace the use of string keys and values with integers --- src/main.rs | 140 +++++++++++++++++++++++----------------------------- 1 file changed, 61 insertions(+), 79 deletions(-) diff --git a/src/main.rs b/src/main.rs index bc5b046..e78561f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4148,8 +4148,8 @@ fn xyz2contours( break; } - let mut obj = Vec::::new(); - let mut curves: HashMap = HashMap::default(); + let mut obj = Vec::<(i64, i64, u8)>::new(); + let mut curves: HashMap<(i64, i64, u8), (i64, i64)> = HashMap::default(); for i in 1..(w - 1) { for j in 2..(h - 1) { @@ -4304,78 +4304,58 @@ fn xyz2contours( .expect("Unable to create file"); let mut f = BufWriter::new(f); + println!("Objects: {}, hashmap items: {}", obj.len(), curves.len()); + for k in obj.iter() { if curves.contains_key(k) { - let separator = "_".to_string(); - let parts = k.split(&separator); - let r = parts.collect::>(); - let x: f64 = r[0].parse::().unwrap(); - let y: f64 = r[1].parse::().unwrap(); - write!(&mut f, "{},{};", x, y).expect("Cannot write to output file"); - let mut res = format!("{}_{}", x, y); + let (x, y, _) = *k; + write!(&mut f, "{},{};", x as f64 / 100.0, y as f64 / 100.0) + .expect("Cannot write to output file"); + let mut res = (x, y); - let parts = curves.get(&k.clone()).unwrap().split(&separator); - let r = parts.collect::>(); - let x: f64 = r[0].parse::().unwrap(); - let y: f64 = r[1].parse::().unwrap(); - write!(&mut f, "{},{};", x, y).expect("Cannot write to output file"); - curves.remove(&k.clone()); - - let mut head = format!("{}_{}", x, y); - if curves.get(&format!("{}_1", head)).unwrap_or(&String::new()) == &res { - curves.remove(&format!("{}_1", head)); + let (x, y) = *curves.get(&k).unwrap(); + write!(&mut f, "{},{};", x as f64 / 100.0, y as f64 / 100.0) + .expect("Cannot write to output file"); + curves.remove(&k); + + let mut head = (x, y); + + if curves.get(&(head.0, head.1, 1)).is_some_and(|v| *v == res) { + curves.remove(&(head.0, head.1, 1)); } - if curves.get(&format!("{}_2", head)).unwrap_or(&String::new()) == &res { - curves.remove(&format!("{}_2", head)); + if curves.get(&(head.0, head.1, 2)).is_some_and(|v| *v == res) { + curves.remove(&(head.0, head.1, 2)); } loop { - if curves.contains_key(&format!("{}_1", head)) - && curves.get(&format!("{}_1", head)).unwrap() != &res - { - res.clone_from(&head); - - let parts = curves - .get(&format!("{}_1", head)) - .unwrap() - .split(&separator); - let r = parts.collect::>(); - let x: f64 = r[0].parse::().unwrap(); - let y: f64 = r[1].parse::().unwrap(); - write!(&mut f, "{},{};", x, y).expect("Cannot write to output file"); - curves.remove(&format!("{}_1", head)); - - head = format!("{}_{}", x, y); - if curves.get(&format!("{}_1", head)).unwrap_or(&String::new()) == &res - { - curves.remove(&format!("{}_1", head)); + if curves.get(&(head.0, head.1, 1)).is_some_and(|v| *v != res) { + res = head; + + let (x, y) = *curves.get(&(head.0, head.1, 1)).unwrap(); + write!(&mut f, "{},{};", x as f64 / 100.0, y as f64 / 100.0) + .expect("Cannot write to output file"); + curves.remove(&(head.0, head.1, 1)); + + head = (x, y); + if curves.get(&(head.0, head.1, 1)).is_some_and(|v| *v == res) { + curves.remove(&(head.0, head.1, 1)); } - if curves.get(&format!("{}_2", head)).unwrap_or(&String::new()) == &res - { - curves.remove(&format!("{}_2", head)); + if curves.get(&(head.0, head.1, 2)).is_some_and(|v| *v == res) { + curves.remove(&(head.0, head.1, 2)); } - } else if curves.contains_key(&format!("{}_2", head)) - && curves.get(&format!("{}_2", head)).unwrap() != &res - { - res.clone_from(&head); - - let parts = curves - .get(&format!("{}_2", head)) - .unwrap() - .split(&separator); - let r = parts.collect::>(); - let x: f64 = r[0].parse::().unwrap(); - let y: f64 = r[1].parse::().unwrap(); - write!(&mut f, "{},{};", x, y).expect("Cannot write to output file"); - curves.remove(&format!("{}_2", head)); - - head = format!("{}_{}", x, y); - if curves.get(&format!("{}_1", head)).unwrap_or(&String::new()) == &res - { - curves.remove(&format!("{}_1", head)); + } else if curves.get(&(head.0, head.1, 2)).is_some_and(|v| *v != res) { + res = head; + + let (x, y) = *curves.get(&(head.0, head.1, 2)).unwrap(); + write!(&mut f, "{},{};", x as f64 / 100.0, y as f64 / 100.0) + .expect("Cannot write to output file"); + curves.remove(&(head.0, head.1, 2)); + + head = (x, y); + if curves.get(&(head.0, head.1, 1)).is_some_and(|v| *v == res) { + curves.remove(&(head.0, head.1, 1)); } - if curves.get(&format!("{}_2", head)).unwrap_or(&String::new()) == &res - { - curves.remove(&format!("{}_2", head)); + if curves.get(&(head.0, head.1, 2)).is_some_and(|v| *v == res) { + curves.remove(&(head.0, head.1, 2)); } } else { f.write_all("\r\n".as_bytes()) @@ -4471,34 +4451,36 @@ fn average(numbers: &Vec) -> f64 { } fn check_obj_in( - obj: &mut Vec, - curves: &mut HashMap, + obj: &mut Vec<(i64, i64, u8)>, + curves: &mut HashMap<(i64, i64, u8), (i64, i64)>, x1: f64, x2: f64, y1: f64, y2: f64, ) { - let x1 = (x1 * 100.0).floor() / 100.0; - let x2 = (x2 * 100.0).floor() / 100.0; - let y1 = (y1 * 100.0).floor() / 100.0; - let y2 = (y2 * 100.0).floor() / 100.0; + // convert the coordinates to integers with 2 decimal places for use as keys + let x1 = (x1 * 100.0).floor() as i64; + let x2 = (x2 * 100.0).floor() as i64; + let y1 = (y1 * 100.0).floor() as i64; + let y2 = (y2 * 100.0).floor() as i64; + if x1 != x2 || y1 != y2 { - let mut key: String = format!("{}_{}_1", x1, y1); + let key = (x1, y1, 1); if !curves.contains_key(&key) { - curves.insert(key.clone(), format!("{}_{}", x2, y2)); + curves.insert(key, (x2, y2)); obj.push(key.clone()); } else { - key = format!("{}_{}_2", x1, y1); - curves.insert(key.clone(), format!("{}_{}", x2, y2)); + let key = (x1, y1, 2); + curves.insert(key.clone(), (x2, y2)); obj.push(key.clone()); } - key = format!("{}_{}_1", x2, y2); + let key = (x2, y2, 1); if !curves.contains_key(&key) { - curves.insert(key.clone(), format!("{}_{}", x1, y1)); + curves.insert(key.clone(), (x1, y1)); obj.push(key.clone()); } else { - key = format!("{}_{}_2", x2, y2); - curves.insert(key.clone(), format!("{}_{}", x1, y1)); + let key = (x2, y2, 2); + curves.insert(key.clone(), (x1, y1)); obj.push(key.clone()); } } From 8a5b7de54629e3ead8a7fb6b6c92aa38433c64ed Mon Sep 17 00:00:00 2001 From: antbern <40672068+antbern@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:51:26 +0200 Subject: [PATCH 2/4] improve file reading and writing for xyz2contours --- src/main.rs | 73 ++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/src/main.rs b/src/main.rs index e78561f..a87d8ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use std::env; use std::error::Error; use std::f32::consts::SQRT_2; use std::f64::consts::PI; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, File}; use std::io::{self, BufRead, BufWriter, Write}; use std::path::{Path, PathBuf}; use std::{thread, time}; @@ -4141,7 +4141,6 @@ fn xyz2contours( let f = File::create(polyline_out).expect("Unable to create file"); let mut f = BufWriter::new(f); - f.write_all(b"").expect("Unable to create file"); loop { if level >= hmax { @@ -4298,14 +4297,6 @@ fn xyz2contours( } } - let f = OpenOptions::new() - .append(true) - .open(polyline_out) - .expect("Unable to create file"); - let mut f = BufWriter::new(f); - - println!("Objects: {}, hashmap items: {}", obj.len(), curves.len()); - for k in obj.iter() { if curves.contains_key(k) { let (x, y, _) = *k; @@ -4365,9 +4356,12 @@ fn xyz2contours( } } } - f.flush().expect("Cannot flush"); level += v; } + // explicitly flush and drop to close the file + f.flush().expect("Cannot flush"); + drop(f); + let f = File::create(Path::new(&format!("{}/{}", tmpfolder, dxffile))) .expect("Unable to create file"); let mut f = BufWriter::new(f); @@ -4378,39 +4372,38 @@ fn xyz2contours( xmin, ymin, xmax, ymax, ).expect("Cannot write dxf file"); - if let Ok(lines) = read_lines(polyline_out) { - for line in lines { - let ip = line.unwrap_or(String::new()); - let parts = ip.split(';'); - let r = parts.collect::>(); - f.write_all("POLYLINE\r\n 66\r\n1\r\n 8\r\ncont\r\n 0\r\n".as_bytes()) - .expect("Cannot write dxf file"); - for (i, d) in r.iter().enumerate() { - if d != &"" { - let ii = i + 1; - let ldata = r.len() - 2; - if ii > 5 && ii < ldata - 5 && ldata > 12 && ii % 2 == 0 { - continue; - } - let xy_raw = d.split(','); - let xy = xy_raw.collect::>(); - let x: f64 = xy[0].parse::().unwrap() * 2.0 * scalefactor + xmin; - let y: f64 = xy[1].parse::().unwrap() * 2.0 * scalefactor + ymin; - write!( - &mut f, - "VERTEX\r\n 8\r\ncont\r\n 10\r\n{}\r\n 20\r\n{}\r\n 0\r\n", - x, y - ) - .expect("Cannot write dxf file"); + read_lines_no_alloc(polyline_out, |line| { + let parts = line.trim().split(';'); + let r = parts.collect::>(); + f.write_all("POLYLINE\r\n 66\r\n1\r\n 8\r\ncont\r\n 0\r\n".as_bytes()) + .expect("Cannot write dxf file"); + for (i, d) in r.iter().enumerate() { + if d != &"" { + let ii = i + 1; + let ldata = r.len() - 2; + if ii > 5 && ii < ldata - 5 && ldata > 12 && ii % 2 == 0 { + continue; } - } - f.write_all("SEQEND\r\n 0\r\n".as_bytes()) + let mut xy_raw = d.split(','); + let x: f64 = + xy_raw.next().unwrap().parse::().unwrap() * 2.0 * scalefactor + xmin; + let y: f64 = + xy_raw.next().unwrap().parse::().unwrap() * 2.0 * scalefactor + ymin; + write!( + &mut f, + "VERTEX\r\n 8\r\ncont\r\n 10\r\n{}\r\n 20\r\n{}\r\n 0\r\n", + x, y + ) .expect("Cannot write dxf file"); + } } - f.write_all("ENDSEC\r\n 0\r\nEOF\r\n".as_bytes()) + f.write_all("SEQEND\r\n 0\r\n".as_bytes()) .expect("Cannot write dxf file"); - println!("Done"); - } + }) + .expect("Cannot read file"); + f.write_all("ENDSEC\r\n 0\r\nEOF\r\n".as_bytes()) + .expect("Cannot write dxf file"); + println!("Done"); } Ok(()) } From 1543916b2623c27e0c101178a966f8a5427bd4d3 Mon Sep 17 00:00:00 2001 From: antbern <40672068+antbern@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:53:38 +0200 Subject: [PATCH 3/4] remove uneccessary clones --- src/main.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index a87d8ae..56bb8ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4461,20 +4461,20 @@ fn check_obj_in( let key = (x1, y1, 1); if !curves.contains_key(&key) { curves.insert(key, (x2, y2)); - obj.push(key.clone()); + obj.push(key); } else { let key = (x1, y1, 2); - curves.insert(key.clone(), (x2, y2)); - obj.push(key.clone()); + curves.insert(key, (x2, y2)); + obj.push(key); } let key = (x2, y2, 1); if !curves.contains_key(&key) { - curves.insert(key.clone(), (x1, y1)); - obj.push(key.clone()); + curves.insert(key, (x1, y1)); + obj.push(key); } else { let key = (x2, y2, 2); - curves.insert(key.clone(), (x1, y1)); - obj.push(key.clone()); + curves.insert(key, (x1, y1)); + obj.push(key); } } } From 5e93e693596188a9b5f1b5335974c0d6996a4aef Mon Sep 17 00:00:00 2001 From: antbern <40672068+antbern@users.noreply.github.com> Date: Sun, 2 Jun 2024 20:22:27 +0200 Subject: [PATCH 4/4] compute average by keeping track of sum and count instead of storing all points in a list --- src/main.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index 56bb8ad..421de0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3977,7 +3977,8 @@ fn xyz2contours( let w: usize = ((xmax - xmin).ceil() / 2.0 / scalefactor) as usize; let h: usize = ((ymax - ymin).ceil() / 2.0 / scalefactor) as usize; - let mut list_alt = vec![vec![Vec::new(); h + 2]; w + 2]; + // a two-dimensional vector of (sum, count) pairs for computing averages + let mut list_alt = vec![vec![(0f64, 0usize); h + 2]; w + 2]; read_lines_no_alloc(xyz_file_in, |line| { let mut parts = line.trim().split(' '); @@ -3992,9 +3993,10 @@ fn xyz2contours( let y: f64 = p1.parse::().unwrap(); let h: f64 = p2.parse::().unwrap(); - list_alt[((x - xmin).floor() / 2.0 / scalefactor) as usize] - [((y - ymin).floor() / 2.0 / scalefactor) as usize] - .push(h); + let (sum, count) = &mut list_alt[((x - xmin).floor() / 2.0 / scalefactor) as usize] + [((y - ymin).floor() / 2.0 / scalefactor) as usize]; + *sum += h; + *count += 1; } }) .expect("could not read file"); @@ -4003,8 +4005,10 @@ fn xyz2contours( for x in 0..w + 1 { for y in 0..h + 1 { - if !list_alt[x][y].is_empty() { - avg_alt[x][y] = average(&list_alt[x][y]); + let (sum, count) = &list_alt[x][y]; + + if *count > 0 { + avg_alt[x][y] = *sum / *count as f64; } } } @@ -4359,7 +4363,6 @@ fn xyz2contours( level += v; } // explicitly flush and drop to close the file - f.flush().expect("Cannot flush"); drop(f); let f = File::create(Path::new(&format!("{}/{}", tmpfolder, dxffile))) @@ -4419,7 +4422,7 @@ where /// Iterates over the lines in a file and calls the callback with a &str reference to each line. /// This function does not allocate new strings for each line, as opposed to using /// [`io::BufReader::lines()`]. -fn read_lines_no_alloc

(filename: P, mut line_callback: impl FnMut(&str) -> ()) -> io::Result<()> +fn read_lines_no_alloc

(filename: P, mut line_callback: impl FnMut(&str)) -> io::Result<()> where P: AsRef, { @@ -4435,14 +4438,6 @@ where Ok(()) } -fn average(numbers: &Vec) -> f64 { - let mut sum = 0.0; - for n in numbers { - sum += n; - } - sum / numbers.len() as f64 -} - fn check_obj_in( obj: &mut Vec<(i64, i64, u8)>, curves: &mut HashMap<(i64, i64, u8), (i64, i64)>,