Skip to main content

b123d-recognisers

CI codecov PyPI Python versions License Ruff mypy

Recover useful engineering features from imported STEP and boundary-representation (B-Rep) geometry.

A STEP file normally gives a CAD application faces, edges, and solids, but not the design intent that produced them. b123d-recognisers analyses that topology and returns deterministic semantic records for features such as holes and counterbores, bosses, slots, pockets, pads, fillets, chamfers, grooves, hole and pocket patterns, and turned steps. The records contain ordinary, JSON-serialisable geometry values rather than build123d or OCP objects.

Most recognition families classify faces by native analytic surface type, so imported geometry should preserve its planes, cylinders and cones. STEP carries them, and every pinned fixture is proven to survive an export and re-import unchanged. Raised Pads additionally have measured support for exact planes re-expressed as B-splines; other B-spline families remain outside the proven domain. Raised Pad recognition also requires exact face membership in one valid closed solid; open shells, invalid bodies, and ambiguous or missing solid ownership return no Pad records. See docs/capabilities.md.

That makes the library a useful foundation for systems which inspect, classify, annotate, compare, or modify imported CAD. For example, a STEP editor can recognise a hole, present its diameter and axis as editable intent, and use those values to drive its own topology-editing operation. The recognisers recover evidence; the consuming CAD system decides what that evidence means and how an edit should be performed.

The package is Apache-2.0 licensed and independent of any drawing or editing application. It uses build123d/OCP internally as its B-Rep kernel, but its purpose is recovering meaning from geometry whose construction history is not available.

Recognise an imported model

Import a STEP file with build123d, then run the shared recognition orchestration to obtain one consistent feature inventory:

from build123d import import_step
from b123d_recognisers import FramedRecognitionResult, build_framed_recognition_result

part = import_step("gearbox-housing.step")
framed = build_framed_recognition_result(part)

if isinstance(framed, FramedRecognitionResult):
    for hole in framed.result.holes:
        print(hole.location, hole.axis, hole.diameter, hole.depth, hole.bottom)

build_framed_recognition_result() shares intermediate geometric analysis across recognisers and is the ordinary entry point for a CAD application. Retain its frame and exact local working shape while consuming its frozen result; the records can also be projected to JSON-compatible dictionaries for storage, indexing, comparison, or an editing pipeline.

For bounded lifecycle explanations from the same single run, use build_framed_recognition_report(). Its immutable report distinguishes evaluated-empty families, classification-gated families, accepted/rejected candidates and supported residual diagnostics. It is deliberately not an exhaustive explanation of unsupported geometry; a missing diagnostic does not prove that no unsupported feature is present.

Individual recognisers are also public when an application needs a narrower answer. Reusable evidence can be injected explicitly so it is not rediscovered:

from b123d_recognisers import analyse_cylinders, recognise_hole_patterns, recognise_holes

cylinders = analyse_cylinders(part)
holes = recognise_holes(part, cyls=cylinders)
patterns = recognise_hole_patterns(holes)

Inspect geometry for declared features

CAD front ends that create a declared feature from a selected face can use the supported, single-face inspection namespace instead of importing recogniser internals:

from b123d_recognisers.inspection import AnalyticSurface, SurfaceKind, inspect_face

inspected = inspect_face(selected_face)
if isinstance(inspected.surface, AnalyticSurface):
    if inspected.surface.kind is SurfaceKind.CYLINDER:
        print(inspected.surface.parameters, inspected.anchor)

The manifest and capability documentation freeze the kind-specific parameter positions and units. When an anchor is present, it is proved in or on the selected face's actual trim, including faces with holes or concave outer boundaries.

The namespace also groups the four consumer-proven family reads: classify_bevel / BevelReject, cone_rims, read_double_d_tool, and floor_face_anchor. Existing root, family-module, and experimental_geometry.inspect_face imports remain exact-object compatibility aliases. GeometryGraph, adjacency, blend collapse, correspondence, Candidate identity, and reconciliation are not part of this supported inspection API. The separate run-local evidence view below exposes only opaque accepted-feature and caller-face references.

inspection_api_manifest() returns the separately versioned, installed-wheel contract for this roster. It does not change the recognition capability-manifest schema. See docs/capabilities.md.

