Skip to content

Releases: gallantlab/pycortex-roidraw

v0.4.1 — the sulcus SVG export pycortex can actually read

Choose a tag to compare

@jackgallant jackgallant released this 09 Jul 20:52

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.

  1. It was not well-formed XML. The fragment used the inkscape: prefix with no namespace
    declaration and no <svg> root, so ET.parse failed with unbound prefix. Nothing — Python,
    lxml, a browser, Inkscape — could open the file.
  2. Its <text data-ptidx> labels crashed db.get_overlay(). Labels.__init__ reads
    float(text.get('x')) off every <text> in a labels layer. Ours carried no x/y, because a
    vertex index cannot supply one. TypeError, before anything rendered.
  3. data-ptidx is pycortex's output, not its input. SVGOverlay.set_coords computes it by
    kd-tree from each label's x/y. A real overlays.svg holds 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, and setAnchorSmooth on a closed curve corrupted the
    smooth[] array — the exact desync the guard on moveAnchor/moveHandle was added to prevent.
  • loadJSON deep-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>

v0.4.0 — sulcus drawing

Choose a tag to compare

@jackgallant jackgallant released this 09 Jul 08:05

Draw sulci as well as ROIs, stored the way pycortex already stores sulci.

Sulci

  • A sulcus is an open editable bezier curve. Pick Sulcus in the panel's new ROI | Sulcus selector and drag along the sulcus.
  • Export sulci (SVG) writes an overlays.svg-compatible fragment: open, unfilled <path> elements in a sulci layer. quickflat.add_sulci, the WebGL viewer, and Inkscape read it natively.
  • Trace each hemisphere separately and give both strokes the same name — they merge into one <g inkscape:label="…"> with a <path> per hemisphere, exactly as pycortex's own CaS is stored.
  • Sulci carry no vertex membership, because pycortex stores none (there is no get_sulci_verts; sulci are display geometry).

ROIs are unchanged

Closed bezier, enclosed-vertex membership, and the pycortex-roidraw/vertexset-v2 JSON are byte-for-byte as before. v1/v2 files still import. No format bump.

Fixes

  • Stale drag target. Pressing Delete while dragging an anchor or handle left the drag pointing into the pre-splice array: past the end it corrupted the tangent-handle arrays; still in range it silently moved the wrong anchor. Pre-existing — it affected closed ROIs identically.
  • An open curve's endpoints are always corners and show a single tangent handle.
  • Panel CSS: the segmented control's width/margin resets were inert due to selector specificity.

Internals

core/bezier.js gained open-curve support behind a closed flag rather than a fork. core/roi-model.jscore/shape-model.js (ROISetShapeSet, every shape carries a kind). New pure core/svg-export.js. No ViewerAdapter contract change.

Verified against a live pycortex viewer over the Chrome DevTools Protocol: sulcal paths bake with no trailing Z, ROI paths keep theirs, same-named sulci merge, names are XML-escaped, and no sulcus leaks into the ROI JSON. Not yet verified: feeding the exported fragment back through pycortex's svgoverlay.py parser. See TESTING.md.

Suite: 152 JS tests + Python tooling, green.

v0.3.4

Choose a tag to compare

@jackgallant jackgallant released this 02 Jul 20:22

Colored ROI outlines + three audit-followup fixes (full-project review after v0.3.3).

  • Colored outlines — ROIs now stroke in their palette color over a white halo (legible on both data and anatomy); the panel swatch finally matches the surface. Imported colors are hex-validated before rendering.
  • Bezier/vertex consistency — a fitted curve that encloses no vertices is no longer attached, so stored vertices never disagree with the editable outline.
  • Display-mode framing — auto-framing now runs only while drawing; unfolding in Display no longer fights the user's camera.
  • Adapter hardening — the vertex-morph offset no longer mutates pycortex's returned vector.

94/94 JS tests. Bundle asset: roidraw.bundle.js (94,844 B, SHA-256 f3c070dce309f6aade95ac8cd5cabd6e3455a5597860eee4cbcbfd66024c9d53).

v0.3.3

Choose a tag to compare

@jackgallant jackgallant released this 29 Jun 21:01

Readability/robustness pass from a full adversarial audit. Mostly internal — drawing/editing/export work as in v0.3.2.

Notable behavior changes:

  • Host preflight is stricter and fails with a clearer, more specific error if a pycortex viewer is missing the geometry roidraw needs (now checks both hemispheres' position + uv).
  • A blank ROI name now falls back to the default instead of being stored empty.
  • Imported ROIs missing a label get one via the same rule as freshly drawn ones (centroid-nearest).
  • The ROI delete control is now a real keyboard-accessible button.

Under the hood: extracted pure hit-testing + a unified draw pipeline, named the magic numbers, added a teardown/destroy() path, and grew the test suite to 93 (incl. a real-projection fake adapter and an independent selection oracle). See TESTING.md.

v0.3.2

Choose a tag to compare

@jackgallant jackgallant released this 29 Jun 18:08

Internal refactor + test hardening. No behavior change vs v0.3.1 — drawing, editing, export/import, and the overlay all work exactly as before.

What changed under the hood:

  • The flat-only Draw latch and the lasso→fit→re-derive pipeline were extracted into pure, unit-tested modules (core/draw-mode.js, draw-pipeline.js); the controller now delegates to them.
  • New test suite (45→80 JS tests): property-based geometry invariants, headless pipeline tests against a synthetic-surface adapter, an adapter-contract guard, a built-bundle smoke test, and CI on every push.
  • preflightHost() now fails loudly and names exactly which pycortex internal is missing if the host viewer drifts, instead of misbehaving silently.

See TESTING.md for what each layer guarantees.

v0.3.1

Choose a tag to compare

@jackgallant jackgallant released this 25 Jun 19:12

Draw-mode UX refinements:

  • Drawing is flat-only. Inflating the surface (unfold slider) while in Draw now returns you to Display.
  • Edit re-flattens. Clicking an ROI's ✎ edit button re-flattens the surface if it has been inflated, so the bezier anchors land on the flatmap.

Drop roidraw.bundle.js into any pycortex viewer (see README) — /releases/latest now points here.

v0.3.0

Choose a tag to compare

@jackgallant jackgallant released this 25 Jun 17:05

Drop-in ROI drawing for pycortex viewers. Download roidraw.bundle.js and add the two <script> tags (see README) to any pycortex WebGL viewer.