spiro_inner/
lib.rs

1/// spiro-rs ::: inner.rs
2///
3/// (c) 2020–2021 Fredrick R. Brennan, based on source code by Raph Levien.
4///
5/// Licensed under same license as Raph Levien's libspiro, upon which this code is based. This code
6/// was transpiled with C2Rust, which I then spent hours and hours cleaning up, removing the
7/// dependency on libc, and removing a lot, but not all of, the unsafe code.
8///
9/// For the original comment by Mr. Levien in spiro.c, see comment above struct SpiroSegment.
10///
11/// I chose to implement it this way, with a transpiler, as the complex mathematics involved here
12/// are really beyond my understanding, and I felt that it'd be way too hard for me to come up with
13/// a brand new implementation. Overall I think it turned out okay, could definitely be better if
14/// someone wants to contribute to removing all the unsafe blocks.
15use std::convert::TryInto as _;
16
17pub mod bezctx_oplist;
18pub mod bezctx_ps;
19use bezctx_oplist::Operation;
20
21#[derive(Copy, Clone)]
22pub struct BezierContext<T> {
23    pub move_fn: fn(&mut Self, f64, f64, bool) -> (),
24    pub line_fn: fn(&mut Self, f64, f64) -> (),
25    pub curve_fn: fn(&mut Self, f64, f64, f64, f64, f64, f64) -> (),
26    // knot_idx
27    pub mark_knot_fn: fn(&mut Self, usize) -> (),
28    pub data: Option<T>,
29}
30
31impl<T> BezierContext<T> {
32    pub fn move_to(&mut self, x: f64, y: f64, is_open: bool) {
33        (self.move_fn)(self, x, y, is_open);
34    }
35    pub fn line_to(&mut self, x: f64, y: f64) {
36        (self.line_fn)(self, x, y);
37    }
38    /// SVG semantics
39    pub fn curve_to(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) {
40        (self.curve_fn)(self, x1, y1, x2, y2, x3, y3);
41    }
42    pub fn mark_knot(&mut self, knot_idx: usize) {
43        (self.mark_knot_fn)(self, knot_idx);
44    }
45}
46
47impl<T> Default for BezierContext<T> {
48    fn default() -> Self {
49        Self {
50            move_fn: |_, _, _, _| {},
51            line_fn: |_, _, _| {},
52            curve_fn: |_, _, _, _, _, _, _| {},
53            mark_knot_fn: |_, _| {},
54            data: None,
55        }
56    }
57}
58
59#[derive(Copy, Clone, Debug)]
60pub struct SpiroCP {
61    pub x: f64,
62    pub y: f64,
63    pub ty: char,
64}
65
66/*
67ppedit - A pattern plate editor for Spiro splines.
68Copyright (C) 2007 Raph Levien
69
70Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
71http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
72<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
73option. This file may not be copied, modified, or distributed
74except according to those terms.
75
76*/
77/* C implementation of third-order polynomial spirals. */
78#[derive(Copy, Clone, Debug, Default)]
79pub struct SpiroSegment {
80    x: f64,
81    y: f64,
82    ty: char,
83    bend_th: f64,
84    ks: [f64; 4],
85    seg_ch: f64,
86    seg_th: f64,
87}
88#[derive(Copy, Clone, Debug, Default)]
89pub struct BandMath {
90    a: [f64; 11],
91    al: [f64; 5],
92}
93/* Integrate polynomial spiral curve over range -.5 .. .5. */
94
95pub fn integrate_spiro(ks: [f64; 4], xy: &mut [f64; 2]) {
96    let th1: f64 = ks[0];
97    let th2: f64 = 0.5 * ks[1];
98    let th3: f64 = 1.0 / 6. * ks[2];
99    let th4: f64 = 1.0 / 24. * ks[3];
100    let mut x: f64 = 0.;
101    let mut y: f64 = 0.;
102    let ds: f64 = 1.0 / 4 as f64;
103    let ds2: f64 = ds * ds;
104    let ds3: f64 = ds2 * ds;
105    let k0: f64 = ks[0] * ds;
106    let k1: f64 = ks[1] * ds;
107    let k2: f64 = ks[2] * ds;
108    let k3: f64 = ks[3] * ds;
109    let mut i: isize = 0;
110    let mut s: f64 = 0.5 * ds - 0.5;
111    while i < 4 {
112        let mut u;
113        let mut v;
114        let km0;
115        let km1;
116        let km2;
117        let km3;
118
119        km0 = ((1.0 / 6. * k3 * s + 0.5 * k2) * s + k1) * s + k0;
120        km1 = ((0.5 * k3 * s + k2) * s + k1) * ds;
121        km2 = (k3 * s + k2) * ds2;
122
123        km3 = k3 * ds3;
124        let t1_1: f64 = km0;
125        let t1_2: f64 = 0.5 * km1;
126        let t1_3: f64 = 1.0 / 6. * km2;
127        let t1_4: f64 = 1.0 / 24. * km3;
128        let t2_2: f64 = t1_1 * t1_1;
129        let t2_3: f64 = 2. * (t1_1 * t1_2);
130        let t2_4: f64 = 2. * (t1_1 * t1_3) + t1_2 * t1_2;
131        let t2_5: f64 = 2. * (t1_1 * t1_4 + t1_2 * t1_3);
132        let t2_6: f64 = 2. * (t1_2 * t1_4) + t1_3 * t1_3;
133        let t2_7: f64 = 2. * (t1_3 * t1_4);
134        let t2_8: f64 = t1_4 * t1_4;
135        let t3_4: f64 = t2_2 * t1_2 + t2_3 * t1_1;
136        let t3_6: f64 = t2_2 * t1_4 + t2_3 * t1_3 + t2_4 * t1_2 + t2_5 * t1_1;
137        let t3_8: f64 = t2_4 * t1_4 + t2_5 * t1_3 + t2_6 * t1_2 + t2_7 * t1_1;
138        let t3_10: f64 = t2_6 * t1_4 + t2_7 * t1_3 + t2_8 * t1_2;
139        let t4_4: f64 = t2_2 * t2_2;
140        let t4_5: f64 = 2. * (t2_2 * t2_3);
141        let t4_6: f64 = 2. * (t2_2 * t2_4) + t2_3 * t2_3;
142        let t4_7: f64 = 2. * (t2_2 * t2_5 + t2_3 * t2_4);
143        let t4_8: f64 = 2. * (t2_2 * t2_6 + t2_3 * t2_5) + t2_4 * t2_4;
144        let t4_9: f64 = 2. * (t2_2 * t2_7 + t2_3 * t2_6 + t2_4 * t2_5);
145        let t4_10: f64 = 2. * (t2_2 * t2_8 + t2_3 * t2_7 + t2_4 * t2_6) + t2_5 * t2_5;
146        let t5_6: f64 = t4_4 * t1_2 + t4_5 * t1_1;
147        let t5_8: f64 = t4_4 * t1_4 + t4_5 * t1_3 + t4_6 * t1_2 + t4_7 * t1_1;
148        let t5_10: f64 = t4_6 * t1_4 + t4_7 * t1_3 + t4_8 * t1_2 + t4_9 * t1_1;
149        let t6_6: f64 = t4_4 * t2_2;
150        let t6_7: f64 = t4_4 * t2_3 + t4_5 * t2_2;
151        let t6_8: f64 = t4_4 * t2_4 + t4_5 * t2_3 + t4_6 * t2_2;
152        let t6_9: f64 = t4_4 * t2_5 + t4_5 * t2_4 + t4_6 * t2_3 + t4_7 * t2_2;
153        let t6_10: f64 = t4_4 * t2_6 + t4_5 * t2_5 + t4_6 * t2_4 + t4_7 * t2_3 + t4_8 * t2_2;
154        let t7_8: f64 = t6_6 * t1_2 + t6_7 * t1_1;
155        let t7_10: f64 = t6_6 * t1_4 + t6_7 * t1_3 + t6_8 * t1_2 + t6_9 * t1_1;
156        let t8_8: f64 = t6_6 * t2_2;
157        let t8_9: f64 = t6_6 * t2_3 + t6_7 * t2_2;
158        let t8_10: f64 = t6_6 * t2_4 + t6_7 * t2_3 + t6_8 * t2_2;
159        let t9_10: f64 = t8_8 * t1_2 + t8_9 * t1_1;
160        let t10_10: f64 = t8_8 * t2_2;
161        u = 1.;
162        v = 0.;
163        v += 1.0 / 12. * t1_2 + 1.0 / 80. * t1_4;
164        u -= 1.0 / 24. * t2_2 + 1.0 / 160. * t2_4 + 1.0 / 896. * t2_6 + 1.0 / 4608. * t2_8;
165        v -= 1.0 / 480. * t3_4 + 1.0 / 2688. * t3_6 + 1.0 / 13824. * t3_8 + 1.0 / 67584. * t3_10;
166        u += 1.0 / 1920. * t4_4 + 1.0 / 10752. * t4_6 + 1.0 / 55296. * t4_8 + 1.0 / 270336. * t4_10;
167        v += 1.0 / 53760. * t5_6 + 1.0 / 276480. * t5_8 + 1.0 / 1.35168e+06 * t5_10;
168        u -= 1.0 / 322560. * t6_6 + 1.0 / 1.65888e+06 * t6_8 + 1.0 / 8.11008e+06 * t6_10;
169        v -= 1.0 / 1.16122e+07 * t7_8 + 1.0 / 5.67706e+07 * t7_10;
170        u += 1.0 / 9.28973e+07 * t8_8 + 1.0 / 4.54164e+08 * t8_10;
171        v += 1.0 / 4.08748e+09 * t9_10;
172        u -= 1.0 / 4.08748e+10 * t10_10;
173
174        let th: f64 = (((th4 * s + th3) * s + th2) * s + th1) * s;
175        let cth: f64 = (th).cos();
176        let sth: f64 = (th).sin();
177        x += cth * u - sth * v;
178        y += cth * v + sth * u;
179        s += ds;
180
181        i += 1
182    }
183    xy[0] = x * ds;
184    xy[1] = y * ds;
185}
186
187pub fn compute_ends(ks: [f64; 4], ends: &mut [[f64; 4]; 2], seg_ch: f64) -> f64 {
188    let mut xy: [f64; 2] = [0.; 2];
189    let ch;
190    let th;
191    let l;
192    let l2;
193    let l3;
194    let th_even;
195    let th_odd;
196    let k0_even;
197    let k0_odd;
198    let k1_even;
199    let k1_odd;
200    let k2_even;
201    let k2_odd;
202    integrate_spiro(ks, &mut xy);
203    ch = xy[0].hypot(xy[1]);
204    th = xy[1].atan2(xy[0]);
205    l = ch / seg_ch;
206    th_even = 0.5 * ks[0] + 1.0 / 48. * ks[2];
207    th_odd = 0.125 * ks[1] + 1.0 / 384. * ks[3] - th;
208    (ends[0])[0] = th_even - th_odd;
209    (ends[1])[0] = th_even + th_odd;
210    k0_even = l * (ks[0] + 0.125 * ks[2]);
211    k0_odd = l * (0.5 * ks[1] + 1.0 / 48. * ks[3]);
212    (ends[0])[1] = k0_even - k0_odd;
213    (ends[1])[1] = k0_even + k0_odd;
214    l2 = l * l;
215    k1_even = l2 * (ks[1] + 0.125 * ks[3]);
216    k1_odd = l2 * 0.5 * ks[2];
217    (ends[0])[2] = k1_even - k1_odd;
218    (ends[1])[2] = k1_even + k1_odd;
219    l3 = l2 * l;
220    k2_even = l3 * ks[2];
221    k2_odd = l3 * 0.5 * ks[3];
222    (ends[0])[3] = k2_even - k2_odd;
223    (ends[1])[3] = k2_even + k2_odd;
224    return l;
225}
226
227pub fn compute_pderivs(s: &SpiroSegment, ends: &mut [[f64; 4]; 2], derivs: &mut [[[f64; 4]; 2]; 4], jinc: isize) {
228    let recip_d: f64 = 2e6;
229    let delta: f64 = 1.0 / recip_d;
230    let mut try_ks: [f64; 4] = [0.; 4];
231    let mut try_ends: [[f64; 4]; 2] = [[0.; 4]; 2];
232    let mut i: usize = 0;
233    let mut j;
234    let mut k;
235    compute_ends(s.ks, ends, s.seg_ch);
236    while (i as isize) < jinc {
237        j = 0;
238        while j < 4 {
239            try_ks[j] = s.ks[j];
240            j += 1
241        }
242        try_ks[i] += delta;
243        compute_ends(try_ks, &mut try_ends, s.seg_ch);
244        k = 0;
245        while k < 2 {
246            j = 0;
247            while j < 4 {
248                (derivs[j])[k][i] = recip_d * (try_ends[k][j] - (ends[k])[j]);
249                j += 1
250            }
251            k += 1
252        }
253        i += 1
254    }
255}
256
257use std::f64::consts::PI;
258fn mod2pi(th: f64) -> f64 {
259    let u = th / (2. * PI);
260    return 2. * PI * (u - (u + 0.5).floor());
261}
262
263pub fn setup_path(src: &[SpiroCP]) -> Vec<SpiroSegment> {
264    let mut n = src.len();
265
266    if n == 0 {
267        return Vec::new();
268    }
269
270    if (src[0]).ty == '{' {
271        n -= 1;
272    };
273
274    let mut r = vec![SpiroSegment::default(); n + 1];
275
276    let mut i = 0;
277    let mut ilast;
278    while i < n {
279        r[i].x = src[i].x;
280        r[i].y = src[i].y;
281        r[i].ty = src[i].ty;
282        r[i].ks[0] = 0.0;
283        r[i].ks[1] = 0.0;
284        r[i].ks[2] = 0.0;
285        r[i].ks[3] = 0.0;
286        i += 1
287    }
288    r[n].x = src[n % src.len()].x;
289    r[n].y = src[n % src.len()].y;
290    r[n].ty = src[n % src.len()].ty;
291    i = 0;
292    while i < n {
293        let dx: f64 = r[i + 1].x - r[i].x;
294        let dy: f64 = r[i + 1].y - r[i].y;
295        r[i].seg_ch = dx.hypot(dy);
296        r[i].seg_th = dy.atan2(dx);
297        i += 1
298    }
299    ilast = n - 1;
300    i = 0;
301    while i < n {
302        if (r[i].ty == '{') || (r[i].ty == '}') || (r[i].ty == 'v') {
303            r[i].bend_th = 0.0;
304        } else {
305            r[i].bend_th = mod2pi(r[i].seg_th - r[ilast].seg_th);
306        }
307        ilast = i;
308        i += 1
309    }
310    r
311}
312
313pub fn bandec11(m: &mut [BandMath], perm: &mut [isize], n: isize) {
314    let mut i: isize = 0;
315    let mut j;
316    let mut k: isize = 0;
317    let mut l: isize = 5;
318    /* pack top triangle to the left. */
319    while i < 5 {
320        j = 0;
321        while j < i + 6 {
322            m[i as usize].a[j as usize] = m[i as usize].a[(j + 5 - i) as usize];
323            j += 1
324        }
325        while j < 11 {
326            m[i as usize].a[j as usize] = 0.0;
327            j += 1
328        }
329        i += 1
330    }
331    while k < n {
332        let mut pivot: isize = k;
333        let mut pivot_val: f64 = m[k as usize].a[0];
334        if l < n {
335            l += 1
336        }
337        j = k + 1;
338        while j < l {
339            if m[j as usize].a[0].abs() > pivot_val.abs() {
340                pivot_val = m[j as usize].a[0];
341                pivot = j;
342            }
343            j += 1
344        }
345        perm[k as usize] = pivot;
346        if pivot != k {
347            j = 0;
348            while j < 11 {
349                let tmp: f64 = m[k as usize].a[j as usize];
350                m[k as usize].a[j as usize] = m[pivot as usize].a[j as usize];
351                m[pivot as usize].a[j as usize] = tmp;
352                j += 1
353            }
354        }
355        if pivot_val.abs() < 1e-12 {
356            pivot_val = 1e-12
357        }
358        let pivot_scale = 1.0 / pivot_val;
359        i = k + 1;
360        while i < l {
361            let x: f64 = m[i as usize].a[0] * pivot_scale;
362            m[k as usize].al[(i - k - 1) as usize] = x;
363            j = 1;
364            while j < 11 {
365                m[i as usize].a[(j - 1) as usize] = m[i as usize].a[j as usize] - x * m[k as usize].a[j as usize];
366                j += 1
367            }
368            m[i as usize].a[10] = 0.0;
369            i += 1
370        }
371        k += 1
372    }
373}
374
375pub fn banbks11(m: &[BandMath], perm: &[isize], v: &mut [f64], n: isize) {
376    let mut i;
377    let mut k: isize = 0;
378    let mut l: isize = 5;
379    /* forward substitution */
380    while k < n {
381        i = perm[k as usize];
382        if i != k {
383            let tmp = v[k as usize];
384            v[k as usize] = v[i as usize];
385            v[i as usize] = tmp;
386        }
387        if l < n {
388            l += 1
389        }
390        i = k + 1;
391        while i < l {
392            v[i as usize] -= m[k as usize].al[(i - k - 1) as usize] * v[k as usize];
393            i += 1
394        }
395        k += 1
396    }
397    /* back substitution */
398    l = 1;
399    i = n - 1;
400    while i >= 0 {
401        let mut x: f64 = v[i as usize];
402        k = 1;
403        while k < l {
404            x -= m[i as usize].a[k as usize] * v[(k + i) as usize];
405            k += 1
406        }
407        v[i as usize] = x / m[i as usize].a[0];
408        if l < 11 {
409            l += 1
410        }
411        i -= 1
412    }
413}
414
415pub fn compute_jinc(ty0: char, ty1: char) -> isize {
416    if ty0 == 'o' || ty1 == 'o' || ty0 == ']' || ty1 == '[' {
417        return 4;
418    } else if ty0 == 'c' && ty1 == 'c' {
419        return 2;
420    } else if (ty0 == '{' || ty0 == 'v' || ty0 == '[') && ty1 == 'c' || ty0 == 'c' && (ty1 == '}' || ty1 == 'v' || ty1 == ']') {
421        return 1;
422    } else {
423        return 0;
424    };
425}
426
427pub fn count_vec(s: &[SpiroSegment], nseg: isize) -> isize {
428    let mut i = 0;
429    let mut n: isize = 0;
430    while i < nseg as usize {
431        n += compute_jinc(s[i].ty, s[i + 1].ty);
432        i += 1
433    }
434    return n;
435}
436
437pub fn add_mat_line(m: &mut [BandMath], v: &mut [f64], derivs: &mut [f64], x: f64, y: f64, j: isize, jj: isize, jinc: isize, nmat: isize) {
438    let mut k: isize = 0;
439    if jj >= 0 {
440        let joff: isize = (j + 5 - jj + nmat) % nmat;
441        v[jj as usize] += x;
442        while k < jinc {
443            m[jj as usize].a[(joff + k) as usize] += y * derivs[k as usize];
444            k += 1
445        }
446    };
447}
448
449pub fn spiro_iter(s: &mut [SpiroSegment], m: &mut [BandMath], perm: &mut [isize], v: &mut [f64], n: isize) -> f64 {
450    let cyclic = s[0].ty != '{' && s[0].ty != 'v';
451    let mut i: isize = 0;
452    let mut j: isize;
453    let mut jj: isize;
454    let nmat: isize = count_vec(s, n);
455    let mut norm: f64;
456    let n_invert: isize;
457    while i < nmat {
458        v[i as usize] = 0.0;
459        j = 0;
460        while j < 11 {
461            m[i as usize].a[j as usize] = 0.0;
462            j += 1
463        }
464        j = 0;
465        while j < 5 {
466            m[i as usize].al[j as usize] = 0.0;
467            j += 1
468        }
469        i += 1
470    }
471    j = 0;
472    if s[0].ty == 'o' {
473        jj = nmat - 2;
474    } else if s[0].ty == 'c' || s[0].ty == '[' || s[0].ty == ']' {
475        jj = nmat - 1;
476    } else {
477        jj = 0;
478    }
479    i = 0;
480    while i < n {
481        let ty0: char = s[i as usize].ty;
482        let ty1: char = s[i as usize + 1].ty;
483        let jinc: isize = compute_jinc(ty0, ty1);
484        let th: f64 = s[i as usize].bend_th;
485        let mut ends: [[f64; 4]; 2] = [[0.; 4]; 2];
486        let mut derivs: [[[f64; 4]; 2]; 4] = [[[0.; 4]; 2]; 4];
487        let mut jthl: isize = -1;
488        let mut jk0l: isize = -1;
489        let mut jk1l: isize = -1;
490        let mut jk2l: isize = -1;
491        let mut jthr: isize = -1;
492        let mut jk0r: isize = -1;
493        let mut jk1r: isize = -1;
494        let mut jk2r: isize = -1;
495        compute_pderivs(&mut s[i as usize], &mut ends, &mut derivs, jinc);
496        /* constraints crossing left */
497        if ty0 == 'o' || ty0 == 'c' || ty0 == '[' || ty0 == ']' {
498            let fresh0 = jj;
499            jj = jj + 1;
500            jthl = fresh0;
501            jj %= nmat;
502            let fresh1 = jj;
503            jj = jj + 1;
504            jk0l = fresh1
505        }
506        if ty0 == 'o' {
507            jj %= nmat;
508            let fresh2 = jj;
509            jj = jj + 1;
510            jk1l = fresh2;
511            let fresh3 = jj;
512            jj = jj + 1;
513            jk2l = fresh3
514        }
515        /* constraints on left */
516        if (ty0 == '[' || ty0 == 'v' || ty0 == '{' || ty0 == 'c') && jinc == 4 {
517            if ty0 != 'c' {
518                let fresh4 = jj;
519                jj = jj + 1;
520                jk1l = fresh4
521            }
522            let fresh5 = jj;
523            jj = jj + 1;
524            jk2l = fresh5
525        }
526        /* constraints on right */
527        if (ty1 == ']' || ty1 == 'v' || ty1 == '}' || ty1 == 'c') && jinc == 4 {
528            if ty1 != 'c' {
529                let fresh6 = jj;
530                jj = jj + 1;
531                jk1r = fresh6
532            }
533            let fresh7 = jj;
534            jj = jj + 1;
535            jk2r = fresh7
536        }
537        /* constraints crossing right */
538        if ty1 == 'o' || ty1 == 'c' || ty1 == '[' || ty1 == ']' {
539            jthr = jj;
540            jk0r = (jj + 1) % nmat
541        }
542        if ty1 == 'o' {
543            jk1r = (jj + 2) % nmat;
544            jk2r = (jj + 3) % nmat
545        }
546
547        add_mat_line(m, v, &mut derivs[0][0], th - ends[0][0], 1., j, jthl, jinc, nmat);
548        add_mat_line(m, v, &mut derivs[1][0], ends[0][1], -1., j, jk0l, jinc, nmat);
549        add_mat_line(m, v, &mut derivs[2][0], ends[0][2], -1., j, jk1l, jinc, nmat);
550        add_mat_line(m, v, &mut derivs[3][0], ends[0][3], -1., j, jk2l, jinc, nmat);
551        add_mat_line(m, v, &mut derivs[0][1], -ends[1][0], 1., j, jthr, jinc, nmat);
552        add_mat_line(m, v, &mut derivs[1][1], -ends[1][1], 1., j, jk0r, jinc, nmat);
553        add_mat_line(m, v, &mut derivs[2][1], -ends[1][2], 1., j, jk1r, jinc, nmat);
554        add_mat_line(m, v, &mut derivs[3][1], -ends[1][3], 1., j, jk2r, jinc, nmat);
555
556        j += jinc;
557        i += 1
558    }
559    if cyclic {
560        let u_nmat: usize = nmat.try_into().unwrap();
561        m.copy_within(..u_nmat, u_nmat);
562        m.copy_within(..u_nmat, 2 * u_nmat);
563        v.copy_within(..u_nmat, u_nmat);
564        v.copy_within(..u_nmat, 2 * u_nmat);
565        n_invert = 3 * nmat;
566        j = nmat
567    } else {
568        n_invert = nmat;
569        j = 0
570    }
571    bandec11(m, perm, n_invert);
572    banbks11(m, perm, v, n_invert);
573    norm = 0.0;
574    i = 0;
575    while i < n {
576        let ty0_0: char = s[i as usize].ty;
577        let ty1_0: char = s[(i + 1) as usize].ty;
578        let jinc_0: isize = compute_jinc(ty0_0, ty1_0);
579        let mut k: isize = 0;
580        while k < jinc_0 {
581            let fresh8 = j;
582            j = j + 1;
583            let dk: f64 = v[fresh8 as usize];
584            s[i as usize].ks[k as usize] += dk;
585            norm += dk * dk;
586            k += 1
587        }
588        i += 1
589    }
590    return norm;
591}
592
593pub fn solve_spiro(s: &mut [SpiroSegment], nseg: isize) -> isize {
594    let nmat: isize = count_vec(s, nseg);
595    let mut n_alloc: usize = nmat.try_into().unwrap();
596    let mut norm: f64;
597    let mut i: isize;
598    if nmat == 0 {
599        return 0;
600    }
601    if s[0].ty != '{' && s[0].ty != 'v' {
602        n_alloc *= 3
603    }
604    if n_alloc < 5 {
605        n_alloc = 5
606    }
607
608    let mut m = vec![BandMath::default(); n_alloc];
609    let mut v = vec![0.0; n_alloc];
610    let mut perm = vec![0; n_alloc];
611
612    i = 0;
613    while i < 10 {
614        norm = spiro_iter(s, &mut m, &mut perm, &mut v, nseg);
615        if norm < 1e-12 {
616            break;
617        }
618        i += 1
619    }
620
621    return 0;
622}
623
624pub fn spiro_seg_to_bpath<T>(ks: [f64; 4], x0: f64, y0: f64, x1: f64, y1: f64, bc: &mut BezierContext<T>, depth: isize) {
625    let bend: f64 = (ks[0]).abs() + (0.5 * ks[1]).abs() + (0.125 * ks[2]).abs() + (1.0 / 48. * ks[3]).abs();
626    if bend == 0. {
627        bc.line_to(x1, y1);
628    } else {
629        let seg_ch: f64 = (x1 - x0).hypot(y1 - y0);
630        let seg_th: f64 = (y1 - y0).atan2(x1 - x0);
631        let mut xy: [f64; 2] = [0.; 2];
632        let ch: f64;
633        let th: f64;
634        let scale: f64;
635        let rot: f64;
636        let th_even: f64;
637        let th_odd: f64;
638        let ul: f64;
639        let vl: f64;
640        let ur: f64;
641        let vr: f64;
642        integrate_spiro(ks, &mut xy);
643        ch = xy[0].hypot(xy[1]);
644        th = xy[1].atan2(xy[0]);
645        scale = seg_ch / ch;
646        rot = seg_th - th;
647        if depth > 5 || bend < 1.0 {
648            th_even = 1.0 / 384. * ks[3] + 1.0 / 8. * ks[1] + rot;
649            th_odd = 1.0 / 48. * ks[2] + 0.5 * ks[0];
650            ul = scale * (1.0 / 3.) * (th_even - th_odd).cos();
651            vl = scale * (1.0 / 3.) * (th_even - th_odd).sin();
652            ur = scale * (1.0 / 3.) * (th_even + th_odd).cos();
653            vr = scale * (1.0 / 3.) * (th_even + th_odd).sin();
654            bc.curve_to(x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1);
655        } else {
656            /* subdivide */
657            let mut ksub: [f64; 4] = [0.; 4];
658            let thsub: f64;
659            let mut xysub: [f64; 2] = [0.; 2];
660            let xmid: f64;
661            let ymid: f64;
662            let cth: f64;
663            let sth: f64;
664            ksub[0] = 0.5 * ks[0] - 0.125 * ks[1] + 1.0 / 64. * ks[2] - 1.0 / 768. * ks[3];
665            ksub[1] = 0.25 * ks[1] - 1.0 / 16. * ks[2] + 1.0 / 128. * ks[3];
666            ksub[2] = 0.125 * ks[2] - 1.0 / 32. * ks[3];
667            ksub[3] = 1.0 / 16. * ks[3];
668            thsub = rot - 0.25 * ks[0] + 1.0 / 32. * ks[1] - 1.0 / 384. * ks[2] + 1.0 / 6144. * ks[3];
669            cth = 0.5 * scale * (thsub).cos();
670            sth = 0.5 * scale * (thsub).sin();
671            integrate_spiro(ksub, &mut xysub);
672            xmid = x0 + cth * xysub[0] - sth * xysub[1];
673            ymid = y0 + cth * xysub[1] + sth * xysub[0];
674            spiro_seg_to_bpath(ksub, x0, y0, xmid, ymid, bc, depth + 1);
675            ksub[0] += 0.25 * ks[1] + 1.0 / 384. * ks[3];
676            ksub[1] += 0.125 * ks[2];
677            ksub[2] += 1.0 / 16. * ks[3];
678            spiro_seg_to_bpath(ksub, xmid, ymid, x1, y1, bc, depth + 1);
679        }
680    };
681}
682
683pub fn spiro_to_bpath<T>(s: &[SpiroSegment], n: isize, bc: &mut BezierContext<T>) {
684    if n == 0 {
685        return;
686    }
687    let mut i = 0;
688    let nsegs: isize = if s[(n - 1) as usize].ty == '}' { (n) - 1 } else { n };
689    while i < nsegs {
690        let x0: f64 = s[i as usize].x;
691        let y0: f64 = s[i as usize].y;
692        let x1: f64 = s[(i + 1) as usize].x;
693        let y1: f64 = s[(i + 1) as usize].y;
694        if i == 0 {
695            bc.move_to(x0, y0, s[0 as usize].ty == '{');
696        }
697        bc.mark_knot(i.try_into().unwrap());
698        spiro_seg_to_bpath(s[i as usize].ks, x0, y0, x1, y1, bc, 0);
699        i += 1
700    }
701}
702
703pub fn get_knot_th(s: &[SpiroSegment], i: isize) -> f64 {
704    let mut ends: [[f64; 4]; 2] = [[0.; 4]; 2];
705    if i == 0 {
706        compute_ends(s[i as usize].ks, &mut ends, s[i as usize].seg_ch);
707        return s[i as usize].seg_th - ends[0][0];
708    } else {
709        compute_ends(s[(i - 1) as usize].ks, &mut ends, s[(i - 1) as usize].seg_ch);
710        return s[(i - 1) as usize].seg_th + ends[1][0];
711    };
712}
713
714pub fn run_spiro(path: &mut [SpiroCP]) -> Vec<Operation> {
715    let path_len = path.len().try_into().unwrap();
716    let mut ctx: BezierContext<Vec<Operation>> = BezierContext::new();
717    let mut segs = setup_path(path);
718    solve_spiro(&mut segs, path_len);
719
720    spiro_to_bpath(&mut segs, path_len, &mut ctx);
721    ctx.data.unwrap_or(vec![])
722}