use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)]
pub struct PresentationId(pub String);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct CameraState {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position: Option<[f64; 3]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub orientation: Option<[f64; 4]>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub properties: BTreeMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct PresentationState {
pub kind: String,
pub order: u32,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub attributes: BTreeMap<String, String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub assets: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct PresentationDocument {
pub id: PresentationId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub schema_version: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub active_view: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub camera: Option<CameraState>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub states: Vec<PresentationState>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub native_ref: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ViewPresentation {
pub id: PresentationId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub object: Option<String>,
pub order: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expanded: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub visible: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selection_style: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub line_width: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub point_size: Option<f64>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub properties: BTreeMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub native_ref: Option<String>,
}
use crate::ids::{
BodyId, CurveId, EdgeId, FaceId, LayerId, OccurrenceId, PmiId, PointId, ProductId, SurfaceId,
VertexId,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum PresentationItem {
Body {
body: BodyId,
},
Face {
face: FaceId,
},
Edge {
edge: EdgeId,
},
Vertex {
vertex: VertexId,
},
Point {
point: PointId,
},
Curve {
curve: CurveId,
},
Surface {
surface: SurfaceId,
},
Product {
product: ProductId,
},
Occurrence {
occurrence: OccurrenceId,
},
Pmi {
annotation: PmiId,
},
Tessellation {
tessellation: String,
},
Source {
source_id: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct PresentationLayer {
pub id: LayerId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub items: Vec<PresentationItem>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::document::CadIr;
use crate::report::Check;
use crate::units::Units;
use crate::validate;
#[test]
fn source_layer_items_validate_without_fabricated_geometry() {
let mut ir = CadIr::empty(Units::default());
ir.model.presentation_layers.push(PresentationLayer {
id: LayerId("test:presentation:layer#construction".into()),
name: "construction".into(),
description: None,
items: vec![PresentationItem::Source {
source_id: "#42".into(),
}],
});
assert!(validate(&ir, Vec::new()).is_ok());
}
#[test]
fn missing_typed_layer_item_is_invalid() {
let mut ir = CadIr::empty(Units::default());
ir.model.presentation_layers.push(PresentationLayer {
id: LayerId("test:presentation:layer#missing".into()),
name: "missing".into(),
description: None,
items: vec![PresentationItem::Face {
face: FaceId("test:model:face#missing".into()),
}],
});
assert!(validate(&ir, Vec::new())
.findings
.iter()
.any(|finding| finding.check == Check::Presentation));
}
}