Skip to main content

brep_kernel/offset/
offset_shell.rs

1use crate::boolean::{assemble_open_fragments, edge_interior_lies_on, finalize_assembled_solid};
2use crate::classification::{parameter_point_in_face, PolygonClass};
3use crate::face_merge::merge_same_surface_faces_open;
4use crate::fragment::{fragment_solid, FaceFragmentRecord, FragmentEdgeSource};
5use crate::imprint::{
6    EdgeSplitRecord, FaceImprints, FaceKey, FacePcurve, ImprintOptions, ImprintPieceRecord,
7    ImprintResultRecord, ImprintVertex,
8};
9use crate::mass_properties::parameter_space_area;
10use crate::project_point_to_surface;
11use crate::topology::{
12    BrepSolid, CoedgeRecord, EdgeRecord, FaceRecord, LoopRecord, ShellRecord, VertexRecord,
13};
14use crate::{
15    apply_edge_splits, build_imprints, classify_point, offset_face_carrier, DiagnosticSeverity,
16    KernelDiagnostics, KernelOutcome, KernelStage, KernelTolerances, PointClass, Vec2, Vec3,
17};
18use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
19use serde::Serialize;
20
21const SOURCE_OPERAND: u8 = 250;
22
23fn debug_enabled() -> bool {
24    std::env::var("BREP_OS_DEBUG").is_ok_and(|value| !value.is_empty() && value != "0")
25}
26
27macro_rules! os_debug {
28    ($($arg:tt)*) => {
29        if debug_enabled() {
30            eprintln!($($arg)*);
31        }
32    };
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
36#[serde(rename_all = "lowercase")]
37pub enum OffsetFaceRole {
38    Source,
39    Offset,
40    Wall,
41}
42
43#[derive(Clone, Debug, Serialize)]
44pub struct OffsetShellResultRecord {
45    pub solid: BrepSolid,
46    pub face_images: Vec<OffsetShellFaceImageRecord>,
47}
48
49#[derive(Clone, Debug, Serialize)]
50pub struct OffsetShellFaceImageRecord {
51    pub role: OffsetFaceRole,
52    pub source_face_id: u64,
53}
54
55#[derive(Clone)]
56struct Carrier {
57    solid: BrepSolid,
58    source_face_id: u64,
59    kind: OffsetFaceRole,
60}
61
62#[path = "offset_shell/carriers.rs"]
63mod carriers;
64#[path = "offset_shell/smooth_sync.rs"]
65mod smooth_sync;
66#[path = "offset_shell/carrier_rebuild.rs"]
67mod carrier_rebuild;
68#[path = "offset_shell/connectors.rs"]
69mod connectors;
70#[path = "offset_shell/orientation.rs"]
71mod orientation;
72#[path = "offset_shell/rim_welds.rs"]
73mod rim_welds;
74#[path = "offset_shell/pipeline.rs"]
75mod pipeline;
76// BREP private tests: 77b8029939f98de3
77
78use carrier_rebuild::*;
79use carriers::*;
80use connectors::*;
81use orientation::*;
82use rim_welds::*;
83use smooth_sync::*;
84
85pub use pipeline::{offset_shell, offset_shell_with_diagnostics};
86pub(crate) use orientation::{flip_all_faces, flip_shell_faces, orient_open_solid_faces};
87
88/// Merge connected shells, preserving the first shell record and face encounter order.
89fn merge_connected_shells(solid: &mut BrepSolid, shell_unions: Vec<(usize, usize)>) {
90    if shell_unions.is_empty() {
91        return;
92    }
93    let mut parent = (0..solid.shells.len()).collect::<Vec<_>>();
94    fn root(parent: &mut [usize], index: usize) -> usize {
95        if parent[index] != index {
96            parent[index] = root(parent, parent[index]);
97        }
98        parent[index]
99    }
100    for (first, second) in shell_unions {
101        let first_root = root(&mut parent, first);
102        let second_root = root(&mut parent, second);
103        if first_root != second_root {
104            parent[second_root] = first_root;
105        }
106    }
107    let original = std::mem::take(&mut solid.shells);
108    let mut merged = Vec::<ShellRecord>::new();
109    let mut group_of = HashMap::<usize, usize>::default();
110    for (index, shell) in original.into_iter().enumerate() {
111        let group = root(&mut parent, index);
112        if let Some(target) = group_of.get(&group).copied() {
113            merged[target].faces.extend(shell.faces);
114        } else {
115            group_of.insert(group, merged.len());
116            merged.push(shell);
117        }
118    }
119    solid.shells = merged;
120}