@niivue/niivue 0.44.0 → 0.44.1

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.d.ts CHANGED
@@ -437,7 +437,7 @@ declare class NVImage {
437
437
  * read a 3D slab of voxels from a volume
438
438
  * @param voxStart - first row, column and slice (RAS order) for selection
439
439
  * @param voxEnd - final row, column and slice (RAS order) for selection
440
- * @param dataType - array data type. Options: 'same' (default), 'uint8', 'float32'
440
+ * @param dataType - array data type. Options: 'same' (default), 'uint8', 'float32', 'scaled', 'normalized', 'windowed'
441
441
  * @returns the an array where ret[0] is the voxel values and ret[1] is dimension of selection
442
442
  * @see {@link https://niivue.github.io/niivue/features/slab_selection.html | live demo usage}
443
443
  */
@@ -1191,6 +1191,9 @@ declare class NVMesh {
1191
1191
  unloadMesh(gl: WebGL2RenderingContext): void;
1192
1192
  updateMesh(gl: WebGL2RenderingContext): void;
1193
1193
  reverseFaces(gl: WebGL2RenderingContext): void;
1194
+ hierarchicalOrder(): number;
1195
+ decimateFaces(n: number, ntarget: number): void;
1196
+ decimateHierarchicalMesh(gl: WebGL2RenderingContext, order?: number): boolean;
1194
1197
  setLayerProperty(id: number, key: keyof NVMeshLayer, val: number | string | boolean, gl: WebGL2RenderingContext): void;
1195
1198
  setProperty(key: keyof this, val: unknown, gl: WebGL2RenderingContext): void;
1196
1199
  generatePosNormClr(pts: Float32Array, tris: Uint32Array, rgba255: Uint8Array): Float32Array;
@@ -1520,6 +1523,7 @@ declare class NVMeshLoaders {
1520
1523
  static readSRF(buffer: ArrayBuffer): DefaultMeshType;
1521
1524
  static readTxtSTL(buffer: ArrayBuffer): DefaultMeshType;
1522
1525
  static readSTL(buffer: ArrayBuffer): DefaultMeshType;
1526
+ static decimateLayerVertices(nVertLayer: number, nVertMesh: number): number;
1523
1527
  static readNII2(buffer: ArrayBuffer, n_vert?: number, anatomicalStructurePrimary?: string): Int32Array | Float32Array | Int16Array | Uint8Array;
1524
1528
  static readNII(buffer: ArrayBuffer, n_vert?: number, anatomicalStructurePrimary?: string): Float32Array | Uint8Array | Int32Array | Int16Array;
1525
1529
  static readMGH(buffer: ArrayBuffer, n_vert?: number, isReadColortables?: boolean): MGH;
@@ -1527,6 +1531,26 @@ declare class NVMeshLoaders {
1527
1531
  static readGII(buffer: ArrayBuffer, n_vert?: number): GII;
1528
1532
  }
1529
1533
 
1534
+ type Extents = {
1535
+ mxDx: number;
1536
+ extentsMin: number | number[];
1537
+ extentsMax: number | number[];
1538
+ };
1539
+ /**
1540
+ * Utilities class for common mesh functions
1541
+ */
1542
+ declare class NVMeshUtilities {
1543
+ static getClusterBoundaryU8(u8: Uint8Array, faces: number[] | Uint32Array): boolean[];
1544
+ static createMZ3(vertices: Float32Array, indices: Uint32Array, compress?: boolean): ArrayBuffer;
1545
+ static createOBJ(vertices: Float32Array, indices: Uint32Array): ArrayBuffer;
1546
+ static createSTL(vertices: Float32Array, indices: Uint32Array): ArrayBuffer;
1547
+ static downloadArrayBuffer(buffer: ArrayBuffer, filename: string): void;
1548
+ static saveMesh(vertices: Float32Array, indices: Uint32Array, filename?: string, compress?: boolean): ArrayBuffer;
1549
+ static getClusterBoundary(rgba8: Uint8Array, faces: number[] | Uint32Array): boolean[];
1550
+ static getExtents(pts: number[] | Float32Array): Extents;
1551
+ static generateNormals(pts: number[] | Float32Array, tris: number[] | Uint32Array): Float32Array;
1552
+ }
1553
+
1530
1554
  type ColormapListEntry = {
1531
1555
  name: string;
1532
1556
  min: number;
@@ -2264,6 +2288,15 @@ declare class Niivue {
2264
2288
  * @see {@link https://niivue.github.io/niivue/features/clipplanes.html | live demo usage}
2265
2289
  */
2266
2290
  indexNearestXYZmm(mesh: number, Xmm: number, Ymm: number, Zmm: number): number[];
2291
+ /**
2292
+ * reduce complexity of FreeSurfer mesh
2293
+ * @param mesh - identity of mesh to change
2294
+ * @param order - decimation order 0..6
2295
+ * @example niivue.decimateHierarchicalMesh(niivue.meshes[0].id, 4)
2296
+ * @returns boolean false if mesh is not hierarchical or of lower order
2297
+ * @see {@link https://niivue.github.io/niivue/features/meshes.html | live demo usage}
2298
+ */
2299
+ decimateHierarchicalMesh(mesh: number, order?: number): boolean;
2267
2300
  /**
2268
2301
  * reverse triangle winding of mesh (swap front and back faces)
2269
2302
  * @param id - identity of mesh to change
@@ -3009,4 +3042,4 @@ declare class Niivue {
3009
3042
  set gl(gl: WebGL2RenderingContext | null);
3010
3043
  }
3011
3044
 
3012
- export { type Connectome, type ConnectomeOptions, DEFAULT_OPTIONS, DRAG_MODE, type DocumentData, type DragReleaseParams, type ExportDocumentData, INITIAL_SCENE_DATA, LabelLineTerminator, LabelTextAlignment, type LegacyConnectome, type LegacyNodes, MULTIPLANAR_TYPE, type NVConfigOptions, type NVConnectomeEdge, type NVConnectomeNode, NVController, NVDocument, NVImage, NVImageFromUrlOptions, NVLabel3D, NVLabel3DStyle, NVMesh, NVMeshFromUrlOptions, NVMeshLayerDefaults, NVMeshLoaders, NVUtilities, type NiftiHeader, type NiiVueLocation, type NiiVueLocationValue, Niivue, type Point, SHOW_RENDER, SLICE_TYPE, type Scene, type Volume, cmapper, ColorTables as colortables };
3045
+ export { type Connectome, type ConnectomeOptions, DEFAULT_OPTIONS, DRAG_MODE, type DocumentData, type DragReleaseParams, type ExportDocumentData, INITIAL_SCENE_DATA, LabelLineTerminator, LabelTextAlignment, type LegacyConnectome, type LegacyNodes, MULTIPLANAR_TYPE, type NVConfigOptions, type NVConnectomeEdge, type NVConnectomeNode, NVController, NVDocument, NVImage, NVImageFromUrlOptions, NVLabel3D, NVLabel3DStyle, NVMesh, NVMeshFromUrlOptions, NVMeshLayerDefaults, NVMeshLoaders, NVMeshUtilities, NVUtilities, type NiftiHeader, type NiiVueLocation, type NiiVueLocationValue, Niivue, type Point, SHOW_RENDER, SLICE_TYPE, type Scene, type Volume, cmapper, ColorTables as colortables };
package/build/index.js CHANGED
@@ -13,7 +13,7 @@ var __publicField = (obj, key, value) => {
13
13
  import { mat4 as mat46, vec2, vec3 as vec311, vec4 as vec48 } from "gl-matrix";
14
14
 
15
15
  // package.json
16
- var version = "0.44.0";
16
+ var version = "0.44.1";
17
17
 
18
18
  // src/logger.ts
19
19
  var _Log = class _Log {
@@ -11254,6 +11254,7 @@ var cmapper = new ColorTables();
11254
11254
 
11255
11255
  // src/nvmesh-utilities.ts
11256
11256
  import { vec3 as vec32 } from "gl-matrix";
11257
+ import { gzipSync } from "fflate/browser";
11257
11258
  var NVMeshUtilities = class {
11258
11259
  static getClusterBoundaryU8(u8, faces) {
11259
11260
  const border = new Array(u8.length).fill(false);
@@ -11279,6 +11280,112 @@ var NVMeshUtilities = class {
11279
11280
  }
11280
11281
  return border;
11281
11282
  }
11283
+ static createMZ3(vertices, indices, compress2 = false) {
11284
+ const magic = 23117;
11285
+ const attr = 3;
11286
+ const nface = indices.length / 3;
11287
+ const nvert = vertices.length / 3;
11288
+ const nskip = 0;
11289
+ const headerSize = 16;
11290
+ const indexSize = nface * 3 * 4;
11291
+ const vertexSize = nvert * 3 * 4;
11292
+ const totalSize = headerSize + indexSize + vertexSize;
11293
+ const buffer = new ArrayBuffer(totalSize);
11294
+ const writer = new DataView(buffer);
11295
+ writer.setUint16(0, magic, true);
11296
+ writer.setUint16(2, attr, true);
11297
+ writer.setUint32(4, nface, true);
11298
+ writer.setUint32(8, nvert, true);
11299
+ writer.setUint32(12, nskip, true);
11300
+ let offset = headerSize;
11301
+ new Uint32Array(buffer, offset, indices.length).set(indices);
11302
+ offset += indexSize;
11303
+ new Float32Array(buffer, offset, vertices.length).set(vertices);
11304
+ if (compress2) {
11305
+ return gzipSync(new Uint8Array(buffer));
11306
+ }
11307
+ return buffer;
11308
+ }
11309
+ static createOBJ(vertices, indices) {
11310
+ let objContent = "";
11311
+ for (let i = 0; i < vertices.length; i += 3) {
11312
+ objContent += `v ${vertices[i]} ${vertices[i + 1]} ${vertices[i + 2]}
11313
+ `;
11314
+ }
11315
+ for (let i = 0; i < indices.length; i += 3) {
11316
+ objContent += `f ${indices[i] + 1} ${indices[i + 1] + 1} ${indices[i + 2] + 1}
11317
+ `;
11318
+ }
11319
+ const encoder = new TextEncoder();
11320
+ const arrayBuffer = encoder.encode(objContent).buffer;
11321
+ return arrayBuffer;
11322
+ }
11323
+ static createSTL(vertices, indices) {
11324
+ const numTriangles = indices.length / 3;
11325
+ const bufferLength = 84 + numTriangles * 50;
11326
+ const arrayBuffer = new ArrayBuffer(bufferLength);
11327
+ const dataView = new DataView(arrayBuffer);
11328
+ for (let i = 0; i < 80; i++) {
11329
+ dataView.setUint8(i, 0);
11330
+ }
11331
+ dataView.setUint32(80, numTriangles, true);
11332
+ let offset = 84;
11333
+ for (let i = 0; i < indices.length; i += 3) {
11334
+ const i0 = indices[i] * 3;
11335
+ const i1 = indices[i + 1] * 3;
11336
+ const i2 = indices[i + 2] * 3;
11337
+ dataView.setFloat32(offset, 0, true);
11338
+ dataView.setFloat32(offset + 4, 0, true);
11339
+ dataView.setFloat32(offset + 8, 0, true);
11340
+ offset += 12;
11341
+ dataView.setFloat32(offset, vertices[i0], true);
11342
+ dataView.setFloat32(offset + 4, vertices[i0 + 1], true);
11343
+ dataView.setFloat32(offset + 8, vertices[i0 + 2], true);
11344
+ offset += 12;
11345
+ dataView.setFloat32(offset, vertices[i1], true);
11346
+ dataView.setFloat32(offset + 4, vertices[i1 + 1], true);
11347
+ dataView.setFloat32(offset + 8, vertices[i1 + 2], true);
11348
+ offset += 12;
11349
+ dataView.setFloat32(offset, vertices[i2], true);
11350
+ dataView.setFloat32(offset + 4, vertices[i2 + 1], true);
11351
+ dataView.setFloat32(offset + 8, vertices[i2 + 2], true);
11352
+ offset += 12;
11353
+ dataView.setUint16(offset, 0, true);
11354
+ offset += 2;
11355
+ }
11356
+ return arrayBuffer;
11357
+ }
11358
+ static downloadArrayBuffer(buffer, filename) {
11359
+ const blob = new Blob([buffer], { type: "application/octet-stream" });
11360
+ const url = URL.createObjectURL(blob);
11361
+ const a = document.createElement("a");
11362
+ a.href = url;
11363
+ a.download = filename;
11364
+ document.body.appendChild(a);
11365
+ a.style.display = "none";
11366
+ a.click();
11367
+ setTimeout(() => {
11368
+ document.body.removeChild(a);
11369
+ URL.revokeObjectURL(url);
11370
+ }, 0);
11371
+ }
11372
+ static saveMesh(vertices, indices, filename = ".mz3", compress2 = false) {
11373
+ let buff = new ArrayBuffer(0);
11374
+ if (/\.obj$/i.test(filename)) {
11375
+ buff = this.createOBJ(vertices, indices);
11376
+ } else if (/\.stl$/i.test(filename)) {
11377
+ buff = this.createSTL(vertices, indices);
11378
+ } else {
11379
+ if (!/\.mz3$/i.test(filename)) {
11380
+ filename += ".mz3";
11381
+ }
11382
+ buff = this.createMZ3(vertices, indices, compress2);
11383
+ }
11384
+ if (filename.length > 4) {
11385
+ this.downloadArrayBuffer(buff, filename);
11386
+ }
11387
+ return buff;
11388
+ }
11282
11389
  static getClusterBoundary(rgba8, faces) {
11283
11390
  const rgba32 = new Uint32Array(rgba8.buffer);
11284
11391
  const border = new Array(rgba32.length).fill(false);
@@ -12669,7 +12776,8 @@ var NVMeshLoaders = class _NVMeshLoaders {
12669
12776
  static readANNOT(buffer, n_vert, isReadColortables = false) {
12670
12777
  const view = new DataView(buffer);
12671
12778
  const n_vertex = view.getUint32(0, false);
12672
- if (n_vert !== n_vertex) {
12779
+ const n_vertexDecimated = this.decimateLayerVertices(n_vertex, n_vert);
12780
+ if (n_vert !== n_vertexDecimated) {
12673
12781
  throw new Error("ANNOT file has different number of vertices than mesh");
12674
12782
  }
12675
12783
  if (buffer.byteLength < 4 + 8 * n_vertex) {
@@ -13917,6 +14025,20 @@ var NVMeshLoaders = class _NVMeshLoaders {
13917
14025
  };
13918
14026
  }
13919
14027
  // readSTL()
14028
+ static decimateLayerVertices(nVertLayer, nVertMesh) {
14029
+ if (nVertLayer % nVertMesh === 0) {
14030
+ return nVertLayer;
14031
+ }
14032
+ const V0 = 12;
14033
+ const orderLayer = Math.round(Math.log((nVertLayer - 2) / (V0 - 2)) / Math.log(4));
14034
+ const orderMesh = Math.round(Math.log((nVertMesh - 2) / (V0 - 2)) / Math.log(4));
14035
+ const nVLayer = Math.pow(4, orderLayer) * (V0 - 2) + 2;
14036
+ const nVMesh = Math.pow(4, orderMesh) * (V0 - 2) + 2;
14037
+ if (nVLayer !== nVertLayer || nVMesh !== nVertMesh) {
14038
+ return nVertLayer;
14039
+ }
14040
+ return nVertMesh;
14041
+ }
13920
14042
  // read NIfTI2 format with embedded CIfTI
13921
14043
  // this variation very specific to connectome workbench
13922
14044
  // https://brainder.org/2015/04/03/the-nifti-2-file-format/
@@ -14077,8 +14199,9 @@ var NVMeshLoaders = class _NVMeshLoaders {
14077
14199
  );
14078
14200
  return scalars2;
14079
14201
  }
14202
+ nvert = this.decimateLayerVertices(nvert, n_vert);
14080
14203
  if (nvert % n_vert !== 0) {
14081
- throw new Error("Vertices in NIfTI (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14204
+ throw new Error("Vertices in layer (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14082
14205
  }
14083
14206
  if (isLittleEndian) {
14084
14207
  if (datatype === 16) {
@@ -14157,8 +14280,9 @@ var NVMeshLoaders = class _NVMeshLoaders {
14157
14280
  const dim = reader.getUint16(40 + i * 2, isLittleEndian);
14158
14281
  nvert *= Math.max(dim, 1);
14159
14282
  }
14283
+ nvert = this.decimateLayerVertices(nvert, n_vert);
14160
14284
  if (nvert % n_vert !== 0) {
14161
- throw new Error("Vertices in NIfTI (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14285
+ throw new Error("Vertices in layer (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14162
14286
  }
14163
14287
  if (isLittleEndian) {
14164
14288
  if (datatype === 16) {
@@ -14214,10 +14338,11 @@ var NVMeshLoaders = class _NVMeshLoaders {
14214
14338
  if (version2 !== 1 || mtype < 0 || mtype > 4) {
14215
14339
  log.warn("Not a valid MGH file");
14216
14340
  }
14217
- const nvert = width * height * depth * nframes;
14341
+ let nvert = width * height * depth * nframes;
14218
14342
  let scalars = [];
14343
+ nvert = this.decimateLayerVertices(nvert, n_vert);
14219
14344
  if (nvert % n_vert !== 0) {
14220
- log.warn("Vertices in NIfTI (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14345
+ log.warn("Vertices in layer (" + nvert + ") is not a multiple of number of vertices (" + n_vert + ")");
14221
14346
  return scalars;
14222
14347
  }
14223
14348
  if (mtype === 3) {
@@ -15961,6 +16086,91 @@ var NVMesh2 = class _NVMesh {
15961
16086
  }
15962
16087
  this.updateMesh(gl);
15963
16088
  }
16089
+ hierarchicalOrder() {
16090
+ const V0 = 12;
16091
+ const F0 = 20;
16092
+ const nF = this.tris.length / 3;
16093
+ const order = Math.log(nF / F0) / Math.log(4);
16094
+ if (nF !== Math.pow(4, order) * F0) {
16095
+ return NaN;
16096
+ }
16097
+ const nV = this.pts.length / 3;
16098
+ if (nV !== Math.pow(4, order) * (V0 - 2) + 2) {
16099
+ return NaN;
16100
+ }
16101
+ for (let i = 0; i < 15; i += 3) {
16102
+ if (this.tris[i] !== 0) {
16103
+ return NaN;
16104
+ }
16105
+ }
16106
+ for (let i = 15; i < 24; i += 3) {
16107
+ if (this.tris[i] !== 3) {
16108
+ return NaN;
16109
+ }
16110
+ }
16111
+ for (let i = 24; i < 30; i += 3) {
16112
+ if (this.tris[i] !== 4) {
16113
+ return NaN;
16114
+ }
16115
+ }
16116
+ return order;
16117
+ }
16118
+ decimateFaces(n, ntarget) {
16119
+ let fac = this.tris;
16120
+ const V0 = 12;
16121
+ const F0 = 20;
16122
+ for (let j = n - 1; j >= ntarget; j--) {
16123
+ const nVjprev = Math.pow(4, j + 1) * (V0 - 2) + 2;
16124
+ const nVj = Math.pow(4, j) * (V0 - 2) + 2;
16125
+ const nFjprev = fac.length / 3;
16126
+ const nFj = Math.pow(4, j) * F0;
16127
+ console.log(`order ${j + 1} -> ${j} vertices ${nVjprev} -> ${nVj} faces ${nFjprev} -> ${nFj}`);
16128
+ const remap = Array.from({ length: nVjprev }, (_, i) => i + 1);
16129
+ for (let i = 0; i < nFjprev; i++) {
16130
+ const v1 = fac[3 * i];
16131
+ const v2 = fac[3 * i + 1];
16132
+ const v3 = fac[3 * i + 2];
16133
+ remap[v1 - 1] = Math.min(remap[v1 - 1], v2, v3);
16134
+ }
16135
+ const facJ = new Uint32Array(nFj * 3);
16136
+ for (let i = 0; i < nFj; i++) {
16137
+ facJ[3 * i] = remap[fac[3 * i] - 1];
16138
+ facJ[3 * i + 1] = remap[fac[3 * i + 1] - 1];
16139
+ facJ[3 * i + 2] = remap[fac[3 * i + 2] - 1];
16140
+ }
16141
+ fac = facJ;
16142
+ }
16143
+ this.tris = new Uint32Array(fac);
16144
+ }
16145
+ // internal function simplifies FreeSurfer triangulated mesh and overlays
16146
+ decimateHierarchicalMesh(gl, order = 4) {
16147
+ const inputOrder = this.hierarchicalOrder();
16148
+ if (isNaN(inputOrder)) {
16149
+ log.warn("Unable to decimate mesh: it does not have a hierarchical structure");
16150
+ return false;
16151
+ }
16152
+ if (order >= inputOrder) {
16153
+ log.warn(`Unable to decimate mesh: input order (${inputOrder}) must be larger than downsampled order (${order})`);
16154
+ return false;
16155
+ }
16156
+ const inputVLength = this.pts.length / 3;
16157
+ const V0 = 12;
16158
+ const nV = Math.pow(4, order) * (V0 - 2) + 2;
16159
+ this.pts = new Float32Array(this.pts.slice(0, nV * 3));
16160
+ this.decimateFaces(inputOrder, order);
16161
+ if (this.layers && this.layers.length > 0) {
16162
+ for (let i = 0; i < this.layers.length; i++) {
16163
+ const layer = this.layers[i];
16164
+ if (layer.values instanceof Float32Array || layer.values.length !== inputVLength) {
16165
+ layer.values = new Float32Array(layer.values.slice(0, nV));
16166
+ } else {
16167
+ log.warn(`decimation logic needs to be updated`);
16168
+ }
16169
+ }
16170
+ }
16171
+ this.updateMesh(gl);
16172
+ return true;
16173
+ }
15964
16174
  // adjust attributes of a mesh layer. invoked by niivue.setMeshLayerProperty()
15965
16175
  // TODO this method is a bit too generic
15966
16176
  setLayerProperty(id, key, val, gl) {
@@ -18016,7 +18226,7 @@ import { vec3 as vec38, vec4 as vec46 } from "gl-matrix";
18016
18226
  import * as nifti from "nifti-reader-js";
18017
18227
  import daikon from "daikon";
18018
18228
  import { mat3, mat4 as mat44, vec3 as vec37, vec4 as vec45 } from "gl-matrix";
18019
- import { Decompress, decompressSync as decompressSync3, gzipSync } from "fflate/browser";
18229
+ import { Decompress, decompressSync as decompressSync3, gzipSync as gzipSync2 } from "fflate/browser";
18020
18230
  import { v4 as uuidv42 } from "@lukeed/uuid";
18021
18231
 
18022
18232
  // src/nvimage/utils.ts
@@ -20875,7 +21085,7 @@ var NVImage = class _NVImage {
20875
21085
  let saveData = null;
20876
21086
  const compress2 = fnm.endsWith(".gz");
20877
21087
  if (compress2) {
20878
- saveData = gzipSync(odata, {
21088
+ saveData = gzipSync2(odata, {
20879
21089
  // GZIP-specific: the filename to use when decompressed
20880
21090
  filename: fnm,
20881
21091
  // GZIP-specific: the modification time. Can be a Date, date string,
@@ -21338,7 +21548,7 @@ var NVImage = class _NVImage {
21338
21548
  * read a 3D slab of voxels from a volume
21339
21549
  * @param voxStart - first row, column and slice (RAS order) for selection
21340
21550
  * @param voxEnd - final row, column and slice (RAS order) for selection
21341
- * @param dataType - array data type. Options: 'same' (default), 'uint8', 'float32'
21551
+ * @param dataType - array data type. Options: 'same' (default), 'uint8', 'float32', 'scaled', 'normalized', 'windowed'
21342
21552
  * @returns the an array where ret[0] is the voxel values and ret[1] is dimension of selection
21343
21553
  * @see {@link https://niivue.github.io/niivue/features/slab_selection.html | live demo usage}
21344
21554
  */
@@ -21362,7 +21572,7 @@ var NVImage = class _NVImage {
21362
21572
  let dt = this.hdr.datatypeCode;
21363
21573
  if (dataType === "uint8") {
21364
21574
  dt = 2 /* DT_UINT8 */;
21365
- } else if (dataType === "float32") {
21575
+ } else if (dataType === "float32" || dataType === "scaled" || dataType === "normalized" || dataType === "windowed") {
21366
21576
  dt = 16 /* DT_FLOAT32 */;
21367
21577
  }
21368
21578
  if (dt === 2 /* DT_UINT8 */) {
@@ -21392,6 +21602,24 @@ var NVImage = class _NVImage {
21392
21602
  }
21393
21603
  }
21394
21604
  }
21605
+ if (dataType === "scaled" || dataType === "normalized" || dataType === "windowed") {
21606
+ for (let i2 = 0; i2 < img.length; i2++) {
21607
+ img[i2] = img[i2] * this.hdr.scl_slope + this.hdr.scl_inter;
21608
+ }
21609
+ }
21610
+ if (dataType === "normalized" || dataType === "windowed") {
21611
+ let mn = this.cal_min;
21612
+ let mx = this.cal_max;
21613
+ if (dataType === "normalized") {
21614
+ mn = this.global_min;
21615
+ mx = this.global_max;
21616
+ }
21617
+ const scale = 1 / (mx - mn);
21618
+ for (let i2 = 0; i2 < img.length; i2++) {
21619
+ img[i2] = (img[i2] - mn) * scale;
21620
+ img[i2] = Math.max(Math.min(img[i2], 1), 0);
21621
+ }
21622
+ }
21395
21623
  return [img, slabDims];
21396
21624
  }
21397
21625
  // getVolumeData()
@@ -25966,6 +26194,24 @@ var Niivue = class _Niivue {
25966
26194
  }
25967
26195
  return this.meshes[idx].indexNearestXYZmm(Xmm, Ymm, Zmm);
25968
26196
  }
26197
+ /**
26198
+ * reduce complexity of FreeSurfer mesh
26199
+ * @param mesh - identity of mesh to change
26200
+ * @param order - decimation order 0..6
26201
+ * @example niivue.decimateHierarchicalMesh(niivue.meshes[0].id, 4)
26202
+ * @returns boolean false if mesh is not hierarchical or of lower order
26203
+ * @see {@link https://niivue.github.io/niivue/features/meshes.html | live demo usage}
26204
+ */
26205
+ decimateHierarchicalMesh(mesh, order = 3) {
26206
+ const idx = this.getMeshIndexByID(mesh);
26207
+ if (idx < 0) {
26208
+ log.warn("reverseFaces() id not loaded", mesh);
26209
+ return;
26210
+ }
26211
+ const ret = this.meshes[idx].decimateHierarchicalMesh(this.gl, order);
26212
+ this.updateGLVolume();
26213
+ return ret;
26214
+ }
25969
26215
  /**
25970
26216
  * reverse triangle winding of mesh (swap front and back faces)
25971
26217
  * @param id - identity of mesh to change
@@ -26464,6 +26710,9 @@ var Niivue = class _Niivue {
26464
26710
  this.renderShader.use(this.gl);
26465
26711
  this.gl.uniform3fv(this.renderShader.uniforms.clipLo, this.opts.clipVolumeLow);
26466
26712
  this.gl.uniform3fv(this.renderShader.uniforms.clipHi, this.opts.clipVolumeHigh);
26713
+ this.pickingImageShader.use(this.gl);
26714
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo, this.opts.clipVolumeLow);
26715
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi, this.opts.clipVolumeHigh);
26467
26716
  this.drawScene();
26468
26717
  }
26469
26718
  /**
@@ -28765,6 +29014,8 @@ var Niivue = class _Niivue {
28765
29014
  this.pickingImageShader.use(this.gl);
28766
29015
  this.gl.uniform1f(this.pickingImageShader.uniforms.overlays, this.overlays.length);
28767
29016
  this.gl.uniform3fv(this.pickingImageShader.uniforms.texVox, vox);
29017
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipLo, this.opts.clipVolumeLow);
29018
+ this.gl.uniform3fv(this.pickingImageShader.uniforms.clipHi, this.opts.clipVolumeHigh);
28768
29019
  let shader = this.sliceMMShader;
28769
29020
  if (this.opts.isV1SliceShader) {
28770
29021
  shader = this.sliceV1Shader;
@@ -32679,6 +32930,7 @@ export {
32679
32930
  NVMeshFromUrlOptions,
32680
32931
  NVMeshLayerDefaults,
32681
32932
  NVMeshLoaders,
32933
+ NVMeshUtilities,
32682
32934
  NVUtilities,
32683
32935
  Niivue,
32684
32936
  SHOW_RENDER,