Oracle: 25/29 (14 and 15 now match Fusion). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
1535 lines
45 KiB
Rust
1535 lines
45 KiB
Rust
use super::*;
|
|
use std::f64::consts::PI;
|
|
use truck_topology::Shell;
|
|
|
|
#[test]
|
|
fn test_split_closed_edges() {
|
|
let vertices = vec![Point2::new(1.0, 0.0)];
|
|
let curve = TrimmedCurve::new(UnitCircle::<Point2>::new(), (0.0, 2.0 * PI));
|
|
let edges = vec![CompressedEdge {
|
|
vertices: (0, 0),
|
|
curve,
|
|
}];
|
|
let faces = vec![
|
|
CompressedFace {
|
|
surface: (),
|
|
orientation: true,
|
|
boundaries: vec![vec![CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
}]],
|
|
},
|
|
CompressedFace {
|
|
surface: (),
|
|
orientation: false,
|
|
boundaries: vec![vec![CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: false,
|
|
}]],
|
|
},
|
|
];
|
|
let mut shell = CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
};
|
|
assert!(Shell::extract(shell.clone()).is_err());
|
|
|
|
split_closed_edges(&mut shell);
|
|
assert!(Shell::extract(shell.clone()).is_ok());
|
|
|
|
let CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
} = &shell;
|
|
assert_eq!(vertices.len(), 2);
|
|
assert_near!(vertices[0], Point2::new(1.0, 0.0));
|
|
assert_near!(vertices[1], Point2::new(-1.0, 0.0));
|
|
|
|
assert_eq!(edges.len(), 2);
|
|
assert_eq!(edges[0].vertices, (0, 1));
|
|
assert_eq!(edges[1].vertices, (1, 0));
|
|
|
|
assert_eq!(
|
|
*faces,
|
|
vec![
|
|
CompressedFace {
|
|
surface: (),
|
|
orientation: true,
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: true,
|
|
}
|
|
]],
|
|
},
|
|
CompressedFace {
|
|
surface: (),
|
|
orientation: false,
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: false,
|
|
}
|
|
]],
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_split_closed_face_simple_cylinder_case() {
|
|
type Surface = RevolutedCurve<Line<Point3>>;
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum Curve {
|
|
Line(Line<Point3>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point3>, Matrix4>>),
|
|
#[allow(clippy::enum_variant_names)]
|
|
PCurve(PCurve<Line<Point2>, Surface>),
|
|
}
|
|
impl From<PCurve<Line<Point2>, Surface>> for Curve {
|
|
fn from(value: PCurve<Line<Point2>, Surface>) -> Self { Self::PCurve(value) }
|
|
}
|
|
|
|
let vertices = vec![
|
|
Point3::new(1.0, 0.0, 0.0),
|
|
Point3::new(-1.0, 0.0, 0.0),
|
|
Point3::new(1.0, 0.0, 1.0),
|
|
Point3::new(-1.0, 0.0, 1.0),
|
|
];
|
|
|
|
let translate = Matrix4::from_translation(Vector3::unit_z());
|
|
let edges = vec![
|
|
CompressedEdge {
|
|
vertices: (0, 1),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(0.0, PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (1, 0),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(PI, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (0, 2),
|
|
curve: Curve::Line(Line(vertices[0], vertices[2])),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (2, 3),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(translate),
|
|
(0.0, PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (3, 2),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(translate),
|
|
(PI, 2.0 * PI),
|
|
)),
|
|
},
|
|
];
|
|
let surface = RevolutedCurve::by_revolution(
|
|
Line(vertices[2], vertices[0]),
|
|
Point3::origin(),
|
|
Vector3::unit_z(),
|
|
);
|
|
let faces = vec![Face {
|
|
surface,
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
]],
|
|
orientation: true,
|
|
}];
|
|
let mut shell = CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
};
|
|
|
|
assert!(Shell::extract(shell.clone()).is_err());
|
|
|
|
split_closed_faces(&mut shell, 0.01, sp);
|
|
|
|
assert!(Shell::extract(shell.clone()).is_ok());
|
|
let CompressedShell {
|
|
ref vertices,
|
|
ref edges,
|
|
ref mut faces,
|
|
} = shell;
|
|
assert_eq!(vertices.len(), 4);
|
|
assert_eq!(edges.len(), 6);
|
|
assert_eq!(edges[5].vertices, (3, 1));
|
|
let curve0 = &edges[5].curve;
|
|
let curve1 = Line(Point3::new(-1.0, 0.0, 1.0), Point3::new(-1.0, 0.0, 0.0));
|
|
for i in 0..=10 {
|
|
let t = i as f64 / 10.0;
|
|
assert_near!(curve0.subs(t), curve1.subs(t));
|
|
}
|
|
assert_eq!(faces.len(), 2);
|
|
let i = faces
|
|
.iter_mut()
|
|
.position(|face| {
|
|
face.boundaries[0].contains(&CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
})
|
|
})
|
|
.unwrap();
|
|
if i == 1 {
|
|
faces.swap(0, 1);
|
|
}
|
|
let i = faces[0].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[0].boundaries[0].rotate_left(i);
|
|
let i = faces[1].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[1].boundaries[0].rotate_left(i);
|
|
|
|
assert_eq!(
|
|
*faces,
|
|
vec![
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: true,
|
|
}
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: false,
|
|
},
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_split_closed_face_cylinder_with_hole() {
|
|
type Surface = RevolutedCurve<Line<Point3>>;
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum ParameterCurve {
|
|
Line(Line<Point2>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point2>, Matrix3>>),
|
|
}
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum Curve {
|
|
Line(Line<Point3>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point3>, Matrix4>>),
|
|
PCurve(PCurve<ParameterCurve, Surface>),
|
|
}
|
|
impl From<PCurve<Line<Point2>, Surface>> for Curve {
|
|
fn from(value: PCurve<Line<Point2>, Surface>) -> Self {
|
|
let (line, surface) = value.decompose();
|
|
Self::PCurve(PCurve::new(ParameterCurve::Line(line), surface))
|
|
}
|
|
}
|
|
|
|
let vertices = vec![
|
|
Point3::new(1.0, 0.0, 0.0),
|
|
Point3::new(-1.0, 0.0, 0.0),
|
|
Point3::new(1.0, 0.0, 1.0),
|
|
Point3::new(-1.0, 0.0, 1.0),
|
|
Point3::new(-1.0, 0.0, 0.25),
|
|
Point3::new(-1.0, 0.0, 0.75),
|
|
];
|
|
|
|
let surface = RevolutedCurve::by_revolution(
|
|
Line(vertices[2], vertices[0]),
|
|
Point3::origin(),
|
|
Vector3::unit_z(),
|
|
);
|
|
|
|
let translate = Matrix4::from_translation(Vector3::unit_z());
|
|
let transform = Matrix3::from_translation(Vector2::new(0.5, PI)) * Matrix3::from_scale(0.25);
|
|
let edges = vec![
|
|
CompressedEdge {
|
|
vertices: (0, 1),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(0.0, PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (1, 0),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(PI, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (0, 2),
|
|
curve: Curve::Line(Line(vertices[0], vertices[2])),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (2, 3),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(translate),
|
|
(0.0, PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (3, 2),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(translate),
|
|
(PI, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (4, 5),
|
|
curve: Curve::PCurve(PCurve::new(
|
|
ParameterCurve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(transform),
|
|
(0.0, PI),
|
|
)),
|
|
surface,
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (5, 4),
|
|
curve: Curve::PCurve(PCurve::new(
|
|
ParameterCurve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(transform),
|
|
(PI, 2.0 * PI),
|
|
)),
|
|
surface,
|
|
)),
|
|
},
|
|
];
|
|
let faces = vec![Face {
|
|
surface,
|
|
boundaries: vec![
|
|
vec![
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
],
|
|
vec![
|
|
CompressedEdgeIndex {
|
|
index: 6,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: false,
|
|
},
|
|
],
|
|
],
|
|
orientation: true,
|
|
}];
|
|
let mut shell = CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
};
|
|
|
|
assert!(Shell::extract(shell.clone()).is_err());
|
|
split_closed_faces(&mut shell, 0.01, sp);
|
|
assert!(Shell::extract(shell.clone()).is_ok());
|
|
|
|
let CompressedShell {
|
|
ref vertices,
|
|
ref edges,
|
|
ref mut faces,
|
|
} = shell;
|
|
assert_eq!(vertices.len(), 6);
|
|
assert_eq!(edges.len(), 9);
|
|
assert_eq!(edges[7].vertices, (3, 5));
|
|
let curve0 = &edges[7].curve;
|
|
let curve1 = Line(Point3::new(-1.0, 0.0, 1.0), Point3::new(-1.0, 0.0, 0.75));
|
|
for i in 0..=10 {
|
|
let t = i as f64 / 10.0;
|
|
assert_near!(curve0.subs(t), curve1.subs(t));
|
|
}
|
|
assert_eq!(edges[8].vertices, (4, 1));
|
|
let curve0 = &edges[8].curve;
|
|
let curve1 = Line(Point3::new(-1.0, 0.0, 0.25), Point3::new(-1.0, 0.0, 0.0));
|
|
for i in 0..=10 {
|
|
let t = i as f64 / 10.0;
|
|
assert_near!(curve0.subs(t), curve1.subs(t));
|
|
}
|
|
assert_eq!(faces.len(), 2);
|
|
let i = faces
|
|
.iter_mut()
|
|
.position(|face| {
|
|
face.boundaries[0].contains(&CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
})
|
|
})
|
|
.unwrap();
|
|
if i == 1 {
|
|
faces.swap(0, 1);
|
|
}
|
|
let i = faces[0].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[0].boundaries[0].rotate_left(i);
|
|
let i = faces[1].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[1].boundaries[0].rotate_left(i);
|
|
|
|
assert_eq!(
|
|
*faces,
|
|
vec![
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 7,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 8,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: true,
|
|
}
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 8,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 6,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 7,
|
|
orientation: false,
|
|
},
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_split_closed_face_cylinder_with_rotated_hole() {
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum ParameterCurve {
|
|
Line(Line<Point2>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point2>, Matrix3>>),
|
|
}
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum Curve {
|
|
Line(Line<Point3>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point3>, Matrix4>>),
|
|
PCurve(PCurve<ParameterCurve, Surface>),
|
|
}
|
|
impl From<PCurve<Line<Point2>, Surface>> for Curve {
|
|
fn from(value: PCurve<Line<Point2>, Surface>) -> Self {
|
|
let (line, surface) = value.decompose();
|
|
Self::PCurve(PCurve::new(ParameterCurve::Line(line), surface))
|
|
}
|
|
}
|
|
type Surface = RevolutedCurve<Line<Point3>>;
|
|
|
|
let surface = RevolutedCurve::by_revolution(
|
|
Line(Point3::new(1.0, 0.0, 1.0), Point3::new(1.0, 0.0, 0.0)),
|
|
Point3::origin(),
|
|
Vector3::unit_z(),
|
|
);
|
|
|
|
let vertices = vec![
|
|
Point3::new(1.0, 0.0, 0.0),
|
|
Point3::new(-1.0, 0.0, 0.0),
|
|
Point3::new(1.0, 0.0, 1.0),
|
|
Point3::new(-1.0, 0.0, 1.0),
|
|
surface.subs(0.5, PI + 0.25),
|
|
surface.subs(0.5, PI - 0.25),
|
|
];
|
|
|
|
let translate = Matrix4::from_translation(Vector3::unit_z());
|
|
let transform = Matrix3::from_translation(Vector2::new(0.5, PI)) * Matrix3::from_scale(0.25);
|
|
let edges = vec![
|
|
CompressedEdge {
|
|
vertices: (0, 1),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(0.0, PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (1, 0),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(PI, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (0, 2),
|
|
curve: Curve::Line(Line(vertices[0], vertices[2])),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (2, 3),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(translate),
|
|
(0.0, PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (3, 2),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(translate),
|
|
(PI, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (4, 5),
|
|
curve: Curve::PCurve(PCurve::new(
|
|
ParameterCurve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(transform),
|
|
(0.5 * PI, 1.5 * PI),
|
|
)),
|
|
surface,
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (5, 4),
|
|
curve: Curve::PCurve(PCurve::new(
|
|
ParameterCurve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(transform),
|
|
(1.5 * PI, 2.5 * PI),
|
|
)),
|
|
surface,
|
|
)),
|
|
},
|
|
];
|
|
let faces = vec![Face {
|
|
surface,
|
|
boundaries: vec![
|
|
vec![
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
],
|
|
vec![
|
|
CompressedEdgeIndex {
|
|
index: 6,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: false,
|
|
},
|
|
],
|
|
],
|
|
orientation: true,
|
|
}];
|
|
let mut shell = CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
};
|
|
|
|
assert!(Shell::extract(shell.clone()).is_err());
|
|
split_closed_faces(&mut shell, 0.01, sp);
|
|
assert!(Shell::extract(shell.clone()).is_ok());
|
|
|
|
let CompressedShell {
|
|
ref vertices,
|
|
ref edges,
|
|
ref mut faces,
|
|
} = shell;
|
|
assert_eq!(vertices.len(), 8);
|
|
assert_eq!(edges.len(), 11);
|
|
assert_eq!(edges[5].vertices, (4, 7));
|
|
assert_eq!(edges[6].vertices, (5, 6));
|
|
assert_eq!(edges[7].vertices, (6, 4));
|
|
assert_eq!(edges[8].vertices, (7, 5));
|
|
assert_eq!(edges[9].vertices, (3, 7));
|
|
let curve0 = &edges[9].curve;
|
|
let curve1 = Line(Point3::new(-1.0, 0.0, 1.0), Point3::new(-1.0, 0.0, 0.75));
|
|
for i in 0..=10 {
|
|
let t = i as f64 / 10.0;
|
|
assert_near!(curve0.subs(t), curve1.subs(t));
|
|
}
|
|
assert_eq!(edges[10].vertices, (6, 1));
|
|
let curve0 = &edges[10].curve;
|
|
let curve1 = Line(Point3::new(-1.0, 0.0, 0.25), Point3::new(-1.0, 0.0, 0.0));
|
|
for i in 0..=10 {
|
|
let t = i as f64 / 10.0;
|
|
assert_near!(curve0.subs(t), curve1.subs(t));
|
|
}
|
|
assert_eq!(faces.len(), 2);
|
|
let i = faces
|
|
.iter_mut()
|
|
.position(|face| {
|
|
face.boundaries[0].contains(&CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
})
|
|
})
|
|
.unwrap();
|
|
if i == 1 {
|
|
faces.swap(0, 1);
|
|
}
|
|
let i = faces[0].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[0].boundaries[0].rotate_left(i);
|
|
let i = faces[1].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[1].boundaries[0].rotate_left(i);
|
|
|
|
assert_eq!(
|
|
*faces,
|
|
vec![
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 9,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 7,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 10,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: true,
|
|
}
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 10,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 6,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 8,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 9,
|
|
orientation: false,
|
|
},
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn too_simple_cylinder() {
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum Curve {
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point3>, Matrix4>>),
|
|
PCurve(PCurve<Line<Point2>, Surface>),
|
|
}
|
|
impl From<PCurve<Line<Point2>, Surface>> for Curve {
|
|
fn from(value: PCurve<Line<Point2>, Surface>) -> Self { Curve::PCurve(value) }
|
|
}
|
|
type Surface = RevolutedCurve<Line<Point3>>;
|
|
|
|
let vertices = vec![Point3::new(1.0, 0.0, 0.0), Point3::new(1.0, 0.0, 1.0)];
|
|
|
|
let translation = Matrix4::from_translation(Vector3::unit_z());
|
|
let circle0 = TrimmedCurve::new(Processor::new(UnitCircle::new()), (0.0, 2.0 * PI));
|
|
let circle1 = TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(translation),
|
|
(0.0, 2.0 * PI),
|
|
);
|
|
let edges = vec![
|
|
CompressedEdge {
|
|
vertices: (0, 0),
|
|
curve: Curve::Arc(circle0),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (1, 1),
|
|
curve: Curve::Arc(circle1),
|
|
},
|
|
];
|
|
|
|
let surface = RevolutedCurve::by_revolution(
|
|
Line(vertices[1], vertices[0]),
|
|
Point3::origin(),
|
|
Vector3::unit_z(),
|
|
);
|
|
let faces = vec![CompressedFace {
|
|
boundaries: vec![
|
|
vec![CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
}],
|
|
vec![CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: false,
|
|
}],
|
|
],
|
|
surface,
|
|
orientation: true,
|
|
}];
|
|
|
|
let mut shell = CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
};
|
|
|
|
assert!(Shell::extract(shell.clone()).is_err());
|
|
split_closed_edges(&mut shell);
|
|
split_closed_faces(&mut shell, 0.01, sp);
|
|
assert!(Shell::extract(shell.clone()).is_ok());
|
|
|
|
let CompressedShell {
|
|
ref vertices,
|
|
ref edges,
|
|
ref mut faces,
|
|
} = shell;
|
|
|
|
assert_eq!(vertices.len(), 4);
|
|
assert_near!(vertices[2], Point3::new(-1.0, 0.0, 0.0));
|
|
assert_near!(vertices[3], Point3::new(-1.0, 0.0, 1.0));
|
|
|
|
assert_eq!(edges.len(), 6);
|
|
assert_eq!(edges[0].vertices, (0, 2));
|
|
assert_eq!(edges[1].vertices, (1, 3));
|
|
assert_eq!(edges[2].vertices, (2, 0));
|
|
assert_eq!(edges[3].vertices, (3, 1));
|
|
assert_eq!(edges[4].vertices, (0, 1));
|
|
assert_eq!(edges[5].vertices, (2, 3));
|
|
|
|
assert_eq!(faces.len(), 2);
|
|
let i = faces
|
|
.iter_mut()
|
|
.position(|face| {
|
|
face.boundaries[0].contains(&CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
})
|
|
})
|
|
.unwrap();
|
|
if i == 1 {
|
|
faces.swap(0, 1);
|
|
}
|
|
let i = faces[0].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[0].boundaries[0].rotate_left(i);
|
|
let i = faces[1].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[1].boundaries[0].rotate_left(i);
|
|
|
|
assert_eq!(
|
|
shell.faces,
|
|
vec![
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: false,
|
|
},
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
Face {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: false,
|
|
},
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn double_closed_boundary_cylinder() {
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum ParameterCurve {
|
|
Line(Line<Point2>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point2>, Matrix3>>),
|
|
}
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum Curve {
|
|
Line(Line<Point3>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point3>, Matrix4>>),
|
|
#[allow(clippy::enum_variant_names)]
|
|
PCurve(PCurve<ParameterCurve, Surface>),
|
|
}
|
|
impl From<PCurve<Line<Point2>, Surface>> for Curve {
|
|
fn from(value: PCurve<Line<Point2>, Surface>) -> Self {
|
|
let (line, surface) = value.decompose();
|
|
Self::PCurve(PCurve::new(ParameterCurve::Line(line), surface))
|
|
}
|
|
}
|
|
type Surface = RevolutedCurve<Line<Point3>>;
|
|
|
|
let vertices = vec![
|
|
Point3::new(1.0, 0.0, 0.0),
|
|
Point3::new(1.0, 0.0, 1.0),
|
|
Point3::new(1.0, 0.0, 0.25),
|
|
Point3::new(-1.0, 0.0, 0.25),
|
|
];
|
|
let surface = RevolutedCurve::by_revolution(
|
|
Line(vertices[1], vertices[0]),
|
|
Point3::origin(),
|
|
Vector3::unit_z(),
|
|
);
|
|
let edges = vec![
|
|
CompressedEdge {
|
|
vertices: (0, 0),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(0.0, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (1, 1),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new())
|
|
.transformed(Matrix4::from_translation(Vector3::unit_z())),
|
|
(0.0, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (2, 2),
|
|
curve: Curve::PCurve(PCurve::new(
|
|
ParameterCurve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(
|
|
Matrix3::from_translation(Vector2::new(0.5, 0.0))
|
|
* Matrix3::from_scale(0.25),
|
|
),
|
|
(0.0, 2.0 * PI),
|
|
)),
|
|
surface,
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (3, 3),
|
|
curve: Curve::PCurve(PCurve::new(
|
|
ParameterCurve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(
|
|
Matrix3::from_translation(Vector2::new(0.5, PI))
|
|
* Matrix3::from_scale(0.25),
|
|
),
|
|
(0.0, 2.0 * PI),
|
|
)),
|
|
surface,
|
|
)),
|
|
},
|
|
];
|
|
let faces = vec![CompressedFace {
|
|
boundaries: vec![
|
|
vec![CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
}],
|
|
vec![CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: false,
|
|
}],
|
|
vec![CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
}],
|
|
vec![CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
}],
|
|
],
|
|
surface,
|
|
orientation: true,
|
|
}];
|
|
let mut shell = CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
};
|
|
|
|
assert!(Shell::extract(shell.clone()).is_err());
|
|
split_closed_edges(&mut shell);
|
|
split_closed_faces(&mut shell, 0.05, sp);
|
|
assert!(Shell::extract(shell.clone()).is_ok());
|
|
|
|
let CompressedShell {
|
|
ref vertices,
|
|
ref edges,
|
|
ref mut faces,
|
|
} = shell;
|
|
|
|
assert_eq!(vertices.len(), 8);
|
|
assert_near!(vertices[0], Point3::new(1.0, 0.0, 0.0));
|
|
assert_near!(vertices[1], Point3::new(1.0, 0.0, 1.0));
|
|
assert_near!(vertices[2], Point3::new(1.0, 0.0, 0.25));
|
|
assert_near!(vertices[3], Point3::new(-1.0, 0.0, 0.25));
|
|
assert_near!(vertices[4], Point3::new(-1.0, 0.0, 0.0));
|
|
assert_near!(vertices[5], Point3::new(-1.0, 0.0, 1.0));
|
|
assert_near!(vertices[6], Point3::new(1.0, 0.0, 0.75));
|
|
assert_near!(vertices[7], Point3::new(-1.0, 0.0, 0.75));
|
|
|
|
assert_eq!(edges.len(), 12);
|
|
assert_eq!(edges[0].vertices, (0, 4));
|
|
assert_eq!(edges[1].vertices, (1, 5));
|
|
assert_eq!(edges[2].vertices, (2, 6));
|
|
assert_eq!(edges[3].vertices, (3, 7));
|
|
assert_eq!(edges[4].vertices, (4, 0));
|
|
assert_eq!(edges[5].vertices, (5, 1));
|
|
assert_eq!(edges[6].vertices, (6, 2));
|
|
assert_eq!(edges[7].vertices, (7, 3));
|
|
assert_eq!(edges[8].vertices, (0, 2));
|
|
assert_eq!(edges[9].vertices, (6, 1));
|
|
assert_eq!(edges[10].vertices, (4, 3));
|
|
assert_eq!(edges[11].vertices, (7, 5));
|
|
|
|
let i = faces
|
|
.iter_mut()
|
|
.position(|face| {
|
|
face.boundaries[0].contains(&CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
})
|
|
})
|
|
.unwrap();
|
|
if i == 1 {
|
|
faces.swap(0, 1);
|
|
}
|
|
let i = faces[0].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[0].boundaries[0].rotate_left(i);
|
|
let i = faces[1].boundaries[0]
|
|
.iter()
|
|
.position(|edge_index| {
|
|
*edge_index
|
|
== CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: true,
|
|
}
|
|
})
|
|
.unwrap();
|
|
faces[1].boundaries[0].rotate_left(i);
|
|
|
|
assert_eq!(
|
|
*faces,
|
|
vec![
|
|
CompressedFace {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 10,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 7,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 11,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 1,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 9,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 2,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 8,
|
|
orientation: false,
|
|
},
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
CompressedFace {
|
|
boundaries: vec![vec![
|
|
CompressedEdgeIndex {
|
|
index: 4,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 8,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 6,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 9,
|
|
orientation: true,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 5,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 11,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 3,
|
|
orientation: false,
|
|
},
|
|
CompressedEdgeIndex {
|
|
index: 10,
|
|
orientation: false,
|
|
},
|
|
]],
|
|
surface,
|
|
orientation: true,
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn many_closed_boundary_cylinder() {
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum ParameterCurve {
|
|
Line(Line<Point2>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point2>, Matrix3>>),
|
|
}
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
ParametricCurve,
|
|
BoundedCurve,
|
|
ParameterDivision1D,
|
|
Cut,
|
|
SearchNearestParameterD1,
|
|
)]
|
|
enum Curve {
|
|
Line(Line<Point3>),
|
|
Arc(TrimmedCurve<Processor<UnitCircle<Point3>, Matrix4>>),
|
|
PCurve(PCurve<ParameterCurve, Surface>),
|
|
}
|
|
impl From<PCurve<Line<Point2>, Surface>> for Curve {
|
|
fn from(value: PCurve<Line<Point2>, Surface>) -> Self {
|
|
let (line, surface) = value.decompose();
|
|
Self::PCurve(PCurve::new(ParameterCurve::Line(line), surface))
|
|
}
|
|
}
|
|
type Surface = RevolutedCurve<Line<Point3>>;
|
|
|
|
const NUM_OF_CIRCLES: usize = 10;
|
|
|
|
let vertices = {
|
|
let mut vertices = vec![Point3::new(1.0, 0.0, 0.0), Point3::new(1.0, 0.0, 1.0)];
|
|
vertices.extend((0..NUM_OF_CIRCLES).map(|i| {
|
|
let t = 2.0 * PI * i as f64 / NUM_OF_CIRCLES as f64;
|
|
Point3::new(f64::cos(t), f64::sin(t), 0.4)
|
|
}));
|
|
vertices
|
|
};
|
|
let surface = RevolutedCurve::by_revolution(
|
|
Line(vertices[1], vertices[0]),
|
|
Point3::origin(),
|
|
Vector3::unit_z(),
|
|
);
|
|
let edges = {
|
|
let mut edges = vec![
|
|
CompressedEdge {
|
|
vertices: (0, 0),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()),
|
|
(0.0, 2.0 * PI),
|
|
)),
|
|
},
|
|
CompressedEdge {
|
|
vertices: (1, 1),
|
|
curve: Curve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new())
|
|
.transformed(Matrix4::from_translation(Vector3::unit_z())),
|
|
(0.0, 2.0 * PI),
|
|
)),
|
|
},
|
|
];
|
|
edges.extend((0..NUM_OF_CIRCLES).map(|i| {
|
|
let t = 2.0 * PI * i as f64 / NUM_OF_CIRCLES as f64;
|
|
CompressedEdge {
|
|
vertices: (2 + i, 2 + i),
|
|
curve: Curve::PCurve(PCurve::new(
|
|
ParameterCurve::Arc(TrimmedCurve::new(
|
|
Processor::new(UnitCircle::new()).transformed(
|
|
Matrix3::from_translation(Vector2::new(0.5, t))
|
|
* Matrix3::from_scale(0.1),
|
|
),
|
|
(0.0, 2.0 * PI),
|
|
)),
|
|
surface,
|
|
)),
|
|
}
|
|
}));
|
|
edges
|
|
};
|
|
let mut boundaries = vec![vec![CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
}]];
|
|
boundaries.extend((0..=NUM_OF_CIRCLES).map(|i| {
|
|
vec![CompressedEdgeIndex {
|
|
index: 1 + i,
|
|
orientation: false,
|
|
}]
|
|
}));
|
|
let faces = vec![CompressedFace {
|
|
boundaries,
|
|
surface,
|
|
orientation: true,
|
|
}];
|
|
let mut shell = CompressedShell {
|
|
vertices,
|
|
edges,
|
|
faces,
|
|
};
|
|
|
|
assert!(Shell::extract(shell.clone()).is_err());
|
|
split_closed_edges(&mut shell);
|
|
split_closed_faces(&mut shell, 0.05, sp);
|
|
assert!(Shell::extract(shell.clone()).is_ok());
|
|
|
|
let CompressedShell {
|
|
ref vertices,
|
|
ref edges,
|
|
ref mut faces,
|
|
} = shell;
|
|
|
|
assert_eq!(vertices.len(), (2 + NUM_OF_CIRCLES) * 2);
|
|
assert_near!(vertices[0], Point3::new(1.0, 0.0, 0.0));
|
|
assert_near!(vertices[1], Point3::new(1.0, 0.0, 1.0));
|
|
assert_near!(vertices[NUM_OF_CIRCLES + 2], Point3::new(-1.0, 0.0, 0.0));
|
|
assert_near!(vertices[NUM_OF_CIRCLES + 3], Point3::new(-1.0, 0.0, 1.0));
|
|
(0..NUM_OF_CIRCLES).for_each(|i| {
|
|
let t = 2.0 * PI * i as f64 / NUM_OF_CIRCLES as f64;
|
|
assert_near!(vertices[2 + i], Point3::new(f64::cos(t), f64::sin(t), 0.4));
|
|
assert_near!(
|
|
vertices[4 + i + NUM_OF_CIRCLES],
|
|
Point3::new(f64::cos(t), f64::sin(t), 0.6)
|
|
);
|
|
});
|
|
|
|
assert_eq!(edges.len(), 8 + NUM_OF_CIRCLES * 2);
|
|
(0..NUM_OF_CIRCLES + 2).for_each(|i| {
|
|
let j = i + 2 + NUM_OF_CIRCLES;
|
|
assert_eq!(edges[i].vertices, (i, j));
|
|
assert_eq!(edges[j].vertices, (j, i));
|
|
});
|
|
let i = 4 + NUM_OF_CIRCLES * 2;
|
|
assert_eq!(edges[i].vertices, (0, 2));
|
|
assert_eq!(edges[i + 1].vertices, (4 + NUM_OF_CIRCLES, 1));
|
|
assert_eq!(
|
|
edges[i + 2].vertices,
|
|
(2 + NUM_OF_CIRCLES, 2 + NUM_OF_CIRCLES / 2)
|
|
);
|
|
assert_eq!(
|
|
edges[i + 3].vertices,
|
|
(4 + NUM_OF_CIRCLES * 3 / 2, 3 + NUM_OF_CIRCLES)
|
|
);
|
|
|
|
let i = faces
|
|
.iter_mut()
|
|
.position(|face| {
|
|
face.boundaries[0].contains(&CompressedEdgeIndex {
|
|
index: 0,
|
|
orientation: true,
|
|
})
|
|
})
|
|
.unwrap();
|
|
if i == 1 {
|
|
faces.swap(0, 1);
|
|
}
|
|
|
|
assert_eq!(faces[0].boundaries.len(), NUM_OF_CIRCLES / 2);
|
|
assert_eq!(faces[1].boundaries.len(), NUM_OF_CIRCLES / 2);
|
|
|
|
(1..NUM_OF_CIRCLES / 2).for_each(|i| {
|
|
(0..=1).for_each(|fid| {
|
|
let eid = faces[fid].boundaries[i][0].index;
|
|
let p = vertices[edges[eid].vertices.0];
|
|
assert!(f64::signum(p.y) == f64::signum(f64::powi(-1.0, fid as i32)));
|
|
});
|
|
});
|
|
}
|
|
|
|
fn sp<S>(surface: &S, p: Point3, hint: Option<(f64, f64)>) -> Option<(f64, f64)>
|
|
where S: SearchParameter<D2, Point = Point3> {
|
|
surface.search_parameter(p, hint, 10)
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(feature = "step-test")]
|
|
fn step_import() {
|
|
use truck_stepio::r#in::*;
|
|
const STEP_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../resources/step/");
|
|
const STEP_FILES: &[&str] = &[
|
|
"occt-cylinder.step",
|
|
"occt-cone.step",
|
|
"abc-0006.step",
|
|
"abc-0008.step",
|
|
"abc-0035.step",
|
|
];
|
|
|
|
STEP_FILES.iter().for_each(|file_name| {
|
|
println!("{file_name}");
|
|
let path = [STEP_DIRECTORY, file_name].concat();
|
|
let step_string = std::fs::read_to_string(path).unwrap();
|
|
let table = Table::from_step(&step_string).unwrap();
|
|
table.shell.values().cloned().for_each(|step_shell| {
|
|
let mut cshell = table.to_compressed_shell(&step_shell).unwrap();
|
|
cshell.robust_split_closed_edges_and_faces(0.05);
|
|
truck_topology::Shell::extract(cshell).unwrap();
|
|
});
|
|
});
|
|
}
|