1use std::cell::RefCell;
10use std::rc::Rc;
11
12use deno_core::OpState;
13
14#[derive(Clone, Debug)]
16pub struct SdfDrawCommand {
17 pub sdf_expr: String,
19 pub fill_type: u32,
21 pub color: [f32; 4],
23 pub color2: [f32; 4],
25 pub fill_param: f32,
27 pub palette_params: [f32; 12],
29 pub gradient_scale: f32,
31 pub x: f32,
33 pub y: f32,
35 pub bounds: f32,
37 pub layer: i32,
39 pub rotation: f32,
41 pub scale: f32,
43 pub opacity: f32,
45}
46
47pub struct SdfState {
49 pub commands: Vec<SdfDrawCommand>,
50}
51
52impl SdfState {
53 pub fn new() -> Self {
54 Self { commands: Vec::new() }
55 }
56}
57
58#[deno_core::op2(fast)]
64fn op_sdf_draw(
65 state: &mut OpState,
66 #[string] sdf_expr: &str,
67 fill_type: f64,
68 r: f64, g: f64, b: f64, a: f64,
70 r2: f64, g2: f64, b2: f64, a2: f64,
72 fill_param: f64,
74 x: f64, y: f64,
76 bounds: f64,
77 layer: f64,
78 rotation: f64,
79 scale: f64,
80 opacity: f64,
81) {
82 let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
83 sdf_state.borrow_mut().commands.push(SdfDrawCommand {
84 sdf_expr: sdf_expr.to_string(),
85 fill_type: fill_type as u32,
86 color: [r as f32, g as f32, b as f32, a as f32],
87 color2: [r2 as f32, g2 as f32, b2 as f32, a2 as f32],
88 fill_param: fill_param as f32,
89 palette_params: [0.0; 12], gradient_scale: 1.0, x: x as f32,
92 y: y as f32,
93 bounds: bounds as f32,
94 layer: layer as i32,
95 rotation: rotation as f32,
96 scale: scale as f32,
97 opacity: opacity as f32,
98 });
99}
100
101#[deno_core::op2(fast)]
104fn op_sdf_set_palette(
105 state: &mut OpState,
106 a_r: f64, a_g: f64, a_b: f64,
107 b_r: f64, b_g: f64, b_b: f64,
108 c_r: f64, c_g: f64, c_b: f64,
109 d_r: f64, d_g: f64, d_b: f64,
110) {
111 let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
112 let mut borrowed = sdf_state.borrow_mut();
113 if let Some(cmd) = borrowed.commands.last_mut() {
114 cmd.palette_params = [
115 a_r as f32, a_g as f32, a_b as f32,
116 b_r as f32, b_g as f32, b_b as f32,
117 c_r as f32, c_g as f32, c_b as f32,
118 d_r as f32, d_g as f32, d_b as f32,
119 ];
120 }
121}
122
123#[deno_core::op2(fast)]
127fn op_sdf_set_gradient_scale(state: &mut OpState, scale: f64) {
128 let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
129 let mut borrowed = sdf_state.borrow_mut();
130 if let Some(cmd) = borrowed.commands.last_mut() {
131 cmd.gradient_scale = scale as f32;
132 }
133}
134
135#[deno_core::op2(fast)]
137fn op_sdf_clear(state: &mut OpState) {
138 let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
139 sdf_state.borrow_mut().commands.clear();
140}
141
142deno_core::extension!(
143 sdf_ext,
144 ops = [
145 op_sdf_draw,
146 op_sdf_set_palette,
147 op_sdf_set_gradient_scale,
148 op_sdf_clear,
149 ],
150);
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_sdf_state_new() {
158 let state = SdfState::new();
159 assert!(state.commands.is_empty());
160 }
161
162 #[test]
163 fn test_sdf_draw_command_fields() {
164 let cmd = SdfDrawCommand {
165 sdf_expr: "sd_circle(p, 20.0)".to_string(),
166 fill_type: 0,
167 color: [1.0, 0.0, 0.0, 1.0],
168 color2: [0.0, 0.0, 0.0, 0.0],
169 fill_param: 0.0,
170 palette_params: [0.0; 12],
171 gradient_scale: 1.0,
172 x: 100.0,
173 y: 200.0,
174 bounds: 25.0,
175 layer: 5,
176 rotation: 0.0,
177 scale: 1.0,
178 opacity: 1.0,
179 };
180 assert_eq!(cmd.layer, 5);
181 assert_eq!(cmd.bounds, 25.0);
182 assert_eq!(cmd.fill_type, 0);
183 }
184
185 #[test]
186 fn test_sdf_state_add_and_drain() {
187 let mut state = SdfState::new();
188 state.commands.push(SdfDrawCommand {
189 sdf_expr: "sd_box(p, vec2<f32>(10.0, 5.0))".to_string(),
190 fill_type: 1,
191 color: [1.0, 1.0, 1.0, 1.0],
192 color2: [0.0, 0.0, 0.0, 0.0],
193 fill_param: 2.0,
194 palette_params: [0.0; 12],
195 gradient_scale: 1.0,
196 x: 50.0,
197 y: 75.0,
198 bounds: 15.0,
199 layer: 0,
200 rotation: 0.785,
201 scale: 2.0,
202 opacity: 0.8,
203 });
204 assert_eq!(state.commands.len(), 1);
205
206 let drained: Vec<_> = state.commands.drain(..).collect();
207 assert_eq!(drained.len(), 1);
208 assert!(state.commands.is_empty());
209 assert_eq!(drained[0].sdf_expr, "sd_box(p, vec2<f32>(10.0, 5.0))");
210 }
211
212 #[test]
213 fn test_sdf_state_multiple_commands() {
214 let mut state = SdfState::new();
215 for i in 0..10 {
216 state.commands.push(SdfDrawCommand {
217 sdf_expr: format!("sd_circle(p, {}.0)", i * 5),
218 fill_type: 0,
219 color: [1.0, 1.0, 1.0, 1.0],
220 color2: [0.0; 4],
221 fill_param: 0.0,
222 palette_params: [0.0; 12],
223 gradient_scale: 1.0,
224 x: i as f32 * 10.0,
225 y: 0.0,
226 bounds: (i * 5 + 5) as f32,
227 layer: i as i32,
228 rotation: 0.0,
229 scale: 1.0,
230 opacity: 1.0,
231 });
232 }
233 assert_eq!(state.commands.len(), 10);
234 }
235
236 #[test]
237 fn test_sdf_palette_params() {
238 let mut state = SdfState::new();
239 state.commands.push(SdfDrawCommand {
240 sdf_expr: "sd_circle(p, 30.0)".to_string(),
241 fill_type: 5,
242 color: [0.0; 4],
243 color2: [0.0; 4],
244 fill_param: 0.0,
245 palette_params: [
246 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.33, 0.67, ],
251 gradient_scale: 1.0,
252 x: 0.0, y: 0.0, bounds: 35.0,
253 layer: 0, rotation: 0.0, scale: 1.0, opacity: 1.0,
254 });
255 let cmd = &state.commands[0];
256 assert_eq!(cmd.palette_params[0], 0.5);
257 assert_eq!(cmd.palette_params[9], 0.0);
258 assert!((cmd.palette_params[10] - 0.33).abs() < 0.001);
259 assert!((cmd.palette_params[11] - 0.67).abs() < 0.001);
260 }
261}