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`
|
- **Done:** media/misc families report a **real `kind`** via `GetEntity`
|
||||||
(`RasterImage`, `Table`, `Leader`, `MultiLeader`, `MLine`, `Mesh`, `Helix`,
|
(`RasterImage`, `Table`, `Leader`, `MultiLeader`, `MLine`, `Mesh`, `Helix`,
|
||||||
`Region`, `Body`, `Surface`, `Face3D`, `Dimension`, `Hatch`) instead of "Other";
|
`Region`, `Body`, `Surface`, `Face3D`, `Dimension`, `Hatch`) instead of "Other";
|
||||||
`RasterImage` has coarse bounds (insertion + u·width + v·height); generic
|
generic `delete` works on all. Covered by `phase5_*` test.
|
||||||
`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)`
|
- **Done (typed create):** `create_raster_image(file_path, insertion, u, v, size)`
|
||||||
→ `Operation::CreateRasterImage` (host auto-registers the ImageDefinition), and
|
→ `Operation::CreateRasterImage` (host auto-registers the ImageDefinition), and
|
||||||
`create_table(insertion_point, data[row][col])` → `Operation::CreateTable`
|
`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 ─────────────────────────
|
// ── Phase 5 remaining: typed raster image create ─────────────────────────
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -473,8 +473,31 @@ pub fn entity_bounds(entity: Option<&EntityType>, id: ObjectId) -> ApiResult<Aab
|
|||||||
}
|
}
|
||||||
Aabb { min, max }
|
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.
|
// Ray/XLine are unbounded by definition — no finite bounds.
|
||||||
_ => return Err(ApiError::Unsupported("bounds for this entity family is not yet implemented".into())),
|
_ => return Err(ApiError::Unsupported("bounds for this entity family is not yet implemented".into())),
|
||||||
};
|
};
|
||||||
Ok(bb)
|
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