quiddity 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. quiddity/__init__.py +380 -0
  2. quiddity/_adjacency.py +1379 -0
  3. quiddity/_analytic_surfaces.py +144 -0
  4. quiddity/_bevel.py +151 -0
  5. quiddity/_blend_view.py +765 -0
  6. quiddity/_body_geometry.py +2315 -0
  7. quiddity/_body_identity.py +72 -0
  8. quiddity/_candidates.py +1026 -0
  9. quiddity/_claims.py +283 -0
  10. quiddity/_corner_section.py +145 -0
  11. quiddity/_correspondence.py +405 -0
  12. quiddity/_correspondence_match.py +2292 -0
  13. quiddity/_correspondence_partition.py +662 -0
  14. quiddity/_cylinder_substrate.py +385 -0
  15. quiddity/_diagnostics.py +117 -0
  16. quiddity/_dispositions.py +278 -0
  17. quiddity/_effective_surfaces.py +1395 -0
  18. quiddity/_features.py +59 -0
  19. quiddity/_geometry.py +365 -0
  20. quiddity/_geometry_evidence.py +59 -0
  21. quiddity/_hole_features.py +1023 -0
  22. quiddity/_hole_patterns.py +286 -0
  23. quiddity/_open_channel_section.py +146 -0
  24. quiddity/_passage_compat.py +202 -0
  25. quiddity/_pattern_geometry.py +253 -0
  26. quiddity/_recess_core.py +979 -0
  27. quiddity/_recess_faces.py +308 -0
  28. quiddity/_recess_features.py +372 -0
  29. quiddity/_recess_obround.py +459 -0
  30. quiddity/_recess_patterns.py +187 -0
  31. quiddity/_recess_records.py +226 -0
  32. quiddity/_recess_reduce.py +358 -0
  33. quiddity/_reconcile.py +570 -0
  34. quiddity/_record.py +34 -0
  35. quiddity/_registry.py +1180 -0
  36. quiddity/_rings.py +337 -0
  37. quiddity/_run.py +98 -0
  38. quiddity/_section_adapters.py +332 -0
  39. quiddity/_section_passages.py +735 -0
  40. quiddity/_section_recess.py +896 -0
  41. quiddity/_section_recess_discovery.py +46 -0
  42. quiddity/_sections.py +734 -0
  43. quiddity/_typing.py +69 -0
  44. quiddity/_volume_probe.py +54 -0
  45. quiddity/angled_steps.py +296 -0
  46. quiddity/blends.py +574 -0
  47. quiddity/capabilities.json +2591 -0
  48. quiddity/capabilities.py +444 -0
  49. quiddity/census.py +125 -0
  50. quiddity/chamfers.py +297 -0
  51. quiddity/circular_blind_steps.py +304 -0
  52. quiddity/countersinks.py +284 -0
  53. quiddity/edge_open_circular_recesses.py +441 -0
  54. quiddity/edge_open_prismatic_recesses.py +414 -0
  55. quiddity/evidence.py +585 -0
  56. quiddity/evidence_api.json +34 -0
  57. quiddity/experimental_geometry.py +283 -0
  58. quiddity/explanations.py +235 -0
  59. quiddity/fillets.py +310 -0
  60. quiddity/flats.py +356 -0
  61. quiddity/frames.py +553 -0
  62. quiddity/grooves.py +360 -0
  63. quiddity/inspection.py +609 -0
  64. quiddity/inspection_api.json +454 -0
  65. quiddity/levels.py +672 -0
  66. quiddity/oriented_slots.py +269 -0
  67. quiddity/pads.py +845 -0
  68. quiddity/paired_ramp_steps.py +260 -0
  69. quiddity/passages.py +659 -0
  70. quiddity/plates.py +442 -0
  71. quiddity/polygonal_bosses.py +903 -0
  72. quiddity/prismatic_pockets.py +543 -0
  73. quiddity/profiled_bores.py +775 -0
  74. quiddity/py.typed +1 -0
  75. quiddity/rectangular_blind_slots.py +233 -0
  76. quiddity/repeating_profiles.py +471 -0
  77. quiddity/result.py +1410 -0
  78. quiddity/round_bottom_slots.py +605 -0
  79. quiddity/section_recesses.py +87 -0
  80. quiddity/slots.py +44 -0
  81. quiddity/step_io.py +56 -0
  82. quiddity/through_steps.py +396 -0
  83. quiddity/turned.py +430 -0
  84. quiddity-0.2.0.dist-info/METADATA +352 -0
  85. quiddity-0.2.0.dist-info/RECORD +90 -0
  86. quiddity-0.2.0.dist-info/WHEEL +4 -0
  87. quiddity-0.2.0.dist-info/entry_points.txt +2 -0
  88. quiddity-0.2.0.dist-info/licenses/LICENSE +202 -0
  89. quiddity-0.2.0.dist-info/licenses/NOTICE +8 -0
  90. quiddity-0.2.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +67 -0