That contract includes the closed BevelReject.reason values and the ordered read_double_d_tool() result: (axis, major_diameter, across_flats, origin, depth, profile_direction). Diameters, origin coordinates, and depth use model-length units; profile_direction is unitless and axis is one of x, y, or z.

Resolve accepted features to caller faces

When a consumer needs the exact faces behind accepted occurrences, use the separate within-run evidence view:

from b123d_recognisers.evidence import build_recognition_evidence

view = build_recognition_evidence(part)
for feature in view.features:
    print(view.family(feature), view.record(feature).to_dict())
    proof_faces = [view.face(ref) for ref in view.defining_faces(feature)]
    feature_faces = [view.face(ref) for ref in view.constituent_faces(feature)]

FeatureRef keeps equal-valued occurrences distinct and FaceRef resolves to an original face of the exact input part. Defining faces prove acceptance; constituent faces are the equal or wider physical membership and do not participate in reconciliation. These opaque references are valid only with their issuing view, cannot be serialized, and are not persistent names across imports, transforms, edits, or separate runs. The caller must not mutate the part while using the view. The initial API is caller-coordinate only; it does not return references to a framed working shape as though they belonged to the original part.

Recognise independently of STEP placement

Use the opt-in framed route when the same physical part must produce local coordinates independent of its placement in the imported file:

from b123d_recognisers import FramedRecognitionResult, build_framed_recognition_result

framed = build_framed_recognition_result(part)
if isinstance(framed, FramedRecognitionResult):
    print(framed.frame.gauge)
    print(framed.part.bounding_box())  # the exact local shape used for recognition
    print(framed.result.holes)  # coordinates and axis letters are local to framed.frame

The paired PartFrame converts points in either direction with to_local() and to_world(). framed.part is the exact topology-preserving local working shape passed to recognition, not a consumer reconstruction. Its evaluated coordinates agree with framed.result, and framed.frame converts between it and the caller's input coordinates. Keep the successful FramedRecognitionResult alive while using topology-bearing recognition evidence: the result owns the identity relationship between that evidence and framed.part; the original input shape is a different caller-space object. FULL means geometry establishes a directed, ordered basis. ORTHOGONAL exposes an unobservable discrete sign or axis interchange, and AXIAL exposes unobservable roll. The axes returned for a gauged frame are deterministic representatives and must not be treated as semantic material directions. Geometry without an analytic direction returns a typed RefusedPartFrame.

This is the ordinary aggregate route for new integrations. If caller/world-coordinate records are deliberately required, use the explicit build_raw_recognition_result(part) route. The historical build_recognition_result(part) name remains a raw compatibility alias throughout 0.4.x and is scheduled for removal in 0.5.0; it will not silently acquire a different return type.

Bounded explanations have the same paired lifecycle:

from b123d_recognisers import FramedRecognitionReport, build_framed_recognition_report

framed_report = build_framed_recognition_report(part)
if isinstance(framed_report, FramedRecognitionReport):
    print(framed_report.report.families)

Use build_raw_recognition_report(part) only when the report and records intentionally remain in caller coordinates. A typed frame refusal never falls back to either raw route automatically.

When classification itself depends on the normalized solid, prepare first and run the aggregate once after making that local decision:

from b123d_recognisers import PreparedFramedPart, prepare_framed_part

prepared = prepare_framed_part(part)
if isinstance(prepared, PreparedFramedPart):
    rotational = classify_local_part(prepared.part, prepared.cylinders)
    framed = prepared.recognise(rotational=rotational)

PreparedFramedPart owns the exact frame, local working shape, and one precomputed cylinder inventory. Its recognise() method injects that inventory into the existing aggregate and pairs the result with the same frame and shape. A RefusedPartFrame remains explicit, so callers may choose one deliberate legacy fallback rather than silently guessing in caller coordinates.

Every recognise_* function returns a deterministic list of frozen dataclass records. Records provide to_dict() projections containing only JSON-serialisable geometry values. The installed package also exposes a versioned capability manifest so larger CAD systems can validate which recognisers and record schemas they consume. See docs/capabilities.md for the proven feature inventory and docs/adr/0002-uniform-deterministic-recogniser-contract.md for the complete contract.

