Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/difftest/src/units/boolean_topology_intersector_p1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,14 @@ impl CurveOnClassifyObjects<H> for Objs<'_> {
}

impl ProtoBrep<H> for Two {
fn merge_point_class_into_brep(
&mut self,
k: &mut Kernel,
pc: &mut PointClassification<H>,
src_point: Point3,
) -> solidcore::Result<solidcore::intersect::point_class::MergeOutputs<H>> {
solidcore::intersect::curve_class::p6::merge_into_object_mut(k, self, pc, src_point)
}
fn brep_squeeze_edge(
&mut self,
k: &mut Kernel,
Expand Down
110 changes: 105 additions & 5 deletions crates/difftest/src/units/intersect_curve_class_p3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@
//! state (see unit `intersect.curve_class_p1`, [`BareTopology`]), so their zone tolerances are
//! compared too.
//!
//! `InsertPointClass` runs through `insert_point_class_mut` on the pool's B-reps (the
//! `Combine` of a boundary merge moves a vertex to a surf/surf/surf point,
//! `SmVertex::SetPoint`), and both sides then report every vertex object's point.
//!
//! Deferred cases: the zone tolerance of an edge, vertex or face classification on a bare
//! stand-in next to a real B-rep (`topology.edge`, `topology.vertex`, `topology.face_p2`),
//! `MergeIntoObject` (`SmVertex::SetPoint`) and the 2D tolerance mapping that needs a surface
//! point solve. Generated scripts are filtered by the shim's run-time checks (scripts it
//! `MergeIntoObject` and the 2D tolerance mapping that needs a surface point solve. Generated scripts are filtered by the shim's run-time checks (scripts it
//! rejects as undefined upstream are dropped).

use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -63,7 +66,14 @@ use solidcore::surface::plane::Plane;
use solidcore::surface::surface::Surface;
use solidcore::surface::volume::Volume;
use solidcore::{EntityRef, SolidError};
use solidcore::topology::Brep;
use solidcore::geom::attribute::{AttributeStore, ObjectKey};
use solidcore::geom::object::ObjectRef;
use solidcore::intersect::curve_class::p5::ClassGeometryMut;
use solidcore::intersect::curve_class::p6::{MergeContext, MergeIntervalBrep};
use solidcore::topology::edge::NotifyEnv;
use solidcore::topology::topology::Topology;
use solidcore::topology::{Brep, EdgeId};
use std::collections::BTreeMap;
use solidcore_ffi::units::heal_face_props::FaceSpec;
use solidcore_ffi::units::intersect_curve_class_p3::{self as ffi, kind, op};

Expand Down Expand Up @@ -585,6 +595,79 @@ struct Pool {
/// Without `topo`: the vertex, edge and face stand-ins as B-rep records
/// ([`BareTopology`]).
bare: BareTopology,
/// The notification records and attributes of the B-rep edits
/// ([`MergeIntervalBrep`]: `Combine`'s `SmVertex::SetPoint`).
records: BTreeMap<ObjectKey, Topology>,
attributes: AttributeStore,
}

/// The identity of an edge's curve (none of this unit's edits makes one).
fn pool_curve_ref(e: EdgeId, kind: i64) -> ObjectRef {
ObjectRef {
key: ObjectKey(((kind as u64) << 32) | u64::from(e.index())),
type_code: kind,
}
}

impl ClassGeometryMut<H> for Pool {
fn curve_mut(&mut self, o: H) -> Option<&mut Curve> {
match self.objs.get_mut(o.0) {
Some(PoolObj::Curve(c)) => Some(c),
_ => None,
}
}
fn topology_entity_mut(&mut self, o: H) -> Option<(&mut Brep, EntityRef)> {
if let Some(t) = self.topo.as_mut() {
let e = *t.entities.get(o.0.checked_sub(t.first)?)?;
Some((&mut t.brep, e))
} else {
let e = self.bare.ent(o.0)?;
Some((&mut self.bare.brep, e))
}
}
}

/// The B-rep of the pool's topology handles, for the vertex move of `Combine` in
/// `InsertPointClass` ([`CurveClassification::insert_point_class_mut`]).
impl MergeIntervalBrep<H> for Pool {
fn merge_context(&mut self, o: H) -> Option<MergeContext<'_, H>> {
let handles = self.handles.clone();
let (brep, target, handle): (&mut Brep, EntityRef, Box<dyn Fn(EntityRef) -> H>) =
if let Some(t) = self.topo.as_mut() {
let target = *t.entities.get(o.0.checked_sub(t.first)?)?;
let (first, entities) = (t.first, t.entities.clone());
let handle = move |e: EntityRef| {
entities
.iter()
.position(|x| *x == e)
.and_then(|i| handles.get(first + i).copied())
.unwrap_or(H(usize::MAX, ClassObjectKind::Other))
};
(&mut t.brep, target, Box::new(handle))
} else {
let target = self.bare.ent(o.0)?;
let ents = self.bare.ents.clone();
let handle = move |e: EntityRef| {
ents.iter()
.position(|t| *t == Some(e))
.and_then(|i| handles.get(i).copied())
.unwrap_or(H(usize::MAX, ClassObjectKind::Other))
};
(&mut self.bare.brep, target, Box::new(handle))
};
Some(MergeContext {
brep,
target,
handle,
notifications: NotifyEnv {
records: &mut self.records,
attributes: &mut self.attributes,
context: None,
key_of: &super::boolean_topology_intersector_p1::p9_entity_ref,
},
curve_ref: &pool_curve_ref,
})
}
}

impl ClassGeometry<H> for Pool {
Expand Down Expand Up @@ -696,6 +779,8 @@ fn build_pool(objects: &[ObjIn]) -> Result<Pool, HarnessError> {
handles: Vec::new(),
topo: None,
bare: BareTopology::default(),
records: BTreeMap::new(),
attributes: AttributeStore::default(),
};
for (i, o) in objects.iter().enumerate() {
// the curve of an edge object, the surface of a face object (earlier pool objects;
Expand Down Expand Up @@ -864,7 +949,7 @@ fn build_pc(k: &Kernel, pool: &Pool, p: &PcIn) -> Result<PointClassification<H>,

#[allow(clippy::too_many_lines)]
fn rust_run(s: &Script) -> Result<Vec<f64>, Fail> {
let k = Kernel::default();
let mut k = Kernel::default();
let mut pool = build_pool(&s.objects)?;
if let Some(face) = &s.face {
add_face(&k, &mut pool, face)?;
Expand Down Expand Up @@ -1050,11 +1135,26 @@ fn rust_run(s: &Script) -> Result<Vec<f64>, Fail> {
}
OpIn::Insert { pc, prm, last, force, .. } => {
let pc = build_pc(&k, &pool, pc)?;
let r = cc.insert_point_class(&k, &pool, &pc, *prm, *last, *force);
// a merge's surf/surf/surf point moves a vertex
// (`Combine`'s SmVertex::SetPoint), as in the shim
let r = cc
.insert_point_class_mut(&mut k, &mut pool, &pc, *prm, *last, *force);
match st(r)? {
Ok(made) => o.extend([0.0, b(made)]),
Err(s) => o.push(s),
}
// every vertex object's point: a merged surf/surf/surf point
// moves one (`Combine`'s SmVertex::SetPoint)
for h in &pool.handles {
if h.1 == ClassObjectKind::Vertex {
// a stand-in next to the real B-rep is a bare record
let p = pool
.classification_vertex_point(*h)
.or_else(|| pool.bare.vertex_point(h.0))
.ok_or_else(|| harness("vertex object without a point"))?;
o.extend([p.x, p.y, p.z]);
}
}
}
OpIn::RemovePc { j, pos, .. } => {
let r = cc.remove_point_class(*j, position(*pos)?);
Expand Down
22 changes: 19 additions & 3 deletions crates/difftest/src/units/intersect_curve_class_p6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
//! Each case works on a fresh copy of that box (the shim builds one per case), and
//! `MergeInterval` edits it through `MergeIntervalBrep`; a merge also reports the box
//! after it (counts, sorted vertex and edge tolerances, the new vertices and edges).
//! `InsertCurveInterval` runs through `insert_curve_interval_mut` (its `Combine` may move
//! the picked vertex to a surf/surf/surf point, `SmVertex::SetPoint`) and reports that
//! vertex's point after it.
//! The `imprint_*` cases pick other box objects and place lines on the box, with its
//! editing enabled, so the vertex, face-split, coincident-edge and wire-edge branches
//! run to completion.
Expand Down Expand Up @@ -597,9 +600,13 @@ impl UnitDiff for Runner {
let mut b = classification(&k, &i.b, i.tol, &g.first)?;
a.set_fillet_data((i.flags & 2 != 0).then_some(Handle::Other), i.flags & 2 != 0);
let result = match i.op {
0 => a
.insert_curve_interval(&k, &g, &ivl(&i.input, i.tol, &g.first)?)
.map(|()| (None, None)),
// a surf/surf/surf point combined into a vertex boundary moves the box's
// vertex (SmVertex::SetPoint), as the shim's InsertCurveInterval does
0 => {
let input = ivl(&i.input, i.tol, &g.first)?;
a.insert_curve_interval_mut(&mut k, &mut g, &input)
.map(|()| (None, None))
}
1 => a.homogenize(&k, &g, &mut b).map(|()| (None, None)),
2 => a
.fix_end_point_class(&k, &g, i.index, Vec3::new(x, y, z))
Expand Down Expand Up @@ -629,6 +636,15 @@ impl UnitDiff for Runner {
if let Some(merged) = &merged {
put_brep(&mut out, &g, merged).map_err(|e| h(e.to_string()))?;
}
// the picked vertex after InsertCurveInterval (a combined
// surf/surf/surf point moves it through SmVertex::SetPoint)
if i.op == 0 {
let Some(EntityRef::Vertex(v)) = g.first.get(2).copied() else {
return Err(h("picked vertex is not a vertex"));
};
let p = topo_vertex::point(&g.brep, v).map_err(|e| h(e.to_string()))?;
out.extend([p.x, p.y, p.z]);
}
Ok(Output::new().values("out", out))
}
}
Expand Down
47 changes: 47 additions & 0 deletions crates/difftest/src/units/topology_proto_topology_p1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,53 @@ impl ProtoTopology<H> for Pool {
/// The bare stand-ins' members (upstream `SmEdge::GetInterval`, `GetCurve`, `GetTolerance`
/// of the stand-in objects the shim makes), and new curves appended to the pool.
impl ProtoBrep<H> for Pool {
/// A vertex stand-in merges in its record (upstream's default-constructed
/// `SmVertex`: the `SetPoint` / `SetExact` / `SetTolerance` of `MergeIntoObject`
/// need no B-rep); an edge, face or region stand-in is joined to no B-rep, so its
/// edit keeps the default deferral.
fn merge_point_class_into_brep(
&mut self,
k: &mut Kernel,
pc: &mut PointClassification<H>,
src_point: Point3,
) -> solidcore::Result<solidcore::intersect::point_class::MergeOutputs<H>> {
let target = pc
.object()
.filter(|o| o.1 == ClassObjectKind::Vertex)
.and_then(|o| self.topo_entity(o));
let Some(target @ EntityRef::Vertex(_)) = target else {
return pc.merge_into_object(k, src_point);
};
let ents = self.topo.ents.clone();
let kinds = self.kinds.clone();
let handle = move |e: EntityRef| {
let i = ents.iter().position(|t| *t == Some(e)).unwrap_or(usize::MAX);
H(i, class_kind_of(kinds.get(i).copied().unwrap_or(-1)))
};
let mut records = BTreeMap::new();
let mut attributes = AttributeStore::default();
let mut env = solidcore::topology::edge::NotifyEnv {
records: &mut records,
attributes: &mut attributes,
context: None,
key_of: &super::boolean_topology_intersector_p1::p9_entity_ref,
};
let curve_ref = |e: solidcore::topology::EdgeId, kind: i64| ObjectRef {
key: ObjectKey(((kind as u64) << 32) | u64::from(e.index())),
type_code: kind,
};
pc.merge_into_brep(
k,
src_point,
solidcore::intersect::point_class::BrepMerge {
brep: &mut self.topo.brep,
target,
handle: &handle,
notifications: &mut env,
curve_ref: &curve_ref,
},
)
}
fn brep_squeeze_edge(
&mut self,
_k: &mut Kernel,
Expand Down
2 changes: 1 addition & 1 deletion crates/difftest/src/units/topology_proto_topology_p2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub(super) fn run_op(
*skip = true;
return Ok(());
};
let _ = res(r.mgr.imprint_vertex_definition(&r.k, &mut r.pool, d), st, skip)?;
let _ = res(r.mgr.imprint_vertex_definition(&mut r.k, &mut r.pool, d), st, skip)?;
if let Some(v) = r.mgr.vertex_def(d) {
out.push(r.pool.id(v.final_vertex(1)));
out.push(r.pool.id(v.final_vertex(2)));
Expand Down
28 changes: 28 additions & 0 deletions crates/solidcore/src/boolean/merge/p1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
//! bounding boxes before any mutation; no translation is faked. Destruction is
//! Rust ownership; the temporary-transform token is restored explicitly after the
//! caller's complete scope, including error exits. There is no input-dependent recursion.
//! `BooleanPostProcess`'s `DeleteTopologicalVertex` runs without the caller's
//! notification identities, so the survivor edge's joined curve comes back owned under
//! a kernel-issued identity of that edge; the owner identity the curve of each
//! surviving edge had before the call is put back (upstream the owner is the same
//! `SmEdge*`), so the caller's identity-keyed walks (the temporary transform's inverse
//! `Transform`) still find the curve.

use std::collections::BTreeMap;

Expand Down Expand Up @@ -545,13 +551,35 @@ pub fn boolean_post_process(
}
if vertex::is_topological_vertex(k, brep, v)? {
let editing = brep.editing_enabled();
// The owners of the two edges' curves: the survivor's joined curve is owned
// by the same edge (`SetCurve`), under the caller's identity of that edge.
let mut owners = Vec::new();
for e in vertex::edges(brep, v, None)? {
owners.push((
e,
brep.edge(e)?
.curve
.as_ref()
.and_then(crate::curve::curve::p2::Curve::owner),
));
}
ignore_failure(crate::topology::brep::p5::delete_topological_vertex(
k,
brep,
editing,
Some(v),
None,
))?;
for (e, owner) in owners {
if !brep.edge_list().contains(&e) {
continue;
}
if let Some(curve) = brep.edge_mut(e)?.curve.as_mut() {
if curve.owner() != owner {
curve.set_owner(owner, None);
}
}
}
}
}
// Combine a wire shell with a faceuse shell when the B-rep has just those two
Expand Down
12 changes: 11 additions & 1 deletion crates/solidcore/src/boolean/merge/p4/arena_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::intersect::curve_class::p1::CurveClassification;
use crate::intersect::curve_class::p5::ClassGeometryMut;
use crate::intersect::curve_class::p6::{MergeContext, MergeIntervalBrep};
use crate::intersect::point_class::{
ClassGeometry, ClassObject, ClassObjectKind, PointClassType, PointClassification,
ClassGeometry, ClassObject, ClassObjectKind, MergeOutputs, PointClassType, PointClassification,
};
use crate::intersect::topology_solver::p1::TopologyObjects;
use crate::kernel::Kernel;
Expand Down Expand Up @@ -495,6 +495,16 @@ impl CurveOnClassifyObjects<H> for Objs<'_, '_> {
}

impl ProtoBrep<H> for ArenaPair<'_> {
/// `MergeIntoObject` on the operand the classification's object names, through
/// [`MergeIntervalBrep::merge_context`].
fn merge_point_class_into_brep(
&mut self,
k: &mut Kernel,
pc: &mut PointClassification<H>,
src_point: Point3,
) -> crate::Result<MergeOutputs<H>> {
crate::intersect::curve_class::p6::merge_into_object_mut(k, self, pc, src_point)
}
fn edge_interval(&self, edge: H) -> Option<Extent1d> {
ed::interval(self.brep(edge)?, eid(edge)).ok()
}
Expand Down
Loading
Loading