use crate::basics::{is_stop, VertexSource, PATH_CMD_MOVE_TO, PATH_CMD_STOP};
use crate::trans_affine::TransAffine;
#[derive(Clone, Copy, PartialEq)]
#[allow(dead_code)]
enum Status {
Initial,
Markers,
Polygon,
Stop,
}
pub struct ConvMarker<'a, ML: VertexSource, MS: VertexSource> {
marker_locator: &'a mut ML,
marker_shapes: &'a mut MS,
transform: TransAffine,
mtx: TransAffine,
status: Status,
marker: u32,
num_markers: u32,
}
impl<'a, ML: VertexSource, MS: VertexSource> ConvMarker<'a, ML, MS> {
pub fn new(marker_locator: &'a mut ML, marker_shapes: &'a mut MS) -> Self {
Self {
marker_locator,
marker_shapes,
transform: TransAffine::new(),
mtx: TransAffine::new(),
status: Status::Initial,
marker: 0,
num_markers: 1,
}
}
pub fn transform(&self) -> &TransAffine {
&self.transform
}
pub fn transform_mut(&mut self) -> &mut TransAffine {
&mut self.transform
}
}
impl<ML: VertexSource, MS: VertexSource> VertexSource for ConvMarker<'_, ML, MS> {
fn rewind(&mut self, _path_id: u32) {
self.status = Status::Initial;
self.marker = 0;
self.num_markers = 1;
}
fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
let mut cmd = PATH_CMD_MOVE_TO;
let mut x1: f64 = 0.0;
let mut y1: f64 = 0.0;
let mut x2: f64 = 0.0;
let mut y2: f64 = 0.0;
loop {
if is_stop(cmd) {
return cmd;
}
match self.status {
Status::Initial => {
if self.num_markers == 0 {
cmd = PATH_CMD_STOP;
continue;
}
self.marker_locator.rewind(self.marker);
self.marker += 1;
self.num_markers = 0;
self.status = Status::Markers;
continue;
}
Status::Markers => {
if is_stop(self.marker_locator.vertex(&mut x1, &mut y1)) {
self.status = Status::Initial;
continue;
}
if is_stop(self.marker_locator.vertex(&mut x2, &mut y2)) {
self.status = Status::Initial;
continue;
}
self.num_markers += 1;
self.mtx = self.transform;
self.mtx
.multiply(&TransAffine::new_rotation((y2 - y1).atan2(x2 - x1)));
self.mtx.multiply(&TransAffine::new_translation(x1, y1));
self.marker_shapes.rewind(self.marker - 1);
self.status = Status::Polygon;
continue;
}
Status::Polygon => {
cmd = self.marker_shapes.vertex(x, y);
if is_stop(cmd) {
cmd = PATH_CMD_MOVE_TO;
self.status = Status::Markers;
continue;
}
self.mtx.transform(x, y);
return cmd;
}
Status::Stop => {
cmd = PATH_CMD_STOP;
continue;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::basics::{is_move_to, is_vertex, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP};
struct SimpleLocator {
vertices: Vec<(f64, f64, u32)>,
pos: usize,
active: bool,
}
impl SimpleLocator {
fn new(vertices: Vec<(f64, f64, u32)>) -> Self {
Self {
vertices,
pos: 0,
active: false,
}
}
}
impl VertexSource for SimpleLocator {
fn rewind(&mut self, path_id: u32) {
if path_id == 0 {
self.pos = 0;
self.active = true;
} else {
self.active = false;
}
}
fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
if !self.active || self.pos >= self.vertices.len() {
return PATH_CMD_STOP;
}
let (vx, vy, cmd) = self.vertices[self.pos];
*x = vx;
*y = vy;
self.pos += 1;
cmd
}
}
struct TriangleMarker {
vertices: [(f64, f64, u32); 4],
pos: usize,
}
impl TriangleMarker {
fn new() -> Self {
Self {
vertices: [
(0.0, -5.0, PATH_CMD_MOVE_TO),
(5.0, 5.0, PATH_CMD_LINE_TO),
(-5.0, 5.0, PATH_CMD_LINE_TO),
(0.0, 0.0, PATH_CMD_STOP),
],
pos: 0,
}
}
}
impl VertexSource for TriangleMarker {
fn rewind(&mut self, _path_id: u32) {
self.pos = 0;
}
fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
if self.pos >= self.vertices.len() {
return PATH_CMD_STOP;
}
let (vx, vy, cmd) = self.vertices[self.pos];
*x = vx;
*y = vy;
self.pos += 1;
cmd
}
}
#[test]
fn test_single_marker_horizontal() {
let mut locator = SimpleLocator::new(vec![
(0.0, 0.0, PATH_CMD_MOVE_TO),
(10.0, 0.0, PATH_CMD_LINE_TO),
]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
let cmd = marker.vertex(&mut x, &mut y);
assert!(is_move_to(cmd));
let cmd = marker.vertex(&mut x, &mut y);
assert!(is_vertex(cmd));
let cmd = marker.vertex(&mut x, &mut y);
assert!(is_vertex(cmd));
}
#[test]
fn test_marker_at_origin_no_rotation() {
let mut locator = SimpleLocator::new(vec![
(0.0, 0.0, PATH_CMD_MOVE_TO),
(10.0, 0.0, PATH_CMD_LINE_TO),
]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
let cmd = marker.vertex(&mut x, &mut y);
assert!(is_move_to(cmd));
assert!((x - 0.0).abs() < 1e-8);
assert!((y - (-5.0)).abs() < 1e-8);
}
#[test]
fn test_marker_terminates() {
let mut locator = SimpleLocator::new(vec![
(0.0, 0.0, PATH_CMD_MOVE_TO),
(10.0, 0.0, PATH_CMD_LINE_TO),
]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
marker.vertex(&mut x, &mut y);
marker.vertex(&mut x, &mut y);
marker.vertex(&mut x, &mut y);
let cmd = marker.vertex(&mut x, &mut y);
assert!(is_stop(cmd));
}
#[test]
fn test_user_transform() {
let mut locator = SimpleLocator::new(vec![
(0.0, 0.0, PATH_CMD_MOVE_TO),
(10.0, 0.0, PATH_CMD_LINE_TO),
]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
*marker.transform_mut() = TransAffine::new_scaling_uniform(2.0);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
marker.vertex(&mut x, &mut y);
assert!((x - 0.0).abs() < 1e-8);
assert!((y - (-10.0)).abs() < 1e-8);
}
#[test]
fn test_empty_locator() {
let mut locator = SimpleLocator::new(vec![]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
let cmd = marker.vertex(&mut x, &mut y);
assert!(is_stop(cmd));
}
#[test]
fn test_single_vertex_locator() {
let mut locator = SimpleLocator::new(vec![(0.0, 0.0, PATH_CMD_MOVE_TO)]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
let cmd = marker.vertex(&mut x, &mut y);
assert!(is_stop(cmd));
}
#[test]
fn test_marker_vertical_edge() {
let mut locator = SimpleLocator::new(vec![
(0.0, 0.0, PATH_CMD_MOVE_TO),
(0.0, 10.0, PATH_CMD_LINE_TO),
]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
marker.vertex(&mut x, &mut y);
assert!((x - 5.0).abs() < 1e-8);
assert!((y - 0.0).abs() < 1e-8);
}
#[test]
fn test_rewind_resets() {
let mut locator = SimpleLocator::new(vec![
(0.0, 0.0, PATH_CMD_MOVE_TO),
(10.0, 0.0, PATH_CMD_LINE_TO),
]);
let mut shape = TriangleMarker::new();
let mut marker = ConvMarker::new(&mut locator, &mut shape);
marker.rewind(0);
let mut x = 0.0;
let mut y = 0.0;
marker.vertex(&mut x, &mut y);
marker.rewind(0);
let mut x2 = 0.0;
let mut y2 = 0.0;
marker.vertex(&mut x2, &mut y2);
assert_eq!(x, x2);
assert_eq!(y, y2);
}
}