quiddity/__init__.py ADDED
@@ -0,0 +1,380 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Copyright 2024-2026 Paul Fremantle
3
+ """Deterministic geometry-only feature recognition for build123d solids.
4
+
5
+ This Apache-2.0 package is the single recognition implementation shared by consumers. Import the
6
+ public surface from here, not the implementation submodules.
7
+
8
+ Recogniser contract (ADR 0002)
9
+ ------------------------------
10
+ A *feature* recogniser takes one of two shapes:
11
+
12
+ - **Part-based** — ``recognise_<feature>(part, *, <tuning / injected deps>) -> list[record]``
13
+ (``recognise_holes(part, *, cyls=None)``, ``recognise_chamfers(part, *, tol=...)``,
14
+ ``recognise_risers(part, *, tol=...)``). Everything after ``part`` is
15
+ **keyword-only** — both tuning and any injected inventory. A recogniser **never
16
+ re-recognises a dependency internally**; the orchestration caller owns the single inventory
17
+ and threads it.
18
+ - **Derived** — ``recognise_<feature>(inventory) -> list[record]``
19
+ (``recognise_hole_patterns(holes)``):
20
+ operates purely on another recogniser's records, no ``part`` and no tuning, so the
21
+ single inventory arg is unambiguous and stays positional.
22
+
23
+ Common to both: a **British** ``recognise_`` verb (not ``find_``/``analyse_``); a
24
+ **deterministic ``list`` of frozen-dataclass records** (empty when absent — never
25
+ ``Optional``-singular, never a bare ``list`` of primitives); **geometry-only records** (no
26
+ build123d types leak out; consumers adapt these values into their own domain models).
27
+
28
+ The contract holds for **every** recogniser, including the two that once strained it —
29
+ their records were simply the wrong shape:
30
+
31
+ - ``recognise_face_levels -> list[FaceLevel]`` (was ``list[float]``) — a level is now a
32
+ ``FaceLevel(z)`` record.
33
+ - ``recognise_turned_steps -> list[TurnedStep]`` (was ``TurnedProfile | None``) — each
34
+ ``TurnedStep`` now carries its ``axis``, so it is a self-contained record and the old
35
+ ``TurnedProfile`` wrapper is no longer the return. ``TurnedProfile`` survives only as a
36
+ **pipeline aggregate** (``TurnedProfile.from_steps``) for consumers that want axis +
37
+ shoulders as a unit — it is not a recogniser return.
38
+
39
+ Record class names avoid consumer-domain ``Feature`` types: for example, the public records are
40
+ ``HoleRecord`` and ``BossRecord`` rather than drawing or manufacturing IR types.
41
+
42
+ ``analyse_cylinders`` / ``full_cylinders`` / ``feature_diameters`` are **not** recognisers
43
+ under this contract — they are cylinder-analysis *substrate* (a tuple of dicts / a diameter
44
+ query), and deliberately keep their names. Likewise the **shared single-face reads**
45
+ (``classify_bevel``/``BevelReject``, ``fillet_anchor``, ``cone_rims``,
46
+ ``floor_face_anchor``, ``step_level_zs``): helpers shared with the declared
47
+ front-end, not recognisers — they traffic in build123d/OCP objects and are exposed only where
48
+ existing downstream compatibility requires them. New declared-feature consumers should use the
49
+ reviewed five-operation roster in :mod:`quiddity.inspection`; the broader helper set is
50
+ not implied to be supported inspection API.
51
+
52
+ ``FaceEdges`` is neither a recogniser nor substrate but a **shared memo**: pass one instance to
53
+ several recognisers running over the same part and they stop re-deriving each face's edges,
54
+ which is about a fifth of a full census. It is exported only because it appears in those
55
+ recognisers' signatures — a ``face_edges=`` parameter no public API could construct would be
56
+ useless. Omit it and every recogniser behaves exactly as before.
57
+
58
+ ``project_step_shoulders`` is likewise not a recogniser but its mirror image: a **pure
59
+ projection** over :func:`recognise_risers`' records, with no ``part`` and no geometry access
60
+ at all. It carries the ``project_`` verb rather than ``recognise_`` because the aggregate owns
61
+ the evidence and each consumer projects. A function that cannot look at a solid cannot become a
62
+ second recognition site.
63
+ """
64
+
65
+ from __future__ import annotations
66
+
67
+ from importlib.metadata import PackageNotFoundError, version
68
+
69
+ from quiddity._adjacency import FaceEdges
70
+ from quiddity._bevel import BevelReject, classify_bevel
71
+ from quiddity._features import (
72
+ BoltCircle,
73
+ BossRecord,
74
+ CounterBore,
75
+ HoleRecord,
76
+ HoleSpec,
77
+ LinearArray,
78
+ RectGrid,
79
+ analyse_cylinders,
80
+ feature_diameters,
81
+ full_cylinders,
82
+ recognise_bosses,
83
+ recognise_hole_patterns,
84
+ recognise_holes,
85
+ )
86
+ from quiddity.angled_steps import (
87
+ AngledStep,
88
+ recognise_angled_steps,
89
+ )
90
+ from quiddity.blends import (
91
+ Blend,
92
+ CircularBlendPath,
93
+ StraightBlendPath,
94
+ recognise_blends,
95
+ )
96
+ from quiddity.chamfers import Chamfer, recognise_chamfers
97
+ from quiddity.circular_blind_steps import (
98
+ CircularBlindStep,
99
+ recognise_circular_blind_steps,
100
+ )
101
+ from quiddity.countersinks import (
102
+ CounterSink,
103
+ cone_rims,
104
+ countersink_matches_hole,
105
+ recognise_countersinks,
106
+ )
107
+ from quiddity.explanations import (
108
+ DispositionExplanation,
109
+ ExplanationCoverage,
110
+ FamilyEvaluation,
111
+ FamilyExplanation,
112
+ RecognitionDiagnostic,
113
+ RecognitionDiagnosticCode,
114
+ RecognitionDiagnosticStatus,
115
+ RecognitionOutcome,
116
+ RecognitionReport,
117
+ ReconciliationReason,
118
+ build_raw_recognition_report,
119
+ build_recognition_report,
120
+ )
121
+ from quiddity.fillets import Fillet, fillet_anchor, recognise_fillets
122
+ from quiddity.flats import Flat, recognise_flats
123
+ from quiddity.frames import (
124
+ FramedEvidence,
125
+ FramedEvidenceRefusalReason,
126
+ FramedPreparation,
127
+ FramedRecognition,
128
+ FramedRecognitionEvidence,
129
+ FramedRecognitionReport,
130
+ FramedRecognitionResult,
131
+ FramedReport,
132
+ FrameGauge,
133
+ FrameInference,
134
+ FrameRefusalReason,
135
+ PartFrame,
136
+ PreparedFramedPart,
137
+ RefusedFramedEvidence,
138
+ RefusedPartFrame,
139
+ build_framed_recognition_evidence,
140
+ build_framed_recognition_report,
141
+ build_framed_recognition_result,
142
+ infer_part_frame,
143
+ prepare_framed_part,
144
+ )
145
+ from quiddity.grooves import Groove, floor_face_anchor, recognise_grooves
146
+ from quiddity.levels import (
147
+ STEP_LADDER_BOUNDARY_MARGIN,
148
+ FaceLevel,
149
+ RiserEvidence,
150
+ StepShoulder,
151
+ project_step_shoulders,
152
+ recognise_face_levels,
153
+ recognise_risers,
154
+ step_level_records,
155
+ step_level_zs,
156
+ )
157
+ from quiddity.oriented_slots import (
158
+ OrientedSlot,
159
+ OrientedSlotArray,
160
+ OrientedSlotGrid,
161
+ recognise_oriented_slot_patterns,
162
+ recognise_oriented_slots,
163
+ )
164
+ from quiddity.pads import RaisedPad, recognise_rectangular_pads
165
+ from quiddity.paired_ramp_steps import PairedRampStep, recognise_paired_ramp_steps
166
+ from quiddity.passages import (
167
+ PassageFrame,
168
+ PassageSection,
169
+ PassageSectionVertex,
170
+ )
171
+ from quiddity.plates import Plate, has_multi_axis_plates, recognise_plates
172
+ from quiddity.polygonal_bosses import (
173
+ PolygonalBoss,
174
+ PolygonalStock,
175
+ recognise_polygonal_bosses,
176
+ recognise_polygonal_stock,
177
+ )
178
+ from quiddity.profiled_bores import DoubleDBore, recognise_double_d_bores
179
+ from quiddity.repeating_profiles import (
180
+ RepeatingRadialProfile,
181
+ recognise_repeating_radial_profiles,
182
+ )
183
+ from quiddity.result import (
184
+ RecognitionResult,
185
+ build_raw_recognition_result,
186
+ build_recognition_result,
187
+ )
188
+ from quiddity.section_recesses import (
189
+ ClosedSectionProfile,
190
+ OpenSectionProfile,
191
+ SectionEnd,
192
+ SectionRecess,
193
+ SectionRecessArray,
194
+ SectionRecessBodyRef,
195
+ SectionRecessClassification,
196
+ SectionRecessDocument,
197
+ SectionRecessEnds,
198
+ SectionRecessEvidence,
199
+ SectionRecessFaceRef,
200
+ SectionRecessGeometry,
201
+ SectionRecessGrid,
202
+ SectionRecessRefusal,
203
+ build_section_recess_document,
204
+ recognise_section_recesses,
205
+ )
206
+ from quiddity.slots import (
207
+ Slot,
208
+ SlotArray,
209
+ SlotGrid,
210
+ recognise_slot_patterns,
211
+ recognise_slots,
212
+ )
213
+ from quiddity.step_io import import_step_geometry
214
+ from quiddity.through_steps import ThroughStep, recognise_through_steps
215
+ from quiddity.turned import (
216
+ TurnedProfile,
217
+ TurnedProfileKey,
218
+ TurnedStep,
219
+ recognise_turned_steps,
220
+ )
221
+
222
+ try:
223
+ __version__ = version("quiddity")
224
+ except PackageNotFoundError: # pragma: no cover - only a bare, uninstalled source tree
225
+ __version__ = "0.2.0"
226
+
227
+ # Imported after the recognition surface because census consumes that public orchestration.
228
+ from quiddity.capabilities import ( # noqa: E402
229
+ CAPABILITY_FORMAT,
230
+ CAPABILITY_FORMAT_VERSION,
231
+ CapabilityManifestError,
232
+ capability_manifest,
233
+ capability_manifest_json,
234
+ validate_capability_manifest,
235
+ )
236
+ from quiddity.census import feature_census # noqa: E402
237
+
238
+ __all__ = [
239
+ "__version__",
240
+ "CAPABILITY_FORMAT",
241
+ "CAPABILITY_FORMAT_VERSION",
242
+ "CapabilityManifestError",
243
+ "STEP_LADDER_BOUNDARY_MARGIN",
244
+ "AngledStep",
245
+ "PassageFrame",
246
+ "PassageSection",
247
+ "PassageSectionVertex",
248
+ "PairedRampStep",
249
+ "SectionRecess",
250
+ "SectionRecessClassification",
251
+ "SectionRecessEnds",
252
+ "SectionRecessEvidence",
253
+ "SectionRecessGeometry",
254
+ "SectionRecessRefusal",
255
+ "SectionRecessArray",
256
+ "SectionRecessGrid",
257
+ "SectionEnd",
258
+ "ClosedSectionProfile",
259
+ "OrientedSlot",
260
+ "OrientedSlotArray",
261
+ "OrientedSlotGrid",
262
+ "BoltCircle",
263
+ "Blend",
264
+ "CircularBlendPath",
265
+ "Chamfer",
266
+ "Fillet",
267
+ "Flat",
268
+ "FrameGauge",
269
+ "FrameInference",
270
+ "FrameRefusalReason",
271
+ "FramedRecognition",
272
+ "FramedEvidence",
273
+ "FramedEvidenceRefusalReason",
274
+ "FramedRecognitionEvidence",
275
+ "FramedRecognitionResult",
276
+ "FramedRecognitionReport",
277
+ "FramedReport",
278
+ "FramedPreparation",
279
+ "RefusedFramedEvidence",
280
+ "DispositionExplanation",
281
+ "ExplanationCoverage",
282
+ "FamilyEvaluation",
283
+ "FamilyExplanation",
284
+ "RecognitionDiagnostic",
285
+ "RecognitionDiagnosticCode",
286
+ "RecognitionDiagnosticStatus",
287
+ "RecognitionOutcome",
288
+ "RecognitionReport",
289
+ "ReconciliationReason",
290
+ "Groove",
291
+ "BossRecord",
292
+ "CounterBore",
293
+ "CounterSink",
294
+ "countersink_matches_hole",
295
+ "DoubleDBore",
296
+ "FaceLevel",
297
+ "HoleRecord",
298
+ "HoleSpec",
299
+ "LinearArray",
300
+ "Plate",
301
+ "PartFrame",
302
+ "PreparedFramedPart",
303
+ "PolygonalBoss",
304
+ "PolygonalStock",
305
+ "RaisedPad",
306
+ "RepeatingRadialProfile",
307
+ "RectGrid",
308
+ "Slot",
309
+ "SlotArray",
310
+ "SlotGrid",
311
+ "RiserEvidence",
312
+ "StepShoulder",
313
+ "StraightBlendPath",
314
+ "TurnedProfile",
315
+ "TurnedProfileKey",
316
+ "TurnedStep",
317
+ "RecognitionResult",
318
+ "RefusedPartFrame",
319
+ "BevelReject",
320
+ "FaceEdges",
321
+ "analyse_cylinders",
322
+ "classify_bevel",
323
+ "cone_rims",
324
+ "fillet_anchor",
325
+ "floor_face_anchor",
326
+ "has_multi_axis_plates",
327
+ "project_step_shoulders",
328
+ "recognise_face_levels",
329
+ "recognise_risers",
330
+ "step_level_records",
331
+ "step_level_zs",
332
+ "feature_diameters",
333
+ "feature_census",
334
+ "import_step_geometry",
335
+ "capability_manifest",
336
+ "capability_manifest_json",
337
+ "validate_capability_manifest",
338
+ "recognise_angled_steps",
339
+ "CircularBlindStep",
340
+ "recognise_circular_blind_steps",
341
+ "recognise_paired_ramp_steps",
342
+ "ThroughStep",
343
+ "recognise_through_steps",
344
+ "recognise_section_recesses",
345
+ "OpenSectionProfile",
346
+ "build_section_recess_document",
347
+ "SectionRecessBodyRef",
348
+ "SectionRecessDocument",
349
+ "SectionRecessFaceRef",
350
+ "recognise_oriented_slots",
351
+ "recognise_oriented_slot_patterns",
352
+ "recognise_bosses",
353
+ "recognise_blends",
354
+ "recognise_chamfers",
355
+ "recognise_fillets",
356
+ "recognise_flats",
357
+ "recognise_grooves",
358
+ "recognise_countersinks",
359
+ "recognise_double_d_bores",
360
+ "recognise_hole_patterns",
361
+ "recognise_holes",
362
+ "recognise_plates",
363
+ "recognise_polygonal_bosses",
364
+ "recognise_polygonal_stock",
365
+ "recognise_rectangular_pads",
366
+ "recognise_repeating_radial_profiles",
367
+ "recognise_slot_patterns",
368
+ "recognise_slots",
369
+ "recognise_turned_steps",
370
+ "build_recognition_result",
371
+ "build_raw_recognition_result",
372
+ "build_framed_recognition_result",
373
+ "build_framed_recognition_evidence",
374
+ "build_framed_recognition_report",
375
+ "build_recognition_report",
376
+ "build_raw_recognition_report",
377
+ "full_cylinders",
378
+ "infer_part_frame",
379
+ "prepare_framed_part",
380
+ ]