2025-12-11 16:00:03 +01:00

284 lines
8.2 KiB
Rust

#![allow(unused_mut, unused_variables)]
use macros::ToJs;
use mem::SerializableResult;
use std::mem::size_of;
use std::sync::{Mutex, OnceLock};
use crate::shapes::{Path, Segment, ToPath};
use crate::{mem, with_current_shape, with_current_shape_mut, STATE};
const RAW_SEGMENT_DATA_SIZE: usize = size_of::<RawSegmentData>();
pub mod bools;
#[repr(C, u16, align(4))]
#[derive(Debug, PartialEq, Clone, Copy, ToJs)]
#[allow(dead_code)]
enum RawSegmentData {
MoveTo(RawMoveCommand) = 0x01,
LineTo(RawLineCommand) = 0x02,
CurveTo(RawCurveCommand) = 0x03,
Close = 0x04,
}
impl RawSegmentData {
pub fn from_segment(segment: Segment) -> Self {
match segment {
Segment::MoveTo(to) => RawSegmentData::MoveTo(RawMoveCommand::new(to)),
Segment::LineTo(to) => RawSegmentData::LineTo(RawLineCommand::new(to)),
Segment::CurveTo((c1, c2, to)) => {
RawSegmentData::CurveTo(RawCurveCommand::new(c1, c2, to))
}
Segment::Close => RawSegmentData::Close,
}
}
}
impl From<[u8; size_of::<RawSegmentData>()]> for RawSegmentData {
fn from(bytes: [u8; size_of::<RawSegmentData>()]) -> Self {
unsafe { std::mem::transmute(bytes) }
}
}
impl TryFrom<&[u8]> for RawSegmentData {
type Error = String;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let data: [u8; RAW_SEGMENT_DATA_SIZE] = bytes
.get(0..RAW_SEGMENT_DATA_SIZE)
.and_then(|slice| slice.try_into().ok())
.ok_or("Invalid path data".to_string())?;
Ok(RawSegmentData::from(data))
}
}
impl From<RawSegmentData> for [u8; RAW_SEGMENT_DATA_SIZE] {
fn from(value: RawSegmentData) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl SerializableResult for RawSegmentData {
type BytesType = [u8; RAW_SEGMENT_DATA_SIZE];
// The generic trait doesn't know the size of the array. This is why the
// clone needs to be here even if it could be generic.
fn clone_to_slice(&self, slice: &mut [u8]) {
let bytes = Self::BytesType::from(*self);
slice.clone_from_slice(&bytes);
}
}
#[repr(C, align(4))]
#[derive(Debug, PartialEq, Clone, Copy)]
struct RawMoveCommand {
_padding: [u32; 4],
x: f32,
y: f32,
}
impl RawMoveCommand {
pub fn new((x, y): (f32, f32)) -> Self {
Self {
_padding: [0u32; 4],
x,
y,
}
}
}
#[repr(C, align(4))]
#[derive(Debug, PartialEq, Clone, Copy)]
struct RawLineCommand {
_padding: [u32; 4],
x: f32,
y: f32,
}
impl RawLineCommand {
pub fn new((x, y): (f32, f32)) -> Self {
Self {
_padding: [0u32; 4],
x,
y,
}
}
}
#[repr(C, align(4))]
#[derive(Debug, PartialEq, Clone, Copy)]
struct RawCurveCommand {
c1_x: f32,
c1_y: f32,
c2_x: f32,
c2_y: f32,
x: f32,
y: f32,
}
impl RawCurveCommand {
pub fn new((c1_x, c1_y): (f32, f32), (c2_x, c2_y): (f32, f32), (x, y): (f32, f32)) -> Self {
Self {
c1_x,
c1_y,
c2_x,
c2_y,
x,
y,
}
}
}
impl From<RawSegmentData> for Segment {
fn from(value: RawSegmentData) -> Self {
match value {
RawSegmentData::MoveTo(cmd) => Segment::MoveTo((cmd.x, cmd.y)),
RawSegmentData::LineTo(cmd) => Segment::LineTo((cmd.x, cmd.y)),
RawSegmentData::CurveTo(cmd) => {
Segment::CurveTo(((cmd.c1_x, cmd.c1_y), (cmd.c2_x, cmd.c2_y), (cmd.x, cmd.y)))
}
RawSegmentData::Close => Segment::Close,
}
}
}
impl From<Vec<RawSegmentData>> for Path {
fn from(value: Vec<RawSegmentData>) -> Self {
let segments = value.into_iter().map(Segment::from).collect();
Path::new(segments)
}
}
static PATH_UPLOAD_BUFFER: OnceLock<Mutex<Vec<u8>>> = OnceLock::new();
fn get_path_upload_buffer() -> &'static Mutex<Vec<u8>> {
PATH_UPLOAD_BUFFER.get_or_init(|| Mutex::new(Vec::new()))
}
#[no_mangle]
pub extern "C" fn start_shape_path_buffer() {
let buffer = get_path_upload_buffer();
let mut buffer = buffer.lock().unwrap();
buffer.clear();
}
#[no_mangle]
pub extern "C" fn set_shape_path_chunk_buffer() {
let bytes = mem::bytes();
let buffer = get_path_upload_buffer();
let mut buffer = buffer.lock().unwrap();
buffer.extend_from_slice(&bytes);
mem::free_bytes();
}
#[no_mangle]
pub extern "C" fn set_shape_path_buffer() {
with_current_shape_mut!(state, |shape: &mut Shape| {
let buffer = get_path_upload_buffer();
let mut buffer = buffer.lock().unwrap();
let chunk_size = size_of::<RawSegmentData>();
if !buffer.len().is_multiple_of(chunk_size) {
// FIXME
println!("Warning: buffer length is not a multiple of chunk size!");
}
let mut segments = Vec::new();
for (i, chunk) in buffer.chunks(chunk_size).enumerate() {
match RawSegmentData::try_from(chunk) {
Ok(seg) => segments.push(Segment::from(seg)),
Err(e) => println!("Error at segment {}: {}", i, e),
}
}
shape.set_path_segments(segments);
buffer.clear();
});
}
#[no_mangle]
pub extern "C" fn set_shape_path_content() {
with_current_shape_mut!(state, |shape: &mut Shape| {
let bytes = mem::bytes();
let segments = bytes
.chunks(size_of::<RawSegmentData>())
.map(|chunk| RawSegmentData::try_from(chunk).expect("Invalid path data"))
.map(Segment::from)
.collect();
shape.set_path_segments(segments);
});
}
#[no_mangle]
pub extern "C" fn current_to_path() -> *mut u8 {
let mut result = Vec::<RawSegmentData>::default();
with_current_shape!(state, |shape: &Shape| {
let path = shape.to_path(&state.shapes);
result = path
.segments()
.iter()
.copied()
.map(RawSegmentData::from_segment)
.collect();
});
mem::write_vec(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_move_command_deserialization() {
let mut bytes = [0x00; size_of::<RawSegmentData>()];
bytes[0..2].copy_from_slice(&0x01_u16.to_le_bytes());
bytes[20..24].copy_from_slice(&1.0_f32.to_le_bytes());
bytes[24..28].copy_from_slice(&2.0_f32.to_le_bytes());
let raw_segment = RawSegmentData::try_from(&bytes[..]).unwrap();
let segment = Segment::from(raw_segment);
assert_eq!(segment, Segment::MoveTo((1.0, 2.0)));
}
#[test]
fn test_line_command_deserialization() {
let mut bytes = [0x00; size_of::<RawSegmentData>()];
bytes[0..2].copy_from_slice(&0x02_u16.to_le_bytes());
bytes[20..24].copy_from_slice(&3.0_f32.to_le_bytes());
bytes[24..28].copy_from_slice(&4.0_f32.to_le_bytes());
let raw_segment = RawSegmentData::try_from(&bytes[..]).unwrap();
let segment = Segment::from(raw_segment);
assert_eq!(segment, Segment::LineTo((3.0, 4.0)));
}
#[test]
fn test_curve_command_deserialization() {
let mut bytes = [0x00; size_of::<RawSegmentData>()];
bytes[0..2].copy_from_slice(&0x03_u16.to_le_bytes());
bytes[4..8].copy_from_slice(&1.0_f32.to_le_bytes());
bytes[8..12].copy_from_slice(&2.0_f32.to_le_bytes());
bytes[12..16].copy_from_slice(&3.0_f32.to_le_bytes());
bytes[16..20].copy_from_slice(&4.0_f32.to_le_bytes());
bytes[20..24].copy_from_slice(&5.0_f32.to_le_bytes());
bytes[24..28].copy_from_slice(&6.0_f32.to_le_bytes());
let raw_segment = RawSegmentData::try_from(&bytes[..]).unwrap();
let segment = Segment::from(raw_segment);
assert_eq!(
segment,
Segment::CurveTo(((1.0, 2.0), (3.0, 4.0), (5.0, 6.0)))
);
}
#[test]
fn test_close_command_deserialization() {
let mut bytes = [0x00; size_of::<RawSegmentData>()];
bytes[0..2].copy_from_slice(&0x04_u16.to_le_bytes());
let raw_segment = RawSegmentData::try_from(&bytes[..]).unwrap();
let segment = Segment::from(raw_segment);
assert_eq!(segment, Segment::Close);
}
}