cadmpeg-codec-step 0.6.8

Read and write ISO 10303-21 STEP Part 21 files for AP203, AP214, and AP242.
Documentation
// SPDX-License-Identifier: Apache-2.0
//! STEP target resolution and export reporting.

use cadmpeg_core::CodecError;
use cadmpeg_ir::codec::write::{
    target::ResolvedWrite, ArenaCoverage, ArenaDisposition, ArenaDispositions, Consumption,
    EncodeInput, ExportBody, WritePath,
};

use crate::export::write_step_outcome;
use crate::loss::StepLossCode;
use crate::options::StepSchema;
use crate::StepCodec;

/// Why this writer cannot reproduce a source schema outside
/// [`StepSchema::TARGETS`].
///
/// STEP has no retained-image path, and every schema this writer emits stamps
/// object-identifier arcs, so an edition-unspecified or unrecognized
/// declaration cannot be written back.
const OFF_CATALOG_SOURCE_REASON: &str =
    "the semantic writer cannot synthesize it, and writing another schema would change what the \
     file declares; name a target to choose one";

/// Resolve the request against the source and synthesize the selected schema.
pub(crate) fn plan(
    codec: &StepCodec,
    input: EncodeInput<'_>,
    resolved: &ResolvedWrite<'_>,
) -> Result<ExportBody, CodecError> {
    let Some(index) = resolved.index() else {
        return Err(resolved.unavailable(OFF_CATALOG_SOURCE_REASON));
    };
    let schema = StepSchema::from_target_index(index).ok_or_else(|| {
        resolved.unavailable("the resolved target index is outside the STEP schema catalog")
    })?;
    let mut bytes = Vec::new();
    let recovered = cadmpeg_ir::codec::write::edge_recovery::recover_edge_curves(input.ir)?;
    let outcome = write_step_outcome(
        &recovered.document,
        &mut bytes,
        schema,
        &codec.options,
        recovered.recovered_edges,
    )?;
    let mut losses = outcome.losses;
    if !recovered.sampled_agreements.is_empty() {
        losses.push(StepLossCode::EdgePcurveAgreementSampled.note(format!(
            "{} recovered edge(s) have coedge curves compared at five parameters; agreement between samples is not established",
            recovered.sampled_agreements.len()
        )));
    }
    if !recovered.sampled_fits.is_empty() {
        losses.push(StepLossCode::EdgePcurveSampledToBspline.note(format!(
            "{} recovered edge(s) were fitted as B-splines with sampled residual at most {} model length units",
            recovered.sampled_fits.len(), recovered.residual_limit
        )));
    }
    if let Some(message) = resolved.displacement_message() {
        losses.push(StepLossCode::SourceDialectDisplaced.note(message));
    }
    Ok(ExportBody {
        bytes,
        census: outcome.census,
        write_path: WritePath::Synthesized {
            // STEP has no retained image: a provided fidelity is never replayed.
            consumption: Consumption::NotConsumed,
        },
        coverage: ArenaCoverage::Declared(coverage(schema)),
        losses,
        notes: outcome.notes,
    })
}

/// What the STEP writer does with each model arena.
///
/// `Builder::note_unrepresented` in `export.rs` charges a specific loss for
/// every arena or record the graph does not carry. Arenas charged as a whole
/// are `Dropped`.
fn coverage(schema: StepSchema) -> ArenaDispositions {
    use ArenaDisposition::{Dropped, Omitted, Reported, Written};
    let ap242 = |supported: bool| if supported { Written } else { Reported };
    ArenaDispositions {
        bodies: Written,
        regions: Written,
        shells: Written,
        faces: Written,
        loops: Written,
        coedges: Written,
        edges: Written,
        vertices: Written,
        points: Written,
        surfaces: Written,
        curves: Written,
        subds: Dropped,
        pcurves: Written,
        procedural_surfaces: Written,
        procedural_curves: Written,
        assets: Dropped,
        features: Dropped,
        feature_input_topologies: Dropped,
        feature_result_topologies: Dropped,
        configurations: Dropped,
        parameters: Dropped,
        sketches: Dropped,
        sketch_entities: Dropped,
        sketch_constraints: Dropped,
        spatial_sketches: Dropped,
        spatial_sketch_entities: Dropped,
        spatial_sketch_constraints: Dropped,
        spreadsheets: Dropped,
        product_definitions: Written,
        occurrences: Written,
        assembly_joints: Dropped,
        drawings: Dropped,
        semantic_annotations: Dropped,
        presentation_documents: Dropped,
        view_presentations: Dropped,
        tessellations: ap242(schema.supports_tessellation()),
        point_clouds: Omitted,
        appearances: Written,
        appearance_bindings: Written,
        attributes: Dropped,
        pmi: ap242(schema.supports_semantic_pmi()),
        presentation_layers: Written,
        physical_materials: Omitted,
        mass_properties: Omitted,
        rigid_bodies: Omitted,
        colliders: Omitted,
        articulations: Omitted,
        physics_scenes: Omitted,
        solver_settings: Omitted,
        declared_measures: Omitted,
    }
}