Anyone using v0.4.0's Export sulci (SVG) should re-export. The file it produced could not be
read by pycortex, or by any XML parser. This release fixes that. ROI drawing and export are
unaffected; nothing about rois.json has changed.
The sulcus export was broken three ways
A code review checked the output against cortex/svgoverlay.py for the first time.
- It was not well-formed XML. The fragment used the
inkscape:prefix with no namespace
declaration and no<svg>root, soET.parsefailed withunbound prefix. Nothing — Python,
lxml, a browser, Inkscape — could open the file. - Its
<text data-ptidx>labels crasheddb.get_overlay().Labels.__init__reads
float(text.get('x'))off every<text>in a labels layer. Ours carried nox/y, because a
vertex index cannot supply one.TypeError, before anything rendered. data-ptidxis pycortex's output, not its input.SVGOverlay.set_coordscomputes it by
kd-tree from each label'sx/y. A realoverlays.svgholds zero<text>elements —
Shape.get_labelpos()derives one label per path, so a two-hemisphere sulcus is labelled twice
for free.
Ten unit tests passed against all of this, because every one of them matched the output string
rather than parsing it.
What sulci.svg looks like now
A standalone SVG document: both namespaces declared, a real <svg> root, and the overlay's
width/height/viewBox so the path coordinates have a frame. No labels — but the labels layer
is still emitted, empty, because _find_layer(layer, "labels") raises ValueError without it.
The file carries its own merge instructions in an XML comment.
Installing it into a subject — this changed
Copy the <g inkscape:label="…"> groups out of #sulci_shapes into the subject's existing
#sulci_shapes group. Do not paste the whole <g id="sulci"> layer: SVGOverlay keys layers by
inkscape:label, so a second one named sulci silently replaces the subject's own, and every
pre-existing sulcus disappears. v0.4.0's docs said "paste or merge", which invited exactly that.
Also fixed, from the same review
- All five bezier edit ops now share one out-of-range contract (return an unchanged copy). Two threw
TypeError, one silently no-op'd, andsetAnchorSmoothon a closed curve corrupted the
smooth[]array — the exact desync the guard onmoveAnchor/moveHandlewas added to prevent. loadJSONdeep-copies the imported bezier and outline, as its comment already claimed.- A shape drawn before the viewer's SVG overlay finishes loading is now retried, instead of sitting
in the panel, listed and counted, never drawn. - The edit toggle refuses a shape with no editable curve, rather than half-entering edit mode.
- Timers and the adapter's host listener are released on
destroy()/ re-attach. - The lasso rejects a stray click, as the trace already did.
- Export byte counts are real bytes, not UTF-16 code units.
Testing
test/test_sulci_svg.py (new, in CI) generates the writer's real output with node and parses it
with ElementTree, using svgoverlay.py's own namespaces and findall queries. It needs neither
cortex nor a subject. Every new assertion was mutation-checked: break the fix, confirm the test
fails. Suite: 152 → 161 JS tests, plus six Python suites.
Still unverified, and stated plainly in TESTING.md: svgoverlay.py itself has never run on this
output. That needs a subject and an importable cortex.
Install
Download roidraw.bundle.js and add two tags to your viewer:
<script src="roidraw.bundle.js"></script>
<script>window.ROIDraw.autoAttach();</script>