forked from 1b-agent/opencadstudio-upstream
ocs_doc_api: read-mostly family bounds (Leader/Mesh/Face3D/MLine/Helix)
- entity_bounds: vertex/point-carrying read-mostly families (Leader.vertices, Mesh.vertices, Face3D 4 corners, MLine vertex positions, Helix start point) via a points-to-AABB helper. Region/Body/Surface/MultiLeader stay Unsupported. - IMPLEMENTATION.md: read-mostly bounds done. - Test read_mostly_family_bounds_leader_and_mesh -> 618 host lib green.
This commit is contained in:
1 parent
6997dfe618
commit
28c8e0ef1a
3 files changed
+77
-2
No files matched your search
@@ -106,8 +106,12 @@ tested: 615 host lib + 8 crate unit + 3 spec↔wire consistency tests green.
|
||||
- **Done:** media/misc families report a **real `kind`** via `GetEntity`
|
||||
(`RasterImage`, `Table`, `Leader`, `MultiLeader`, `MLine`, `Mesh`, `Helix`,
|
||||
`Region`, `Body`, `Surface`, `Face3D`, `Dimension`, `Hatch`) instead of "Other";
|
||||
`RasterImage` has coarse bounds (insertion + u·width + v·height); generic
|
||||
`delete` works on all. Covered by `phase5_*` test.
|
||||
generic `delete` works on all. Covered by `phase5_*` test.
|
||||
- **Done (read-mostly bounds):** `bounds()` now works for the vertex/point-carrying
|
||||
families — `Leader`, `Mesh`, `Face3D` (4 corners), `MLine` (vertex positions),
|
||||
`Helix` (start point) — via a points-to-AABB helper (`read_mostly_family_bounds_*`
|
||||
test). `RasterImage` has 4-corner bounds. `Region`/`Body`/`Surface`/`MultiLeader`
|
||||
stay `Unsupported` for bounds (complex/nested geometry).
|
||||
- **Done (typed create):** `create_raster_image(file_path, insertion, u, v, size)`
|
||||
→ `Operation::CreateRasterImage` (host auto-registers the ImageDefinition), and
|
||||
`create_table(insertion_point, data[row][col])` → `Operation::CreateTable`
|
||||
|
||||
@@ -1301,6 +1301,54 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Read-mostly family bounds (Leader/Mesh/Face3D/MLine/Helix) ──────────
|
||||
|
||||
#[test]
|
||||
fn read_mostly_family_bounds_leader_and_mesh() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let mut host = HostSession::new(&mut app, 0);
|
||||
// Leader with vertices at (0,0,0),(5,5,0),(10,0,0) -> bounds x[0,10] y[0,5].
|
||||
let mut leader = acadrust::entities::Leader::default();
|
||||
leader.vertices = vec![
|
||||
acadrust::types::Vector3::new(0.0, 0.0, 0.0),
|
||||
acadrust::types::Vector3::new(5.0, 5.0, 0.0),
|
||||
acadrust::types::Vector3::new(10.0, 0.0, 0.0),
|
||||
];
|
||||
let leader_h = host.document_mut().add_entity(EntityType::Leader(leader)).unwrap();
|
||||
// Mesh with vertices in a unit cube at (2..3, 2..3, 0).
|
||||
let mut mesh = acadrust::entities::Mesh::default();
|
||||
mesh.vertices = vec![
|
||||
acadrust::types::Vector3::new(2.0, 2.0, 0.0),
|
||||
acadrust::types::Vector3::new(3.0, 2.0, 0.0),
|
||||
acadrust::types::Vector3::new(3.0, 3.0, 0.0),
|
||||
acadrust::types::Vector3::new(2.0, 3.0, 0.0),
|
||||
];
|
||||
let mesh_h = host.document_mut().add_entity(EntityType::Mesh(mesh)).unwrap();
|
||||
|
||||
let receipt = dispatch(&mut host, DocApiEnvelope::queries(vec![
|
||||
Query::GetBounds { id: handle_to_obj(leader_h) },
|
||||
Query::GetBounds { id: handle_to_obj(mesh_h) },
|
||||
Query::GetEntity { id: handle_to_obj(leader_h) },
|
||||
])).unwrap();
|
||||
match &receipt.query_results[0] {
|
||||
QueryResult::Bounds(b) => {
|
||||
assert!((b.min[0] - 0.0).abs() < 1e-6 && (b.max[0] - 10.0).abs() < 1e-6, "{b:?}");
|
||||
assert!((b.min[1] - 0.0).abs() < 1e-6 && (b.max[1] - 5.0).abs() < 1e-6, "{b:?}");
|
||||
}
|
||||
other => panic!("expected bounds, got {other:?}"),
|
||||
}
|
||||
match &receipt.query_results[1] {
|
||||
QueryResult::Bounds(b) => {
|
||||
assert!((b.min[0] - 2.0).abs() < 1e-6 && (b.max[0] - 3.0).abs() < 1e-6, "{b:?}");
|
||||
}
|
||||
other => panic!("expected bounds, got {other:?}"),
|
||||
}
|
||||
match &receipt.query_results[2] {
|
||||
QueryResult::Entity(e) => assert_eq!(e.kind, "Leader"),
|
||||
other => panic!("expected entity, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 5 remaining: typed raster image create ─────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -473,8 +473,31 @@ pub fn entity_bounds(entity: Option<&EntityType>, id: ObjectId) -> ApiResult<Aab
|
||||
}
|
||||
Aabb { min, max }
|
||||
}
|
||||
// Read-mostly families with vertex/point data: bounds from their points.
|
||||
EntityType::Leader(l) => points_aabb(l.vertices.iter().collect(), id)?,
|
||||
EntityType::Mesh(m) => points_aabb(m.vertices.iter().collect(), id)?,
|
||||
EntityType::Face3D(f) => points_aabb(
|
||||
[&f.first_corner, &f.second_corner, &f.third_corner, &f.fourth_corner].into_iter().collect(),
|
||||
id,
|
||||
)?,
|
||||
EntityType::MLine(m) => points_aabb(m.vertices.iter().map(|v| &v.position).collect(), id)?,
|
||||
EntityType::Helix(h) => points_aabb([&h.start_point].into_iter().collect(), id)?,
|
||||
// Ray/XLine are unbounded by definition — no finite bounds.
|
||||
_ => return Err(ApiError::Unsupported("bounds for this entity family is not yet implemented".into())),
|
||||
};
|
||||
Ok(bb)
|
||||
}
|
||||
|
||||
/// AABB over a set of 3D points. `UnknownId` if empty (no points to bound).
|
||||
fn points_aabb(pts: Vec<&Vector3>, id: ObjectId) -> ApiResult<Aabb> {
|
||||
if pts.is_empty() {
|
||||
return Err(ApiError::UnknownId(id));
|
||||
}
|
||||
let mut min = [f64::INFINITY; 3];
|
||||
let mut max = [f64::NEG_INFINITY; 3];
|
||||
for p in pts {
|
||||
min = [min[0].min(p.x), min[1].min(p.y), min[2].min(p.z)];
|
||||
max = [max[0].max(p.x), max[1].max(p.y), max[2].max(p.z)];
|
||||
}
|
||||
Ok(Aabb { min, max })
|
||||
}
|
||||
Reference in new issue
Block a user