@niivue/niivue 0.40.0 → 0.41.0

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
@@ -14,7 +14,7 @@ import { Subject } from "rxjs";
14
14
  import { mat4 as mat46, vec2, vec3 as vec310, vec4 as vec47 } from "gl-matrix";
15
15
 
16
16
  // package.json
17
- var version = "0.40.0";
17
+ var version = "0.41.0";
18
18
 
19
19
  // src/logger.ts
20
20
  var _Log = class _Log {
@@ -1004,7 +1004,7 @@ uniform float layer;
1004
1004
  uniform highp sampler2D colormap;
1005
1005
  uniform lowp sampler3D blend3D;
1006
1006
  uniform float opacity;
1007
- uniform vec3 xyzFrac;
1007
+ uniform vec4 xyzaFrac;
1008
1008
  uniform mat4 mtx;
1009
1009
  void main(void) {
1010
1010
  vec4 vx = vec4(TexCoord.x, TexCoord.y, coordZ, 1.0) * mtx;
@@ -1012,41 +1012,30 @@ void main(void) {
1012
1012
  FragColor = vec4(0.0, 0.0, 0.0, 0.0);
1013
1013
  if (idx == uint(0))
1014
1014
  return;
1015
- if (xyzFrac.x > 0.0) { //outline
1016
- vx = vec4(TexCoord.x+xyzFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1015
+ //idx = ((idx - uint(1)) % uint(100))+uint(1);
1016
+ float textureWidth = float(textureSize(colormap, 0).x);
1017
+ float fx = (float(idx)+0.5) / textureWidth;
1018
+ float nlayer = float(textureSize(colormap, 0).y);
1019
+ float y = ((2.0 * layer) + 1.5)/nlayer;
1020
+ FragColor = texture(colormap, vec2(fx, y)).rgba;
1021
+ float alpha = FragColor.a;
1022
+ FragColor.a *= opacity;
1023
+ if (xyzaFrac.a > 0.0) { //outline
1024
+ vx = vec4(TexCoord.x+xyzaFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1017
1025
  uint R = uint(texture(intensityVol, vx.xyz).r);
1018
- vx = vec4(TexCoord.x-xyzFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1026
+ vx = vec4(TexCoord.x-xyzaFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1019
1027
  uint L = uint(texture(intensityVol, vx.xyz).r);
1020
- vx = vec4(TexCoord.x, TexCoord.y+xyzFrac.y, coordZ, 1.0) * mtx;
1028
+ vx = vec4(TexCoord.x, TexCoord.y+xyzaFrac.y, coordZ, 1.0) * mtx;
1021
1029
  uint A = uint(texture(intensityVol, vx.xyz).r);
1022
- vx = vec4(TexCoord.x, TexCoord.y-xyzFrac.y, coordZ, 1.0) * mtx;
1030
+ vx = vec4(TexCoord.x, TexCoord.y-xyzaFrac.y, coordZ, 1.0) * mtx;
1023
1031
  uint P = uint(texture(intensityVol, vx.xyz).r);
1024
- vx = vec4(TexCoord.x, TexCoord.y, coordZ+xyzFrac.z, 1.0) * mtx;
1032
+ vx = vec4(TexCoord.x, TexCoord.y, coordZ+xyzaFrac.z, 1.0) * mtx;
1025
1033
  uint S = uint(texture(intensityVol, vx.xyz).r);
1026
- vx = vec4(TexCoord.x, TexCoord.y, coordZ-xyzFrac.z, 1.0) * mtx;
1034
+ vx = vec4(TexCoord.x, TexCoord.y, coordZ-xyzaFrac.z, 1.0) * mtx;
1027
1035
  uint I = uint(texture(intensityVol, vx.xyz).r);
1028
- if ((idx == R) && (idx == L) && (idx == A) && (idx == P) && (idx == S) && (idx == I))
1029
- return;
1036
+ if ((idx != R) || (idx != L) || (idx != A) || (idx != P) || (idx != S) || (idx != I))
1037
+ FragColor.a = alpha * xyzaFrac.a;
1030
1038
  }
1031
- idx = ((idx - uint(1)) % uint(100))+uint(1);
1032
- float textureWidth = float(textureSize(colormap, 0).x);
1033
- float fx = (float(idx)+0.5) / textureWidth;
1034
- float nlayer = float(textureSize(colormap, 0).y);
1035
- float y = ((2.0 * layer) + 1.5)/nlayer;
1036
- FragColor = texture(colormap, vec2(fx, y)).rgba;
1037
- //FragColor.a *= opacity;
1038
- FragColor.a = opacity;
1039
- return;
1040
-
1041
- if (layer < 2.0) return;
1042
- //vec2 texXY = TexCoord.xy*0.5 +vec2(0.5,0.5);
1043
- //vec4 prevColor = texture(blend3D, vec3(texXY, coordZ));
1044
- vec4 prevColor = texture(blend3D, vec3(TexCoord.xy, coordZ));
1045
- // https://en.wikipedia.org/wiki/Alpha_compositing
1046
- float aout = FragColor.a + (1.0 - FragColor.a) * prevColor.a;
1047
- if (aout <= 0.0) return;
1048
- FragColor.rgb = ((FragColor.rgb * FragColor.a) + (prevColor.rgb * prevColor.a * (1.0 - FragColor.a))) / aout;
1049
- FragColor.a = aout;
1050
1039
  }`;
1051
1040
  var fragOrientShader = `#line 691
1052
1041
  precision highp int;
@@ -1279,9 +1268,11 @@ uniform mat4 mvpMtx;
1279
1268
  uniform mat4 normMtx;
1280
1269
  out vec4 vClr;
1281
1270
  out vec3 vN;
1271
+ out vec4 vP;
1282
1272
  void main(void) {
1283
1273
  vec3 lightPosition = vec3(0.0, 0.0, -10.0);
1284
- gl_Position = mvpMtx * vec4(pos, 1.0);
1274
+ vP = vec4(pos, 1.0);
1275
+ gl_Position = mvpMtx * vP;
1285
1276
  vN = normalize((normMtx * vec4(norm.xyz,1.0)).xyz);
1286
1277
  //vV = -vec3(modelMtx*vec4(pos,1.0));
1287
1278
  vClr = clr;
@@ -11044,7 +11035,7 @@ var ColorTables = class {
11044
11035
  const cmap = this.colormapFromKey(key);
11045
11036
  return this.makeLut(cmap.R, cmap.G, cmap.B, cmap.A, cmap.I, isInvert);
11046
11037
  }
11047
- makeLabelLut(cm, alphaFill = 64) {
11038
+ makeLabelLut(cm, alphaFill = 255) {
11048
11039
  if (cm.R === void 0 || cm.G === void 0 || cm.B === void 0) {
11049
11040
  throw new Error(`Invalid colormap table: ${cm}`);
11050
11041
  }
@@ -12174,7 +12165,7 @@ var NVMeshLoaders = class _NVMeshLoaders {
12174
12165
  }
12175
12166
  // readTxtVTK()
12176
12167
  // read mesh overlay to influence vertex colors
12177
- static readLayer(name = "", buffer, nvmesh, opacity = 0.5, colormap = "warm", colormapNegative = "winter", useNegativeCmap = false, cal_min = null, cal_max = null, isOutlineBorder = false) {
12168
+ static readLayer(name = "", buffer, nvmesh, opacity = 0.5, colormap = "warm", colormapNegative = "winter", useNegativeCmap = false, cal_min = null, cal_max = null, outlineBorder = 0) {
12178
12169
  const layer = {
12179
12170
  colormapInvert: false,
12180
12171
  alphaThreshold: false,
@@ -12271,7 +12262,7 @@ var NVMeshLoaders = class _NVMeshLoaders {
12271
12262
  }
12272
12263
  layer.nFrame4D = layer.values.length / n_vert;
12273
12264
  layer.frame4D = 0;
12274
- layer.isOutlineBorder = isOutlineBorder;
12265
+ layer.outlineBorder = outlineBorder;
12275
12266
  let mn = layer.values[0];
12276
12267
  let mx = layer.values[0];
12277
12268
  for (let i = 0; i < layer.values.length; i++) {
@@ -12941,7 +12932,11 @@ var NVMeshLoaders = class _NVMeshLoaders {
12941
12932
  isSCALAR = true;
12942
12933
  }
12943
12934
  if (isSCALAR) {
12944
- const FSizeWoScalars = 16 + nskip + (isFace ? 1 : 0) * nface * 12 + (isVert ? 1 : 0) * n_vert * 12 + (isRGBA ? 1 : 0) * n_vert * 4;
12935
+ let nv = n_vert;
12936
+ if (n_vert === 0) {
12937
+ nv = nvert;
12938
+ }
12939
+ const FSizeWoScalars = 16 + nskip + (isFace ? 1 : 0) * nface * 12 + (isVert ? 1 : 0) * nv * 12 + (isRGBA ? 1 : 0) * nv * 4;
12945
12940
  const scalarFloats = Math.floor((_buffer.byteLength - FSizeWoScalars) / bytesPerScalar);
12946
12941
  if (nvert !== n_vert && scalarFloats % n_vert === 0) {
12947
12942
  log.warn("Issue 729: mz3 mismatch scalar NVERT does not match mesh NVERT");
@@ -13405,9 +13400,51 @@ var NVMeshLoaders = class _NVMeshLoaders {
13405
13400
  };
13406
13401
  }
13407
13402
  // readOFF()
13403
+ static readOBJMNI(buffer) {
13404
+ const enc = new TextDecoder("utf-8");
13405
+ const txt = enc.decode(buffer);
13406
+ const items = txt.trim().split(/\s*,\s*|\s+/);
13407
+ if (items.length < 1 || items[0] !== "P") {
13408
+ log.warn("This is not a valid MNI OBJ mesh.");
13409
+ }
13410
+ let j = 6;
13411
+ const nVert = parseInt(items[j++]);
13412
+ const nVertX3 = nVert * 3;
13413
+ const positions = new Float32Array(nVertX3);
13414
+ for (let i = 0; i < nVertX3; i++) {
13415
+ positions[i] = parseFloat(items[j++]);
13416
+ }
13417
+ j += nVertX3;
13418
+ const nTri = parseInt(items[j++]);
13419
+ const colour_flag = parseInt(items[j++]);
13420
+ if (nTri < 1 || colour_flag < 0 || colour_flag > 2) {
13421
+ log.warn("This is not a valid MNI OBJ mesh.");
13422
+ }
13423
+ let num_c = 1;
13424
+ if (colour_flag === 1) {
13425
+ num_c = nTri;
13426
+ } else if (colour_flag === 1) {
13427
+ num_c = nVert;
13428
+ }
13429
+ j += num_c * 4;
13430
+ j += nTri;
13431
+ const nTriX3 = nTri * 3;
13432
+ const indices = new Int32Array(nTriX3);
13433
+ for (let i = 0; i < nTriX3; i++) {
13434
+ indices[i] = parseInt(items[j++]);
13435
+ }
13436
+ return {
13437
+ positions,
13438
+ indices
13439
+ };
13440
+ }
13441
+ // readOBJMNI()
13408
13442
  static readOBJ(buffer) {
13409
13443
  const enc = new TextDecoder("utf-8");
13410
13444
  const txt = enc.decode(buffer);
13445
+ if (txt[0] === "P") {
13446
+ return this.readOBJMNI(buffer);
13447
+ }
13411
13448
  const lines = txt.split("\n");
13412
13449
  const n = lines.length;
13413
13450
  const pts = [];
@@ -14099,33 +14136,20 @@ var NVMeshLoaders = class _NVMeshLoaders {
14099
14136
  static readX3D(buffer) {
14100
14137
  const len = buffer.byteLength;
14101
14138
  if (len < 20) {
14102
- throw new Error("File too small to be GII: bytes = " + len);
14139
+ throw new Error("File too small to be X3D: bytes = " + len);
14103
14140
  }
14104
14141
  const bytes = new Uint8Array(buffer);
14105
14142
  let pos = 0;
14106
- function readStrX() {
14107
- while (pos < len && bytes[pos] === 10) {
14143
+ function readStr() {
14144
+ while (pos < len && bytes[pos] !== 60) {
14108
14145
  pos++;
14109
14146
  }
14110
- const startPos = pos;
14111
- while (pos < len && bytes[pos] !== 10) {
14147
+ const startP = pos;
14148
+ while (pos < len && bytes[pos] !== 62) {
14112
14149
  pos++;
14113
14150
  }
14114
- pos++;
14115
- if (pos - startPos < 1) {
14116
- return "";
14117
- }
14118
- return new TextDecoder().decode(buffer.slice(startPos, pos - 1)).trim();
14119
- }
14120
- function readStr() {
14121
- let line2 = readStrX();
14122
- if (!line2.startsWith("<") || line2.endsWith(">")) {
14123
- return line2;
14124
- }
14125
- while (pos < len && !line2.endsWith(">")) {
14126
- line2 += readStrX();
14127
- }
14128
- return line2;
14151
+ const endP = pos;
14152
+ return new TextDecoder().decode(buffer.slice(startP, endP + 1)).trim();
14129
14153
  }
14130
14154
  let line = readStr();
14131
14155
  function readStringTag(TagName) {
@@ -14146,22 +14170,30 @@ var NVMeshLoaders = class _NVMeshLoaders {
14146
14170
  const delimiter = line[fpos + TagName.length + 1];
14147
14171
  const spos = line.indexOf(delimiter, fpos) + 1;
14148
14172
  const epos = line.indexOf(delimiter, spos);
14149
- const str = line.slice(spos, epos).trim();
14150
- const items = str.trim().split(/\s+/);
14173
+ let str = line.slice(spos, epos).trim();
14174
+ str = str.replace(/,\s*$/, "");
14175
+ const items = str.trim().split(/\s*,\s*|\s+/);
14151
14176
  if (items.length < 2) {
14152
14177
  return parseFloat(str);
14153
14178
  }
14154
- const ret = [];
14179
+ let ret = new Array(items.length);
14180
+ let j = 0;
14155
14181
  for (let i = 0; i < items.length; i++) {
14156
- ret.push(parseFloat(items[i]));
14182
+ const v = parseFloat(items[i]);
14183
+ if (!isFinite(v)) {
14184
+ continue;
14185
+ }
14186
+ ret[j] = v;
14187
+ j++;
14157
14188
  }
14189
+ ret = ret.slice(0, j);
14158
14190
  return ret;
14159
14191
  }
14160
14192
  if (!line.includes("xml version")) {
14161
14193
  log.warn("Not a X3D image");
14162
14194
  }
14163
14195
  let positions = [];
14164
- const indices = [];
14196
+ let indices = [];
14165
14197
  let rgba255 = [];
14166
14198
  let color = [];
14167
14199
  let translation = [0, 0, 0, 0];
@@ -14239,12 +14271,20 @@ var NVMeshLoaders = class _NVMeshLoaders {
14239
14271
  height = -2;
14240
14272
  coordIndex = readNumericTag("coordIndex");
14241
14273
  }
14274
+ if (line.startsWith("<IndexedTriangleSet")) {
14275
+ height = -7;
14276
+ coordIndex = readNumericTag("index");
14277
+ }
14242
14278
  if (line.startsWith("<IndexedTriangleStripSet")) {
14243
14279
  height = -3;
14244
14280
  coordIndex = readNumericTag("index");
14245
14281
  }
14246
14282
  if (line.startsWith("<Coordinate")) {
14247
14283
  point = readNumericTag("point");
14284
+ const rem = point.length % 3;
14285
+ if (rem !== 0) {
14286
+ point = point.slice(0, -rem);
14287
+ }
14248
14288
  }
14249
14289
  if (line.startsWith("<Color")) {
14250
14290
  color = readNumericTag("color");
@@ -14262,7 +14302,7 @@ var NVMeshLoaders = class _NVMeshLoaders {
14262
14302
  log.warn("Unsupported x3d shape: ElevationGrid");
14263
14303
  }
14264
14304
  }
14265
- if (height < -3) {
14305
+ if (height < -3 && height !== -7) {
14266
14306
  } else if (height < -1) {
14267
14307
  if (coordIndex.length < 1 || point.length < 3 || point.length === void 0) {
14268
14308
  log.warn("Indexed mesh must specify indices and points");
@@ -14270,7 +14310,9 @@ var NVMeshLoaders = class _NVMeshLoaders {
14270
14310
  }
14271
14311
  const idx0 = Math.floor(positions.length / 3);
14272
14312
  let j = 2;
14273
- if (height === -2) {
14313
+ if (height === -7) {
14314
+ indices = [...indices, ...coordIndex];
14315
+ } else if (height === -2) {
14274
14316
  let triStart = 0;
14275
14317
  while (j < coordIndex.length) {
14276
14318
  if (coordIndex[j] >= 0) {
@@ -14329,7 +14371,7 @@ var NVMeshLoaders = class _NVMeshLoaders {
14329
14371
  }
14330
14372
  }
14331
14373
  return {
14332
- positions,
14374
+ positions: Float32Array.from(positions),
14333
14375
  indices: Int32Array.from(indices),
14334
14376
  rgba255
14335
14377
  };
@@ -15359,6 +15401,9 @@ var NVMesh = class _NVMesh {
15359
15401
  if (opacity <= 0 || layer.cal_min > layer.cal_max) {
15360
15402
  continue;
15361
15403
  }
15404
+ if (layer.outlineBorder === void 0) {
15405
+ layer.outlineBorder = 0;
15406
+ }
15362
15407
  if (layer.isAdditiveBlend === void 0) {
15363
15408
  layer.isAdditiveBlend = false;
15364
15409
  }
@@ -15380,23 +15425,23 @@ var NVMesh = class _NVMesh {
15380
15425
  rgba8[k + 2] = lut2[idx + 2];
15381
15426
  k += 4;
15382
15427
  }
15383
- let opaque = new Array(nvtx).fill(true);
15384
- if (layer.isOutlineBorder) {
15428
+ let opaque = new Array(nvtx).fill(false);
15429
+ if (layer.outlineBorder > 0) {
15385
15430
  opaque = NVMeshUtilities.getClusterBoundary(rgba8, this.tris);
15386
15431
  }
15387
15432
  k = 0;
15388
15433
  for (let j = 0; j < layer.values.length; j++) {
15389
- if (!opaque[j]) {
15390
- k += 4;
15391
- continue;
15434
+ let opa = opacity;
15435
+ if (opaque[j]) {
15436
+ opa = layer.outlineBorder;
15392
15437
  }
15393
15438
  let vtx = j * 28 + 24;
15394
15439
  if (this.f32PerVertex !== 7) {
15395
15440
  vtx = j * 20 + 16;
15396
15441
  }
15397
- u8[vtx + 0] = lerp(u8[vtx + 0], rgba8[k + 0], opacity);
15398
- u8[vtx + 1] = lerp(u8[vtx + 1], rgba8[k + 1], opacity);
15399
- u8[vtx + 2] = lerp(u8[vtx + 2], rgba8[k + 2], opacity);
15442
+ u8[vtx + 0] = lerp(u8[vtx + 0], rgba8[k + 0], opa);
15443
+ u8[vtx + 1] = lerp(u8[vtx + 1], rgba8[k + 1], opa);
15444
+ u8[vtx + 2] = lerp(u8[vtx + 2], rgba8[k + 2], opa);
15400
15445
  k += 4;
15401
15446
  }
15402
15447
  continue;
@@ -15404,7 +15449,7 @@ var NVMesh = class _NVMesh {
15404
15449
  if (layer.values instanceof Uint8Array) {
15405
15450
  const rgba8 = new Uint8Array(layer.values.buffer);
15406
15451
  let opaque = new Array(nvtx).fill(true);
15407
- if (layer.isOutlineBorder) {
15452
+ if (layer.outlineBorder > 0) {
15408
15453
  opaque = NVMeshUtilities.getClusterBoundary(rgba8, this.tris);
15409
15454
  }
15410
15455
  let k = 0;
@@ -15413,14 +15458,13 @@ var NVMesh = class _NVMesh {
15413
15458
  if (this.f32PerVertex !== 7) {
15414
15459
  vtx = j * 20 + 16;
15415
15460
  }
15416
- if (!opaque[j]) {
15417
- u8[vtx + 3] = 0;
15418
- k += 4;
15419
- continue;
15461
+ let opa = opacity;
15462
+ if (opaque[j]) {
15463
+ opa = layer.outlineBorder;
15420
15464
  }
15421
- u8[vtx + 0] = lerp(u8[vtx + 0], rgba8[k + 0], opacity);
15422
- u8[vtx + 1] = lerp(u8[vtx + 1], rgba8[k + 1], opacity);
15423
- u8[vtx + 2] = lerp(u8[vtx + 2], rgba8[k + 2], opacity);
15465
+ u8[vtx + 0] = lerp(u8[vtx + 0], rgba8[k + 0], opa);
15466
+ u8[vtx + 1] = lerp(u8[vtx + 1], rgba8[k + 1], opa);
15467
+ u8[vtx + 2] = lerp(u8[vtx + 2], rgba8[k + 2], opa);
15424
15468
  k += 4;
15425
15469
  }
15426
15470
  continue;
@@ -15447,7 +15491,7 @@ var NVMesh = class _NVMesh {
15447
15491
  if (!layer.isTransparentBelowCalMin) {
15448
15492
  mnCal = Number.NEGATIVE_INFINITY;
15449
15493
  }
15450
- if (!layer.isOutlineBorder) {
15494
+ if (layer.outlineBorder <= 0) {
15451
15495
  for (let j = 0; j < nvtx; j++) {
15452
15496
  const v = layer.values[j + frameOffset];
15453
15497
  if (v < mnCal) {
@@ -15470,9 +15514,10 @@ var NVMesh = class _NVMesh {
15470
15514
  additiveRGBA[j4 + 2] = additiveBlend(additiveRGBA[j4 + 2], lut[v255 + 2]);
15471
15515
  additiveRGBA[j4 + 3] = additiveBlend(additiveRGBA[j4 + 3], 255);
15472
15516
  } else {
15473
- u8[vtx + 0] = lerp(u8[vtx + 0], lut[v255 + 0], opacity);
15474
- u8[vtx + 1] = lerp(u8[vtx + 1], lut[v255 + 1], opacity);
15475
- u8[vtx + 2] = lerp(u8[vtx + 2], lut[v255 + 2], opacity);
15517
+ const opa = opacity;
15518
+ u8[vtx + 0] = lerp(u8[vtx + 0], lut[v255 + 0], opa);
15519
+ u8[vtx + 1] = lerp(u8[vtx + 1], lut[v255 + 1], opa);
15520
+ u8[vtx + 2] = lerp(u8[vtx + 2], lut[v255 + 2], opa);
15476
15521
  }
15477
15522
  }
15478
15523
  } else {
@@ -15487,23 +15532,26 @@ var NVMesh = class _NVMesh {
15487
15532
  }
15488
15533
  const opaque = NVMeshUtilities.getClusterBoundaryU8(v255s, this.tris);
15489
15534
  for (let j = 0; j < nvtx; j++) {
15490
- let v255 = 255;
15491
- if (!opaque[j]) {
15535
+ if (v255s[j] < 1) {
15492
15536
  continue;
15493
15537
  }
15494
- v255 = Math.min(255, v255) * 4;
15538
+ let opa = opacity;
15539
+ if (opaque[j]) {
15540
+ opa = layer.outlineBorder;
15541
+ }
15542
+ const v255 = v255s[j] * 4;
15495
15543
  let vtx = j * 28 + 24;
15496
15544
  if (this.f32PerVertex !== 7) {
15497
15545
  vtx = j * 20 + 16;
15498
15546
  }
15499
- u8[vtx + 0] = lerp(u8[vtx + 0], lut[v255 + 0], opacity);
15500
- u8[vtx + 1] = lerp(u8[vtx + 1], lut[v255 + 1], opacity);
15501
- u8[vtx + 2] = lerp(u8[vtx + 2], lut[v255 + 2], opacity);
15547
+ u8[vtx + 0] = lerp(u8[vtx + 0], lut[v255 + 0], opa);
15548
+ u8[vtx + 1] = lerp(u8[vtx + 1], lut[v255 + 1], opa);
15549
+ u8[vtx + 2] = lerp(u8[vtx + 2], lut[v255 + 2], opa);
15502
15550
  }
15503
15551
  }
15504
15552
  if (layer.useNegativeCmap) {
15505
15553
  const lut2 = cmapper.colormap(layer.colormapNegative, layer.colormapInvert);
15506
- if (!layer.isOutlineBorder) {
15554
+ if (layer.outlineBorder <= 0) {
15507
15555
  let mn2 = layer.cal_min;
15508
15556
  let mx = layer.cal_max;
15509
15557
  if (isFinite(layer.cal_minNeg) && isFinite(layer.cal_minNeg)) {
@@ -15560,17 +15608,18 @@ var NVMesh = class _NVMesh {
15560
15608
  const opaque = NVMeshUtilities.getClusterBoundaryU8(v255s, this.tris);
15561
15609
  for (let j = 0; j < nvtx; j++) {
15562
15610
  let v255 = 255;
15563
- if (!opaque[j]) {
15564
- continue;
15611
+ let opa = opacity;
15612
+ if (opaque[j]) {
15613
+ opa = layer.outlineBorder;
15565
15614
  }
15566
15615
  v255 = Math.min(255, v255) * 4;
15567
15616
  let vtx = j * 28 + 24;
15568
15617
  if (this.f32PerVertex !== 7) {
15569
15618
  vtx = j * 20 + 16;
15570
15619
  }
15571
- u8[vtx + 0] = lerp(u8[vtx + 0], lut2[v255 + 0], opacity);
15572
- u8[vtx + 1] = lerp(u8[vtx + 1], lut2[v255 + 1], opacity);
15573
- u8[vtx + 2] = lerp(u8[vtx + 2], lut2[v255 + 2], opacity);
15620
+ u8[vtx + 0] = lerp(u8[vtx + 0], lut2[v255 + 0], opa);
15621
+ u8[vtx + 1] = lerp(u8[vtx + 1], lut2[v255 + 1], opa);
15622
+ u8[vtx + 2] = lerp(u8[vtx + 2], lut2[v255 + 2], opa);
15574
15623
  }
15575
15624
  }
15576
15625
  }
@@ -17950,7 +17999,7 @@ var NVIMAGE_TYPE = Object.freeze({
17950
17999
  return imageType;
17951
18000
  }
17952
18001
  });
17953
- var NVImageFromUrlOptions = (url, urlImageData = "", name = "", colormap = "gray", opacity = 1, cal_min = NaN, cal_max = NaN, trustCalMinMax = true, percentileFrac = 0.02, ignoreZeroVoxels = false, visible = true, useQFormNotSForm = false, colormapNegative = "", frame4D = 0, imageType = NVIMAGE_TYPE.UNKNOWN, cal_minNeg = NaN, cal_maxNeg = NaN, colorbarVisible = true, alphaThreshold = false, colormapLabel = []) => {
18002
+ var NVImageFromUrlOptions = (url, urlImageData = "", name = "", colormap = "gray", opacity = 1, cal_min = NaN, cal_max = NaN, trustCalMinMax = true, percentileFrac = 0.02, ignoreZeroVoxels = false, useQFormNotSForm = false, colormapNegative = "", frame4D = 0, imageType = NVIMAGE_TYPE.UNKNOWN, cal_minNeg = NaN, cal_maxNeg = NaN, colorbarVisible = true, alphaThreshold = false, colormapLabel = []) => {
17954
18003
  return {
17955
18004
  url,
17956
18005
  urlImageData,
@@ -17963,7 +18012,6 @@ var NVImageFromUrlOptions = (url, urlImageData = "", name = "", colormap = "gray
17963
18012
  trustCalMinMax,
17964
18013
  percentileFrac,
17965
18014
  ignoreZeroVoxels,
17966
- visible,
17967
18015
  useQFormNotSForm,
17968
18016
  colormapNegative,
17969
18017
  imageType,
@@ -18165,7 +18213,6 @@ var NVImage = class _NVImage {
18165
18213
  * @param trustCalMinMax - whether or not to trust cal_min and cal_max from the nifti header (trusting results in faster loading)
18166
18214
  * @param percentileFrac - the percentile to use for setting the robust range of the display values (smart intensity setting for images with large ranges)
18167
18215
  * @param ignoreZeroVoxels - whether or not to ignore zero voxels in setting the robust range of display values
18168
- * @param visible - whether or not this image is to be visible
18169
18216
  * @param useQFormNotSForm - give precedence to QForm (Quaternion) or SForm (Matrix)
18170
18217
  * @param colormapNegative - a color map to use for symmetrical negative intensities
18171
18218
  * @param frame4D - volume displayed, 0 indexed, must be less than nFrame4D
@@ -18181,7 +18228,7 @@ var NVImage = class _NVImage {
18181
18228
  * @param colorbarVisible - TODO
18182
18229
  * @param colormapLabel - TODO
18183
18230
  */
18184
- constructor(dataBuffer = null, name = "", colormap = "gray", opacity = 1, pairedImgData = null, cal_min = NaN, cal_max = NaN, trustCalMinMax = true, percentileFrac = 0.02, ignoreZeroVoxels = false, visible = true, useQFormNotSForm = false, colormapNegative = "", frame4D = 0, imageType = NVIMAGE_TYPE.UNKNOWN, cal_minNeg = NaN, cal_maxNeg = NaN, colorbarVisible = true, colormapLabel = null) {
18231
+ constructor(dataBuffer = null, name = "", colormap = "gray", opacity = 1, pairedImgData = null, cal_min = NaN, cal_max = NaN, trustCalMinMax = true, percentileFrac = 0.02, ignoreZeroVoxels = false, useQFormNotSForm = false, colormapNegative = "", frame4D = 0, imageType = NVIMAGE_TYPE.UNKNOWN, cal_minNeg = NaN, cal_maxNeg = NaN, colorbarVisible = true, colormapLabel = null) {
18185
18232
  __publicField(this, "name");
18186
18233
  __publicField(this, "id");
18187
18234
  __publicField(this, "url");
@@ -18202,7 +18249,6 @@ var NVImage = class _NVImage {
18202
18249
  __publicField(this, "cal_minNeg");
18203
18250
  __publicField(this, "cal_maxNeg");
18204
18251
  __publicField(this, "colorbarVisible", true);
18205
- __publicField(this, "visible");
18206
18252
  __publicField(this, "modulationImage", null);
18207
18253
  __publicField(this, "modulateAlpha", 0);
18208
18254
  // if !=0, mod transparency with expon power |Alpha|
@@ -18305,7 +18351,6 @@ var NVImage = class _NVImage {
18305
18351
  this.cal_minNeg = cal_minNeg;
18306
18352
  this.cal_maxNeg = cal_maxNeg;
18307
18353
  this.colorbarVisible = colorbarVisible;
18308
- this.visible = visible;
18309
18354
  this.useQFormNotSForm = useQFormNotSForm;
18310
18355
  if (!dataBuffer) {
18311
18356
  return;
@@ -20568,7 +20613,6 @@ var NVImage = class _NVImage {
20568
20613
  trustCalMinMax = true,
20569
20614
  percentileFrac = 0.02,
20570
20615
  ignoreZeroVoxels = false,
20571
- visible = true,
20572
20616
  useQFormNotSForm = false,
20573
20617
  colormapNegative = "",
20574
20618
  frame4D = 0,
@@ -20710,7 +20754,6 @@ var NVImage = class _NVImage {
20710
20754
  trustCalMinMax,
20711
20755
  percentileFrac,
20712
20756
  ignoreZeroVoxels,
20713
- visible,
20714
20757
  useQFormNotSForm,
20715
20758
  colormapNegative,
20716
20759
  frame4D,
@@ -20755,7 +20798,6 @@ var NVImage = class _NVImage {
20755
20798
  trustCalMinMax = true,
20756
20799
  percentileFrac = 0.02,
20757
20800
  ignoreZeroVoxels = false,
20758
- visible = true,
20759
20801
  useQFormNotSForm = false,
20760
20802
  colormapNegative = "",
20761
20803
  frame4D = 0,
@@ -20845,7 +20887,6 @@ var NVImage = class _NVImage {
20845
20887
  trustCalMinMax,
20846
20888
  percentileFrac,
20847
20889
  ignoreZeroVoxels,
20848
- visible,
20849
20890
  useQFormNotSForm,
20850
20891
  colormapNegative,
20851
20892
  frame4D,
@@ -20877,8 +20918,7 @@ var NVImage = class _NVImage {
20877
20918
  cal_max = NaN,
20878
20919
  trustCalMinMax = true,
20879
20920
  percentileFrac = 0.02,
20880
- ignoreZeroVoxels = false,
20881
- visible = true
20921
+ ignoreZeroVoxels = false
20882
20922
  }) {
20883
20923
  function base64ToArrayBuffer(base642) {
20884
20924
  const binary_string = window.atob(base642);
@@ -20903,8 +20943,7 @@ var NVImage = class _NVImage {
20903
20943
  cal_max,
20904
20944
  trustCalMinMax,
20905
20945
  percentileFrac,
20906
- ignoreZeroVoxels,
20907
- visible
20946
+ ignoreZeroVoxels
20908
20947
  );
20909
20948
  } catch (err) {
20910
20949
  log.debug(err);
@@ -21172,8 +21211,6 @@ var NVImage = class _NVImage {
21172
21211
  // percentileFrac
21173
21212
  this.ignoreZeroVoxels,
21174
21213
  // ignoreZeroVoxels
21175
- this.visible,
21176
- // visible
21177
21214
  this.useQFormNotSForm,
21178
21215
  // useQFormNotSForm
21179
21216
  this.colormapNegative,
@@ -21373,7 +21410,7 @@ var DEFAULT_OPTIONS = {
21373
21410
  keyDebounceTime: 50,
21374
21411
  isNearestInterpolation: false,
21375
21412
  isResizeCanvas: true,
21376
- isAtlasOutline: false,
21413
+ atlasOutline: 0,
21377
21414
  isRuler: false,
21378
21415
  isColorbar: false,
21379
21416
  isOrientCube: false,
@@ -21649,7 +21686,6 @@ var NVDocument = class _NVDocument {
21649
21686
  trustCalMinMax: true,
21650
21687
  percentileFrac: 0.02,
21651
21688
  ignoreZeroVoxels: false,
21652
- visible: true,
21653
21689
  useQFormNotSForm: false,
21654
21690
  colormapNegative: "",
21655
21691
  colormapLabel: [],
@@ -21695,7 +21731,6 @@ var NVDocument = class _NVDocument {
21695
21731
  trustCalMinMax: true,
21696
21732
  percentileFrac: 0.02,
21697
21733
  ignoreZeroVoxels: false,
21698
- visible: true,
21699
21734
  useQFormNotSForm: false,
21700
21735
  colormapNegative: "",
21701
21736
  colormapLabel: [],
@@ -21748,7 +21783,7 @@ var NVDocument = class _NVDocument {
21748
21783
  values: layer.values,
21749
21784
  nFrame4D: layer.nFrame4D,
21750
21785
  frame4D: 0,
21751
- isOutlineBorder: layer.isOutlineBorder,
21786
+ outlineBorder: layer.outlineBorder,
21752
21787
  global_min: layer.global_min,
21753
21788
  global_max: layer.global_max,
21754
21789
  cal_min: layer.cal_min,
@@ -23115,6 +23150,8 @@ var Niivue = class _Niivue {
23115
23150
  __publicField(this, "position");
23116
23151
  __publicField(this, "extentsMin");
23117
23152
  __publicField(this, "extentsMax");
23153
+ // ResizeObserver
23154
+ __publicField(this, "resizeObserver", null);
23118
23155
  __publicField(this, "syncOpts", {});
23119
23156
  __publicField(this, "readyForSync", false);
23120
23157
  // UI Data
@@ -23606,6 +23643,8 @@ var Niivue = class _Niivue {
23606
23643
  this.canvas.width = this.canvas.offsetWidth;
23607
23644
  this.canvas.height = this.canvas.offsetHeight;
23608
23645
  window.addEventListener("resize", this.resizeListener.bind(this));
23646
+ this.resizeObserver = new ResizeObserver(this.resizeListener.bind(this));
23647
+ this.resizeObserver.observe(this.canvas.parentElement);
23609
23648
  }
23610
23649
  this.registerInteractions();
23611
23650
  await this.init();
@@ -25211,6 +25250,24 @@ var Niivue = class _Niivue {
25211
25250
  this.updateGLVolume();
25212
25251
  this.onMeshPropertyChanged(idx, key, val);
25213
25252
  }
25253
+ /**
25254
+ * returns the index of the mesh vertex that is closest to the provided coordinates
25255
+ * @param id - identity of mesh to change
25256
+ * @param Xmm - location in left/right dimension
25257
+ * @param Ymm - location in posterior/anterior dimension
25258
+ * @param Zmm - location in foot/head dimension
25259
+ * @returns the an array where ret[0] is the mesh index and ret[1] is distance from vertex to coordinates
25260
+ * @example niivue.indexNearestXYZmm(niivue.meshes[0].id, -22, 42, 13)
25261
+ * @see {@link https://niivue.github.io/niivue/features/clipplanes.html | live demo usage}
25262
+ */
25263
+ indexNearestXYZmm(mesh, Xmm, Ymm, Zmm) {
25264
+ const idx = this.getMeshIndexByID(mesh);
25265
+ if (idx < 0) {
25266
+ log.warn("indexNearestXYZmm() id not loaded", mesh);
25267
+ return [NaN, NaN];
25268
+ }
25269
+ return this.meshes[idx].indexNearestXYZmm(Xmm, Ymm, Zmm);
25270
+ }
25214
25271
  /**
25215
25272
  * reverse triangle winding of mesh (swap front and back faces)
25216
25273
  * @param id - identity of mesh to change
@@ -27880,13 +27937,16 @@ var Niivue = class _Niivue {
27880
27937
  if (!this.back.dims) {
27881
27938
  throw new Error("back.dims undefined");
27882
27939
  }
27940
+ let outline = 0;
27883
27941
  if (hdr.intent_code === 1002) {
27884
- let x = 1 / this.back.dims[1];
27885
- if (!this.opts.isAtlasOutline) {
27886
- x = -x;
27887
- }
27888
- this.gl.uniform3fv(orientShader.uniforms.xyzFrac, [x, 1 / this.back.dims[2], 1 / this.back.dims[3]]);
27942
+ outline = this.opts.atlasOutline;
27889
27943
  }
27944
+ this.gl.uniform4fv(orientShader.uniforms.xyzaFrac, [
27945
+ 1 / this.back.dims[1],
27946
+ 1 / this.back.dims[2],
27947
+ 1 / this.back.dims[3],
27948
+ outline
27949
+ ]);
27890
27950
  log.debug("back dims: ", this.back.dims);
27891
27951
  for (let i = 0; i < this.back.dims[3]; i++) {
27892
27952
  const coordZ = 1 / this.back.dims[3] * (i + 0.5);
@@ -28100,7 +28160,7 @@ var Niivue = class _Niivue {
28100
28160
  */
28101
28161
  getFrame4D(id) {
28102
28162
  const idx = this.getVolumeIndexByID(id);
28103
- return this.volumes[idx].nFrame4D;
28163
+ return this.volumes[idx].frame4D;
28104
28164
  }
28105
28165
  // not included in public docs
28106
28166
  colormapFromKey(name) {
@@ -29013,7 +29073,7 @@ var Niivue = class _Niivue {
29013
29073
  }
29014
29074
  // not included in public docs
29015
29075
  setAtlasOutline(isOutline) {
29016
- this.opts.isAtlasOutline = isOutline;
29076
+ this.opts.atlasOutline = isOutline;
29017
29077
  this.updateGLVolume();
29018
29078
  this.drawScene();
29019
29079
  }