Repository navigation
Releases
0.5.0
Upgrading from 0.4.9:
- Rust API. Recognition records, evidence and fingerprint documents, reports,
refusal/error types and options are#[non_exhaustive]: add fallback match arms,
destructure with.., and build values withDefault::default()or the provided
constructors (Rust API evolution). - Fingerprints are
quiddity-rust/fingerprint/4.correspondrefuses stored/3
fingerprints; re-fingerprint each part from its own geometry with this release. - Assembly manifests are
quiddity-manifest/2, one instance per assembly occurrence.
/1manifests are refused; rescan the source. - Recognition schema stays
quiddity-rust/recognition/6. New families and fields are
additive. - Thanks to @xu9299 for shared recognition/evidence analysis (#227), recess evidence
without solid ownership (#234), Windows snapshot provenance paths (#237) and faster
face integration (#246).
Breaking (Rust API)
- Recognition records,
evidence and fingerprint documents, reports, refusal/error types and defaultable
recognition options are now#[non_exhaustive]. Match extensible enums with a
fallback arm and destructure extensible structs with... Construct options with
Default::default()then assign public fields; use the provided constructors for
caller-authored values. See Rust API evolution for the full list
and the closed value types deliberately kept exhaustive. - Presentation text placements, callout geometry, polyline roles, evidence statuses
and face-accounting groups are also non-exhaustive; useTextPlacement::newor
presentation::Callout::default()for construction and handle unknown variants. - Breaking public Rust API changes require a minor bump before 1.0. This includes
field/variant additions on exhaustive types and removing or feature-gating APIs.
CI checks both published crates against their latest stable crates.io release;
a non-empty Unreleased Breaking (Rust API) section sanctions a pending minor bump. - Breaking (fingerprint format): fingerprints are
quiddity-rust/fingerprint/4
(FINGERPRINT_VERSION). Values changed several times within/3(#71, #84/#85, #92,
#100, #118, #134, #189) without a way to tell which vintage a stored fingerprint has, so
correspondnow refuses/3with an error asking for the part to be re-fingerprinted.
Consumers that store fingerprints must re-fingerprint each part from its own geometry
with this quiddity; there is no migration. The/4values are this release's values;
the structure and fields are unchanged. Recognition schema stays/6. For #254.
Changed
- Face integration reuses complete cached moments, skips kink searches that cannot
affect refinement, and stops once an accepted leaf makes the existing refusal
inevitable. Integration nodes, error criteria and returned results are unchanged.
From #246 (@xu9299). - Breaking (manifest format): assembly manifests are
quiddity-manifest/2, with one
instancesentry per assembly occurrence. A product's solids and surface models are all
placed by that one entry;/1listed one entry per body, so a multi-solid part was placed
over itself several times./1manifests are refused with "rescan the source". From #190. - Section-recess candidates whose faces have no solid owner (open shells, or faces dropped
in a degraded run) no longer fail evidence for the whole file. recognition/6 gains a
section_recess_source_refusalsfamily naming those sources; a returned projection error
now gives a partial result (complete: false) instead of an evidence error. Eligibility
checks the defining and consulted faces, so a candidate consulting a face on another solid
is refused (evidence).SectionRecessProjectiongains
source_refusalsand is#[non_exhaustive]. From #234 (@xu9299).
Documentation
- haecceity states its scope: it reads, queries and writes B-reps and does not model them
(README "Scope", crate docs and the crates.io description). From #203.
Fixed
-
Refuse incompatible complex targets in schema reference slots throughout shape
topology and geometry, and loops with neither edges nor a vertex. Empty programmatic faces no longer panic in loop placement.
Nine regression inputs and 256 time-bounded token mutations exercise this path;
the mutation harness also rejects caught family panics. -
Python native entry points catch internal Rust panics as
quiddity.QuiddityError
(aRuntimeError, caught byexcept Exception) with the panic message. Normal
input errors retain their existingValueError/PmiErrortypes. -
PMI edition upgrades report R4 repairs for unset required
label/textattributes,
writing empty strings without inventing identifiers or other values. Ordinary AP242
writes preserve those defects; Add refuses source-record repairs with their kinds and ids.
Rust API:UpgradeRepairKindgainsR4UnsetRequiredStringand becomes
#[non_exhaustive]; matches need a fallback arm (see Breaking above). CLI and Python: a write
report'sinstances.replacedcounts distinct records, so a record with two repairs
counts once. -
Mass quadrature judges tangent rank independently of the UV parameter scales. The narrow
spline sheetcaptured/.../2fe0e2383b80a0b8now has its true area (10 mm², was 0.011),
and the recovery length and tolerances derived from it follow. -
A solid whose face normals are mixed (some inward, some outward) and whose volume is
negative has its inward faces repaired, but only when every face's material side is
proved, the wires stay coherent, the mass becomes positive and every repaired face is
re-proved outward.input_repairsreportssolid 0: mixed face senses repaired (N faces).
The check is cheap and looks at the first eight faces, so it is face-order dependent: a
shell whose first decisive face is outward is not repaired
(recognition trust). In the fixtures, MFCAD++ (test split)
and MFInstSeg it fires only ontruck_master_shaft_hole:- its volume is now +46140.66 mm³, where it was −47426.50;
- its Ø30×40 outer cylinder is a
BossRecordØ30 h40 instead of a flat-bottomed
HoleRecord40 deep; - its fingerprint normals, convex/concave adjacency and frame refusal
(no-analytic-direction, wasno-material) follow.
-
Two known differences from OCCT are recorded. Both are checked by independent integrals
that agree with ours, and neither changes a comparison band:truck_master_shaft_holeis 1.12e-4 above OCCT, which builds approximate cap pcurves.
The part's bore intersection curves lie up to 0.12 mm off their surfaces, so its
volume is only defined to about 2e-4 (the design intent is 46139.11 mm³).varying_pitch_sweepdiffers by 4.2% in volume because OCCT integrates its open pcurve
wires without closing the gaps. OCCT's own mesh agrees with ours, andtools/gvp.py
counts it asKNOWN-REFERENCEwhile ours stays within the band of that mesh.
-
The snapshot tool uses
/in manifest-relative paths so Windows input digests
and nested snapshots can be checked against the same provenance manifest. From #237
(@xu9299). -
Mass integrals wrap closed spline tangents with their points and use consistent
containment and integration domains for seamless torus bands. This restores the
missing cylindrical areas in NIST STC-10 and the toroidal area in FTC-11, and
corrects four adjoining STC-10 faces. Volume centroids and face moments now
require valid mass integrals instead of accepting rejected face contributions.
Mass validity shares the moments' quadrature and the existing per-face cache,
avoiding a separate mass pass on faces first queried for their moments. -
Closed rectangular pockets with analytic floor rounds retain their full depth and
vertical corner radius (#228), extending the blind-recess floor-claim work (#205,
#207). Optionalfloor_radiusreports a proved uniform floor radius;
floor_filletsattaches each treated face, supporting wall and local radius to
the pocket, including mixed sharp/rounded floors. Open slots stay slots.
FTC-08 has the same defect: pockets 8→9, slots 4→3 and fillets 95→91.
Its surviving slot ranks 1–3 become 0–2; fillet ranks 66–73 become 64–71
and 76–94 become 72–90. Existing pocket ranks and the other 99 corpus
parts are unchanged. Face-specific inventory loss entries retain the historical
slot/fillet baselines and tighten all three FTC-08 pocket floors to nine.
The six existing section projections remain unchanged; one explicit refusal
records the newly recognised pocket's unsupported toroidal floor geometry.
Ball-end corners with equal wall/floor radii use spherical patches; their floor
faces are owned once by the pocket. Floor-treated pockets supersede duplicate
prismatic readings, and raw slot fallbacks survive until a pocket is accepted.
Unsupported nonanalytic revolution floor transitions issuePocketRefusal
rather than a truncated-depth pocket. FTC-08 raw slot candidates return to four;
its physical slot loss allowance is retired while accepted ranks stay unchanged.
Across revisions, adding a floor fillet to an existing pocket matches as an
orphaned feature plus a new feature, as aNone→Somecorner radius already does.
Shared per-solid floor analysis, early mouth checks and reuse of legacy cavity
completion avoid repeated graph walks on parts without floor-blended pockets. -
Internal bevel leg ties use intrinsic support area; symmetric ties remain unordered in
fingerprints. Feature-policy angular tolerances retain slightly noisy STEP supports,
with the slope angle measured on the actual supports rather than an ideal right angle.
Added
assembly_layoutexample generates contiguous, interleaved and seeded shuffled
assemblies, and streams scale fixtures with fresh...
0.4.9
Upgrading from 0.4.8: recognition schema /6, fingerprints /3, quiddity-rust/evidence/1 (one additive key), the haecceity.pmi/1 schema and the PMI reader string /19 are unchanged. Rust API: EvidenceDocument is not #[non_exhaustive] and gains edge_sources, so code that builds it with a struct literal must add the field (#202). quiddity::cli and the quiddity binary now need the default cli feature: a build with default-features = false (for example, to leave out presentation) must add features = ["cli"] to keep them, or leave it out to keep clap out (#201). The command line is now parsed by clap (#199); see Changed for the usage cases that now exit differently. Recognition output changes where a circular blind step cut by a second feature is now recognised in place of a cylindrical_profiles record (#215).
Added
quiddity completions SHELL(#199) prints a shell completion script (bash, zsh, fish,
powershell or elvish) to stdout; the README gives the install one-liners.quiddity --versionprints the version.- Evidence (#202):
quiddity-rust/evidence/1gainsedge_sources, parallel to
face_sources: per edge, the STEPEDGE_CURVE{entity, instance}it was read from, or
null. The key is additive, so the schema version stays/1(as forfailures); existing
keys and values are unchanged. Rust API:EvidenceDocument(not#[non_exhaustive])
gainsedge_sources, so struct literals need it; new aliasEvidenceEdgeSource. - Python (#202):
pmi.read(step, findings=True)returns(document, findings), keeping the
reader's findings on the selected part (or no part) from a successful read; without the
argument the result is the same dict as before.quiddity.parts(step)returns exactly
quiddity parts' JSON.quiddity.step_entities(step)lists the file's DATA instances in
file order:#N, entity type (each leaf of a complex instance) and referenced instances.
Rust API: newfile_api::partsandfile_api::step_entities;quiddity partsnow goes
throughfile_api::parts, with unchanged output. correspondence::congruent(a, b)proves two parts geometrically identical: it returns a
proper rigid motion takingaontobwith the face pairs, orNone. It tries an exact
B-rep match, the centroid translation, thenalign, and accepts a motion only when every
face matches and samples of the trimmed faces agree both ways within 1e-5 mm. Names are never
read, and mirror images do not match.congruent_with(a, af, b, bf)takes fingerprints
already made, for comparing one part against many. It moves draftwright's pack check into
quiddity for Specify to share (#209, for specify-core#150). haecceity gainsPart::same_brep,
the exact B-rep comparison.
Changed
- The
quidditycommand line (the binary and the Python console script) is parsed by clap
instead of by hand. For everything that worked before, commands, flags, defaults, exit
codes, output and refusals are unchanged, as are the messages for values checked by quiddity
(--part,--mode,--presentation). Help and usage errors are clap's (Usage: quiddity …, on stderr with exit 2 as before). Newly refused or changed:pmi jsonusage errors (a
missing argument or-o, an unknown command, an unknown, repeated or valueless option) exit
2 instead of 1, with clap's message (a bad--partvalue is still refused with exit 1);
evidence --framedwith no file exits 2 (was 1,--framedread as the file); a trailing
flag with no value is reported by clap;-h/--helpafter a command (parts,evidence,
fingerprints,capabilities, …) shows its help and exits 0 (was an error: 1 where read as
a file, 2 forcapabilities);-Vprints the version (was read as a file, exit 1); a file
to recognise namedcompletionsmust be given as./completions. Newly accepted: a positional after
the flags (evidence --framed part.step,pmi read --part 1 part.step),-- FILE, and--option=VALUE
(--output=out.step). The command line is the root crate's defaultclifeature, so a
library build can leave clap out (default-features = false) (#199, #201).
Fixed
0.4.8
Upgrading from 0.4.7: recognition schema /6, fingerprints /3 and the haecceity.pmi/1 schema are unchanged. The PMI reader string moves from haecceity-pmi-read/17 to /19.
- PMI presentation follows CAx-IF PMI Recommended Practice 4.1 (#193):
- Source leaders are tessellated inside their callouts, with no bare polyline curve occurrences.
- Placeholders carry a direct axis placement, the standard PMI type names and links to their semantic PMI.
- Curve sets use the Table 18 names (
note,label, …). - Alignment moves from placeholder names to AP242 identification metadata. Older files remain readable.
- PMI writes add the recommended-practice reference to FILE_DESCRIPTION, replacing an older PMI reference and keeping everything else.
- A default Notes saved view (#198): general notes in one plane get an AP242 saved view named
Notes, facing their annotation plane and framing the part and notes upright.- It is checked with NIST's STEP File Analyzer 5.41 (no errors; one expected advisory) and against NIST CTC-02's entity layout.
- Add keeps foreign views and defaults, and reuses its own Notes view. Replace and remove clean it up. Edition-1 Add skips it; Replace upgrades edition 1 to 4 only when the view can be written.
- In assemblies, only the first eligible part gets a view. Notes on several planes, or in several text orientations, produce a factual finding instead.
PmiRead.saved_views(Rust) lists saved views. Default views no longer produce a face-less shape-aspect finding.
- Unique draughting-model names (#198): new models get unique names to satisfy UR1, such as
PMI callouts (2)ordatum feature symbols (2), including on assemblies without notes. - Materials (#198): a material with no separate name repeats its ID as its description, so no semantic text is empty. Its absence still round-trips.
- Selected placed bodies (#194, for #111; thanks to @xu9299):
StepFile::placed(&self, keep: &[usize])reads chosen body instances in file coordinates, with their original STEP identities for PMI attachment. For example, it can read a plate and one of two coincident pins. - Rust API:
PmiRead(not#[non_exhaustive]) gainssaved_views: Vec<SavedView>, so struct literals and exhaustive patterns need it. New:pmi::SavedViewandStepFile::placed.WriteReport(non-exhaustive) gainssaved_view_findings. - CLI and Python: write reports gain
saved_view_findingsas an optional key. - Contributions: contributions are now taken under Apache-2.0 with a DCO sign-off (
git commit -s), replacing the CLA (#192, #197). A Windows path-separator fix applies to the p21 broken-fixture test (#196).
Written STEP changes for every PMI write (RP 4.1 presentation and the FILE_DESCRIPTION reference), for files with general notes (a Notes view; Replace may upgrade edition 1 to 4), for assemblies (draughting-model names) and for materials without a separate name.
0.4.7
Upgrading from 0.4.6: recognition schema /6, fingerprints /3 and the haecceity.pmi/1 schema are unchanged. The PMI reader string moves from haecceity-pmi-read/15 to /17.
- Sealed interior voids are not holes (#189): a closed internal cavity (a void shell of the solid) is reported only as
interior_voids, no longer also asholesandhole_patterns. Every sealed shell, spheres included, now gets an exactclosed_shellrecord from its shell volume, so existing void records' volume and sample fields, and their fingerprints, change. Existinginterior_voidsids keep their order. Holes that open to the outside are unchanged. - PMI round trip on the NIST CTC/FTC models (#188): all ten available CTC/FTC AP242 files round-trip with empty differences, and the NIST strict test runs in CI.
- Dimensions with no stated value are written through unchanged, with no refusal from
writeorvalidate(Python no longer raisesPmiErrorfor them), no invented value and no nonconformance finding. - A projected zone whose end has no geometry is kept and written; the reader reports it as a
factfinding (it wasnonconformance). - Anonymous attribute units are matched by content, so PMI carried into a new file (
quiddity brep --pmi) binds the same unit; that path also writes the standard mass units it needs. - Replace keeps datum support shared with PMI it can't read, and rebinds it only to equal replacement datums (
WriteReport.retained_shared). - Upgrade repair R3: when the written PMI needs a newer AP242 edition, INTEGER representation items written as exactly integral REALs are written as INTEGERs, each reported.
- Dimensions with no stated value are written through unchanged, with no refusal from
- Rust API:
FindingKindgainsFactandUpgradeRepairKindgainsR3IntegerLiteral; neither enum is#[non_exhaustive], so exhaustive matches need an arm.pmi::write::WriteReport(non-exhaustive) gainsretained_shared: Vec<u64>. New:Finding::preserved_factandPart::shell_mass(signed volume and area of a closed shell). - CLI and Python: write reports gain
retained_sharedas an optional key; PMI JSON findings can now havekind: "fact".
0.4.6
Upgrading from 0.4.5: recognition schema /6, fingerprints /3, the haecceity.pmi/1 schema and the PMI reader string haecceity-pmi-read/15 are unchanged.
- Add mode keeps the file's own bytes (#185): every Add write now keeps pre-existing carriers, usages, controls and presentation at their original ids. This reverses 0.4.5's Add-mode normalisation, which broke
datum_feature.WR2when adding a datum to NIST CTC-01. Any Add write that would change an original entity fails atomically withWriteError::AddPreservation, which names the entities. That covers R1/R2 upgrade repairs and AP214 keyword rewrites; use Replace for those. Skipped normalisations are reported inWriteReport.preserved_normalizationsaslegacy-text-rechunking,kept-usage-factoring,functional-sar-isolationorlegacy-endpoint-retirement. - Source callout positions (#183): PMI writing accepts an optional
haecceity.presentation/1companion: Rust/JSONwrite_with_presentation, Pythonpresentation=, and CLI--source-presentationonquiddity pmi write(exactly one selected part) andquiddity pmi json import. The CLI flag exists only in json-import builds./1adds the optionalGraphic.leader; readers that validate against the 0.4.5 schema (additionalProperties: false) reject documents that use it. Without a companion, #183 changes nothing. - Notes layout (#184): unplaced feature notes become their own leadered callouts, and the general-notes block moves clear of drawn ink. Written presentation changes for files with feature notes or a crowded notes position; four writer goldens changed (
pmi_gaps,pmi_gaps_gram,spool_fits_intent,assembly_plate_pin_intent). - Reports:
WriteReportaddspresentation_warnings(placed text edge-on or overlapping, and feature notes whose glyphs can't be drawn) andpreserved_normalizations. The CLIpmi writereport and the Python report gain both as optional keys;pmi json importaddspresentation_warnings. Pythonwriter_findingsgains both categories. - Rust API:
WriteErrorgainsAddPreservationand is not#[non_exhaustive], so exhaustive matches need an arm.json::presentation::Graphicgains the pub fieldleaderand is not#[non_exhaustive], so struct literals must supply it.
Added
-
Notes blocks use actual projected strokes and row text cells, selecting the first clear downward offset instead of avoiding whole callout boxes. Feature-note extent matches frame placement.
-
Generate individual leadered callouts for unplaced feature notes; keep the general-notes block clear of generated, source-placed, and kept file ink.
-
Source callouts honour /1 text alignment, attach leaders to the box edge towards
the tip, and refuse conflicting placements on a shared datum symbol. JSON CLI
import accepts--source-presentationwith xcaf requirement ids. -
Source placement requires the
json-importandpresentationfeatures.
The CLI source flag is hidden withoutjson-import; builds without presentation
drawing refuse source input clearly. Minimal builds retain their previous features. -
PMI writing accepts optional source callout planes, text positions and leader
tips through Rust/JSONwrite_with_presentation, Pythonpresentation=, and
CLI--source-presentation FILE. Placed notes have their own note-linked
callouts. Invalid placements fail atomically; edge-on text and overlaps warn
without moving supplied positions. Saved views remain a later phase. -
haecceity.presentation/1adds optionalGraphic.leader(a model-space point
in millimetres). Old companions still read.WriteReportadds
presentation_warnings; #183 changes no writer golden.
Fixed
- Add upgrade refusals name the required repairs and entity ids, explain that
Replace retains a part with no PMI, and warn that it re-authors existing PMI.
Retained legacy aliases no longer generate classification certificates or gain
checker exemptions; before/after checks retain their original findings. Reuse
across representations sharing a context is limited to Add preservation.
The CLI and Python reports carrypreserved_normalizationsrecords ({id, normalization}). - Add preserves original entity bytes: every Add write keeps pre-existing
carriers, usages, controls and presentation at their original ids. Add no longer
retires legacy endpoint wrappers, isolates functional SARs or rechunks retained
legacy text, superseding 0.4.5's normalization behavior. Skipped normalizations
appear inWriteReport.preserved_normalizations; schema upgrades requiring entity
repairs now fail atomically in Add (use Replace for those repairs). - Adding a datum on a kept face or pattern emits a separate
.T.datum_feature,
fixing NIST CTC-01's.F.perpendicularity carrier (datum_feature.WR2). Only
faces with kept usages get independent GISUs in new representations; uncovered
faces use ordinary carriers. Pattern datums retain their member scope. Non-datum
additions also get independent usages when safe reuse is impossible. Each narrow
CAx-IF §5.1 exception names the actual aspect and preserved carrier in
WriteReport.geometry_carrier_exemptions; unrelated source aspects are not exempt.
Kept carriers are never promoted in any mode; datum leaves created in the same
write remain reusable. No EXPRESS validation rule is exempted.
0.4.5
Upgrading from 0.4.4: recognition schema /6, fingerprints /3 and the haecceity.pmi/1 schema are unchanged. The PMI reader string advances to haecceity-pmi-read/15. Semantic PMI reads as before, with two exceptions: the datum findings below, and aspects that 0.4.5 writes named haecceity.feature/1. Old files are unaffected by the second.
- Writer (#181): written STEP links PMI to geometry the way CAx-IF PMI v4.1 §5.1 and §6.5 recommend. Each face has one shape aspect, which every dimension and tolerance on it reuses, and datum features are referenced directly. Semantic values are unchanged, and OpenCascade reads every checked-in written golden as before. A narrow exemption covers two shared-carrier shapes that OpenCascade 7.9.3 and NIST SFA 5.41 misread in the strict form. They are reported in
WriteReport.geometry_carrier_exemptions, which the CLI and Python report also show as an optionalgeometry_carrier_exemptionskey. The thumbwheel golden also keeps one allow-listed presentation-only carrier. - Reader (#180): a datum whose feature reaches no face is now an
Unresolvedfinding. A Solid Edge re-export of NIST CTC-01 that loses datum B's feature is pinned as a fixture. - Recognition (#178, #179): through holes with partly obstructed exits, and helical coil springs. The schema fields are additive, but existing records change on affected parts: spring wire faces lose their hole, boss and fillet records, and partly covered exits become
through. - Rust API:
Featuresgains the public fieldhelical_springsand is not#[non_exhaustive], so struct literals must supply it (..Default::default()still works).PHYSICAL_FAMILIESnow has 43 entries. New public types:HelicalSpring,SpringTolerances,ExitObstruction,GeometryCarrierExemption.
Fixed
- Datum features that resolve to no faces are reported as
Unresolvedfindings:datum <L> feature resolves to no faceswhen the feature is kept but reaches no face, anddatum <L> is not read: what establishes it does not resolvewhen nothing establishes the datum (previously a Nonconformance, "datum is established by neither a feature nor a target"; such a datum was not read before either). The Solid Edge 2026 re-export of NIST CTC-01 and the control it was made from are fixtures (tests/fixtures/ap242/reexport/). The re-export reports exactly datum B unresolved. Writer round trips are checked so that no datum becomes unresolved. - PMI dimensions and tolerances reuse the face's existing shape aspect and GISU,
including datum features (CAx-IF PMI v4.1 §5.1 / §6.5.1–2). Location endpoints no
longer use facelessSHAPE_ASPECT('DIMENSION')wrappers. Multi-face features
retain composites of shared per-face carriers so OCCT reads one GISU per face
without violating IIRU UR2. Adding a datum to a kept ordinary feature specializes
that carrier todatum_feature, reported inrewritten, preserving its id and usages.
Consumer note: written STEP entity linking changes; semantic PMI values and
haecceity.pmi/1are unchanged. Reader binding advances tohaecceity-pmi-read/15:
explicitly authored standalone members useSHAPE_ASPECT.name='haecceity.feature/1'
so they remain features when shared as composition members. Old files read as before.
Specialized areas, runouts, groups and zones keep their meaning; plain requirements
share neutral geometry carriers instead. Complex neutral composites can gain a datum
in place. Legacy endpoint wrappers are retired on Add; independent same-content
features retain separate removal provenance. A second datum label on an already
established feature is refused underdatum_feature.WR1.
Adding a datum to a kept pattern specializes the same group aspect to a datum
feature. Without presentation, adding a datum and its own size keeps the §6.5.3
combined dimension over the kept geometry carrier, preserving existing feature controls.
For ambiguous shared controlled-pair and datum-target-support SARs that both
OCCT 7.9.3 XCAF and NIST SFA 5.41 read as composition, retain private forwarding
aspects under a narrow §5.1 exemption.
WriteReport.geometry_carrier_exemptionsand the optional CLI/Python report field
list each actual exempt addition by structural condition and endpoint/carrier id.
Datum-feature endpoints stay direct; semantic face/GISU uniqueness remains mandatory.
The thumbwheel Add golden retains one explicitly allow-listed presentation-only
carrier so XCAF preserves the internal-thread callout, while coalescing its other
producer duplicates. The raw Specify input is unchanged.
Specify ownership stamps (specify-core:written:v1) are deleted with removed records;
haecceity does not reattach them. Specify's own Replace re-stamps newly emitted roots
after the haecceity write (specify-core-rust/src/writer.rs:784,src/provenance.rs:91).
Added
-
Recognise complete round-wire helical coil springs from swept NURBS tubes and connected cylinder/torus approximations. The optional
helical_springs/HelicalSpringrecord supplies a file-space axis, hand, wire/mean/outer/inner diameters, physical free length, measured total coils, pitch, end type and exact claimed faces. Active coils remain null with an explicit basis when inactive ends are unproved; rate is never inferred. Spring claims suppress individual wire hole/boss/fillet callouts. Draftwright consumers can dimension free length and outer diameter on a simplified spring view and populate a data table from the record. Recognition schema 6 and fingerprint format 3 are unchanged for this additive family; Python recognition JSON exposes the same record. -
Partially obstructed hole exits: bores spanning their local wall now report
bottom: "through"when a planar partial cap leaves part of the exit open.
Optionalexit_obstructionson hole records (also in pattern members) names
each mouth, its covering face indices and approximate covered-area fraction.
Full caps retain blind classifications; undecidable geometry remainsunknown.
Consumers should re-recognise affected parts, draw THRU and warn about the
obstruction. Recognition schema/6and fingerprints/3are unchanged: the
diagnostic field is additive, omitted when empty, and excluded from fingerprints.
RustHoleRecordliterals must supplyexit_obstructions(normallyVec::new()).
0.4.4
Upgrading from 0.4.3: recognition schema /6, fingerprints /3, the haecceity.pmi/1 schema and the PMI reader string haecceity-pmi-read/14 are unchanged, so semantic PMI reads as in 0.4.3. STEP writing changes only for parts with threads. Presentation JSON gains the optional fields below, including for files without threads.
- Thread callouts (#175): written presentation now includes internal and external thread callouts (stated pitch, class and depth, or THRU) in a stack local to the feature, with any controls on that thread. Thread-free writes are unchanged.
- Presentation JSON:
haecceity.presentation/1gains two optional fields so a host can redraw text in its own font.polyline_rolesis per graphic.cell_boxis per text placement: it appears on read file text when the file states a planar extent and a known alignment, and on generated text. Older documents still read. Readers that validate against the 0.4.3 schema (additionalProperties: false), or haecceity 0.4.3's JSON import, will reject the new fields. The Python package's schema copy is updated. - API note: the Rust structs
json::presentation::Graphic(polyline_roles),json::presentation::TextPlacement(cell_box) andpresentation::Callout(frame_strokes,text_symbols,cell_boxes) gain public fields and are not#[non_exhaustive]. Code that builds them as struct literals must add the new fields; code that only reads them is unaffected. New public items: the functionpresentation::threadand the enumjson::presentation::PolylineRole(not#[non_exhaustive]).
Added
- Generate internal/external thread callouts with stated pitch, class and depth (or THRU), in feature-local stacks. Presentation JSON adds optional polyline roles and text cell boxes for host-font rendering; older documents remain readable. Five feature-on STEP goldens gain thread graphics:
thread_replaced,assembly_plate_pin_intent,editor_assembly_pin_alone,string_post_tapped_intent, andthumbwheel_thread_knurl_intent; their XCAF captures are refreshed. Thread-free writes and feature-off goldens are unchanged.
0.4.3
Upgrading from 0.4.2: recognition schema /6, fingerprints /3 and the haecceity.pmi/1 schema are unchanged; only files whose datum features have several constituents read differently.
- Datum constituents (#173): a face aspect composing a datum feature maps to the whole datum only when it is the datum's whole face set. Constituents of a multi-face datum (e.g. three separately sized lands) keep their own features and dimensions; the datum still holds all faces. Single-constituent datums (NIST STC10's seven folds, the Solid Edge CTC-01 re-export) read as in 0.4.2. Specify can drop its temporary LM10UU writer adjustment.
- Bounded STEP strings (#172): strings the writer generates are at most 8192 encoded bytes, so OpenCascade can read written files (NIST STC-10 failed before). Long descriptive values, including haecceity's JSON item-id map, are written as ordered lists of text chunks; legacy single strings still read. Readers of haecceity's ordering property or descriptive text outside haecceity must join the chunks in list order. Strings carried unchanged from the input are written as they are, with a non-fatal
long_stringswarning. Generated names, identifiers and fallback product/header names longer than the bound are now explicit errors (never truncated), and a generated note graphic whose wording exceeds it is namedGeneral notes(the full text stays in the chunked descriptive items). API note: both RustWriteReports (#[non_exhaustive]) gainlong_strings: Vec<LongStringWarning>(record,encoded_bytes,byte_offset); the new public constpmi::write::MAX_STRING_LITERAL_BYTESis 8192; report JSON carrieslong_stringsonly when there are warnings. - Presentation (#171): generated size callouts sit above their feature control frames in one annotation plane, and saved-view packing reserves label ink, so callouts no longer overlap. Written PMI graphics change accordingly.
- PMI reader string: changes from
haecceity-pmi-read/13tohaecceity-pmi-read/14.
Fixed
- A plain face aspect composing a datum feature aliases the datum only when its face
set equals the whole datum feature's. Constituents of a multi-land datum retain
separate features and size dimensions; the datum still collects all faces once.
Single-constituent datum features, all-around groups and per-member datum groups
retain their existing semantics. Consumers: legacy PMI reader binding advances
tohaecceity-pmi-read/14; re-read bound PMI documents. Specify can remove its
workaround for the three independent Ø19 sizes on LM10UU's datum A lands. - STEP PMI writes bound generated encoded string literals to 8192 bytes, preventing
OpenCascade's scanner overflow on NIST STC-10. Long descriptive values,
including JSON item-id maps, UDA payloads and notes, use ordered compound/list
representation items of text chunks; legacy single-string values still read.
Consumer note: readers of haecceity's ordering property or descriptive text
must concatenate marked chunks in list order. Semantic JSON is unchanged.
Generated indivisible oversized names/identifiers are now explicit errors.
Retained source/header/presentation text is preserved with non-fatal
long_stringswarnings naming the record and encoded length; both PMI and
B-rep write reports expose the warnings (CLI and Python reports include them).
Source product labels are preserved by B-rep writes; newly generated fallback
product/header names remain bounded. Add also rechunks producer-owned legacy
descriptive metadata at its original ids, preserving links and decoded values.
API note: both Rust write reports gainlong_strings(LongStringWarning), and
pmi::write::MAX_STRING_LITERAL_BYTESis public; optional report JSON
adds that field only when warnings exist. Existing semantic JSON is unchanged. - Keep generated size callouts above their feature control frames in one annotation
plane, and reserve plain label ink in saved-view packing to prevent overlapping
callouts without loosening feature-distance or spindle ladder drift limits.
0.4.2
Upgrading from 0.4.1: recognition schema /6 and fingerprints /3 are unchanged.
- Recognition (#157, #159, #160): turned steps on stock with proved spanner flats are kept, interrupted coaxial torus fillets are grouped, and chamfer/angled-step corners are omitted unless proven. At your next pin, affected parts' fillet records renumber their run-local ids where interrupted arcs merge (64 groups in the corpus), some
cornervalues become null where they could not be proven, and turned parts with flats gain their turned steps. - PMI reading (#168): strictly reversed dimension limits and signed deviations are read swapped, with a non-fatal finding, instead of refusing the dimension. Plain face aspects composing a datum feature resolve to that datum feature. On NIST STC10 seven duplicate features fold into their datum features (173 → 166), with references re-indexed.
- PMI identifiers and zones (#170): duplicate or empty source identifiers no longer abort the JSON read (NIST STC06 and STC09 now export); repeated ids fall back to content-derived JSON ids, with a non-fatal finding. Each
haecceity.pmi/1item gains an optionalidentifiercarrying the file's characteristic number, duplicates included. Update closed-object schemas for this additive field (the Python schema mirror includes it). Rust struct literals ofjson::Semanticand JSON-enabledPartIdentifiersneed the newidentifiers/json_identifiersfields. An exporter's runout zone with no angle on a non-runout tolerance recovers its stated zone form, with a finding. - PMI reader string: changes from
haecceity-pmi-read/11tohaecceity-pmi-read/13(the/12step in #168 was never released).
Fixed
-
PMI JSON reads no longer abort on duplicate or empty source identifiers. Unique wire
ids fall back to content identities, and non-fatal findings retain the affected STEP
instances and identifier values. Optional per-itemidentifierfields carry source
characteristic numbers, including duplicates, independently of referenceidfields.
Consumers: update closed-object schemas for this additivehaecceity.pmi/1field;
resolve references byid. The Python schema mirror includes the addition.
Rustjson::Semanticgainsidentifiers; JSON-enabledPartIdentifiersgains
json_identifiersfor round-trip source metadata, so struct literals need those fields. -
Recover an explicitly stated diameter/plain zone on a non-runout tolerance when an
exporter supplies a runout-zone definition without its required orientation. Report
the nonconformance; refuse when the missing orientation determines the zone's meaning.
Legacy PMI read binding/13identifies the additional source-identifier findings. -
For #157, #159, #160, retain turned steps on stock with proved spanner flats,
group interrupted coaxial torus fillets with exact defining faces, and omit
chamfer/angled-step corners unless their wedge is consistent at published
precision and has no material interior, retaining correct boundary-touching corners. Recognition /6 and fingerprints /3 are unchanged.
At your next pin: affected parts' fillet records renumber their run-local ids where
interrupted arcs merge (64 groups in the corpus), and somecornervalues become null
where they could not be proven. Turned parts with flats gain their turned steps. -
PMI import repairs strictly reversed dimension limits and signed deviations with
a non-fatal nonconformance finding identifying the source instances. Equal bounds
and reversals whose nominal lies outside the corrected absolute limits remain refused. -
Plain face aspects composing an indivisible datum feature retain the owning feature
identity when referenced by tolerances, properties or presentation; their faces are
collected once. All-around groups and per-member datum groups keep their semantics.
Legacy PMI reader binding/12identifies the corrected output.
0.4.1
Upgrading from 0.4.0: recognition schema /6 and fingerprints /3 are unchanged, and successful recognition output is byte-identical.
- Panic isolation (#161): a panicking family no longer aborts the run. Incomplete documents add optional
failuresandcomplete: false. - Presentation: text placements carry optional
stroked; generated general notes are one numbered GENERAL NOTES block beside the part; regenerated ladders and datum flags sit by their features again; generated presentations gain one view per annotation plane. Feature-on (written PMI) graphics change accordingly. - Callout reading (#166):
PmiRead.calloutslists decoded graphical PMI annotations and their semantic links. - API note:
haecceity::pmi::PmiReadgains the public fieldcallouts; code that buildsPmiReadwith a struct literal must add it. Code that receives it frompmi::readis unaffected. - AP214: files with FreeCAD's
PRODUCT_TYPEnow take PMI (pzfreo/specify-core#89), with each keyword rewrite reported inedition_rewrites.
Added
- Haecceity
PmiRead.calloutslists decoded graphical PMI annotations and every
DMIA semantic link, including unresolved definitions, independently of the existing
presentation accounting count. Links use part-qualified model ids and follow JSON
transport ordering; note callout reading remains compatible.
Fixed
- Generated part notes share one numbered GENERAL NOTES block in JSON and STEP,
retaining each requirement's semantic link and the bounds-relative placement. - Regenerated PMI uses measured, compact dimension ladders and reserves datum
flags before labels. Saved-view packing keeps labels near their features and
text placements use the actual stroke transform, including clipped short spans.
Written linear ladders share the compact ink spacing and retain seven-view clearance. - For pzfreo/specify-core#89, write PMI into legacy AP214 files containing
PRODUCT_TYPE, changing only that keyword toPRODUCT_RELATED_PRODUCT_CATEGORY
and reporting each edition rewrite with its instance id and entity names. - For #161, isolate panicking recognition families and block their dependents while
preserving independent results. Incomplete recognition/6 and evidence/1 documents
add optionalfailuresandcomplete: false; successful output is unchanged. - Haecceity presentation v1 adds optional per-text
strokedmetadata, avoiding
duplicate generated labels, and orthographic views for generated annotation planes.
Generated general notes now form a bounds-relative column two capital heights
beside the part; written STEP notes use the same placement rule.