use crate::data_structure::Document;
use crate::render::tessellation::{document_bbox, entity_fills, entity_polylines};
const SPAN: i32 = 30000; const MARGIN: i32 = 500;
fn put_u16(buf: &mut Vec<u8>, v: u16) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn put_i16(buf: &mut Vec<u8>, v: i16) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn put_u32(buf: &mut Vec<u8>, v: u32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn rec_header(buf: &mut Vec<u8>, words: u32, func: u16) {
put_u32(buf, words);
put_u16(buf, func);
}
pub fn export(doc: &Document) -> Result<Vec<u8>, String> {
if doc.entity_count() == 0 {
return Err("Canvas is empty, nothing to export".to_string());
}
let Some((min, max)) = document_bbox(doc) else {
return Err("Canvas is empty, nothing to export".to_string());
};
let mut polys: Vec<Vec<(i16, i16)>> = Vec::new();
let mut fills: Vec<(Vec<(i16, i16)>, (u8, u8, u8))> = Vec::new();
let w = (max.x - min.x).max(1e-9);
let h = (max.y - min.y).max(1e-9);
let inner = (SPAN - 2 * MARGIN) as f64;
let map = |x: f64, y: f64| -> (i16, i16) {
let px = MARGIN as f64 + (x - min.x) / w * inner;
let py = MARGIN as f64 + (max.y - y) / h * inner;
(px.round() as i16, py.round() as i16)
};
for entity in doc.entities().values() {
for (pts, color) in entity_fills(entity) {
if pts.len() < 3 {
continue;
}
let scr: Vec<(i16, i16)> = pts.iter().map(|p| map(p.x, p.y)).collect();
fills.push((scr, color));
}
for (pts, closed) in entity_polylines(entity) {
if pts.len() < 2 {
continue;
}
let mut scr: Vec<(i16, i16)> = pts.iter().map(|p| map(p.x, p.y)).collect();
if closed {
scr.push(scr[0]);
}
polys.push(scr);
}
}
if polys.is_empty() && fills.is_empty() {
return Err("Nothing to draw".to_string());
}
let mut recs: Vec<u8> = Vec::new();
let mut max_record: u32 = 3;
rec_header(&mut recs, 5, 0x020B);
put_i16(&mut recs, 0);
put_i16(&mut recs, 0);
rec_header(&mut recs, 5, 0x020C);
put_i16(&mut recs, SPAN as i16);
put_i16(&mut recs, SPAN as i16);
rec_header(&mut recs, 8, 0x02FA);
put_i16(&mut recs, 0); put_i16(&mut recs, 30); put_i16(&mut recs, 0);
put_u32(&mut recs, 0x00000000); max_record = max_record.max(8);
for (_, color) in &fills {
rec_header(&mut recs, 7, 0x02FC);
put_i16(&mut recs, 0); put_u32(
&mut recs,
((color.2 as u32) << 16) | ((color.1 as u32) << 8) | color.0 as u32, );
put_i16(&mut recs, 0); max_record = max_record.max(7);
}
rec_header(&mut recs, 4, 0x012D);
put_u16(&mut recs, 0);
for (i, (pts, _)) in fills.iter().enumerate() {
rec_header(&mut recs, 4, 0x012D);
put_u16(&mut recs, 1 + i as u16);
let n = pts.len() as u32;
let words = 4 + 2 * n;
rec_header(&mut recs, words, 0x0324);
put_u16(&mut recs, n as u16);
for &(x, y) in pts {
put_i16(&mut recs, x);
put_i16(&mut recs, y);
}
max_record = max_record.max(words);
}
rec_header(&mut recs, 4, 0x012D);
put_u16(&mut recs, 0);
for pts in &polys {
let n = pts.len() as u32;
let words = 4 + 2 * n;
rec_header(&mut recs, words, 0x0325);
put_u16(&mut recs, n as u16);
for &(x, y) in pts {
put_i16(&mut recs, x);
put_i16(&mut recs, y);
}
max_record = max_record.max(words);
}
rec_header(&mut recs, 4, 0x01F0);
put_u16(&mut recs, 0);
rec_header(&mut recs, 3, 0x0000);
put_u16(&mut recs, 0);
let total_words = ((18 + recs.len()) / 2) as u32;
let mut meta: Vec<u8> = Vec::new();
put_u16(&mut meta, 1); put_u16(&mut meta, 9); put_u16(&mut meta, 0x0300); put_u32(&mut meta, total_words);
put_u16(&mut meta, 1 + fills.len() as u16); put_u32(&mut meta, max_record);
put_u16(&mut meta, 0);
meta.extend_from_slice(&recs);
let mut out: Vec<u8> = Vec::new();
put_u32(&mut out, 0x9AC6_CDD7); put_i16(&mut out, 0); put_i16(&mut out, 0); put_i16(&mut out, 0); put_i16(&mut out, SPAN as i16); put_i16(&mut out, SPAN as i16); put_i16(&mut out, 1440); put_i16(&mut out, 0); let mut cs: u16 = 0;
for i in 0..10 {
cs ^= u16::from_le_bytes([out[i * 2], out[i * 2 + 1]]);
}
put_u16(&mut out, cs);
out.extend_from_slice(&meta);
Ok(out)
}