ifc_lite_wasm/api/
appearance_transfer.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4use super::IfcAPI;
5use ifc_lite_processing::appearance::{
6    plan_mesh_transfer, plan_point_transfer, MeshTransferPlan, MeshTransferRequest,
7    TransferPointPayload,
8};
9use wasm_bindgen::prelude::*;
10
11#[wasm_bindgen]
12impl IfcAPI {
13    /// Registered mesh observations over canonical target albedo. Host verifies
14    /// original GLB identity against decoded source mesh/image and freezes frames.
15    /// Run in an owned cancellable worker. IFPA output adds `transfer` coverage;
16    /// `plan` is null when no sample is observed. Never infer accuracy approval.
17    #[wasm_bindgen(js_name = planMeshTransfer)]
18    pub fn plan_mesh_transfer(
19        &self,
20        content: &[u8],
21        request_json: &str,
22        rgba: &[u8],
23    ) -> Result<Vec<u8>, JsError> {
24        let request = parse(request_json)?;
25        encode(plan_mesh_transfer(content, &request, rgba)).map_err(|e| JsError::new(&e))
26    }
27    /// Registered RGB point-cloud observations (#4381). `request_json.source`
28    /// is `{kind:'points', …}`; `positions` (3n f64, source-frame metres) and
29    /// `colors` (3n RGB8) are the payload, `normals` (3n f32, oriented) and
30    /// `stations` (n indices into `source.viewpoints`) are empty when absent.
31    /// `rgba` carries only the target's existing rasters. Same output as
32    /// `planMeshTransfer`; `transfer.source` records the orientation used.
33    // Flat typed-array arguments are the wasm-bindgen boundary: each buffer
34    // crosses once without a JSON or struct wrapper (as export_glb does).
35    #[allow(clippy::too_many_arguments)]
36    #[wasm_bindgen(js_name = planPointTransfer)]
37    pub fn plan_point_transfer(
38        &self,
39        content: &[u8],
40        request_json: &str,
41        rgba: &[u8],
42        positions: &[f64],
43        colors: &[u8],
44        normals: &[f32],
45        stations: &[u32],
46    ) -> Result<Vec<u8>, JsError> {
47        let request = parse(request_json)?;
48        let payload = TransferPointPayload { positions, colors, normals, stations };
49        encode(plan_point_transfer(content, &request, rgba, &payload)).map_err(|e| JsError::new(&e))
50    }
51}
52fn parse(request_json: &str) -> Result<MeshTransferRequest, JsError> {
53    if request_json.len() > 64 * 1024 * 1024 {
54        return Err(JsError::new("Transfer request exceeds 64 MiB"));
55    }
56    serde_json::from_str(request_json)
57        .map_err(|e| JsError::new(&format!("Invalid transfer request: {e}")))
58}
59fn encode(result: Result<MeshTransferPlan, String>) -> Result<Vec<u8>, String> {
60    let result = result?;
61    super::appearance_atlas::encode_atlas(
62        result.output.as_ref(),
63        result.texels_per_metre,
64        &serde_json::json!({"transfer":result.transfer}),
65    )
66}