Repository navigation
Add an anti-aliased voxeliser mode and release 3.10.0 - #382
Merged
Merged
Conversation
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>
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
computeVoxel.rasterise_segments(..., antialias=False)andprocess_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. Withantialias=Trueeach 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:
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:connect, so is a voxel with no face neighbour among them.connect, and enclosed voxels are filled.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:
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.
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³:
Topology:
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:
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:
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
antialias=False,tests/test_antialias.DefaultTestscompares against the 3.9.0 voxeliser in-process. That istests/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_unchangednow pinsantialiasas an appended keyword defaulting to False. It is the one existing test changed.main.pyand the command line are unchanged.library.code_hashcoverscomputeVoxel.pyandlibrary.py, so a library begun under 3.9 is refused on resume under 3.10, as across earlier releases.library.LIBRARY_VERSION. Python 3.9 syntax;computeVoxelis numpy-only.Tests
tests/test_antialias.pyruns in about one minute. It covers:With
VSYSTEM_SLOW_TESTS=1:FixtureTopologySweepruns the full fixture sweep (every voxel size, fit and slab);AntialiasBenchmarkreproduces the tables above.🤖 Generated with Claude Code