sim_lib_view_interference/
surface.rs1use std::sync::Arc;
4
5use sim_kernel::{CapabilityName, Cx, Diagnostic, Error, Expr, Result, ShapeRef, Symbol};
6use sim_lib_interference_runtime::{
7 install_interference_records, projection_shape_symbol, study_shape_symbol,
8};
9use sim_lib_view::{
10 Draft, Lens, LensKind, LensMeta, LensRegistry, Operation, SurfaceCaps, SurfaceCodec,
11};
12
13use crate::intent::{self, EditClass};
14
15pub const INTERFERENCE_SURFACE_CODEC_ID: &str = "surface:interference";
17
18pub const INTERFERENCE_PROJECT_CAPABILITY: &str = "interference/project";
20
21pub const INTERFERENCE_SOLVE_CAPABILITY: &str = "interference/solve";
23
24pub const MAX_ANIMATION_FRAMES: usize = 120;
26
27pub fn surface_interference_codec_symbol() -> Symbol {
29 Symbol::new(INTERFERENCE_SURFACE_CODEC_ID)
30}
31
32pub fn register_interference_surface(cx: &mut Cx, registry: &mut LensRegistry) -> Result<()> {
35 install_interference_records(cx)?;
36 let study_shape = registered_shape(cx, study_shape_symbol())?;
37 let id = surface_interference_codec_symbol();
38 registry.register(Lens::metadata_only(
39 LensMeta::new(id.clone(), LensKind::View)
40 .claiming_shape(study_shape)
41 .with_quality_cost(240, 24),
42 ));
43 registry.register_surface_codec(id, Arc::new(InterferenceSurfaceCodec::new()));
44 Ok(())
45}
46
47#[derive(Clone, Copy, Debug, Default)]
49pub struct InterferenceSurfaceCodec;
50
51impl InterferenceSurfaceCodec {
52 pub const fn new() -> Self {
54 Self
55 }
56
57 pub fn encode_animation(
63 &self,
64 cx: &mut Cx,
65 value: &Expr,
66 caps: &SurfaceCaps,
67 frames: usize,
68 ) -> Result<Vec<Expr>> {
69 let study = intent::decode_study(cx, value)?;
70 crate::scene::animation_scenes(cx, &study, caps, frames)
71 }
72}
73
74impl SurfaceCodec for InterferenceSurfaceCodec {
75 fn encode(&self, cx: &mut Cx, value: &Expr, caps: &SurfaceCaps) -> Result<Expr> {
76 let study = intent::decode_study(cx, value)?;
77 crate::scene::study_scene(cx, &study, caps)
78 }
79
80 fn decode(&self, cx: &mut Cx, value: &Expr, submitted: &Expr) -> Result<Draft> {
81 let study = intent::decode_study(cx, value)?;
82 if let Err(error) = sim_lib_intent::validate_intent(submitted) {
83 return Ok(rejected(
84 value,
85 format!("invalid interference Intent: {error}"),
86 ));
87 }
88 match intent::classify_edit(cx, &study, value, submitted) {
89 Ok(edit) => Ok(Draft::clean(value.clone(), intent::encode_edit(cx, &edit)?)),
90 Err(error) => Ok(rejected(value, error.to_string())),
91 }
92 }
93
94 fn commit(&self, cx: &mut Cx, draft: &Draft) -> Result<Operation> {
95 if !draft.committable || !draft.diagnostics.is_empty() {
96 return Err(Error::HostError(
97 "interference draft is not committable".to_owned(),
98 ));
99 }
100 let study = intent::decode_study(cx, &draft.base)?;
101 let edit = intent::decode_edit(cx, &study, &draft.proposed)?;
102 match edit {
103 EditClass::Projection(request) => {
104 Ok(Operation::new(intent::project_form(&draft.base, &request))
105 .with_result_shape(registered_shape(cx, projection_shape_symbol())?)
106 .requiring(CapabilityName::new(INTERFERENCE_PROJECT_CAPABILITY)))
107 }
108 EditClass::Model(request) => Ok(Operation::new(intent::solve_form(cx, &request)?)
109 .with_result_shape(registered_shape(cx, study_shape_symbol())?)
110 .requiring(CapabilityName::new(INTERFERENCE_SOLVE_CAPABILITY))),
111 }
112 }
113}
114
115fn registered_shape(cx: &mut Cx, symbol: Symbol) -> Result<ShapeRef> {
116 install_interference_records(cx)?;
117 cx.registry()
118 .shape_by_symbol(&symbol)
119 .cloned()
120 .ok_or(Error::UnknownSymbol { symbol })
121}
122
123fn rejected(base: &Expr, message: String) -> Draft {
124 Draft::rejected(
125 base.clone(),
126 Diagnostic::error(message)
127 .with_code(Symbol::qualified("interference-surface", "invalid-edit")),
128 )
129}