1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
//! Generic row identity shared by batches, attributes and relations.
#[cfg(test)]
#[path = "rows_tests.rs"]
mod tests;
use crate::StructureHandle;
use crate::TemplatePartRef;
use crate::handle::{InstanceBatchHandle, PointBatchHandle, RelationBatchHandle};
use std::sync::Arc;
/// Caller-owned namespace for stable source-row identity.
///
/// This value is never reused or interpreted by the engine. Scene handles
/// remain generational and independent from this external namespace.
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct SourceNamespace(pub u64);
/// Stable source identity for one homogeneous row table.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SourceRows {
namespace: SourceNamespace,
row_count: u32,
keys: Option<Arc<[u64]>>,
}
impl SourceRows {
/// Uses row order as identity inside `namespace` without a key allocation.
#[must_use]
pub const fn ordered(namespace: SourceNamespace, row_count: u32) -> Self {
Self {
namespace,
row_count,
keys: None,
}
}
/// Retains explicit source keys without copying their shared allocation.
///
/// # Errors
///
/// Keys must be non-empty, unique and fit the portable row limit.
pub fn keyed(namespace: SourceNamespace, keys: Arc<[u64]>) -> Result<Self, crate::CoreError> {
let row_count =
u32::try_from(keys.len()).map_err(|_| invalid("source row count exceeds u32"))?;
if keys.is_empty() {
return Err(invalid("source keys must be non-empty"));
}
let mut seen = hashbrown::HashSet::with_capacity(keys.len());
if keys.iter().any(|key| !seen.insert(*key)) {
return Err(invalid("source keys must be unique within their namespace"));
}
Ok(Self {
namespace,
row_count,
keys: Some(keys),
})
}
/// External namespace shared by related source tables.
#[must_use]
pub const fn namespace(&self) -> SourceNamespace {
self.namespace
}
/// Number of rows in the table.
#[must_use]
pub const fn len(&self) -> u32 {
self.row_count
}
/// Whether this table is empty.
#[must_use]
pub const fn is_empty(&self) -> bool {
self.row_count == 0
}
/// Optional explicit keys in logical row order.
#[must_use]
pub fn keys(&self) -> Option<&Arc<[u64]>> {
self.keys.as_ref()
}
/// Resolves the external key for a row. Unkeyed rows use their exact index.
#[must_use]
pub fn key(&self, row: u32) -> Option<u64> {
if row >= self.row_count {
return None;
}
self.keys.as_ref().map_or_else(
|| Some(u64::from(row)),
|keys| keys.get(row as usize).copied(),
)
}
}
/// Exact homogeneous table targeted by attributes and selections.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum RowDomain {
/// Source atoms of one placed structure.
Atoms(StructureHandle),
/// Generic point rows.
Points(PointBatchHandle),
/// Rigid transform rows in one instance batch.
Instances(InstanceBatchHandle),
/// Shared analytic parts, flattened sphere-first then capsule.
TemplateParts(InstanceBatchHandle),
/// Generic relation rows.
Relations(RelationBatchHandle),
}
impl RowDomain {
/// Whether rows in this domain resolve directly to world-space positions.
#[must_use]
pub const fn is_spatial(self) -> bool {
!matches!(self, Self::Relations(_))
}
}
/// Exact scene entity identity: generational domain plus logical row.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct RowEntityRef {
domain: RowDomain,
row: u32,
}
/// Cold-path resolution of a flattened template-part picking row.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct TemplatePartPick {
reference: TemplatePartRef,
instance_source_key: u64,
part_source_key: u64,
}
impl TemplatePartPick {
pub(crate) const fn new(
reference: TemplatePartRef,
instance_source_key: u64,
part_source_key: u64,
) -> Self {
Self {
reference,
instance_source_key,
part_source_key,
}
}
/// Exact scene occurrence selected by the attachment.
#[must_use]
pub const fn reference(self) -> TemplatePartRef {
self.reference
}
/// External key of the rigid instance row.
#[must_use]
pub const fn instance_source_key(self) -> u64 {
self.instance_source_key
}
/// External key of the shared local template part.
#[must_use]
pub const fn part_source_key(self) -> u64 {
self.part_source_key
}
}
impl RowEntityRef {
/// Creates an unchecked reference. Scene APIs validate row and generation.
#[must_use]
pub const fn new(domain: RowDomain, row: u32) -> Self {
Self { domain, row }
}
/// Homogeneous table containing this row.
#[must_use]
pub const fn domain(self) -> RowDomain {
self.domain
}
/// Logical row within the domain.
#[must_use]
pub const fn row(self) -> u32 {
self.row
}
}
const fn invalid(reason: &'static str) -> crate::CoreError {
crate::CoreError::InvalidAttribute { reason }
}