use crate::data::PolyData;
#[derive(Debug, Clone)]
pub struct GlyphInstance {
pub position: [f32; 3],
pub scale: f32,
pub color: Option<[f32; 3]>,
}
#[derive(Debug, Clone)]
pub struct InstancedGlyphs {
pub template: PolyData,
pub instances: Vec<GlyphInstance>,
}
impl InstancedGlyphs {
pub fn new(template: PolyData) -> Self {
Self {
template,
instances: Vec::new(),
}
}
pub fn add(&mut self, position: [f32; 3]) {
self.instances.push(GlyphInstance {
position,
scale: 1.0,
color: None,
});
}
pub fn add_with(&mut self, position: [f32; 3], scale: f32, color: [f32; 3]) {
self.instances.push(GlyphInstance {
position,
scale,
color: Some(color),
});
}
pub fn flatten(&self) -> PolyData {
let tpl_npts = self.template.points.len();
if tpl_npts == 0 || self.instances.is_empty() {
return PolyData::new();
}
let mut result = PolyData::new();
for inst in &self.instances {
let base = result.points.len() as i64;
for i in 0..tpl_npts {
let p = self.template.points.get(i);
result.points.push([
p[0] * inst.scale as f64 + inst.position[0] as f64,
p[1] * inst.scale as f64 + inst.position[1] as f64,
p[2] * inst.scale as f64 + inst.position[2] as f64,
]);
}
for cell in self.template.polys.iter() {
let offset_cell: Vec<i64> = cell.iter().map(|&id| id + base).collect();
result.polys.push_cell(&offset_cell);
}
}
result
}
pub fn to_flat_poly_data(&self, glyph: &PolyData) -> PolyData {
let tpl_npts = glyph.points.len();
if tpl_npts == 0 || self.instances.is_empty() {
return PolyData::new();
}
let mut result = PolyData::new();
for inst in &self.instances {
let base = result.points.len() as i64;
for i in 0..tpl_npts {
let p = glyph.points.get(i);
result.points.push([
p[0] * inst.scale as f64 + inst.position[0] as f64,
p[1] * inst.scale as f64 + inst.position[1] as f64,
p[2] * inst.scale as f64 + inst.position[2] as f64,
]);
}
for cell in glyph.polys.iter() {
let offset_cell: Vec<i64> = cell.iter().map(|&id| id + base).collect();
result.polys.push_cell(&offset_cell);
}
}
result
}
pub fn len(&self) -> usize {
self.instances.len()
}
pub fn is_empty(&self) -> bool {
self.instances.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flatten_instances() {
let template = PolyData::from_triangles(
vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
vec![[0, 1, 2]],
);
let mut glyphs = InstancedGlyphs::new(template);
glyphs.add([0.0, 0.0, 0.0]);
glyphs.add([5.0, 0.0, 0.0]);
glyphs.add([0.0, 5.0, 0.0]);
let result = glyphs.flatten();
assert_eq!(result.points.len(), 9); assert_eq!(result.polys.num_cells(), 3);
let p = result.points.get(3);
assert!((p[0] - 5.0).abs() < 1e-10);
}
#[test]
fn scaled_instances() {
let template = PolyData::from_triangles(
vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
vec![[0, 1, 2]],
);
let mut glyphs = InstancedGlyphs::new(template);
glyphs.add_with([0.0, 0.0, 0.0], 2.0, [1.0, 0.0, 0.0]);
let result = glyphs.flatten();
let p = result.points.get(1);
assert!((p[0] - 2.0).abs() < 1e-10); }
#[test]
fn to_flat_poly_data_external_glyph() {
let template = PolyData::from_triangles(
vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
vec![[0, 1, 2]],
);
let glyph = PolyData::from_triangles(
vec![[0.0, 0.0, 0.0], [2.0, 0.0, 0.0], [0.0, 2.0, 0.0]],
vec![[0, 1, 2]],
);
let mut glyphs = InstancedGlyphs::new(template);
glyphs.add([0.0, 0.0, 0.0]);
glyphs.add([10.0, 0.0, 0.0]);
let result = glyphs.to_flat_poly_data(&glyph);
assert_eq!(result.points.len(), 6); assert_eq!(result.polys.num_cells(), 2);
let p = result.points.get(4); assert!((p[0] - 12.0).abs() < 1e-10);
}
#[test]
fn empty_instances() {
let template = PolyData::from_triangles(
vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
vec![[0, 1, 2]],
);
let glyphs = InstancedGlyphs::new(template);
assert!(glyphs.is_empty());
let result = glyphs.flatten();
assert_eq!(result.points.len(), 0);
}
}