Skip to content

Add an anti-aliased voxeliser mode and release 3.10.0 - #382

Merged
psweens merged 4 commits into
masterfrom
claude/awesome-ritchie-pgunla
Oct 20, 2026
Merged

psweens merged 4 commits into
masterfrom
claude/awesome-ritchie-pgunla

Conversation

@psweens

@psweens psweens commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

computeVoxel.rasterise_segments(..., antialias=False) and process_network(..., antialias=False) gain an opt-in anti-aliased mode. By default a capsule sets every voxel whose centre it contains. At about two voxels across, how many that is swings with the centre line's position against the grid, so such a vessel renders beaded (about 1 voxel, then 2 × 2 across) and its area depends on its sub-voxel offset. With antialias=True each vessel gets the cross-section its geometry asks for in every lattice plane across it. Vessels two voxels across then keep their calibre at every orientation and offset, without constrictions. Off (the default), both functions run 3.9's code and draw its volumes bit for bit. Released as 3.10.0 with a CHANGELOG entry; not merged, tagged or published.

The rule, and why not band-limit-then-threshold

The requested rule (supersampled coverage → Gaussian σ 0.5 → threshold) was implemented and measured first. On a straight tube it is still the voxel centres inside a smooth, translation-invariant tube, so it beads exactly like the default:

  • d = 2: CV along the tube 0.101 (3.9: 0.103); oblique short necks 1.14 against 1.06 per mm;
  • foam network, at matched volume: 3–9 short necks per mm, depending on the threshold.

Smoothing before a binary threshold cannot remove lattice point-count fluctuation, so no σ is exposed.

The shipped rule is stated in full in _rasterise_antialiased's docstring. Polyline by polyline:

  1. Interior. Voxels at coverage-ramp 1, at least ½ voxel inside r_eff = √(r² − 1/12), are drawn.
  2. Counts. The other voxels near the surface fall into slices: a slice is one lattice plane across a run of segments sharing a dominant axis (hysteresis 0.1). A slice gets N = floor(T + e + 0.5) voxels:
    • T is its sum of the ramp clip(0.5 + r_eff − dist, 0, 1), so that the slice and the interior hold the capsule's volume;
    • e is half the previous plane's rounding remainder.
  3. Selection. Each slice is filled by score (dist − r), chain voxels first.
  4. Thin vessels. Below 2.45 voxels across, a voxel is skipped if it would complete a 2 × 2 × 2 cube with the polyline's drawn voxels. With connect, so is a voxel with no face neighbour among them.
  5. Clearance. A voxel outside the capsules is drawn only if every capsule more than √3 from its owner's lies more than √3 + max(0.5, √3/2 − r_min) from it. That distance is measured to the capsule's axis less its larger radius, so it holds along steep tapers.
  6. Finishing. The chain is unioned in with connect, and enclosed voxels are filled.
  7. Culling. Only voxels near the volume are examined, and segments far outside it are skipped.

Fixed constants: carry 0.5, hysteresis 0.1, cube threshold 2.45, support 0.5, separation √3. The 2.45 threshold was chosen by measurement: with it, straight tubes of d 2.2–2.75 have no short necks, while 2.3 leaves 1.96 per mm at d = 2.3.

How the rule was chosen:

  • Five rule families were prototyped against one measurement suite: per-plane quota, digital-tube stencils, dithering, thin/thick regimes and lattice zonogons.
  • Two independent judges re-ran the leaders; the quota family won.
  • Two grafts were added: the cube test runs against all of a polyline's drawn voxels, and a clearance guard makes the topology guarantee hold by construction.
  • An adversarial review then confirmed 16 findings, each reproduced independently; all are fixed in a9e888c, with tests shown to catch each mutation. The main ones:
    • the clearance used the score at the clamped projection, which a steep terminal taper could defeat;
    • segment boxes were not clipped to the volume, so a long segment through a small window ran out of memory;
    • one test asserted a non-guarantee for vessels 2 voxels apart.

Acceptance numbers, 3.9 → 3.10

Straight tubes: 20 directions (axis-aligned, face and body diagonals, 12 random) × 3 random offsets, 120 voxels long. A tube's area is its voxel count per lattice plane across the dominant axis × |u_k|. Rates are per mm at 2 µm.

