sdf.PolarPattern
sdf.PolarPattern(sdf, count, axis='z', *, skip=())
Union of count copies rotated evenly around an axis.
Copy i is the child rotated by i * 2π/count about the axis; evaluated as the minimum over all rotated child evaluations, so the count must be a static Python int. Copy 0 is the original, which is why the child’s analytic faces still land on the result.
The default axis is the local z axis through the origin. A bolt circle rarely sits there, and no letter can be made to fit it either: a name says which way an axis points but not where it is, and where it is happens to be most of what a bolt circle means. So any other axis is given as a line — an :class:~cadjoint.construction.faces.Axis, most usefully the one a revolve already declares as solid.axis.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| sdf | Child shape (SDF node or plain callable). | required | |
| count | int | Number of copies (static Python int, >= 1). | required |
| axis | str | 'z' for the local z axis through the origin, or an Axis to rotate about that line. |
'z' |
| skip | Instance indices in 1 .. count - 1 that are not emitted. A ring of ribs or bolts is a pattern right up until something else — a coolant gallery, a cable exit, a keyway — has to occupy one of its stations, and then the choice is between abandoning the pattern and suppressing an instance. The kept copies keep the angles they would have had: instance i is still at i * 2π/count, so suppressing one leaves a gap rather than respacing the ring. Instance 0 is the seed the child’s faces are declared against and cannot be suppressed — rotate the seed instead. |
() |
Example
# Six screws on the bearing housing's own axis of revolution.
bolts = PolarPattern(screw, count=6, axis=housing.axis)
# Eight gussets, minus the two the coolant gallery passes through.
ribs = PolarPattern(rib, count=8, axis=housing.axis, skip=(3, 5))
Attributes
| Name | Description |
|---|---|
| skip | The instance indices this pattern suppresses, ascending. |
Methods
| Name | Description |
|---|---|
| patch_fields | Every kept copy’s patches, instance-major. |
| sdf | Pure function for the polar pattern (count must be concrete). |
patch_fields
sdf.PolarPattern.patch_fields()
Every kept copy’s patches, instance-major.
:meth:sdf is a min over the kept instances of the child evaluated at p rotated by -i * 2π/count about the pattern axis, so the ring’s surface is the union of those copies’ surfaces and the decomposition is the child’s fields under each kept instance’s point map. Instance 0 is left unrotated, exactly as :meth:sdf leaves it — origin + (p - origin) only equals p up to rounding, and copy 0 is the seed the child’s faces belong to.
The result holds len(kept) * len(child fields) fields laid out instance-major: all of instance kept[0]’s patches, then all of kept[1]’s, in the order :func:_kept_instances returns. Patch j of instance slot n is index n * len(child fields) + j — the slot, not the instance index, since suppressed copies contribute no fields at all.
Ownership by argmin_i |f_i| is exact wherever the copies are disjoint (a ring of bolts or ribs); where two copies overlap, the seam between them is a CSG seam the consumer re-derives from the surfaces like any other.
sdf
sdf.PolarPattern.sdf(child_sdf, p, count, origin, direction, skip_mask=0)
Pure function for the polar pattern (count must be concrete).
Copies 1..N-1 are traced once, over an array of instance angles, so the emitted program holds two copies of the pattern’s geometry (the unrotated original and the batched rest) rather than count of them. Copy 0 stays a separate, unrotated evaluation: origin + (p - origin) is only equal to p up to rounding, and copy 0 is the one the child’s face references are declared against. Under :func:~cadjoint.sdf._lowering.scalar_lowering every instance is unrolled again, because a shader cannot carry the batch axis.