Rendering Engine:
WebGPU, the successor to WebGL. Browsers or
pages without WebGPU keep working: the viewer falls back to WebGL2 by
itself, and both draw the same picture. A shiny app served over plain
http from anything but the local machine is such a page, since browsers
only expose WebGPU in secure contextsMRI slices, volumes, electrodes, streamlines
and fat lines) were rewritten as three.js node materials, because
WebGPU cannot compile the previous GLSL codeGPU device is lost (a driver reset, or a laptop waking up)
now shows a message with a reload button instead of freezingshiny, a viewer that gets re-inserted (for example by renderUI())
reuses its cached viewer. Every re-insert used to build a second viewer while
the first one kept its graphics contexts, so a few re-inserts exhausted the
browser's supply of themthree.js Inspector panel,
replacing stats-gl, which always reported zero GPU timeStandalone Viewers (save_brain):
Chrome, Safari and Firefox.
Volume gradient maps, which were silently skipped in saved viewers, are
computed again, and surfaces and streamlines do their heavy set-up off the
main thread as they do on a serverRemote Control:
R scripts, Python, and AI agents
through the hub's MCP mode. viewer_hub_start() starts a small hub on this
computer, or reuses the one already running, and later viewers show its
passkey in the control panel's "Passkey" field; "Connect" joins the hub. A
viewer never connects on its own, not even a page that connected before.
Once connected, "Auto-connect" (on by default) brings it back when its hub
restarts with the same passkeybrain_proxy() sends in
shiny, read the viewer's state, receive clicks and other events, and take
screenshots that look like what the user sees. Agents find a running hub and
join it, or start one and give the user its passkey; they can also stop a hub
and remove a viewer. Viewers tell the hub whether they are shown, so a viewer
that its app keeps running hidden (for example after its pane was closed) is
listed as hiddenMCP tool drop_file
with a file on this computer (up to about 200 MB) or the text of a table;
other programs send the command drop_file. The answer names the objects and
control panel entries that the file addedNode.js 18 or newer) listens on 127.0.0.1 only. Without a key,
any program or web page on this computer can connect;
viewer_hub_start(generate_key = TRUE) adds one. save_brain() keeps the
hub's port in a saved viewer, not its keyexamples/ folder has R and Python clients and a skill
for AI agentsVolume Rendering Improvements:
Sobel filter for computing gradients on both continuous (MRI) and discrete (atlas parcellation) volume dataMatCap (Material Capture) texture-based lighting for improved visual quality of volumetric surfacesRawMaterial to wrap up changes to the uniformsGPU to render unless there is a need toNIfTI volumes too large for the viewer now load instead of crashing it.
A volume with more than 2^27 voxels per frame, or more than 2048 voxels
along an axis, is read decimated: the longest axis is halved until the
volume fits, and only those voxels are read (nearest voxel, no averaging).
When the non-zero voxels then fill a small enough box (at most 2^24
voxels), that box is read again at a finer resolution. Voxels keep their
exact positions, a console warning says how the volume was read, and the
limits are adjustable (MAX_VOLUME_VOXELS, MAX_VOLUME_CORE_VOXELS and
MAX_VOLUME_AXIS in the viewer constants). Compressed files are inflated
piece by piece, so a 1024^3 volume, or a .nii.gz larger than the
browser's memory for a single array, loads without being held wholeWorker Thread Infrastructure:
invokeWorker() method to ViewerApp for unified worker thread dispatch with fallback supportArrayBuffer transferable support for zero-copy data transfer between main thread and workerscomputeVolumeGradients as a worker-callable function for offloading heavy gradient computationDataCloneError (TypeError: Type error in Safari)
for every surfaceShader Optimizations:
colorChannels and dithering uniforms to compile-time shader defines for better GPU performanceUSE_GRADIENT_MAP and SINGLE_CHANNEL shader variants for optimized code pathsViewLighting) so the four views (main
plus three side canvases), which intentionally light with different
lights, no longer evict and rebuild each other's compiled shader and
pipeline on every switch, several times a frameEngine Updates:
three.js engine to r186three.js Clock with a single shared Timer, advanced once per frame, so all animation deltas within a frame come from one time baser185 and gave it a private shader key so it no longer competes with the upstream onejsm folder; optimized electrode shader to calculate inverse modelViewProjection in JavaScript rather than vertex-shaderjsPDF versionMain Camera Controls:
three.js Controls base
class and Pointer Events, replacing the separate mouse and touch handler
pairs it had carried since r80. Drags now use pointer capture, so releasing
the button outside the viewer (or outside the browser window) no longer
leaves the camera spinning, and pen input works like a mouseevent.deltaMode. Firefox reports wheel deltas in
lines rather than pixels, which made one wheel notch zoom roughly 30x less
there than in Chrome; both now zoom at the same ratecanvas.trackball.cursorZoom = false for the previous zoom-to-center behaviorgetBoundingClientRect() instead of the
deprecated window.pageXOffset, so rotation and panning stay aligned after
the surrounding page scrollsshiny tab,
display: none) no longer produces a NaN camera and a permanently blank
canvasA/S/D key bindings, which used the
deprecated keyCode API and collided with the viewer's own a, s, and d
shortcutswheel listener is registered as non-passive, removing the Chrome
console warning the viewer used to emit on every loadControl Panel:
Tweakpane instead of lil-gui.
The addController()/onChange()/folder API is unchanged, so existing
controllers = list(...) calls keep working for every controller not
renamed belowFrustum Near/Frustum Far became a single
Frustum controller, and Line MinLen/Line MaxLen became a single
Line Length controllerFrustum Near/Frustum Far and Line MinLen/Line MaxLen
are replaced by the Frustum and Line Length range controllers above.
Update any
controllers = list("Frustum Near" = ..., "Frustum Far" = ...) to
controllers = list("Frustum" = list(min = ..., max = ...)), and
controllers = list("Line MinLen" = ..., "Line MaxLen" = ...) to
controllers = list("Line Length" = list(min = ..., max = ...))Display Data (Graph) line plot:
it traces the focused electrode's value over the recording's time axis
with a play-head cursor, and stays hidden unless a continuous variable
with more than one time point is shown on the focused electrodeRadius Value picks a continuous
variable, Radius Range sets the values mapped onto two radii (the
variable's range by default), and Radius Ratio sets those radii in
millimeters, for example 0.1,2, or as multiples of each contact's own
radius with a leading x, for example x0.5,2. The middle of the range
draws the middle radius (1 for 0,2), and on each side of it the radius
changes on a log scale: its log is linear in the value. No contact is drawn
smaller than 0.1 mm, or 0.01 mm if its own radius is under 0.5 mm, or
0.0001 mm under 0.05 mm. Blank keeps the
original radius. Sizes follow the animation time; contacts without a value keep
their radius. Sphere electrodes and the contacts of prototype electrodes
are resized, prototype shafts are notVoxel Label and Line Simplify Factor now recompute only once a drag or
edit finishes, rather than on every intermediate value while draggingBackground Color also refreshes the activity graph above once
the color stops changing, keeping its colors in sync with the panel themeCopy Controller State and Paste to Set State still save and restore
panel values under the new panel engineView Layouts:
View Layout controller (under Volume Settings, after
Show Panels) for slice-centered layouts similar to ITK-SNAP:
3dview (the default, unchanged), sliceview-flat (the three slices in a
row), sliceview-axial, sliceview-coronal, sliceview-sagittal (one
slice featured large) and sliceview-twobytwo (a 2x2 grid). Set it from R
with controllers = list("View Layout" = "sliceview-twobytwo")sliceview-* layouts the slice panels are locked in place, the 3D view is
framed in the foreground color, and the canvas text (legend, timestamp,
title) scales with the 3D view so it does not crowd a small panell cycles the view layouts; Voxel Display moved to Shift+LStreamline Visualization:
Line Simplify Factor controller: a bundle's points are dropped while the
drawn line stays within that many millimeters of the original, and 0 draws
every point. Tract files are often resampled far finer than the screen can
show (the CIT168 alic bundles carry a point every 0.08 mm), and every
segment is transformed again in each of the four views. At the default
0.05 mm, those bundles draw 14 times fewer segments, use a third of the
graphics memory, and take between a fifth and a tenth of the frame time they
used to. Streamline lengths and the Line Selector distances are still
measured on all of the pointsWebGPU rejects, dropping the whole
frame. It was reachable with Line MinLen on any bundle of fewer than 200
streamlinestt streamline formattrk format; supported tck format (drag and drop)trk, tck, tt) no longer also shows up under
Configure ROI Surfaces. Its Visibility controller there did nothing, stayed
after Clear Uploaded Streamlines, and had the same name as the working one
under Configure Streamlines, so the remote controllers command could not
hide the tractKDTree to query the streamlines that intersect the target volumeadd_streamline resolves sub-folders of a circuit, so part of a circuit can be
loaded on its own (alic/stn/* instead of alic/*); the circuit is still the
top-level folder, and a bundle in a sub-folder keeps that sub-path in its namecircuit/bundle key, the same key add_streamline takes, so two circuits
holding a bundle of the same name are no longer ambiguous. Update any
controllers = list("Show: <bundle>" = ...) to
controllers = list("Show: <circuit>/<bundle>" = ...), including for the
default circuitSurface Thresholding:
Surface Threshold Data, Surface Threshold Range, and
Surface Threshold Method controllers, so a surface can be colored by one
vertex data while being thresholded on another, the way electrodes already
could. Vertices failing the threshold fall back to the underlay
(curv/sulc) colorSurface Color Data, including
drag-and-dropped [custom measurement] and [custom annotation]. Continuous
measurements use the same seven operators as the electrode threshold;
annotations threshold on a |-separated list of label namessync from voxels and sync from electrodes as well as verticesannot files now keep their per-vertex color table keys and label
names, which is what makes annotation thresholding possiblesync from electrodes and 200 active contacts, thresholding away 87% of the
surface renders about 3x faster; the un-thresholded path is unchangedSurface Colors:
Surface Color Map, Surface Color Min, and Surface Color Max
controllers, so continuous surface data added with add_annotation (for
example surf/sulc or surf/curv) can be re-colored from the viewer, not
only drag-and-dropped measurements. They show when Surface Color is
vertices and the Surface Color Data is continuous, and can be set from R,
e.g. controllers = list("Surface Color Map" = "viridis")±max|v| of the selected data and resets
whenever Surface Color Data changes. Both hemispheres now share this range
(they used to be scaled separately), and values outside it are clamped to the
end colors; use the Surface Threshold controllers to hide values insteadColor Map (Continuous) selector sets Surface Color Map, and the
surface Clipping Min, Clipping Max, and Dynamic Color controllers are
removedShiny Proxy:
brain_proxy() has a new field controller_specs and method
get_controller_specs(): the type, choices, range, folder, and visibility of
every controller, so an application can check values before calling
set_controllers(). The viewer re-sends them when its control panel changesshiny half a second after any
change, including changes whose handlers did not report (for example
Background Color or Voxel Display) and crosshair moves made by clicking
a slice, so $controllers and $plane_position no longer go stalebackground, surface_type, display_variable, and side_display
now follow the controllers; they used to return their defaults because the
viewer never sent themplane_position is reactive, as documentedset_display_data(), which failed in the viewer, and
set_focused_electrode(), which the viewer ignoredviewerApp.captureOnce event on the viewer's wrapper (.threejs-brain-canvas)
with an object as detail: the viewer draws a frame and, right after drawing
it, adds { canvas, dataURI } (a PNG data URL) to detail.views for the
main view and each shown side view, and sets detail.dataURI to the main
view. The viewer draws only when something changes, and a copy of a WebGPU
canvas taken outside the frame that drew it can be blank (Chromium) or out
of date. shidashi sends the event before it captures the viewer for an
agentset_zoom_level() and set_matrix_world() now redraw the viewer at once;
the change used to show only after the next mouse moveMinor Changes:
merge_brain is called but the subject is missingAdditional Data text containing an <a href=...> link now
renders as a clickable link in a message bar at the bottom of the canvas
instead of raw markup; links open in a new tabraveio from comments and using scanner RAS for slicesfsaverage_inCIT168 templateradiographic viewread_colormap to support reading from RAVE (JSON) or ITK-SNAP formatvolume_to_surf with the default implicit smoothing reduces surfaces with
more than max_vertices vertices (500,000 by default) with
ravetools::vcg_decimate before smoothing; a whole-brain atlas at
sub-millimeter resolution gives millions of vertices and used to crash R
while smoothing. The same lambda and degree smooth a decimated mesh
more, so raise max_vertices to smooth at full resolution. With a
ravetools version that has no vcg_decimate (0.3.2 and earlier, whose
implicit smoother crashes on large surfaces), surfaces with more than
20,000 vertices are returned without smoothing, with a warningvolume_to_surf gains smooth_method = "explicit" to smooth with
ravetools::mris_smooth (smooth_iterations rounds of neighbor averaging)
instead of the implicit solver, falling back to the "laplace" type of
ravetools::vcg_smooth_explicit when mris_smooth is not available, and
smooth_method = "none" to skip smoothing; neither is decimatedMeshBasicMaterial when rendering sphere electrodes for better performanceBlueRed color palette to match with the ravebuiltins colorfreesurfer_brain and embrace the new universal interface threeBrainrender method for template brain for remembering the states in shiny applicationsmerge_brain also gains new argument electrode_priority to control the priority when setting electrode shapeFreeSurfer curv file tooMNI152 R-axis to infer the hemisphere insteadYBA atlas color-mapdataURL0.52 (sulc) rather than sulc = 0.52), so a long data name, such as a
dropped file's, can run off the panel without hiding the valueElectrode Localization:
InstancedMesh contacts: inactive contacts suppress outlines; hide inactives visibility mode discards inactive contact fragments via HIDE_INACTIVE_CONTACTS shader defineguessHemisphere() to infer electrode hemisphere from FreeSurfer anatomical labelNIfTI and FreeSurfer):
FSLabel_*) were read from a voxel half a voxel below the
contact, so a contact near a region boundary could get the neighboring
region's label. Labels saved by earlier versions may be wrong; for
example, 6 of the 48 saved aparc+aseg labels of the RAVE demo subject
yael_demo_001 changeCT with non-cubic voxels placed contacts slightly off the
depth of the clicked voxel (up to 0.03 mm on a 0.39 x 0.39 x 1 mm CT)CT passed to brain$localize() or localization_module() as a loaded
image (rather than a file path) failed to show up, and once shown sat half
a CT voxel off in every direction (0.87 mm with 1 mm voxels), as did every
contact placed or refined on it. RAVE's electrode localization module
passes a file path and was not affected by this oneNew Experimental Electrode Prototypes:
PMT-2102-16-099 specificationsBF09R-SP51X-0BB specificationsBF07R-SP21X-0B0, BF08R-SP21X-0C2, BF09R-SP21X-0BB, and BF12R-SP21X-0C3 (AdTech) specificationsRD16R-SP03/05X (AdTech) specificationsAdTech RD series specifications (RD06R, RD08R, RD10R, RD12R, RD14R variants in multiple spacing sizes)DIXI-D08-15PIX and DIXI-D08-18PIXEL specificationsDIXI-MM08 electrode specificationsBehnke-Fried electrode specificationsNeuroOne/Zimmer electrode specificationsMedtronic 3387 and Medtronic 3389 DBS lead specificationsNeuroPixel NP1001, NP2004, and NP2014 shank specificationsSpencer-MXene-7x18 ECoG array specificationsDrag and Drop Improvements:
WebKit before fallback to naive approaches to support dropping in foldersTime column for a variable that
is already loaded (for example the values again after the coordinates, or edited
values after a _colormap.csv file) now updates the electrodes. It used to fail
silently and keep the previous valuesBug Fixes:
NIfTI volumes load; they used to fail with a missing type
errorNIfTI-2 headers are read correctly. Dimensions and data offsets with a
byte of 128 or more were misread (an axis of 384 voxels read as 128), so
such volumes failed to load or showed shifted voxelsdatacube2 (atlas) ISO surface: an unnormalized color-key byte
made every non-zero label take the >= 1 branch, so the whole surface
rendered as a single flat color (the last entry in the color map). Colors
now decode from display space correctly, with a matched emissive term so
the surface's brightness lines up with the ray-marched volume it stands in
for, and GLTF export bakes in the same corrected colorssRGB to linear color twiceMRI slices and slice overlays are drawn with each voxel exactly where its
coordinates say it is. They used to be shifted by up to half a voxel, which
showed at high zoom as the cross-hair not landing on the voxel it readssync from voxels read the voxel whose center is
nearest instead of a neighboring onethree.js r185 upgrade they showed one frame late, so a change could appear
not to take effect until the next interactionbrain_proxy$set_electrode_data() from a shiny module. Electrodes whose
value did not change between two such updates could keep the color of the
viewer's first frame (seen as stale cluster colors in RAVE's clustering
modules), and the side panels could keep an electrode's previous color after
any data changeMNI152 but no MNI305 columns now get MNI305
converted from MNI152; unless coord_sys was MNI152, the values used to
be copied unchanged. Rows whose MNI152 coordinates are all zero keep their
MNI305 coordinates missinglocalization_module() and localize() given a CT already read into R
now convert volumes whose NIfTI transform is coded as MNI152 from
MNI152 to MNI305 before placing them; they used to be treated as MNI305volume_to_surf warns and returns a mesh with a single vertex and no face
when no voxel lies within the thresholds, and still saves it to save_to;
it used to stop with a dimension is zero, which aborted loops over atlas
labelsvolume_to_surf treats NA, NaN, and infinite voxels as invalid and
leaves them out of the surface; infinite voxels above threshold_lb used
to be part of the surface when threshold_ub was NAcal_min and cal_max, always calculate the data minimum and maximum instead.jspdf versionatlas and annotation types are not specified explicitlyfMRI)AdTech-SD16R-AP0?XGLB format issuethreeBrain does not automatically download templates if annotation is not foundWASM by default to avoid triggering CORS condition that will terminate the programFileDataHandler classes to handle drag & drop files; supported drop-in electrode coordinate filesGIfTI reader now respect the transforms if there exists a transform with target space to be the scanner_anatadd_annotation automatically compiles annotations from template if missing#ffffffshader ignoring always-depth flag when outline is off; updated jsPDF to the 2.5.2 buildROI matricescolor and underlayColor attributes for surface underlayshader to support under- and over-lay colorsSurfaceShader to use morph normal and transformed positionscrosshair position is changed to scannerRAS and Affine MNI152; added tooltip title support for controllers; added global clock for app; updated ViewerCanvas constructor arguments; supported useMorphTarget for surface objectsuseMatrix when contacts are represented as spheresPMT electrodesget_atlas_values for continuous atlases such as binary or probabilistic ROIRAS distance instead of voxel indexing distance, hence more accurate when the atlas volume has imbalanced slice countactive-voxel to column-row-slice but still keep the naming for backward compatibilityVoxel filter is linear now when displayed at side slices only and when the slice mode is not active-voxelvoxel modeDBS (or electrodes with non-zero model up vectors)WebAssemblypandoc to save the whole page self-contained-500 so it does not hide behind the transparent surfacesadjust_brightness interpreted as quantileCT threshold back to positive (3000) when switching from DBS leads back to sEEG leads.Abbott segmented electrodes 6170-6173, with segmented electrodes clockwise viewing from proximal endDBS electrode prototype maker uses natural cos for x and sin for y. However, this will cause electrodes to rely on rendering on back side (material)BSC and Medtronic electrode orientation (now is counter-clockwise when viewing from proximal end)DBS segmented contactsMedtronic segmented electrodesBSC-DB electrodes for 2202 2201surface_offset has been added to electrode fieldcvs_avg35_inMNI152 has the correct talXFM (scanner to MNI305) nowload_mesh to surface object so users can load surface mesh for computation in Rcalculate_distances to compute the distances from a point to its projection to a list of meshDistanceShifted in electrode table is taken into account and will be passed to electrode instancesup vector is zero or is parallel with the model directionDBS electrode prototype BSC-DB-2202set_transform_from_points now tries to set rotation from transform Euler angle when the prototype guided marker is 1-ranked (electrode strip, DBS electrodes)GLTF not showing inner-most contact issuedevel version of ravetools from checkGithub action check scriptrhub checkshiny app to change current color map via proxyset_electrode_data to brain proxy class, allowing shiny applications to change the electrode data, set color palettes, and set value ranges in the same callT1D99v2 for monkey brainGLTF for datacube2fix_electrode_color bugISO surface generation from volumeGLTF is wrapped with try-catchfix_electrode_color is exclusive by defaultRAVE logo and fixed style issuesp to toggling the visibility and shift+p to switching surface typesGLTF binary format3D and 2D canvas;*h.pial.gii to FreeSurfer format when the files are missingDIXI (AM, BM, CM) electrode specificationsthreejs engine is upgraded to v160; added support for NIfTI and GIfTI imagesdataURI. This results in a truly standalone viewer: no extra engine required, only web-browser is needed.DBSROI surfacesnii,mgz) or surface (fs,gii) files to the viewer. The surface coordinate system is inferred from file name following BIDS convention. The surface color can be set via a color-map (csv) or in the file namefrustums (near and far) separatelysnap-to-electrode and line-of-sight mode in addition to canonical anatomical slices.
line-of-sight mode, the underlay image will be sliced dynamically with a plane orthogonal to the line of sight; two other (orthogonal) planes parallel to the line of sightsnap-to-electrode, the volume slices will be snapped to electrode direction (only available when electrode prototype is used and electrode direction defined). The other two planes will dynamically slice the MRIQR code to the viewer, allowing people to link to the publicationplot_slices to allow plotting MRI slices for each contact in canonical orderbrain$electrodes$fix_electrode_colorrave_slices.nii[.gz] under the mri/ in FreeSurfer folderJavaScript workers now (can be disabled with enable_cache=TRUE)ACPC alignmenttarget flag so objects can be rendered differently on main canvas versus side canvastype-company-version.json formatUV mapping on sphere electrodesbroadcast() to more controllers;quarto/rmarkdownsEEG electrodesinstancedMesh to render actual electrode spheres when prototype is used and users choose to see the actual contact locationsmapToTemplate for electrode prototypesPial surface does not write to depth buffer when it's super transparentarcball radiusshaders to render electrode outlinethreshold-only to show contacts that pass threshold even no display value is givenCITATIONRAVE colorbrain$plotdistanceRatio to prevent large shift (auto adjust)pial surface center as trackball centerdatacube2 to make rendering more natural on main canvasMRI slices visible when surfaces are missinginstancedMesh) are now click-abledispose is cleaner now, it also fires eventsmakeClickable and removeClickable to replace previous add_clickable functionregister_object to registerToMapdatacubedatacube overlay texture, including using clamp-to-border instead clamp-to-edgeinstancedMesh now works under contact-only modenormalize method (replaced by getNormalizedImage) from NiftiImage and MGHImagerandomColor, testColorString) and file-name utility functionsaddColorCoat and using ElectrodeMaterial, this results in massive code improvement in electrode instanceHexString, indicating that all values should be rendered with such color; Drag & Drop volumes can change to single colorsdatacube2)NamedLut color error when a value range is zeroRShinyDriver issue when object does not have construction parameters (using getThreeBrainInstance instead)ROIJavaScript map is hidden when compiled so browsers will not complain about missing mapsshader crash issue on WindowsWebGL2 is the hard requirement now; users will be notified if this requirement is not metcontroller.load to handle invalid set controller requesttextsprite depth issuesNIfTI filessform and qform are inconsistent in NIfTIROI labelas_subcortical_label generates correct white-matter labelsfreesurfer_lutauto-adjust feature (electrode localization)UV mapping issue in sphere electrode geometryshader issue when transparency is set to negative (treated as 1)NIfTI headers to get calculated color intensities before applying heuristic approachshader loop that accidentally used dynamic variable for looping, which may crash on Windows in certain situationsREADME.mdz-index base to the side canvas layer (div)Trackball uses width instead of height of the viewer as the Arcball radius; mouse positions is calculated whenever mouse-down event is triggered (allowing more accurate track-ball calculation in Shiny applications)col2hexstr internal functionbrain$electrodes$fix_electrode_color to fix the electrode colors under given data names (to display DBS electrodes, for example)controller.load more robust against errorsTextSpriteshaderthree.js to v160 with light model improvedrave_slices.nii to allowed MRI prefix in FreeSurfer folder with highest priority, such that this image will be treated as default volume to load in side canvas (default is still brain.finalsurfs.mgz)MRI to change brightness dynamically in viewervoxels index from zerosbrain$get_atlas_labels to guess the atlas labels from given masks or atlas filespandoc, which comes with RStudio)png to dependenceAC-PC alignment, available in RAVE - YAEL modulebrain to plot with additional customized geometriesplot_slides to plot MRI centered at each electrode contact for slide-to-slide visualizationm (previous shortcut to change the material type is changed to shift+M)CT in JavaScript when the sform and qform are different (have different code)YAEL paper is finally outget_ijk2ras to get Norig and Torig matrixMRI slices to be NIfTI formatdatacube2qform when reading NIfTI filesauto-refine functions in electrode localizationThis version is a major update.
threeBrain::threeBrain as the formal way to create brain objectsJavaScript engine to v150HTML tags from saved widget so the widget can be used from Jupyter notebook$render method to remember viewer statesThis version plans for a major update. Here are some highlights:
CT instead of re-sampled onesJavaScript engine from R code. The JavaScript code is available as npm package and is used via sub-moduleJavaScript, this allows reading files directly in viewer, and no cache is neededthreejs is required: standard npm distribution is usedGUI is implementedJavaScript directly via event dispatchersvoxel values are displayed when setting crosshairdatacube2 material uniformsthreejs to r148, and use npm distribution "three"data-GUI and use lil-GUIThrottledEventDispatcher.jsMouseKeyboard class to track mouse and keyboard events, greatly reduced canvas burdenanimParameter object to canvas to keep track of the animation parametersnii, mgh/mgz, FreeSurfer surface, FreeSurfer node value binary formatsthreeBrain to allow 3D viewer to run without cache (with fs only)JavaScript codenpm now)GUI support to change electrode label visibility and sizehandle_resize now put off resizing function when detecting widget size is too smallxfm not parsed correctly when the line starts with blank spacesJavaScript eventsraveio or ravedash is loadedPython3Windows, avoid backslashesDTI), or connect two electrodes dynamicallyCT to be either path or the actual data when calling localizing functionNifTi orientation issues and use sformNifTi transform matrix has determinant not equally to 1sync from voxels now clamp the surface nodesVoxel Display controller to toggle volume display modesl from Voxel Type to Voxel DisplayfragmentShader uses non-linear function to calculate fragment colors combined with face normal; this will create smoother resultsDataCube2 geometry uses ConvexHull instead of box/sphere to improve the performanceDataCube2 are no longer hidden when sync from voxels is on: users can use shortcuts l and k to easily control the visibility.download_template_subject can download other templates such as fsaverage, cvs_avg35, bert.Voxel Labels while surface colors are sync from voxelsThis version mainly works on the electrode localization. Most changes occur in the JavaScript engine.
RAVELocElectrode classshiny applicationdefault and volume side foldersChrome-specificTextTexture in the internal code to display text Sprite easilycsvMNI305 coordinatescrayon, base64enc, htmltools, pryr from dependencethreejs version to v131devFirefox)Mac and Linux (On Windows, it still needs R to be installed)threejs to a newer version and fixed compatibility issuespdf formatfreesurferformats face index starting from 1 instead of 0MNI305 coordinates instead of tkRAS coordinatesreticulate and related functions to support native R functionsMNI305 calculation issuesview_ct_t1 to view CT aligned to T1 imagesFreeSurfer brainnibabel and use native R packagesFreeSurfer and SUMA files sulcbrain_proxy to control in shiny elementFreeMesh material can be switched between MeshPhongMaterial and MeshLambertMaterialbackground, cex (font magnification), timestamp (display time)controllers (list) to override control UIsyncviewers (threejs_brain(control_presets = ...)) to synchronize multiple viewers in shiny environment.nii files, nibabel is now optionalT1.mgz or brainmask of brain.finalsurf is not found. This allows users only run FreeSurfer to the first stage, which only takes around 8 to 10 minutes instead of 6 hours.htmlwidgets in shiny mode. When calling threejsBrainOutput under shiny::renderUI, the DOM element might get reset by shiny, causing a new 3D viewer created. This might consume more resource, causing memory leaks and even conflict context renderings. Also event listeners couldn't reset correctly. A cache is created and now is reusing the canvas.threejs not garbage collecting geometries and texturesFreeSurfer folder.mgz, .m3z files