d (voxels) worst tube's area error mean area bias mean CV along tube short necks / mm (oblique)
1.5 60 % → 13 % +4.8 % → +4.0 % 0.192 → 0.140 0 → 0
2 55 % → 6.5 % −1.5 % → −0.6 % 0.105 → 0.078 0.80 → 0
2.5 22 % → 3.0 % −0.1 % → +0.2 % 0.092 → 0.041 0 → 0
3 13 % → 2.0 % +1.0 % → −0.3 % 0.056 → 0.020 0 → 0
4 13 % → 3.5 % +0.4 % → +0.2 % 0.039 → 0.019 0 → 0
8 4.5 % → 0.5 % +0.1 % → 0.0 % 0.014 → 0.005 0 → 0
  • Tortuous tubes, short necks per mm (8 seeds): 4.06 → 0 at d = 2 and 12.5 → 0 at d = 2.2.

  • Networks grown at d_min 4 µm, rendered at 2 µm in 256³:

    • foam (98 % of its centre line at d ≈ 2): short necks 4.92 → 0 per mm, 26-components 2 → 2, 6-components 5 → 2, voxels +0.08 %;
    • capillary_bed: short necks 0.95 → 0, components equal.
  • Topology:

    • fixtures rendered whole (2, 4 and 8 µm; isotropic 64×64×32 to 512×512×140) have 3.9's 26-components;
    • in 48 slab renders, the pieces touching no face are as many as 3.9's;
    • in 1,200 tube pairs 1.74–1.9 voxels apart and 300 pairs 2 apart, none touched that 3.9 kept apart. Vessels thinner than √3 can touch through their chains under both.
  • Tapers and junctions render as one face-connected piece, with no cavity and no seam (3.9 itself leaves a one-voxel cavity in a d = 3 junction).

  • Runtime against 3.9:

    case ratio
    foam at 2 µm ×0.7
    capillary_bed at 2 µm ×0.8
    fixtures rendered whole at 2 µm ×0.5–1.1
    64³ and 128³ windows of the foam network ×0.1–0.4
    28 µm vessels at 1 µm ×1.9
    28 µm vessels at 0.5 µm ×3.0
    fat vessels sampled every 0.3 voxel ×2.2
    Lnet_i9_s3 in 512³ 5.0 s against 8.4 s

    Memory: 35–50 MB besides the volume on a 256³ window at 2 µm; 215 MB against 158 MB at 512³.

Where the targets bind (please weigh in)

  • Per-tube CV. The ≤ 0.10 target is met as a mean (0.078 at d = 2). The worst tube stays at 0.144: a plane that asks for n + ½ voxels must either alternate between n and n + 1 or carry a half-voxel bias. The carry of 0.5 is the knob; 1.0 gives exact means but a mean CV of about 0.107.

  • Long necks above the cube threshold. Short necks are zero at every diameter measured. Above 2.45, a vessel is too thin for an unbroken 2 × 2 × 2 opening, which costs about 4 voxels per unit length (+27 % over π r² at d = 2), yet holds cubes. Necks of any length on tortuous tubes, 3.9 → 3.10:

    • 38.8 → 0 per mm at d = 2;
    • 17.9 → 4.6 at 2.45;
    • 4.6 → 9.6 at 2.6.

    Raising the threshold to about 2.8 removes them, at the cost of cross- or staircase-shaped sections up to 5.6 µm vessels.

  • Clipped faces. Where a volume face cuts vessels, pieces touching that face can differ: a grazing edge kept whole, or one extra one-voxel piece (2 of 48 slabs).

  • Reference annotations. Thresholding a blurred image, which is how the reference annotations are made, also cannot remove beading at a true 2-voxel calibre. Their 0.37 per mm suggests their 4 µm vessels render wider than 2 voxels. That is worth checking against their measured area per unit length before vox2vess recipes are calibrated to nominal diameter.

Compatibility

  • With antialias=False, tests/test_antialias.DefaultTests compares against the 3.9.0 voxeliser in-process. That is tests/fixtures/reference_code_3_8/computeVoxel.py, unchanged since 3.2, with its sha256 checked. All pinned-release tests pass unchanged.
  • tests/test_geometry.ContractTests.test_process_network_signature_is_unchanged now pins antialias as an appended keyword defaulting to False. It is the one existing test changed.
  • main.py and the command line are unchanged.
  • library.code_hash covers computeVoxel.py and library.py, so a library begun under 3.9 is refused on resume under 3.10, as across earlier releases.
  • Version 3.10.0 in pyproject and library.LIBRARY_VERSION. Python 3.9 syntax; computeVoxel is numpy-only.

Tests

tests/test_antialias.py runs in about one minute. It covers:

  • straight-tube area and CV, with and without the chain;
  • short necks on oblique and tortuous 2-voxel vessels;
  • merges, including a steep terminal taper, and fixture components;
  • face connectivity of thin polylines;
  • tapers and junctions;
  • the stated invariants, determinism and polyline-order independence;
  • windows equal to crops, and the cost of a long segment.

With VSYSTEM_SLOW_TESTS=1:

  • FixtureTopologySweep runs the full fixture sweep (every voxel size, fit and slab);
  • AntialiasBenchmark reproduces the tables above.

