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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! Retained source records for lossy file-format curve imports.
//!
//! Import adapters sit on Yap's exact-computation boundary: finite file data
//! may be admitted, but its provenance and tolerance must remain visible until
//! certified predicates replace it. The records here are deliberately small
//! evidence objects for STEP/DXF/application import layers. They do not parse
//! those formats, and they do not make imported finite samples native topology.
//! See Chee Yap, "Towards Exact Geometric Computation," *Computational
//! Geometry* 7(1-2), 3-23 (1997).
use crate::{CurveError, CurveResult, RetainedTopologyStatus};
/// Source family for a lossy retained curve import record.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RetainedImportFormat2 {
/// Plain finite `f64` polyline input with no external file handle.
FinitePolyline,
/// STEP entity evidence, typically keyed by an entity id.
Step,
/// DXF entity evidence, typically keyed by a handle table index.
Dxf,
/// Application-local import evidence.
Application,
}
/// Source topology for a lossy retained curve import record.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RetainedImportTopology2 {
/// The source evidence was an open finite line string.
OpenLineString,
/// The source evidence was a closed finite ring.
ClosedRing,
}
/// Absolute/relative tolerance carried from an import source.
///
/// These are evidence values only. They may explain why the source was lossy,
/// but exact topology must still be decided by certified predicates rather than
/// by accepting a tolerance band as geometry.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RetainedSourceTolerance2 {
absolute: f64,
relative: f64,
}
/// Retained audit record for one imported finite curve carrier.
#[derive(Clone, Debug, PartialEq)]
pub struct RetainedImportRecord2 {
format: RetainedImportFormat2,
source_topology: RetainedImportTopology2,
source_index: u64,
source_tolerance: Option<RetainedSourceTolerance2>,
input_point_count: usize,
emitted_segment_count: usize,
discarded_duplicate_count: usize,
topology_status: RetainedTopologyStatus,
}
impl RetainedSourceTolerance2 {
/// Constructs finite nonnegative absolute/relative source tolerances.
pub fn try_new(absolute: f64, relative: f64) -> CurveResult<Self> {
if !absolute.is_finite() || !relative.is_finite() || absolute < 0.0 || relative < 0.0 {
return Err(CurveError::InvalidImportRecord);
}
Ok(Self { absolute, relative })
}
/// Returns the absolute source tolerance.
pub const fn absolute(self) -> f64 {
self.absolute
}
/// Returns the relative source tolerance.
pub const fn relative(self) -> f64 {
self.relative
}
/// Returns true when the source declared an exact zero tolerance.
pub const fn is_zero(self) -> bool {
self.absolute == 0.0 && self.relative == 0.0
}
}
impl RetainedImportRecord2 {
/// Constructs an open-line-string retained lossy-import audit record.
///
/// `discarded_duplicate_count` records finite duplicate samples that were
/// consumed as file/import metadata rather than emitted as zero-length
/// topology. The topology status is always [`RetainedTopologyStatus::ImportedLossy`]
/// because this record crosses a finite or external file-format boundary.
/// Use [`Self::try_new_closed_ring`] for closed-ring evidence.
pub fn try_new(
format: RetainedImportFormat2,
source_index: u64,
source_tolerance: Option<RetainedSourceTolerance2>,
input_point_count: usize,
emitted_segment_count: usize,
discarded_duplicate_count: usize,
) -> CurveResult<Self> {
Self::try_new_open_line_string(
format,
source_index,
source_tolerance,
input_point_count,
emitted_segment_count,
discarded_duplicate_count,
)
}
/// Constructs an open-line-string retained lossy-import audit record.
pub fn try_new_open_line_string(
format: RetainedImportFormat2,
source_index: u64,
source_tolerance: Option<RetainedSourceTolerance2>,
input_point_count: usize,
emitted_segment_count: usize,
discarded_duplicate_count: usize,
) -> CurveResult<Self> {
let edge_evidence_count = emitted_segment_count
.checked_add(discarded_duplicate_count)
.ok_or(CurveError::InvalidImportRecord)?;
if input_point_count < 2
|| emitted_segment_count == 0
|| edge_evidence_count != input_point_count - 1
{
return Err(CurveError::InvalidImportRecord);
}
Ok(Self::from_validated_counts(
format,
RetainedImportTopology2::OpenLineString,
source_index,
source_tolerance,
input_point_count,
emitted_segment_count,
discarded_duplicate_count,
))
}
/// Constructs a closed-ring retained lossy-import audit record.
pub fn try_new_closed_ring(
format: RetainedImportFormat2,
source_index: u64,
source_tolerance: Option<RetainedSourceTolerance2>,
input_point_count: usize,
emitted_segment_count: usize,
discarded_duplicate_count: usize,
) -> CurveResult<Self> {
let edge_evidence_count = emitted_segment_count
.checked_add(discarded_duplicate_count)
.ok_or(CurveError::InvalidImportRecord)?;
if input_point_count < 3
|| emitted_segment_count < 3
|| edge_evidence_count != input_point_count
{
return Err(CurveError::InvalidImportRecord);
}
Ok(Self::from_validated_counts(
format,
RetainedImportTopology2::ClosedRing,
source_index,
source_tolerance,
input_point_count,
emitted_segment_count,
discarded_duplicate_count,
))
}
fn from_validated_counts(
format: RetainedImportFormat2,
source_topology: RetainedImportTopology2,
source_index: u64,
source_tolerance: Option<RetainedSourceTolerance2>,
input_point_count: usize,
emitted_segment_count: usize,
discarded_duplicate_count: usize,
) -> Self {
Self {
format,
source_topology,
source_index,
source_tolerance,
input_point_count,
emitted_segment_count,
discarded_duplicate_count,
topology_status: RetainedTopologyStatus::ImportedLossy,
}
}
/// Constructs an open-line-string STEP import record.
pub fn step(
entity_id: u64,
source_tolerance: Option<RetainedSourceTolerance2>,
input_point_count: usize,
emitted_segment_count: usize,
discarded_duplicate_count: usize,
) -> CurveResult<Self> {
Self::try_new_open_line_string(
RetainedImportFormat2::Step,
entity_id,
source_tolerance,
input_point_count,
emitted_segment_count,
discarded_duplicate_count,
)
}
/// Constructs an open-line-string DXF import record.
pub fn dxf(
handle_index: u64,
source_tolerance: Option<RetainedSourceTolerance2>,
input_point_count: usize,
emitted_segment_count: usize,
discarded_duplicate_count: usize,
) -> CurveResult<Self> {
Self::try_new_open_line_string(
RetainedImportFormat2::Dxf,
handle_index,
source_tolerance,
input_point_count,
emitted_segment_count,
discarded_duplicate_count,
)
}
/// Returns the retained import format.
pub const fn format(&self) -> RetainedImportFormat2 {
self.format
}
/// Returns whether the retained source evidence was open or closed.
pub const fn source_topology(&self) -> RetainedImportTopology2 {
self.source_topology
}
/// Returns the opaque source index.
pub const fn source_index(&self) -> u64 {
self.source_index
}
/// Returns source tolerance evidence, if supplied by the importer.
pub const fn source_tolerance(&self) -> Option<RetainedSourceTolerance2> {
self.source_tolerance
}
/// Returns the number of finite input points.
pub const fn input_point_count(&self) -> usize {
self.input_point_count
}
/// Returns the number of native segments emitted from the import.
pub const fn emitted_segment_count(&self) -> usize {
self.emitted_segment_count
}
/// Returns duplicate finite edges discarded before topology construction.
pub const fn discarded_duplicate_count(&self) -> usize {
self.discarded_duplicate_count
}
/// Returns the topology readiness status for this retained import.
pub const fn topology_status(&self) -> RetainedTopologyStatus {
self.topology_status
}
}