svgdoc/
svgdoc.rs

1/*!
2Generates the SVG for the documentation and serves as a simple example.
3*/
4
5#![allow(dead_code)]
6
7use cvmath::*;
8
9mod svg;
10use self::svg::SvgWriter;
11
12const ARROW_SIZE: f32 = 8.0;
13
14//----------------------------------------------------------------
15
16fn main() {
17	write_svg("src/vec.rs:LEN_HAT", &len_hat());
18	write_svg("src/vec.rs:DISTANCE_HAT", &distance_hat());
19	write_svg("src/vec.rs:LERP", &lerp());
20	write_svg("src/vec.rs:SLERP", &slerp());
21	write_svg("src/vec.rs:NLERP", &nlerp());
22	write_svg("src/vec.rs:PROJECT_SCALAR", &project_scalar());
23	write_svg("src/vec.rs:REFLECT_2D", &reflect_2d());
24}
25
26fn write_svg(id: &str, svg: &str) {
27	println!("{} {}", id, svg);
28	let mut s = id.split(":"); // Split between file & tag
29	// Read the file contents
30	use std::io::{Read, Write};
31	use std::fs::File;
32	let path = s.next().unwrap();
33	let contents = {
34		let mut file = File::open(path).unwrap();
35		let mut contents = String::new();
36		file.read_to_string(&mut contents).unwrap();
37		contents
38	};
39	// Replace the svg
40	let tag = s.next().unwrap();
41	let start = contents.find(&format!("<!--{}-->", tag)).unwrap();
42	let end = start + contents[start..].find("\n").unwrap();
43	let spliced = format!("{}<!--{}-->{}{}", &contents[..start], tag, svg, &contents[end..]);
44	// Write the file contents back
45	let mut file = File::create(path).unwrap();
46	file.write_all(spliced.as_ref()).unwrap();
47}
48
49//----------------------------------------------------------------
50// Drawing for len_hat
51
52fn len_hat() -> String {
53	let this = Point2(360.5, 20.0);
54	let origin = Point2(40.0, 100.0);
55
56	let vhat = Point2(this.x, origin.y);
57
58	let mut svg = SvgWriter::new(400, 120);
59	svg.arrow(origin, this, ARROW_SIZE).stroke("black");
60	svg.arrow(origin, vhat, ARROW_SIZE).stroke("grey").stroke_width(0.5);
61	svg.arrow(vhat, this, ARROW_SIZE).stroke("grey").stroke_width(0.5);
62	svg.circle(origin, 2.0);
63	svg.text(this + (5.0, 0.0), "this");
64	svg.text((origin + vhat) * 0.5 + (0.0, 15.0), "x").fill("grey");
65	svg.text((vhat + this) * 0.5 + (5.0, 0.0), "y").fill("grey");
66	svg.close()
67}
68
69//----------------------------------------------------------------
70// Drawing for distance_hat
71
72fn distance_hat() -> String {
73	let this = Point2(40.0, 100.0);
74	let to = Point2(360.5, 20.0);
75
76	let vhat = Point2(to.x, this.y);
77
78	let mut svg = SvgWriter::new(400, 120);
79	svg.line(this, to).stroke("black");
80	svg.arrow(this, vhat, ARROW_SIZE).stroke("grey").stroke_width(0.5);
81	svg.arrow(vhat, to, ARROW_SIZE).stroke("grey").stroke_width(0.5);
82	svg.circle(this, 2.0);
83	svg.circle(to, 2.0);
84	svg.text(this + (-20.0, -10.0), "this");
85	svg.text(to + (5.0, 0.0), "to");
86	svg.text((this + vhat) * 0.5 + (0.0, 15.0), "x").fill("grey");
87	svg.text((vhat + to) * 0.5 + (5.0, 0.0), "y").fill("grey");
88	svg.close()
89}
90
91//----------------------------------------------------------------
92// Drawing for lerp
93
94fn lerp() -> String {
95	let v1 = Point2(40.0, 100.0);
96	let v2 = Point2(360.0, 20.0);
97
98	let tgreen = 0.2;
99	let vgreen = v1.lerp(v2, tgreen);
100
101	let tblue = 0.5;
102	let vblue = v1.lerp(v2, tblue);
103
104	let mut svg = SvgWriter::new(400, 120);
105	svg.line(v1, vgreen).stroke("green");
106	svg.line(vgreen, vblue).stroke("blue");
107	svg.line(vblue, v2).stroke("black");
108	svg.circle(v1, 2.0).fill("black");
109	svg.circle(v2, 2.0).fill("black");
110	svg.circle(vgreen, 2.0).fill("green");
111	svg.circle(vblue, 2.0).fill("blue");
112	svg.text(v1 - (20.0, 10.0), "self").fill("black");
113	svg.text(v2 - (15.0, -20.0), "rhs").fill("black");
114	svg.text(vgreen - (20.0, -20.0), "t = 0.2").fill("green");
115	svg.text(vblue - (20.0, -20.0), "t = 0.5").fill("blue");
116	svg.close()
117}
118
119//----------------------------------------------------------------
120// Drawing for slerp and nlerp
121
122fn slerp_nlerp<F: Fn(Point2<f32>, Point2<f32>, f32) -> Point2<f32>>(f: F, name: &str) -> String {
123	let v1 = Point2(100.0, 70.0);
124	let v2 = Point2(300.0, 70.0);
125
126	// Calculate circle center given two points and a radius
127	let radius = 120.0;
128	let vhalf = (v1 + v2) * 0.5;
129	let vdist = v1.distance(v2);
130	let vbase = (v2 - v1).cw().resize((radius * radius - vdist * vdist * 0.25f32).sqrt());
131	let center = vhalf + vbase; // vhalf - vbase for the other solution
132
133	// Calculate lerp
134	let lerp = v1.lerp(v2, 0.75);
135
136	// Calculate slerps
137	let leg1 = v1 - center;
138	let leg2 = v2 - center;
139	let slerp = center + f(leg1, leg2, 0.75);
140	let p1 = center + f(leg1, leg2, 0.25);
141	let p2 = center + f(leg1, leg2, 0.5);
142	let cstart = center + f(leg1, leg2, -0.1);
143	let cend = center + f(leg1, leg2, 1.1);
144
145	// Render time
146	let mut svg = SvgWriter::new(400, 140);
147	svg.arrow(center, v1, ARROW_SIZE).stroke("black").stroke_width(0.5);
148	svg.arrow(center, v2, ARROW_SIZE).stroke("black").stroke_width(0.5);
149	svg.arrow(center, p1, ARROW_SIZE).stroke("green").stroke_width(0.25);
150	svg.arrow(center, p2, ARROW_SIZE).stroke("green").stroke_width(0.25);
151	svg.arrow(center, slerp, ARROW_SIZE).stroke("green");
152	svg.arc(cstart, v1, radius).stroke("black").stroke_width(0.5);
153	svg.arc(v1, slerp, radius).stroke("green");
154	svg.arc(slerp, v2, radius).stroke("black");
155	svg.arc(v2, cend, radius).stroke("black").stroke_width(0.5);
156	svg.line(v1, lerp).stroke("blue").stroke_width(0.5);
157	svg.circle(v1, 2.0).fill("black");
158	svg.circle(v2, 2.0).fill("black");
159	svg.circle(lerp, 2.0).fill("blue");
160	svg.circle(slerp, 2.0).fill("green");
161	svg.text(p1 - (45.0, 5.0), "t = 0.25").fill("green").font_size(10.0);
162	svg.text(p2 - (20.0, 5.0), "t = 0.50").fill("green").font_size(10.0);
163	svg.text(slerp - (0.0, 5.0), "t = 0.75").fill("green").font_size(10.0);
164	svg.text(lerp - (20.0, -20.0), "lerp").fill("blue");
165	svg.text(slerp - (60.0, -10.0), name).fill("green");
166	svg.text(v1 - (50.0, 0.0), "self").fill("black");
167	svg.text(v2 - (-10.0, 0.0), "rhs").fill("black");
168	svg.close()
169}
170
171fn slerp() -> String {
172	slerp_nlerp(Point2::slerp, "slerp")
173}
174fn nlerp() -> String {
175	slerp_nlerp(Point2::nlerp, "nlerp")
176}
177
178//----------------------------------------------------------------
179// Drawing for project_scalar
180
181fn project_scalar() -> String {
182	let v = Point2(360.0, 120.0);
183	let this = Point2(200.0, 20.0);
184	let origin = Point2(40.0, 160.0);
185
186	let p = origin + (this - origin).project(v - origin);
187
188	// Calculate the right angle symbol
189	let ra = (v - origin).resize(20.0);
190	let pra1 = p - ra;
191	let pra2 = pra1 + ra.ccw();
192	let pra3 = pra2 + ra;
193
194	// Calculate the scalar projection length
195	let offset = (p - origin).resize(15.0).cw();
196	let sl1 = origin + offset;
197	let sl2 = p + offset;
198	let sll1 = sl1 - offset * 0.25;
199	let sll2 = sl1 + offset * 0.25;
200	let slr1 = sl2 - offset * 0.25;
201	let slr2 = sl2 + offset * 0.25;
202
203	// Render time
204	let mut svg = SvgWriter::new(400, 200);
205	svg.arrow(origin, this, ARROW_SIZE).stroke("black");
206	svg.arrow(origin, v, ARROW_SIZE).stroke("black");
207	svg.circle(origin, 2.0).fill("black");
208	svg.line(p, this).stroke("black").stroke_dasharray(&[5.0, 5.0]).stroke_width(0.5);
209	svg.line(pra1, pra2).stroke("black").stroke_width(0.5);
210	svg.line(pra2, pra3).stroke("black").stroke_width(0.5);
211	svg.line(sl1, sl2).stroke("red").stroke_width(1.5);
212	svg.line(sll1, sll2).stroke("red").stroke_width(1.5);
213	svg.line(slr1, slr2).stroke("red").stroke_width(1.5);
214	svg.text(this + Vec2(5.0, 5.0), "self").fill("black");
215	svg.text(v + Vec2(-20.0, 22.0), "v").fill("black");
216	svg.close()
217}
218
219//----------------------------------------------------------------
220// Drawing for reflect
221
222fn reflect_2d() -> String {
223	// Calculate data
224	let v = Vec2 { x: 10.0, y: 2.5 };
225	let this = Vec2 { x: 4.0, y: 4.0 };
226	let p = this.project(v);
227	let pv = p - this;
228	let result = p + pv;
229	let origin = Vec2::ZERO;
230
231	// Visualize data
232	let transform = Transform2::translate((40.0f32, 120.0f32)) * Mat2::scale((25.0, -25.0));
233	let this = transform * this;
234	let v = transform * v;
235	let p = transform * p;
236	let pv = transform * pv;
237	let result = transform * result;
238	let origin = transform * origin;
239
240	let mut svg = SvgWriter::new(400, 200);
241	svg.line(this, result).stroke("black").stroke_width(0.5).stroke_dasharray(&[5.0, 5.0]);
242	svg.line(p, pv).stroke("black").stroke_width(0.5).stroke_dasharray(&[5.0, 5.0]);
243	svg.line(pv, result).stroke("black").stroke_width(0.5).stroke_dasharray(&[5.0, 5.0]);
244	svg.arrow(origin, v, ARROW_SIZE).stroke("black");
245	svg.arrow(origin, this, ARROW_SIZE).stroke("black");
246	svg.arrow(origin, result, ARROW_SIZE).stroke("red");
247	svg.circle(p, 2.0).fill("black");
248	svg.text(v, "v").fill("black");
249	svg.text(this, "self").fill("black");
250	svg.text(p + Vec2(8.0, 10.0), "p").fill("black");
251	svg.text(result, "result").fill("red");
252	svg.text(p.lerp(pv, 0.9) + Vec2(-15.0, -5.0), "-self").fill("black");
253	svg.text(pv.lerp(result, 0.8) + Vec2(0.0, 15.0), "+p").fill("black");
254	svg.close()
255}