#![allow(dead_code)]
use robust::{Coord, orient2d};
use togo::prelude::*;
use crate::offsetraw::OffsetRaw;
const ZERO: f64 = 0f64;
const EPS_CONNECT_RAW: f64 = 1e-8;
#[doc(hidden)]
pub fn offset_connect_raw(raws: &Vec<Vec<OffsetRaw>>, off: f64) -> Vec<Vec<Arc>> {
let mut res = Vec::with_capacity(raws.len());
for raw in raws.iter() {
res.push(offset_connect_raw_single(raw, off));
}
res
}
#[doc(hidden)]
pub const ID_PADDING: usize = 100000;
pub fn offset_connect_raw_single(raws: &Vec<OffsetRaw>, off: f64) -> Vec<Arc> {
let mut res = Vec::with_capacity(raws.len() + 1);
if off < ZERO {
return res;
}
if raws.is_empty() {
return res;
}
let last = raws.len() - 1;
for i in 0..last {
let old = raws[i].arc;
let old_next = raws[i + 1].arc;
let g0 = raws[i].g;
let g1 = raws[i + 1].g;
let orig = raws[i].orig;
let (mut connect, check, convex) = arc_connect_new(old, old_next, g0, g1, orig, off);
connect.id(ID_PADDING + old.id);
if convex {
if check {
res.push(connect);
} else {
let mut small = arcseg(connect.a, connect.b);
small.id(ID_PADDING + old.id);
res.push(small);
}
}
}
let last = raws.last().unwrap();
let old = last.arc;
let raw_next = raws.first().unwrap();
let old_next = raw_next.arc;
let g0 = last.g;
let g1 = raw_next.g;
let orig = last.orig;
let (mut connect, check, convex) = arc_connect_new(old, old_next, g0, g1, orig, off);
connect.id(ID_PADDING + old.id);
if convex {
if check {
res.push(connect);
} else {
let mut small = arcseg(connect.a, connect.b);
small.id(ID_PADDING + old.id);
res.push(small);
}
}
res
}
fn arc_connect_new(
old: Arc,
old_next: Arc,
g0: f64,
g1: f64,
orig: Point,
off: f64,
) -> (Arc, bool, bool) {
let seg: Arc;
let convex: bool;
let b = Coord {
x: orig.x,
y: orig.y,
};
if g0 >= ZERO && g1 >= ZERO {
seg = arc(old.b, old_next.a, orig, off);
let a = Coord {
x: old.b.x,
y: old.b.y,
};
let c = Coord {
x: old_next.a.x,
y: old_next.a.y,
};
convex = orient2d(a, b, c) < ZERO;
} else if g0 >= ZERO && g1 < ZERO {
seg = arc(old.b, old_next.b, orig, off);
let a = Coord {
x: old.b.x,
y: old.b.y,
};
let c = Coord {
x: old_next.b.x,
y: old_next.b.y,
};
convex = orient2d(a, b, c) < ZERO;
} else if g0 < ZERO && g1 >= ZERO {
seg = arc(old.a, old_next.a, orig, off);
let a = Coord {
x: old.a.x,
y: old.a.y,
};
let c = Coord {
x: old_next.a.x,
y: old_next.a.y,
};
convex = orient2d(a, b, c) < ZERO;
} else {
seg = arc(old.a, old_next.b, orig, off);
let a = Coord {
x: old.a.x,
y: old.a.y,
};
let c = Coord {
x: old_next.b.x,
y: old_next.b.y,
};
convex = orient2d(a, b, c) < ZERO;
}
(seg, seg.is_valid(EPS_CONNECT_RAW), convex)
}
#[cfg(test)]
mod test_offset_connect_raw {
use crate::{
poly::pline_01, offset::svg_offset_raws, offset_arcs_raw::{offset_polyline_raw, poly_to_raws}
};
use super::*;
#[test]
#[ignore = "svg output"]
fn test_offset_connect_segments_arcs_00_svg() {
let pline = vec![vec![
pvertex(point(100.0, 100.0), 0.5),
pvertex(point(200.0, 200.0), 0.5),
]];
let poly_raws = poly_to_raws(&pline);
let mut svg = svg(300.0, 350.0);
svg_offset_raws(&mut svg, &poly_raws, "red");
let off: f64 = 52.25;
let offset_raw = offset_polyline_raw(&poly_raws, off);
svg_offset_raws(&mut svg, &offset_raw, "blue");
let offset_connect = offset_connect_raw(&offset_raw, off);
svg.arclines(&offset_connect, "violet");
svg.write();
}
#[test]
#[ignore = "svg output"]
fn test_offset_connect_segments_arcs_01() {
let pline = vec![vec![
pvertex(point(100.0, 210.0), 0.5),
pvertex(point(280.0, 180.0), 5.0),
pvertex(point(300.0, 200.0), -0.5),
pvertex(point(200.0, 300.0), -0.5),
pvertex(point(100.0, 300.0), 0.5),
pvertex(point(0.0, 200.0), 0.5),
]];
let poly_raws = poly_to_raws(&pline);
let mut svg = svg(300.0, 400.0);
svg_offset_raws(&mut svg, &poly_raws, "red");
let off: f64 = 22.0;
let offset_raw = offset_polyline_raw(&poly_raws, off);
svg_offset_raws(&mut svg, &offset_raw, "blue");
let offset_connect = offset_connect_raw(&offset_raw, off);
svg.arclines(&offset_connect, "violet");
svg.write();
}
#[test]
#[ignore = "svg output"]
fn test_offset_connect_segments_lines_01() {
}
#[test]
#[ignore = "svg output"]
fn test_offset_connect_segments_02() {
}
#[test]
fn test_offset_connect_segments_03() {
let plines = pline_01();
let mut svg = svg(400.0, 600.0);
svg.polyline(&plines[0], "grey");
let off: f64 = 16.00;
let poly_raws = poly_to_raws(&plines);
let offset_raw1 = offset_polyline_raw(&poly_raws, off);
let offset_raw2 = offset_connect_raw(&offset_raw1, off);
svg_offset_raws(&mut svg, &offset_raw1, "red");
svg.arclines(&offset_raw2, "blue");
svg.write();
}
}
#[cfg(test)]
mod test_offset_connect_raw_single {
use super::*;
#[test]
fn test_empty_input() {
let raws = vec![];
let result = offset_connect_raw_single(&raws, 5.0);
assert_eq!(result.len(), 0);
}
#[test]
fn test_single_element() {
let arc = arcseg(point(0.0, 0.0), point(1.0, 0.0));
let raw = OffsetRaw::new(arc, point(0.0, 0.0), 0.0);
let raws = vec![raw];
let result = offset_connect_raw_single(&raws, 5.0);
assert!(result.len() <= 1);
}
#[test]
fn test_two_line_segments_positive_g() {
let offset_dist = 0.5;
let arc1 = arcseg(point(0.0, offset_dist), point(2.0, offset_dist));
let arc2 = arcseg(point(2.0 + offset_dist, 0.5), point(2.0 + offset_dist, 2.5));
let raw1 = OffsetRaw::new(arc1, point(1.0, 0.0), 0.0); let raw2 = OffsetRaw::new(arc2, point(2.0, 1.5), 0.0); let raws = vec![raw1, raw2];
let result = offset_connect_raw_single(&raws, offset_dist);
assert!(result.len() <= 2);
}
#[test]
fn test_two_line_segments_mixed_g() {
let arc1 = arcseg(point(0.0, 1.0), point(2.5, 1.0));
let arc2 = arcseg(point(3.5, 1.2), point(6.0, 1.2));
let raw1 = OffsetRaw::new(arc1, point(1.25, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(4.75, 0.0), 0.0);
let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_two_line_segments_negative_g() {
let arc1 = arcseg(point(0.0, 0.5), point(1.5, 0.5));
let arc2 = arcseg(point(2.5, 0.7), point(4.0, 0.7));
let raw1 = OffsetRaw::new(arc1, point(0.75, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(3.25, 0.0), 0.0);
let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_square_path() {
let offset_dist = 0.3;
let arc1 = arcseg(point(-offset_dist, -offset_dist), point(1.0 + offset_dist, -offset_dist)); let arc2 = arcseg(point(1.0 + offset_dist, 0.0), point(1.0 + offset_dist, 1.0)); let arc3 = arcseg(point(1.0, 1.0 + offset_dist), point(-offset_dist, 1.0 + offset_dist)); let arc4 = arcseg(point(-offset_dist, 1.0), point(-offset_dist, 0.0));
let raw1 = OffsetRaw::new(arc1, point(0.5, 0.0), 0.0); let raw2 = OffsetRaw::new(arc2, point(1.0, 0.5), 0.0); let raw3 = OffsetRaw::new(arc3, point(0.5, 1.0), 0.0); let raw4 = OffsetRaw::new(arc4, point(0.0, 0.5), 0.0); let raws = vec![raw1, raw2, raw3, raw4];
let result = offset_connect_raw_single(&raws, offset_dist);
assert!(result.len() <= 4);
}
#[test]
fn test_triangle_path() {
let arc1 = arcseg(point(0.0, 0.0), point(2.0, 0.0)); let arc2 = arcseg(point(2.2, 0.2), point(1.2, 1.8)); let arc3 = arcseg(point(0.8, 1.8), point(-0.2, 0.2));
let raw1 = OffsetRaw::new(arc1, point(1.0, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(1.7, 1.0), 0.0);
let raw3 = OffsetRaw::new(arc3, point(0.3, 1.0), 0.0);
let raws = vec![raw1, raw2, raw3];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 3);
}
#[test]
fn test_arc_segments() {
let arc1 = arc_from_bulge(point(0.0, 0.0), point(2.0, 0.0), 0.3);
let arc2 = arc_from_bulge(point(3.0, 0.0), point(5.0, 0.0), -0.3);
assert!(arc1.is_valid(1e-10));
assert!(arc2.is_valid(1e-10));
let raw1 = OffsetRaw::new(arc1, point(1.0, 0.5), 1.0);
let raw2 = OffsetRaw::new(arc2, point(4.0, 0.5), 1.0);
let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_zero_offset() {
let arc1 = arcseg(point(0.0, 0.0), point(1.0, 0.0));
let arc2 = arcseg(point(2.0, 0.0), point(3.0, 0.0));
let raw1 = OffsetRaw::new(arc1, point(0.5, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(2.5, 0.0), 0.0);
let raws = vec![raw1, raw2];
let offset = 0.0;
let result = offset_connect_raw_single(&raws, offset);
assert_eq!(result.len(), 0);
}
#[test]
fn test_negative_offset() {
let arc1 = arcseg(point(0.0, 0.0), point(1.0, 0.0));
let arc2 = arcseg(point(2.0, 0.0), point(3.0, 0.0));
let raw1 = OffsetRaw::new(arc1, point(0.5, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(2.5, 0.0), 0.0);
let raws = vec![raw1, raw2];
let offset = -1.0;
let result = offset_connect_raw_single(&raws, offset);
assert_eq!(result.len(), 0);
}
#[test]
fn test_large_offset() {
let arc1 = arcseg(point(0.0, 0.0), point(1.0, 0.0));
let arc2 = arcseg(point(2.0, 0.0), point(3.0, 0.0));
let raw1 = OffsetRaw::new(arc1, point(0.5, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(2.5, 0.0), 0.0);
let raws = vec![raw1, raw2];
let offset = 1000.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_disconnected_segments() {
let arc1 = arcseg(point(0.0, 0.0), point(1.0, 0.0));
let arc2 = arcseg(point(3.0, 0.0), point(4.0, 0.0));
let raw1 = OffsetRaw::new(arc1, point(0.5, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(3.5, 0.0), 0.0);
let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_realistic_offset_gaps_straight() {
let offset_dist = 1.0;
let arc1 = arcseg(point(1.0, offset_dist), point(3.0, offset_dist));
let arc2 = arcseg(point(4.5, offset_dist), point(6.5, offset_dist));
let arc3 = arcseg(point(8.0, offset_dist), point(10.0, offset_dist));
let raw1 = OffsetRaw::new(arc1, point(2.0, 0.0), 0.0); let raw2 = OffsetRaw::new(arc2, point(5.5, 0.0), 0.0);
let raw3 = OffsetRaw::new(arc3, point(9.0, 0.0), 0.0);
let raws = vec![raw1, raw2, raw3];
let result = offset_connect_raw_single(&raws, offset_dist);
assert!(result.len() <= raws.len());
}
#[test]
fn test_realistic_offset_gaps_corner() {
let offset_dist = 1.0;
let arc1 = arcseg(point(0.0, offset_dist), point(2.0, offset_dist));
let arc2 = arcseg(point(2.0 + offset_dist, 1.0), point(2.0 + offset_dist, 3.0));
let raw1 = OffsetRaw::new(arc1, point(1.0, 0.0), 0.0); let raw2 = OffsetRaw::new(arc2, point(2.0, 2.0), 0.0); let raws = vec![raw1, raw2];
let result = offset_connect_raw_single(&raws, offset_dist);
assert!(result.len() <= 2);
}
#[test]
fn test_realistic_large_gaps() {
let arc1 = arcseg(point(0.0, 1.0), point(2.0, 1.0));
let arc2 = arcseg(point(8.0, 1.0), point(10.0, 1.0));
let raw1 = OffsetRaw::new(arc1, point(1.0, 0.0), 1.0);
let raw2 = OffsetRaw::new(arc2, point(9.0, 0.0), 1.0);
let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_realistic_curved_segments_with_gaps() {
let arc1 = arc_from_bulge(point(0.0, 0.0), point(2.0, 2.0), 0.5);
let arc2 = arc_from_bulge(point(4.0, 2.0), point(6.0, 0.0), -0.5);
assert!(arc1.is_valid(1e-10));
assert!(arc2.is_valid(1e-10));
let raw1 = OffsetRaw::new(arc1, point(1.0, 1.0), 1.0);
let raw2 = OffsetRaw::new(arc2, point(5.0, 1.0), 1.0);
let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_very_small_segments() {
let arc1 = arcseg(point(0.0, 0.0), point(0.001, 0.0));
let arc2 = arcseg(point(0.002, 0.0), point(0.003, 0.0));
let raw1 = OffsetRaw::new(arc1, point(0.0005, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(0.0025, 0.0), 0.0);
let raws = vec![raw1, raw2];
let offset = 0.1;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_concave_vs_convex_angles() {
let arc1 = arcseg(point(0.0, 0.0), point(2.0, 0.0));
let arc2 = arcseg(point(3.0, -1.0), point(5.0, -1.0));
let raw1 = OffsetRaw::new(arc1, point(1.0, 0.0), 0.0);
let raw2 = OffsetRaw::new(arc2, point(4.0, 0.0), -0.5); let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
assert!(result.len() <= 2);
}
#[test]
fn test_all_g_value_combinations() {
let arc1 = arc_from_bulge(point(0.0, 0.0), point(1.0, 0.0), 0.5);
let arc2 = arc_from_bulge(point(2.0, 0.0), point(3.0, 0.0), 0.5);
assert!(arc1.is_valid(1e-10));
assert!(arc2.is_valid(1e-10));
let g_combinations = vec![
(1.0, 1.0), (1.0, -1.0), (-1.0, 1.0), (-1.0, -1.0), ];
for (g1, g2) in g_combinations {
let raw1 = OffsetRaw::new(arc1, point(0.5, 0.0), g1);
let raw2 = OffsetRaw::new(arc2, point(2.5, 0.0), g2);
let raws = vec![raw1, raw2];
let result = offset_connect_raw_single(&raws, 1.0);
assert!(result.len() <= 2);
}
}
#[test]
fn test_id_assignment() {
let arc1 = arcseg(point(0.0, 0.0), point(1.0, 0.0));
let arc2 = arcseg(point(2.0, 0.0), point(3.0, 0.0));
let mut raw1 = OffsetRaw::new(arc1, point(0.5, 0.0), 0.0);
let mut raw2 = OffsetRaw::new(arc2, point(2.5, 0.0), 0.0);
raw1.arc.id(5);
raw2.arc.id(10);
let raws = vec![raw1, raw2];
let offset = 1.0;
let result = offset_connect_raw_single(&raws, offset);
if !result.is_empty() {
assert!(result.len() >= 1);
}
}
}