Skip to content

Commit 6a83ef2

Browse files
committed
rework
1 parent 5d5fa23 commit 6a83ef2

File tree

6 files changed

+269
-139
lines changed

6 files changed

+269
-139
lines changed

string-bean-cli/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ path = "src/main.rs"
1010
[dependencies]
1111
clap = { version = "4.3.19", features = ["derive"] }
1212
image = "0.24.6"
13-
string-bean = { path = "../string-bean" }
13+
string-bean = { path = "../string-bean" }
14+
line_drawing = "1.0.0"

string-bean-cli/src/main.rs

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,28 @@ use std::fs::File;
22
use std::io::Write;
33

44
use clap::Parser;
5-
use image;
6-
7-
use string_bean;
85

96
#[derive(Parser)]
107
struct CliArgs {
118
input_file: String,
129
output_file: String,
1310
#[clap(short = 'c', default_value_t = 500)]
14-
num_chords: u64,
11+
line_count: usize,
1512
#[clap(short = 'o', default_value_t = 0.2)]
1613
line_opacity: f64,
1714
#[arg(short = 'a', default_value_t = 288)]
18-
num_anchors: u64,
15+
anchor_count: u64,
1916
#[arg(short = 'g', default_value_t = 0)]
20-
num_anchor_gap: usize,
17+
anchor_gap_count: usize,
2118
#[arg(short = 'r', default_value_t = usize::MAX)]
2219
radius: usize,
2320
#[arg(short = 'p', default_value_t = 5.0)]
2421
penalty: f64,
2522

26-
#[arg(short = 'w', default_value_t = 850)]
27-
output_width: u64,
28-
#[arg(short = 'h', default_value_t = 850)]
29-
output_height: u64,
23+
#[arg(default_value_t = 850)]
24+
width: u64,
25+
#[arg(default_value_t = 850)]
26+
height: u64,
3027
}
3128

