Skip to main content

shade/d2/
pen.rs

1use super::*;
2
3/// Pen tool, draws lines.
4#[derive(Clone, Debug, PartialEq)]
5pub struct Pen<T> {
6	/// Vertex template.
7	pub template: T,
8}
9
10impl<V: TVertex, U: TUniform> DrawBuilder<V, U> {
11	/// Draws a line from `a` to `b`.
12	#[inline(never)]
13	pub fn draw_line<T: ToVertex<V>>(&mut self, pen: &Pen<T>, a: Point2f, b: Point2f) {
14		let vertices = [
15			pen.template.to_vertex(a, 0),
16			pen.template.to_vertex(b, 1),
17		];
18		let mut cv = self.begin(PrimType::Lines, 2, 1);
19		cv.add_index2(0, 1);
20		cv.add_vertices(&vertices);
21	}
22
23	/// Draws lines.
24	#[inline(never)]
25	pub fn draw_lines<T: ToVertex<V>>(&mut self, pen: &Pen<T>, pts: &[Point2f], lines: &[(u32, u32)]) {
26		let mut cv = self.begin(PrimType::Lines, pts.len(), lines.len());
27		for i in 0..lines.len() {
28			cv.add_index2(lines[i].0, lines[i].1);
29		}
30		for i in 0..pts.len() {
31			cv.add_vertex(pen.template.to_vertex(pts[i], i));
32		}
33	}
34
35	/// Draws a rectangle with lines.
36	#[inline(never)]
37	pub fn draw_line_rect<T: ToVertex<V>>(&mut self, pen: &Pen<T>, rc: &Bounds2f) {
38		let vertices = [
39			pen.template.to_vertex(rc.bottom_left(), 0),
40			pen.template.to_vertex(rc.top_left(), 1),
41			pen.template.to_vertex(rc.top_right(), 2),
42			pen.template.to_vertex(rc.bottom_right(), 3),
43		];
44		let mut cv = self.begin(PrimType::Lines, 4, 4);
45		cv.add_indices(&[0, 1, 1, 2, 2, 3, 3, 0]);
46		cv.add_vertices(&vertices);
47	}
48
49	/// Draws a rounded rectangle with lines.
50	#[inline(never)]
51	pub fn draw_round_rect<T: ToVertex<V>>(&mut self, pen: &Pen<T>, rc: &Bounds2f, sx: f32, sy: f32, _segments: i32) {
52		// Fixup parameters
53		let sx = if sx + sx > rc.width() { rc.width() * 0.5 } else { sx };
54		let sy = if sy + sy > rc.height() { rc.height() * 0.5 } else { sy };
55
56		// Edge case: just draw a rectangle
57		if sx <= 0.0 || sy <= 0.0 {
58			return self.draw_line_rect(pen, rc);
59		}
60
61		// Draw the rounded corners as quad beziers
62		// https://en.wikipedia.org/wiki/Composite_B%C3%A9zier_curve#Approximating_circular_arcs
63
64		// TODO? Do it properly so the edges are synced up
65		unimplemented!()
66	}
67
68	/// Draws an arrow.
69	pub fn draw_arrow<T: ToVertex<V>>(&mut self, pen: &Pen<T>, start: Point2f, end: Point2f, head: f32) {
70		let pth = (end - start).resize(head);
71		let pta = (end - pth) + pth.ccw() * 0.5;
72		let ptb = (end - pth) + pth.cw() * 0.5;
73		let vertices = [
74			pen.template.to_vertex(start, 0),
75			pen.template.to_vertex(end, 1),
76			pen.template.to_vertex(pta, 2),
77			pen.template.to_vertex(ptb, 3),
78		];
79		let mut cv = self.begin(PrimType::Lines, 4, 3);
80		cv.add_indices(&[0, 1, 2, 1, 3, 1]);
81		cv.add_vertices(&vertices);
82	}
83
84	/// Draws connected lines.
85	#[inline(never)]
86	pub fn draw_poly_line<T: ToVertex<V>>(&mut self, pen: &Pen<T>, pts: &[Point2f], close: bool) {
87		// Degenerate polyline
88		if pts.len() < 2 {
89			return;
90		}
91		// open: n vertices, n - 1 primitives, (n - 1) * 2 indices
92		// close: n vertices, n primitives, n * 2 indices
93		let n = pts.len() - (!close) as usize;
94		let mut cv = self.begin(PrimType::Lines, pts.len(), n);
95		// Add indices
96		for i in 0..pts.len() - 1 {
97			cv.add_index2(i as u32, (i + 1) as u32);
98		}
99		if close {
100			cv.add_index2((pts.len() - 1) as u32, 0);
101		}
102		// Add vertices
103		for v in 0..pts.len() {
104			cv.add_vertex(pen.template.to_vertex(pts[v], v));
105		}
106	}
107
108	/// Draws an ellipse.
109	#[inline(never)]
110	pub fn draw_ellipse<T: ToVertex<V>>(&mut self, pen: &Pen<T>, rc: &Bounds2f, segments: i32) {
111		// n vertices, n primitives, n * 2 indices
112		let n = cmp::max(3, segments) as usize;
113		let mut cv = self.begin(PrimType::Lines, n, n);
114
115		// Add indices
116		for i in 0..n - 1 {
117			cv.add_index2(i as u32, (i + 1) as u32);
118		}
119		cv.add_index2((n - 1) as u32, 0);
120
121		// Precompute trigs
122		let (s, c) = (Angle::TURN / (n as i32 as f32)).sin_cos();
123		let radius = rc.size() * 0.5;
124		let center = rc.top_left() + radius;
125		let mut pt = Point2(1.0, 0.0);
126
127		// Add vertices
128		// http://slabode.exofire.net/circle_draw.shtml
129		for v in 0..n {
130			cv.add_vertex(pen.template.to_vertex(center + pt * radius, v));
131			// Apply rotation matrix
132			let x = pt.x;
133			pt.x = c * x - s * pt.y;
134			pt.y = s * x + c * pt.y;
135		}
136	}
137
138	/// Draws an arc.
139	#[inline(never)]
140	pub fn draw_arc<T: ToVertex<V>>(&mut self, pen: &Pen<T>, rc: &Bounds2f, start: Angle<f32>, sweep: Angle<f32>, segments: i32) {
141		if sweep <= -Angle::TURN || sweep >= Angle::TURN {
142			return self.draw_ellipse(pen, rc, segments);
143		}
144
145		// n + 1 vertices, n primitives, n * 2 indices
146		let n = cmp::max(2, segments) as usize;
147		let mut cv = self.begin(PrimType::Lines, n + 1, n);
148
149		// Add indices
150		for i in 0..n {
151			cv.add_index2(i as u32, (i + 1) as u32);
152		}
153
154		// Precompute trigs
155		let (s, c) = (sweep / (n as i32 as f32)).sin_cos();
156		let radius = rc.size() * 0.5;
157		let center = rc.top_left() + radius;
158		let mut pt = {
159			let (y, x) = start.sin_cos();
160			Point2(x, y)
161		};
162
163		// Add vertices
164		// http://slabode.exofire.net/circle_draw.shtml
165		for v in 0..n + 1 {
166			cv.add_vertex(pen.template.to_vertex(center + pt * radius, v));
167			// Apply rotation matrix
168			let x = pt.x;
169			pt.x = c * x - s * pt.y;
170			pt.y = s * x + c * pt.y;
171		}
172	}
173
174	#[inline(never)]
175	pub fn draw_bezier2<T: ToVertex<V>>(&mut self, pen: &Pen<T>, pts: &[Point2f; 3], segments: i32) {
176		// n + 1 vertices, n primitives, n * 2 indices
177		let n = cmp::max(2, segments) as usize;
178		let mut cv = self.begin(PrimType::Lines, n + 1, n);
179
180		// Add indices
181		for i in 0..n {
182			cv.add_index2(i as u32, (i + 1) as u32);
183		}
184
185		// Add vertices
186		for v in 0..n + 1 {
187			let pt = curve::bezier2(v as i32 as f32 / n as i32 as f32, pts[0], pts[1], pts[2]);
188			cv.add_vertex(pen.template.to_vertex(pt, v));
189		}
190	}
191
192	#[inline(never)]
193	pub fn draw_bezier3<T: ToVertex<V>>(&mut self, pen: &Pen<T>, pts: &[Point2f; 4], segments: i32) {
194		// n + 1 vertices, n primitives, n * 2 indices
195		let n = cmp::max(2, segments) as usize;
196		let mut cv = self.begin(PrimType::Lines, n + 1, n);
197
198		// Add indices
199		for i in 0..n {
200			cv.add_index2(i as u32, (i + 1) as u32);
201		}
202
203		// Add vertices
204		for v in 0..n + 1 {
205			let pt = curve::bezier3(v as i32 as f32 / n as i32 as f32, pts[0], pts[1], pts[2], pts[3]);
206			cv.add_vertex(pen.template.to_vertex(pt, v));
207		}
208	}
209
210	#[inline(never)]
211	pub fn draw_cspline<T: ToVertex<V>>(&mut self, pen: &Pen<T>, pts: &[Point2f], tension: f32, segments: i32) {
212		// Degenerate cspline
213		if pts.len() < 2 {
214			return;
215		}
216
217		// On first iteration, incoming velocity is zero
218		let mut u = Point2::ZERO;
219		let mut v;
220		let tension = (1.0 - tension) * 0.5;
221
222		for i in 0..pts.len() - 1 {
223			// Calculate the end point velocity
224			v = if i == pts.len() - 2 {
225				// On last iteration, outgoing velocity is zero
226				Point2::ZERO
227			}
228			else {
229				(pts[i + 2] - pts[i]) * tension
230			};
231			let curve = [
232				pts[i],
233				pts[i] + u * (1.0 / 3.0),
234				pts[i + 1] - v * (1.0 / 3.0),
235				pts[i + 1],
236			];
237			self.draw_bezier3(pen, &curve, segments);
238			u = v;
239		}
240	}
241}