Skip to main content

del_msh_core/
polyloop3.rs

1//! methods for 3D poly loop
2
3use num_traits::AsPrimitive;
4
5pub fn vtx2framex<T>(vtx2xyz: &[T]) -> Vec<T>
6where
7    T: num_traits::Float + 'static + Copy,
8    f64: AsPrimitive<T>,
9{
10    use del_geo_core::vec3::Vec3;
11    let num_vtx = vtx2xyz.len() / 3;
12    let mut vtx2bin = vec![T::zero(); num_vtx * 3];
13    {
14        // first segment
15        let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, 0);
16        let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, 1);
17        let v01 = p1.sub(p0);
18        let (x, _) = del_geo_core::vec3::basis_xy_from_basis_z(&v01);
19        crate::vtx2xyz::to_vec3_mut(&mut vtx2bin, 0).copy_from_slice(&x);
20    }
21    for iseg1 in 1..num_vtx {
22        // parallel transport
23        let iv0 = iseg1 - 1;
24        let iv1 = iseg1;
25        let iv2 = (iseg1 + 1) % num_vtx;
26        let iseg0 = iseg1 - 1;
27        let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, iv0);
28        let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, iv1);
29        let p2 = crate::vtx2xyz::to_vec3(vtx2xyz, iv2);
30        let v01 = p1.sub(p0);
31        let v12 = p2.sub(p1);
32        let rot = del_geo_core::mat3_col_major::minimum_rotation_matrix(&v01, &v12);
33        let b01 = crate::vtx2xyz::to_vec3(&vtx2bin, iseg0);
34        let b12 = del_geo_core::mat3_col_major::mult_vec(&rot, b01);
35        crate::vtx2xyz::to_vec3_mut(&mut vtx2bin, iseg1).copy_from_slice(&b12);
36    }
37    vtx2bin
38}
39
40pub fn framez<T>(vtx2xyz: &[T], i_vtx: usize) -> [T; 3]
41where
42    T: num_traits::Float + Copy,
43{
44    let num_vtx = vtx2xyz.len() / 3;
45    assert!(i_vtx < num_vtx);
46    let i0_vtx = (i_vtx + num_vtx - 1) % num_vtx;
47    // let i1_vtx = i_vtx;
48    let i2_vtx = (i_vtx + 1) % num_vtx;
49    let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, i0_vtx);
50    let p2 = crate::vtx2xyz::to_vec3(vtx2xyz, i2_vtx);
51    use del_geo_core::vec3::Vec3;
52    p2.sub(p0).normalize()
53}
54
55fn match_frames_of_two_ends<T>(vtx2xyz: &[T], vtx2bin0: &[T]) -> Vec<T>
56where
57    T: num_traits::Float + Copy + 'static + std::fmt::Display,
58    f64: AsPrimitive<T>,
59    usize: AsPrimitive<T>,
60{
61    use del_geo_core::vec3::Vec3;
62    let num_vtx = vtx2xyz.len() / 3;
63    let theta = {
64        let x0 = crate::vtx2xyz::to_vec3(vtx2bin0, 0);
65        let p0 = &crate::vtx2xyz::to_vec3(vtx2xyz, 0);
66        let p1 = &crate::vtx2xyz::to_vec3(vtx2xyz, 1);
67        let v01 = p1.sub(p0).normalize();
68        assert!(x0.dot(&v01).abs() < 1.0e-6_f64.as_());
69        let xn = crate::vtx2xyz::to_vec3(vtx2bin0, num_vtx - 1);
70        let pn = &crate::vtx2xyz::to_vec3(vtx2xyz, num_vtx - 1);
71        let vn0 = p0.sub(pn).normalize();
72        let rot = del_geo_core::mat3_col_major::minimum_rotation_matrix(&vn0, &v01);
73        let x1a = del_geo_core::mat3_col_major::mult_vec(&rot, xn);
74        let y0 = v01.cross(x0);
75        assert!(
76            x1a.dot(&v01).abs() < 1.0e-4f64.as_(),
77            "{}",
78            x1a.dot(&v01).abs()
79        );
80        assert!((y0.norm() - 1.0_f64.as_()).abs() < 1.0e-6_f64.as_());
81        let c0 = x1a.dot(x0);
82        let s0 = x1a.dot(&y0);
83        T::atan2(s0, c0)
84    };
85    let theta_step = theta / num_vtx.as_();
86    let mut vtx2bin1 = vec![T::zero(); num_vtx * 3];
87    for iseg in 0..num_vtx {
88        let dtheta = theta_step * iseg.as_();
89        let x0 = crate::vtx2xyz::to_vec3(vtx2bin0, iseg);
90        let ivtx0 = iseg;
91        let ivtx1 = (iseg + 1) % num_vtx;
92        let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, ivtx1);
93        let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, ivtx0);
94        let v01 = p1.sub(p0).normalize();
95        let y0 = v01.cross(x0);
96        assert!(
97            (x0.cross(&y0).dot(&v01) - 1.as_()).abs() < 1.0e-3_f64.as_(),
98            "{}",
99            x0.cross(&y0).dot(&v01)
100        );
101        let x0 = x0.scale(dtheta.sin());
102        let y0 = y0.scale(dtheta.cos());
103        let x1 = x0.add(&y0);
104        crate::vtx2xyz::to_vec3_mut(&mut vtx2bin1, iseg).copy_from_slice(&x1);
105    }
106    vtx2bin1
107}
108
109pub fn smooth_frame<T>(vtx2xyz: &[T]) -> Vec<T>
110where
111    T: num_traits::Float + 'static + Copy + std::fmt::Display,
112    f64: AsPrimitive<T>,
113    usize: AsPrimitive<T>,
114{
115    let vtx2bin0 = vtx2framex(vtx2xyz);
116    // dbg!(&vtx2bin0);
117    match_frames_of_two_ends(vtx2xyz, &vtx2bin0)
118}
119
120pub fn normal_binormal<T>(vtx2xyz: &[T]) -> (Vec<T>, Vec<T>)
121where
122    T: num_traits::Float + Copy,
123{
124    use del_geo_core::vec3::Vec3;
125    let num_vtx = vtx2xyz.len() / 3;
126    let mut vtx2bin = vec![T::zero(); num_vtx * 3];
127    let mut vtx2nrm = vec![T::zero(); num_vtx * 3];
128    for ivtx1 in 0..num_vtx {
129        let ivtx0 = (ivtx1 + num_vtx - 1) % num_vtx;
130        let ivtx2 = (ivtx1 + 1) % num_vtx;
131        let v0 = crate::vtx2xyz::to_vec3(vtx2xyz, ivtx0);
132        let v1 = crate::vtx2xyz::to_vec3(vtx2xyz, ivtx1);
133        let v2 = crate::vtx2xyz::to_vec3(vtx2xyz, ivtx2);
134        let v01 = v1.sub(v0);
135        let v12 = v2.sub(v1);
136        let binormal = v12.cross(&v01).normalize();
137        crate::vtx2xyz::to_vec3_mut(&mut vtx2bin, ivtx1).copy_from_slice(&binormal);
138        let norm = v01.add(&v12).cross(&binormal).normalize();
139        crate::vtx2xyz::to_vec3_mut(&mut vtx2nrm, ivtx1).copy_from_slice(&norm);
140    }
141    (vtx2nrm, vtx2bin)
142}
143
144pub fn smooth_gradient_of_distance(vtx2xyz: &[f64], q: &[f64; 3]) -> [f64; 3] {
145    use del_geo_core::vec3::Vec3;
146    let n = vtx2xyz.len() / 3;
147    let mut dd = [0f64; 3];
148    for i_seg in 0..n {
149        let ip0 = i_seg;
150        let ip1 = (i_seg + 1) % n;
151        let (_, dd0) = del_geo_core::edge3::wdw_integral_of_inverse_distance_cubic(
152            q,
153            crate::vtx2xyz::to_vec3(vtx2xyz, ip0),
154            crate::vtx2xyz::to_vec3(vtx2xyz, ip1),
155        );
156        dd.add_in_place(&dd0);
157    }
158    dd
159}
160
161pub fn extend_avoid_intersection(
162    p0: &[f64; 3],
163    v0: &[f64; 3],
164    vtx2xyz: &[f64],
165    eps: f64,
166    n: usize,
167) -> [f64; 3] {
168    use del_geo_core::vec3::Vec3;
169    let mut p1 = p0.add(&v0.scale(eps));
170    for _i in 0..n {
171        let v1 = smooth_gradient_of_distance(vtx2xyz, &p1)
172            .normalize()
173            .scale(-1f64);
174        p1.add_in_place(&v1.scale(eps));
175    }
176    p1
177}
178
179pub fn tube_mesh_avoid_intersection(
180    vtx2xyz: &[f64],
181    vtx2bin: &[f64],
182    eps: f64,
183    niter: usize,
184) -> (Vec<usize>, Vec<f64>) {
185    use del_geo_core::vec3::Vec3;
186    let n = 8;
187    let dtheta = std::f64::consts::PI * 2. / n as f64;
188    let num_vtx = vtx2xyz.len() / 3;
189    let mut pnt2xyz = Vec::<f64>::new();
190    for ipnt in 0..num_vtx {
191        let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, ipnt);
192        let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, (ipnt + 1) % num_vtx);
193        let z0 = p1.sub(p0).normalize();
194        let x0 = crate::vtx2xyz::to_vec3(vtx2bin, ipnt);
195        let y0 = z0.cross(x0);
196        for i in 0..n {
197            let theta = dtheta * i as f64;
198            let x0 = x0.scale(theta.cos());
199            let y0 = y0.scale(theta.sin());
200            let v0 = x0.add(&y0);
201            let q0 = extend_avoid_intersection(p0, &v0, vtx2xyz, eps, niter);
202            // let q0 = p0 + v0.scale(rad);
203            q0.iter().for_each(|&v| pnt2xyz.push(v));
204        }
205    }
206
207    let mut tri2pnt = Vec::<usize>::new();
208    for iseg in 0..num_vtx {
209        let ipnt0 = iseg;
210        let ipnt1 = (ipnt0 + 1) % num_vtx;
211        for i in 0..n {
212            tri2pnt.push(ipnt0 * n + i);
213            tri2pnt.push(ipnt0 * n + (i + 1) % n);
214            tri2pnt.push(ipnt1 * n + i);
215            //
216            tri2pnt.push(ipnt1 * n + (i + 1) % n);
217            tri2pnt.push(ipnt1 * n + i);
218            tri2pnt.push(ipnt0 * n + (i + 1) % n);
219        }
220    }
221    (tri2pnt, pnt2xyz)
222}
223
224pub fn write_wavefrontobj<P: AsRef<std::path::Path>>(filepath: P, vtx2xyz: &[f32]) {
225    use std::io::Write;
226    let mut file = std::fs::File::create(filepath).expect("file not found.");
227    for vtx in vtx2xyz.chunks(3) {
228        writeln!(file, "v {} {} {}", vtx[0], vtx[1], vtx[2]).expect("fail");
229    }
230    write!(file, "l ").expect("fail");
231    for i in 1..vtx2xyz.len() / 3 + 1 {
232        write!(file, "{} ", i).expect("fail");
233    }
234    writeln!(file, "1").expect("fail");
235}
236
237pub fn nearest_to_edge3<T>(vtx2xyz: &[T], p0: &[T; 3], p1: &[T; 3]) -> (T, T, T)
238where
239    T: num_traits::Float + Copy + 'static,
240    usize: AsPrimitive<T>,
241{
242    let num_vtx = vtx2xyz.len() / 3;
243    assert_eq!(vtx2xyz.len(), num_vtx * 3);
244    let mut res = (T::max_value(), T::zero(), T::zero());
245    for i_edge in 0..num_vtx {
246        let iv0 = i_edge;
247        let iv1 = (i_edge + 1) % num_vtx;
248        let q0 = crate::vtx2xyz::to_vec3(vtx2xyz, iv0);
249        let q1 = crate::vtx2xyz::to_vec3(vtx2xyz, iv1);
250        let (dist, r0, r1) = del_geo_core::edge3::nearest_to_edge3(p0, p1, q0, q1);
251        if dist > res.0 {
252            continue;
253        }
254        //dbg!((p0+(p1-p0)*r0));
255        //dbg!((q0+(q1-q0)*r1));
256        res.0 = dist;
257        res.1 = <usize as AsPrimitive<T>>::as_(i_edge) + r1;
258        res.2 = r0;
259    }
260    res
261}
262
263pub fn nearest_to_point3<T>(vtx2xyz: &[T], p0: &[T; 3]) -> (T, T)
264where
265    T: num_traits::Float + Copy + 'static,
266    f64: AsPrimitive<T>,
267    usize: AsPrimitive<T>,
268{
269    assert_eq!(p0.len(), 3);
270    let num_vtx = vtx2xyz.len() / 3;
271    assert_eq!(vtx2xyz.len(), num_vtx * 3);
272    let mut res = (T::max_value(), T::zero());
273    for i_edge in 0..num_vtx {
274        let iv0 = i_edge;
275        let iv1 = (i_edge + 1) % num_vtx;
276        let q0 = crate::vtx2xyz::to_vec3(vtx2xyz, iv0);
277        let q1 = crate::vtx2xyz::to_vec3(vtx2xyz, iv1);
278        let (dist, rq) = del_geo_core::edge3::nearest_to_point3(q0, q1, p0);
279        if dist < res.0 {
280            //dbg!((p0+(p1-p0)*r0));
281            //dbg!((q0+(q1-q0)*r1));
282            res.0 = dist;
283            res.1 = <usize as AsPrimitive<T>>::as_(i_edge) + rq;
284        }
285    }
286    res
287}
288
289pub fn winding_number(vtx2xyz: &[f64], org: &[f64; 3], dir: &[f64; 3]) -> f64 {
290    use del_geo_core::vec3::Vec3;
291    use num_traits::FloatConst;
292    //let org = nalgebra::Vector3::<f64>::from_row_slice(org);
293    //let dir = nalgebra::Vector3::<f64>::from_row_slice(dir);
294    let num_vtx = vtx2xyz.len() / 3;
295    assert_eq!(vtx2xyz.len(), num_vtx * 3);
296    let mut sum = 0.;
297    for i_edge in 0..num_vtx {
298        let iv0 = i_edge;
299        let iv1 = (i_edge + 1) % num_vtx;
300        let q0 = crate::vtx2xyz::to_vec3(vtx2xyz, iv0).sub(org);
301        let q1 = crate::vtx2xyz::to_vec3(vtx2xyz, iv1).sub(org);
302        let q0 = q0.sub(&dir.scale(q0.dot(dir)));
303        let q1 = q1.sub(&dir.scale(q1.dot(dir)));
304        let q0 = q0.normalize();
305        let q1 = q1.normalize();
306        let s = q0.cross(&q1).dot(dir);
307        let c = q0.dot(&q1);
308        sum += s.atan2(c);
309    }
310    sum * f64::FRAC_1_PI() * 0.5
311}
312
313#[allow(clippy::identity_op)]
314pub fn position_from_barycentric_coordinate<T>(vtx2xyz: &[T], r: T) -> [T; 3]
315where
316    T: num_traits::Float + AsPrimitive<usize> + std::fmt::Display + std::fmt::Debug,
317    usize: AsPrimitive<T>,
318{
319    use del_geo_core::vec3::Vec3;
320    let ied: usize = r.as_();
321    let ned = vtx2xyz.len() / 3;
322    if r.as_() == ned {
323        assert_eq!(ied.as_(), r);
324        return *crate::vtx2xyz::to_vec3(vtx2xyz, 0);
325    }
326    assert!(ied < ned, "{}, {}, {}", r, ied, ned);
327    let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, ied);
328    let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, (ied + 1) % ned);
329    let r0 = r - ied.as_();
330    p0.add(&p1.sub(p0).scale(r0))
331}
332
333#[allow(clippy::identity_op)]
334pub fn smooth<T>(vtx2xyz: &[T], r: T, num_iter: usize) -> Vec<T>
335where
336    T: num_traits::Float + Copy + 'static,
337    f64: AsPrimitive<T>,
338{
339    use del_geo_core::vec3::Vec3;
340    let num_vtx = vtx2xyz.len() / 3;
341    let mut vtx2xyz1 = Vec::from(vtx2xyz);
342    for _iter in 0..num_iter {
343        for ip1 in 0..num_vtx {
344            let ip0 = (ip1 + num_vtx - 1) % num_vtx;
345            let ip2 = (ip1 + 1) % num_vtx;
346            let p0 = crate::vtx2xyz::to_vec3(&vtx2xyz1, ip0);
347            let p1 = crate::vtx2xyz::to_vec3(&vtx2xyz1, ip1);
348            let p2 = crate::vtx2xyz::to_vec3(&vtx2xyz1, ip2);
349            let pm = p0.add(p2).scale(0.5f64.as_());
350            let p1n = del_geo_core::edge3::position_from_ratio(p1, &pm, r);
351            vtx2xyz1[ip1 * 3 + 0] = p1n[0];
352            vtx2xyz1[ip1 * 3 + 1] = p1n[1];
353            vtx2xyz1[ip1 * 3 + 2] = p1n[2];
354        }
355    }
356    vtx2xyz1
357}
358
359/// TODO: it might be better to specify the normal vector
360pub fn to_trimesh3_torus(
361    vtx2xyz: &[f32],
362    vtx2bin: &[f32],
363    rad: f32,
364    ndiv_circum: usize,
365) -> (Vec<usize>, Vec<f32>) {
366    use del_geo_core::vec3::Vec3;
367    let n = ndiv_circum;
368    let dtheta = std::f32::consts::PI * 2. / n as f32;
369    let num_vtx = vtx2xyz.len() / 3;
370    let mut pnt2xyz = Vec::<f32>::new();
371    for ipnt in 0..num_vtx {
372        let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, ipnt);
373        let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, (ipnt + 1) % num_vtx);
374        let z0 = p1.sub(p0).normalize();
375        let x0 = crate::vtx2xyz::to_vec3(vtx2bin, ipnt);
376        let y0 = z0.cross(x0);
377        for i in 0..n {
378            let theta = dtheta * i as f32;
379            let v0 = x0.scale(theta.cos()).add(&y0.scale(theta.sin()));
380            let q0 = p0.add(&v0.scale(rad));
381            q0.iter().for_each(|&v| pnt2xyz.push(v));
382        }
383    }
384
385    let mut tri2pnt = Vec::<usize>::new();
386    for iseg in 0..num_vtx {
387        let ipnt0 = iseg;
388        let ipnt1 = (ipnt0 + 1) % num_vtx;
389        for i in 0..n {
390            tri2pnt.push(ipnt0 * n + i);
391            tri2pnt.push(ipnt0 * n + (i + 1) % n);
392            tri2pnt.push(ipnt1 * n + i);
393            //
394            tri2pnt.push(ipnt1 * n + (i + 1) % n);
395            tri2pnt.push(ipnt1 * n + i);
396            tri2pnt.push(ipnt0 * n + (i + 1) % n);
397        }
398    }
399    (tri2pnt, pnt2xyz)
400}