3229
fn main() -> Result<(), std::io::Error> {
@@ -36,39 +33,48 @@ fn main() -> Result<(), std::io::Error> {
3633
let width = img.width() as usize;
3734
let height = img.height() as usize;
3835

36+
let (x_mid, y_mid) = (width / 2, height / 2);
37+
let radius = args.radius.min(x_mid.min(y_mid)) as f64;
38+
39+
let anchors: Vec<_> = (0..args.anchor_count)
40+
.map(|anchor| anchor as f64 * 2.0 * std::f64::consts::PI / args.anchor_count as f64)
41+
.map(|angle| {
42+
(
43+
x_mid as f64 + radius * angle.cos(),
44+
y_mid as f64 * radius * angle.sin(),
45+
)
46+
})
47+
.collect();
48+
3949
let mut planner = string_bean::ThreadPlanner::new(
40-
args.num_chords,
4150
args.line_opacity,
42-
args.num_anchors,
43-
args.num_anchor_gap,
44-
args.radius,
51+
&anchors,
52+
args.anchor_gap_count,
4553
args.penalty,
54+
grid_raytrace,
4655
width,
4756
height,
48-
&mut img.into_vec(),
57+
&img.into_vec(),
4958
);
5059

51-
let anchors = planner.get_moves(0).unwrap();
60+
let anchors = planner.get_moves(0, args.line_count).unwrap();
5261

5362
write_svg(&args, &anchors)?;
5463

5564
Ok(())
5665
}
5766

58-
fn write_svg(args: &CliArgs, anchors: &Vec<usize>) -> Result<(), std::io::Error> {
59-
let (x_mid, y_mid) = (
60-
args.output_width as f64 / 2.0,
61-
args.output_height as f64 / 2.0,
62-
);
67+
fn write_svg(args: &CliArgs, anchors: &[usize]) -> Result<(), std::io::Error> {
68+
let (x_mid, y_mid) = (args.width as f64 / 2.0, args.height as f64 / 2.0);
6369
let radius = x_mid.min(y_mid);
64-
let degrees_per_anchor: f64 = 2.0 * std::f64::consts::PI / args.num_anchors as f64;
70+
let degrees_per_anchor: f64 = 2.0 * std::f64::consts::PI / args.anchor_count as f64;
6571

6672
let mut svg_file = File::create(&args.output_file)?;
6773

68-
write!(
74+
writeln!(
6975
svg_file,
70-
"<svg width=\"{}\" height=\"{}\" xmlns=\"http://www.w3.org/2000/svg\">\n",
71-
args.output_width, args.output_height
76+
"<svg width=\"{}\" height=\"{}\" xmlns=\"http://www.w3.org/2000/svg\">",
77+
args.width, args.height
7278
)?;
7379

7480
for anchor_pairs in anchors.windows(2) {
@@ -79,14 +85,52 @@ fn write_svg(args: &CliArgs, anchors: &Vec<usize>) -> Result<(), std::io::Error>
7985
);
8086
let (x0, y0) = (x_mid + radius * deg1.cos(), y_mid + radius * deg1.sin());
8187
let (x1, y1) = (x_mid + radius * deg2.cos(), y_mid + radius * deg2.sin());
82-
write!(
88+
writeln!(
8389
svg_file,
84-
"<line x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\" opacity=\"{}\" style=\"stroke:rgb(0,0,0); stroke-width:1\" />\n",
90+
"<line x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\" opacity=\"{}\" style=\"stroke:rgb(0,0,0); stroke-width:1\" />",
8591
x0, y0, x1, y1, args.line_opacity
8692
)?;
8793
}
8894

89-
write!(svg_file, "</svg>")?;
95+
writeln!(svg_file, "</svg>")?;
9096

9197
Ok(())
9298
}
99+
100+
/// https://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
101+
fn grid_raytrace(
102+
x0: f64,
103+
y0: f64,
104+
x1: f64,
105+
y1: f64,
106+
) -> impl Iterator<Item = ((usize, usize), f64)> {
107+
let (x0, y0) = (x0 as i64, y0 as i64);
108+
let (x1, y1) = (x1 as i64, y1 as i64);
109+
110+
let mut dx = (x1 - x0).abs();
111+
let mut dy = (y1 - y0).abs();
112+
let mut x = x0;
113+
let mut y = y0;
114+
115+
let n = 1 + dx + dy;
116+
let x_inc = (x1 - x0).signum();
117+
let y_inc = (y1 - y0).signum();
118+
119+
let mut error = dx - dy;
120+
dx *= 2;
121+
dy *= 2;
122+
123+
(0..n).map(move |_| {
124+
let point = ((x as usize, y as usize), 1.0);
125+
126+
if error > 0 {
127+
x += x_inc;
128+
error -= dy;
129+
} else {
130+
y += y_inc;
131+
error += dx;
132+
}
133+
134+
point
135+
})
136+
}

string-bean-wasm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ crate-type = ["cdylib"]
1111
[dependencies]
1212
string-bean = { path = "../string-bean" }
1313
wasm-bindgen = "0.2.87"
14+
line_drawing = "1.0.0"

string-bean-wasm/src/lib.rs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,44 @@
11
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
22

3+
use line_drawing::XiaolinWu;
4+
35
#[wasm_bindgen]
46
pub fn plan_as_json(
5-
num_chords: u32,
7+
line_count: u32,
68
line_opacity: f32,
7-
num_anchors: u32,
8-
num_anchors_gap: usize,
9+
anchor_count: u32,
10+
anchor_gap_count: usize,
911
radius: usize,
1012
penalty: f32,
1113
width: usize,
1214
height: usize,
1315
image_buffer: &[u8],
1416
start_anchor: usize,
1517
) -> JsValue {
18+
let anchors: Vec<_> = (0..anchor_count)
19+
.map(|anchor| anchor as f64 * 2.0 * std::f64::consts::PI / anchor_count as f64)
20+
.map(|angle| {
21+
(
22+
radius as f64 * (1.0 + angle.cos()),
23+
radius as f64 * (1.0 + angle.sin()),
24+
)
25+
})
26+
.collect();
27+
1628
let mut planner = string_bean::ThreadPlanner::new(
17-
num_chords as _,
1829
line_opacity as _,
19-
num_anchors as _,
20-
num_anchors_gap as _,
21-
radius,
30+
&anchors,
31+
anchor_gap_count,
2232
penalty as _,
33+
grid_raytrace,
2334
width,
2435
height,
2536
image_buffer,
2637
);
2738

28-
let moves = planner.get_moves(start_anchor).unwrap_or(Vec::new());
39+
let moves = planner
40+
.get_moves(start_anchor, line_count as _)
41+
.unwrap_or(Vec::new());
2942

3043
JsValue::from_str(&format!(
3144
"[{}]",
@@ -36,3 +49,40 @@ pub fn plan_as_json(
3649
.join(","),
3750
))
3851
}
52+
/// https://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
53+
fn grid_raytrace(
54+
x0: f64,
55+
y0: f64,
56+
x1: f64,
57+
y1: f64,
58+
) -> impl Iterator<Item = ((usize, usize), f64)> {
59+
let (x0, y0) = (x0 as i64, y0 as i64);
60+
let (x1, y1) = (x1 as i64, y1 as i64);
61+
62+
let mut dx = (x1 - x0).abs();
63+
let mut dy = (y1 - y0).abs();
64+
let mut x = x0;
65+
let mut y = y0;
66+
67+
let n = 1 + dx + dy;
68+
let x_inc = (x1 - x0).signum();
69+
let y_inc = (y1 - y0).signum();
70+
71+
let mut error = dx - dy;
72+
dx *= 2;
73+
dy *= 2;
74+
75+
(0..n).map(move |_| {
76+
let point = ((x as usize, y as usize), 1.0);
77+
78+
if error > 0 {
79+
x += x_inc;
80+
error -= dy;
81+
} else {
82+
y += y_inc;
83+
error += dx;
84+
}
85+
86+
point
87+
})
88+
}

string-bean/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
name = "string-bean"
33
version = "0.1.0"
44
edition = "2021"
5-

0 commit comments

Comments
 (0)