aidoku/structs/
canvas.rs

1//! Structs and enums used for drawing on a canvas.
2//!
3//! A lot of APIs are largely based on [raqote](https://crates.io/crates/raqote).
4use crate::alloc::Vec;
5use serde::{Deserialize, Serialize};
6
7pub type Transform = euclid::default::Transform2D<f32>;
8pub type Angle = euclid::Angle<f32>;
9
10/// A rectangle.
11#[derive(Debug, PartialEq, Clone, Copy)]
12pub struct Rect {
13	pub x: f32,
14	pub y: f32,
15	pub width: f32,
16	pub height: f32,
17}
18
19impl Rect {
20	/// Creates a new rectangle with the given position and size.
21	pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
22		Rect {
23			x,
24			y,
25			width,
26			height,
27		}
28	}
29}
30
31/// A 2D point.
32#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
33pub struct Point {
34	pub x: f32,
35	pub y: f32,
36}
37
38impl Point {
39	/// Creates a new point with the given coordinates.
40	pub fn new(x: f32, y: f32) -> Self {
41		Point { x, y }
42	}
43}
44
45#[derive(Debug, PartialEq, Serialize, Deserialize)]
46pub enum PathOp {
47	MoveTo(Point),
48	LineTo(Point),
49	QuadTo(Point, Point),
50	CubicTo(Point, Point, Point),
51	Arc(Point, f32, f32, f32),
52	Close,
53}
54
55/// A 2D path.
56#[derive(Debug, Serialize, Deserialize, Default)]
57pub struct Path {
58	pub ops: Vec<PathOp>,
59	#[serde(skip)]
60	#[cfg(feature = "imports")]
61	pub(crate) ptr: Option<i32>,
62}
63
64impl Path {
65	pub fn rect(rect: &Rect) -> Self {
66		PathBuilder::new().rect(rect).build()
67	}
68}
69
70impl PartialEq for Path {
71	fn eq(&self, other: &Self) -> bool {
72		self.ops == other.ops
73	}
74}
75
76/// A builder for paths.
77#[derive(Debug, Default)]
78pub struct PathBuilder {
79	path: Path,
80}
81
82impl PathBuilder {
83	/// Creates a new path builder.
84	pub fn new() -> Self {
85		PathBuilder::default()
86	}
87
88	/// Moves the current point to `x`, `y`.
89	pub fn move_to(mut self, x: f32, y: f32) -> Self {
90		self.path.ops.push(PathOp::MoveTo(Point::new(x, y)));
91		self
92	}
93
94	/// Adds a line segment from the current point to `x`, `y`.
95	pub fn line_to(mut self, x: f32, y: f32) -> Self {
96		self.path.ops.push(PathOp::LineTo(Point::new(x, y)));
97		self
98	}
99
100	/// Adds a quadratic bezier from the current point to `x`, `y`,
101	/// using a control point of `cx`, `cy`.
102	pub fn quad_to(mut self, cx: f32, cy: f32, x: f32, y: f32) -> Self {
103		self.path
104			.ops
105			.push(PathOp::QuadTo(Point::new(cx, cy), Point::new(x, y)));
106		self
107	}
108
109	/// Adds a rect to the path.
110	pub fn rect(self, rect: &Rect) -> Self {
111		self.move_to(rect.x, rect.y)
112			.line_to(rect.x + rect.width, rect.y)
113			.line_to(rect.x + rect.width, rect.y + rect.height)
114			.line_to(rect.x, rect.y + rect.height)
115			.close()
116	}
117
118	/// Adds a cubic bezier from the current point to `x`, `y`,
119	/// using control points `cx1`, `cy1` and `cx2`, `cy2`.
120	pub fn cubic_to(mut self, x: f32, y: f32, cx1: f32, cy1: f32, cx2: f32, cy2: f32) -> Self {
121		self.path.ops.push(PathOp::CubicTo(
122			Point::new(x, y),
123			Point::new(cx1, cy1),
124			Point::new(cx2, cy2),
125		));
126		self
127	}
128
129	/// Adds an arc approximated by quadratic beziers with center `x`, `y`
130	/// and radius `r` starting at `start_angle` and sweeping by `sweep_angle`.
131	/// For a positive `sweep_angle` the sweep is done clockwise, for a negative
132	/// `sweep_angle` the sweep is done counterclockwise.
133	pub fn arc(mut self, x: f32, y: f32, radius: f32, start_angle: f32, sweep_angle: f32) -> Self {
134		self.path.ops.push(PathOp::Arc(
135			Point::new(x, y),
136			radius,
137			start_angle,
138			sweep_angle,
139		));
140		self
141	}
142
143	/// Closes the current subpath.
144	pub fn close(mut self) -> Self {
145		self.path.ops.push(PathOp::Close);
146		self
147	}
148
149	/// Builds the path.
150	pub fn build(self) -> Path {
151		#[cfg(feature = "imports")]
152		{
153			use crate::imports::std::encode;
154			let mut path = self.path;
155			path.ptr = Some(unsafe { encode(&path) });
156			path
157		}
158		#[cfg(not(feature = "imports"))]
159		self.path
160	}
161}
162
163/// A color with red, green, blue, and alpha components.
164#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
165pub struct Color {
166	pub red: f32,
167	pub green: f32,
168	pub blue: f32,
169	pub alpha: f32,
170}
171
172impl Color {
173	/// Creates a new color with the given components.
174	pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
175		Self {
176			red,
177			green,
178			blue,
179			alpha,
180		}
181	}
182
183	/// The color black.
184	pub fn black() -> Self {
185		Self::new(0.0, 0.0, 0.0, 1.0)
186	}
187
188	/// The color white.
189	pub fn white() -> Self {
190		Self::new(255.0, 255.0, 255.0, 1.0)
191	}
192
193	/// The color red.
194	pub fn red() -> Self {
195		Self::new(255.0, 0.0, 0.0, 1.0)
196	}
197
198	/// The color green.
199	pub fn green() -> Self {
200		Self::new(0.0, 255.0, 0.0, 1.0)
201	}
202
203	/// The color blue.
204	pub fn blue() -> Self {
205		Self::new(0.0, 0.0, 255.0, 1.0)
206	}
207}
208
209impl Default for Color {
210	fn default() -> Self {
211		Self::black()
212	}
213}
214
215/// The endpoint style of a line.
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
217pub enum LineCap {
218	Round,
219	Square,
220	#[default]
221	Butt,
222}
223
224/// The style of connected line joins.
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
226pub enum LineJoin {
227	Round,
228	Bevel,
229	#[default]
230	Miter,
231}
232
233/// The configuration options for drawing strokes.
234#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
235pub struct StrokeStyle {
236	pub color: Color,
237	pub width: f32,
238	pub cap: LineCap,
239	pub join: LineJoin,
240	pub miter_limit: f32,
241	pub dash_array: Vec<f32>,
242	pub dash_offset: f32,
243}
244
245impl Default for StrokeStyle {
246	fn default() -> Self {
247		Self {
248			color: Default::default(),
249			width: 1.,
250			cap: Default::default(),
251			join: Default::default(),
252			miter_limit: 10.,
253			dash_array: Vec::new(),
254			dash_offset: 0.,
255		}
256	}
257}
258
259/// A standard typeface weight.
260#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
261pub enum FontWeight {
262	UltraLight = 0,
263	Thin = 1,
264	Light = 2,
265	#[default]
266	Regular = 3,
267	Medium = 4,
268	Semibold = 5,
269	Bold = 6,
270	Heavy = 7,
271	Black = 8,
272}
273
274impl From<u8> for FontWeight {
275	fn from(value: u8) -> Self {
276		match value {
277			0 => FontWeight::UltraLight,
278			1 => FontWeight::Thin,
279			2 => FontWeight::Light,
280			3 => FontWeight::Regular,
281			4 => FontWeight::Medium,
282			5 => FontWeight::Semibold,
283			6 => FontWeight::Bold,
284			7 => FontWeight::Heavy,
285			8 => FontWeight::Black,
286			_ => FontWeight::Regular,
287		}
288	}
289}