forked from 1b-agent/opencadstudio-upstream
ocs_doc_api: Phase 4 (paper-space viewports)
- Operation::CreateViewport(ViewportSpec) appended to the wire enum (append-only). - backend add_viewport (center/width/height/view_target/view_height). - Viewport bounds (center +- w/2,h/2), transform (moves center), ensure_transformable. - facade EntityCollection::create_viewport; spec Viewport family; api_reference + binding_schema regenerated; mocks updated. - Test phase4_create_viewport_bounds_transform -> 600 host lib green. - IMPLEMENTATION.md: Phase 4 done (set_view/view-query remaining).
This commit is contained in:
1 parent
b03f68737e
commit
7a481c6ce2
13 files changed
+150
-5
No files matched your search
@@ -125,6 +125,20 @@ impl DocApiBackend for HostSession<'_> {
|
||||
Ok(handle_to_obj(handle))
|
||||
}
|
||||
|
||||
fn add_viewport(&mut self, spec: &ocs_doc_api::ops::ViewportSpec) -> ApiResult<ObjectId> {
|
||||
use acadrust::entities::Viewport;
|
||||
use acadrust::types::Vector3;
|
||||
let v3 = |p: [f64; 3]| Vector3::new(p[0], p[1], p[2]);
|
||||
let mut vp = Viewport::new();
|
||||
vp.center = v3(spec.center);
|
||||
vp.width = spec.width;
|
||||
vp.height = spec.height;
|
||||
vp.view_target = v3(spec.view_target);
|
||||
vp.view_height = spec.view_height;
|
||||
let handle = self.scene_mut().add_entity(EntityType::Viewport(vp));
|
||||
Ok(handle_to_obj(handle))
|
||||
}
|
||||
|
||||
fn add_vertex(&mut self, id: ObjectId, at: usize, point: [f64; 3]) -> ApiResult<()> {
|
||||
let handle = obj_to_handle(id);
|
||||
let Some(entity) = self.document().get_entity(handle).cloned() else {
|
||||
@@ -183,6 +197,7 @@ impl DocApiBackend for HostSession<'_> {
|
||||
| EntityType::Ray(_)
|
||||
| EntityType::XLine(_)
|
||||
| EntityType::Insert(_)
|
||||
| EntityType::Viewport(_)
|
||||
| EntityType::Point(_)
|
||||
| EntityType::LwPolyline(_)
|
||||
);
|
||||
@@ -626,6 +641,44 @@ mod tests {
|
||||
assert!((r.base_point.z - 5.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
// ── Phase 4: paper-space viewports ───────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn phase4_create_viewport_bounds_transform() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let mut host = HostSession::new(&mut app, 0);
|
||||
// A 40x30 paper-space viewport at (50,50,0) looking at model origin.
|
||||
let vp = new_id(&dispatch(&mut host, DocApiEnvelope::op(Operation::CreateViewport(
|
||||
ocs_doc_api::ops::ViewportSpec {
|
||||
center: [50.0, 50.0, 0.0], width: 40.0, height: 30.0,
|
||||
view_target: [0.0, 0.0, 0.0], view_height: 100.0,
|
||||
}))).unwrap());
|
||||
let handle = obj_to_handle(vp);
|
||||
let Some(EntityType::Viewport(v)) = host.document().get_entity(handle) else { panic!("viewport not found") };
|
||||
assert!((v.width - 40.0).abs() < 1e-9 && (v.view_height - 100.0).abs() < 1e-9);
|
||||
|
||||
// Bounds = center ± width/2, height/2.
|
||||
let receipt = dispatch(&mut host, DocApiEnvelope::queries(vec![
|
||||
Query::GetEntity { id: vp }, Query::GetBounds { id: vp }])).unwrap();
|
||||
match &receipt.query_results[0] {
|
||||
QueryResult::Entity(e) => assert_eq!(e.kind, "Viewport"),
|
||||
other => panic!("expected entity, got {other:?}"),
|
||||
}
|
||||
match &receipt.query_results[1] {
|
||||
QueryResult::Bounds(b) => {
|
||||
assert!((b.min[0] - 30.0).abs() < 1e-6 && (b.max[0] - 70.0).abs() < 1e-6, "{b:?}");
|
||||
assert!((b.min[1] - 35.0).abs() < 1e-6 && (b.max[1] - 65.0).abs() < 1e-6, "{b:?}");
|
||||
}
|
||||
other => panic!("expected bounds, got {other:?}"),
|
||||
}
|
||||
|
||||
// Transform moves the viewport's paper-space center.
|
||||
dispatch(&mut host, DocApiEnvelope::op(Operation::Transform {
|
||||
id: vp, placement: ocs_doc_api::PlacementSpec::at([10.0, 0.0, 0.0]) })).unwrap();
|
||||
let Some(EntityType::Viewport(v)) = host.document().get_entity(handle) else { panic!("viewport not found") };
|
||||
assert!((v.center.x - 60.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
// ── Phase 3: containers (block references) ───────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -129,6 +129,11 @@ pub fn transform_entity_geometry(
|
||||
ins.insert_point = apply(ins.insert_point);
|
||||
EntityType::Insert(ins)
|
||||
}
|
||||
EntityType::Viewport(vp) => {
|
||||
let mut vp = vp.clone();
|
||||
vp.center = apply(vp.center);
|
||||
EntityType::Viewport(vp)
|
||||
}
|
||||
EntityType::Point(pt) => {
|
||||
let mut pt = pt.clone();
|
||||
pt.location = apply(pt.location);
|
||||
@@ -242,6 +247,7 @@ pub fn entity_kind_name(entity: &EntityType) -> &'static str {
|
||||
EntityType::Ray(_) => "Ray",
|
||||
EntityType::XLine(_) => "XLine",
|
||||
EntityType::Insert(_) => "Insert",
|
||||
EntityType::Viewport(_) => "Viewport",
|
||||
EntityType::Text(_) => "Text",
|
||||
EntityType::MText(_) => "MText",
|
||||
_ => "Other",
|
||||
@@ -311,6 +317,18 @@ pub fn entity_bounds(entity: Option<&EntityType>, id: ObjectId) -> ApiResult<Aab
|
||||
}
|
||||
Aabb { min, max }
|
||||
}
|
||||
EntityType::Viewport(vp) => Aabb {
|
||||
min: [
|
||||
vp.center.x - vp.width / 2.0,
|
||||
vp.center.y - vp.height / 2.0,
|
||||
vp.center.z,
|
||||
],
|
||||
max: [
|
||||
vp.center.x + vp.width / 2.0,
|
||||
vp.center.y + vp.height / 2.0,
|
||||
vp.center.z,
|
||||
],
|
||||
},
|
||||
// Ray/XLine are unbounded by definition — no finite bounds.
|
||||
_ => return Err(ApiError::Unsupported("bounds for this entity family is not yet implemented".into())),
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user