1use num_traits::AsPrimitive;
4
5pub fn winding_number<Real>(vtx2xy: &[Real], p: &[Real; 2]) -> Real
6where
7 Real: num_traits::Float + num_traits::FloatConst,
8{
9 let num_vtx = vtx2xy.len() / 2;
10 let mut wn: Real = Real::zero();
11 for i in 0..num_vtx {
12 let j = (i + 1) % num_vtx;
13 wn = wn
14 + del_geo_core::edge2::winding_number(
15 arrayref::array_ref![vtx2xy, i * 2, 2],
16 arrayref::array_ref![vtx2xy, j * 2, 2],
17 p,
18 );
19 }
20 wn
21}
22
23pub fn is_include_a_point<Real>(vtx2xy: &[Real], p: &[Real; 2]) -> bool
24where
25 Real: num_traits::Float + num_traits::FloatConst,
26{
27 let wn = winding_number(vtx2xy, p);
28 let one = Real::one();
29 let thres = one / (one + one + one + one + one);
30 if (wn - one).abs() < thres {
31 return true;
32 }
33 false
34}
35
36pub fn is_include_polyloop2<Real>(vtx2xy_outside: &[Real], vtx2xy_inside: &[Real]) -> bool
37where
38 Real: num_traits::Float + num_traits::FloatConst + std::fmt::Debug,
39{
40 dbg!("todo: make");
41 let mut is_out = false;
42 let one = Real::one();
43 let thres = one / (one + one + one + one + one);
44 for xy_in in vtx2xy_inside.chunks(2) {
45 let xy_in = [xy_in[0], xy_in[1]];
46 let wn = winding_number(vtx2xy_outside, &xy_in);
47 if (wn - one).abs() > thres {
48 is_out = true
49 }
50 }
51 is_out
52}
53
54pub fn maximum_penetration_of_included_point2s<Real>(
55 vtx2xy_outside: &[Real],
56 vtx2xy_inside: &[Real],
57) -> Option<([Real; 2], [Real; 2])>
58where
59 Real: num_traits::Float + num_traits::FloatConst + 'static + std::fmt::Debug,
60 usize: AsPrimitive<Real>,
61{
62 let zero = Real::zero();
63 let one = Real::one();
64 let thres = one / (one + one + one + one + one);
65 let mut dist_min: Option<Real> = None;
66 let mut pos_outside_min = [zero; 2];
67 let mut pos_inside_min = [zero; 2];
68 for xy_in in vtx2xy_inside.chunks(2) {
69 let xy_in = [xy_in[0], xy_in[1]];
70 let wn = winding_number(vtx2xy_outside, &xy_in);
71 if (wn - one).abs() < thres {
72 continue;
73 }
74 let (_lcoord, po) = nearest_to_point(vtx2xy_outside, &xy_in).unwrap();
75 let dist = del_geo_core::edge2::length(&xy_in, &po);
76 let is_update = if let Some(dist_min) = dist_min {
77 dist > dist_min
78 } else {
79 true
80 };
81 if is_update {
82 dist_min = Some(dist);
83 pos_outside_min = po;
84 pos_inside_min = xy_in;
85 }
86 }
87 let _dist_min = dist_min?;
88 Some((pos_outside_min, pos_inside_min))
89}
90
91pub fn area<T>(vtx2xy: &[T]) -> T
93where
94 T: num_traits::Float,
95{
96 let num_vtx = vtx2xy.len() / 2;
97 assert_eq!(vtx2xy.len(), num_vtx * 2);
98 let zero = [T::zero(), T::zero()];
99 let mut area = T::zero();
100 for i_edge in 0..num_vtx {
101 let i0 = i_edge;
102 let i1 = (i_edge + 1) % num_vtx;
103 let p0 = arrayref::array_ref![vtx2xy, i0 * 2, 2];
104 let p1 = arrayref::array_ref![vtx2xy, i1 * 2, 2];
105 area = area + del_geo_core::tri2::area(&zero, p0, p1);
106 }
107 area
108}
109
110pub fn cog_as_face<T>(vtx2xy: &[T]) -> [T; 2]
112where
113 T: num_traits::Float + std::ops::AddAssign + std::ops::DivAssign,
114{
115 let frac_three = T::one() / (T::one() + T::one() + T::one());
116 let num_vtx = vtx2xy.len() / 2;
117 assert_eq!(vtx2xy.len(), num_vtx * 2);
118 let zero = [T::zero(); 2];
119 let mut area = T::zero();
120 let mut cog = [T::zero(); 2];
121 for i_edge in 0..num_vtx {
122 let i0 = i_edge;
123 let i1 = (i_edge + 1) % num_vtx;
124 let p0 = arrayref::array_ref![vtx2xy, i0 * 2, 2];
125 let p1 = arrayref::array_ref![vtx2xy, i1 * 2, 2];
126 let area0 = del_geo_core::tri2::area(&zero, p0, p1);
127 area += area0;
128 cog[0] += (p0[0] + p1[0]) * frac_three * area0;
129 cog[1] += (p0[1] + p1[1]) * frac_three * area0;
130 }
131 cog[0] /= area;
132 cog[1] /= area;
133 cog
134}
135
136#[test]
137fn test_cog_() {
138 let vtx2xy: Vec<f32> = vec![
139 -1.0, -5.0, -0.5, -5.0, 0.5, -5.0, 1.0, -5.0, 1.0, 5.0, -1.0, 5.0,
140 ];
141 let cog = cog_as_face(&vtx2xy);
142 assert!(cog[0].abs() < 1.0e-8);
143 assert!(cog[1].abs() < 1.0e-8);
144}
145
146pub fn from_pentagram<Real>(center: &[Real], scale: Real) -> Vec<Real>
148where
149 Real: num_traits::Float + num_traits::FloatConst,
150{
151 let one = Real::one();
152 let two = one + one;
153 let three = two + one;
154 let five = two + three;
155 let dt: Real = Real::PI() / five;
156 let hp: Real = Real::FRAC_PI_2();
157 let ratio = two / (three + five.sqrt());
158 let mut xys = Vec::<Real>::new();
159 for i in 0..10usize {
160 let rad = if i % 2 == 0 { scale } else { ratio * scale };
161 let i = Real::from(rad).unwrap();
162 xys.push((dt * i + hp).cos() * rad + center[0]);
163 xys.push((dt * i + hp).sin() * rad + center[1]);
164 }
165 xys
166}
167
168pub fn from_circle(rad: f32, n: usize) -> Vec<f32> {
169 let mut vtx2xy = vec![0f32; 2 * n];
170 for i in 0..n {
171 let theta = std::f32::consts::PI * 2_f32 * i as f32 / n as f32;
172 vtx2xy[i * 2] = rad * f32::cos(theta);
173 vtx2xy[i * 2 + 1] = rad * f32::sin(theta);
174 }
175 vtx2xy
176}
177
178pub fn distance_to_point<Real>(vtx2xy: &[Real], g: &[Real; 2]) -> Option<Real>
179where
180 Real: num_traits::Float + std::fmt::Debug + 'static,
181 usize: AsPrimitive<Real>,
182{
183 let (_local_coord, pos) = nearest_to_point(vtx2xy, g)?;
184 let dist = del_geo_core::edge2::length(&pos, g);
185 Some(dist)
186}
187
188pub fn nearest_to_point<Real>(vtx2xy: &[Real], g: &[Real; 2]) -> Option<(Real, [Real; 2])>
189where
190 Real: num_traits::Float + std::fmt::Debug + 'static,
191 usize: AsPrimitive<Real>,
192{
193 let np = vtx2xy.len() / 2;
195 let mut dist_min: Option<Real> = None;
196 let mut p_near = [Real::zero(), Real::zero()];
197 let mut i_edge_min = usize::MAX;
198 let mut ratio_min = Real::zero();
199 for ip in 0..np {
200 let jp = (ip + 1) % np;
201 let pi = crate::vtx2xy::to_vec2(vtx2xy, ip);
202 let pj = crate::vtx2xy::to_vec2(vtx2xy, jp);
203 let (ratio, pos) = del_geo_core::edge2::nearest_to_point(pi, pj, g);
204 let dist = del_geo_core::edge2::length(&pos, g);
205 let is_update = if let Some(dist_min) = dist_min {
206 dist < dist_min
207 } else {
208 true
209 };
210 if is_update {
211 dist_min = Some(dist);
212 p_near = pos;
213 i_edge_min = ip;
214 ratio_min = ratio;
215 };
216 }
217 dist_min.map(|_dist_min| (i_edge_min.as_() + ratio_min, p_near))
218}
219
220pub fn moment_of_inertia(vtx2xy: &[f32], pivot: &[f32; 2]) -> f32 {
221 use del_geo_core::vec2;
222 let ne = vtx2xy.len() / 2;
223 let mut sum_i = 0.0;
224 for ie in 0..ne {
225 let ip0 = ie;
226 let ip1 = (ie + 1) % ne;
227 let p0 = [vtx2xy[ip0 * 2] - pivot[0], vtx2xy[ip0 * 2 + 1] - pivot[1]];
228 let p1 = [vtx2xy[ip1 * 2] - pivot[0], vtx2xy[ip1 * 2 + 1] - pivot[1]];
229 let a0 = vec2::area_quadrilateral(&p0, &p1) * 0.5;
230 sum_i += a0 * (vec2::dot(&p0, &p0) + vec2::dot(&p0, &p1) + vec2::dot(&p1, &p1));
231 }
232 sum_i * (1.0 / 6.0)
233}
234
235pub fn wdw_sdf(vtx2xy: &[f32], q: &[f32; 2]) -> (f32, [f32; 2]) {
239 use del_geo_core::vec2;
240 let nej = vtx2xy.len() / 2;
241 let mut min_dist = -1.0;
242 let mut winding_number = 0f32;
243 let mut pos_near = [0f32; 2];
244 let mut ie_near = 0;
245 for iej in 0..nej {
246 let ps = arrayref::array_ref!(vtx2xy, (iej % nej) * 2, 2);
247 let pe = arrayref::array_ref!(vtx2xy, ((iej + 1) % nej) * 2, 2);
248 winding_number += del_geo_core::edge2::winding_number(ps, pe, q);
249 let (_rm, pm) = del_geo_core::edge2::nearest_to_point(ps, pe, q);
250 let dist0 = del_geo_core::edge2::length(&pm, q);
251 if min_dist > 0. && dist0 > min_dist {
252 continue;
253 }
254 min_dist = dist0;
255 pos_near = pm;
256 ie_near = iej;
257 }
258 let normal_out = {
260 let ps = arrayref::array_ref!(vtx2xy, (ie_near % nej) * 2, 2);
262 let pe = arrayref::array_ref!(vtx2xy, ((ie_near + 1) % nej) * 2, 2);
263 let ne = vec2::sub(pe, ps);
264 let ne = vec2::rotate(&ne, -std::f32::consts::PI * 0.5);
265 vec2::normalize(&ne)
266 };
267 if (winding_number - 1.0).abs() < 0.5 {
270 let normal = if min_dist < 1.0e-5 {
272 normal_out
273 } else {
274 vec2::normalize(&vec2::sub(&pos_near, q))
275 };
276 (-min_dist, normal)
277 } else {
278 let normal = if min_dist < 1.0e-5 {
279 normal_out
280 } else {
281 vec2::normalize(&vec2::sub(q, &pos_near))
282 };
283 (min_dist, normal)
284 }
285}
286
287#[test]
288fn test_polygon2_sdf() {
289 let vtx2xy = vec![0., 0., 1.0, 0.0, 1.0, 0.2, 0.0, 0.2];
290 use del_geo_core::vec2;
291 {
292 let (sdf, normal) = wdw_sdf(&vtx2xy, &[0.01, 0.1]);
293 assert!((sdf + 0.01).abs() < 1.0e-5);
294 assert!(vec2::length(&vec2::sub(&normal, &[-1., 0.])) < 1.0e-5);
295 }
296 {
297 let (sdf, normal) = wdw_sdf(&vtx2xy, &[-0.01, 0.1]);
298 assert!((sdf - 0.01).abs() < 1.0e-5);
299 assert!(vec2::length(&vec2::sub(&normal, &[-1., 0.])) < 1.0e-5);
300 }
301}
302
303pub fn to_uniform_density_random_points<Real>(
304 vtx2xy: &[Real],
305 cell_len: Real,
306 rng: &mut rand::rngs::StdRng,
307) -> Vec<Real>
308where
309 Real: num_traits::Float + num_traits::FloatConst + AsPrimitive<usize>,
310 rand::distr::StandardUniform: rand::distr::Distribution<Real>,
311 usize: AsPrimitive<Real>,
312{
313 let aabb = crate::vtx2xy::aabb2(vtx2xy);
314 use rand::Rng;
315 let base_pos = [
316 aabb[0] - cell_len * rng.random::<Real>(),
317 aabb[1] - cell_len * rng.random::<Real>(),
318 ];
319 let nx = ((aabb[2] - base_pos[0]) / cell_len).as_() + 1;
320 let ny = ((aabb[3] - base_pos[1]) / cell_len).as_() + 1;
321 let mut res = vec![];
322 for ix in 0..nx {
323 for iy in 0..ny {
324 let x = base_pos[0] + (ix.as_() + rng.random::<Real>()) * cell_len;
325 let y = base_pos[1] + (iy.as_() + rng.random::<Real>()) * cell_len;
326 let is_inside = is_include_a_point(vtx2xy, &[x, y]);
327 if !is_inside {
328 continue;
329 }
330 res.push(x);
331 res.push(y);
332 }
333 }
334 res
335}
336
337#[allow(clippy::identity_op)]
338pub fn to_svg<Real>(vtx2xy: &[Real], transform: &[Real; 9]) -> String
339where
340 Real: std::fmt::Display + Copy + num_traits::Float,
341{
342 let mut res = String::new();
343 for ivtx in 0..vtx2xy.len() / 2 {
344 let x = vtx2xy[ivtx * 2 + 0];
345 let y = vtx2xy[ivtx * 2 + 1];
346 let a = del_geo_core::mat3_col_major::transform_homogeneous(transform, &[x, y]).unwrap();
347 res += format!("{} {}", a[0], a[1]).as_str();
348 if ivtx != vtx2xy.len() / 2 - 1 {
349 res += ",";
350 }
351 }
352 res
353}
354
355#[test]
356fn test_circle() {
357 let vtx2xy0 = from_circle(1.0, 300);
358 let arclen0 = crate::polyloop::arclength::<f32, 2>(&vtx2xy0);
359 assert!((arclen0 - 2. * std::f32::consts::PI).abs() < 1.0e-3);
360 {
362 let ndiv1 = 330;
363 let vtx2xy1 = crate::polyloop::resample::<f32, 2>(vtx2xy0.as_slice(), ndiv1);
364 assert_eq!(vtx2xy1.len(), ndiv1 * 2);
365 let arclen1 = crate::polyloop::arclength::<f32, 2>(vtx2xy1.as_slice());
366 assert!((arclen0 - arclen1).abs() < 1.0e-3);
367 let edge2length1 = crate::polyloop::edge2length::<f32, 2>(vtx2xy1.as_slice());
368 let min_edge_len1 = edge2length1
369 .iter()
370 .min_by(|a, b| a.partial_cmp(b).unwrap())
371 .unwrap();
372 assert!((min_edge_len1 - arclen1 / ndiv1 as f32).abs() < 1.0e-3);
373 }
374 {
375 let ndiv2 = 156;
376 let vtx2xy2 = crate::polyloop::resample::<f32, 2>(vtx2xy0.as_slice(), ndiv2);
377 assert_eq!(vtx2xy2.len(), ndiv2 * 2);
378 let arclen2 = crate::polyloop::arclength::<f32, 2>(vtx2xy2.as_slice());
379 assert!((arclen0 - arclen2).abs() < 1.0e-3);
380 let edge2length2 = crate::polyloop::edge2length::<f32, 2>(vtx2xy2.as_slice());
381 let min_edge_len2 = edge2length2
382 .iter()
383 .min_by(|a, b| a.partial_cmp(b).unwrap())
384 .unwrap();
385 assert!((min_edge_len2 - arclen2 / ndiv2 as f32).abs() < 1.0e-3);
386 }
387}
388
389pub fn meshing_to_trimesh2<Index, Real>(
390 vtxl2xy: &[Real],
391 edge_length_boundary: Real,
392 edge_length_internal: Real,
393) -> (Vec<Index>, Vec<Real>)
394where
395 Real: Copy
396 + 'static
397 + num_traits::Float
398 + AsPrimitive<usize>
399 + std::fmt::Display
400 + std::fmt::Debug,
401 Index: Copy + 'static,
402 f64: AsPrimitive<Real>,
403 usize: AsPrimitive<Real> + AsPrimitive<Index>,
404{
405 crate::trimesh2_dynamic::meshing_from_polyloop2::<Index, Real>(
406 vtxl2xy,
407 edge_length_boundary,
408 edge_length_internal,
409 )
410}
411
412pub fn poisson_disk_sampling<RNG>(
413 vtxl2xy: &[f32],
414 radius: f32,
415 num_iteration: usize,
416 reng: &mut RNG,
417) -> Vec<f32>
418where
419 RNG: rand::Rng,
420{
421 use del_geo_core::vec2::Vec2;
422 let (tri2vtx, vtx2xyz) =
423 crate::trimesh2_dynamic::meshing_from_polyloop2::<usize, f32>(vtxl2xy, -1., -1.);
424 let tri2cumarea = crate::trimesh::tri2cumsumarea(&tri2vtx, &vtx2xyz, 2);
425 let mut vtx2vectwo: Vec<[f32; 2]> = vec![];
426 for _iter in 0..num_iteration {
427 let (i_tri, r0, r1) =
428 crate::trimesh::sample_uniformly(&tri2cumarea, reng.random(), reng.random());
429 let pos = crate::trimesh::position_from_barycentric_coordinate::<f32, 2>(
430 &tri2vtx, &vtx2xyz, i_tri, r0, r1,
431 );
432 let mut is_near = false;
433 for pos0 in &vtx2vectwo {
434 if pos0.sub(&pos).norm() > radius {
436 continue;
437 }
438 is_near = true;
439 break;
440 }
441 if is_near {
442 continue;
443 }
444 vtx2vectwo.push(pos);
445 }
446 use slice_of_array::SliceFlatExt;
447 vtx2vectwo.flat().to_vec()
448}
449
450#[test]
451fn test_poisson_disk_sampling() {
452 let mut reng = rand::rng();
453 let vtxl2xy = vec![0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0];
454 let vtx2xy = poisson_disk_sampling(&vtxl2xy, 0.1, 2000, &mut reng);
455 {
456 let mut vtxl2xy = vtxl2xy.clone();
458 vtxl2xy.extend(vtx2xy);
459 crate::io_obj::save_edge2vtx_vtx2xyz(
460 "../target/poisson_disk.obj",
461 &[0, 1, 1, 2, 2, 3, 3, 0],
462 &vtxl2xy,
463 2,
464 )
465 .unwrap();
466 }
467}