use crate::{KernelRefusal, KernelStage, OrRefuse};
use super::*;
fn one_call_imprint_enabled() -> bool {
switch!("BREP_OFFSET_ONE_CALL_IMPRINT", true)
}
thread_local! {
static ONE_CALL_RUNS: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
}
pub(super) fn take_one_call_runs() -> u64 {
ONE_CALL_RUNS.with(|count| count.replace(0))
}
fn coheres_face_normals(solid: &mut BrepSolid) -> Result<(), KernelRefusal> {
for face in solid.shells.iter_mut().flat_map(|shell| &mut shell.faces) {
face.same_sense = parameter_space_area(face).or_refuse(KernelStage::Sew, "parameter_space_area")? > 0.0;
}
for shell_index in 0..solid.shells.len() {
let single = BrepSolid {
mass_properties_cache: Default::default(),
cells: Default::default(),
id: solid.id,
vertices: solid.vertices.clone(),
edges: solid.edges.clone(),
shells: vec![solid.shells[shell_index].clone()],
genus: 0,
kind: crate::BodyKind::Solid,
};
if crate::signed_volume_against(&single, 0.0).or_refuse(KernelStage::Sew, "solid_signed_volume")? < 0.0 {
flip_shell_faces(&mut solid.shells[shell_index])?;
}
}
Ok(())
}
fn split_self_touching_source(source: &BrepSolid) -> Result<Option<BrepSolid>, KernelRefusal> {
if std::env::var("BREP_SELF_TOUCH_SPLIT").as_deref() == Ok("0") {
return Ok(None);
}
let scale = crate::solid_scale(source);
let tolerance = 1e-7f64.max(scale * 1e-9);
let edge_splits = crate::imprint::self_touch_edge_splits(source, SOURCE_OPERAND, tolerance, scale)?;
if edge_splits.is_empty() {
return Ok(None);
}
os_debug!(
"self-touch: source split on {} edge(s): {:?}",
edge_splits.len(),
edge_splits
.iter()
.map(|split| split.edge_id)
.collect::<Vec<_>>(),
);
let imprint = ImprintResultRecord {
edge_splits,
..empty_imprint()
};
Ok(Some(apply_edge_splits(source, SOURCE_OPERAND, &imprint)?))
}
const RULED_NEIGHBOUR_CLEARANCE_BANDS: f64 = 16.0;
const REACH_CLEARANCE: f64 = 0.25;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum Body {
Shell,
Whole,
}
#[cfg(not(test))]
fn forced_direct_refusal() -> Option<KernelRefusal> {
None
}
fn every_skin_piece_enabled() -> bool {
crate::switch!("BREP_OFFSET_EVERY_SKIN_PIECE", true)
}
fn every_seeded_skin_piece_enabled() -> bool {
crate::switch!("BREP_OFFSET_EVERY_SEEDED_SKIN_PIECE", true)
}
pub fn offset_shell(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
) -> Result<OffsetShellResultRecord, KernelRefusal> {
let direct = match forced_direct_refusal() {
Some(forced) => Err(forced),
None => offset_body(source, opening_face_ids, distance, Body::Shell, "offset_shell"),
};
let mut shelled = offset_shell_lanes(source, opening_face_ids, distance, direct)?;
if membrane_shells_on() {
drop_folded_shells(&mut shelled.solid, distance);
let membranes = membrane_shell_indices(&shelled.solid, distance);
let paired = paired_face_ids(&shelled.solid);
if !membranes.is_empty() || !paired.is_empty() {
for (first, second) in &paired {
let shell = shelled.solid.shells.iter().position(|shell| shell.faces.iter().any(|face| face.id == *first));
os_debug!(
"membrane pair: faces {first} and {second} in shell {shell:?} of {:?} faces",
shell.map(|index| shelled.solid.shells[index].faces.len())
);
}
return Err(KernelRefusal::unsupported(KernelStage::Validate, "offset_shell_membrane", format!(
"offset_shell: at this distance the result has {} shell(s) enclosing no volume and \
{} pair(s) of faces bounded by the same edges{} (zero-thickness membranes \
beside the cavity), which no solid carries",
membranes.len(),
paired.len(),
paired.first().map(|(a, b)| format!(", first faces {a} and {b}")).unwrap_or_default()
)));
}
}
Ok(shelled)
}
fn membrane_shells_on() -> bool {
switch!("BREP_OFFSET_MEMBRANE_SHELLS", true)
}
fn face_is_folded(face: &FaceRecord, edges: &HashMap<u64, &EdgeRecord>) -> bool {
let mut balance: HashMap<u64, (usize, usize)> = HashMap::default();
for coedge in face.loops.iter().flat_map(|l| &l.coedges) {
if edges.get(&coedge.edge_id).is_some_and(|edge| edge.degenerate) {
continue;
}
let entry = balance.entry(coedge.edge_id).or_default();
if coedge.forward {
entry.0 += 1;
} else {
entry.1 += 1;
}
}
!balance.is_empty() && balance.values().all(|&(forward, reversed)| forward == 1 && reversed == 1)
}
fn shell_is_bag(shell: &ShellRecord, edges: &HashMap<u64, &EdgeRecord>) -> bool {
let mut sets: HashMap<Vec<u64>, usize> = HashMap::default();
for face in &shell.faces {
let mut set: Vec<u64> = face
.loops
.iter()
.flat_map(|l| &l.coedges)
.map(|coedge| coedge.edge_id)
.filter(|id| !edges.get(id).is_some_and(|edge| edge.degenerate))
.collect();
set.sort_unstable();
set.dedup();
if set.is_empty() {
return false;
}
*sets.entry(set).or_default() += 1;
}
!sets.is_empty() && sets.values().all(|&count| count == 2)
}
pub(crate) fn drop_folded_shells(solid: &mut BrepSolid, distance: f64) {
let edges: HashMap<u64, &EdgeRecord> = solid.edges.iter().map(|edge| (edge.id, edge)).collect();
let empty = membrane_shell_indices(solid, distance);
let folded: Vec<usize> = (0..solid.shells.len())
.filter(|&index| {
let shell = &solid.shells[index];
empty.contains(&index)
|| ((shell.faces.iter().all(|face| face_is_folded(face, &edges)) || shell_is_bag(shell, &edges))
&& shell_volume_and_area(solid, index)
.is_some_and(|(volume, area)| volume.abs() <= 1e-2 * distance.abs() * area))
})
.collect();
if folded.is_empty() || folded.len() == solid.shells.len() {
return;
}
let mut trimmed = solid.clone();
let mut index = 0;
trimmed.shells.retain(|_| {
let keep = !folded.contains(&index);
index += 1;
keep
});
let used_edges: HashSet<u64> = trimmed
.shells
.iter()
.flat_map(|shell| &shell.faces)
.flat_map(|face| &face.loops)
.flat_map(|l| &l.coedges)
.map(|coedge| coedge.edge_id)
.collect();
trimmed.edges.retain(|edge| used_edges.contains(&edge.id));
let used_vertices: HashSet<u64> = trimmed
.edges
.iter()
.flat_map(|edge| [edge.start_vertex_id, edge.end_vertex_id])
.collect();
trimmed.vertices.retain(|vertex| used_vertices.contains(&vertex.id));
crate::boolean::refresh_derived_genus(&mut trimmed);
let issues = trimmed.validate();
if !issues.is_empty() {
os_debug!("folded shells {folded:?} kept: without them the result does not validate: {issues:?}");
return;
}
os_debug!("dropped folded shells {folded:?}");
*solid = trimmed;
}
pub(crate) fn paired_face_ids(solid: &BrepSolid) -> Vec<(u64, u64)> {
let mut pairs = Vec::new();
let degenerate: HashSet<u64> = solid.edges.iter().filter(|edge| edge.degenerate).map(|edge| edge.id).collect();
for shell in &solid.shells {
let mut seen: HashMap<Vec<u64>, u64> = HashMap::default();
for face in &shell.faces {
let mut edges: Vec<u64> = face
.loops
.iter()
.flat_map(|l| &l.coedges)
.map(|c| c.edge_id)
.filter(|id| !degenerate.contains(id))
.collect();
edges.sort_unstable();
if edges.len() < 2 {
continue;
}
match seen.get(&edges) {
Some(&other) => pairs.push((other, face.id)),
None => {
seen.insert(edges, face.id);
}
}
}
}
pairs
}
fn shell_volume_and_area(solid: &BrepSolid, index: usize) -> Option<(f64, f64)> {
let mut alone = solid.clone();
alone.shells = vec![solid.shells[index].clone()];
crate::solid_mass_properties(&alone).ok().map(|mass| (mass.volume, mass.surface_area))
}
pub(crate) fn membrane_shell_indices(solid: &BrepSolid, distance: f64) -> Vec<usize> {
if solid.vertices.is_empty() {
return Vec::new();
}
let mut lo = Vec3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY);
let mut hi = Vec3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY);
for vertex in &solid.vertices {
lo = Vec3::new(lo.x.min(vertex.point.x), lo.y.min(vertex.point.y), lo.z.min(vertex.point.z));
hi = Vec3::new(hi.x.max(vertex.point.x), hi.y.max(vertex.point.y), hi.z.max(vertex.point.z));
}
let thickness = (1e-6 * hi.sub(lo).length()).min(1e-3 * distance.abs());
(0..solid.shells.len())
.filter(|&index| {
shell_volume_and_area(solid, index).is_some_and(|(volume, area)| volume.abs() <= thickness * area)
})
.collect()
}
fn offset_shell_lanes(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
direct: Result<OffsetShellResultRecord, KernelRefusal>,
) -> Result<OffsetShellResultRecord, KernelRefusal> {
match direct {
Ok(result) => Ok(result),
Err(direct) if super::solid::direct_refusal_takes_the_boolean_lane(&direct) => {
os_debug!("direct pipeline refused ({direct}); taking the boolean lane");
match super::solid::offset_shell_by_boolean(source, opening_face_ids, distance) {
Ok(result) => Ok(result),
Err(boolean) => {
os_debug!("boolean lane refused too ({boolean})");
Err(direct)
}
}
}
Err(direct) => Err(direct),
}
}
fn offset_exact_iso_pcurves_on() -> bool {
switch!("BREP_OFFSET_EXACT_ISO_PCURVES", true)
}
pub(super) fn offset_body(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
body: Body,
entry: &str,
) -> Result<OffsetShellResultRecord, KernelRefusal> {
profile::reset();
let _profile_sink = crate::imprint::ProfileSink::open();
let started = web_time::Instant::now();
let result = offset_shell_any_lane(source, opening_face_ids, distance, body).and_then(|result| {
let mut clock = web_time::Instant::now();
let connected = outward_shell_is_connected(source, distance, &result);
profile::lap(&mut clock, profile::ACCEPTANCE);
connected.map(|()| result)
});
profile::add(profile::TOTAL, started.elapsed());
profile::report(entry);
result
}
pub(super) fn outward_shell_is_connected(
source: &BrepSolid,
distance: f64,
result: &OffsetShellResultRecord,
) -> Result<(), KernelRefusal> {
if distance >= 0.0 || result.solid.shells.len() <= 1 {
return Ok(());
}
let scale = crate::solid_scale(source).max(1.0);
let volumes = result
.solid
.shells
.iter()
.map(|shell| {
let mut one = result.solid.clone();
one.shells = vec![shell.clone()];
crate::solid_mass_properties(&one)
.map(|properties| properties.volume)
.or_refuse(KernelStage::Validate, "solid_mass_properties")
})
.collect::<Result<Vec<f64>, KernelRefusal>>()?;
let total = volumes.iter().map(|volume| volume.abs()).sum::<f64>();
let floor = (1e-9 * total).max(1e-12 * scale * scale * scale);
let empty = volumes.iter().filter(|volume| volume.abs() <= floor).count();
if empty == 0 {
return Ok(());
}
Err(refusals::empty_shell(result.solid.shells.len(), empty, floor).into())
}
fn offset_shell_any_lane(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
body: Body,
) -> Result<OffsetShellResultRecord, KernelRefusal> {
let prepared = split_self_touching_source(source)?;
let source = prepared.as_ref().unwrap_or(source);
if body == Body::Whole {
if opening_face_ids.is_empty() {
if let Some(result) = sphere_body::sphere_body(source, distance) {
return Ok(result);
}
if let Some(result) = super::torus_body::torus_apple(source, distance) {
return Ok(result);
}
}
return imprint_attempts(source, opening_face_ids, distance, body);
}
match revolved_wedge::recognize(source, opening_face_ids, distance) {
Ok(revolved_wedge::Recognition::Wedge(wedge)) => {
os_debug!("revolved wedge lane: {}", wedge.report());
return revolved_wedge::build(&wedge, source);
}
Ok(revolved_wedge::Recognition::Declined(reason)) => {
os_debug!("revolved wedge lane declined: {reason}");
}
Err(error) => {
os_debug!("revolved wedge lane declined: could not read the source ({error})");
}
}
if let Some(result) = sphere_cut_shell::sphere_cut_shell(source, opening_face_ids, distance) {
return Ok(result);
}
if let Some(result) = planar_shell::planar_shell(source, opening_face_ids, distance) {
return Ok(result);
}
imprint_attempts(source, opening_face_ids, distance, body)
}
fn imprint_attempts(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
body: Body,
) -> Result<OffsetShellResultRecord, KernelRefusal> {
let mut extension_fired = false;
let mut reflex_rebuilds = 0usize;
let mut shadowed_points = Vec::new();
let mut omitted = HashSet::default();
let mut vacated_notes = Vec::new();
let result = match offset_shell_impl(
source,
opening_face_ids,
distance,
body,
true,
&mut extension_fired,
&mut reflex_rebuilds,
&HashSet::default(),
&HashSet::default(),
false,
&mut shadowed_points,
&mut omitted,
&mut vacated_notes,
) {
Ok(result) => {
os_debug!(
"offset_shell result: FIRST attempt (extension {}, reflex-rebuilt {})",
if extension_fired { "fired" } else { "idle" },
reflex_rebuilds,
);
Ok(result)
}
Err(error) if extension_fired => {
os_debug!("extended pipeline failed ({error}); retrying without carrier extension");
let mut unused = false;
let mut retry_reflex = 0usize;
shadowed_points.clear();
omitted.clear();
let retried = offset_shell_impl(
source,
opening_face_ids,
distance,
body,
false,
&mut unused,
&mut retry_reflex,
&HashSet::default(),
&HashSet::default(),
false,
&mut shadowed_points,
&mut omitted,
&mut vacated_notes,
);
os_debug!(
"offset_shell result: RETRY without extension ({}, reflex-rebuilt {retry_reflex})",
if retried.is_ok() { "ok" } else { "err" },
);
retried
}
Err(error) => Err(error),
};
match result {
Err(error) if !refusal_names_its_cause(&error.message) => {
let crossing = name_offset_crossing(source, opening_face_ids, distance, &shadowed_points);
let junction = crossing
.is_none()
.then(|| name_retained_opening_junction(source, opening_face_ids, distance))
.flatten();
Err(cause_in_front(error, crossing, junction, vacated_notes.first()))
}
result => result,
}
}
pub(super) fn cause_in_front(
error: KernelRefusal,
crossing: Option<String>,
junction: Option<String>,
note: Option<&String>,
) -> KernelRefusal {
if let Some(crossing) = crossing {
return error.with_message(|error| {
format!(
"offset_shell: {crossing}; a skin partly shadowed that way is not yet \
assembled ({error})"
)
});
}
if let Some(junction) = junction {
return error.with_message(|error| format!("offset_shell: {junction} ({error})"));
}
match note {
Some(note) => error.with_message(|error| {
format!(
"offset_shell: a rim vacated by an omitted support was left open — {note} \
({error})"
)
}),
None => error,
}
}
fn refusal_names_its_cause(error: &str) -> bool {
[
" coincide near ",
" touch tangentially near ",
"is sealed off from the opening",
"cannot tell whether",
"omitting the shadowed offsets of source faces",
"no retained face keeps any offset skin",
"is shadowed at every one of its",
"a rim vacated by an omitted support was left open",
"junction, so its offset ends inside the cavity",
"cross near",
"folds where the carve",
"is curved and not a ruled surface",
"cannot be built",
]
.iter()
.any(|name| error.contains(name))
}
fn offset_shell_impl(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
body: Body,
extend_oblique_carriers: bool,
extension_fired: &mut bool,
reflex_rebuilds: &mut usize,
shadowed_supports: &HashSet<u64>,
cavity_bounds: &HashSet<u64>,
every_seeded_piece: bool,
shadowed_points: &mut Vec<(Vec3, u64)>,
omitted_supports: &mut HashSet<u64>,
vacated_notes: &mut Vec<String>,
) -> Result<OffsetShellResultRecord, KernelRefusal> {
profile::attempt();
let mut clock = web_time::Instant::now();
omitted_supports.clone_from(shadowed_supports);
vacated_notes.clear();
if !distance.is_finite() || distance.abs() <= MINIMUM_DISTANCE {
return Err(KernelRefusal::input(KernelStage::Collect, "offset_shell_distance", "offset_shell: distance must be finite and non-zero"));
}
if opening_face_ids.is_empty() && body == Body::Shell {
return Err(KernelRefusal::input(KernelStage::Collect, "offset_shell_no_opening", "offset_shell: at least one opening face is required"));
}
let source_faces = source
.shells
.iter()
.flat_map(|shell| &shell.faces)
.collect::<Vec<_>>();
let opening_set = opening_face_ids.iter().copied().collect::<HashSet<_>>();
if opening_set
.iter()
.any(|id| !source_faces.iter().any(|face| face.id == *id))
{
return Err(KernelRefusal::input(KernelStage::Collect, "offset_shell_foreign_opening", "offset_shell: opening face does not belong to source"));
}
let retained = source_faces
.iter()
.filter(|face| !opening_set.contains(&face.id))
.map(|face| face.id)
.collect::<Vec<_>>();
if retained.is_empty() {
return Err(refusals::no_retained_face().into());
}
let mut carriers = Vec::new();
let mut open_section_reach = false;
if distance < 0.0 && std::env::var("BREP_OFFSET_OPEN_SECTION_REACH").as_deref() != Ok("0") {
for face in &source_faces {
if retained.contains(&face.id) && open_profile_has_construction_headroom(face)? {
open_section_reach = true;
break;
}
}
}
let mut carved_supports = HashMap::<u64, FaceRecord>::default();
let mut tangent_rim_rounds: Vec<(u64, u64, u64)> = Vec::new();
let mut shielded_cusps = HashSet::<u64>::default();
for face_id in &retained {
let face = source_faces.iter().find(|face| face.id == *face_id).unwrap();
profile::lap(&mut clock, profile::CARRIER_TRIM);
let collapsed = profile::detail("offset_support_collapsed", || offset_support_collapsed(face, distance))?;
profile::lap(&mut clock, profile::CARRIER_REGULARITY);
if collapsed {
os_debug!("omitting collapsed offset support for source face {face_id}");
continue;
}
if shadowed_supports.contains(face_id) {
os_debug!("omitting shadowed offset support for source face {face_id}");
continue;
}
let miter = source_faces
.iter()
.find(|face| face.id == *face_id)
.map(|face| face_reflex_miter_tan(source, face))
.transpose()?
.flatten();
let reflex_extension = miter.map(|worst_tan| {
(distance.abs() * worst_tan * 1.5)
.max(distance.abs())
.min(distance.abs() * 12.0)
});
if let Some(extension) = reflex_extension {
os_debug!("carrier for src face {face_id}: reflex-extended by {extension:.4}");
}
let mut curved_reach = None;
let planar_reach = planar_opening_reach(face, &source_faces, &opening_set, distance)?;
let extension = if distance < 0.0 {
outward_carrier_extension(source, face, &source_faces, &opening_set, distance.abs())?
} else {
let mut inward = crate::CarrierExtension::uniform(reflex_extension.unwrap_or(0.0));
if let Some(pad) = thin_skin_pad(source, face, distance)? {
inward = crate::CarrierExtension::uniform(inward.u_min.max(pad));
os_debug!("carrier for src face {face_id}: thin skin padded by {pad:.6}");
}
if let Some(reach) = planar_reach {
let grown = reach + REACH_CLEARANCE * distance;
inward = crate::CarrierExtension::uniform(inward.u_min.max(grown));
os_debug!("carrier for src face {face_id}: planar reach to its opening {reach:.6}");
}
if reflex_extension.is_some() {
let [u_min, u_max, v_min, v_max] =
apex_corner_reach(source, face, &source_faces, &opening_set, distance)?;
if u_min.max(u_max).max(v_min).max(v_max) > 0.0 {
inward.u_min = inward.u_min.max(u_min);
inward.u_max = inward.u_max.max(u_max);
inward.v_min = inward.v_min.max(v_min);
inward.v_max = inward.v_max.max(v_max);
os_debug!(
"carrier for src face {face_id}: apex corner reach u=({:.4},{:.4}) v=({:.4},{:.4})",
inward.u_min, inward.u_max, inward.v_min, inward.v_max,
);
}
}
if let Some((at_v_max, reach)) =
fallshort_opening_reach(source, face, &source_faces, &opening_set, distance)?
{
let grown = reach + REACH_CLEARANCE * distance.abs();
if at_v_max {
inward.v_max = inward.v_max.max(grown);
} else {
inward.v_min = inward.v_min.max(grown);
}
os_debug!(
"carrier for src face {face_id}: inward reach to its opening plane {reach:.6} at v_{} ",
if at_v_max { "max" } else { "min" },
);
}
if let Some((opening, gap)) =
offset_falls_short_of_curved_opening(source, face, &source_faces, &opening_set, distance)?
{
if std::env::var("BREP_OFFSET_CURVED_REACH").as_deref() != Ok("0") {
curved_reach = curved_opening_reach_source(source, face, distance)?;
}
let mut ruled_reach = false;
if curved_reach.is_none() {
if let Some((at_v_max, reach)) =
curved_fallshort_reach(source, face, &source_faces, &opening_set, distance)?
{
let grown = reach + REACH_CLEARANCE * distance.abs();
if at_v_max {
inward.v_max = inward.v_max.max(grown);
} else {
inward.v_min = inward.v_min.max(grown);
}
ruled_reach = true;
os_debug!(
"carrier for src face {face_id}: ruled reach to its curved opening {opening} {reach:.6} at v_{}",
if at_v_max { "max" } else { "min" },
);
}
}
if curved_reach.is_none() && !ruled_reach && super::rim_round::tangent_rim_round_enabled() {
for coedge in face.loops.iter().flat_map(|loop_record| &loop_record.coedges) {
let Some((mate, mate_use)) = junction_mate(&source_faces, face, coedge.edge_id) else {
continue;
};
if mate.id != opening || !uses_are_tangent(face, coedge, mate, mate_use)? {
continue;
}
os_debug!("tangent rim round: source face {face_id} meets opening {opening} tangentially along edge {}", coedge.edge_id);
tangent_rim_rounds.push((*face_id, opening, coedge.edge_id));
}
}
if curved_reach.is_none() && !ruled_reach && !tangent_rim_rounds.iter().any(|(id, _, _)| id == face_id) {
return Err(KernelRefusal::unsupported(KernelStage::Refine, "offset_shell_curved_opening_short", format!(
"offset_shell: the offset skin of source face {face_id} stops {gap:.3e} short of \
curved opening face {opening}; only a planar opening's skin is grown to meet it"
)));
}
}
inward
};
if distance < 0.0 {
os_debug!(
"carrier for src face {face_id}: outward extension u=({:.3},{:.3}) v=({:.3},{:.3})",
extension.u_min,
extension.u_max,
extension.v_min,
extension.v_max,
);
}
profile::lap(&mut clock, profile::CARRIER_TRIM);
let carved = profile::detail("carve_folded_support", || carve_shell_support(source, face, distance, opening_face_ids))?;
profile::lap(&mut clock, profile::CARRIER_REGULARITY);
let cut_rim = if carved.is_none() && curved_reach.is_none() {
cut_rim_full_rectangle_source(source, face, &source_faces, &opening_set, distance)?
} else {
None
};
let sharp_side = if carved.is_none() && cut_rim.is_none() && curved_reach.is_none() {
match sharp_side_continued_source(source, face, &source_faces, &opening_set, distance, &extension)? {
Some(continued) => Some(continued),
None => match band_reach_continued_source(source, face, distance, &extension)? {
Some(widened) => Some(widened),
None => reflex_side_widened_source(source, face, &source_faces, &opening_set, distance, &extension)?,
},
}
} else {
None
};
let carrier_source = match (&carved, &cut_rim) {
(None, None) if curved_reach.is_some() => curved_reach.as_ref().unwrap(),
(None, None) if sharp_side.is_some() => sharp_side.as_ref().unwrap(),
(None, Some(rebuilt)) => rebuilt,
(Some(carved), _) => {
os_debug!(
"carving the fold off source face {face_id}: {:.1}% of its trim kept, \
{} new boundary pcurve(s)",
carved.kept_fraction * 100.0,
carved.fold_boundary
);
carved_supports.insert(*face_id, carved.face.clone());
if carved.shielded_cusp {
shielded_cusps.insert(*face_id);
}
&carved.solid
}
(None, None) => source,
};
profile::lap(&mut clock, profile::CARRIER_TRIM);
let (mut carrier, lane) = carrier_solid_sided_lane(carrier_source, *face_id, distance, &extension)?;
profile::carrier_built(lane, clock.elapsed());
clock = web_time::Instant::now();
if (distance < 0.0 || reflex_extension.is_some()) && face.surface.is_affine().or_refuse(KernelStage::Sew, "is_affine")? {
drop_wall_interior_loops(&mut carrier)?;
let pads = [extension.u_min, extension.u_max, extension.v_min, extension.v_max];
let widest = pads.iter().copied().fold(f64::NEG_INFINITY, f64::max);
let narrowest = pads.iter().copied().fold(f64::INFINITY, f64::min);
let uniform = (extension.u_min == extension.u_max
&& extension.u_min == extension.v_min
&& extension.u_min == extension.v_max)
|| (convex_polygon_outline_enabled() && widest - narrowest <= 1e-9 * widest.abs());
if distance < 0.0 && uniform {
let clearance =
RULED_NEIGHBOUR_CLEARANCE_BANDS * offset_skin_band(crate::solid_scale(source), distance);
if let Some(rebuilt) = outline_offset_carrier(source, face, widest, clearance, &carrier)? {
os_debug!(
"carrier for src face {face_id}: outline trimmed by its offset by {:.4}",
widest + clearance
);
carrier = rebuilt;
}
}
}
if distance > 0.0
&& reflex_extension.is_some()
&& face.surface.is_affine().or_refuse(KernelStage::Refine, "is_affine")?
&& outline_has_curved_element(source, face)?
{
carrier = full_rectangle_face(&carrier, &carrier.shells[0].faces[0])?;
}
if planar_reach.is_some() {
carrier = full_rectangle_face(&carrier, &carrier.shells[0].faces[0])?;
}
if open_section_reach {
if let Some(continued) = open_section_construction(source, face, &carrier, &extension, distance)? {
carrier = continued;
os_debug!("carrier for src face {face_id}: open section uses its natural construction rectangle");
}
}
if distance < 0.0 && !shadowed_supports.is_empty() {
let report = close_vacated_rims(
&mut carrier,
source_faces
.iter()
.find(|face| face.id == *face_id)
.copied()
.unwrap_or(face),
&source_faces,
shadowed_supports,
)?;
if report.closed > 0 {
os_debug!(
"carrier for src face {face_id}: {} vacated rim run(s) bridged",
report.closed
);
}
for declined in &report.declined {
os_debug!("carrier for src face {face_id}: vacated rim left alone — {declined}");
vacated_notes.push(format!("source face {face_id}: {declined}"));
}
}
carriers.push(Carrier {
solid: carrier,
source_face_id: *face_id,
kind: OffsetFaceRole::Offset,
});
}
profile::lap(&mut clock, profile::CARRIER_TRIM);
let mut apex_cap_sources = HashSet::default();
let mut apex_cap_pairs: Vec<(usize, usize)> = Vec::new();
{
let retained_count = carriers.len();
for index in 0..retained_count {
let carved_face = carved_supports.get(&carriers[index].source_face_id);
let source_face = carved_face
.or_else(|| source_by_id_lookup(&source_faces, carriers[index].source_face_id));
let Some(source_face) = source_face else {
continue;
};
let carrier_surface = carriers[index].solid.shells[0].faces[0].surface.clone();
for coedge in source_face
.loops
.iter()
.flat_map(|loop_record| &loop_record.coedges)
{
let Some(edge) = source
.edges
.iter()
.find(|edge| edge.id == coedge.edge_id)
else {
continue;
};
if !edge.degenerate {
continue;
}
let [p0, p1] = coedge.pcurve.domain().or_refuse(KernelStage::Sew, "domain")?;
let mut ring = Vec::new();
for sample in 0..=8 {
let uv = coedge
.pcurve
.evaluate(p0 + (p1 - p0) * sample as f64 / 8.0).or_refuse(KernelStage::Sew, "evaluate")?;
ring.push(carrier_surface.evaluate(uv.x, uv.y).or_refuse(KernelStage::Sew, "evaluate")?);
}
let spread = ring
.iter()
.map(|point| point.sub(ring[0]).length())
.fold(0.0f64, f64::max);
if spread <= distance.abs() * 1e-2 {
continue;
}
let apex = source
.vertices
.iter()
.find(|vertex| vertex.id == edge.start_vertex_id)
.map(|vertex| vertex.point)
.ok_or_else(|| "offset_shell: apex vertex missing".to_string()).or_refuse(KernelStage::Sew, "offset_shell_apex_vertex_missing")?;
let centroid = ring
.iter()
.fold(Vec3::default(), |sum, point| sum.add(*point))
.scale(1.0 / ring.len() as f64);
let axis = apex.sub(centroid);
let axis = if axis.length() > 1e-9 {
axis.normalized().or_refuse(KernelStage::Sew, "normalized")?
} else {
Vec3::new(0.0, 0.0, 1.0)
};
os_debug!(
"apex-cap carrier for src face {} at ({:.3},{:.3},{:.3}) r={:.3}",
carriers[index].source_face_id,
apex.x,
apex.y,
apex.z,
distance.abs(),
);
apex_cap_sources.insert(index);
apex_cap_sources.insert(carriers.len());
apex_cap_pairs.push((index, carriers.len()));
carriers.push(Carrier {
solid: crate::make_sphere_brep(apex, distance.abs(), axis).or_refuse(KernelStage::Sew, "make_sphere_brep")?,
source_face_id: carriers[index].source_face_id,
kind: OffsetFaceRole::Offset,
});
}
}
}
if !tangent_rim_rounds.is_empty() {
let tolerance = 1e-7f64.max(crate::solid_scale(source) * 1e-9);
for (neighbour_id, opening_id, edge_id) in &tangent_rim_rounds {
let Some(neighbour_index) = carriers.iter().position(|carrier| carrier.source_face_id == *neighbour_id && matches!(carrier.kind, OffsetFaceRole::Offset)) else {
continue;
};
let Some(edge) = source.edges.iter().find(|edge| edge.id == *edge_id) else { continue };
let Some(opening_face) = source_by_id_lookup(&source_faces, *opening_id) else { continue };
match super::rim_round::circular_rim_round(edge, opening_face, distance, tolerance)? {
Some(round) => {
os_debug!("tangent rim round: torus about edge {edge_id} (neighbour {neighbour_id}, opening {opening_id}) as carrier {}", carriers.len());
apex_cap_sources.insert(neighbour_index);
apex_cap_sources.insert(carriers.len());
apex_cap_pairs.push((neighbour_index, carriers.len()));
carriers.push(Carrier { solid: round, source_face_id: *neighbour_id, kind: OffsetFaceRole::Offset });
}
None => {
return Err(KernelRefusal::unsupported(KernelStage::Refine, "offset_shell_tangent_rim_round", format!(
"offset_shell: the round about the tangent rim (edge {edge_id}) between source face {neighbour_id} and \
curved opening face {opening_id} is built only for a circular rim inside its own circle"
)));
}
}
}
}
profile::lap(&mut clock, profile::APEX_CAPS);
if distance < 0.0 {
for face_id in &retained {
if carved_supports.contains_key(face_id)
|| !carriers.iter().any(|carrier| carrier.source_face_id == *face_id)
{
continue;
}
let face = source_by_id_lookup(&source_faces, *face_id).unwrap();
if let Some(fold) = profile::detail("outward_fold_the_carve_missed", || outward_fold_the_carve_missed(face, distance))? {
return Err(KernelRefusal::unsupported(KernelStage::Refine, "offset_shell_outward_fold_band", format!(
"offset_shell: the outward offset of source face {face_id} folds where the carve \
saw no fold (area factor {:.3e} at u={:.6}, v={:.6}); a fold band that thin is \
not built",
fold.factor, fold.u, fold.v
)));
}
}
}
profile::lap(&mut clock, profile::CARRIER_REGULARITY);
let mut ruled_walls = HashSet::default();
let mut inward_extended_walls = HashSet::default();
let mut reflex_wall_neighbors = HashMap::default();
let mut ruled_built = HashMap::<u64, BrepSolid>::default();
if distance < 0.0 {
for face_id in opening_face_ids {
let source_face = source_by_id_lookup(&source_faces, *face_id).unwrap();
if source_face.surface.is_affine().or_refuse(KernelStage::Sew, "is_affine")? {
continue;
}
if !ruled_opening(source_face)? {
return Err(KernelRefusal::unsupported(KernelStage::Refine, "offset_shell_curved_opening", format!(
"offset_shell: opening face {face_id} is curved and not a ruled surface; an \
outward wall is built only on a planar or ruled opening"
)));
}
let pad = outward_wall_pad(
source,
source_face,
&source_faces,
&opening_set,
distance.abs(),
)?;
let tolerance = 1e-7f64.max(crate::solid_scale(source) * 1e-9);
match extended_ruled_wall(source, source_face, pad, tolerance)? {
Ok(wall) => {
os_debug!(
"wall for opening face {face_id}: ruled, pad {pad:.4}, sides reach {:.4?}",
wall.reached
);
ruled_built.insert(*face_id, wall.solid);
}
Err(reason) => {
return Err(KernelRefusal::unsupported(KernelStage::Refine, "offset_shell_ruled_wall", format!(
"offset_shell: the outward wall of ruled opening face {face_id} cannot be \
built: {reason}"
)));
}
}
}
}
let ruled_openings = ruled_built
.keys()
.filter_map(|id| source_by_id_lookup(&source_faces, *id))
.collect::<Vec<_>>();
for face_id in opening_face_ids {
let source_face = source_faces
.iter()
.find(|face| face.id == *face_id)
.unwrap();
if let Some(wall) = ruled_built.remove(face_id) {
ruled_walls.insert(carriers.len());
carriers.push(Carrier {
solid: wall,
source_face_id: *face_id,
kind: OffsetFaceRole::Wall,
});
continue;
}
if distance > 0.0
&& source_face.surface.is_affine().or_refuse(KernelStage::Refine, "is_affine")?
{
if let Some((pad, neighbors)) =
inward_wall_pad(source, source_face, &source_faces, &opening_set, distance)?
{
let padded = carrier_solid(source, *face_id, 0.0, pad)?;
let wall = full_rectangle_face(&padded, &padded.shells[0].faces[0])?;
inward_extended_walls.insert(carriers.len());
reflex_wall_neighbors.insert(carriers.len(), neighbors);
carriers.push(Carrier {
solid: wall,
source_face_id: *face_id,
kind: OffsetFaceRole::Wall,
});
continue;
}
}
carriers.push(Carrier {
solid: if distance < 0.0 && source_face.surface.is_affine().or_refuse(KernelStage::Sew, "is_affine")? {
let pad = outward_wall_pad(
source,
source_face,
&source_faces,
&opening_set,
distance.abs(),
)?;
let pad = if ruled_openings
.iter()
.any(|ruled| source_faces_adjacent(source_face, ruled))
{
pad + RULED_NEIGHBOUR_CLEARANCE_BANDS
* offset_skin_band(crate::solid_scale(source), distance)
} else if outline_has_free_element(source, source_face)? {
pad + RULED_NEIGHBOUR_CLEARANCE_BANDS * offset_skin_band(crate::solid_scale(source), distance)
} else {
pad
};
os_debug!("wall for opening face {face_id}: outward pad {pad:.4}");
let mut scaled = carrier_solid(source, *face_id, 0.0, pad)?;
drop_wall_interior_loops(&mut scaled)?;
let mut wall = match profile::detail("offset_outline_wall", || offset_outline_wall(source, source_face, pad))? {
Ok(outline) => {
let same = profile::detail("walls_trim_the_same_region", || walls_trim_the_same_region(
&outline,
&scaled,
1e-9f64.max(crate::solid_scale(source) * 1e-11),
))?;
if same {
os_debug!("wall for opening face {face_id}: scaled, which IS its outline offset");
scaled
} else {
os_debug!("wall for opening face {face_id}: outline offset by {pad:.4}");
outline
}
}
Err(reason) => {
let carrier_surfaces = carriers
.iter()
.filter(|carrier| matches!(carrier.kind, OffsetFaceRole::Offset))
.map(|carrier| (carrier.source_face_id, &carrier.solid.shells[0].faces[0].surface))
.collect::<Vec<_>>();
if let Err(uncovered) = scaled_wall_covers_offset(
source,
source_face,
&scaled,
pad,
&source_faces,
&opening_set,
&carrier_surfaces,
)? {
return Err(KernelRefusal::unsupported(KernelStage::Refine, "offset_shell_opening_wall_coverage", format!(
"offset_shell: opening face {face_id}'s outline is not offset \
({reason}), and its scaled outward wall does not contain every \
retained neighbour's grown offset section (a point of one lies \
{uncovered:.3e} outside)"
)));
}
os_debug!(
"wall for opening face {face_id}: scaled, which covers its offset ({reason})"
);
scaled
}
};
drop_wall_interior_loops(&mut wall)?;
wall
} else {
standalone_face(source, *face_id)?
},
source_face_id: *face_id,
kind: OffsetFaceRole::Wall,
});
}
if carriers.len() >= SOURCE_OPERAND as usize {
return Err(KernelRefusal::unsupported(KernelStage::Collect, "offset_shell_carrier_count", "offset_shell: too many carrier faces for current ABI"));
}
profile::lap(&mut clock, profile::WALLS);
let scale = crate::solid_scale(source);
crate::report_scale_migration("offset_shell", scale, || {
source
.vertices
.iter()
.map(|vertex| vertex.point.length())
.fold(1.0, f64::max)
});
let smooth = synchronize_smooth_offset_boundaries(&mut carriers, &source_faces, scale)?;
let reflex_rebuilt = rebuild_reflex_rim_carriers(
&mut carriers,
source,
&source_faces,
&smooth.pairs,
scale,
distance,
)?;
*reflex_rebuilds = reflex_rebuilt.len();
if extend_oblique_carriers {
let oblique_extended = extend_offset_carriers_past_open_hole_rims(
&mut carriers,
source,
&source_faces,
&opening_set,
&smooth.pairs,
scale,
)?;
let mut already_extended = oblique_extended.clone();
already_extended.extend(reflex_rebuilt.iter().copied());
let fallshort_extended = extend_fallshort_curved_carriers(
&mut carriers,
source,
&source_faces,
&opening_set,
&smooth.pairs,
&already_extended,
scale,
distance,
)?;
*extension_fired = !oblique_extended.is_empty() || fallshort_extended > 0;
os_debug!(
"extended {} oblique + {fallshort_extended} fall-short carriers past their opening planes",
oblique_extended.len(),
);
}
profile::lap(&mut clock, profile::EXTENSION);
let tolerance = 1e-7f64.max(scale * 1e-9);
let pair_tolerance = (scale * 1e-8).max(2e-6);
let reach = distance.abs() * 4.0 + pair_tolerance;
let carrier_samples = carriers
.iter()
.map(|carrier| carrier_extent_points(&carrier.solid))
.collect::<Result<Vec<_>, _>>()?;
let carrier_bounds = carrier_samples
.iter()
.map(|samples| Bounds::from_nonempty_points(samples))
.collect::<Option<Vec<_>>>()
.ok_or_else(|| "offset_shell: carrier has no boundary samples".to_string()).or_refuse(KernelStage::Sew, "offset_shell_carrier_has_no_boundary_samples")?;
let source_by_id = source_faces
.iter()
.map(|face| (face.id, *face))
.collect::<HashMap<_, _>>();
let source_samples = source_faces
.iter()
.map(|face| {
let standalone = standalone_face(source, face.id)?;
Ok((face.id, edge_sample_points(&standalone)?))
})
.collect::<Result<HashMap<_, _>, KernelRefusal>>()?;
if debug_enabled() {
for (index, carrier) in carriers.iter().enumerate() {
let face = &carrier.solid.shells[0].faces[0];
let samples = &carrier_samples[index];
let bounds =
samples
.iter()
.fold((Vec3::default(), Vec3::default()), |(low, high), point| {
(
Vec3 {
x: low.x.min(point.x),
y: low.y.min(point.y),
z: low.z.min(point.z),
},
Vec3 {
x: high.x.max(point.x),
y: high.y.max(point.y),
z: high.z.max(point.z),
},
)
});
let surface = &face.surface;
let rational = surface
.control_points
.iter()
.flatten()
.any(|point| (point.w - 1.0).abs() > 1e-12);
os_debug!(
"carrier[{index}] kind={:?} source_face={} loops={} deg=({},{}) net={}x{} rational={rational} bounds=({:.3},{:.3},{:.3})..({:.3},{:.3},{:.3})",
carrier.kind, carrier.source_face_id, face.loops.len(),
surface.degree_u, surface.degree_v,
surface.control_points.len(),
surface.control_points.first().map(|row| row.len()).unwrap_or(0),
bounds.0.x, bounds.0.y, bounds.0.z, bounds.1.x, bounds.1.y, bounds.1.z,
);
}
for (index, carrier) in carriers.iter().enumerate() {
if !matches!(carrier.kind, OffsetFaceRole::Offset) {
continue;
}
let source_face = source_by_id[&carrier.source_face_id];
let carrier_surface = &carrier.solid.shells[0].faces[0].surface;
let (ku, kv) = (
crate::KnotVector::new(
source_face.surface.knots_u.clone(),
source_face.surface.degree_u,
),
crate::KnotVector::new(
source_face.surface.knots_v.clone(),
source_face.surface.degree_v,
),
);
if let (Ok(ku), Ok(kv)) = (ku, kv) {
let [u0, u1] = ku.domain();
let [v0, v1] = kv.domain();
let mut worst = 0.0f64;
let mut worst_at = (0.0f64, 0.0f64);
for iu in 0..=24 {
for iv in 0..=24 {
let u = u0 + (u1 - u0) * iu as f64 / 24.0;
let v = v0 + (v1 - v0) * iv as f64 / 24.0;
let expected = face_offsets(source_face)
.at(u, v, -distance)
.map(|sample| sample.point);
let actual = carrier_surface.evaluate(u, v);
if let (Ok(expected), Ok(actual)) = (expected, actual) {
let error = expected.sub(actual).length();
if error > worst {
worst = error;
worst_at = (u, v);
}
}
}
}
let mut normal_swing = 0.0f64;
let mut previous: Option<Vec3> = None;
for iu in 0..=48 {
let u = u0 + (u1 - u0) * iu as f64 / 48.0;
let v = (v0 + v1) / 2.0;
if let Ok(normal) = face_normal(source_face, u, v) {
if let Some(previous) = previous {
normal_swing = normal_swing.max(previous.sub(normal).length());
}
previous = Some(normal);
}
}
let weights = source_face
.surface
.control_points
.iter()
.flatten()
.map(|point| point.w)
.fold((f64::MAX, f64::MIN), |(low, high), w| {
(low.min(w), high.max(w))
});
os_debug!(
"carrier[{index}] offset fit error max={worst:.6} at=({:.4},{:.4}) domain_u=({u0:.4},{u1:.4}) normal_step_max={normal_swing:.4} src_net={}x{} src_deg=({},{}) src_w=({:.4},{:.4}) knots_u={:?}",
worst_at.0, worst_at.1,
source_face.surface.control_points.len(),
source_face.surface.control_points.first().map(|row| row.len()).unwrap_or(0),
source_face.surface.degree_u, source_face.surface.degree_v,
weights.0, weights.1,
&source_face.surface.knots_u,
);
}
}
os_debug!("smooth pairs: {:?}", smooth.pairs);
}
let mut imprint = empty_imprint();
let mut next_piece_id = 1;
let mut next_vertex_id = 1;
let split_profile = crate::imprint::profile_enabled();
let mut split_ms = [0.0f64; 9];
let mut split_clock = split_profile.then(web_time::Instant::now);
let mut split_lap = |slot: usize, clock: &mut Option<web_time::Instant>| {
if let Some(started) = clock {
let now = web_time::Instant::now();
split_ms[slot] += (now - *started).as_secs_f64() * 1e3;
*started = now;
}
};
let pair_admitted = |first: usize, second: usize| -> bool {
let first_source = source_by_id[&carriers[first].source_face_id];
let second_source = source_by_id[&carriers[second].source_face_id];
if matches!(carriers[first].kind, OffsetFaceRole::Wall)
&& matches!(carriers[second].kind, OffsetFaceRole::Wall)
{
if (distance > 0.0
&& !inward_extended_walls.contains(&first)
&& !inward_extended_walls.contains(&second))
|| !source_faces_adjacent(first_source, second_source)
{
return false;
}
}
if smooth.pairs.contains(&(first, second)) {
os_debug!("pair ({first},{second}) skipped: smooth");
return false;
}
if !carrier_bounds[first].intersects_upper(carrier_bounds[second], pair_tolerance) {
os_debug!("pair ({first},{second}) skipped: bounds");
return false;
}
if !source_faces_adjacent(first_source, second_source)
&& sample_separation(
&source_samples[&first_source.id],
&source_samples[&second_source.id],
) > reach
{
os_debug!("pair ({first},{second}) skipped: separation");
return false;
}
true
};
let mut wall_source_pairs: Vec<(usize, u64)> = Vec::new();
if body == Body::Shell && (distance < 0.0 || !inward_extended_walls.is_empty()) {
for index in 0..carriers.len() {
if !matches!(carriers[index].kind, OffsetFaceRole::Wall) {
continue;
}
if distance > 0.0 && !inward_extended_walls.contains(&index) {
continue;
}
let opening = source_by_id[&carriers[index].source_face_id];
for retained_id in &retained {
if source_faces_adjacent(opening, source_by_id[retained_id]) {
wall_source_pairs.push((index, *retained_id));
}
}
}
}
let one_call = one_call_imprint_enabled();
let mut one_call_done = false;
if one_call {
let mut members: Vec<(u8, &BrepSolid)> = carriers.iter().enumerate().map(|(index, carrier)| (index as u8, &carrier.solid)).collect();
let mut source_members: Vec<(u64, BrepSolid)> = Vec::new();
for (_, retained_id) in &wall_source_pairs {
if !source_members.iter().any(|(id, _)| id == retained_id) {
source_members.push((*retained_id, standalone_face(source, *retained_id)?));
}
}
for (_, solid) in &source_members {
members.push((SOURCE_OPERAND, solid));
}
let composite = compose_imprint_operands(&members);
let mut admitted: HashSet<(usize, usize)> = HashSet::default();
for first in 0..carriers.len() {
for second in first + 1..carriers.len() {
if pair_admitted(first, second) {
admitted.insert((composite.face_index[first], composite.face_index[second]));
}
}
}
for (index, retained_id) in &wall_source_pairs {
let member = carriers.len() + source_members.iter().position(|(id, _)| id == retained_id).unwrap();
let (a, b) = (composite.face_index[*index], composite.face_index[member]);
admitted.insert((a.min(b), a.max(b)));
}
split_lap(0, &mut split_clock);
match crate::imprint::build_carrier_imprints_within(
&composite.solid,
&|first, second| admitted.contains(&(first, second)),
&ImprintOptions {
tolerance,
maximum_fit_points: 96,
local_fit: false,
fit_chunk_points: None,
maximum_ssi_step: Some((scale * 0.0005).max(tolerance * 100.0)),
span_budget: None,
hermite_sections: false,
tangent_curve_lane: false,
exact_iso_pcurves: offset_exact_iso_pcurves_on(),
march_starts: false,
two_radius_junctions: false,
tangent_curve_imprint: false,
},
!shielded_cusps.is_empty(),
) {
Ok(record) => {
split_lap(1, &mut split_clock);
imprint = translate_composite_imprint(record, &composite, tolerance, scale)?;
next_piece_id = imprint.pieces.iter().map(|piece| piece.id).max().unwrap_or(0) + 1;
next_vertex_id = imprint.vertices.iter().map(|vertex| vertex.id).max().unwrap_or(0) + 1;
one_call_done = true;
ONE_CALL_RUNS.with(|count| count.set(count.get() + 1));
split_lap(2, &mut split_clock);
os_debug!(
"one-call imprint: {} members, {} admitted pairs, {} pieces",
members.len(),
admitted.len(),
imprint.pieces.len(),
);
}
Err(error) => {
os_debug!("one-call imprint failed, per-pair path: {error}");
}
}
}
for first in 0..carriers.len() {
for second in first + 1..carriers.len() {
if one_call_done {
break;
}
if !pair_admitted(first, second) {
continue;
}
split_lap(0, &mut split_clock);
let pair = match crate::imprint::build_carrier_imprints(
&carriers[first].solid,
&carriers[second].solid,
&ImprintOptions {
tolerance,
maximum_fit_points: 96,
local_fit: false,
fit_chunk_points: None,
maximum_ssi_step: Some((scale * 0.0005).max(tolerance * 100.0)),
span_budget: None,
hermite_sections: false,
tangent_curve_lane: false,
exact_iso_pcurves: offset_exact_iso_pcurves_on(),
march_starts: false,
two_radius_junctions: false,
tangent_curve_imprint: false,
},
!shielded_cusps.is_empty(),
) {
Ok(pair) => pair,
Err(error) => {
os_debug!("pair ({first},{second}) imprint failed: {error}");
continue;
}
};
split_lap(1, &mut split_clock);
let pieces_before = imprint.pieces.len();
merge_pair_imprint(
&mut imprint,
pair,
first as u8,
second as u8,
&mut next_piece_id,
&mut next_vertex_id,
tolerance,
scale,
);
split_lap(2, &mut split_clock);
os_debug!(
"pair ({first},{second}) src=({},{}) pieces+={}",
carriers[first].source_face_id,
carriers[second].source_face_id,
imprint.pieces.len() - pieces_before,
);
}
}
if body == Body::Shell && (distance < 0.0 || !inward_extended_walls.is_empty()) {
for index in 0..carriers.len() {
if one_call_done {
break;
}
if !matches!(carriers[index].kind, OffsetFaceRole::Wall) {
continue;
}
if distance > 0.0 && !inward_extended_walls.contains(&index) {
continue;
}
let opening = source_by_id[&carriers[index].source_face_id];
for retained_id in &retained {
let retained_face = source_by_id[retained_id];
if !source_faces_adjacent(opening, retained_face) {
continue;
}
split_lap(0, &mut split_clock);
let neighbor = standalone_face(source, *retained_id)?;
split_lap(3, &mut split_clock);
let pair = match build_imprints(
&carriers[index].solid,
&neighbor,
&ImprintOptions {
tolerance,
maximum_fit_points: 96,
local_fit: false,
fit_chunk_points: None,
maximum_ssi_step: Some((scale * 0.0005).max(tolerance * 100.0)),
span_budget: None,
hermite_sections: false,
tangent_curve_lane: false,
exact_iso_pcurves: offset_exact_iso_pcurves_on(),
march_starts: false,
two_radius_junctions: false,
tangent_curve_imprint: false,
},
) {
Ok(pair) => pair,
Err(error) => {
os_debug!(
"wall×source pair ({index},{retained_id}) imprint failed: {error}"
);
continue;
}
};
split_lap(4, &mut split_clock);
let pieces_before = imprint.pieces.len();
merge_pair_imprint(
&mut imprint,
pair,
index as u8,
SOURCE_OPERAND,
&mut next_piece_id,
&mut next_vertex_id,
tolerance,
scale,
);
split_lap(5, &mut split_clock);
os_debug!(
"wall×source pair ({index},{retained_id}) pieces+={}",
imprint.pieces.len() - pieces_before,
);
}
}
}
for (cone_index, cap_index) in &apex_cap_pairs {
let cap_face_id = carriers[*cap_index].solid.shells[0].faces[0].id;
let piece_ids = imprint
.by_face
.iter()
.find(|entry| entry.operand == *cap_index as u8 && entry.face_id == cap_face_id)
.map(|entry| entry.piece_ids.clone())
.unwrap_or_default();
let coincidence_band = 2e-5f64.max(scale * 1e-7);
for piece_id in piece_ids {
let Some((curve, t0, t1)) = imprint
.pieces
.iter()
.find(|piece| piece.id == piece_id)
.map(|piece| (piece.curve.clone(), piece.t0, piece.t1))
else {
continue;
};
for endpoint in [curve.evaluate(t0).or_refuse(KernelStage::Sew, "evaluate")?, curve.evaluate(t1).or_refuse(KernelStage::Sew, "evaluate")?] {
for edge in &carriers[*cone_index].solid.edges {
if edge.degenerate {
continue;
}
let projection = crate::project_point_to_curve(&edge.curve, endpoint).or_refuse(KernelStage::Sew, "project_point_to_curve")?;
if projection.distance > coincidence_band
|| projection.u < edge.t0 + 1e-9
|| projection.u > edge.t1 - 1e-9
{
continue;
}
let parameter = projection.u;
if let Some(existing) = imprint.edge_splits.iter_mut().find(|split| {
split.operand == *cone_index as u8 && split.edge_id == edge.id
}) {
if !existing
.parameters
.iter()
.any(|value| (*value - parameter).abs() <= 1e-8)
{
existing.parameters.push(parameter);
}
} else {
imprint.edge_splits.push(EdgeSplitRecord {
operand: *cone_index as u8,
edge_id: edge.id,
parameters: vec![parameter],
});
}
os_debug!(
"apex-cap ring split: cone carrier {cone_index} edge {} at t={parameter:.6}",
edge.id,
);
}
}
}
}
if debug_enabled() {
for piece in &imprint.pieces {
let start = piece.curve.evaluate(piece.t0);
let mid = piece.curve.evaluate((piece.t0 + piece.t1) * 0.5);
let end = piece.curve.evaluate(piece.t1);
if let (Ok(start), Ok(mid), Ok(end)) = (start, mid, end) {
os_debug!(
"piece[{}] ({:.4},{:.4},{:.4})..({:.4},{:.4},{:.4})..({:.4},{:.4},{:.4}) on {:?}",
piece.id,
start.x,
start.y,
start.z,
mid.x,
mid.y,
mid.z,
end.x,
end.y,
end.z,
piece.support_faces,
);
for pcurve in &piece.pcurves {
let uv = |t: f64| pcurve.pcurve.evaluate(t).map(|p| (p.x, p.y)).unwrap_or((f64::NAN, f64::NAN));
let [a, b] = pcurve.pcurve.domain().unwrap_or([0.0, 1.0]);
os_debug!(
" pcurve operand={} face={} uv {:?}..{:?}..{:?}",
pcurve.operand, pcurve.face_id, uv(a), uv(0.5 * (a + b)), uv(b),
);
}
}
}
}
split_lap(6, &mut split_clock);
synchronize_smooth_edge_splits(&mut imprint, &smooth.edge_pairs);
split_lap(7, &mut split_clock);
let boundary_coincidence_tolerance = 2e-5f64.max(scale * 1e-7);
let piece_geometry = imprint
.pieces
.iter()
.map(|piece| {
(
piece.id,
FragmentEdgeGeometry {
curve: piece.curve.clone(),
t0: piece.t0,
t1: piece.t1,
},
)
})
.collect::<HashMap<_, _>>();
for (index, carrier) in carriers.iter().enumerate() {
if !matches!(carrier.kind, OffsetFaceRole::Offset) {
continue;
}
let boundaries = carrier
.solid
.edges
.iter()
.map(|edge| FragmentEdgeGeometry {
curve: edge.curve.clone(),
t0: edge.t0,
t1: edge.t1,
})
.collect::<Vec<_>>();
if let Some(by_face) = imprint
.by_face
.iter_mut()
.find(|entry| entry.operand == index as u8)
{
let before = by_face.piece_ids.clone();
by_face.piece_ids.retain(|piece_id| {
let piece = &piece_geometry[piece_id];
!boundaries.iter().any(|boundary| {
fragment_edge_lies_on(piece, boundary, boundary_coincidence_tolerance)
.unwrap_or(false)
})
});
let candidates = by_face.piece_ids.clone();
by_face.piece_ids.retain(|id| {
let edge = &piece_geometry[id];
if edge.curve.degree != 1 || edge.curve.control_points.len() != 2 {
return true;
}
!candidates.iter().any(|other_id| {
if id == other_id { return false; }
let other = &piece_geometry[other_id];
if other.curve.degree != 1 || other.curve.control_points.len() != 2 {
return false;
}
fragment_edge_lies_on(edge, other, tolerance).unwrap_or(false)
&& (other_id < id
|| !fragment_edge_lies_on(other, edge, tolerance).unwrap_or(false))
})
});
if debug_enabled() && before.len() != by_face.piece_ids.len() {
os_debug!(
"carrier[{index}] dropped boundary-coincident pieces: {:?} -> {:?}",
before,
by_face.piece_ids,
);
}
}
}
if debug_enabled() {
for entry in &imprint.by_face {
os_debug!(
"by_face operand={} face={} pieces={:?}",
entry.operand,
entry.face_id,
entry.piece_ids,
);
}
}
split_lap(8, &mut split_clock);
if split_profile {
profile_line!(
"offset_shell.imprint_split skips={:.3} pair_build={:.3} pair_merge={:.3} standalone={:.3} wall_build={:.3} wall_merge={:.3} ring={:.3} smooth={:.3} filter={:.3}",
split_ms[0], split_ms[1], split_ms[2], split_ms[3], split_ms[4], split_ms[5], split_ms[6], split_ms[7], split_ms[8],
);
}
profile::lap(&mut clock, profile::IMPRINT);
let mut split_carriers = Vec::new();
let mut fragments_by_carrier = Vec::new();
for (index, carrier) in carriers.iter().enumerate() {
let split = if matches!(carrier.kind, OffsetFaceRole::Offset)
&& !apex_cap_sources.contains(&index)
{
carrier.solid.clone()
} else {
apply_edge_splits(&carrier.solid, index as u8, &imprint)?
};
let fragment_clock = web_time::Instant::now();
let fragments = fragment_solid(&split, index as u8, &imprint)?;
os_debug!(
"carrier[{index}] kind={:?} src={} fragments={} ({:.3} ms)",
carrier.kind,
carrier.source_face_id,
fragments.len(),
fragment_clock.elapsed().as_secs_f64() * 1e3,
);
if debug_enabled() {
for (fragment_index, fragment) in fragments.iter().enumerate() {
let mut boundary_count = 0usize;
let mut imprint_pieces = Vec::new();
let mut derived_count = 0usize;
for coedge in fragment
.loops
.iter()
.flat_map(|loop_record| &loop_record.coedges)
{
match &coedge.source {
FragmentEdgeSource::Boundary { .. }
| FragmentEdgeSource::SharedBoundary { .. } => boundary_count += 1,
FragmentEdgeSource::Imprint { piece_id } => imprint_pieces.push(*piece_id),
FragmentEdgeSource::Derived { .. } => derived_count += 1,
}
}
os_debug!(
" frag[{index}.{fragment_index}] test=({:.3},{:.3},{:.3}) uv=({:.3},{:.3}) boundary={boundary_count} derived={derived_count} pieces={imprint_pieces:?}",
fragment.test_point.x, fragment.test_point.y, fragment.test_point.z,
fragment.test_uv.x, fragment.test_uv.y,
);
}
}
split_carriers.push(split);
fragments_by_carrier.push(fragments);
}
profile::lap(&mut clock, profile::FRAGMENT);
let mut assembly_sources = [(SOURCE_OPERAND, source)]
.into_iter()
.collect::<HashMap<_, _>>();
for (index, carrier) in split_carriers.iter().enumerate() {
assembly_sources.insert(index as u8, carrier);
}
let source_edge_by_id = source
.edges
.iter()
.map(|edge| (edge.id, edge))
.collect::<HashMap<_, _>>();
let retained_faces_with_edges = retained
.iter()
.map(|face_id| {
let face = source_by_id[face_id];
let edges = face
.loops
.iter()
.flat_map(|loop_record| &loop_record.coedges)
.filter_map(|coedge| source_edge_by_id.get(&coedge.edge_id).copied())
.collect::<Vec<_>>();
(face, edges)
})
.collect::<Vec<_>>();
let mut offset_skin_tolerance = offset_skin_band(scale, distance);
if thin_skin_pad_enabled() && distance > 0.0 {
offset_skin_tolerance = offset_skin_tolerance.min(0.25 * distance.abs());
}
let mut opening_planes: Vec<(usize, Vec3, Vec3)> = Vec::new();
let mut opening_ruled: Vec<(usize, f64)> = Vec::new();
if distance < 0.0 {
for (index, carrier) in carriers.iter().enumerate() {
if !matches!(carrier.kind, OffsetFaceRole::Wall) {
continue;
}
let opening = source_by_id[&carrier.source_face_id];
let [u0, u1] = opening.surface.domain_u().or_refuse(KernelStage::Sew, "domain_u")?;
let [v0, v1] = opening.surface.domain_v().or_refuse(KernelStage::Sew, "domain_v")?;
let (u, v) = ((u0 + u1) * 0.5, (v0 + v1) * 0.5);
if ruled_walls.contains(&index) {
let wall = &split_carriers[index].shells[0].faces[0];
let probe = project_point_to_surface(&wall.surface, opening.surface.evaluate(u, v).or_refuse(KernelStage::Sew, "evaluate")?).or_refuse(KernelStage::Sew, "project_point_to_surface")?;
let side = face_normal(wall, probe.u, probe.v)?.dot(face_normal(opening, u, v)?);
opening_ruled.push((index, if side < 0.0 { -1.0 } else { 1.0 }));
continue;
}
if !opening.surface.is_affine().or_refuse(KernelStage::Sew, "is_affine")? {
continue;
}
opening_planes.push((
index,
opening.surface.evaluate(u, v).or_refuse(KernelStage::Sew, "evaluate")?,
face_normal(opening, u, v)?,
));
}
}
let in_front_of_other_opening = |wall_index: usize, point: Vec3| -> Result<bool, KernelRefusal> {
let band = 2e-3f64.max(scale * 5e-5);
for (index, plane_point, outward) in &opening_planes {
if *index == wall_index || point.sub(*plane_point).dot(*outward) <= band {
continue;
}
let face = &split_carriers[*index].shells[0].faces[0];
let projection = project_point_to_surface(&face.surface, point).or_refuse(KernelStage::Sew, "project_point_to_surface")?;
let uv = Vec2 {
x: projection.u,
y: projection.v,
};
if parameter_point_in_face(face, uv, 1e-8).or_refuse(KernelStage::Sew, "parameter_point_in_face")? != PolygonClass::Outside {
return Ok(true);
}
}
for (index, side) in &opening_ruled {
if *index == wall_index {
continue;
}
let face = &split_carriers[*index].shells[0].faces[0];
let projection = project_point_to_surface(&face.surface, point).or_refuse(KernelStage::Sew, "project_point_to_surface")?;
let outward = face_normal(face, projection.u, projection.v)?.scale(*side);
if point.sub(projection.point).dot(outward) <= band {
continue;
}
let uv = Vec2 {
x: projection.u,
y: projection.v,
};
if parameter_point_in_face(face, uv, 1e-8).or_refuse(KernelStage::Sew, "parameter_point_in_face")? != PolygonClass::Outside {
return Ok(true);
}
}
Ok(false)
};
let mut chosen_offsets = Vec::new();
let mut chosen_walls = Vec::new();
let mut newly_shadowed = HashSet::<u64>::default();
for (index, carrier) in carriers.iter().enumerate() {
let raw_seeds = source_seeds(source, carrier.source_face_id)?;
let mut seeds = Vec::with_capacity(raw_seeds.len());
for raw_seed in raw_seeds {
let seed = if matches!(carrier.kind, OffsetFaceRole::Offset) {
offset_seed(
source_by_id[&carrier.source_face_id],
raw_seed,
&split_carriers[index].shells[0].faces[0].surface,
distance,
2e-3f64.max(scale * 5e-5),
)?
.unwrap_or(raw_seed)
} else if inward_extended_walls.contains(&index) {
offset_seed(
source_by_id[&carrier.source_face_id],
raw_seed,
&split_carriers[index].shells[0].faces[0].surface,
0.0,
2e-3f64.max(scale * 5e-5),
)?
.unwrap_or(raw_seed)
} else {
raw_seed
};
if seed.sub(raw_seed).length() > 1e-9 {
os_debug!(
" seed[{index}] moved from ({:.4},{:.4}) to the offset image ({:.4},{:.4})",
raw_seed.x,
raw_seed.y,
seed.x,
seed.y
);
}
seeds.push(seed);
}
let seed = seeds[0];
if matches!(carrier.kind, OffsetFaceRole::Offset) {
let mut readings = Vec::with_capacity(fragments_by_carrier[index].len());
let cusp_neighbors = if shielded_cusps.is_empty() {
Vec::new()
} else {
retained_faces_with_edges.iter()
.filter(|(face, _)| face.id != carrier.source_face_id)
.cloned().collect::<Vec<_>>()
};
for fragment in &fragments_by_carrier[index] {
let reading = offset_skin_reading(
fragment.test_point,
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
);
let reading = if matches!(&reading, SkinReading::On) && !cusp_neighbors.is_empty() {
offset_skin_reading(
fragment.test_point,
&cusp_neighbors,
distance,
4.0 * KernelTolerances::for_solid(source, 1e-7).intersection_fit,
)
} else {
reading
};
let reading = match reading {
SkinReading::On => match coincident_offset_face(
fragment.test_point,
carrier.source_face_id,
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
)? {
Some((face, separation)) if separation <= distance.abs() + tolerance * 10.0 => {
if tangent_touch_on() {
if let Some((farther, far)) = tangent_touch(
fragment,
face,
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
)? {
return Err(KernelRefusal::unsupported(KernelStage::Select, refusals::TANGENT_OFFSETS, format!(
"offset_shell: the offsets of source faces {} and {face} touch tangentially near \
({:.4}, {:.4}, {:.4}) — the material between those faces is exactly twice the \
shell distance thick along that touch (separation {separation:.6} against {:.6}) \
and thicker beside it (the offset reads {far:.4} from face {face} at ({:.4}, {:.4}, {:.4})), so the cavity would \
pinch to a line there; a cavity pinched to a line is not supported — use a \
shell distance a little below {:.4}",
carrier.source_face_id,
fragment.test_point.x,
fragment.test_point.y,
fragment.test_point.z,
distance.abs(),
farther.x,
farther.y,
farther.z,
distance.abs(),
)));
}
}
SkinReading::Shadowed { face, separation }
}
_ => SkinReading::On,
},
other => other,
};
let reading = match reading {
SkinReading::Shadowed { .. } if cavity_bounds.contains(&carrier.source_face_id) => SkinReading::On,
other => other,
};
readings.push(reading);
}
for (fragment, reading) in fragments_by_carrier[index].iter().zip(&readings) {
if let SkinReading::Unmeasured { face, reason } = reading {
return Err(unmeasured_skin_refusal(fragment.test_point, *face, reason));
}
}
let adrift = fragments_by_carrier[index]
.iter()
.map(|fragment| {
point_adrift_of_its_own_offset(
fragment.test_point,
carrier.source_face_id,
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
)
})
.collect::<Vec<_>>();
let mut skinless = readings
.iter()
.any(|reading| matches!(reading, SkinReading::Shadowed { .. }))
|| adrift.iter().any(|flag| *flag);
for (fragment, reading) in fragments_by_carrier[index].iter().zip(&readings) {
if !skinless {
break;
}
if matches!(reading, SkinReading::On)
&& !adrift[fragments_by_carrier[index]
.iter()
.position(|other| std::ptr::eq(other, fragment))
.unwrap_or(0)]
{
let class = classify_point(fragment.test_point, source, tolerance * 10.0).or_refuse(KernelStage::Sew, "classify_point")?.class;
skinless = (distance > 0.0 && class == PointClass::Out)
|| (distance < 0.0 && class == PointClass::In);
}
}
if skinless && !apex_cap_sources.contains(&index) {
if body == Body::Shell && distance > 0.0 && cascade_omission_enabled() {
let held = fragments_holding_true_skin(
source_by_id[&carrier.source_face_id],
source,
&fragments_by_carrier[index],
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
)?;
if !held.is_empty() {
os_debug!(" select[{index}] src={}: {} fragment(s) hold true skin", carrier.source_face_id, held.len());
chosen_offsets.extend(held);
continue;
}
}
if !shadowed_supports.is_empty() && !(body == Body::Shell && distance > 0.0 && cascade_omission_enabled()) {
let mut omitted = shadowed_supports.iter().copied().collect::<Vec<_>>();
omitted.sort_unstable();
return Err(KernelRefusal::unsupported(KernelStage::Select, "offset_shell_shadow_cascade", format!(
"offset_shell: omitting the shadowed offsets of source faces {omitted:?} \
leaves the offset of source face {} shadowed at every one of its {} \
fragment test points, which no longer stand for their fragments once \
those carriers stop cutting it",
carrier.source_face_id,
readings.len()
)));
}
os_debug!(
" select[{index}] src={} SHADOWED over all {} fragment(s): {:?}",
carrier.source_face_id,
readings.len(),
readings
);
newly_shadowed.insert(carrier.source_face_id);
continue;
}
for (fragment, reading) in fragments_by_carrier[index].iter().zip(&readings) {
if shadowed_points.len() >= MAXIMUM_CROSSING_READS {
break;
}
if matches!(reading, SkinReading::Shadowed { .. }) {
shadowed_points.push((fragment.test_point, carrier.source_face_id));
}
}
let mut on_skin = readings
.iter()
.zip(&adrift)
.map(|(reading, adrift)| matches!(reading, SkinReading::On) && !adrift)
.collect::<Vec<_>>();
if open_section_reach {
if let Some(ends) = open_profile_end_neighbours(index, &carriers, &source_faces)? {
for (keep, fragment) in on_skin.iter_mut().zip(&fragments_by_carrier[index]) {
*keep = !outside_continued_neighbours(fragment.test_point, &ends, &carriers, tolerance)?;
}
os_debug!(" select[{index}] open profile kept {} central regions", on_skin.iter().filter(|keep| **keep).count());
}
}
let skin_filter_active = on_skin.iter().any(|flag| *flag)
|| apex_cap_pairs.iter().any(|(_, cap)| *cap == index);
let opening_limits = if inward_extended_walls.is_empty() {
Vec::new()
} else {
let openings = inward_extended_walls
.iter()
.map(|index| source_by_id[&carriers[*index].source_face_id])
.collect::<Vec<_>>();
opening_trim_halfspaces(source_by_id[&carrier.source_face_id], &openings, tolerance)?
};
let mut classified = Vec::new();
let mut viable = Vec::new();
for (fragment_index, fragment) in fragments_by_carrier[index].iter().enumerate() {
let class = classify_point(fragment.test_point, source, tolerance * 10.0).or_refuse(KernelStage::Sew, "classify_point")?.class;
let excluded_by_class = (distance > 0.0 && class == PointClass::Out)
|| (distance < 0.0 && class == PointClass::In);
let contact = if excluded_by_class {
false
} else {
distance > 0.0
&& fragment_has_sustained_source_contact(
fragment,
&source_faces,
&assembly_sources,
&imprint,
2e-3f64.max(scale * 5e-5),
)?
};
os_debug!(
" select[{index}.{fragment_index}] class={class:?} contact={contact} on_skin={} seed_in={:?} uv=({:.3},{:.3})",
on_skin[fragment_index],
parameter_point_in_face(&fragment_as_trim(fragment), seed, 1e-8),
fragment.test_uv.x, fragment.test_uv.y,
);
let past_opening = opening_limits.iter().any(|(origin, normal)| {
fragment.test_point.sub(*origin).dot(*normal) < -tolerance * 10.0
});
if excluded_by_class
|| past_opening
|| (skin_filter_active && !on_skin[fragment_index])
{
continue;
}
classified.push(fragment.clone());
if contact {
continue;
}
viable.push(fragment.clone());
}
let mut region_skins = Vec::new();
let continued_neighbours = if distance > 0.0 {
continued_sphere_neighbours(index, &carriers, &source_faces, scale)?
} else { None };
if let Some(neighbours) = &continued_neighbours {
for fragment in &classified {
if outside_continued_neighbours(fragment.test_point, neighbours, &carriers, tolerance)? {
region_skins.push(fragment.clone());
}
}
os_debug!(" select[{index}] continued sphere kept {} regions", region_skins.len());
}
for &seed in seeds.iter().filter(|_| continued_neighbours.is_none()) {
os_debug!(" select[{index}] seed=({:.4},{:.4})", seed.x, seed.y);
let seeded = viable
.iter()
.filter(|fragment| {
parameter_point_in_face(&fragment_as_trim(fragment), seed, 1e-8)
.is_ok_and(|class| class != PolygonClass::Outside)
})
.cloned()
.collect::<Vec<_>>();
let seeded_classified = classified
.iter()
.filter(|fragment| {
parameter_point_in_face(&fragment_as_trim(fragment), seed, 1e-8)
.is_ok_and(|class| class != PolygonClass::Outside)
})
.cloned()
.collect::<Vec<_>>();
let selected = if viable.is_empty() {
if !skin_filter_active {
None
} else {
let mut by_area = classified
.iter()
.cloned()
.map(|fragment| {
let area = parameter_space_area(&fragment_as_trim(&fragment)).or_refuse(KernelStage::Sew, "parameter_space_area")?.abs();
Ok((fragment, area))
})
.collect::<Result<Vec<_>, KernelRefusal>>()?;
by_area.sort_by(|a, b| b.1.total_cmp(&a.1));
by_area.into_iter().next().map(|entry| entry.0)
}
} else if !seeded_classified.is_empty() {
let mut candidates = seeded_classified;
candidates.sort_by(|a, b| {
a.test_uv
.sub(seed)
.length()
.total_cmp(&b.test_uv.sub(seed).length())
});
candidates.into_iter().next()
} else if viable.len() > 2 {
let mut by_area = viable
.iter()
.cloned()
.map(|fragment| {
let area = parameter_space_area(&fragment_as_trim(&fragment)).or_refuse(KernelStage::Sew, "parameter_space_area")?.abs();
Ok((fragment, area))
})
.collect::<Result<Vec<_>, KernelRefusal>>()?;
by_area.sort_by(|a, b| b.1.total_cmp(&a.1));
by_area.into_iter().next().map(|entry| entry.0)
} else {
let mut candidates = if seeded.is_empty() { viable.clone() } else { seeded };
candidates.sort_by(|a, b| {
a.test_uv
.sub(seed)
.length()
.total_cmp(&b.test_uv.sub(seed).length())
});
candidates.into_iter().next()
};
let mut drop_debris = false;
if let Some(fragment) = &selected {
os_debug!(
" select[{index}] src={} chosen test point departs its own face's offset \
distance by {:?}",
carrier.source_face_id,
own_offset_departure(
fragment.test_point,
carrier.source_face_id,
&retained_faces_with_edges,
distance
)
);
os_debug!(
" select[{index}] src={} chosen test point departs in the MITER metric by {:?}",
carrier.source_face_id,
own_miter_departure(
fragment.test_point,
carrier.source_face_id,
&retained_faces_with_edges,
distance
)
);
let coincidence = coincident_offset_face(
fragment.test_point,
carrier.source_face_id,
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
)?;
let coincidence = match coincidence {
Some((other, separation))
if coincide_debris_on()
&& fragment_is_band_debris(fragment, offset_skin_tolerance)? =>
{
os_debug!(
" select[{index}] src={} drops corner debris coincident with {other} \
(separation {separation}) at {:?}",
carrier.source_face_id,
fragment.test_point
);
drop_debris = true;
None
}
reading => reading,
};
if let Some((other, separation)) = coincidence {
return Err(KernelRefusal::unsupported(KernelStage::Select, "offset_shell_coincident_offsets", format!(
"offset_shell: the offsets of source faces {} and {other} coincide near \
({:.4}, {:.4}, {:.4}) — the material between those faces is twice the \
shell distance thick (separation {separation:.6} against {:.6}, band \
{offset_skin_tolerance:.1e}), so the cavity between them has no \
thickness there; a cavity pinched to zero thickness is not supported",
carrier.source_face_id,
fragment.test_point.x,
fragment.test_point.y,
fragment.test_point.z,
distance.abs(),
)));
}
}
let selected = if drop_debris { None } else { selected };
if let Some(fragment) = selected {
if !region_skins
.iter()
.any(|other: &FaceFragmentRecord| {
other.test_point.sub(fragment.test_point).length() <= 1e-12
})
{
region_skins.push(fragment);
}
}
}
if every_skin_piece_enabled()
&& distance > 0.0
&& (viable.is_empty() || every_seeded_piece)
&& skin_filter_active
&& inward_extended_walls.is_empty()
&& classified.len() > region_skins.len()
{
for fragment in &classified {
if region_skins.iter().any(|other: &FaceFragmentRecord| other.test_point.sub(fragment.test_point).length() <= 1e-12) {
continue;
}
let departure = own_offset_departure(fragment.test_point, carrier.source_face_id, &retained_faces_with_edges, distance);
os_debug!(" select[{index}] every skin piece: test point ({:.4},{:.4},{:.4}) departs its own face's offset by {departure:?}", fragment.test_point.x, fragment.test_point.y, fragment.test_point.z);
if departure.is_some_and(|departure| departure.abs() <= offset_skin_tolerance) {
region_skins.push(fragment.clone());
}
}
}
if !inward_extended_walls.is_empty() && skin_filter_active {
region_skins = classified;
}
chosen_offsets.extend(region_skins);
} else if body == Body::Whole {
for (fragment_index, fragment) in fragments_by_carrier[index].iter().enumerate() {
if seeds.iter().any(|seed| {
parameter_point_in_face(&fragment_as_trim(fragment), *seed, 1e-8)
.is_ok_and(|class| class != PolygonClass::Outside)
}) {
os_debug!(" held[{index}.{fragment_index}] kept: contains the face's seed");
chosen_walls.push(fragment.clone());
}
}
} else {
let opening = source_by_id[&carrier.source_face_id];
let wall_seeds = opening_wall_seeds(
opening,
&split_carriers[index].shells[0].faces[0],
source,
&opening_set,
distance,
)?;
let carrier_planar =
surface_is_planar(&split_carriers[index].shells[0].faces[0].surface, tolerance)
.unwrap_or(false);
let extended_inward = inward_extended_walls.contains(&index);
let outside_source = |point| -> Result<bool, KernelRefusal> {
Ok(extended_inward
&& classify_point(point, source, tolerance * 10.0)
.or_refuse(KernelStage::Select, "classify_point")?.class == PointClass::Out)
};
let wall_on_skin = |point| {
if extended_inward {
let inside_source = classify_point(point, source, tolerance * 10.0)
.or_refuse(KernelStage::Select, "classify_point")?.class == PointClass::In;
point_on_inward_opening_skin(
point,
&retained_faces_with_edges,
&reflex_wall_neighbors[&index],
inside_source,
distance,
offset_skin_tolerance,
)
} else {
point_on_offset_skin(
point,
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
)
}
};
let mut selected_indices = Vec::new();
for (fragment_index, fragment) in fragments_by_carrier[index].iter().enumerate() {
if outside_source(fragment.test_point)? {
continue;
}
if parameter_point_in_face(&fragment_as_trim(fragment), seed, 1e-8).or_refuse(KernelStage::Sew, "parameter_point_in_face")?
== PolygonClass::Outside
{
if in_front_of_other_opening(index, fragment.test_point)? {
os_debug!(
" wall[{index}.{fragment_index}] skipped: in front of another \
opening at ({:.3},{:.3},{:.3})",
fragment.test_point.x,
fragment.test_point.y,
fragment.test_point.z,
);
continue;
}
if wall_on_skin(fragment.test_point)? && !(carrier_planar
&& !extended_inward
&& wall_seeds.iter().any(|wall_seed| {
parameter_point_in_face(&fragment_as_trim(fragment), *wall_seed, 1e-8)
.is_ok_and(|class| class != PolygonClass::Outside)
}))
{
os_debug!(
" wall[{index}.{fragment_index}] skipped: void cross-section at \
({:.3},{:.3},{:.3})",
fragment.test_point.x,
fragment.test_point.y,
fragment.test_point.z,
);
continue;
}
if distance < 0.0
&& wall_surplus_beyond_the_sections(fragment, index as u8, |piece_id| {
imprint.pieces.iter().find(|piece| piece.id == piece_id).is_some_and(|piece| {
piece.support_faces.iter().any(|key| key.operand == SOURCE_OPERAND)
})
})
&& !wall_seeds.iter().any(|wall_seed| {
parameter_point_in_face(&fragment_as_trim(fragment), *wall_seed, 1e-8)
.is_ok_and(|class| class != PolygonClass::Outside)
})
{
os_debug!(
" wall[{index}.{fragment_index}] skipped: surplus beyond the sections at \
({:.3},{:.3},{:.3}), touching the wall outline and no source rim",
fragment.test_point.x,
fragment.test_point.y,
fragment.test_point.z,
);
continue;
}
selected_indices.push(fragment_index);
}
}
for wall_seed in wall_seeds {
let fragments = &fragments_by_carrier[index];
let nearest_containing = fragments
.iter()
.enumerate()
.filter(|(_, fragment)| {
parameter_point_in_face(&fragment_as_trim(fragment), wall_seed, 1e-8)
.is_ok_and(|class| class != PolygonClass::Outside)
})
.min_by(|(_, first), (_, second)| {
first
.test_uv
.sub(wall_seed)
.length()
.total_cmp(&second.test_uv.sub(wall_seed).length())
})
.map(|(fragment_index, _)| fragment_index);
let nearest = if nearest_containing.is_some() {
nearest_containing
} else {
let seed_point = split_carriers[index].shells[0].faces[0]
.surface
.evaluate(wall_seed.x, wall_seed.y).or_refuse(KernelStage::Sew, "evaluate")?;
fragments
.iter()
.enumerate()
.min_by(|(_, first), (_, second)| {
first
.test_point
.sub(seed_point)
.length()
.total_cmp(&second.test_point.sub(seed_point).length())
})
.map(|(fragment_index, _)| fragment_index)
};
if let Some(fragment_index) = nearest {
let candidate = &fragments_by_carrier[index][fragment_index];
if outside_source(candidate.test_point)?
|| in_front_of_other_opening(index, candidate.test_point)?
{
continue;
}
if (nearest_containing.is_none() || !carrier_planar || extended_inward)
&& wall_on_skin(candidate.test_point)?
{
os_debug!(
" wall[{index}.{fragment_index}] seed-hit skipped: void \
cross-section (planar={carrier_planar}) at ({:.3},{:.3},{:.3})",
candidate.test_point.x,
candidate.test_point.y,
candidate.test_point.z,
);
continue;
}
let existing = chosen_offsets.iter().cloned().chain(
selected_indices
.iter()
.map(|selected| fragments_by_carrier[index][*selected].clone()),
);
if !selected_indices.contains(&fragment_index)
&& !would_overuse_existing_boundary(
candidate,
existing,
&assembly_sources,
&imprint,
2e-3,
)?
{
selected_indices.push(fragment_index);
}
}
}
for fragment_index in selected_indices.iter().copied() {
let mut fragment = fragments_by_carrier[index][fragment_index].clone();
if extended_inward
&& classify_point(fragment.test_point, source, tolerance * 10.0)
.or_refuse(KernelStage::Select, "classify_point")?.class == PointClass::In
{
flip_fragment(&mut fragment)?;
}
chosen_walls.push(fragment);
}
}
}
if debug_enabled() {
for fragment in &chosen_offsets {
os_debug!(
"chosen offset: operand={} src={} test=({:.3},{:.3},{:.3})",
fragment.operand,
carriers[fragment.operand as usize].source_face_id,
fragment.test_point.x,
fragment.test_point.y,
fragment.test_point.z,
);
for (loop_index, loop_record) in fragment.loops.iter().enumerate() {
for coedge in &loop_record.coedges {
let surface = &fragment.surface;
let [c0, c1] = coedge.pcurve.domain().unwrap_or([0.0, 0.0]);
let uv0 = coedge.pcurve.evaluate(c0);
let uv1 = coedge.pcurve.evaluate(c1);
if let (Ok(uv0), Ok(uv1)) = (uv0, uv1) {
let p0 = surface.evaluate(uv0.x, uv0.y);
let p1 = surface.evaluate(uv1.x, uv1.y);
if let (Ok(p0), Ok(p1)) = (p0, p1) {
let kind = match &coedge.source {
FragmentEdgeSource::Boundary { edge_id, .. } => {
format!("bnd:{edge_id}")
}
FragmentEdgeSource::SharedBoundary { edge_id, .. } => {
format!("sbnd:{edge_id}")
}
FragmentEdgeSource::Imprint { piece_id } => {
format!("imp:{piece_id}")
}
FragmentEdgeSource::Derived { .. } => "derived".to_string(),
};
os_debug!(
" loop{loop_index} {kind} ({:.4},{:.4},{:.4})..({:.4},{:.4},{:.4})",
p0.x, p0.y, p0.z, p1.x, p1.y, p1.z,
);
}
}
}
}
}
for fragment in &chosen_walls {
os_debug!(
"chosen wall: operand={} src={} test=({:.3},{:.3},{:.3})",
fragment.operand,
carriers[fragment.operand as usize].source_face_id,
fragment.test_point.x,
fragment.test_point.y,
fragment.test_point.z,
);
}
}
if !newly_shadowed.is_empty() {
let mut omitted = shadowed_supports.clone();
omitted.extend(newly_shadowed.iter().copied());
os_debug!(
"re-running without shadowed offset supports {:?} (omitted before: {:?})",
newly_shadowed,
shadowed_supports
);
shadowed_points.clear();
profile::lap(&mut clock, profile::SELECT);
let cascaded = !shadowed_supports.is_empty();
let rebuilt = offset_shell_impl(
source,
opening_face_ids,
distance,
body,
extend_oblique_carriers,
extension_fired,
reflex_rebuilds,
&omitted,
cavity_bounds,
every_seeded_piece,
shadowed_points,
omitted_supports,
vacated_notes,
);
let rebuilt = rebuilt?;
if cascaded {
refuse_a_cascade_that_kept_the_cavity(source, opening_face_ids, distance, &rebuilt.solid)?;
}
return Ok(rebuilt);
}
if chosen_offsets.is_empty() && !shadowed_supports.is_empty() {
if matches!(body, Body::Shell) {
if let Some(witness) = cavity_witness(source, opening_face_ids, distance)? {
if !witness.bounds.is_subset(cavity_bounds) {
let mut bounds = cavity_bounds.clone();
bounds.extend(witness.bounds.iter().copied());
os_debug!(
"every skin read shadowed, but ({:.4}, {:.4}, {:.4}) lies {:.6} from every retained face: re-running with faces {:?} kept",
witness.point.x, witness.point.y, witness.point.z, witness.clearance, bounds
);
shadowed_points.clear();
profile::lap(&mut clock, profile::SELECT);
return offset_shell_impl(
source,
opening_face_ids,
distance,
body,
extend_oblique_carriers,
extension_fired,
reflex_rebuilds,
&HashSet::default(),
&bounds,
every_seeded_piece,
shadowed_points,
omitted_supports,
vacated_notes,
);
}
return Err(KernelRefusal::unsupported(KernelStage::Select, "offset_shell_cavity_unbuilt", format!(
"offset_shell: a cavity remains at this distance (the point ({:.4}, {:.4}, {:.4}) is {:.4} from \
every retained face, past the shell distance {distance}), but every offset skin around it \
reads as covered by another face, so it could not be built; returning the solid unchanged \
would be wrong — use a smaller shell distance",
witness.point.x, witness.point.y, witness.point.z, witness.clearance
)));
}
if let Some(unchanged) = empty_cavity_shell(source, opening_face_ids, distance)? {
return Ok(unchanged);
}
}
let mut omitted = shadowed_supports.iter().copied().collect::<Vec<_>>();
omitted.sort_unstable();
return Err(KernelRefusal::unsupported(KernelStage::Select, "offset_shell_all_shadowed", format!(
"offset_shell: no retained face keeps any offset skin once the shadowed offsets of \
source faces {omitted:?} are omitted — the solid is nowhere thicker than twice the \
shell distance, so there is no cavity to shell"
)));
}
if chosen_offsets.is_empty() {
return Err(KernelRefusal::internal(KernelStage::Select, "offset_shell_no_boundary", "offset_shell: carrier intersections produced no offset boundary"));
}
let mut boundary_use_count = HashMap::<[i64; 9], usize>::default();
for fragment in &chosen_offsets {
for coedge in fragment
.loops
.iter()
.flat_map(|loop_record| &loop_record.coedges)
{
let key = fragment_edge_segment_key(&coedge.source, &assembly_sources, &imprint)?;
*boundary_use_count.entry(key).or_default() += 1;
}
}
let mut manifold_walls = Vec::new();
for fragment in chosen_walls {
let keys = fragment
.loops
.iter()
.flat_map(|loop_record| &loop_record.coedges)
.map(|coedge| fragment_edge_segment_key(&coedge.source, &assembly_sources, &imprint))
.collect::<Result<Vec<_>, _>>()?;
if !keys
.iter()
.any(|key| boundary_use_count.get(key).copied().unwrap_or(0) >= 2)
{
for key in keys {
*boundary_use_count.entry(key).or_default() += 1;
}
manifold_walls.push(fragment);
}
}
os_debug!(
"manifold walls kept: {} operands={:?}",
manifold_walls.len(),
manifold_walls
.iter()
.map(|fragment| fragment.operand)
.collect::<Vec<_>>(),
);
let chosen_walls = manifold_walls;
let sealed = if sealed_hollow_on() && body == Body::Shell {
sealed_skins(&chosen_offsets, &chosen_walls, &assembly_sources, &imprint, |fragment| {
carriers[fragment.operand as usize].source_face_id
})?
} else {
Vec::new()
};
if !sealed.is_empty() {
os_debug!(
"sealed skins: {:?}",
sealed.iter().map(|skin| (&skin.sources, skin.centre, skin.edge_middles.len())).collect::<Vec<_>>()
);
}
profile::lap(&mut clock, profile::SELECT);
let stage_clock = web_time::Instant::now();
let source_fragments = if body == Body::Whole {
Vec::new()
} else {
fragment_solid(source, SOURCE_OPERAND, &empty_imprint())?
};
os_debug!("stage: source fragments {:.3}s", stage_clock.elapsed().as_secs_f64());
let mut selected = source_fragments
.into_iter()
.filter(|fragment| retained.contains(&fragment.source_face_id))
.collect::<Vec<_>>();
let mut face_images = selected
.iter()
.map(|fragment| OffsetShellFaceImageRecord {
role: OffsetFaceRole::Source,
source_face_id: fragment.source_face_id,
})
.collect::<Vec<_>>();
if distance < 0.0 {
for fragment in &mut selected {
flip_fragment(fragment)?;
}
}
if distance > 0.0 && body == Body::Shell {
for fragment in &mut chosen_offsets {
flip_fragment(fragment)?;
}
}
face_images.extend(
chosen_offsets
.iter()
.map(|fragment| OffsetShellFaceImageRecord {
role: OffsetFaceRole::Offset,
source_face_id: carriers[fragment.operand as usize].source_face_id,
}),
);
face_images.extend(
chosen_walls
.iter()
.map(|fragment| OffsetShellFaceImageRecord {
role: OffsetFaceRole::Wall,
source_face_id: carriers[fragment.operand as usize].source_face_id,
}),
);
selected.extend(chosen_offsets);
selected.extend(chosen_walls);
let mut assembly_weld = 2e-3f64.max(scale * 5e-5);
if thin_skin_pad_enabled() && distance > 0.0 {
assembly_weld = assembly_weld.min(0.25 * distance.abs());
}
let mut solid = profile::detail("assemble_open_fragments", || assemble_open_fragments(
selected,
&assembly_sources,
&imprint,
assembly_weld,
))?;
os_debug!("stage: assembled open fragments {:.3}s", stage_clock.elapsed().as_secs_f64());
profile::detail("orient_open_solid_faces", || orient_open_solid_faces(&mut solid))?;
os_debug!("stage: oriented {:.3}s", stage_clock.elapsed().as_secs_f64());
let connector_images = profile::detail("complete_sharp_offset_connectors", || complete_sharp_offset_connectors(
&mut solid,
source,
&face_images,
distance,
2e-3f64.max(scale * 5e-5),
))?;
os_debug!(
"sharp connector completion added {} faces ({:.3}s)",
connector_images.len(),
stage_clock.elapsed().as_secs_f64()
);
if !connector_images.is_empty() {
for shell in &mut solid.shells {
shell.faces.retain(|face| {
!matches!(
face_images
.get(face.id.saturating_sub(1) as usize)
.map(|image| image.role),
Some(OffsetFaceRole::Wall)
)
});
}
solid.shells.retain(|shell| !shell.faces.is_empty());
}
face_images.extend(connector_images);
profile::detail("orient_open_solid_faces", || orient_open_solid_faces(&mut solid))?;
let welded_rims = profile::detail("weld_coplanar_orphan_rims", || weld_coplanar_orphan_rims(&mut solid, 2e-3f64.max(scale * 5e-5)))?;
os_debug!("welded {welded_rims} coplanar orphan rims into opening caps");
let rim_band = profile::detail("weld_free_form_rim_bands", || weld_free_form_rim_bands(
&mut solid,
&mut face_images,
&carriers,
source,
2e-3f64.max(scale * 5e-5),
))?;
let rim_bands = rim_band.welded;
os_debug!("welded {rim_bands} free-form rim bands");
let pair_rims =
profile::detail("weld_coplanar_rim_pair_annuli", || weld_coplanar_rim_pair_annuli(&mut solid, &mut face_images, 2e-3f64.max(scale * 5e-5)))?;
os_debug!("welded {pair_rims} coplanar rim pairs into annulus walls");
if debug_enabled() {
for (shell_index, shell) in solid.shells.iter().enumerate() {
for face in &shell.faces {
os_debug!(
"POSTWELD shell {} face {} same_sense={} loops={}",
shell_index,
face.id,
face.same_sense,
face.loops.len()
);
for (loop_index, loop_record) in face.loops.iter().enumerate() {
let walk = loop_record
.coedges
.iter()
.map(|coedge| {
let spin = (|| -> Result<f64, KernelRefusal> {
let [d0, d1] = coedge.pcurve.domain().or_refuse(KernelStage::Sew, "domain")?;
let a = coedge.pcurve.evaluate(d0 + (d1 - d0) * 0.45).or_refuse(KernelStage::Sew, "evaluate")?;
let b = coedge.pcurve.evaluate(d0 + (d1 - d0) * 0.55).or_refuse(KernelStage::Sew, "evaluate")?;
let pa = face.surface.evaluate(a.x, a.y).or_refuse(KernelStage::Sew, "evaluate")?;
let pb = face.surface.evaluate(b.x, b.y).or_refuse(KernelStage::Sew, "evaluate")?;
Ok(pa.cross(pb).z)
})()
.unwrap_or(f64::NAN);
format!(
"{}{}(spin{:+.0})",
if coedge.forward { "+" } else { "-" },
coedge.edge_id,
spin.signum()
)
})
.collect::<Vec<_>>();
os_debug!(" loop {} {:?}", loop_index, walk);
}
}
}
}
let completion_carriers = carriers.iter().collect::<Vec<_>>();
let completion_tolerance = 2e-3f64.max(scale * 5e-5);
let duplicate_welds = profile::detail("weld_duplicate_one_use_arcs", || weld_duplicate_one_use_arcs(&mut solid, completion_tolerance))?;
os_debug!("welded {duplicate_welds} duplicate one-use boundary arcs");
if debug_enabled() {
let mut use_counts = HashMap::<u64, usize>::default();
for coedge in solid
.shells
.iter()
.flat_map(|shell| &shell.faces)
.flat_map(|face| &face.loops)
.flat_map(|loop_record| &loop_record.coedges)
{
*use_counts.entry(coedge.edge_id).or_default() += 1;
}
let open = use_counts.values().filter(|count| **count == 1).count();
os_debug!(
"assembled: faces={} open_edges={open}",
solid
.shells
.iter()
.map(|shell| shell.faces.len())
.sum::<usize>()
);
}
let completed_images = profile::detail("complete_opening_boundary_cycles", || complete_opening_boundary_cycles(
&mut solid,
&completion_carriers,
&face_images,
completion_tolerance,
))?;
os_debug!("completion added {} faces", completed_images.len());
face_images.extend(completed_images);
let solid = profile::detail("merge_same_surface_faces_open", || merge_same_surface_faces_open(&solid, (1e-7f64).max(scale * 1e-9)))?;
let assembly_tolerance = 2e-3f64.max(scale * 5e-5);
let finalized = profile::detail("finalize_assembled_solid", || finalize_assembled_solid(solid, assembly_tolerance)).map_err(|error| {
match &rim_band.decline {
Some(reason) => error.with_message(|message| format!("{message}; offset_shell: {reason}")),
None => error,
}
});
let mut solid = match finalized {
Ok(solid) => solid,
Err(error) => {
os_debug!("finalize_assembled_solid: {}", error.message);
if !every_seeded_piece && body == Body::Shell && distance > 0.0 && every_seeded_skin_piece_enabled() {
let retried = offset_shell_impl(
source,
opening_face_ids,
distance,
body,
extend_oblique_carriers,
extension_fired,
reflex_rebuilds,
shadowed_supports,
cavity_bounds,
true,
shadowed_points,
omitted_supports,
vacated_notes,
);
if retried.is_ok() {
return retried;
}
}
if let Some((index, own, other, thickness)) = sealing_distance(
&sealed,
source,
&retained_faces_with_edges,
distance,
offset_skin_tolerance,
)? {
let skin = &sealed[index];
let bounding = skin.sources.iter().map(u64::to_string).collect::<Vec<_>>().join(", ");
return Err(KernelRefusal::unsupported(KernelStage::Sew, refusals::SEALED_HOLLOW, format!(
"offset_shell: at this distance the cavity splits — a hollow bounded by the offsets of \
source faces {bounding} near ({:.4}, {:.4}, {:.4}) is sealed off from the opening, and a \
sealed hollow could not be assembled here. It is closed off where the offsets of source \
faces {own} and {other} cross, which they do from {:.4} up ({thickness:.4} of material \
between them); below {:.4} it opens into the rest of the cavity, so use a shell distance \
below that",
skin.centre.x,
skin.centre.y,
skin.centre.z,
0.5 * thickness,
0.5 * thickness,
)));
}
return Err(error);
}
};
profile::detail("reconstruct_analytic_shell", || crate::offset_retrim::reconstruct_analytic_shell(&mut solid))?;
if rim_bands + pair_rims > 0 || !inward_extended_walls.is_empty() {
profile::detail("coheres_face_normals", || coheres_face_normals(&mut solid))?;
}
let solid = solid;
profile::lap(&mut clock, profile::ASSEMBLY);
face_images = solid
.shells
.iter()
.flat_map(|shell| &shell.faces)
.map(|face| {
face_images
.get(face.id.saturating_sub(1) as usize)
.cloned()
.ok_or_else(|| "offset_shell: merged face lost provenance".to_string())
})
.collect::<Result<Vec<_>, _>>().or_refuse(KernelStage::Sew, "offset_shell_merged_face_lost_provenance")?;
let bore_rings = claim_bore_end_rings(&solid, &mut face_images)?;
os_debug!("{bore_rings} bore end rings took the bore wall's provenance");
let mut rim_use_counts = HashMap::<u64, usize>::default();
for coedge in solid
.shells
.iter()
.flat_map(|shell| &shell.faces)
.flat_map(|face| &face.loops)
.flat_map(|loop_record| &loop_record.coedges)
{
*rim_use_counts.entry(coedge.edge_id).or_default() += 1;
}
if debug_enabled() {
for (shell_index, shell) in solid.shells.iter().enumerate() {
for face in &shell.faces {
let role = face_images
.get(face.id.saturating_sub(1) as usize)
.map(|image| image.role);
let c = face.surface.evaluate(0.5, 0.5).unwrap_or_default();
os_debug!(
"DUMPFACE shell {} face {} affine={} role={:?} loops={} center=({:.3},{:.3},{:.3})",
shell_index, face.id, face.surface.is_affine().unwrap_or(false), role,
face.loops.len(), c.x, c.y, c.z
);
for (li, lp) in face.loops.iter().enumerate() {
let ids = lp.coedges.iter().map(|c| c.edge_id).collect::<Vec<_>>();
os_debug!(" loop {} edges {:?}", li, ids);
}
}
}
for edge in &solid.edges {
let uses = rim_use_counts.get(&edge.id).copied().unwrap_or(0);
let s = edge.curve.evaluate(edge.t0).or_refuse(KernelStage::Sew, "evaluate")?;
let m = edge.curve.evaluate((edge.t0 + edge.t1) * 0.5).or_refuse(KernelStage::Sew, "evaluate")?;
let q = edge.curve.evaluate(edge.t0 + (edge.t1 - edge.t0) * 0.25).or_refuse(KernelStage::Sew, "evaluate")?;
os_debug!(
"DUMPEDGE edge {} uses={} closed={} deg={} cpts={} cdeg={} knots={:?} start=({:.3},{:.3},{:.3}) q=({:.3},{:.3},{:.3}) mid=({:.3},{:.3},{:.3})",
edge.id, uses, edge.start_vertex_id == edge.end_vertex_id, edge.degenerate,
edge.curve.control_points.len(), edge.curve.degree, edge.curve.knots,
s.x, s.y, s.z, q.x, q.y, q.z, m.x, m.y, m.z
);
}
}
for edge in &solid.edges {
if rim_use_counts.get(&edge.id).copied().unwrap_or(0) != 1
|| edge.start_vertex_id != edge.end_vertex_id
{
continue;
}
let anchor = edge.curve.evaluate(edge.t0).or_refuse(KernelStage::Sew, "evaluate")?;
let sweep_threshold = 1e-4f64.max(scale * 1e-6);
let sweeps = [0.25, 0.5, 0.75].into_iter().any(|fraction| {
edge.curve
.evaluate(edge.t0 + (edge.t1 - edge.t0) * fraction)
.map(|point| point.sub(anchor).length() > sweep_threshold)
.unwrap_or(false)
});
if sweeps {
if debug_enabled() {
let rim_mid = edge.curve.evaluate((edge.t0 + edge.t1) * 0.5).or_refuse(KernelStage::Sew, "evaluate")?;
os_debug!(
"PROBE orphan rim edge {} anchor=({:.4},{:.4},{:.4}) mid=({:.4},{:.4},{:.4})",
edge.id,
anchor.x,
anchor.y,
anchor.z,
rim_mid.x,
rim_mid.y,
rim_mid.z
);
for (shell_index, shell) in solid.shells.iter().enumerate() {
for face in &shell.faces {
let uses_rim = face
.loops
.iter()
.flat_map(|l| &l.coedges)
.any(|c| c.edge_id == edge.id);
let affine = face.surface.is_affine().unwrap_or(false);
let on = edge_on_surface(edge, &face.surface, 2e-3f64.max(scale * 5e-5))
.unwrap_or(false);
let proj = project_point_to_surface(&face.surface, rim_mid).or_refuse(KernelStage::Sew, "project_point_to_surface")?;
let contains = if on {
parameter_point_in_face(
face,
Vec2 {
x: proj.u,
y: proj.v,
},
2e-3f64.max(scale * 5e-5),
).or_refuse(KernelStage::Sew, "parameter_point_in_face")? != PolygonClass::Outside
} else {
false
};
if on || uses_rim {
os_debug!(
" face {} shell {} affine={} uses_rim={} on_surf={} proj_dist={:.5} contains={}",
face.id, shell_index, affine, uses_rim, on, proj.distance, contains
);
}
}
}
}
return Err(refusals::unwelded_curved_rim(edge.id, anchor).into());
}
}
let mut solid = solid;
let mut loop_ids = HashSet::default();
let mut coedge_ids = HashSet::default();
for face in solid.shells.iter().flat_map(|shell| &shell.faces) {
for wire in &face.loops {
loop_ids.insert(wire.id);
coedge_ids.extend(wire.coedges.iter().map(|coedge| coedge.id));
}
}
let mut seen_loops = HashSet::default();
let mut seen_coedges = HashSet::default();
let mut next_loop = 1;
let mut next_coedge = 1;
for face in solid.shells.iter_mut().flat_map(|shell| &mut shell.faces) {
for wire in &mut face.loops {
if !seen_loops.insert(wire.id) {
while !loop_ids.insert(next_loop) { next_loop += 1; }
wire.id = next_loop;
}
for coedge in &mut wire.coedges {
if !seen_coedges.insert(coedge.id) {
while !coedge_ids.insert(next_coedge) { next_coedge += 1; }
coedge.id = next_coedge;
}
}
}
}
let solid = profile::detail("accept_offset_result", || super::acceptance::accept_offset_result(solid))?;
profile::lap(&mut clock, profile::ACCEPTANCE);
Ok(OffsetShellResultRecord { solid, face_images })
}
pub fn offset_shell_with_diagnostics(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
tolerances: Option<KernelTolerances>,
) -> Result<KernelOutcome<OffsetShellResultRecord>, KernelRefusal> {
let policy = tolerances.unwrap_or_else(|| KernelTolerances::for_solid(source, 1e-7));
policy.check().or_refuse(KernelStage::Sew, "check")?;
let mut diagnostics = KernelDiagnostics::default();
diagnostics.count_n(
"collect.source_faces",
source
.shells
.iter()
.map(|shell| shell.faces.len() as u64)
.sum(),
);
diagnostics.count_n("collect.opening_faces", opening_face_ids.len() as u64);
diagnostics.measure_max("offset.distance", distance.abs());
diagnostics.measure_max("tolerance.model", policy.model);
let fit_scope = crate::PcurveFitScope::open();
let polyline_scope = crate::PolylineFitScope::open();
let result = offset_shell(source, opening_face_ids, distance)?;
fit_scope.close().report_into(&mut diagnostics);
polyline_scope.close().report_into(&mut diagnostics);
let timings = profile::snapshot();
diagnostics.count_n("shell.attempts", timings.attempts);
for (phase, count) in &timings.carriers {
diagnostics.count_n(format!("shell.{phase}"), *count);
}
for (phase, ms) in &timings.ms {
diagnostics.measure_max(format!("timing.{phase}_ms"), *ms);
}
diagnostics.count_n(
"sew.output_faces",
result
.solid
.shells
.iter()
.map(|shell| shell.faces.len() as u64)
.sum(),
);
diagnostics.count_n("sew.face_images", result.face_images.len() as u64);
let validation = result.solid.validate_detailed(&policy);
diagnostics.measure_max("validate.max_pcurve_error", validation.max_pcurve_error);
diagnostics.count_n("validate.issues", validation.issues.len() as u64);
diagnostics.count_n(
"validate.wire_warnings",
validation.wire_warnings.len() as u64,
);
for warning in validation.wire_warnings {
diagnostics.event(
DiagnosticSeverity::Warning,
KernelStage::Validate,
"validate.uv_wire",
warning.message,
);
}
if !validation.issues.is_empty() {
for issue in &validation.issues {
diagnostics.event(
DiagnosticSeverity::Error,
KernelStage::Validate,
"validate.brep",
issue.message.clone(),
);
}
return Err(KernelRefusal::internal(KernelStage::Validate, "offset_shell_diagnostic_result", format!(
"offset_shell: invalid diagnostic result: {:?}",
validation.issues
)));
}
Ok(KernelOutcome {
value: result,
diagnostics,
})
}
pub(super) fn wall_surplus_beyond_the_sections(
fragment: &FaceFragmentRecord,
wall_operand: u8,
cut_by_source: impl Fn(u64) -> bool,
) -> bool {
let mut on_outline = false;
for coedge in fragment.loops.iter().flat_map(|record| &record.coedges) {
match &coedge.source {
FragmentEdgeSource::Boundary { operand, .. } if *operand == wall_operand => on_outline = true,
FragmentEdgeSource::Boundary { operand, .. } | FragmentEdgeSource::SharedBoundary { operand, .. }
if *operand == SOURCE_OPERAND =>
{
return false;
}
FragmentEdgeSource::Imprint { piece_id } => {
if cut_by_source(*piece_id) {
return false;
}
}
_ => {}
}
}
on_outline
}
fn cascade_omission_enabled() -> bool {
switch!("BREP_OFFSET_SHELL_CASCADE_OMISSION", true)
}
fn refuse_a_cascade_that_kept_the_cavity(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
result: &BrepSolid,
) -> Result<(), KernelRefusal> {
let Some(witness) = cavity_witness(source, opening_face_ids, distance)? else {
return Ok(());
};
let scale = crate::solid_scale(source).max(1.0);
let class = classify_point(witness.point, result, 1e-8 * scale).or_refuse(KernelStage::Select, "classify_point")?.class;
if class == PointClass::In {
return Err(KernelRefusal::unsupported(KernelStage::Select, "offset_shell_shadow_cascade", format!(
"offset_shell: omitting offsets that read shadowed only after earlier omissions leaves material at \
({:.4}, {:.4}, {:.4}), which is {:.4} from every retained face, past the shell distance {distance}; \
the cavity there could not be built — use a smaller shell distance",
witness.point.x, witness.point.y, witness.point.z, witness.clearance
)));
}
Ok(())
}
fn cavity_witness_enabled() -> bool {
switch!("BREP_OFFSET_SHELL_CAVITY_WITNESS", true)
}
#[derive(Debug)]
pub(super) struct CavityWitness {
pub(super) point: Vec3,
pub(super) clearance: f64,
pub(super) bounds: HashSet<u64>,
}
pub(super) fn cavity_witness(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
) -> Result<Option<CavityWitness>, KernelRefusal> {
if !cavity_witness_enabled() || distance <= 0.0 || opening_face_ids.is_empty() {
return Ok(None);
}
let faces = source.shells.iter().flat_map(|shell| &shell.faces).collect::<Vec<_>>();
let retained = faces.iter().filter(|face| !opening_face_ids.contains(&face.id)).copied().collect::<Vec<_>>();
if retained.is_empty() {
return Ok(None);
}
let edge_by_id = source.edges.iter().map(|edge| (edge.id, edge)).collect::<HashMap<_, _>>();
let retained = retained
.into_iter()
.map(|face| {
let edges = face
.loops
.iter()
.flat_map(|loop_record| &loop_record.coedges)
.filter_map(|coedge| edge_by_id.get(&coedge.edge_id).copied())
.collect::<Vec<_>>();
(face, edges)
})
.collect::<Vec<_>>();
let Some((low, high)) = control_hull(source, &faces) else { return Ok(None) };
let scale = crate::solid_scale(source).max(1.0);
let classify_tolerance = 1e-8 * scale;
let margin = 1e-6 * scale;
let readings = |point: Vec3| -> Result<Vec<(u64, f64)>, KernelRefusal> {
retained.iter().map(|(face, edges)| Ok((face.id, distance_to_trimmed_face(face, edges, point)?))).collect()
};
let clearance = |values: &[(u64, f64)]| values.iter().map(|(_, value)| *value).fold(f64::INFINITY, f64::min);
let inside = |point: Vec3| -> Result<bool, KernelRefusal> {
Ok(classify_point(point, source, classify_tolerance).or_refuse(KernelStage::Select, "classify_point")?.class == PointClass::In)
};
const CELLS: usize = 12;
const STARTS: usize = 24;
const MOVES: usize = 160;
let span = high.sub(low);
let mut starts = Vec::new();
for i in 0..CELLS {
for j in 0..CELLS {
for k in 0..CELLS {
let at = |index: usize| (index as f64 + 0.5) / CELLS as f64;
let point = Vec3::new(low.x + span.x * at(i), low.y + span.y * at(j), low.z + span.z * at(k));
if inside(point)? {
starts.push((clearance(&readings(point)?), point));
}
}
}
}
starts.sort_by(|a, b| b.0.total_cmp(&a.0));
starts.truncate(STARTS);
const DIRECTIONS: [(f64, f64, f64); 14] = [
(1.0, 0.0, 0.0), (-1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, 1.0), (0.0, 0.0, -1.0),
(1.0, 1.0, 1.0), (-1.0, -1.0, -1.0), (1.0, 1.0, -1.0), (-1.0, -1.0, 1.0),
(1.0, -1.0, 1.0), (-1.0, 1.0, -1.0), (-1.0, 1.0, 1.0), (1.0, -1.0, -1.0),
];
let first_step = span.x.max(span.y).max(span.z) / CELLS as f64;
let mut footless = None;
for (start_clearance, start) in starts {
let (mut point, mut best, mut step) = (start, start_clearance, first_step);
for _ in 0..MOVES {
if step < 1e-6 * scale {
break;
}
let mut moved = false;
for (x, y, z) in DIRECTIONS {
let length = (x * x + y * y + z * z).sqrt();
let next = Vec3::new(point.x + step * x / length, point.y + step * y / length, point.z + step * z / length);
let value = clearance(&readings(next)?);
if value > best && inside(next)? {
(point, best, moved) = (next, value, true);
break;
}
}
if !moved {
step *= 0.5;
}
}
if best > distance + margin {
let nudge = 0.5 * (best - distance);
let mut votes = HashMap::<u64, usize>::default();
for (x, y, z) in DIRECTIONS {
let length = (x * x + y * y + z * z).sqrt();
let near = Vec3::new(point.x + nudge * x / length, point.y + nudge * y / length, point.z + nudge * z / length);
let values = readings(near)?;
let Some((id, _)) = values.iter().min_by(|a, b| a.1.total_cmp(&b.1)).copied() else { continue };
let Some((face, _)) = retained.iter().find(|(face, _)| face.id == id) else { continue };
if foot_inside_trim(face, near)? {
*votes.entry(id).or_default() += 1;
}
}
let most = votes.values().copied().max().unwrap_or(0);
let bounds = votes.iter().filter(|(_, count)| **count * 2 >= most).map(|(id, _)| *id).collect::<HashSet<_>>();
let witness = CavityWitness { point, clearance: best, bounds };
if !witness.bounds.is_empty() {
return Ok(Some(witness));
}
footless = Some(witness);
}
}
Ok(footless)
}
fn control_hull(source: &BrepSolid, faces: &[&FaceRecord]) -> Option<(Vec3, Vec3)> {
let mut low = Vec3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY);
let mut high = Vec3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY);
for vertex in &source.vertices {
low = Vec3::new(low.x.min(vertex.point.x), low.y.min(vertex.point.y), low.z.min(vertex.point.z));
high = Vec3::new(high.x.max(vertex.point.x), high.y.max(vertex.point.y), high.z.max(vertex.point.z));
}
for control in faces.iter().flat_map(|face| face.surface.control_points.iter().flatten()) {
let point = control.point().ok()?;
low = Vec3::new(low.x.min(point.x), low.y.min(point.y), low.z.min(point.z));
high = Vec3::new(high.x.max(point.x), high.y.max(point.y), high.z.max(point.z));
}
Some((low, high))
}
fn empty_cavity_enabled() -> bool {
switch!("BREP_OFFSET_SHELL_EMPTY_CAVITY", true)
}
fn empty_cavity_shell(
source: &BrepSolid,
opening_face_ids: &[u64],
distance: f64,
) -> Result<Option<OffsetShellResultRecord>, KernelRefusal> {
if !empty_cavity_enabled() || distance <= 0.0 || opening_face_ids.is_empty() {
return Ok(None);
}
let faces = source.shells.iter().flat_map(|shell| &shell.faces).collect::<Vec<_>>();
let retained = faces.iter().filter(|face| !opening_face_ids.contains(&face.id)).copied().collect::<Vec<_>>();
if retained.is_empty() {
return Ok(None);
}
let edges = source.edges.iter().collect::<Vec<_>>();
let Some((low, high)) = control_hull(source, &faces) else { return Ok(None) };
let scale = crate::solid_scale(source).max(1.0);
let tolerance = 1e-9 * scale;
const CELLS: usize = 12;
let span = high.sub(low);
let mut inside = 0usize;
for i in 0..CELLS {
for j in 0..CELLS {
for k in 0..CELLS {
let at = |index: usize| (index as f64 + 0.5) / CELLS as f64;
let point = Vec3::new(low.x + span.x * at(i), low.y + span.y * at(j), low.z + span.z * at(k));
let class = classify_point(point, source, tolerance * 10.0).or_refuse(KernelStage::Select, "classify_point")?.class;
if class != PointClass::In {
continue;
}
inside += 1;
let mut nearest = f64::INFINITY;
for face in &retained {
nearest = nearest.min(distance_to_trimmed_face(face, &edges, point)?);
if nearest <= distance + tolerance {
break;
}
}
if nearest > distance + tolerance {
os_debug!(
"empty cavity declined: ({:.4}, {:.4}, {:.4}) lies {nearest:.6} from every retained face",
point.x, point.y, point.z
);
return Ok(None);
}
}
}
}
if inside == 0 {
return Ok(None);
}
os_debug!("empty cavity: every retained skin is shadowed and {inside} sampled interior points lie within the distance");
let face_images = faces
.iter()
.map(|face| OffsetShellFaceImageRecord { role: OffsetFaceRole::Source, source_face_id: face.id })
.collect();
Ok(Some(OffsetShellResultRecord { solid: source.clone(), face_images }))
}
fn thin_skin_pad_enabled() -> bool {
switch!("BREP_OFFSET_THIN_SKIN_PAD", true)
}
fn thin_skin_pad(source: &BrepSolid, face: &FaceRecord, distance: f64) -> Result<Option<f64>, KernelRefusal> {
if !thin_skin_pad_enabled() || distance <= 0.0 || !face.surface.is_affine().or_refuse(KernelStage::Refine, "is_affine")? {
return Ok(None);
}
let size = crate::solid_scale(source).max(1e-9);
if distance >= 2e-3 * size {
return Ok(None);
}
Ok(Some(4e-3 * size + distance))
}