🤖 Generated with Claude Code

rasterise_segments and process_network take antialias=False. Off, they
run 3.9's code and draw its volumes bit for bit. On, each vessel gets the
cross-section its capsules ask for in every lattice plane across its
dominant axis instead of every voxel whose centre a capsule contains, so
vessels about two voxels across keep their calibre along their length at
every orientation and sub-voxel offset instead of rendering beaded.

The interior (coverage ramp 1) is drawn outright. The voxels near the
surface are counted per plane from the coverage ramp, rounded by error
diffusion that carries half of each plane's remainder, and taken nearest
first, chain voxels first. Below 2.45 voxels across no 2x2x2 cube is
completed, so thin vessels have no opening to pinch into necks. A voxel
outside the capsules is drawn only where it cannot touch a capsule more
than sqrt(3) voxels from its own, so vessels that far apart touch only
where the default has them touch. The face-connected chain is unioned in
with connect, enclosed voxels are filled, and segments far outside the
volume are skipped.

Smoothing and thresholding the capsules' coverage was measured first and
left out: on a straight vessel it is still the voxel centres inside a
smooth tube, and it beads as the default does.

tests/test_antialias.py checks the default against the 3.9.0 voxeliser
(tests/fixtures/reference_code_3_8/computeVoxel.py, sha256 checked), and
the anti-aliased mode's area and cross-section along straight tubes,
short necks on oblique and tortuous two-voxel vessels, merges and
components, tapers and junctions, its stated invariants and its
determinism. A slow benchmark measures 3.9 against 3.10. The process_network
contract test pins antialias as an appended keyword. README section
"Anti-aliased rendering", CHANGELOG 3.10.0, pyproject and
library.LIBRARY_VERSION 3.10.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d on resume

library.code_hash covers computeVoxel.py and library.py, which 3.10
changes, so vsystem-library refuses to resume a 3.9 library, as across
earlier releases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@psweens psweens changed the title Add anti-aliased voxeliser mode to rasterise_segments Add an anti-aliased voxeliser mode and release 3.10.0 Oct 19, 2026
…and bound its cost

The clearance test kept a voxel outside the capsules away from every
capsule more than sqrt(3) from its owner's using the score at the clamped
projection. Behind the thin end of a steep taper that understates how near
the wide end is, so a two-point taper ending its polyline (r 1.5 to 3.8
over 0.65 voxel) could touch a tube 1.78 voxels away that the default keeps
apart. The distance is now taken to the axis less the larger radius, which
never exceeds the distance to the capsule.

Segment boxes are cut back to the volume grown by 2 sqrt(3) (r + 0.5) + 9
voxels, which keeps every plane across a vessel that reaches into the
volume whole. A box larger than a chunk is split into x slabs, candidates
are merged as they accumulate, the clearance grid is built in chunks over
the cells a candidate can reach, and the thin slices look up only the drawn
voxels around them. A segment 1,000 voxels long through a 64^3 volume takes
0.3 s instead of hours, a 256^3 window at 2 um needs 35-50 MB besides the
volume, and fat finely sampled vessels cost 1.7-2.2 times the default
instead of 3. A segment with a radius that is not finite keeps its chain,
as by default. The documented guarantees name where a cube can still form
in a thin vessel: a voxel enclosed at a sharp kink, or a junction with
another polyline.

The tests compare vessels 2 voxels apart with the default rather than
asserting that they never touch, which the chain of a vessel thinner than
sqrt(3) voxels can make false under both. They check that vessels sqrt(3)
across and more than sqrt(3) apart never touch, with enough parallel pairs
to see the clearance test removed, and the steep taper. They also check
anti-aliasing without the chain, face connectivity of thin polylines on
tight curves and along volume faces, a smoother cross-section than the
default at two voxels over a larger sample, windows equal to the crop of
the whole, and the cost of a long segment. The fixture component checks
keep a fast subset, and the full sweep of voxel sizes, fits and slabs runs
with VSYSTEM_SLOW_TESTS=1. The module takes about a minute instead of
nearly three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… implementation

Windows of the foam network take 0.1-0.4 of the default's time and match
the crop of the whole except where a carry restarts at the cull margin. A
long segment through a small volume costs what the volume holds. Renders of
28 um vessels take about twice the default's time at 1 um and three times
at 0.5 um, and 512^3 takes 5.0 s against 8.4 s at 215 MB against 158 MB. A
256^3 window at 2 um needs 35-50 MB besides the volume.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@psweens
psweens merged commit 075fdd6 into master Oct 20, 2026
6 checks passed
@psweens
psweens deleted the claude/awesome-ritchie-pgunla branch October 20, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants