Skip to content
Shapemetry

@huukhanhnguyen/types / objects/hatch

objects/hatch

HatchData

HatchData = { hatches: HatchEntity[]; }

Object-tier hatch datablock — the bag of hatches.

hatches

hatches: HatchEntity[]


HatchEntity

HatchEntity = { angle?: number; attributes?: Record<string, unknown> | null; color?: string; fill?: HatchFill; origin?: [number, number]; pattern: string; region: Curve2d; scale?: number; }

One hatch: a region, the line layers filling it, and where those layers sit.

HATCH SPACE is the 2D frame the lines are laid out in — millimetres on the region's own plane, NEVER a surface's uv. A parameterization is not arc-length, so a uv-laid pattern stretches on a revolved face, and two neighbouring faces have unrelated uv so their hatching breaks at the shared edge. Every surveyed system lays out on a plane in drawing units instead.

angle?

optional angle?: number

Rotation of every layer about HatchEntity.origin, in degrees. Absent === 0.

attributes?

optional attributes?: Record<string, unknown> | null

Opaque per-hatch claims. Absent or null === no claims.

color?

optional color?: string

One colour for every layer, overriding each layer's own. Absent === each layer keeps the colour its pattern gave it.

A drawing standard asks for the same pattern in another colour far more often than it asks for a different pattern, and duplicating the record to change a colour is how one pattern becomes six. The override is a single colour on purpose: a per-layer override would be the pattern again, written twice.

fill?

optional fill?: HatchFill

Paint behind every layer, over the whole region. Absent === the region is not painted and only the lines are drawn.

origin?

optional origin?: [number, number]

Where the pattern is anchored, in hatch space. Absent === [0, 0] of hatch space — NOT the region's own extent, and that difference is the whole point.

Two regions sharing a plane share hatch space, so an anchor of [0, 0] is the same world point for both and their hatching lines up across the edge they share; anchoring to each region's own extent would give them two different anchors and break exactly that. It is also why the pattern stays put when the region is edited. Rhino calls this m_basepoint; Archicad exposes the same choice as linking to the project origin versus linking to the fill's own origin.

pattern

pattern: string

The patterns[] record whose line families fill this region, BY NAME.

By name and never inline: two hatches drawn with concrete must be the same concrete, one edit must reach both, and every interchange format references a pattern by name rather than embedding it. A pattern that draws nothing is how a region gets HatchEntity.fill and no linework over it — the empty case lives in the RECORD, so there is still exactly one way to say it.

region

region: Curve2d

The region being filled: closed rings, with transformation placing their plane in the world. Rings that nest punch holes by the even-odd rule — no ring declares itself an island, the same way BrepLoop does not, because two ends of one claim can disagree.

