mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 04:52:10 +00:00
Merge pull request #1445 from brett19/fix/topology-point-solve-faces
fix: BrepIdentity::of_brep honours a copy's geometry_owner_keys
This commit is contained in:
@@ -370,10 +370,15 @@ impl BrepIdentity<'static> {
|
||||
}
|
||||
|
||||
impl BrepIdentity<'static> {
|
||||
/// The identities `b`'s geometry owners use by its recorded naming scheme
|
||||
/// ([`Brep::naming`]) for `brep_id`.
|
||||
/// The base identities of `b` at `brep_id` by its recorded naming scheme alone
|
||||
/// ([`Brep::naming`]): the Euler operators' notification identities for a
|
||||
/// [`NamingScheme::Notify`] B-rep (one built through `new_brep` and the face
|
||||
/// builders), else [`BrepIdentity::arena`]. It ignores the proven owner keys `b`
|
||||
/// records (`Brep::geometry_owner_keys`), so it misses the geometry of a copy whose
|
||||
/// owners are the caller's keys ([`crate::topology::brep::p3::api_brep_copy`] under
|
||||
/// a caller `key_of`): prefer [`Self::of_brep_with_id`].
|
||||
#[must_use]
|
||||
pub fn of_brep_with_id(brep_id: BrepId, b: &Brep) -> Self {
|
||||
pub fn of_naming_with_id(brep_id: BrepId, b: &Brep) -> Self {
|
||||
match b.naming() {
|
||||
NamingScheme::Notify => Self {
|
||||
brep_id,
|
||||
@@ -384,102 +389,62 @@ impl BrepIdentity<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The identities `b`'s geometry owners use by its recorded naming scheme
|
||||
/// ([`Brep::naming`]): the Euler operators' notification identities for a
|
||||
/// [`NamingScheme::Notify`] B-rep (one built through `new_brep` and the face
|
||||
/// builders), else [`BrepIdentity::arena`]. A query under the wrong scheme resolves
|
||||
/// no geometry owner and silently finds nothing (`plan/findings.md`, "two
|
||||
/// owner-naming schemes").
|
||||
/// The identities `b`'s geometry owners use, at `brep_id`: its naming scheme
|
||||
/// ([`Self::of_naming_with_id`]) overridden by the proven producer owner keys it
|
||||
/// records (`Brep::geometry_owner_keys`), as [`ProvenBrepIdentity::new`]. The
|
||||
/// overrides are owned, so the result is a [`ProvenBrepIdentity`]; pass
|
||||
/// [`ProvenBrepIdentity::identity`] where a [`BrepIdentity`] is taken. A query
|
||||
/// under the wrong identities resolves no geometry owner and finds nothing
|
||||
/// (`plan/findings.md`, "two owner-naming schemes"; #1440).
|
||||
#[must_use]
|
||||
pub fn of_brep(b: &Brep) -> Self {
|
||||
pub fn of_brep_with_id(brep_id: BrepId, b: &Brep) -> ProvenBrepIdentity<'static> {
|
||||
ProvenBrepIdentity::new(Self::of_naming_with_id(brep_id, b), b)
|
||||
}
|
||||
|
||||
/// [`Self::of_brep_with_id`] at [`BrepId`] `0`.
|
||||
#[must_use]
|
||||
pub fn of_brep(b: &Brep) -> ProvenBrepIdentity<'static> {
|
||||
Self::of_brep_with_id(BrepId::from_index(0), b)
|
||||
}
|
||||
}
|
||||
|
||||
/// The cache-object identities of a [`NamingScheme::Notify`] B-rep
|
||||
/// ([`crate::topology::loopuse::notify_ref`] for its edges, faces and vertices).
|
||||
fn notify_identity(object: CacheObject) -> ObjectRef {
|
||||
crate::topology::brep::p2::caller_cache_key(&crate::topology::loopuse::notify_ref, object)
|
||||
}
|
||||
|
||||
impl Default for BrepIdentity<'_> {
|
||||
/// [`BrepIdentity::arena`] of the first B-rep of the caller's store (a caller
|
||||
/// querying one B-rep at a time).
|
||||
fn default() -> Self {
|
||||
BrepIdentity::arena(BrepId::from_index(0))
|
||||
}
|
||||
}
|
||||
|
||||
/// The ray directions of [`BrepQuery::point_3d_classify`], in firing order: each axis
|
||||
/// forward, then backward when the forward ray fails.
|
||||
const CLASSIFY_RAYS: [(Vec3, Vec3); 3] = [
|
||||
(Vec3::new(1.0, 0.0, 0.0), Vec3::new(-1.0, 0.0, 0.0)),
|
||||
(Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, -1.0, 0.0)),
|
||||
(Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, -1.0)),
|
||||
];
|
||||
|
||||
type ProvenKeyFn<'a> = Box<dyn Fn(CacheObject) -> ObjectRef + 'a>;
|
||||
|
||||
/// A B-rep's queries over its cache: the identities and the `SM_OC_BREP` cache, which
|
||||
/// upstream builds when a query first needs it and keeps on the B-rep
|
||||
/// (`SmSAGObject::CacheMakeOrValidate`). Keep one per B-rep state and reuse it across
|
||||
/// queries; edits to the B-rep invalidate it.
|
||||
pub struct BrepQuery<'a> {
|
||||
identity: BrepIdentity<'a>,
|
||||
/// A [`BrepIdentity`] with the proven producer owner keys of its B-rep
|
||||
/// (`Brep::geometry_owner_keys`) overriding the base keys where they differ: what
|
||||
/// [`BrepIdentity::of_brep`] and [`BrepQuery::of_brep`] resolve a B-rep's records by.
|
||||
/// It owns its overrides and does not borrow the B-rep.
|
||||
pub struct ProvenBrepIdentity<'a> {
|
||||
base: BrepIdentity<'a>,
|
||||
proven_keys: Option<ProvenKeyFn<'a>>,
|
||||
cache: OnceCell<BrepCache>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for BrepQuery<'_> {
|
||||
impl core::fmt::Debug for ProvenBrepIdentity<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("BrepQuery")
|
||||
.field("identity", &self.identity)
|
||||
.field("cache", &self.cache)
|
||||
.finish_non_exhaustive()
|
||||
f.debug_struct("ProvenBrepIdentity")
|
||||
.field("base", &self.base)
|
||||
.field("overridden", &self.proven_keys.is_some())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BrepQuery<'static> {
|
||||
/// The queries of a B-rep with the default identities ([`BrepIdentity::default`]).
|
||||
fn default() -> Self {
|
||||
Self::new(BrepIdentity::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl BrepQuery<'static> {
|
||||
/// Queries of `b` at [`BrepId`] `0`, resolving records by `b`'s naming scheme and
|
||||
/// proven producer geometry-owner identities (`Brep::geometry_owner_keys`).
|
||||
impl<'a> ProvenBrepIdentity<'a> {
|
||||
/// `base` without overrides.
|
||||
#[must_use]
|
||||
pub fn of_brep(b: &Brep) -> Self {
|
||||
Self::of_brep_with_id(BrepId::from_index(0), b)
|
||||
}
|
||||
|
||||
/// Queries of `b` at `brep_id`, resolving records by `b`'s naming scheme and proven
|
||||
/// producer geometry-owner identities (`Brep::geometry_owner_keys`).
|
||||
#[must_use]
|
||||
pub fn of_brep_with_id(brep_id: BrepId, b: &Brep) -> Self {
|
||||
Self::with_proven_owners(BrepIdentity::of_brep_with_id(brep_id, b), b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BrepQuery<'a> {
|
||||
/// The queries of the B-rep with `identity`; its cache is built on first use.
|
||||
#[must_use]
|
||||
pub const fn new(identity: BrepIdentity<'a>) -> Self {
|
||||
pub const fn plain(base: BrepIdentity<'a>) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
base,
|
||||
proven_keys: None,
|
||||
cache: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Queries of `b` with `identity` as the base naming, overriding edge/face keys where
|
||||
/// `b` records a proven producer geometry-owner key (`Brep::geometry_owner_keys`) that
|
||||
/// differs from `identity.keys`. Unowned (`None`) and foreign-owned geometry not
|
||||
/// recorded in `b.geometry_owner_keys()` is still excluded.
|
||||
/// `base`, overriding edge/face keys where `b` records a proven producer
|
||||
/// geometry-owner key (`Brep::geometry_owner_keys`) that differs from `base.keys`
|
||||
/// for geometry actually owned by it. Unowned (`None`) and foreign-owned geometry
|
||||
/// not recorded in `b.geometry_owner_keys()` is still excluded.
|
||||
#[must_use]
|
||||
pub fn with_proven_owners(identity: BrepIdentity<'a>, b: &Brep) -> Self {
|
||||
let base_keys = identity.keys;
|
||||
pub fn new(base: BrepIdentity<'a>, b: &Brep) -> Self {
|
||||
let base_keys = base.keys;
|
||||
let mut overrides = std::collections::BTreeMap::new();
|
||||
for (&entity, &proven) in b.geometry_owner_keys() {
|
||||
let matches_base = match entity {
|
||||
@@ -513,42 +478,138 @@ impl<'a> BrepQuery<'a> {
|
||||
.unwrap_or_else(|| base_keys(object))
|
||||
}) as ProvenKeyFn<'a>
|
||||
});
|
||||
Self { base, proven_keys }
|
||||
}
|
||||
|
||||
/// The base identities, without the overrides.
|
||||
#[must_use]
|
||||
pub const fn base(&self) -> BrepIdentity<'a> {
|
||||
self.base
|
||||
}
|
||||
|
||||
/// The effective identities: the base with the proven overrides applied.
|
||||
#[must_use]
|
||||
pub fn identity(&self) -> BrepIdentity<'_> {
|
||||
BrepIdentity {
|
||||
brep_id: self.base.brep_id,
|
||||
keys: match &self.proven_keys {
|
||||
Some(keys) => keys.as_ref(),
|
||||
None => self.base.keys,
|
||||
},
|
||||
owner_of: self.base.owner_of,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The cache-object identities of a [`NamingScheme::Notify`] B-rep
|
||||
/// ([`crate::topology::loopuse::notify_ref`] for its edges, faces and vertices).
|
||||
fn notify_identity(object: CacheObject) -> ObjectRef {
|
||||
crate::topology::brep::p2::caller_cache_key(&crate::topology::loopuse::notify_ref, object)
|
||||
}
|
||||
|
||||
impl Default for BrepIdentity<'_> {
|
||||
/// [`BrepIdentity::arena`] of the first B-rep of the caller's store (a caller
|
||||
/// querying one B-rep at a time).
|
||||
fn default() -> Self {
|
||||
BrepIdentity::arena(BrepId::from_index(0))
|
||||
}
|
||||
}
|
||||
|
||||
/// The ray directions of [`BrepQuery::point_3d_classify`], in firing order: each axis
|
||||
/// forward, then backward when the forward ray fails.
|
||||
const CLASSIFY_RAYS: [(Vec3, Vec3); 3] = [
|
||||
(Vec3::new(1.0, 0.0, 0.0), Vec3::new(-1.0, 0.0, 0.0)),
|
||||
(Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, -1.0, 0.0)),
|
||||
(Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, -1.0)),
|
||||
];
|
||||
|
||||
/// A B-rep's queries over its cache: the identities and the `SM_OC_BREP` cache, which
|
||||
/// upstream builds when a query first needs it and keeps on the B-rep
|
||||
/// (`SmSAGObject::CacheMakeOrValidate`). Keep one per B-rep state and reuse it across
|
||||
/// queries; edits to the B-rep invalidate it.
|
||||
pub struct BrepQuery<'a> {
|
||||
identity: ProvenBrepIdentity<'a>,
|
||||
cache: OnceCell<BrepCache>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for BrepQuery<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("BrepQuery")
|
||||
.field("identity", &self.identity)
|
||||
.field("cache", &self.cache)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BrepQuery<'static> {
|
||||
/// The queries of a B-rep with the default identities ([`BrepIdentity::default`]).
|
||||
fn default() -> Self {
|
||||
Self::new(BrepIdentity::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl BrepQuery<'static> {
|
||||
/// Queries of `b` at [`BrepId`] `0`, resolving records by `b`'s naming scheme and
|
||||
/// proven producer geometry-owner identities (`Brep::geometry_owner_keys`).
|
||||
#[must_use]
|
||||
pub fn of_brep(b: &Brep) -> Self {
|
||||
Self::of_brep_with_id(BrepId::from_index(0), b)
|
||||
}
|
||||
|
||||
/// Queries of `b` at `brep_id`, resolving records by `b`'s naming scheme and proven
|
||||
/// producer geometry-owner identities (`Brep::geometry_owner_keys`).
|
||||
#[must_use]
|
||||
pub fn of_brep_with_id(brep_id: BrepId, b: &Brep) -> Self {
|
||||
Self::from_proven(BrepIdentity::of_brep_with_id(brep_id, b))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BrepQuery<'a> {
|
||||
/// The queries of the B-rep with `identity`; its cache is built on first use.
|
||||
#[must_use]
|
||||
pub const fn new(identity: BrepIdentity<'a>) -> Self {
|
||||
Self::from_proven(ProvenBrepIdentity::plain(identity))
|
||||
}
|
||||
|
||||
/// The queries of the B-rep with `identity` (proven overrides included).
|
||||
#[must_use]
|
||||
pub const fn from_proven(identity: ProvenBrepIdentity<'a>) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
proven_keys,
|
||||
cache: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Queries of `b` with `identity` as the base naming, overriding edge/face keys where
|
||||
/// `b` records a proven producer geometry-owner key (`Brep::geometry_owner_keys`)
|
||||
/// ([`ProvenBrepIdentity::new`]).
|
||||
#[must_use]
|
||||
pub fn with_proven_owners(identity: BrepIdentity<'a>, b: &Brep) -> Self {
|
||||
Self::from_proven(ProvenBrepIdentity::new(identity, b))
|
||||
}
|
||||
|
||||
/// The queries over a cache the caller already holds for the B-rep.
|
||||
#[must_use]
|
||||
pub fn with_cache(identity: BrepIdentity<'a>, cache: BrepCache) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
proven_keys: None,
|
||||
identity: ProvenBrepIdentity::plain(identity),
|
||||
cache: OnceCell::from(cache),
|
||||
}
|
||||
}
|
||||
|
||||
/// The identities.
|
||||
/// The base identities (without proven overrides).
|
||||
#[must_use]
|
||||
pub const fn identity(&self) -> BrepIdentity<'a> {
|
||||
self.identity
|
||||
self.identity.base()
|
||||
}
|
||||
|
||||
/// The effective [`BrepIdentity`], including any proven geometry-owner overrides
|
||||
/// recorded on the B-rep when built with [`Self::of_brep`] or
|
||||
/// [`Self::with_proven_owners`].
|
||||
/// [`Self::with_proven_owners`] (the same as [`BrepIdentity::of_brep`]'s
|
||||
/// [`ProvenBrepIdentity::identity`]).
|
||||
#[must_use]
|
||||
pub fn effective_identity(&self) -> BrepIdentity<'_> {
|
||||
BrepIdentity {
|
||||
brep_id: self.identity.brep_id,
|
||||
keys: match &self.proven_keys {
|
||||
Some(keys) => keys.as_ref(),
|
||||
None => self.identity.keys,
|
||||
},
|
||||
owner_of: self.identity.owner_of,
|
||||
}
|
||||
self.identity.identity()
|
||||
}
|
||||
|
||||
/// The cache of `b`, built keyed by the identities when first asked for.
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
//! #1440: `BrepIdentity::of_brep` of a copy made by `api_brep_copy` under caller keys
|
||||
//! resolves the copy's geometry owners (its recorded `geometry_owner_keys`), so a face
|
||||
//! closest-point query over the copy finds the face, not only its vertices.
|
||||
|
||||
use solidcore::geom::attribute::ObjectKey;
|
||||
use solidcore::geom::object::{ObjectRef, Shape};
|
||||
use solidcore::kernel::{Kernel, KernelOptions};
|
||||
use solidcore::math::Vec3;
|
||||
use solidcore::primitive::api_primitives::create_box;
|
||||
use solidcore::query::api_queries::face_closest_point_in_shape;
|
||||
use solidcore::query::brep::{BrepIdentity, LiveBrepObjects};
|
||||
use solidcore::topology::brep::p1::faces;
|
||||
use solidcore::topology::brep::{p2, p3};
|
||||
use solidcore::{Brep, BrepId, EntityRef};
|
||||
|
||||
/// A caller naming: the notification layout shifted by `base`.
|
||||
fn named(e: EntityRef, base: u64) -> ObjectRef {
|
||||
let (type_code, kind, index) = match e {
|
||||
EntityRef::Brep(x) => (16011, 0u64, x.index()),
|
||||
EntityRef::Region(x) => (16012, 1, x.index()),
|
||||
EntityRef::Shell(x) => (16013, 2, x.index()),
|
||||
EntityRef::Faceuse(x) => (16022, 3, x.index()),
|
||||
EntityRef::Face(x) => (16002, 4, x.index()),
|
||||
EntityRef::Loopuse(x) => (16004, 5, x.index()),
|
||||
EntityRef::Loop(x) => (16003, 6, x.index()),
|
||||
EntityRef::Edgeuse(x) => (16005, 7, x.index()),
|
||||
EntityRef::Edge(x) => (16016, 8, x.index()),
|
||||
EntityRef::Vertexuse(x) => (16006, 9, x.index()),
|
||||
EntityRef::Vertex(x) => (16021, 10, x.index()),
|
||||
};
|
||||
ObjectRef {
|
||||
type_code,
|
||||
key: ObjectKey(base + 1 + (kind << 32) + u64::from(index)),
|
||||
}
|
||||
}
|
||||
|
||||
const ONE: BrepId = BrepId::from_index(0);
|
||||
|
||||
#[test]
|
||||
fn face_closest_point_on_a_caller_keyed_copy() {
|
||||
let mut k = Kernel::new(&KernelOptions::default());
|
||||
let b = create_box(&mut k, Vec3::new(0.0, 0.0, 0.0), 30.0, 20.0, 10.0).unwrap();
|
||||
let mut w = {
|
||||
let mut state = p2::ArenaNotifyState::new(&Brep::new());
|
||||
let mut env = state.env(None);
|
||||
let key_of = |e| named(e, 100_000_000);
|
||||
env.key_of = &key_of;
|
||||
p3::api_brep_copy(&mut k, &mut env, Some(&b), &|e| named(e, 0)).unwrap()
|
||||
};
|
||||
let ids = BrepIdentity::of_brep(&w);
|
||||
let identity = ids.identity();
|
||||
// 10 above the centre of the top face (z = 10)
|
||||
let p = Vec3::new(15.0, 10.0, 20.0);
|
||||
let mut found = Vec::new();
|
||||
for f in faces(&w, None).unwrap() {
|
||||
let shape = Shape::new(&mut k, &(ONE, &w), &[], &[(ONE, f)], &[], &[]).unwrap();
|
||||
let objects = LiveBrepObjects::new(&mut w, &identity, None).unwrap();
|
||||
let d = face_closest_point_in_shape(&k, &shape, &objects, (ONE, f), p).unwrap();
|
||||
found.push((d.distance, d.point));
|
||||
}
|
||||
found.sort_by(|a, b| a.0.total_cmp(&b.0));
|
||||
let side_y = 200.0_f64.sqrt(); // y = 0 / y = 20 faces: their top edge
|
||||
let side_x = 325.0_f64.sqrt(); // x = 0 / x = 30 faces: their top edge
|
||||
let expected = [10.0, side_y, side_y, side_x, side_x, 20.0];
|
||||
for ((d, _), e) in found.iter().zip(expected) {
|
||||
assert!((d - e).abs() <= 1e-9 * e, "distance {d}, expected {e}");
|
||||
}
|
||||
let (top, point) = found[0];
|
||||
assert_eq!(top, 10.0);
|
||||
assert_eq!(point, Vec3::new(15.0, 10.0, 10.0));
|
||||
assert_eq!(found[5].1, Vec3::new(15.0, 10.0, 0.0));
|
||||
}
|
||||
Reference in New Issue
Block a user