add rectangle and circle shapes

This commit is contained in:
liquidex 2024-08-21 21:11:30 +02:00
parent 50094c3872
commit 7933057062
4 changed files with 129 additions and 10 deletions

View file

@ -1,6 +1,7 @@
use alloc::vec::Vec;
use tiny_skia::{
BlendMode, Color, LineCap, Paint, PathBuilder, Pixmap, Shader, Stroke as SStroke, Transform,
BlendMode, Color, LineCap, Paint, PathBuilder, Pixmap, Rect, Shader, Stroke as SStroke,
Transform,
};
use crate::{
@ -132,6 +133,46 @@ impl<'a> Renderer<'a> {
None,
);
}
Shape::Rect(position, size) => {
let mut pb = PathBuilder::new();
if let Some(rect) =
tiny_skia::Rect::from_xywh(position.x, position.y, size.x, size.y)
{
pb.push_rect(rect);
}
let path = pb.finish().unwrap();
self.pixmap_mut().stroke_path(
&path,
&paint,
&SStroke {
width: stroke.thickness,
line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
}
Shape::Circle(position, radius) => {
let mut pb = PathBuilder::new();
pb.push_circle(position.x, position.y, radius);
let path = pb.finish().unwrap();
self.pixmap_mut().stroke_path(
&path,
&paint,
&SStroke {
width: stroke.thickness,
line_cap: LineCap::Square,
..Default::default()
},
transform,
None,
);
}
}
Ok(())

View file

@ -102,7 +102,7 @@ pub mod fns {
use alloc::vec::Vec;
use crate::{
value::{List, Ref, Rgba, Scribble, Shape, Stroke, Value, Vec4},
value::{List, Ref, Rgba, Scribble, Shape, Stroke, Value, Vec2, Vec4},
vm::{Exception, FnArgs, Vm},
};
@ -139,6 +139,8 @@ pub mod fns {
0xc0 "to-shape" => to_shape_f,
0xc1 "line" => line,
0xc2 "rect" => rect,
0xc3 "circle" => circle,
0xe0 "stroke" => stroke,
}
}
@ -410,7 +412,7 @@ pub mod fns {
None
}
}
Value::Vec4(vec) => Some(Shape::Point(vec)),
Value::Vec4(vec) => Some(Shape::Point(vec.into())),
}
}
@ -436,7 +438,50 @@ pub mod fns {
let start = args.get_vec4(vm, 0, ERROR)?;
let end = args.get_vec4(vm, 1, ERROR)?;
let id = vm.create_ref(Ref::Shape(Shape::Line(start, end)))?;
let id = vm.create_ref(Ref::Shape(Shape::Line(start.into(), end.into())))?;
Ok(Value::Ref(id))
}
pub fn rect(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
static ARGS2: &str = "arguments to 2-argument (rect) must be (vec)";
static ARGS4: &str = "arguments to 4-argument (rect) must be numbers";
let (position, size) = match args.num() {
2 => (args.get_vec4(vm, 0, ARGS2)?.into(), args.get_vec4(vm, 1, ARGS2)?.into()),
4 => (
Vec2 {
x: args.get_number(vm, 0, ARGS4)?,
y: args.get_number(vm, 1, ARGS4)?,
},
Vec2 {
x: args.get_number(vm, 2, ARGS4)?,
y: args.get_number(vm, 3, ARGS4)?,
},
),
_ => return Err(vm.create_exception("(rect) expects 2 arguments (rect position size) or 4 arguments (rect x y width height)"))
};
let id = vm.create_ref(Ref::Shape(Shape::Rect(position, size)))?;
Ok(Value::Ref(id))
}
pub fn circle(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
static ARGS2: &str = "arguments to 2-argument (circle) must be (vec) and a number";
static ARGS3: &str = "arguments to 3-argument (circle) must be numbers";
let (position, radius) = match args.num() {
2 => (args.get_vec4(vm, 0, ARGS2)?.into(), args.get_number(vm, 1, ARGS2)?),
3 => (
Vec2 {
x: args.get_number(vm, 0, ARGS3)?,
y: args.get_number(vm, 1, ARGS3)?,
},
args.get_number(vm, 2, ARGS3)?
),
_ => return Err(vm.create_exception("(circle) expects 2 arguments (circle position radius) or 3 arguments (circle x y radius)"))
};
let id = vm.create_ref(Ref::Shape(Shape::Circle(position, radius)))?;
Ok(Value::Ref(id))
}

View file

@ -67,6 +67,21 @@ impl From<f32> for Value {
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct Vec2 {
pub x: f32,
pub y: f32,
}
impl From<Vec4> for Vec2 {
fn from(value: Vec4) -> Self {
Self {
x: value.x,
y: value.y,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct Vec4 {
pub x: f32,
@ -151,8 +166,10 @@ pub struct List {
#[derive(Debug, Clone)]
pub enum Shape {
Point(Vec4),
Line(Vec4, Vec4),
Point(Vec2),
Line(Vec2, Vec2),
Rect(Vec2, Vec2),
Circle(Vec2, f32),
}
#[derive(Debug, Clone)]

View file

@ -32,6 +32,8 @@ pub struct Vm {
#[derive(Debug, Clone, Copy)]
pub struct VmImage {
stack: usize,
call_stack: usize,
refs: usize,
defs: usize,
fuel: usize,
@ -76,6 +78,8 @@ impl Vm {
"cannot image VM while running code"
);
VmImage {
stack: self.stack.len(),
call_stack: self.call_stack.len(),
refs: self.refs.len(),
defs: self.defs.len(),
fuel: self.fuel,
@ -84,10 +88,21 @@ impl Vm {
}
pub fn restore_image(&mut self, image: &VmImage) {
assert!(
self.stack.is_empty() && self.call_stack.is_empty(),
"cannot restore VM image while running code"
);
// NOTE: My initial assumption here was that system functions should not be able to restore
// the VM if it's running code.
// Turns out that was a bad assumption to make, because the VM may fail with an exception,
// in which case the call stack and stack may not be empty.
// assert!(
// self.stack.is_empty() && self.call_stack.is_empty(),
// "cannot restore VM image while running code"
// );
self.stack.resize_with(image.stack, || {
panic!("image must be a subset of the current VM")
});
self.call_stack.resize_with(image.call_stack, || {
panic!("image must be a subset of the current VM")
});
self.refs.resize_with(image.refs, || {
panic!("image must be a subset of the current VM")
});
@ -95,6 +110,7 @@ impl Vm {
panic!("image must be a subset of the current VM")
});
self.fuel = image.fuel;
self.memory = image.memory;
}
pub fn apply_defs(&mut self, defs: &Defs) {