Feature declarations

These constructors create Draftwright IR features from build123d objects or explicit values. They are useful when supplying a declared PartModel; most applications should prefer the corresponding Sheet verbs.

hole(obj=None, *, diameter=None, at=None, axis=None, through=True, depth=None, cbore=None, spotface=None, csink=None, thread=None, count=1, members=())

A drilled hole. Either hole(tool_cylinder) — read ⌀ / axis / location off the build123d object you subtracted — or hole(diameter=6, at=(20, 10, 0), axis="z").

cbore / spotface are (diameter, depth) pairs; csink is a (major_diameter, included_angle) pair (a flat-head seat, callout ⌵ Ø.. × ..°); thread is a tap/thread spec string (e.g. "M3x0.5") folded onto the callout (#764); count + members describe a machining-spec group drawn as one count×.

An object supplies defaults; any explicit keyword overrides that field (#451).

double_d_bore(obj=None, *, major_diameter=None, across_flats=None, at=None, axis=None, depth=None, through=True, profile_direction=None)

A through double-D bore, from its cutter or explicit geometric values.

major_diameter is the diameter of the parent circle and across_flats is the distance between its two parallel chords. The object form accepts an extruded cutter with exactly that boundary; arbitrary line/arc profiles and blind profiles fail closed.

boss(obj=None, *, diameter=None, height=None, at=None, axis=None, span=None, thread=None)

An external cylindrical boss / OD. Either boss(cylinder) or boss(diameter=6, at=(0, 0, 0), axis="x") (parametric). An object supplies defaults; any explicit keyword overrides that field (#451).

polygonal_boss(*, side_count, across_flats, height, at, axis, flat_directions, flat_centres, span=None)

A declared regular polygonal-prism boss, sized across flats and by height.

This explicit form carries the physical side-face directions and anchor points used by placement. Geometry-object inference is deliberately absent: a detached prism does not prove that it is an attached boss, while an author calling this verb does.

step(obj=None, *, diameter=None, length=None, at=None, axis=None, span=None, thread=None)

One axial segment of a turned profile — its OD + length. Either step(segment) (⌀ from the cylindrical face, length + centre from the bbox along its axis) or explicit step(diameter=4, length=10, at=(0, 0, 0), axis="x"). span (the two axial end-points) is derived from at + length when not given. An object supplies defaults; any explicit keyword overrides that field (#451).

chamfer(obj=None, *, axis=None, leg=None, leg1=None, leg2=None, angle=None, at=None)

A chamfer (bevelled edge, #560/#576). Either chamfer(bevel_face) — the oblique chamfer face supplies axis, both legs and a leader point on the bevel — or explicit chamfer(axis="z", leg=6, at=(x, y, z)). leg is an equal-leg 45° chamfer (callout C{leg}); give both leg1/leg2 for an asymmetric one ({leg} × {angle}°). The angle is always derived from the legs; an explicit angle= is only validated against them (a one-leg-plus-angle spec is not yet supported — #581). An object supplies defaults; any explicit keyword overrides (#451).

fillet(obj=None, *, axis=None, radius=None, at=None)

A fillet (rounded edge, #561). Either fillet(round_face) — the cylindrical blend face supplies axis, radius and a leader point on the round — or explicit fillet(axis="z", radius=3, at=(x, y, z)). Called out R{radius} (grouped n× R for equal radii). An object supplies defaults; any explicit keyword overrides (#451).

flat(obj=None, *, axis=None, across=None, at=None, axis_line=None, stock_span=None, axis_direction=None)

A machined flat on round stock (#148b). Either flat(flat_face) — the planar face supplies the leader point at (axis= and across= still required, being unrecoverable from a plane) — or fully explicit flat(axis="z", across=15, at=(x, y, z)). axis_direction= carries a non-principal stock direction; axis_line= and stock_span= then complete its geometric identity. Called out {across} A/F (across flats). An object supplies the at default; any explicit keyword overrides (#451).

groove(obj=None, *, axis=None, width=None, diameter=None, at=None)

A turned / circlip groove on round stock (#148c). Either groove(floor_face) — the reduced-OD floor face supplies axis, width, diameter and the leader point at — or fully explicit groove(axis="z", width=3, diameter=16, at=(x, y, z)). Called out {width} WIDE × ø{diameter}. An object supplies defaults; any explicit keyword overrides (#451).

rotational(*, od, bores=(), at=None, axis=None)

A turned body's axial furniture — its outer diameter, rotation axis and concentric bores (#945). Explicit values only: rotational(od=30, bores=(16,), axis="z").

The last recognised kind with no declarative surface, which made it the last ADR 0011 round-trip gap (epic #574). Its absence was load-bearing rather than cosmetic: a RotationalFeature carries planned dimensions (od, each bore), so a generated script could not name them, and the dimension mirror fell back to auto_dimensions() for the whole part — one unsupported feature turning every other declaration in that script from explicit back to implicit (#938).

There is deliberately no object form, unlike every sibling verb — the signature is keyword-only so rotational(shaft) is rejected by Python rather than accepted and then guessed at. An object form would have to re-derive the OD, the axis AND the concentric bores from geometry, and detection does not read those off the solid: they come from the part CLASSIFICATION (analysis._classify_geometry / _sizing_bores), which also decides which concentric bores are sizing bores. A declare-side reimplementation would be a second inference path for one fact, and the first cut proved the point — it silently dropped every bore and picked the wrong axis for a cylinder as long as it is wide (#949 review). A convenient object form that quietly changes the drawing is worse than an explicit one. Restoring it needs the classification factored into something both sides call — #950.

bores are concentric bore diameters in display order, each placed as a centred leader, and are Z-axis only — a rule :class:~draftwright.model.ir.RotationalFeature owns and states, since every route into the IR needs it. at is a point on the rotation axis (default the origin); it is carried for round-trip identity and does not itself position the furniture, which the renderer places from the part's projected centre (#952).

plate(obj=None, *, axis=None, lo=None, hi=None, u=None, v=None)

A thin slab's thickness (#559/#577) — a base plate, an upright wall, a rib. Either plate(slab_box) — the thin axis, its lo/hi extent, and the u/v slab centre read off the object's bbox — or explicit plate(axis="z", lo=0, hi=4, u=10, v=5). hi - lo is the thickness; u/v locate the thickness dim on the other two axes (in axis order). An object supplies defaults; any explicit keyword overrides (#451).

step_level(obj=None, *, base=None, levels=None, shoulders=None, datum=None, at=None, level_supports=None)

A prismatic height ladder + step-position shoulders (#555/#578) — a rebated / stepped block. Either step_level(part)base, the interior levels, the (axis, position) shoulders, the datum and the frame anchor at read off the part — or explicit step_level(base=0, levels=(10,), shoulders=(("x", 30),)). levels are the interior step Z-coords (unique, strictly increasing, each above base); a shoulder is where a step changes height, its position measured from datum along a horizontal axis (x/y). at is the IR frame origin (like every sibling constructor); it defaults to the datum X/Y at base and is inert for step rendering. level_supports may carry one (level, x_span, y_span) record per level so a dimension retains the face that established it (#915). An object supplies defaults; any explicit keyword overrides that field (#451).

slot(obj=None, *, width=None, length=None, long_axis=None, width_axis=None, depth_axis=None, w_center=None, lo=None, hi=None, at=None)

A milled slot / reduced across-flats section. From an object the depth (through, not stored) axis defaults to the shortest bbox span; the two remaining axes are read as long_axis (the longer) / width_axis (the shorter). lo/hi are the extent along the long axis and w_center the centre across the width axis. An object supplies defaults; any explicit keyword overrides that field (#451).

Pass depth_axis= when the cutter's through span is not the shortest — a through-Z milled slot cut by a tall cutter has Z as its longest span, so the shortest-span default would mistake Z for the long axis (#490). Naming the depth axis excludes it, so long/width are read from the two in-plane axes.

pocket(obj=None, *, width=None, length=None, depth=None, long_axis=None, width_axis=None, depth_axis=None, w_center=None, lo=None, hi=None, at=None, edge_anchored=False)

A blind rectangular recess — a floored slot/pocket, dimensioned width × length × depth (#148a). The blind counterpart of :func:slot: unlike a through-slot the depth IS a stored size, read from the object's span along the depth_axis. From an object the depth axis defaults to the shortest bbox span (a shallow recess); the two remaining axes are long_axis (the longer) / width_axis (the shorter). Pass depth_axis= when the recess is deeper than it is wide (#490-style). lo/hi are the extent along the long axis and w_center the centre across the width axis. An object supplies defaults; any explicit keyword overrides that field.

channel(*, width, long_axis, width_axis, w_center, lo, hi, d_lo, d_hi, open_sign=1, at=None)

A full-span floored channel, dimensioned only by wall-to-wall width.

The longitudinal and depth bounds identify the physical channel and preserve recognition/declaration parity; they are not additional dimensions.

pad(obj=None, *, x0=None, x1=None, y0=None, y1=None, z0=None, z1=None, at=None)

A bounded rectangular raised pad, dimensioned by footprint and location.

pad(pad_solid) reads its axis-aligned bounding box; the explicit flavour accepts the six bounds used by generated Sheet scripts.

pattern(member, *, kind, count, at=None, axis=None, members=(), bcd=None, pitch=None, direction=None, grid=None, rows=None, cols=None, angle=None)

A hole pattern = count × a member hole (build one with :func:hole). The arrangement (bolt_circle / linear / grid) and its defining dims (bcd / pitch / grid + rows/cols/angle) are supplied, not read.

at (the pattern centre) and axis default to the member's frame. The member centres are computed from the arrangement so the pattern renders like a detected one (its count× balloon, BCD centreline and pitch dims anchor on members); pass members= explicitly to override the computed layout (required for kind="other").

pocket_pattern(member, *, kind='linear', count, at=None, members=(), pitch=None, direction=None, grid=None, rows=None, cols=None, angle=None)

count × an identical blind pocket in a linear / grid array (#841) — the recess analog of :func:pattern. member is one representative pocket (build it with :func:pocket); the array renders as ONE grouped N× W × L × D DEEP callout plus the (n-1)× pitch dim(s), instead of N competing size dims.

The arrangement lies in the pocket's OPENING plane (perpendicular to its depth axis), so the members are laid out about at (default the member's own centre) in that plane — pass members= to override the computed layout. pitch (linear) / grid + rows/cols (grid) define the spacing and are read by the pitch-dim furniture.

slot_pattern(member, *, kind='linear', count, at=None, members=(), pitch=None, direction=None, grid=None, rows=None, cols=None, angle=None)

count × an identical milled slot in a linear / grid array (#841) — the through-slot analog of :func:pocket_pattern. member is one representative slot (build it with :func:slot); the array renders as ONE grouped N× SLOT W × L leader plus the (n-1)× pitch dim(s), instead of N competing size dims (some of which drop for lack of room, #841 behaviour 1).

A slot has no depth, so the array lies in the face plane perpendicular to the slot's THROUGH axis (the one that is neither width nor long). Members are laid out about at (default the member's own centre) in that plane; pitch (linear) / grid + rows/cols (grid) define the spacing. Explicit members= is rejected — the computed layout is the truthful one (see :func:pocket_pattern).

envelope(obj)

The overall bounding box of obj as width (X) / height (Z) / depth (Y), matching the detector's prismatic envelope.

Measured on the SOLIDS, not on obj as handed in. An AP242 STEP import is a compound of the part plus its PMI presentation geometry — annotation planes, leader curves — and measuring the whole thing declared CTC01 as 1170 × 650 where the part is 800 × 450: an envelope 370 mm too wide, silently, in anything a user declared by hand (#977).

The frame origin is the bbox CENTRE, matching detect.py — it was bb.min.Z, so a hand-declared envelope sat a half-height below a detected one on the same part. Invisible through the emitter, which bakes the detected frame explicitly, and therefore only ever wrong for someone writing sheet.envelope() themselves — which is what the README shows. The same shape of defect as the measurement above: this verb claimed to match the detector and did not (#977).

_solids_body is the engine's existing answer to that, already used by _analyse and Sheet.model so a caller inspects the body the engine draws (#453). Sharing it is what makes the docstring's "matching the detector's" claim true rather than aspirational; the detected path was always right, and it is this declared verb that was measuring the file.

datum(letter, ref, part=None, *, view=None, side=None)

A datum feature symbol (ISO 5459) on ref — a feature or a planar face (ADR 0011 P2c).

finish(ra, ref, part=None, *, view=None, side=None)

A surface-finish symbol (ISO 1302, Ra) on ref — a feature or a planar face (P2c).

note(text, ref, part=None, *, view=None, side=None)

A free-text manufacturing note (#488) on a leader to ref — a feature or a planar face (ADR 0011 P2c). The shop callouts detection can't infer: thread specs (M3x0.5 TAP), DEBURR, chip-relief, knurl. Placed like the GD&T items (a first-class ADR 0009 corridor candidate), not the dimension planner.

control_frame(characteristic, tolerance, ref, part=None, *, datums=(), diameter=False, modifier=None, view=None, side=None)

A geometric-tolerance feature control frame (ISO 1101) on ref — a feature or a planar face (ADR 0011 P2c.2). characteristic is a lowercase ISO 1101 name ("position" …); datums the referenced datum letters; diameter prefixes the zone with ; modifier a material-condition symbol ("M"/"L"/"P").

measured_dimension(*, kind, value, label, dominant_axis, ref_pts, ref_bbox=None, at=None, axis=None, upper_tol=None, lower_tol=None, lower_bound=None, upper_bound=None, source='sheet', source_kind=None, source_id='')

A pre-authored drafting dimension from explicit measured values — the IR constructor behind :meth:Sheet.measured_dimension (#704: extracted so build_drawing(model=…) callers can author one without the façade). Validates the kind against :data:~draftwright.model.ir.AUTHORED_DIMENSION_KINDS, needs ≥2 ref_pts, and derives at (the ref_bbox centre, else the ref_pts centroid) when not given. lower_bound and upper_bound carry a limit range and must be supplied together; they are mutually exclusive with deviation tolerances. source_id preserves an external record identity; ordinary Sheet declarations leave it blank.