@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.d.ts +25 -17
- package/build/index.js +182 -122
- package/build/index.js.map +1 -1
- package/build/index.min.js +1 -1
- package/package.json +6 -5
- package/src/colortables.ts +1 -1
- package/src/index.html +1 -1
- package/src/niivue/index.ts +35 -11
- package/src/nvdocument.ts +3 -5
- package/src/nvimage/index.ts +2 -13
- package/src/nvimage/utils.ts +0 -8
- package/src/nvmesh-loaders.ts +94 -44
- package/src/nvmesh-types.ts +2 -2
- package/src/nvmesh.ts +40 -35
- package/src/shader-srcs.ts +21 -31
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.
|
|
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
|
|
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
|
-
|
|
1016
|
-
|
|
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-
|
|
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+
|
|
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-
|
|
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+
|
|
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-
|
|
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
|
|
1029
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
14107
|
-
while (pos < len && bytes[pos]
|
|
14143
|
+
function readStr() {
|
|
14144
|
+
while (pos < len && bytes[pos] !== 60) {
|
|
14108
14145
|
pos++;
|
|
14109
14146
|
}
|
|
14110
|
-
const
|
|
14111
|
-
while (pos < len && bytes[pos] !==
|
|
14147
|
+
const startP = pos;
|
|
14148
|
+
while (pos < len && bytes[pos] !== 62) {
|
|
14112
14149
|
pos++;
|
|
14113
14150
|
}
|
|
14114
|
-
pos
|
|
14115
|
-
|
|
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
|
-
|
|
14150
|
-
|
|
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
|
-
|
|
14179
|
+
let ret = new Array(items.length);
|
|
14180
|
+
let j = 0;
|
|
14155
14181
|
for (let i = 0; i < items.length; i++) {
|
|
14156
|
-
|
|
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
|
-
|
|
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 === -
|
|
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(
|
|
15384
|
-
if (layer.
|
|
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
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
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],
|
|
15398
|
-
u8[vtx + 1] = lerp(u8[vtx + 1], rgba8[k + 1],
|
|
15399
|
-
u8[vtx + 2] = lerp(u8[vtx + 2], rgba8[k + 2],
|
|
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.
|
|
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
|
-
|
|
15417
|
-
|
|
15418
|
-
|
|
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],
|
|
15422
|
-
u8[vtx + 1] = lerp(u8[vtx + 1], rgba8[k + 1],
|
|
15423
|
-
u8[vtx + 2] = lerp(u8[vtx + 2], rgba8[k + 2],
|
|
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 (
|
|
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
|
-
|
|
15474
|
-
u8[vtx +
|
|
15475
|
-
u8[vtx +
|
|
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
|
-
|
|
15491
|
-
if (!opaque[j]) {
|
|
15535
|
+
if (v255s[j] < 1) {
|
|
15492
15536
|
continue;
|
|
15493
15537
|
}
|
|
15494
|
-
|
|
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],
|
|
15500
|
-
u8[vtx + 1] = lerp(u8[vtx + 1], lut[v255 + 1],
|
|
15501
|
-
u8[vtx + 2] = lerp(u8[vtx + 2], lut[v255 + 2],
|
|
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 (
|
|
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
|
-
|
|
15564
|
-
|
|
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],
|
|
15572
|
-
u8[vtx + 1] = lerp(u8[vtx + 1], lut2[v255 + 1],
|
|
15573
|
-
u8[vtx + 2] = lerp(u8[vtx + 2], lut2[v255 + 2],
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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].
|
|
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.
|
|
29076
|
+
this.opts.atlasOutline = isOutline;
|
|
29017
29077
|
this.updateGLVolume();
|
|
29018
29078
|
this.drawScene();
|
|
29019
29079
|
}
|