@niivue/niivue 0.44.0 → 0.44.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -4,16 +4,13 @@ var __export = (target, all) => {
4
4
  for (var name in all)
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
- var __publicField = (obj, key, value) => {
8
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
9
- return value;
10
- };
7
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
11
8
 
12
9
  // src/niivue/index.ts
13
10
  import { mat4 as mat46, vec2, vec3 as vec311, vec4 as vec48 } from "gl-matrix";
14
11
 
15
12
  // package.json
16
- var version = "0.44.0";
13
+ var version = "0.44.2";
17
14
 
18
15
  // src/logger.ts
19
16
  var _Log = class _Log {
@@ -1271,11 +1268,11 @@ uniform mat4 mvpMtx;
1271
1268
  uniform mat4 normMtx;
1272
1269
  out vec4 vClr;
1273
1270
  out vec3 vN;
1274
- out vec4 vP;
1271
+ out vec4 vPc;
1275
1272
  void main(void) {
1276
1273
  vec3 lightPosition = vec3(0.0, 0.0, -10.0);
1277
- vP = vec4(pos, 1.0);
1278
- gl_Position = mvpMtx * vP;
1274
+ vPc = mvpMtx * vec4(pos, 1.0);
1275
+ gl_Position = vPc;
1279
1276
  vN = normalize((normMtx * vec4(norm.xyz,1.0)).xyz);
1280
1277
  //vV = -vec3(modelMtx*vec4(pos,1.0));
1281
1278
  vClr = clr;
@@ -1420,6 +1417,45 @@ void main() {
1420
1417
  s *= specularWhite;
1421
1418
  color = vec4(d3 + s3 + s, opacity);
1422
1419
  }`;
1420
+ var fragMeshShaderCrevice = `#version 300 es
1421
+ precision highp int;
1422
+ precision highp float;
1423
+ uniform float opacity;
1424
+ in vec4 vClr;
1425
+ in vec3 vN;
1426
+ in vec4 vPc;
1427
+ out vec4 color;
1428
+ void main() {
1429
+ vec3 n = normalize(vN);
1430
+ // Compute curvature
1431
+ vec3 dx = dFdx(n);
1432
+ vec3 dy = dFdy(n);
1433
+ vec3 xneg = n - dx;
1434
+ vec3 xpos = n + dx;
1435
+ vec3 yneg = n - dy;
1436
+ vec3 ypos = n + dy;
1437
+ float depth = length(vPc.xyz);
1438
+ float curv = (cross(xneg, xpos).y - cross(yneg, ypos).x) / depth;
1439
+ //at this stage 0.5 for flat, with valleys dark and ridges bright
1440
+ curv = 1.0 - (curv + 0.5);
1441
+ //clamp
1442
+ curv = min(max(curv, 0.0), 1.0);
1443
+ // easing function
1444
+ curv = pow(curv, 0.5);
1445
+ //modulate ambient and diffuse with curvature
1446
+ vec3 r = vec3(0.0, 0.0, 1.0); //rayDir: for orthographic projections moving in Z direction (no need for normal matrix)
1447
+ float ambient = 0.6;
1448
+ float diffuse = 0.6;
1449
+ float specular = 0.2;
1450
+ float shininess = 10.0;
1451
+ vec3 lightPosition = vec3(0.0, 10.0, -2.0);
1452
+ vec3 l = normalize(lightPosition);
1453
+ float lightNormDot = dot(n, l);
1454
+ vec3 a = vClr.rgb * ambient * curv;
1455
+ vec3 d = max(lightNormDot, 0.0) * vClr.rgb * diffuse;
1456
+ float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1457
+ color = vec4(a + d + s, opacity);
1458
+ }`;
1423
1459
  var fragMeshShader = `#version 300 es
1424
1460
  precision highp int;
1425
1461
  precision highp float;
@@ -11254,6 +11290,7 @@ var cmapper = new ColorTables();
11254
11290
 
11255
11291
  // src/nvmesh-utilities.ts
11256
11292
  import { vec3 as vec32 } from "gl-matrix";
11293
+ import { gzipSync } from "fflate/browser";
11257
11294
  var NVMeshUtilities = class {
11258
11295
  static getClusterBoundaryU8(u8, faces) {
11259
11296
  const border = new Array(u8.length).fill(false);
@@ -11279,6 +11316,112 @@ var NVMeshUtilities = class {
11279
11316
  }
11280
11317
  return border;
11281
11318
  }
11319
+ static createMZ3(vertices, indices, compress2 = false) {
11320
+ const magic = 23117;
11321
+ const attr = 3;
11322
+ const nface = indices.length / 3;
11323
+ const nvert = vertices.length / 3;
11324
+ const nskip = 0;
11325
+ const headerSize = 16;
11326
+ const indexSize = nface * 3 * 4;
11327
+ const vertexSize = nvert * 3 * 4;
11328
+ const totalSize = headerSize + indexSize + vertexSize;
11329
+ const buffer = new ArrayBuffer(totalSize);
11330
+ const writer = new DataView(buffer);
11331
+ writer.setUint16(0, magic, true);
11332
+ writer.setUint16(2, attr, true);
11333
+ writer.setUint32(4, nface, true);
11334
+ writer.setUint32(8, nvert, true);
11335
+ writer.setUint32(12, nskip, true);
11336
+ let offset = headerSize;
11337
+ new Uint32Array(buffer, offset, indices.length).set(indices);
11338
+ offset += indexSize;
11339
+ new Float32Array(buffer, offset, vertices.length).set(vertices);
11340
+ if (compress2) {
11341
+ return gzipSync(new Uint8Array(buffer));
11342
+ }
11343
+ return buffer;
11344
+ }
11345
+ static createOBJ(vertices, indices) {
11346
+ let objContent = "";
11347
+ for (let i = 0; i < vertices.length; i += 3) {
11348
+ objContent += `v ${vertices[i]} ${vertices[i + 1]} ${vertices[i + 2]}
11349
+ `;
11350
+ }
11351
+ for (let i = 0; i < indices.length; i += 3) {
11352
+ objContent += `f ${indices[i] + 1} ${indices[i + 1] + 1} ${indices[i + 2] + 1}
11353
+ `;
11354
+ }
11355
+ const encoder = new TextEncoder();
11356
+ const arrayBuffer = encoder.encode(objContent).buffer;
11357
+ return arrayBuffer;
11358
+ }
11359
+ static createSTL(vertices, indices) {
11360
+ const numTriangles = indices.length / 3;
11361
+ const bufferLength = 84 + numTriangles * 50;
11362
+ const arrayBuffer = new ArrayBuffer(bufferLength);
11363
+ const dataView = new DataView(arrayBuffer);
11364
+ for (let i = 0; i < 80; i++) {
11365
+ dataView.setUint8(i, 0);
11366
+ }
11367
+ dataView.setUint32(80, numTriangles, true);
11368
+ let offset = 84;
11369
+ for (let i = 0; i < indices.length; i += 3) {
11370
+ const i0 = indices[i] * 3;
11371
+ const i1 = indices[i + 1] * 3;
11372
+ const i2 = indices[i + 2] * 3;
11373
+ dataView.setFloat32(offset, 0, true);
11374
+ dataView.setFloat32(offset + 4, 0, true);
11375
+ dataView.setFloat32(offset + 8, 0, true);
11376
+ offset += 12;
11377
+ dataView.setFloat32(offset, vertices[i0], true);
11378
+ dataView.setFloat32(offset + 4, vertices[i0 + 1], true);
11379
+ dataView.setFloat32(offset + 8, vertices[i0 + 2], true);
11380
+ offset += 12;
11381
+ dataView.setFloat32(offset, vertices[i1], true);
11382
+ dataView.setFloat32(offset + 4, vertices[i1 + 1], true);
11383
+ dataView.setFloat32(offset + 8, vertices[i1 + 2], true);
11384
+ offset += 12;
11385
+ dataView.setFloat32(offset, vertices[i2], true);
11386
+ dataView.setFloat32(offset + 4, vertices[i2 + 1], true);
11387
+ dataView.setFloat32(offset + 8, vertices[i2 + 2], true);
11388
+ offset += 12;
11389
+ dataView.setUint16(offset, 0, true);
11390
+ offset += 2;
11391
+ }
11392
+ return arrayBuffer;
11393
+ }
11394
+ static downloadArrayBuffer(buffer, filename) {
11395
+ const blob = new Blob([buffer], { type: "application/octet-stream" });
11396
+ const url = URL.createObjectURL(blob);
11397
+ const a = document.createElement("a");
11398
+ a.href = url;
11399
+ a.download = filename;
11400
+ document.body.appendChild(a);
11401
+ a.style.display = "none";
11402
+ a.click();
11403
+ setTimeout(() => {
11404
+ document.body.removeChild(a);
11405
+ URL.revokeObjectURL(url);
11406
+ }, 0);
11407
+ }
11408
+ static saveMesh(vertices, indices, filename = ".mz3", compress2 = false) {
11409
+ let buff = new ArrayBuffer(0);
11410
+ if (/\.obj$/i.test(filename)) {
11411
+ buff = this.createOBJ(vertices, indices);
11412
+ } else if (/\.stl$/i.test(filename)) {
11413
+ buff = this.createSTL(vertices, indices);
11414
+ } else {
11415
+ if (!/\.mz3$/i.test(filename)) {
11416
+ filename += ".mz3";
11417
+ }
11418
+ buff = this.createMZ3(vertices, indices, compress2);
11419
+ }
11420
+ if (filename.length > 4) {
11421
+ this.downloadArrayBuffer(buff, filename);
11422
+ }
11423
+ return buff;
11424
+ }
11282
11425
  static getClusterBoundary(rgba8, faces) {
11283
11426
  const rgba32 = new Uint32Array(rgba8.buffer);
11284
11427
  const border = new Array(rgba32.length).fill(false);
@@ -12669,7 +12812,8 @@ var NVMeshLoaders = class _NVMeshLoaders {
12669
12812
  static readANNOT(buffer, n_vert, isReadColortables = false) {
12670
12813
  const view = new DataView(buffer);
12671
12814
  const n_vertex = view.getUint32(0, false);
12672
- if (n_vert !== n_vertex) {
12815
+ const n_vertexDecimated = this.decimateLayerVertices(n_vertex, n_vert);
12816
+ if (n_vert !== n_vertexDecimated) {
12673
12817
  throw new Error("ANNOT file has different number of vertices than mesh");
12674
12818
  }
12675
12819
  if (buffer.byteLength < 4 + 8 * n_vertex) {
@@ -13917,6 +14061,20 @@ var NVMeshLoaders = class _NVMeshLoaders {
13917
14061
  };
13918
14062
  }
13919
14063
  // readSTL()
14064
+ static decimateLayerVertices(nVertLayer, nVertMesh) {
14065
+ if (nVertLayer % nVertMesh === 0) {
14066
+ return nVertLayer;
14067
+ }
14068
+ const V0 = 12;
14069
+ const orderLayer = Math.round(Math.log((nVertLayer - 2) / (V0 - 2)) / Math.log(4));
14070
+ const orderMesh = Math.round(Math.log((nVertMesh - 2) / (V0 - 2)) / Math.log(4));
14071
+ const nVLayer = Math.pow(4, orderLayer) * (V0 - 2) + 2;
14072
+ const nVMesh = Math.pow(4, orderMesh) * (V0 - 2) + 2;
14073
+ if (nVLayer !== nVertLayer || nVMesh !== nVertMesh) {
14074
+ return nVertLayer;
14075
+ }
14076
+ return nVertMesh;
14077
+ }
13920
14078
  // read NIfTI2 format with embedded CIfTI
13921
14079
  // this variation very specific to connectome workbench
13922
14080
  // https://brainder.org/2015/04/03/the-nifti-2-file-format/
@@ -14077,8 +14235,9 @@ var NVMeshLoaders = class _NVMeshLoaders {
14077
14235
  );
14078
14236
  return scalars2;
14079
14237
  }
14238
+ nvert = this.decimateLayerVertices(nvert, n_vert);
14080
14239
  if (nvert % n_vert !== 0) {
14081
- throw new Error("Vertices in NIfTI (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14240
+ throw new Error("Vertices in layer (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14082
14241
  }
14083
14242
  if (isLittleEndian) {
14084
14243
  if (datatype === 16) {
@@ -14157,8 +14316,9 @@ var NVMeshLoaders = class _NVMeshLoaders {
14157
14316
  const dim = reader.getUint16(40 + i * 2, isLittleEndian);
14158
14317
  nvert *= Math.max(dim, 1);
14159
14318
  }
14319
+ nvert = this.decimateLayerVertices(nvert, n_vert);
14160
14320
  if (nvert % n_vert !== 0) {
14161
- throw new Error("Vertices in NIfTI (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14321
+ throw new Error("Vertices in layer (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14162
14322
  }
14163
14323
  if (isLittleEndian) {
14164
14324
  if (datatype === 16) {
@@ -14214,10 +14374,11 @@ var NVMeshLoaders = class _NVMeshLoaders {
14214
14374
  if (version2 !== 1 || mtype < 0 || mtype > 4) {
14215
14375
  log.warn("Not a valid MGH file");
14216
14376
  }
14217
- const nvert = width * height * depth * nframes;
14377
+ let nvert = width * height * depth * nframes;
14218
14378
  let scalars = [];
14379
+ nvert = this.decimateLayerVertices(nvert, n_vert);
14219
14380
  if (nvert % n_vert !== 0) {
14220
- log.warn("Vertices in NIfTI (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14381
+ log.warn("Vertices in layer (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14221
14382
  return scalars;
14222
14383
  }
14223
14384
  if (mtype === 3) {
@@ -15961,6 +16122,91 @@ var NVMesh2 = class _NVMesh {
15961
16122
  }
15962
16123
  this.updateMesh(gl);
15963
16124
  }
16125
+ hierarchicalOrder() {
16126
+ const V0 = 12;
16127
+ const F0 = 20;
16128
+ const nF = this.tris.length / 3;
16129
+ const order = Math.log(nF / F0) / Math.log(4);
16130
+ if (nF !== Math.pow(4, order) * F0) {
16131
+ return NaN;
16132
+ }
16133
+ const nV = this.pts.length / 3;
16134
+ if (nV !== Math.pow(4, order) * (V0 - 2) + 2) {
16135
+ return NaN;
16136
+ }
16137
+ for (let i = 0; i < 15; i += 3) {
16138
+ if (this.tris[i] !== 0) {
16139
+ return NaN;
16140
+ }
16141
+ }
16142
+ for (let i = 15; i < 24; i += 3) {
16143
+ if (this.tris[i] !== 3) {
16144
+ return NaN;
16145
+ }
16146
+ }
16147
+ for (let i = 24; i < 30; i += 3) {
16148
+ if (this.tris[i] !== 4) {
16149
+ return NaN;
16150
+ }
16151
+ }
16152
+ return order;
16153
+ }
16154
+ decimateFaces(n, ntarget) {
16155
+ let fac = this.tris;
16156
+ const V0 = 12;
16157
+ const F0 = 20;
16158
+ for (let j = n - 1; j >= ntarget; j--) {
16159
+ const nVjprev = Math.pow(4, j + 1) * (V0 - 2) + 2;
16160
+ const nVj = Math.pow(4, j) * (V0 - 2) + 2;
16161
+ const nFjprev = fac.length / 3;
16162
+ const nFj = Math.pow(4, j) * F0;
16163
+ console.log(`order ${j + 1} -> ${j} vertices ${nVjprev} -> ${nVj} faces ${nFjprev} -> ${nFj}`);
16164
+ const remap = Array.from({ length: nVjprev }, (_, i) => i + 1);
16165
+ for (let i = 0; i < nFjprev; i++) {
16166
+ const v1 = fac[3 * i];
16167
+ const v2 = fac[3 * i + 1];
16168
+ const v3 = fac[3 * i + 2];
16169
+ remap[v1 - 1] = Math.min(remap[v1 - 1], v2, v3);
16170
+ }
16171
+ const facJ = new Uint32Array(nFj * 3);
16172
+ for (let i = 0; i < nFj; i++) {
16173
+ facJ[3 * i] = remap[fac[3 * i] - 1];
16174
+ facJ[3 * i + 1] = remap[fac[3 * i + 1] - 1];
16175
+ facJ[3 * i + 2] = remap[fac[3 * i + 2] - 1];
16176
+ }
16177
+ fac = facJ;
16178
+ }
16179
+ this.tris = new Uint32Array(fac);
16180
+ }
16181
+ // internal function simplifies FreeSurfer triangulated mesh and overlays
16182
+ decimateHierarchicalMesh(gl, order = 4) {
16183
+ const inputOrder = this.hierarchicalOrder();
16184
+ if (isNaN(inputOrder)) {
16185
+ log.warn("Unable to decimate mesh: it does not have a hierarchical structure");
16186
+ return false;
16187
+ }
16188
+ if (order >= inputOrder) {
16189
+ log.warn(`Unable to decimate mesh: input order (${inputOrder}) must be larger than downsampled order (${order})`);
16190
+ return false;
16191
+ }
16192
+ const inputVLength = this.pts.length / 3;
16193
+ const V0 = 12;
16194
+ const nV = Math.pow(4, order) * (V0 - 2) + 2;
16195
+ this.pts = new Float32Array(this.pts.slice(0, nV * 3));
16196
+ this.decimateFaces(inputOrder, order);
16197
+ if (this.layers && this.layers.length > 0) {
16198
+ for (let i = 0; i < this.layers.length; i++) {
16199
+ const layer = this.layers[i];
16200
+ if (layer.values instanceof Float32Array || layer.values.length !== inputVLength) {
16201
+ layer.values = new Float32Array(layer.values.slice(0, nV));
16202
+ } else {
16203
+ log.warn(`decimation logic needs to be updated`);
16204
+ }
16205
+ }
16206
+ }
16207
+ this.updateMesh(gl);
16208
+ return true;
16209
+ }
15964
16210
  // adjust attributes of a mesh layer. invoked by niivue.setMeshLayerProperty()
15965
16211
  // TODO this method is a bit too generic
15966
16212
  setLayerProperty(id, key, val, gl) {
@@ -18016,7 +18262,7 @@ import { vec3 as vec38, vec4 as vec46 } from "gl-matrix";
18016
18262
  import * as nifti from "nifti-reader-js";
18017
18263
  import daikon from "daikon";
18018
18264
  import { mat3, mat4 as mat44, vec3 as vec37, vec4 as vec45 } from "gl-matrix";
18019
- import { Decompress, decompressSync as decompressSync3, gzipSync } from "fflate/browser";
18265
+ import { Decompress, decompressSync as decompressSync3, gzipSync as gzipSync2 } from "fflate/browser";
18020
18266
  import { v4 as uuidv42 } from "@lukeed/uuid";
18021
18267
 
18022
18268
  // src/nvimage/utils.ts
@@ -18159,76 +18405,90 @@ function getBestTransform(imageDirections, voxelDimensions, imagePosition) {
18159
18405
  }
18160
18406
  return m;
18161
18407
  }
18162
- function str2Buffer(str) {
18408
+ function str2Buffer(str, maxLen = 80) {
18409
+ str = str.replace(/[`$]/g, "");
18163
18410
  const bytes = [];
18164
- for (let i = 0; i < str.length; i++) {
18411
+ const len = Math.min(maxLen, str.length);
18412
+ for (let i = 0; i < len; i++) {
18165
18413
  const char = str.charCodeAt(i);
18166
18414
  bytes.push(char & 255);
18167
18415
  }
18168
18416
  return bytes;
18169
18417
  }
18170
- function hdrToArrayBuffer(hdr, isDrawing8 = false) {
18418
+ function hdrToArrayBuffer(hdr, isDrawing8 = false, isInputEndian = false) {
18171
18419
  const SHORT_SIZE = 2;
18172
18420
  const FLOAT32_SIZE = 4;
18421
+ let isLittleEndian = true;
18422
+ if (isInputEndian) {
18423
+ isLittleEndian = hdr.littleEndian;
18424
+ }
18173
18425
  const byteArray = new Uint8Array(348);
18174
18426
  const view = new DataView(byteArray.buffer);
18175
- view.setInt32(0, 348, hdr.littleEndian);
18427
+ view.setInt32(0, 348, isLittleEndian);
18428
+ view.setUint8(38, 114);
18176
18429
  view.setUint8(39, hdr.dim_info);
18177
18430
  for (let i = 0; i < 8; i++) {
18178
- view.setUint16(40 + SHORT_SIZE * i, hdr.dims[i], hdr.littleEndian);
18431
+ view.setUint16(40 + SHORT_SIZE * i, hdr.dims[i], isLittleEndian);
18179
18432
  }
18180
- view.setFloat32(56, hdr.intent_p1, hdr.littleEndian);
18181
- view.setFloat32(60, hdr.intent_p2, hdr.littleEndian);
18182
- view.setFloat32(64, hdr.intent_p3, hdr.littleEndian);
18183
- view.setInt16(68, hdr.intent_code, hdr.littleEndian);
18433
+ view.setFloat32(56, hdr.intent_p1, isLittleEndian);
18434
+ view.setFloat32(60, hdr.intent_p2, isLittleEndian);
18435
+ view.setFloat32(64, hdr.intent_p3, isLittleEndian);
18436
+ view.setInt16(68, hdr.intent_code, isLittleEndian);
18184
18437
  if (isDrawing8) {
18185
- view.setInt16(70, 2, hdr.littleEndian);
18186
- view.setInt16(72, 8, hdr.littleEndian);
18438
+ view.setInt16(70, 2, isLittleEndian);
18439
+ view.setInt16(72, 8, isLittleEndian);
18187
18440
  } else {
18188
- view.setInt16(70, hdr.datatypeCode, hdr.littleEndian);
18189
- view.setInt16(72, hdr.numBitsPerVoxel, hdr.littleEndian);
18441
+ view.setInt16(70, hdr.datatypeCode, isLittleEndian);
18442
+ view.setInt16(72, hdr.numBitsPerVoxel, isLittleEndian);
18190
18443
  }
18191
- view.setInt16(74, hdr.slice_start, hdr.littleEndian);
18444
+ view.setInt16(74, hdr.slice_start, isLittleEndian);
18192
18445
  for (let i = 0; i < 8; i++) {
18193
- view.setFloat32(76 + FLOAT32_SIZE * i, hdr.pixDims[i], hdr.littleEndian);
18446
+ view.setFloat32(76 + FLOAT32_SIZE * i, hdr.pixDims[i], isLittleEndian);
18194
18447
  }
18195
18448
  if (isDrawing8) {
18196
- view.setFloat32(108, 352, hdr.littleEndian);
18197
- view.setFloat32(112, 1, hdr.littleEndian);
18198
- view.setFloat32(116, 0, hdr.littleEndian);
18449
+ view.setFloat32(108, 352, isLittleEndian);
18450
+ view.setFloat32(112, 1, isLittleEndian);
18451
+ view.setFloat32(116, 0, isLittleEndian);
18199
18452
  } else {
18200
- view.setFloat32(108, hdr.vox_offset, hdr.littleEndian);
18201
- view.setFloat32(112, hdr.scl_slope, hdr.littleEndian);
18202
- view.setFloat32(116, hdr.scl_inter, hdr.littleEndian);
18453
+ view.setFloat32(108, 352, isLittleEndian);
18454
+ view.setFloat32(112, hdr.scl_slope, isLittleEndian);
18455
+ view.setFloat32(116, hdr.scl_inter, isLittleEndian);
18203
18456
  }
18204
- view.setInt16(120, hdr.slice_end, hdr.littleEndian);
18457
+ view.setInt16(120, hdr.slice_end, isLittleEndian);
18205
18458
  view.setUint8(122, hdr.slice_code);
18206
- view.setUint8(123, hdr.xyzt_units);
18459
+ if (hdr.xyzt_units === 0) {
18460
+ view.setUint8(123, 10);
18461
+ } else {
18462
+ view.setUint8(123, hdr.xyzt_units);
18463
+ }
18207
18464
  if (isDrawing8) {
18208
- view.setFloat32(124, 0, hdr.littleEndian);
18209
- view.setFloat32(128, 0, hdr.littleEndian);
18465
+ view.setFloat32(124, 0, isLittleEndian);
18466
+ view.setFloat32(128, 0, isLittleEndian);
18210
18467
  } else {
18211
- view.setFloat32(124, hdr.cal_max, hdr.littleEndian);
18212
- view.setFloat32(128, hdr.cal_min, hdr.littleEndian);
18468
+ view.setFloat32(124, hdr.cal_max, isLittleEndian);
18469
+ view.setFloat32(128, hdr.cal_min, isLittleEndian);
18213
18470
  }
18214
- view.setFloat32(132, hdr.slice_duration, hdr.littleEndian);
18215
- view.setFloat32(136, hdr.toffset, hdr.littleEndian);
18471
+ view.setFloat32(132, hdr.slice_duration, isLittleEndian);
18472
+ view.setFloat32(136, hdr.toffset, isLittleEndian);
18216
18473
  byteArray.set(str2Buffer(hdr.description), 148);
18217
18474
  byteArray.set(str2Buffer(hdr.aux_file), 228);
18218
- view.setInt16(252, hdr.qform_code, hdr.littleEndian);
18219
- view.setInt16(254, hdr.sform_code, hdr.littleEndian);
18220
- view.setFloat32(256, hdr.quatern_b, hdr.littleEndian);
18221
- view.setFloat32(260, hdr.quatern_c, hdr.littleEndian);
18222
- view.setFloat32(264, hdr.quatern_d, hdr.littleEndian);
18223
- view.setFloat32(268, hdr.qoffset_x, hdr.littleEndian);
18224
- view.setFloat32(272, hdr.qoffset_y, hdr.littleEndian);
18225
- view.setFloat32(276, hdr.qoffset_z, hdr.littleEndian);
18475
+ view.setInt16(252, hdr.qform_code, isLittleEndian);
18476
+ if (hdr.sform_code < 1 || hdr.sform_code < 1) {
18477
+ view.setInt16(254, 1, isLittleEndian);
18478
+ } else {
18479
+ view.setInt16(254, hdr.sform_code, isLittleEndian);
18480
+ }
18481
+ view.setFloat32(256, hdr.quatern_b, isLittleEndian);
18482
+ view.setFloat32(260, hdr.quatern_c, isLittleEndian);
18483
+ view.setFloat32(264, hdr.quatern_d, isLittleEndian);
18484
+ view.setFloat32(268, hdr.qoffset_x, isLittleEndian);
18485
+ view.setFloat32(272, hdr.qoffset_y, isLittleEndian);
18486
+ view.setFloat32(276, hdr.qoffset_z, isLittleEndian);
18226
18487
  const flattened = hdr.affine.flat();
18227
18488
  for (let i = 0; i < 12; i++) {
18228
- view.setFloat32(280 + FLOAT32_SIZE * i, flattened[i], hdr.littleEndian);
18489
+ view.setFloat32(280 + FLOAT32_SIZE * i, flattened[i], isLittleEndian);
18229
18490
  }
18230
- byteArray.set(str2Buffer(hdr.intent_name), 328);
18231
- byteArray.set(str2Buffer(hdr.magic), 344);
18491
+ view.setInt32(344, 3222382, true);
18232
18492
  return byteArray;
18233
18493
  }
18234
18494
  function getExtents(positions, forceOriginInVolume = true) {
@@ -20875,7 +21135,7 @@ var NVImage = class _NVImage {
20875
21135
  let saveData = null;
20876
21136
  const compress2 = fnm.endsWith(".gz");
20877
21137
  if (compress2) {
20878
- saveData = gzipSync(odata, {
21138
+ saveData = gzipSync2(odata, {
20879
21139
  // GZIP-specific: the filename to use when decompressed
20880
21140
  filename: fnm,
20881
21141
  // GZIP-specific: the modification time. Can be a Date, date string,
@@ -21338,7 +21598,7 @@ var NVImage = class _NVImage {
21338
21598
  * read a 3D slab of voxels from a volume
21339
21599
  * @param voxStart - first row, column and slice (RAS order) for selection
21340
21600
  * @param voxEnd - final row, column and slice (RAS order) for selection
21341
- * @param dataType - array data type. Options: 'same' (default), 'uint8', 'float32'
21601
+ * @param dataType - array data type. Options: 'same' (default), 'uint8', 'float32', 'scaled', 'normalized', 'windowed'
21342
21602
  * @returns the an array where ret[0] is the voxel values and ret[1] is dimension of selection
21343
21603
  * @see {@link https://niivue.github.io/niivue/features/slab_selection.html | live demo usage}
21344
21604
  */
@@ -21362,7 +21622,7 @@ var NVImage = class _NVImage {
21362
21622
  let dt = this.hdr.datatypeCode;
21363
21623
  if (dataType === "uint8") {
21364
21624
  dt = 2 /* DT_UINT8 */;
21365
- } else if (dataType === "float32") {
21625
+ } else if (dataType === "float32" || dataType === "scaled" || dataType === "normalized" || dataType === "windowed") {
21366
21626
  dt = 16 /* DT_FLOAT32 */;
21367
21627
  }
21368
21628
  if (dt === 2 /* DT_UINT8 */) {
@@ -21392,6 +21652,24 @@ var NVImage = class _NVImage {
21392
21652
  }
21393
21653
  }
21394
21654
  }
21655
+ if (dataType === "scaled" || dataType === "normalized" || dataType === "windowed") {
21656
+ for (let i2 = 0; i2 < img.length; i2++) {
21657
+ img[i2] = img[i2] * this.hdr.scl_slope + this.hdr.scl_inter;
21658
+ }
21659
+ }
21660
+ if (dataType === "normalized" || dataType === "windowed") {
21661
+ let mn = this.cal_min;
21662
+ let mx = this.cal_max;
21663
+ if (dataType === "normalized") {
21664
+ mn = this.global_min;
21665
+ mx = this.global_max;
21666
+ }
21667
+ const scale = 1 / (mx - mn);
21668
+ for (let i2 = 0; i2 < img.length; i2++) {
21669
+ img[i2] = (img[i2] - mn) * scale;
21670
+ img[i2] = Math.max(Math.min(img[i2], 1), 0);
21671
+ }
21672
+ }
21395
21673
  return [img, slabDims];
21396
21674
  }
21397
21675
  // getVolumeData()
@@ -23873,6 +24151,10 @@ var Niivue = class _Niivue {
23873
24151
  Name: "Hemispheric",
23874
24152
  Frag: fragMeshHemiShader
23875
24153
  },
24154
+ {
24155
+ Name: "Crevice",
24156
+ Frag: fragMeshShaderCrevice
24157
+ },
23876
24158
  {
23877
24159
  Name: "Edge",
23878
24160
  Frag: fragMeshEdgeShader
@@ -25275,7 +25557,6 @@ var Niivue = class _Niivue {
25275
25557
  this.loadDrawing(volume);
25276
25558
  } else {
25277
25559
  this.addVolume(volume);
25278
- this.setDrawingEnabled(true);
25279
25560
  }
25280
25561
  }).catch((e2) => {
25281
25562
  throw e2;
@@ -25966,6 +26247,24 @@ var Niivue = class _Niivue {
25966
26247
  }
25967
26248
  return this.meshes[idx].indexNearestXYZmm(Xmm, Ymm, Zmm);
25968
26249
  }
26250
+ /**
26251
+ * reduce complexity of FreeSurfer mesh
26252
+ * @param mesh - identity of mesh to change
26253
+ * @param order - decimation order 0..6
26254
+ * @example niivue.decimateHierarchicalMesh(niivue.meshes[0].id, 4)
26255
+ * @returns boolean false if mesh is not hierarchical or of lower order
26256
+ * @see {@link https://niivue.github.io/niivue/features/meshes.html | live demo usage}
26257
+ */
26258
+ decimateHierarchicalMesh(mesh, order = 3) {
26259
+ const idx = this.getMeshIndexByID(mesh);
26260
+ if (idx < 0) {
26261
+ log.warn("reverseFaces() id not loaded", mesh);
26262
+ return;
26263
+ }
26264
+ const ret = this.meshes[idx].decimateHierarchicalMesh(this.gl, order);
26265
+ this.updateGLVolume();
26266
+ return ret;
26267
+ }
25969
26268
  /**
25970
26269
  * reverse triangle winding of mesh (swap front and back faces)
25971
26270
  * @param id - identity of mesh to change
@@ -26464,6 +26763,9 @@ var Niivue = class _Niivue {
26464
26763
  this.renderShader.use(this.gl);
26465
26764
  this.gl.uniform3fv(this.renderShader.uniforms.clipLo, this.opts.clipVolumeLow);
26466
26765
  this.gl.uniform3fv(this.renderShader.uniforms.clipHi, this.opts.clipVolumeHigh);
26766
+ this.pickingImageShader.use(this.gl);
26767
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo, this.opts.clipVolumeLow);
26768
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi, this.opts.clipVolumeHigh);
26467
26769
  this.drawScene();
26468
26770
  }
26469
26771
  /**
@@ -28765,6 +29067,8 @@ var Niivue = class _Niivue {
28765
29067
  this.pickingImageShader.use(this.gl);
28766
29068
  this.gl.uniform1f(this.pickingImageShader.uniforms.overlays, this.overlays.length);
28767
29069
  this.gl.uniform3fv(this.pickingImageShader.uniforms.texVox, vox);
29070
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo, this.opts.clipVolumeLow);
29071
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi, this.opts.clipVolumeHigh);
28768
29072
  let shader = this.sliceMMShader;
28769
29073
  if (this.opts.isV1SliceShader) {
28770
29074
  shader = this.sliceV1Shader;
@@ -32679,6 +32983,7 @@ export {
32679
32983
  NVMeshFromUrlOptions,
32680
32984
  NVMeshLayerDefaults,
32681
32985
  NVMeshLoaders,
32986
+ NVMeshUtilities,
32682
32987
  NVUtilities,
32683
32988
  Niivue,
32684
32989
  SHOW_RENDER,