Rings, not a BrepData: a hatch region is planar by definition, and carrying a trimmed-surface record for it stored the plane twice (once as Hatch.plane, once inside the face's surface) and let the two disagree.

scale?

optional scale?: number

Uniform scale about HatchEntity.origin, multiplying each layer's shift, spacing, dashes and origin. Absent === 1.


HatchFill

HatchFill = { color: string; type: "solid"; } | { end: [number, number]; repeat?: number; start: [number, number]; stops: ColorStop[]; type: "linearGradient"; } | { end: [number, number]; repeat?: number; start: [number, number]; stops: ColorStop[]; type: "radialGradient"; }

Paint laid over the whole region, behind the lines. Archicad carries the same thing as a fill's background pen; DXF as its solid-fill flag.

PDF is the system that got the shape right — one object discriminated by a type tag. Rhino instead put an enum on the pattern and had to abandon its gradient arm (the enum value is commented out in opennurbs_hatch.h), then bolt gradient back on as four loose fields of ON_Hatch.

ONE tag, never a tag inside a tag: the two gradient arms are spelled out in full and named after SVG's own elements, linearGradient and radialGradient.

Union Members

Type Literal

{ color: string; type: "solid"; }

color

color: string

CSS colour string.

type

type: "solid"


Type Literal

{ end: [number, number]; repeat?: number; start: [number, number]; stops: ColorStop[]; type: "linearGradient"; }

end

end: [number, number]

repeat?

optional repeat?: number

Ramp repetitions across start→end. Absent === 1.

start

start: [number, number]

Ramp endpoints in hatch space.

stops

stops: ColorStop[]

Ramp stops, the shape the textures lane already normalizes to.

type

type: "linearGradient"


Type Literal

{ end: [number, number]; repeat?: number; start: [number, number]; stops: ColorStop[]; type: "radialGradient"; }

end

end: [number, number]

repeat?

optional repeat?: number

Ramp repetitions across start→end. Absent === 1.

start

start: [number, number]

Centre, and a point on the outer ring, in hatch space.

stops

stops: ColorStop[]

Ramp stops, the shape the textures lane already normalizes to.

type

type: "radialGradient"


HatchInstance

HatchInstance = BaseInstance & { material?: string; }


HatchLine

HatchLine = { angle: number; dashes?: number[]; origin: [number, number]; shift: number; spacing: number; }

One family of parallel infinite lines carrying a repeating pen cycle — the whole mathematics of a hatch, and nothing else. One of these is exactly one data line of an AutoCAD .pat file, so a pattern definition copies straight in and straight back out.

Measured 2026-08-22 against the AutoCAD 2023 acad.pat shipped on this machine (72 patterns, 266 data lines): the format has EXACTLY this one mechanism. There is no finite-shape primitive. *AR-CONC's stones are three families of short dashes at three angles whose marks cross — the triangle is a visual effect, not an object, and the three cycles (9.0, 7.2, 7.6488) are deliberately incommensurate so the result never repeats.

A closed repeating figure IS constructible with this mechanism: give the families a common lattice and the dashes meet at their endpoints. What the mechanism cannot do is FILL such a figure — lines bound nothing on their own, which is what HatchEntity.fill is for.

angle

angle: number

Direction of the family, in degrees.

dashes?

optional dashes?: number[]

Repeating pen cycle measured along the line, in the SVG spelling: all values POSITIVE, alternating, EVEN indices draw and ODD indices lift. An odd-length list repeats once to make it even, as SVG's stroke-dasharray does.

.pat instead signs the values (negative = lift). The signs are the one thing that makes that format hard to read, and they are recovered on export by alternating them back.

A 0 in a DRAW slot is a dot, painted at HatchLineEntity.dotSize. Absent === a solid, unbroken line.

origin

origin: [number, number]

A point the first line passes through, in hatch space.

shift

shift: number

Slide ALONG the line direction between one line and the next.

Sliding a solid line along itself returns the same line, so this only becomes visible once HatchLine.dashes breaks it up: it staggers the marks of successive lines. A value that does not divide the dash cycle never comes back into phase, which is how a pattern reads as random while staying fully determined.

spacing

spacing: number

Distance ACROSS, between one line and the next.


HatchLineEntity

HatchLineEntity = { color?: string; dotSize?: number; line: HatchLine; lineWidth?: number; }

One drawn layer of a hatch: how the line looks, plus where the lines are.

The split is the point. line is pure mathematics and is exactly a .pat data line, so it round-trips to DXF untouched and an author (or a generator) can paste one in. Everything beside it is appearance, which .pat has no room for and which is simply dropped on export to that format.

Layers paint in array order, so a later layer draws over an earlier one.

color?

optional color?: string

CSS colour string, the spelling Material.baseColor already uses. Absent === inherit from the placement's material.

dotSize?

optional dotSize?: number

Diameter painted for a zero-length dash. Absent === HatchLineEntity.lineWidth.

line

line: HatchLine

Where the lines are.

lineWidth?

optional lineWidth?: number

Stroke width in millimetres. Absent === the renderer's hairline.


HatchObject

HatchObject = BaseObject & { data: HatchData; instances: HatchInstance[]; type: "hatch"; }

An object whose data is hatches and nothing else.

Last updated: 📖 8 min readEdit on GitHub