131 lines
3.9 KiB
Rust
131 lines
3.9 KiB
Rust
pub use super::*;
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
struct SegmentPoint {
|
|
input: RadianPoint,
|
|
cartesian: Cartesian,
|
|
projected: ProjectedPoint,
|
|
}
|
|
|
|
impl SegmentPoint {
|
|
fn new<P>(projection: P, input: RadianPoint) -> Self
|
|
where
|
|
P: Projection,
|
|
{
|
|
SegmentPoint {
|
|
input,
|
|
cartesian: input.into(),
|
|
projected: projection.project(input),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn resample_segment<P, S>(sink: &mut S, projection: &P, from: SegmentPoint, to: SegmentPoint, stroke: bool, recursion_limit: u32, resolution: f32)
|
|
where
|
|
P: Projection,
|
|
S: DrawPath<Point = ProjectedPoint>,
|
|
{
|
|
let cos_min_distance = 30.0f32.to_radians().cos();
|
|
|
|
let dx = to.projected.x - from.projected.x;
|
|
let dy = to.projected.y - from.projected.y;
|
|
let dist_square = dx*dx + dy*dy;
|
|
if dist_square > (4.0 * resolution) && recursion_limit > 1 {
|
|
let mid_cartesian = {
|
|
// normalize mid point between from and to (onto sphere)
|
|
let s = from.cartesian + to.cartesian; // normalizing anyway, drop `*0.5`
|
|
s / (s * s).sqrt()
|
|
};
|
|
let mid_lambda = if (mid_cartesian.c.abs() - 1.0).abs() < F32_PRECISION
|
|
|| (from.input.lambda - to.input.lambda).abs() < F32_PRECISION
|
|
{
|
|
// close to poles (a and b will be near zero, atan2 would fail)
|
|
// or from/to lambdas close together (atan2 should work though in this case, but it would convert -pi to pi)
|
|
(from.input.lambda + to.input.lambda) / 2.0
|
|
} else {
|
|
f32::atan2(mid_cartesian.b, mid_cartesian.a)
|
|
};
|
|
let mid_input = RadianPoint {
|
|
phi: mid_cartesian.c.asin(),
|
|
lambda: mid_lambda,
|
|
};
|
|
let mid = SegmentPoint {
|
|
input: mid_input,
|
|
cartesian: mid_cartesian,
|
|
projected: projection.project(mid_input),
|
|
};
|
|
|
|
let dx2 = mid.projected.x - from.projected.x;
|
|
let dy2 = mid.projected.y - from.projected.y;
|
|
// (dy, -dx): orthogonal vector to (dx, dy)
|
|
// norm(dy, -dx) * (dx2, dy2): (projected) distance of mid from line between from and to
|
|
let mid_line_dist_square = {
|
|
let d = dy*dx2 - dx*dy2;
|
|
(d * d) / dist_square
|
|
};
|
|
// norm(dx, dy) * (dx2, dy2): (projected) "progress" of mid *on* the line between from and to
|
|
// let mid_progress = (dx*dx2 + dy*dy2) / dist_square;
|
|
if mid_line_dist_square > resolution // perpendicular projected distance
|
|
// /* this is broken and probably not needed */ || (mid_progress - 0.5).abs() > 0.3 // midpoint close to an end
|
|
|| (to.cartesian * from.cartesian) < cos_min_distance // angular distance
|
|
{
|
|
resample_segment(sink, projection, from, mid, stroke, recursion_limit - 1, resolution);
|
|
sink.line(mid.projected, stroke);
|
|
resample_segment(sink, projection, mid, to, stroke, recursion_limit - 1, resolution);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ResamplePath<P: Projection, S> {
|
|
projection: P,
|
|
sink: S,
|
|
resolution: f32,
|
|
recursion_limit: u32,
|
|
start_prev: Option<(bool, SegmentPoint, SegmentPoint)>,
|
|
}
|
|
|
|
impl<P: Projection, S: DrawPath<Point=ProjectedPoint>> ResamplePath<P, S> {
|
|
pub fn new(projection: P, sink: S, resolution: f32) -> Self {
|
|
Self {
|
|
projection,
|
|
sink,
|
|
resolution,
|
|
recursion_limit: 16,
|
|
start_prev: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<P: Projection, S: DrawPath<Point=ProjectedPoint>> PathTransformer for ResamplePath<P, S> {
|
|
type Sink = S;
|
|
type Point = RadianPoint;
|
|
|
|
fn sink(&mut self) -> &mut Self::Sink {
|
|
&mut self.sink
|
|
}
|
|
|
|
fn transform_line(&mut self, to: Self::Point, stroke: bool) {
|
|
let to = SegmentPoint::new(&self.projection, to);
|
|
if let Some((_init_stroke, _start, prev)) = &mut self.start_prev {
|
|
resample_segment(&mut self.sink, &self.projection, *prev, to, stroke, self.recursion_limit, self.resolution);
|
|
self.sink.line(to.projected, stroke);
|
|
*prev = to;
|
|
} else {
|
|
self.sink.line(to.projected, stroke);
|
|
self.start_prev = Some((stroke, to, to));
|
|
}
|
|
}
|
|
|
|
fn transform_ring_end(&mut self) {
|
|
if let Some((init_stroke, start, prev)) = self.start_prev.take() {
|
|
resample_segment(&mut self.sink, &self.projection, prev, start, init_stroke, self.recursion_limit, self.resolution);
|
|
}
|
|
self.sink().ring_end();
|
|
}
|
|
|
|
fn transform_path_end(&mut self) {
|
|
self.start_prev = None;
|
|
self.sink().path_end();
|
|
}
|
|
}
|