Skip to main content

sim_lib_view_interference/
surface.rs

1//! Ranked `SurfaceCodec` registration and reversible operation compilation.
2
3use 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
15/// Stable registry id for the interference study surface.
16pub const INTERFERENCE_SURFACE_CODEC_ID: &str = "surface:interference";
17
18/// Authority required to realize a projection edit.
19pub const INTERFERENCE_PROJECT_CAPABILITY: &str = "interference/project";
20
21/// Authority required to realize a model edit that performs a new solve.
22pub const INTERFERENCE_SOLVE_CAPABILITY: &str = "interference/solve";
23
24/// Absolute number of pre-projected animation frames accepted by one request.
25pub const MAX_ANIMATION_FRAMES: usize = 120;
26
27/// Returns the surface registry symbol.
28pub fn surface_interference_codec_symbol() -> Symbol {
29    Symbol::new(INTERFERENCE_SURFACE_CODEC_ID)
30}
31
32/// Installs the published interference record Shapes and registers one ranked
33/// surface codec claiming exactly `interference/Study`.
34pub 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/// Reversible codec for complete, certified interference studies.
48#[derive(Clone, Copy, Debug, Default)]
49pub struct InterferenceSurfaceCodec;
50
51impl InterferenceSurfaceCodec {
52    /// Builds the stateless interference surface codec.
53    pub const fn new() -> Self {
54        Self
55    }
56
57    /// Pre-projects a bounded cycle of instantaneous-field Scenes.
58    ///
59    /// Every frame goes through the domain detector reducer. The total frame
60    /// budget is divided before projection, and zero or excessive frame counts
61    /// fail before any frame allocation.
62    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}