1use crate::pde::finite_element::{ElementType, Point, Triangle, TriangularMesh};
8use crate::pde::{PDEError, PDEResult};
9use std::collections::{HashMap, HashSet};
10use std::f64::consts::PI;
11
12#[derive(Debug, Clone)]
14pub struct MeshGenerationParams {
15 pub element_size: f64,
17 pub min_angle: f64,
19 pub max_angle: f64,
21 pub quality_iterations: usize,
23 pub element_type: ElementType,
25 pub boundary_refinement_iterations: usize,
27}
28
29impl Default for MeshGenerationParams {
30 fn default() -> Self {
31 Self {
32 element_size: 0.1,
33 min_angle: 20.0,
34 max_angle: 140.0,
35 quality_iterations: 5,
36 element_type: ElementType::Linear,
37 boundary_refinement_iterations: 3,
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44pub enum Domain {
45 Rectangle {
47 x_min: f64,
48 y_min: f64,
49 x_max: f64,
50 y_max: f64,
51 },
52 Circle {
54 center_x: f64,
55 center_y: f64,
56 radius: f64,
57 },
58 Ellipse {
60 center_x: f64,
61 center_y: f64,
62 a: f64,
63 b: f64,
64 rotation: f64,
65 },
66 LShape {
68 width: f64,
69 height: f64,
70 notch_width: f64,
71 notch_height: f64,
72 },
73 Polygon { vertices: Vec<Point> },
75 Annulus {
77 center_x: f64,
78 center_y: f64,
79 inner_radius: f64,
80 outer_radius: f64,
81 },
82}
83
84#[derive(Debug, Clone, Default)]
86pub struct BoundarySpecification {
87 pub boundary_markers: HashMap<String, i32>,
89 pub point_markers: HashMap<String, i32>,
91}
92
93#[derive(Debug, Clone)]
95pub struct MeshQuality {
96 pub min_angle: f64,
98 pub max_angle: f64,
100 pub avg_element_size: f64,
102 pub min_aspect_ratio: f64,
104 pub poor_quality_elements: usize,
106 pub quality_score: f64,
108}
109
110pub struct AutoMeshGenerator {
112 params: MeshGenerationParams,
113}
114
115impl Default for AutoMeshGenerator {
116 fn default() -> Self {
117 Self::new(MeshGenerationParams::default())
118 }
119}
120
121impl AutoMeshGenerator {
122 pub fn new(params: MeshGenerationParams) -> Self {
124 Self { params }
125 }
126
127 pub fn with_default_params(&self) -> Self {
129 Self::default()
130 }
131
132 pub fn generatemesh(
134 &mut self,
135 domain: &Domain,
136 boundary_spec: &BoundarySpecification,
137 ) -> PDEResult<TriangularMesh> {
138 match domain {
139 Domain::Rectangle {
140 x_min,
141 y_min,
142 x_max,
143 y_max,
144 } => self.generate_rectanglemesh(*x_min, *y_min, *x_max, *y_max, boundary_spec),
145 Domain::Circle {
146 center_x,
147 center_y,
148 radius,
149 } => self.generate_circlemesh(*center_x, *center_y, *radius, boundary_spec),
150 Domain::Ellipse {
151 center_x,
152 center_y,
153 a,
154 b,
155 rotation,
156 } => self.generate_ellipsemesh(*center_x, *center_y, *a, *b, *rotation, boundary_spec),
157 Domain::LShape {
158 width,
159 height,
160 notch_width,
161 notch_height,
162 } => self.generate_lshapemesh(
163 *width,
164 *height,
165 *notch_width,
166 *notch_height,
167 boundary_spec,
168 ),
169 Domain::Polygon { vertices } => self.generate_polygonmesh(vertices, boundary_spec),
170 Domain::Annulus {
171 center_x,
172 center_y,
173 inner_radius,
174 outer_radius,
175 } => self.generate_annulusmesh(
176 *center_x,
177 *center_y,
178 *inner_radius,
179 *outer_radius,
180 boundary_spec,
181 ),
182 }
183 }
184
185 fn generate_rectanglemesh(
187 &mut self,
188 x_min: f64,
189 y_min: f64,
190 x_max: f64,
191 y_max: f64,
192 boundary_spec: &BoundarySpecification,
193 ) -> PDEResult<TriangularMesh> {
194 let width = x_max - x_min;
195 let height = y_max - y_min;
196
197 let nx = ((width / self.params.element_size).ceil() as usize).max(2);
199 let ny = ((height / self.params.element_size).ceil() as usize).max(2);
200
201 let dx = width / (nx - 1) as f64;
202 let dy = height / (ny - 1) as f64;
203
204 let mut points = Vec::new();
206 for j in 0..ny {
207 for i in 0..nx {
208 let x = x_min + i as f64 * dx;
209 let y = y_min + j as f64 * dy;
210 points.push(Point::new(x, y));
211 }
212 }
213
214 let mut triangles = Vec::new();
216 for j in 0..ny - 1 {
217 for i in 0..nx - 1 {
218 let idx = |row: usize, col: usize| row * nx + col;
219
220 let p0 = idx(j, i);
221 let p1 = idx(j, i + 1);
222 let p2 = idx(j + 1, i);
223 let p3 = idx(j + 1, i + 1);
224
225 triangles.push(Triangle::new([p0, p1, p2], Some(1)));
227 triangles.push(Triangle::new([p1, p3, p2], Some(1)));
229 }
230 }
231
232 let mut mesh = TriangularMesh::new();
233 mesh.points = points;
234 mesh.elements = triangles;
235 self.apply_boundary_markers(
236 &mut mesh,
237 boundary_spec,
238 &Domain::Rectangle {
239 x_min,
240 y_min,
241 x_max,
242 y_max,
243 },
244 )?;
245 self.improvemesh_quality(&mut mesh)?;
246
247 Ok(mesh)
248 }
249
250 fn generate_circlemesh(
252 &mut self,
253 center_x: f64,
254 center_y: f64,
255 radius: f64,
256 boundary_spec: &BoundarySpecification,
257 ) -> PDEResult<TriangularMesh> {
258 let circumference = 2.0 * PI * radius;
260 let n_theta = ((circumference / self.params.element_size).ceil() as usize).max(8);
261 let n_r = ((radius / self.params.element_size).ceil() as usize).max(2);
262
263 let mut points = Vec::new();
264 let mut triangles = Vec::new();
265
266 points.push(Point::new(center_x, center_y));
268
269 for i in 1..=n_r {
271 let r = radius * i as f64 / n_r as f64;
272 for j in 0..n_theta {
273 let theta = 2.0 * PI * j as f64 / n_theta as f64;
274 let _x = center_x + r * theta.cos();
275 let y = center_y + r * theta.sin();
276 points.push(Point::new(_x, y));
277 }
278 }
279
280 for j in 0..n_theta {
283 let p1 = 1 + j;
284 let p2 = 1 + (j + 1) % n_theta;
285 triangles.push(Triangle::new([0, p1, p2], Some(1)));
286 }
287
288 for i in 0..n_r - 1 {
290 let ring1_start = 1 + i * n_theta;
291 let ring2_start = 1 + (i + 1) * n_theta;
292
293 for j in 0..n_theta {
294 let p1 = ring1_start + j;
295 let p2 = ring1_start + (j + 1) % n_theta;
296 let p3 = ring2_start + j;
297 let p4 = ring2_start + (j + 1) % n_theta;
298
299 triangles.push(Triangle::new([p1, p2, p3], Some(1)));
300 triangles.push(Triangle::new([p2, p4, p3], Some(1)));
301 }
302 }
303
304 let mut mesh = TriangularMesh::new();
305 mesh.points = points;
306 mesh.elements = triangles;
307 self.apply_boundary_markers(
308 &mut mesh,
309 boundary_spec,
310 &Domain::Circle {
311 center_x,
312 center_y,
313 radius,
314 },
315 )?;
316 self.improvemesh_quality(&mut mesh)?;
317
318 Ok(mesh)
319 }
320
321 fn generate_ellipsemesh(
323 &mut self,
324 center_x: f64,
325 center_y: f64,
326 a: f64,
327 b: f64,
328 rotation: f64,
329 boundary_spec: &BoundarySpecification,
330 ) -> PDEResult<TriangularMesh> {
331 let max_radius = a.max(b);
333 let mut mesh =
334 self.generate_circlemesh(0.0, 0.0, max_radius, &BoundarySpecification::default())?;
335
336 let cos_rot = rotation.cos();
338 let sin_rot = rotation.sin();
339
340 for point in &mut mesh.points {
341 point.x *= a / max_radius;
343 point.y *= b / max_radius;
344
345 let x_rot = point.x * cos_rot - point.y * sin_rot;
347 let y_rot = point.x * sin_rot + point.y * cos_rot;
348
349 point.x = center_x + x_rot;
351 point.y = center_y + y_rot;
352 }
353
354 self.apply_boundary_markers(
355 &mut mesh,
356 boundary_spec,
357 &Domain::Ellipse {
358 center_x,
359 center_y,
360 a,
361 b,
362 rotation,
363 },
364 )?;
365 self.improvemesh_quality(&mut mesh)?;
366
367 Ok(mesh)
368 }
369
370 fn generate_lshapemesh(
372 &mut self,
373 width: f64,
374 height: f64,
375 notch_width: f64,
376 notch_height: f64,
377 boundary_spec: &BoundarySpecification,
378 ) -> PDEResult<TriangularMesh> {
379 let mesh1 = self.generate_rectanglemesh(
381 0.0,
382 0.0,
383 width,
384 height - notch_height,
385 &BoundarySpecification::default(),
386 )?;
387 let mesh2 = self.generate_rectanglemesh(
388 0.0,
389 height - notch_height,
390 width - notch_width,
391 height,
392 &BoundarySpecification::default(),
393 )?;
394
395 let combinedmesh = AutoMeshGenerator::combinemeshes(&[mesh1, mesh2])?;
397 let mut mesh = combinedmesh;
398
399 self.apply_boundary_markers(
400 &mut mesh,
401 boundary_spec,
402 &Domain::LShape {
403 width,
404 height,
405 notch_width,
406 notch_height,
407 },
408 )?;
409 self.improvemesh_quality(&mut mesh)?;
410
411 Ok(mesh)
412 }
413
414 fn generate_polygonmesh(
416 &self,
417 vertices: &[Point],
418 boundary_spec: &BoundarySpecification,
419 ) -> PDEResult<TriangularMesh> {
420 if vertices.len() < 3 {
421 return Err(PDEError::FiniteElementError(
422 "Polygon must have at least 3 vertices".to_string(),
423 ));
424 }
425
426 let mut points = vertices.to_vec();
429 let mut triangles = Vec::new();
430
431 let (min_x, max_x) = vertices
433 .iter()
434 .map(|p| p.x)
435 .fold((f64::INFINITY, f64::NEG_INFINITY), |(min, max), x| {
436 (min.min(x), max.max(x))
437 });
438 let (min_y, max_y) = vertices
439 .iter()
440 .map(|p| p.y)
441 .fold((f64::INFINITY, f64::NEG_INFINITY), |(min, max), y| {
442 (min.min(y), max.max(y))
443 });
444
445 let nx = ((max_x - min_x) / self.params.element_size) as usize;
447 let ny = ((max_y - min_y) / self.params.element_size) as usize;
448
449 for i in 1..nx {
450 for j in 1..ny {
451 let x = min_x + (max_x - min_x) * i as f64 / nx as f64;
452 let y = min_y + (max_y - min_y) * j as f64 / ny as f64;
453 let point = Point::new(x, y);
454
455 if AutoMeshGenerator::point_in_polygon(&point, vertices) {
457 points.push(point);
458 }
459 }
460 }
461
462 for i in 1..vertices.len() - 1 {
464 triangles.push(Triangle::new([0, i, i + 1], Some(1)));
465 }
466
467 let mut mesh = TriangularMesh::new();
468 mesh.points = points;
469 mesh.elements = triangles;
470 self.apply_boundary_markers(
471 &mut mesh,
472 boundary_spec,
473 &Domain::Polygon {
474 vertices: vertices.to_vec(),
475 },
476 )?;
477
478 Ok(mesh)
479 }
480
481 fn generate_annulusmesh(
483 &mut self,
484 center_x: f64,
485 center_y: f64,
486 inner_radius: f64,
487 outer_radius: f64,
488 boundary_spec: &BoundarySpecification,
489 ) -> PDEResult<TriangularMesh> {
490 if inner_radius >= outer_radius {
491 return Err(PDEError::FiniteElementError(
492 "Inner _radius must be less than outer _radius".to_string(),
493 ));
494 }
495
496 let n_theta = ((2.0 * PI * outer_radius / self.params.element_size).ceil() as usize).max(8);
498 let n_r =
499 (((outer_radius - inner_radius) / self.params.element_size).ceil() as usize).max(2);
500
501 let mut points = Vec::new();
502 let mut triangles = Vec::new();
503
504 for i in 0..=n_r {
506 let r = inner_radius + (outer_radius - inner_radius) * i as f64 / n_r as f64;
507 for j in 0..n_theta {
508 let theta = 2.0 * PI * j as f64 / n_theta as f64;
509 let _x = center_x + r * theta.cos();
510 let y = center_y + r * theta.sin();
511 points.push(Point::new(_x, y));
512 }
513 }
514
515 for i in 0..n_r {
517 let ring1_start = i * n_theta;
518 let ring2_start = (i + 1) * n_theta;
519
520 for j in 0..n_theta {
521 let p1 = ring1_start + j;
522 let p2 = ring1_start + (j + 1) % n_theta;
523 let p3 = ring2_start + j;
524 let p4 = ring2_start + (j + 1) % n_theta;
525
526 triangles.push(Triangle::new([p1, p2, p3], Some(1)));
527 triangles.push(Triangle::new([p2, p4, p3], Some(1)));
528 }
529 }
530
531 let mut mesh = TriangularMesh::new();
532 mesh.points = points;
533 mesh.elements = triangles;
534 self.apply_boundary_markers(
535 &mut mesh,
536 boundary_spec,
537 &Domain::Annulus {
538 center_x,
539 center_y,
540 inner_radius,
541 outer_radius,
542 },
543 )?;
544 self.improvemesh_quality(&mut mesh)?;
545
546 Ok(mesh)
547 }
548
549 fn apply_boundary_markers(
551 &self,
552 mesh: &mut TriangularMesh,
553 boundary_spec: &BoundarySpecification,
554 _domain: &Domain,
555 ) -> PDEResult<()> {
556 if boundary_spec.boundary_markers.is_empty() {
561 let boundary_edges = AutoMeshGenerator::find_boundary_edges(mesh);
563 for _p1_p2 in boundary_edges {
564 }
567 }
568
569 Ok(())
570 }
571
572 fn find_boundary_edges(mesh: &TriangularMesh) -> Vec<(usize, usize)> {
574 let mut edge_count = HashMap::new();
575
576 for element in &mesh.elements {
578 let [p1, p2, p3] = element.nodes;
579 let edges = [
580 (p1.min(p2), p1.max(p2)),
581 (p2.min(p3), p2.max(p3)),
582 (p3.min(p1), p3.max(p1)),
583 ];
584
585 for edge in &edges {
586 *edge_count.entry(*edge).or_insert(0) += 1;
587 }
588 }
589
590 edge_count
592 .into_iter()
593 .filter(|(_, count)| *count == 1)
594 .map(|(edge, _)| edge)
595 .collect()
596 }
597
598 fn improvemesh_quality(&mut self, mesh: &mut TriangularMesh) -> PDEResult<()> {
600 for _ in 0..self.params.quality_iterations {
601 self.laplacian_smoothing(mesh)?;
603
604 self.quality_refinement(mesh)?;
606 }
607
608 Ok(())
609 }
610
611 fn laplacian_smoothing(&mut self, mesh: &mut TriangularMesh) -> PDEResult<()> {
613 let n_points = mesh.points.len();
614 let mut new_positions = vec![Point::new(0.0, 0.0); n_points];
615 let mut neighbor_counts = vec![0; n_points];
616
617 for element in &mesh.elements {
619 let [p1, p2, p3] = element.nodes;
620
621 new_positions[p1].x += mesh.points[p2].x + mesh.points[p3].x;
622 new_positions[p1].y += mesh.points[p2].y + mesh.points[p3].y;
623 neighbor_counts[p1] += 2;
624
625 new_positions[p2].x += mesh.points[p1].x + mesh.points[p3].x;
626 new_positions[p2].y += mesh.points[p1].y + mesh.points[p3].y;
627 neighbor_counts[p2] += 2;
628
629 new_positions[p3].x += mesh.points[p1].x + mesh.points[p2].x;
630 new_positions[p3].y += mesh.points[p1].y + mesh.points[p2].y;
631 neighbor_counts[p3] += 2;
632 }
633
634 let boundary_edges = AutoMeshGenerator::find_boundary_edges(mesh);
636 let boundary_points: HashSet<usize> = boundary_edges
637 .iter()
638 .flat_map(|(p1, p2)| vec![*p1, *p2])
639 .collect();
640
641 for i in 0..n_points {
642 if !boundary_points.contains(&i) && neighbor_counts[i] > 0 {
643 mesh.points[i].x = new_positions[i].x / neighbor_counts[i] as f64;
644 mesh.points[i].y = new_positions[i].y / neighbor_counts[i] as f64;
645 }
646 }
647
648 Ok(())
649 }
650
651 fn quality_refinement(&mut self, mesh: &mut TriangularMesh) -> PDEResult<()> {
653 let mut elements_to_refine = Vec::new();
654
655 for (i, element) in mesh.elements.iter().enumerate() {
657 let quality = self.element_quality(mesh, element);
658 if quality.min_angle < self.params.min_angle
659 || quality.max_angle > self.params.max_angle
660 {
661 elements_to_refine.push(i);
662 }
663 }
664
665 Ok(())
669 }
670
671 fn element_quality(&self, mesh: &TriangularMesh, element: &Triangle) -> ElementQuality {
673 let [p1, p2, p3] = element.nodes;
674 let a = &mesh.points[p1];
675 let b = &mesh.points[p2];
676 let c = &mesh.points[p3];
677
678 let ab = ((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt();
680 let bc = ((c.x - b.x).powi(2) + (c.y - b.y).powi(2)).sqrt();
681 let ca = ((a.x - c.x).powi(2) + (a.y - c.y).powi(2)).sqrt();
682
683 let angle_a =
685 ((bc.powi(2) + ca.powi(2) - ab.powi(2)) / (2.0 * bc * ca)).acos() * 180.0 / PI;
686 let angle_b =
687 ((ca.powi(2) + ab.powi(2) - bc.powi(2)) / (2.0 * ca * ab)).acos() * 180.0 / PI;
688 let angle_c = 180.0 - angle_a - angle_b;
689
690 let min_angle = angle_a.min(angle_b).min(angle_c);
691 let max_angle = angle_a.max(angle_b).max(angle_c);
692
693 let area = 0.5 * ((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)).abs();
695
696 let s = (ab + bc + ca) / 2.0;
698 let inradius = area / s;
699 let circumradius = (ab * bc * ca) / (4.0 * area);
700 let aspect_ratio = circumradius / inradius;
701
702 ElementQuality {
703 min_angle,
704 max_angle,
705 area,
706 aspect_ratio,
707 }
708 }
709
710 fn point_in_polygon(point: &Point, polygon: &[Point]) -> bool {
712 let mut inside = false;
713 let mut j = polygon.len() - 1;
714
715 for i in 0..polygon.len() {
716 if ((polygon[i].y > point.y) != (polygon[j].y > point.y))
717 && (point.x
718 < (polygon[j].x - polygon[i].x) * (point.y - polygon[i].y)
719 / (polygon[j].y - polygon[i].y)
720 + polygon[i].x)
721 {
722 inside = !inside;
723 }
724 j = i;
725 }
726
727 inside
728 }
729
730 fn combinemeshes(meshes: &[TriangularMesh]) -> PDEResult<TriangularMesh> {
732 if meshes.is_empty() {
733 return Err(PDEError::FiniteElementError(
734 "Cannot combine empty mesh list".to_string(),
735 ));
736 }
737
738 let mut combined_points = Vec::new();
739 let mut combined_elements = Vec::new();
740 let mut point_offset = 0;
741
742 for mesh in meshes {
743 combined_points.extend(mesh.points.iter().cloned());
745
746 for element in &mesh.elements {
748 let [p1, p2, p3] = element.nodes;
749 combined_elements.push(Triangle::new(
750 [p1 + point_offset, p2 + point_offset, p3 + point_offset],
751 element.marker,
752 ));
753 }
754
755 point_offset += mesh.points.len();
756 }
757
758 let mut combinedmesh = TriangularMesh::new();
759 combinedmesh.points = combined_points;
760 combinedmesh.elements = combined_elements;
761 Ok(combinedmesh)
762 }
763
764 pub fn assessmesh_quality(&self, mesh: &TriangularMesh) -> MeshQuality {
766 let mut min_angle = f64::INFINITY;
767 let mut max_angle: f64 = 0.0;
768 let mut total_area = 0.0;
769 let mut min_aspect_ratio = f64::INFINITY;
770 let mut poor_quality_count = 0;
771
772 for element in &mesh.elements {
773 let quality = self.element_quality(mesh, element);
774
775 min_angle = min_angle.min(quality.min_angle);
776 max_angle = max_angle.max(quality.max_angle);
777 total_area += quality.area;
778 min_aspect_ratio = min_aspect_ratio.min(quality.aspect_ratio);
779
780 if quality.min_angle < self.params.min_angle
781 || quality.max_angle > self.params.max_angle
782 {
783 poor_quality_count += 1;
784 }
785 }
786
787 let avg_element_size = (total_area / mesh.elements.len() as f64).sqrt();
788 let quality_score = 1.0 - (poor_quality_count as f64 / mesh.elements.len() as f64);
789
790 MeshQuality {
791 min_angle,
792 max_angle,
793 avg_element_size,
794 min_aspect_ratio,
795 poor_quality_elements: poor_quality_count,
796 quality_score,
797 }
798 }
799}
800
801#[derive(Debug, Clone)]
803struct ElementQuality {
804 min_angle: f64,
805 max_angle: f64,
806 area: f64,
807 aspect_ratio: f64,
808}
809
810#[cfg(test)]
811mod tests {
812 use super::*;
813
814 #[test]
815 fn test_rectanglemesh_generation() {
816 let mut generator = AutoMeshGenerator::default();
817 let domain = Domain::Rectangle {
818 x_min: 0.0,
819 y_min: 0.0,
820 x_max: 1.0,
821 y_max: 1.0,
822 };
823 let boundary_spec = BoundarySpecification::default();
824
825 let mesh = generator.generatemesh(&domain, &boundary_spec).unwrap();
826
827 assert!(!mesh.points.is_empty());
828 assert!(!mesh.elements.is_empty());
829
830 for point in &mesh.points {
832 assert!(point.x >= 0.0 && point.x <= 1.0);
833 assert!(point.y >= 0.0 && point.y <= 1.0);
834 }
835 }
836
837 #[test]
838 fn test_circlemesh_generation() {
839 let params = MeshGenerationParams {
840 element_size: 0.2,
841 ..Default::default()
842 };
843 let mut generator = AutoMeshGenerator::new(params);
844
845 let domain = Domain::Circle {
846 center_x: 0.0,
847 center_y: 0.0,
848 radius: 1.0,
849 };
850 let boundary_spec = BoundarySpecification::default();
851
852 let mesh = generator.generatemesh(&domain, &boundary_spec).unwrap();
853
854 assert!(!mesh.points.is_empty());
855 assert!(!mesh.elements.is_empty());
856
857 for point in &mesh.points {
859 let distance = (point.x.powi(2) + point.y.powi(2)).sqrt();
860 assert!(distance <= 1.01); }
862 }
863
864 #[test]
865 fn testmesh_quality_assessment() {
866 let mut generator = AutoMeshGenerator::default();
867 let domain = Domain::Rectangle {
868 x_min: 0.0,
869 y_min: 0.0,
870 x_max: 1.0,
871 y_max: 1.0,
872 };
873 let boundary_spec = BoundarySpecification::default();
874
875 let mesh = generator.generatemesh(&domain, &boundary_spec).unwrap();
876 let quality = generator.assessmesh_quality(&mesh);
877
878 assert!(quality.min_angle > 0.0);
879 assert!(quality.max_angle < 180.0);
880 assert!(quality.avg_element_size > 0.0);
881 assert!(quality.quality_score >= 0.0 && quality.quality_score <= 1.0);
882 }
883
884 #[test]
885 fn test_point_in_polygon() {
886 let generator = AutoMeshGenerator::default();
887
888 let polygon = vec![
890 Point::new(0.0, 0.0),
891 Point::new(1.0, 0.0),
892 Point::new(1.0, 1.0),
893 Point::new(0.0, 1.0),
894 ];
895
896 assert!(AutoMeshGenerator::point_in_polygon(
897 &Point::new(0.5, 0.5),
898 &polygon
899 ));
900 assert!(!AutoMeshGenerator::point_in_polygon(
901 &Point::new(1.5, 0.5),
902 &polygon
903 ));
904 assert!(!AutoMeshGenerator::point_in_polygon(
905 &Point::new(-0.5, 0.5),
906 &polygon
907 ));
908 }
909
910 #[test]
911 fn test_annulusmesh_generation() {
912 let mut generator = AutoMeshGenerator::default();
913 let domain = Domain::Annulus {
914 center_x: 0.0,
915 center_y: 0.0,
916 inner_radius: 0.5,
917 outer_radius: 1.0,
918 };
919 let boundary_spec = BoundarySpecification::default();
920
921 let mesh = generator.generatemesh(&domain, &boundary_spec).unwrap();
922
923 assert!(!mesh.points.is_empty());
924 assert!(!mesh.elements.is_empty());
925
926 for point in &mesh.points {
928 let distance = (point.x.powi(2) + point.y.powi(2)).sqrt();
929 assert!((0.49..=1.01).contains(&distance)); }
931 }
932
933 #[test]
934 fn test_lshapemesh_generation() {
935 let mut generator = AutoMeshGenerator::default();
936 let domain = Domain::LShape {
937 width: 2.0,
938 height: 2.0,
939 notch_width: 1.0,
940 notch_height: 1.0,
941 };
942 let boundary_spec = BoundarySpecification::default();
943
944 let mesh = generator.generatemesh(&domain, &boundary_spec).unwrap();
945
946 assert!(!mesh.points.is_empty());
947 assert!(!mesh.elements.is_empty());
948 }
949
950 #[test]
951 fn test_custommesh_parameters() {
952 let params = MeshGenerationParams {
953 element_size: 0.05,
954 min_angle: 25.0,
955 max_angle: 135.0,
956 quality_iterations: 10,
957 element_type: ElementType::Linear,
958 boundary_refinement_iterations: 5,
959 };
960
961 let mut generator = AutoMeshGenerator::new(params);
962 let domain = Domain::Rectangle {
963 x_min: 0.0,
964 y_min: 0.0,
965 x_max: 1.0,
966 y_max: 1.0,
967 };
968 let boundary_spec = BoundarySpecification::default();
969
970 let mesh = generator.generatemesh(&domain, &boundary_spec).unwrap();
971 let quality = generator.assessmesh_quality(&mesh);
972
973 assert!(mesh.elements.len() > 10);
975 assert!(quality.avg_element_size < 0.2);
976 }
977}