1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use crate::error::Error;
use crate::ffi;
use crate::solid::Solid;
use glam::DVec3;
impl Face {
/// Create a planar face from a polygon defined by 3D points.
///
/// Points must be coplanar and form a non-degenerate polygon (at least 3 points).
/// The face normal follows the right-hand rule: the normal points toward you
/// when the points appear counter-clockwise.
///
/// Uses `BRepBuilderAPI_MakePolygon` + `BRepBuilderAPI_MakeFace`.
///
/// # Errors
/// Returns [`Error::InvalidPolygon`] if the points are non-planar, degenerate,
/// or fewer than 3 points are provided.
pub fn from_polygon(points: &[DVec3]) -> Result<Face, Error> {
let coords: Vec<f64> = points.iter().flat_map(|p| [p.x, p.y, p.z]).collect();
let inner = ffi::face_from_polygon(&coords);
if inner.is_null() {
return Err(Error::InvalidPolygon);
}
Ok(Face::new(inner))
}
}
/// A face topology shape.
pub struct Face {
pub(crate) inner: cxx::UniquePtr<ffi::TopoDS_Face>,
}
impl Face {
/// Create a Face wrapping a `TopoDS_Face`.
pub(crate) fn new(inner: cxx::UniquePtr<ffi::TopoDS_Face>) -> Self {
Face { inner }
}
/// Return the `TShapeId` (underlying `TopoDS_TShape*` address) of this face.
///
/// Use this to look up or set entries in `Shape::colormap`.
/// Only available when compiled with `--features color`.
#[cfg(feature = "color")]
pub fn tshape_id(&self) -> crate::shape::TShapeId {
crate::shape::TShapeId(ffi::face_tshape_id(&self.inner))
}
/// Get the normal vector at the center of mass of this face.
///
/// The center of mass is computed using surface-area-weighted integration,
/// and the normal is evaluated at that point on the surface.
pub fn normal_at_center(&self) -> DVec3 {
let mut nx = 0.0;
let mut ny = 0.0;
let mut nz = 0.0;
ffi::face_normal_at_center(&self.inner, &mut nx, &mut ny, &mut nz);
DVec3::new(nx, ny, nz)
}
/// Get the center of mass of this face.
///
/// Computed using `BRepGProp::SurfaceProperties`, which gives the
/// surface-area-weighted centroid.
pub fn center_of_mass(&self) -> DVec3 {
let mut cx = 0.0;
let mut cy = 0.0;
let mut cz = 0.0;
ffi::face_center_of_mass(&self.inner, &mut cx, &mut cy, &mut cz);
DVec3::new(cx, cy, cz)
}
/// Extrude this face along the given direction vector to create a solid.
///
/// Uses `BRepPrimAPI_MakePrism`. The result can be converted to a `Shape`
/// via `Shape::from(solid)`.
pub fn extrude(&self, dir: DVec3) -> Result<Solid, Error> {
let shape = ffi::face_extrude(&self.inner, dir.x, dir.y, dir.z);
if shape.is_null() {
return Err(Error::ExtrudeFailed);
}
Ok(Solid::new(shape))
}
/// Revolve this face around an axis to create a solid.
///
/// Uses `BRepPrimAPI_MakeRevol`. The face's current position defines the
/// start of the revolution (angle = 0). The result can be converted to a
/// `Shape` via `Shape::from(solid)`.
///
/// - `axis_origin`: a point on the rotation axis
/// - `axis_dir`: direction of the rotation axis (normalised by OCCT)
/// - `angle`: rotation angle in radians (`std::f64::consts::TAU` for a full revolution)
///
/// # Errors
/// Returns [`Error::RevolveFailed`] if the operation fails (e.g. the face
/// crosses the rotation axis, causing self-intersection).
pub fn revolve(&self, axis_origin: DVec3, axis_dir: DVec3, angle: f64) -> Result<Solid, Error> {
let shape = ffi::face_revolve(
&self.inner,
axis_origin.x, axis_origin.y, axis_origin.z,
axis_dir.x, axis_dir.y, axis_dir.z,
angle,
);
if shape.is_null() {
return Err(Error::RevolveFailed);
}
Ok(Solid::new(shape))
}
}