Something went wrong. Try again.
atproto Thingiverse but good
Something went wrong. Try again.
29 kB · 891 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892use crate::bfc::{BfcFrame, BfcState};use crate::cache::{CacheKey, NormalizedPath};use crate::geom::{Bounds3, ColourCode, GeometryRecord, Transform, deserialize_optional_bounds3};use crate::mpd::OwnedVirtualFile;use crate::scanner::{LineType, Token, TokenKind};use crate::texmap::{TexmapAssociation, TexmapEvent, TexmapState, TextureDescriptor};use crate::types::{Diagnostic, LimitCounters, SCHEMA_VERSION, Span};use serde::{Deserialize, Serialize};use smol_str::SmolStr;use std::borrow::Cow;use std::collections::BTreeMap;use std::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]pub struct ColourData { pub r: u8, pub g: u8, pub b: u8, pub alpha: Option<u8>, pub luminance: Option<u8>,}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]pub enum ColourEdge { Code(ColourCode), Data(ColourData),}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]#[serde(rename_all = "snake_case")]pub enum ColourFinish { Chrome, Pearlescent, Rubber, MatteMetallic, Metal,}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]#[serde(rename_all = "snake_case")]pub enum ColourFabricKind { Velvet, Canvas, String, Fur,}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]#[serde(rename_all = "snake_case")]pub enum ColourMaterialKind { Glitter, Speckle, Fabric { variant: Option<ColourFabricKind> },}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct ColourMaterial<'src> { /// The source spelling of the material kind, retained for compatibility. #[serde(borrow)] pub kind: Cow<'src, str>, pub typed_kind: ColourMaterialKind, pub value: Option<ColourData>, pub fraction: Option<f64>, pub vfraction: Option<f64>, pub size: Option<f64>, pub min_size: Option<f64>, pub max_size: Option<f64>, #[serde(borrow)] pub extra: Vec<Cow<'src, str>>,}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct Colour<'src> { pub code: ColourCode, #[serde(borrow)] pub name: Option<Cow<'src, str>>, pub data: ColourData, pub edge: ColourEdge, #[serde(default)] pub finishes: Vec<ColourFinish>, #[serde(default)] pub material: Option<ColourMaterial<'src>>,}
const FIRST_LOCAL_COLOUR_SLOT: u32 = 512;const LAST_LOCAL_COLOUR_SLOT: u32 = 65_534;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]pub struct ColourSlotError;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct ColourTable<'src> { #[serde(borrow)] entries: BTreeMap<ColourCode, Colour<'src>>, local_slots: BTreeMap<ColourCode, u16>, next_local_slot: u32,}
impl<'src> Default for ColourTable<'src> { fn default() -> Self { Self::new() }}
impl<'src> ColourTable<'src> { pub fn new() -> Self { Self { entries: BTreeMap::new(), local_slots: BTreeMap::new(), next_local_slot: FIRST_LOCAL_COLOUR_SLOT, } }
pub fn record(&mut self, colour: Colour<'src>) { self.entries.insert(colour.code, colour); }
pub fn get(&self, code: impl Into<ColourCode>) -> Option<&Colour<'src>> { self.entries.get(&code.into()) }
pub fn find(&self, code: impl Into<ColourCode>, find_edge: bool) -> Option<&ColourData> { let code = code.into(); let colour = self.entries.get(&code)?; if !find_edge { return Some(&colour.data); } match &colour.edge { ColourEdge::Data(data) => Some(data), ColourEdge::Code(edge_code) => self.entries.get(edge_code).map(|edge| &edge.data), } }
pub fn local_slot(&self, code: impl Into<ColourCode>) -> Option<u16> { self.local_slots.get(&code.into()).copied() }
pub fn len(&self) -> usize { self.entries.len() }
pub fn is_empty(&self) -> bool { self.entries.is_empty() }
pub(crate) fn merge_scope(&mut self, scope: ColourScope<'src>) { self.entries.extend(scope.committed.into_iter().flatten()); self.local_slots.extend(scope.local_slots); self.next_local_slot = scope.next_local_slot; }
pub(crate) fn next_local_slot(&self) -> u32 { self.next_local_slot }
pub(crate) fn into_owned(self) -> ColourTable<'static> { ColourTable { entries: self .entries .into_iter() .map(|(code, colour)| { ( code, Colour { code: colour.code, name: colour.name.map(|name| Cow::Owned(name.into_owned())), data: colour.data, edge: colour.edge, finishes: colour.finishes, material: colour.material.map(|material| ColourMaterial { kind: Cow::Owned(material.kind.into_owned()), typed_kind: material.typed_kind, value: material.value, fraction: material.fraction, vfraction: material.vfraction, size: material.size, min_size: material.min_size, max_size: material.max_size, extra: material .extra .into_iter() .map(|value| Cow::Owned(value.into_owned())) .collect(), }), }, ) }) .collect(), local_slots: self.local_slots, next_local_slot: self.next_local_slot, } }}
/// File-local transactional colour definitions.////// Records are invisible until `commit`. Dropping or rolling back a scope/// discards both definitions and provisional local slots, so failed parses do/// not affect later virtual files or parses.#[derive(Clone, Debug, PartialEq)]pub struct ColourScope<'src> { committed: Vec<BTreeMap<ColourCode, Colour<'src>>>, active: BTreeMap<ColourCode, Colour<'src>>, local_slots: BTreeMap<ColourCode, u16>, active_slot_start: u32, next_local_slot: u32,}
impl<'src> ColourScope<'src> { pub fn new() -> Self { Self::with_first_local_slot(FIRST_LOCAL_COLOUR_SLOT) }
pub(crate) fn with_first_local_slot(first_local_slot: u32) -> Self { Self { committed: Vec::new(), active: BTreeMap::new(), local_slots: BTreeMap::new(), active_slot_start: first_local_slot, next_local_slot: first_local_slot, } }
pub fn record(&mut self, colour: Colour<'src>) -> Result<u16, ColourSlotError> { let code = colour.code; if let Some(slot) = self.local_slots.get(&code).copied() { self.active.insert(code, colour); return Ok(slot); } let slot = self.allocate_local_slot()?; self.local_slots.insert(code, slot); self.active.insert(code, colour); Ok(slot) }
pub fn commit(&mut self) { if !self.active.is_empty() { self.committed.push(std::mem::take(&mut self.active)); } self.active_slot_start = self.next_local_slot; }
pub fn rollback(&mut self) { self.active.clear(); self.local_slots .retain(|_, slot| u32::from(*slot) < self.active_slot_start); self.next_local_slot = self.active_slot_start; }
pub fn has_uncommitted(&self) -> bool { !self.active.is_empty() }
pub fn get(&self, code: impl Into<ColourCode>) -> Option<&Colour<'src>> { if !self.active.is_empty() { return None; } let code = code.into(); self.committed.iter().rev().find_map(|map| map.get(&code)) }
pub fn find(&self, code: impl Into<ColourCode>, find_edge: bool) -> Option<&ColourData> { let mut code = code.into(); for (map_index, map) in self.committed.iter().enumerate().rev() { let Some(colour) = map.get(&code) else { continue; }; if !find_edge { return Some(&colour.data); } match &colour.edge { ColourEdge::Data(data) => return Some(data), ColourEdge::Code(edge_code) => { if let Some(edge) = map.get(edge_code) { return Some(&edge.data); } code = *edge_code; for older_map in self.committed[..map_index].iter().rev() { if let Some(edge) = older_map.get(&code) { return Some(&edge.data); } } return None; } } } None }
pub fn local_slot(&self, code: impl Into<ColourCode>) -> Option<u16> { self.local_slots.get(&code.into()).copied() }
fn allocate_local_slot(&mut self) -> Result<u16, ColourSlotError> { if self.next_local_slot > LAST_LOCAL_COLOUR_SLOT { return Err(ColourSlotError); } let slot = u16::try_from(self.next_local_slot).map_err(|_| ColourSlotError)?; self.next_local_slot += 1; Ok(slot) }}
impl Default for ColourScope<'_> { fn default() -> Self { Self::new() }}
pub(crate) fn parse_colour<'src>(tokens: &[Token<'src>]) -> Option<Colour<'src>> { if tokens.len() < 9 || tokens.first()?.kind != TokenKind::Zero || tokens.get(1)?.kind != TokenKind::Colour || tokens.get(2)?.kind != TokenKind::Identifier { return None; } let mut index = 3; if !matches_keyword(tokens.get(index), TokenKind::Code) { return None; } let code = parse_colour_code(tokens.get(index + 1)?.text)?; index += 2; if !matches_keyword(tokens.get(index), TokenKind::Value) { return None; } let (r, g, b) = parse_rgb(tokens.get(index + 1)?.text)?; index += 2; if !matches_keyword(tokens.get(index), TokenKind::Edge) { return None; } let edge_token = tokens.get(index + 1)?.text; let edge = parse_rgb(edge_token) .map(|(r, g, b)| { ColourEdge::Data(ColourData { r, g, b, alpha: None, luminance: None, }) }) .or_else(|| parse_colour_code(edge_token).map(ColourEdge::Code))?; index += 2;
let mut data = ColourData { r, g, b, alpha: None, luminance: None, }; let mut finishes = Vec::new(); let mut material = None; while index < tokens.len() { match tokens[index].kind { TokenKind::Alpha => { if data.alpha.is_some() { return None; } data.alpha = Some(parse_byte(tokens.get(index + 1)?.text)?); index += 2; } TokenKind::Luminance => { if data.luminance.is_some() { return None; } data.luminance = Some(parse_byte(tokens.get(index + 1)?.text)?); index += 2; } TokenKind::Chrome | TokenKind::Pearlescent | TokenKind::Rubber | TokenKind::MatteMetallic | TokenKind::Metal => { if !finishes.is_empty() || material.is_some() { return None; } finishes.push(match tokens[index].kind { TokenKind::Chrome => ColourFinish::Chrome, TokenKind::Pearlescent => ColourFinish::Pearlescent, TokenKind::Rubber => ColourFinish::Rubber, TokenKind::MatteMetallic => ColourFinish::MatteMetallic, TokenKind::Metal => ColourFinish::Metal, _ => unreachable!(), }); index += 1; } TokenKind::Material => { if !finishes.is_empty() || material.is_some() { return None; } material = Some(parse_material(&tokens[index + 1..])?); index = tokens.len(); } _ => return None, } } Some(Colour { code, name: Some(Cow::Borrowed(tokens[2].text)), data, edge, finishes, material, })}
fn matches_keyword(token: Option<&Token<'_>>, expected: TokenKind) -> bool { token.is_some_and(|token| token.kind == expected)}
fn parse_material<'src>(tokens: &[Token<'src>]) -> Option<ColourMaterial<'src>> { let kind_token = tokens.first()?; let typed_kind = match kind_token.text { "GLITTER" => ColourMaterialKind::Glitter, "SPECKLE" => ColourMaterialKind::Speckle, "FABRIC" => ColourMaterialKind::Fabric { variant: None }, _ => return None, }; if matches!(typed_kind, ColourMaterialKind::Fabric { .. }) { let variant = tokens.get(1).map(|token| match token.text { "VELVET" => Some(ColourFabricKind::Velvet), "CANVAS" => Some(ColourFabricKind::Canvas), "STRING" => Some(ColourFabricKind::String), "FUR" => Some(ColourFabricKind::Fur), _ => None, }); if tokens.len() > 2 || variant.is_some_and(|variant| variant.is_none()) { return None; } return Some(ColourMaterial { kind: Cow::Borrowed(kind_token.text), typed_kind: ColourMaterialKind::Fabric { variant: variant.flatten(), }, value: None, fraction: None, vfraction: None, size: None, min_size: None, max_size: None, extra: Vec::new(), }); } let mut value = None; let mut fraction = None; let mut vfraction = None; let mut size = None; let mut min_size = None; let mut max_size = None; let mut index = 1; while index < tokens.len() { let key = tokens[index].text; if index + 1 >= tokens.len() { return None; } let value_token = &tokens[index + 1]; match key { "VALUE" if value.is_none() => { let (r, g, b) = parse_rgb(value_token.text)?; value = Some(ColourData { r, g, b, alpha: None, luminance: None, }); } "ALPHA" if value.is_some() && value.as_ref()?.alpha.is_none() => { value.as_mut()?.alpha = Some(parse_byte(value_token.text)?); } "LUMINANCE" if value.is_some() && value.as_ref()?.luminance.is_none() => { value.as_mut()?.luminance = Some(parse_byte(value_token.text)?); } "FRACTION" if fraction.is_none() => fraction = Some(parse_fraction(value_token.text)?), "VFRACTION" if vfraction.is_none() => { vfraction = Some(parse_fraction(value_token.text)?) } "SIZE" if size.is_none() => size = Some(parse_positive(value_token.text)?), "MINSIZE" if min_size.is_none() => min_size = Some(parse_positive(value_token.text)?), "MAXSIZE" if max_size.is_none() => max_size = Some(parse_positive(value_token.text)?), _ => return None, } index += 2; } let is_glitter = matches!(typed_kind, ColourMaterialKind::Glitter); let is_speckle = matches!(typed_kind, ColourMaterialKind::Speckle); if value.is_none() || fraction.is_none() { return None; } if is_glitter { if vfraction.is_none() || size.is_none() || min_size.is_some() || max_size.is_some() { return None; } } else if is_speckle && (vfraction.is_some() || size.is_some() || min_size.is_none() || max_size.is_none()) { return None; } if size.is_some() == (min_size.is_some() || max_size.is_some()) || min_size.is_some() != max_size.is_some() { return None; } Some(ColourMaterial { kind: Cow::Borrowed(kind_token.text), typed_kind, value, fraction, vfraction, size, min_size, max_size, extra: Vec::new(), })}
fn parse_colour_code(text: &str) -> Option<ColourCode> { (!text.is_empty() && text.bytes().all(|byte| byte.is_ascii_digit())) .then(|| text.parse::<u32>().ok()) .flatten() .map(ColourCode)}
fn parse_byte(text: &str) -> Option<u8> { (!text.is_empty() && text.bytes().all(|byte| byte.is_ascii_digit())) .then(|| text.parse::<u16>().ok()) .flatten() .and_then(|value| u8::try_from(value).ok())}
fn parse_fraction(text: &str) -> Option<f64> { let value = text.parse::<f64>().ok()?; (value > 0.0 && value < 1.0 && value.is_finite()).then_some(value)}
fn parse_positive(text: &str) -> Option<f64> { let value = text.parse::<f64>().ok()?; (value > 0.0 && value.is_finite()).then_some(value)}
fn parse_rgb(text: &str) -> Option<(u8, u8, u8)> { let hex = text.strip_prefix('#').or_else(|| text.strip_prefix("0x"))?; if hex.len() != 6 { return None; } Some(( u8::from_str_radix(&hex[0..2], 16).ok()?, u8::from_str_radix(&hex[2..4], 16).ok()?, u8::from_str_radix(&hex[4..6], 16).ok()?, ))}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]pub struct SyntaxRecord<'src> { pub line_type: Option<LineType>, #[serde(borrow)] pub raw_tail: Cow<'src, str>, pub span: Span, #[serde(borrow)] pub line_ending: Cow<'src, str>, #[serde(borrow)] pub fields: Vec<Cow<'src, str>>,}impl<'src> SyntaxRecord<'src> { pub(crate) fn into_owned(self) -> SyntaxRecord<'static> { SyntaxRecord { line_type: self.line_type, raw_tail: Cow::Owned(self.raw_tail.into_owned()), span: self.span, line_ending: Cow::Owned(self.line_ending.into_owned()), fields: self .fields .into_iter() .map(|field| Cow::Owned(field.into_owned())) .collect(), } }}#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]pub struct DataPayload { pub filename: String, pub bytes: Vec<u8>,}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]pub struct SemanticRecord<'src> { #[serde(borrow)] pub canonical_path: Option<Cow<'src, str>>, #[serde(borrow)] pub root_identity: Option<Cow<'src, str>>, #[serde(borrow)] pub target_identity: Option<Cow<'src, str>>, #[serde(borrow)] pub cache_identity: Option<Cow<'src, str>>, pub bfc_state: BfcState, pub colour_state: ColourCode, pub steps: Vec<String>, #[serde(borrow)] pub limits: Vec<Cow<'src, str>>, pub texmap_events: Vec<TexmapEvent<'src>>, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub texmap_geometry: Vec<crate::texmap::TexmapGeometryAssociation>, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub data_payloads: Vec<DataPayload>,}impl<'src> SemanticRecord<'src> { pub(crate) fn into_owned(self) -> SemanticRecord<'static> { SemanticRecord { canonical_path: self .canonical_path .map(|value| Cow::Owned(value.into_owned())), root_identity: self .root_identity .map(|value| Cow::Owned(value.into_owned())), target_identity: self .target_identity .map(|value| Cow::Owned(value.into_owned())), cache_identity: self .cache_identity .map(|value| Cow::Owned(value.into_owned())), bfc_state: self.bfc_state, colour_state: self.colour_state, steps: self.steps, limits: self .limits .into_iter() .map(|value| Cow::Owned(value.into_owned())) .collect(), texmap_events: self .texmap_events .into_iter() .map(TexmapEvent::into_owned) .collect(), texmap_geometry: self.texmap_geometry, data_payloads: self.data_payloads, } }}#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]pub struct ModelId(pub u32);
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct InstanceRecord { pub model: ModelId, pub instance_id: SmolStr, #[serde(default)] pub parent: Option<u32>, pub source_model: SmolStr, pub target_model: SmolStr, #[serde(default)] pub source_span: Option<Span>, #[serde(default)] pub target_key: Option<CacheKey>, pub transform: Transform, pub inverted: bool, #[serde(default)] pub bfc: Option<BfcFrame>, #[serde(default)] pub direct_colour: Option<ColourCode>, #[serde(default)] pub colour: Option<ColourCode>, #[serde(default)] pub texmap_state: Option<TexmapState>, #[serde(default)] pub texmap_reference: Option<String>, #[serde(default)] pub texmap: Option<TexmapAssociation>,}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct SceneRecord { pub model_id: SmolStr, pub instance_ids: Vec<SmolStr>, #[serde(default)] pub instances: Vec<InstanceRecord>, pub triangles: u64, pub quads: u64, pub lines: u64, pub conditional_lines: u64, #[serde(deserialize_with = "deserialize_optional_bounds3")] pub bounds: Option<Bounds3>, pub reflection: bool,}#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct CanonicalRecord<'src> { #[serde(borrow)] pub schema_version: Cow<'src, str>, pub syntax: Vec<SyntaxRecord<'src>>, pub semantic: SemanticRecord<'src>, pub scene: SceneRecord, pub diagnostics: Vec<Diagnostic>, pub provenance_id: Cow<'src, str>,}
impl<'src> CanonicalRecord<'src> { pub fn into_owned(self) -> CanonicalRecord<'static> { CanonicalRecord { schema_version: Cow::Owned(self.schema_version.into_owned()), syntax: self .syntax .into_iter() .map(SyntaxRecord::into_owned) .collect(), semantic: self.semantic.into_owned(), scene: self.scene, diagnostics: self.diagnostics, provenance_id: Cow::Owned(self.provenance_id.into_owned()), } }}
#[derive(Clone, Debug)]pub(crate) struct DataPayloadBuilder { pub(crate) filename: String, pub(crate) encoded: String, pub(crate) span: Span,}
#[derive(Clone, Debug)]pub(crate) struct Include<'src> { pub(crate) name: Cow<'src, str>, pub transform: Transform, pub(crate) span: Span, pub(crate) inverted: bool, pub(crate) bfc: BfcFrame, pub(crate) colour: ColourCode, pub(crate) texmap_state: TexmapState, pub(crate) texmap: Option<TextureDescriptor<'src>>, pub(crate) texmap_association: Option<TexmapAssociation>,}
#[derive(Clone, Debug)]pub(crate) enum ModelEvent<'src> { Step, Indices(u64), Include(Cow<'src, str>),}
#[derive(Clone, Debug)]pub(crate) struct ModelData<'src> { pub(crate) file_id: u32, pub(crate) path: NormalizedPath, pub(crate) key: CacheKey, pub(crate) bfc: BfcFrame, pub(crate) colour: ColourCode, pub(crate) steps: Vec<Cow<'src, str>>, /// LDParse-compatible direct-mesh index offsets recorded at each STEP. /// This is distinct from the root semantic step labels used by policy. pub(crate) step_ends: Vec<u64>, pub(crate) mesh_index_count: u64, pub(crate) events: Vec<ModelEvent<'src>>, pub(crate) texmap_state: TexmapState, pub(crate) texmap_descriptor: Option<TextureDescriptor<'src>>, pub(crate) texmap_stack: Vec<(TexmapState, Option<TextureDescriptor<'src>>, bool)>, pub(crate) texmap_next_span: Option<Span>, pub(crate) texmap_fallback_seen: bool, pub(crate) texmap_events: Vec<TexmapEvent<'src>>, pub(crate) texmap_geometry: Vec<crate::texmap::TexmapGeometryAssociation>, pub(crate) data_payload: Option<DataPayloadBuilder>, pub(crate) data_payloads: Vec<DataPayload>, pub(crate) includes: Vec<Include<'src>>, pub(crate) triangles: u64, pub(crate) quads: u64, pub(crate) lines: u64, pub(crate) conditional_lines: u64, pub(crate) geometry: Vec<GeometryRecord>, pub(crate) bounds: Option<Bounds3>,}
pub struct ParseResult<'src> { pub syntax: Vec<SyntaxRecord<'src>>, pub semantic: SemanticRecord<'src>, pub scene: SceneRecord, pub diagnostics: Vec<Diagnostic>, pub counters: LimitCounters, pub files: Vec<OwnedVirtualFile>, pub models: Vec<ModelSummary>, pub colours: ColourTable<'src>, pub provenance_id: Cow<'src, str>,}
/// An owned parse result. The retained semantic result and its reservation are/// one object: projection borrows or clones data while this owner remains live.pub struct OwnedParseResult { pub result: ParseResult<'static>,}
impl fmt::Debug for OwnedParseResult { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter .debug_struct("OwnedParseResult") .field("schema_version", &SCHEMA_VERSION) .finish() }}
impl std::ops::Deref for OwnedParseResult { type Target = ParseResult<'static>;
fn deref(&self) -> &Self::Target { &self.result }}
impl OwnedParseResult { pub(crate) fn from_parse_result(result: ParseResult<'_>) -> Self { Self { result: result.into_owned(), } }
pub fn project(&self) -> CanonicalRecord<'static> { self.result.project() }
/// Move both the canonical projection and its retained arena owner together. pub fn into_projection(self) -> OwnedProjection { OwnedProjection { canonical: self.result.project(), } }}
pub struct OwnedProjection { pub canonical: CanonicalRecord<'static>,}
impl OwnedProjection { pub fn project(&self) -> &CanonicalRecord<'static> { &self.canonical }}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct LdMeshSemantic { pub positions: Vec<[f64; 3]>, pub normals: Option<Vec<[f64; 3]>>, pub indices: Vec<u32>, pub bf_indices: Vec<u32>, pub step_offsets: Vec<usize>,}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]pub struct ModelSummary { pub model: ModelId, pub id: SmolStr, pub path: SmolStr, pub cache_key: CacheKey, pub depth: u64, pub include_spans: Vec<Span>, pub includes: Vec<SmolStr>, pub triangles: u64, pub quads: u64, pub lines: u64, pub conditional_lines: u64, /// LDParse-authoritative direct-mesh index offsets for this model's steps. pub step_ends: Vec<usize>, pub bfc: BfcState,}impl<'src> ParseResult<'src> { pub fn project(&self) -> CanonicalRecord<'src> { CanonicalRecord { schema_version: Cow::Borrowed(SCHEMA_VERSION), syntax: self.syntax.clone(), semantic: self.semantic.clone(), scene: self.scene.clone(), diagnostics: self.diagnostics.clone(), provenance_id: self.provenance_id.clone(), } }
pub(crate) fn into_owned(self) -> ParseResult<'static> { ParseResult { syntax: self .syntax .into_iter() .map(SyntaxRecord::into_owned) .collect(), semantic: self.semantic.into_owned(), scene: self.scene, diagnostics: self.diagnostics, counters: self.counters, files: self.files, models: self.models, colours: self.colours.into_owned(), provenance_id: Cow::Owned(self.provenance_id.into_owned()), } }}