use std::cell::RefCell;
use std::rc::Rc;
use deno_core::OpState;
#[derive(Clone, Debug)]
pub struct SdfDrawCommand {
pub sdf_expr: String,
pub fill_type: u32,
pub color: [f32; 4],
pub color2: [f32; 4],
pub fill_param: f32,
pub palette_params: [f32; 12],
pub gradient_scale: f32,
pub x: f32,
pub y: f32,
pub bounds: f32,
pub layer: i32,
pub rotation: f32,
pub scale: f32,
pub opacity: f32,
}
pub struct SdfState {
pub commands: Vec<SdfDrawCommand>,
}
impl SdfState {
pub fn new() -> Self {
Self { commands: Vec::new() }
}
}
#[deno_core::op2(fast)]
fn op_sdf_draw(
state: &mut OpState,
#[string] sdf_expr: &str,
fill_type: f64,
r: f64, g: f64, b: f64, a: f64,
r2: f64, g2: f64, b2: f64, a2: f64,
fill_param: f64,
x: f64, y: f64,
bounds: f64,
layer: f64,
rotation: f64,
scale: f64,
opacity: f64,
) {
let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
sdf_state.borrow_mut().commands.push(SdfDrawCommand {
sdf_expr: sdf_expr.to_string(),
fill_type: fill_type as u32,
color: [r as f32, g as f32, b as f32, a as f32],
color2: [r2 as f32, g2 as f32, b2 as f32, a2 as f32],
fill_param: fill_param as f32,
palette_params: [0.0; 12], gradient_scale: 1.0, x: x as f32,
y: y as f32,
bounds: bounds as f32,
layer: layer as i32,
rotation: rotation as f32,
scale: scale as f32,
opacity: opacity as f32,
});
}
#[deno_core::op2(fast)]
fn op_sdf_set_palette(
state: &mut OpState,
a_r: f64, a_g: f64, a_b: f64,
b_r: f64, b_g: f64, b_b: f64,
c_r: f64, c_g: f64, c_b: f64,
d_r: f64, d_g: f64, d_b: f64,
) {
let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
let mut borrowed = sdf_state.borrow_mut();
if let Some(cmd) = borrowed.commands.last_mut() {
cmd.palette_params = [
a_r as f32, a_g as f32, a_b as f32,
b_r as f32, b_g as f32, b_b as f32,
c_r as f32, c_g as f32, c_b as f32,
d_r as f32, d_g as f32, d_b as f32,
];
}
}
#[deno_core::op2(fast)]
fn op_sdf_set_gradient_scale(state: &mut OpState, scale: f64) {
let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
let mut borrowed = sdf_state.borrow_mut();
if let Some(cmd) = borrowed.commands.last_mut() {
cmd.gradient_scale = scale as f32;
}
}
#[deno_core::op2(fast)]
fn op_sdf_clear(state: &mut OpState) {
let sdf_state = state.borrow::<Rc<RefCell<SdfState>>>();
sdf_state.borrow_mut().commands.clear();
}
deno_core::extension!(
sdf_ext,
ops = [
op_sdf_draw,
op_sdf_set_palette,
op_sdf_set_gradient_scale,
op_sdf_clear,
],
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sdf_state_new() {
let state = SdfState::new();
assert!(state.commands.is_empty());
}
#[test]
fn test_sdf_draw_command_fields() {
let cmd = SdfDrawCommand {
sdf_expr: "sd_circle(p, 20.0)".to_string(),
fill_type: 0,
color: [1.0, 0.0, 0.0, 1.0],
color2: [0.0, 0.0, 0.0, 0.0],
fill_param: 0.0,
palette_params: [0.0; 12],
gradient_scale: 1.0,
x: 100.0,
y: 200.0,
bounds: 25.0,
layer: 5,
rotation: 0.0,
scale: 1.0,
opacity: 1.0,
};
assert_eq!(cmd.layer, 5);
assert_eq!(cmd.bounds, 25.0);
assert_eq!(cmd.fill_type, 0);
}
#[test]
fn test_sdf_state_add_and_drain() {
let mut state = SdfState::new();
state.commands.push(SdfDrawCommand {
sdf_expr: "sd_box(p, vec2<f32>(10.0, 5.0))".to_string(),
fill_type: 1,
color: [1.0, 1.0, 1.0, 1.0],
color2: [0.0, 0.0, 0.0, 0.0],
fill_param: 2.0,
palette_params: [0.0; 12],
gradient_scale: 1.0,
x: 50.0,
y: 75.0,
bounds: 15.0,
layer: 0,
rotation: 0.785,
scale: 2.0,
opacity: 0.8,
});
assert_eq!(state.commands.len(), 1);
let drained: Vec<_> = state.commands.drain(..).collect();
assert_eq!(drained.len(), 1);
assert!(state.commands.is_empty());
assert_eq!(drained[0].sdf_expr, "sd_box(p, vec2<f32>(10.0, 5.0))");
}
#[test]
fn test_sdf_state_multiple_commands() {
let mut state = SdfState::new();
for i in 0..10 {
state.commands.push(SdfDrawCommand {
sdf_expr: format!("sd_circle(p, {}.0)", i * 5),
fill_type: 0,
color: [1.0, 1.0, 1.0, 1.0],
color2: [0.0; 4],
fill_param: 0.0,
palette_params: [0.0; 12],
gradient_scale: 1.0,
x: i as f32 * 10.0,
y: 0.0,
bounds: (i * 5 + 5) as f32,
layer: i as i32,
rotation: 0.0,
scale: 1.0,
opacity: 1.0,
});
}
assert_eq!(state.commands.len(), 10);
}
#[test]
fn test_sdf_palette_params() {
let mut state = SdfState::new();
state.commands.push(SdfDrawCommand {
sdf_expr: "sd_circle(p, 30.0)".to_string(),
fill_type: 5,
color: [0.0; 4],
color2: [0.0; 4],
fill_param: 0.0,
palette_params: [
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.33, 0.67, ],
gradient_scale: 1.0,
x: 0.0, y: 0.0, bounds: 35.0,
layer: 0, rotation: 0.0, scale: 1.0, opacity: 1.0,
});
let cmd = &state.commands[0];
assert_eq!(cmd.palette_params[0], 0.5);
assert_eq!(cmd.palette_params[9], 0.0);
assert!((cmd.palette_params[10] - 0.33).abs() < 0.001);
assert!((cmd.palette_params[11] - 0.67).abs() < 0.001);
}
}