1extern crate core;
87
88use core::ops::Range;
89use core::hint::unreachable_unchecked;
90use core::fmt;
91
92use wasm_bindgen::prelude::*;
93use serde::{Serialize, Deserialize};
94use serde_wasm_bindgen::{to_value, from_value};
95
96#[cfg(feature = "double_precision")]
97type Float = f32;
98#[cfg(not(feature = "double_precision"))]
99type Float = f64;
100
101#[wasm_bindgen]
103#[derive(Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
104pub struct Point { pub x: Float, pub y: Float }
105
106impl fmt::Debug for Point {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        write!(f, "{{\"x\":{},\"y\":{}}}", self.x, self.y)
109    }
110}
111
112#[wasm_bindgen]
113impl Point {
114    #[wasm_bindgen(constructor)]
115    pub fn new(x: f64, y: f64) -> Point {
116        Point { x, y }
117    }
118    #[inline]
119    fn add(&self, p: Point) -> Point {
120        Point {
121            x: self.x + p.x,
122            y: self.y + p.y
123        }
124    }
125
126    #[inline]
127    fn subtract(&self, p: Point) -> Point {
128        Point {
129            x: self.x - p.x,
130            y: self.y - p.y
131        }
132    }
133
134    #[inline]
135    fn multiply(&self, f: Float) -> Point {
136        Point {
137            x: self.x * f,
138            y: self.y * f
139        }
140    }
141
142    #[inline]
143    fn negate(&self) -> Point {
144        Point {
145            x: -self.x,
146            y: -self.y
147        }
148    }
149
150    #[inline]
151    fn distance(&self, p: Point) -> Float {
152        let dx = p.x - self.x;
153        let dy = p.y - self.y;
154        dx.hypot(dy)
155    }
156
157    #[inline]
158    fn dot(&self, p: Point) -> Float {
159        self.x * p.x + self.y * p.y
160    }
161
162    #[inline]
163    fn normalize(&self, length: Float) -> Point {
164        let current =  self.x.hypot(self.y);
165        let scale = if current.abs() > Float::EPSILON { length / current } else { 0.0 };
166        let res = Point { x: self.x * scale, y: self.y * scale };
167        res
168    }
169}
170
171pub const DEFAULT_TOLERANCE: f32 = 2.5;
173pub const TARGET_AUTO_SCALE_AREA: Float = 1000000.0;
174
175#[wasm_bindgen]
195pub fn simplify_js(points_js: &JsValue, tolerance: f64, auto_scale_for_precision: bool) -> JsValue {
196    let mut points: Vec<Point> = from_value(points_js.clone()).unwrap();
198
199    let mut scale = 1.0;
200    if auto_scale_for_precision {
202        let mut area = 0.0;
203        let mut j = points.len() - 1;
204        for i in 0..points.len() {
205            area += (points[j].x + points[i].x) * (points[j].y - points[i].y);
206            j = i;
207        }
208        area *= 0.5;
209        area = area.abs();
210
211        scale = TARGET_AUTO_SCALE_AREA / area;
213
214        for p in points.iter_mut() {
215            p.x *= scale;
216            p.y *= scale;
217        }
218    }
219
220    let scaled_tolerance = tolerance as Float * scale;
221
222    let mut simplified = simplify(&points, scaled_tolerance);
224
225    if auto_scale_for_precision {
227        for p in simplified.iter_mut() {
228            p.x /= scale;
229            p.y /= scale;
230        }
231    }
232
233    to_value(&simplified).unwrap()
235}
236
237pub fn simplify(points: &[Point], tolerance: Float) -> Vec<Point> {
253
254    let mut cur_points = points.windows(2).filter_map(|w| {
256        let first = w.get(0)?;
257        let next = w.get(1)?;
258        if first == next { None } else { Some(*first) }
259    }).collect::<Vec<Point>>();
260
261    if let (Some(last_minus_one), Some(last)) = (points.get(points.len() - 2), points.last()) {
263        if last_minus_one != last { cur_points.push(*last); }
264    }
265
266    if cur_points.len() < 2 {
268        return cur_points;
269    } else if cur_points.len() == 2 {
270        return vec![cur_points[0], cur_points[1]];
271    }
272
273    let closed = cur_points.first() == cur_points.last();
275
276    if closed {
279        let last = match points.last().copied() {
280            Some(s) => s,
281            None => unsafe { unreachable_unchecked() },
282        };
283        let first = match points.first().copied() {
284            Some(s) => s,
285            None => unsafe { unreachable_unchecked() },
286        };
287        let mut new_cur_points = vec![last];
288        new_cur_points.extend(cur_points.drain(..));
289        new_cur_points.push(first);
290        cur_points = new_cur_points;
291    }
292
293    fit(&cur_points[..], tolerance)
294}
295
296#[derive(Clone)]
297struct Split {
298    global_range: Range<usize>,
299    tan1: Point,
300    tan2: Point,
301}
302
303#[inline]
304fn fit(points: &[Point], tolerance: Float) -> Vec<Point> {
305
306    let mut segments = Vec::new();
309    let distances = chord_length_parametrize(points);
310
311    if distances.len() != points.len() {
312        return segments; }
314
315    if points.len() == 0 {
317        return Vec::new();
318    } else if points.len() == 1 {
319        return vec![points[0]];
320    } else if points.len() == 2 {
321        return vec![points[0], points[1]];
322    } else {
323        let mut splits_to_eval = vec![Split {
324            global_range: 0..points.len(),
325            tan1: points[1].subtract(points[0]),
326            tan2: points[points.len() - 2].subtract(points[points.len() - 1]),
327        }];
328
329        while let Some(split) = splits_to_eval.pop() {
330
331            if split.global_range.end > points.len() || split.global_range.end > distances.len() {
333                continue;
334            }
335
336            let result = fit_cubic(FitCubicParams {
337                points: &points[split.global_range.clone()],
338                chord_lengths: &distances[split.global_range.clone()],
339                segments: &mut segments,
340                error: tolerance,
341                tan1: split.tan1,
342                tan2: split.tan2,
343            });
344
345            if let Some(r) = result {
346                if split.global_range.start > split.global_range.start + r + 1 ||
348                   split.global_range.start + r > split.global_range.end {
349                    continue;
350                }
351                if split.global_range.start + r + 1 >= points.len() || split.global_range.start + r - 1 >= points.len() {
352                    continue;
353                }
354                let tan_center = points[split.global_range.start + r - 1].subtract(points[split.global_range.start + r + 1]);
356
357                splits_to_eval.extend_from_slice(&[
358                    Split {
359                        global_range: (split.global_range.start + r)..split.global_range.end,
360                        tan1: tan_center.negate(),
361                        tan2: split.tan2,
362                    },
363                    Split {
364                        global_range: split.global_range.start..(split.global_range.start + r + 1),
365                        tan1: split.tan1,
366                        tan2: tan_center,
367                    },
368                ]);
369            }
370        }
371
372        segments
373    }
374
375}
376
377struct FitCubicParams<'a> {
378    segments: &'a mut Vec<Point>,
379    points: &'a [Point],
380    chord_lengths: &'a [Float],
381    error: Float,
382    tan1: Point,
383    tan2: Point,
384}
385
386#[inline]
387fn fit_cubic(params: FitCubicParams) -> Option<usize> {
388
389    let FitCubicParams { segments, points, chord_lengths, error, tan1, tan2 } = params;
390
391    if points.len() < 2 {
393        return None;
394    } else if points.len() == 2 {
395        let pt1 = points[0];
396        let pt2 = points[1];
397        let dist = pt1.distance(pt2) / 3.0;
398        add_curve(segments, &[
399            pt1,
400            pt1.add(tan1.normalize(dist)),
401            pt2.add(tan2.normalize(dist)),
402            pt2
403        ]);
404        return None;
405    }
406
407    let mut u_prime = chord_lengths.to_owned();
412    let u_prime_first = match u_prime.first().copied() {
413        Some(s) => s,
414        None => unsafe { unreachable_unchecked() },
415    };
416    let u_prime_last = match u_prime.last().copied() {
417        Some(s) => s,
418        None => unsafe { unreachable_unchecked() },
419    };
420    let u_prime_last = u_prime_last - u_prime_first;
421    u_prime.iter_mut().for_each(|p| { *p = (*p - u_prime_first) / u_prime_last; });
422
423    let mut max_error = error.max(error.powi(2));
424    let mut parameters_in_order = true;
425    let mut split = 2;
426
427    for _ in 0..4 {
429
430        let curve = generate_bezier(points, &u_prime, tan1, tan2);
431
432        let max = find_max_error(points, &curve, &u_prime);
434
435        if max.error < error && parameters_in_order {
436            add_curve(segments, &curve);
438            return None;
439        }
440
441        split = max.index;
442
443        if max.error >= max_error {
445            break;
446        }
447        parameters_in_order = reparameterize(points, &mut u_prime, &curve);
448        max_error = max.error;
449    }
450
451    Some(split)
452}
453
454#[inline]
455fn add_curve(segments: &mut Vec<Point>, curve: &[Point;4]) {
456    segments.extend_from_slice(curve);
457}
458
459#[inline]
460#[allow(non_snake_case)]
461fn generate_bezier(points: &[Point], u_prime: &[Float], tan1: Point, tan2: Point) -> [Point;4] {
462
463    const BEZIER_EPSILON: Float = 1e-12;
464
465    debug_assert!(u_prime.len() > 2);
466    debug_assert!(points.len() > 2);
467    debug_assert!(u_prime.len() == points.len());
468
469    let pt1 = &points[0];
470    let pt2 = &points[points.len() - 1];
471
472    let mut C = [
474        [0.0, 0.0],
475        [0.0, 0.0]
476    ];
477    let mut X = [0.0, 0.0];
478
479    for (p, u) in points.iter().zip(u_prime.iter()) {
480
481        let t = 1.0 - u;
482        let b = 3.0 * u * t;
483        let b0 = t * t * t;
484        let b1 = b * t;
485        let b2 = b * u;
486        let b3 = u * u * u;
487        let a1 = tan1.normalize(b1);
488        let a2 = tan2.normalize(b2);
489        let pt1_multiplied = pt1.multiply(b0 + b1);
490        let pt2_multiplied = pt2.multiply(b2 + b3);
491        let tmp = p.subtract(pt1_multiplied).subtract(pt2_multiplied);
492
493        C[0][0] += a1.dot(a1);
494        C[0][1] += a1.dot(a2);
495        C[1][0] = C[0][1];
496        C[1][1] += a2.dot(a2);
497
498        X[0] += a1.dot(tmp);
499        X[1] += a2.dot(tmp);
500    }
501
502    let det_c0_c1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
504
505    let mut alpha1;
506    let mut alpha2;
507
508    if det_c0_c1.abs() > BEZIER_EPSILON {
509        let det_c0_x = C[0][0] * X[1]    - C[1][0] * X[0];
511        let det_x_c1 = X[0]    * C[1][1] - X[1]    * C[0][1];
512        alpha1 = det_x_c1 / det_c0_c1;
514        alpha2 = det_c0_x / det_c0_c1;
515    } else {
516        let c0 = C[0][0] + C[0][1];
518        let c1 = C[1][0] + C[1][1];
519        alpha1 = if c0.abs() > BEZIER_EPSILON {
520            X[0] / c0
521        } else if c1.abs() > BEZIER_EPSILON {
522            X[1] / c1
523        } else {
524            0.0
525        };
526        alpha2 = alpha1;
527    }
528
529    let seg_length = pt2.distance(*pt1);
533    let eps = BEZIER_EPSILON * seg_length;
534    let mut handle1_2 = None;
535
536    if alpha1 < eps || alpha2 < eps {
537        alpha1 = seg_length / 3.0;
540        alpha2 = alpha1;
541    } else {
542        let line = pt2.subtract(*pt1);
545
546        let tmp_handle_1 = tan1.normalize(alpha1);
549        let tmp_handle_2 = tan2.normalize(alpha2);
550
551        let seg_length_squared = seg_length * seg_length;
552
553        if tmp_handle_1.dot(line) - tmp_handle_2.dot(line) > seg_length_squared {
554            alpha1 = seg_length / 3.0;
556            alpha2 = alpha1;
557            handle1_2 = None;
559        } else {
560            handle1_2 = Some((tmp_handle_1, tmp_handle_2));
561        }
562    }
563
564    if let Some((h1, h2)) = handle1_2 {
567        [*pt1, pt1.add(h1), pt2.add(h2), *pt2]
568    } else {
569        [*pt1, pt1.add(tan1.normalize(alpha1)), pt2.add(tan2.normalize(alpha2)), *pt2]
570    }
571}
572
573#[inline]
576fn reparameterize(points: &[Point], u: &mut [Float], curve: &[Point;4]) -> bool {
577
578    points.iter().zip(u.iter_mut()).for_each(|(p, u)| { *u = find_root(curve, p, *u); });
579
580    !u.windows(2).any(|w| w[1] <= w[0])
583}
584
585#[inline]
586fn find_root(curve: &[Point;4], point: &Point, u: Float) -> Float {
587
588    let mut curve1 = [Point { x: 0.0, y: 0.0 };3];
589    let mut curve2 = [Point { x: 0.0, y: 0.0 };2];
590
591    for i in 0..curve1.len() {
593        curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3.0);
594    }
595
596    for i in 0..curve2.len() {
598        curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2.0);
599    }
600
601    let pt = evaluate_4(&curve, u);
603    let pt1 = evaluate_3(&curve1, u);
604    let pt2 = evaluate_2(&curve2, u);
605    let diff = pt.subtract(*point);
606    let df = pt1.dot(pt1) + diff.dot(pt2);
607
608    if df.abs() < Float::EPSILON {
610        u
611    } else {
612        u - diff.dot(pt1) / df
613    }
614}
615
616macro_rules! evaluate {
617    ($curve:expr, $t:expr) => {{
618        let mut tmp = *$curve;
620
621        for i in 1..$curve.len() {
623            for j in 0..($curve.len() - i) {
624                tmp[j] = tmp[j].multiply(1.0 - $t).add(tmp[j + 1].multiply($t));
625            }
626        }
627
628        tmp[0]
629    }};
630}
631
632#[inline]
634fn evaluate_4(curve: &[Point;4], t: Float) -> Point { let ret = evaluate!(curve, t); ret }
635#[inline]
636fn evaluate_3(curve: &[Point;3], t: Float) -> Point { let ret = evaluate!(curve, t); ret }
637#[inline]
638fn evaluate_2(curve: &[Point;2], t: Float) -> Point { let ret = evaluate!(curve, t); ret }
639
640#[inline]
642fn chord_length_parametrize(points: &[Point]) -> Vec<Float> {
643
644    let mut u = vec![0.0;points.len()];
645    let mut last_dist = 0.0;
646
647    for (prev, (next_id, next)) in points.iter().zip(points.iter().enumerate().skip(1)) {
648        let new_dist = last_dist + prev.distance(*next);
649        unsafe { *u.get_unchecked_mut(next_id) = new_dist; }
650        last_dist = new_dist;
651    }
652
653    for val in u.iter_mut() {
654        *val /= last_dist;
655    }
656
657    u
658}
659
660struct FindMaxErrorReturn {
661    error: Float,
662    index: usize
663}
664
665#[inline]
667fn find_max_error(points: &[Point], curve: &[Point;4], u: &[Float]) -> FindMaxErrorReturn {
668
669    let mut index = points.len() / 2.0 as usize;
670    let mut max_dist = 0.0;
671
672    for (i, (p, u)) in points.iter().zip(u.iter()).enumerate() {
673        let point_on_curve = evaluate_4(curve, *u);
674        let dist = point_on_curve.subtract(*p);
675        let dist_squared = dist.x.mul_add(dist.x, dist.y.powi(2)); if dist_squared >= max_dist {
678            max_dist = dist_squared;
679            index = i;
680        }
681    }
682
683    FindMaxErrorReturn {
684        error: max_dist,
685        index: index
686    }
687}