Project an aggregate step ladder

The aggregate owns the one geometry-only rule that chooses between Z-turned shoulders and already filtered prismatic levels. Pass only the Z envelope it needs; no build123d object crosses this projection boundary:

z_min = part.bounding_box().min.Z
z_max = part.bounding_box().max.Z
step_zs = result.step_ladder_for_z_span(z_min, z_max)

The default boundary_margin=0.6 is measured in model length units (normally millimetres) and strictly excludes turned end faces at both ends. It can be overridden explicitly. The former result.step_ladder(bound_box) call remains as a deprecated 0.2.x compatibility shim and will be removed no earlier than 1.0.0. See ADR 0006 for the caller inventory and boundary decision.

Scope

Feature recognition is deliberately separate from feature editing. This package reports geometric facts; it does not mutate the source model, guess manufacturing intent, or prescribe a downstream CAD representation. That boundary lets an editor, drawing engine, CAM tool, model checker, or search/indexing service adopt the same recognition layer while retaining its own policy.

b123d-recognisers began as the recognition layer of Draftwright, but the runtime package does not import Draftwright and is designed for standalone use.

Migrated behavior

The initial 0.1 release series preserves the recognition behavior of Draftwright commit 3fe20b0f71a71deced06b310943dd44cc66e355e. The migration includes every public recogniser, shared cylinder/level substrates, the aggregate result, and feature_census. There are no feature policy changes; one previously platform-dependent numerical axis tie is normalized to the pinned baseline result. The checked-in semantic corpus records and continuously verifies the compatibility boundary; see migration/PARITY.md.

The dependency direction is:

consumer → b123d-recognisers → build123d/OCP

The runtime package does not import Draftwright and does not return build123d or OCP objects in public feature records.

Contributors: see Adding a recogniser for the AAG predicate, candidate/evidence, registry, reconciliation, and verification path.

Maintainers: see the release guide for the TestPyPI-first, OIDC-only publication process.

Licence

Apache License 2.0. See LICENSE, NOTICE, and THIRD_PARTY_NOTICES.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

b123d_recognisers-0.4.12.tar.gz (8.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

b123d_recognisers-0.4.12-py3-none-any.whl (413.0 kB view details)

Uploaded Python 3

File details

Details for the file b123d_recognisers-0.4.12.tar.gz.

File metadata

  • Download URL: b123d_recognisers-0.4.12.tar.gz
  • Upload date:
  • Size: 8.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for b123d_recognisers-0.4.12.tar.gz
Algorithm Hash digest
SHA256 a19efb333abd181b30f03b20be2aa575c1ffe1134bbda3765795a8ef7db68279
MD5 12d1fd4beb600fa06d257f4bda0c82e7
BLAKE2b-256 89c4a9a9324188b18543012a1a1946ef6892d29baa31edc1c9619dc60028af6d

See more details on using hashes here.

Provenance

The following attestation bundles were made for b123d_recognisers-0.4.12.tar.gz:

Publisher: publish.yml on pzfreo/b123d-recognisers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file b123d_recognisers-0.4.12-py3-none-any.whl.

File metadata

File hashes

Hashes for b123d_recognisers-0.4.12-py3-none-any.whl
Algorithm Hash digest
SHA256 2a5552cd8a81e288f11c2b6b73c1022f9b0c332c6c821c4936cd1978fe19fedc
MD5 0e645bdbe70589645f2ecd4206c88cea
BLAKE2b-256 b8de0beb8ad13b4f8abf1cc1314c9badb3d10519b50f200ae025cd5aa5a55ae0

See more details on using hashes here.

Provenance

The following attestation bundles were made for b123d_recognisers-0.4.12-py3-none-any.whl:

Publisher: publish.yml on pzfreo/b123d-recognisers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.16

2 files

0.4.15

2 files

0.4.14

2 files

0.4.13

2 files

This release

0.4.12 This release

2 files

0.4.11

2 files

0.4.10

2 files

0.4.9

2 files

0.4.8

2 files

0.4.7

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.9

2 files

0.2.8

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page