ocs_doc_api: Phase 5 (media & misc, read-mostly)

- entity_kind_name names the media/misc families (RasterImage, Table, Leader,
  MultiLeader, MLine, Mesh, Helix, Region, Body, Surface, Face3D, Dimension,
  Hatch) so GetEntity reports a real kind instead of 'Other'.
- RasterImage coarse bounds (insertion + u*width + v*height).
- Generic delete already works on these families; no typed create in v1
  (read-only EntityView per plan).
- spec RasterImage/Table read-mostly families; IMPLEMENTATION.md updated.
- Test phase5_media_entities_read_kind_and_bounds -> 601 host lib green.
This commit is contained in:
Sebastian committed 2026-09-06 01:22:28 +02:00
1 parent 7a481c6ce2
commit f24c0b2d75
6 files changed
+110 -4

No files matched your search

+37
View File
@@ -679,6 +679,43 @@ mod tests {
assert!((v.center.x - 60.0).abs() < 1e-6);
}
// ── Phase 5: media & misc (read-mostly) ──────────────────────────────────
#[test]
fn phase5_media_entities_read_kind_and_bounds() {
let mut app = OpenCADStudio::new_for_test();
let mut host = HostSession::new(&mut app, 0);
// Insert a RasterImage directly (read-mostly: DocApi reads kind + bounds).
let img = {
use acadrust::entities::RasterImage;
use acadrust::types::{Vector2, Vector3};
let mut img = RasterImage::default();
img.insertion_point = Vector3::new(10.0, 20.0, 0.0);
img.u_vector = Vector3::new(0.5, 0.0, 0.0); // 0.5 world-units/pixel in X
img.v_vector = Vector3::new(0.0, 0.5, 0.0);
img.size = Vector2::new(100.0, 50.0);
host.document_mut().add_entity(EntityType::RasterImage(img)).unwrap()
};
let img_id = handle_to_obj(img);
// GetEntity reports kind "RasterImage"; bounds = insertion + u*100 + v*50 = (60, 45).
let receipt = dispatch(&mut host, DocApiEnvelope::queries(vec![
Query::GetEntity { id: img_id }, Query::GetBounds { id: img_id }])).unwrap();
match &receipt.query_results[0] {
QueryResult::Entity(e) => assert_eq!(e.kind, "RasterImage"),
other => panic!("expected entity, got {other:?}"),
}
match &receipt.query_results[1] {
QueryResult::Bounds(b) => {
assert!((b.min[0] - 10.0).abs() < 1e-6 && (b.max[0] - 60.0).abs() < 1e-6, "{b:?}");
assert!((b.min[1] - 20.0).abs() < 1e-6 && (b.max[1] - 45.0).abs() < 1e-6, "{b:?}");
}
other => panic!("expected bounds, got {other:?}"),
}
// Generic delete works on media entities (read-mostly, but deletable).
dispatch(&mut host, DocApiEnvelope::op(Operation::Delete { id: img_id })).unwrap();
assert!(host.document().get_entity(img).is_none());
}
// ── Phase 3: containers (block references) ───────────────────────────────
#[test]
+24
View File
@@ -250,6 +250,20 @@ pub fn entity_kind_name(entity: &EntityType) -> &'static str {
EntityType::Viewport(_) => "Viewport",
EntityType::Text(_) => "Text",
EntityType::MText(_) => "MText",
// Media & misc (phase 5, read-mostly): named so GetEntity reports a real kind.
EntityType::RasterImage(_) => "RasterImage",
EntityType::Table(_) => "Table",
EntityType::Leader(_) => "Leader",
EntityType::MultiLeader(_) => "MultiLeader",
EntityType::MLine(_) => "MLine",
EntityType::Mesh(_) => "Mesh",
EntityType::Helix(_) => "Helix",
EntityType::Region(_) => "Region",
EntityType::Body(_) => "Body",
EntityType::Surface(_) => "Surface",
EntityType::Face3D(_) => "Face3D",
EntityType::Dimension(_) => "Dimension",
EntityType::Hatch(_) => "Hatch",
_ => "Other",
}
}
@@ -329,6 +343,16 @@ pub fn entity_bounds(entity: Option<&EntityType>, id: ObjectId) -> ApiResult<Aab
vp.center.z,
],
},
EntityType::RasterImage(img) => {
// Coarse image rect: origin + u*width + v*height (in world units).
let w = img.u_vector.x * img.size.x + img.v_vector.x * img.size.y;
let h = img.u_vector.y * img.size.x + img.v_vector.y * img.size.y;
let (x0, y0) = (img.insertion_point.x, img.insertion_point.y);
Aabb {
min: [x0.min(x0 + w), y0.min(y0 + h), img.insertion_point.z],
max: [x0.max(x0 + w), y0.max(y0 + h), img.insertion_point.z],
}
}
// Ray/XLine are unbounded by definition — no finite bounds.
_ => return Err(ApiError::Unsupported("bounds for this entity family is not yet implemented".into())),
};