@niivue/niivue 0.44.2 → 0.45.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/README.md +9 -0
- package/build/index.d.ts +31 -6
- package/build/index.js +429 -86
- package/build/index.js.map +1 -1
- package/build/index.min.js +1 -1
- package/dist/index.d.ts +31 -6
- package/dist/index.js +429 -86
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/niivue.umd.js +30 -30
- package/package.json +1 -1
- package/src/cow.iwm.cbor +0 -0
- package/src/fslmean.iwi.cbor +0 -0
- package/src/index.html +47 -8
- package/src/niivue/index.ts +520 -96
- package/src/nvdocument.ts +11 -0
- package/src/nvimage/index.ts +4 -1
- package/src/nvlabel.ts +34 -1
- package/src/nvmesh-loaders.ts +24 -1
package/build/index.js
CHANGED
|
@@ -10,7 +10,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
10
10
|
import { mat4 as mat46, vec2, vec3 as vec311, vec4 as vec48 } from "gl-matrix";
|
|
11
11
|
|
|
12
12
|
// package.json
|
|
13
|
-
var version = "0.
|
|
13
|
+
var version = "0.45.0";
|
|
14
14
|
|
|
15
15
|
// src/logger.ts
|
|
16
16
|
var _Log = class _Log {
|
|
@@ -12393,6 +12393,12 @@ var NVMeshLoaders = class _NVMeshLoaders {
|
|
|
12393
12393
|
while (lines[pos].length < 1) {
|
|
12394
12394
|
pos++;
|
|
12395
12395
|
}
|
|
12396
|
+
if (lines[pos].startsWith("METADATA")) {
|
|
12397
|
+
while (lines[pos].length > 1) {
|
|
12398
|
+
pos++;
|
|
12399
|
+
}
|
|
12400
|
+
pos++;
|
|
12401
|
+
}
|
|
12396
12402
|
items = lines[pos].trim().split(/\s+/);
|
|
12397
12403
|
pos++;
|
|
12398
12404
|
if (items[0].includes("LINES")) {
|
|
@@ -12571,7 +12577,7 @@ var NVMeshLoaders = class _NVMeshLoaders {
|
|
|
12571
12577
|
layer.values = obj;
|
|
12572
12578
|
}
|
|
12573
12579
|
}
|
|
12574
|
-
} else if (ext === "CRV" || ext === "CURV") {
|
|
12580
|
+
} else if (ext === "CRV" || ext === "CURV" || ext === "THICKNESS" || ext === "AREA") {
|
|
12575
12581
|
layer.values = _NVMeshLoaders.readCURV(buffer, n_vert);
|
|
12576
12582
|
layer.isTransparentBelowCalMin = false;
|
|
12577
12583
|
} else if (ext === "GII") {
|
|
@@ -12596,6 +12602,9 @@ var NVMeshLoaders = class _NVMeshLoaders {
|
|
|
12596
12602
|
layer.values = _NVMeshLoaders.readSMP(buffer, n_vert);
|
|
12597
12603
|
} else if (ext === "STC") {
|
|
12598
12604
|
layer.values = _NVMeshLoaders.readSTC(buffer, n_vert);
|
|
12605
|
+
} else if (_NVMeshLoaders.isCurv(buffer)) {
|
|
12606
|
+
layer.values = _NVMeshLoaders.readCURV(buffer, n_vert);
|
|
12607
|
+
layer.isTransparentBelowCalMin = false;
|
|
12599
12608
|
} else {
|
|
12600
12609
|
log.warn("Unknown layer overlay format " + name);
|
|
12601
12610
|
return layer;
|
|
@@ -12769,6 +12778,17 @@ var NVMeshLoaders = class _NVMeshLoaders {
|
|
|
12769
12778
|
return f32;
|
|
12770
12779
|
}
|
|
12771
12780
|
// readSTC()
|
|
12781
|
+
static isCurv(buffer) {
|
|
12782
|
+
const view = new DataView(buffer);
|
|
12783
|
+
const sig0 = view.getUint8(0);
|
|
12784
|
+
const sig1 = view.getUint8(1);
|
|
12785
|
+
const sig2 = view.getUint8(2);
|
|
12786
|
+
if (sig0 !== 255 || sig1 !== 255 || sig2 !== 255) {
|
|
12787
|
+
utiltiesLogger.debug("Unable to recognize file type: does not appear to be FreeSurfer format.");
|
|
12788
|
+
return false;
|
|
12789
|
+
}
|
|
12790
|
+
return true;
|
|
12791
|
+
}
|
|
12772
12792
|
// read freesurfer curv big-endian format
|
|
12773
12793
|
// https://github.com/bonilhamusclab/MRIcroS/blob/master/%2BfileUtils/%2Bpial/readPial.m
|
|
12774
12794
|
// http://www.grahamwideman.com/gw/brain/fs/surfacefileformats.htm
|
|
@@ -20032,7 +20052,7 @@ var NVImage = class _NVImage {
|
|
|
20032
20052
|
hdr.datatypeCode = 64 /* DT_FLOAT64 */;
|
|
20033
20053
|
break;
|
|
20034
20054
|
default:
|
|
20035
|
-
throw new Error("Unsupported
|
|
20055
|
+
throw new Error("Unsupported MHA data type: " + items[0]);
|
|
20036
20056
|
}
|
|
20037
20057
|
}
|
|
20038
20058
|
if (line.startsWith("ObjectType") && !items[0].includes("Image")) {
|
|
@@ -20054,6 +20074,9 @@ var NVImage = class _NVImage {
|
|
|
20054
20074
|
[mat33[2], mat33[5], mat33[8], offset[2]],
|
|
20055
20075
|
[0, 0, 0, 1]
|
|
20056
20076
|
];
|
|
20077
|
+
while (bytes[pos] === 10) {
|
|
20078
|
+
pos++;
|
|
20079
|
+
}
|
|
20057
20080
|
hdr.vox_offset = pos;
|
|
20058
20081
|
if (isDetached && pairedImgData) {
|
|
20059
20082
|
if (isGz) {
|
|
@@ -22261,6 +22284,7 @@ var DEFAULT_OPTIONS = {
|
|
|
22261
22284
|
multiplanarPadPixels: 0,
|
|
22262
22285
|
// @deprecated
|
|
22263
22286
|
multiplanarForceRender: false,
|
|
22287
|
+
multiplanarEqualSize: false,
|
|
22264
22288
|
multiplanarShowRender: 2 /* AUTO */,
|
|
22265
22289
|
// auto is the same behaviour as multiplanarForceRender: false
|
|
22266
22290
|
isRadiologicalConvention: false,
|
|
@@ -22301,6 +22325,7 @@ var DEFAULT_OPTIONS = {
|
|
|
22301
22325
|
clickToSegmentBright: true
|
|
22302
22326
|
};
|
|
22303
22327
|
var INITIAL_SCENE_DATA = {
|
|
22328
|
+
gamma: 1,
|
|
22304
22329
|
azimuth: 110,
|
|
22305
22330
|
elevation: 10,
|
|
22306
22331
|
crosshairPos: vec38.create(),
|
|
@@ -22539,6 +22564,9 @@ var NVDocument = class _NVDocument {
|
|
|
22539
22564
|
data.opts.meshThicknessOn2D = "infinity";
|
|
22540
22565
|
}
|
|
22541
22566
|
data.labels = [...this.data.labels];
|
|
22567
|
+
for (const label of data.labels) {
|
|
22568
|
+
delete label.onClick;
|
|
22569
|
+
}
|
|
22542
22570
|
data.customData = this.customData;
|
|
22543
22571
|
if (this.volumes.length) {
|
|
22544
22572
|
let imageOptions = this.imageOptionsArray[0];
|
|
@@ -22794,6 +22822,19 @@ var LabelLineTerminator = /* @__PURE__ */ ((LabelLineTerminator2) => {
|
|
|
22794
22822
|
LabelLineTerminator2["RING"] = "ring";
|
|
22795
22823
|
return LabelLineTerminator2;
|
|
22796
22824
|
})(LabelLineTerminator || {});
|
|
22825
|
+
var LabelAnchorPoint = /* @__PURE__ */ ((LabelAnchorPoint2) => {
|
|
22826
|
+
LabelAnchorPoint2[LabelAnchorPoint2["NONE"] = 0 /* NONE */] = "NONE";
|
|
22827
|
+
LabelAnchorPoint2[LabelAnchorPoint2["TOPLEFT"] = 9] = "TOPLEFT";
|
|
22828
|
+
LabelAnchorPoint2[LabelAnchorPoint2["TOPCENTER"] = 10] = "TOPCENTER";
|
|
22829
|
+
LabelAnchorPoint2[LabelAnchorPoint2["TOPRIGHT"] = 12] = "TOPRIGHT";
|
|
22830
|
+
LabelAnchorPoint2[LabelAnchorPoint2["MIDDLELEFT"] = 17] = "MIDDLELEFT";
|
|
22831
|
+
LabelAnchorPoint2[LabelAnchorPoint2["MIDDLECENTER"] = 18] = "MIDDLECENTER";
|
|
22832
|
+
LabelAnchorPoint2[LabelAnchorPoint2["MIDDLERIGHT"] = 20] = "MIDDLERIGHT";
|
|
22833
|
+
LabelAnchorPoint2[LabelAnchorPoint2["BOTTOMLEFT"] = 33] = "BOTTOMLEFT";
|
|
22834
|
+
LabelAnchorPoint2[LabelAnchorPoint2["BOTTOMCENTER"] = 34] = "BOTTOMCENTER";
|
|
22835
|
+
LabelAnchorPoint2[LabelAnchorPoint2["BOTTOMRIGHT"] = 36] = "BOTTOMRIGHT";
|
|
22836
|
+
return LabelAnchorPoint2;
|
|
22837
|
+
})(LabelAnchorPoint || {});
|
|
22797
22838
|
var NVLabel3DStyle = class {
|
|
22798
22839
|
/**
|
|
22799
22840
|
* @param textColor - Color of text
|
|
@@ -22831,13 +22872,17 @@ var NVLabel3D = class {
|
|
|
22831
22872
|
* @param style - The style of the label
|
|
22832
22873
|
* @param points - An array of points label for label lines
|
|
22833
22874
|
*/
|
|
22834
|
-
constructor(text, style, points) {
|
|
22875
|
+
constructor(text, style, points, anchor, onClick) {
|
|
22835
22876
|
__publicField(this, "text");
|
|
22836
22877
|
__publicField(this, "style");
|
|
22837
22878
|
__publicField(this, "points");
|
|
22879
|
+
__publicField(this, "anchor");
|
|
22880
|
+
__publicField(this, "onClick");
|
|
22838
22881
|
this.text = text;
|
|
22839
22882
|
this.style = style;
|
|
22840
22883
|
this.points = points;
|
|
22884
|
+
this.anchor = anchor || 0 /* NONE */;
|
|
22885
|
+
this.onClick = onClick;
|
|
22841
22886
|
}
|
|
22842
22887
|
};
|
|
22843
22888
|
|
|
@@ -23979,6 +24024,7 @@ var Niivue = class _Niivue {
|
|
|
23979
24024
|
* @param options - options object to set modifiable Niivue properties
|
|
23980
24025
|
*/
|
|
23981
24026
|
constructor(options = DEFAULT_OPTIONS) {
|
|
24027
|
+
__publicField(this, "loaders", {});
|
|
23982
24028
|
__publicField(this, "canvas", null);
|
|
23983
24029
|
// the reference to the canvas element on the page
|
|
23984
24030
|
__publicField(this, "_gl", null);
|
|
@@ -24588,6 +24634,11 @@ var Niivue = class _Niivue {
|
|
|
24588
24634
|
}
|
|
24589
24635
|
const thisMM = this.frac2mm(this.scene.crosshairPos);
|
|
24590
24636
|
if (this.otherNV instanceof _Niivue) {
|
|
24637
|
+
const thisGamma = this.scene.gamma;
|
|
24638
|
+
const otherGamma = this.otherNV.scene.gamma;
|
|
24639
|
+
if (thisGamma !== otherGamma) {
|
|
24640
|
+
this.otherNV.setGamma(thisGamma);
|
|
24641
|
+
}
|
|
24591
24642
|
if (this.syncOpts["2d"]) {
|
|
24592
24643
|
this.otherNV.scene.crosshairPos = this.otherNV.mm2frac(thisMM);
|
|
24593
24644
|
this.otherNV.scene.pan2Dxyzmm = vec48.clone(this.scene.pan2Dxyzmm);
|
|
@@ -24604,6 +24655,11 @@ var Niivue = class _Niivue {
|
|
|
24604
24655
|
if (this.otherNV[i] === this) {
|
|
24605
24656
|
continue;
|
|
24606
24657
|
}
|
|
24658
|
+
const thisGamma = this.scene.gamma;
|
|
24659
|
+
const otherGamma = this.otherNV[i].scene.gamma;
|
|
24660
|
+
if (thisGamma !== otherGamma) {
|
|
24661
|
+
this.otherNV[i].setGamma(thisGamma);
|
|
24662
|
+
}
|
|
24607
24663
|
if (this.syncOpts["2d"]) {
|
|
24608
24664
|
this.otherNV[i].scene.crosshairPos = this.otherNV[i].mm2frac(thisMM);
|
|
24609
24665
|
this.otherNV[i].scene.pan2Dxyzmm = vec48.clone(this.scene.pan2Dxyzmm);
|
|
@@ -24719,6 +24775,10 @@ var Niivue = class _Niivue {
|
|
|
24719
24775
|
const [x, y] = [pos.x * this.uiData.dpr, pos.y * this.uiData.dpr];
|
|
24720
24776
|
const label = this.getLabelAtPoint([x, y]);
|
|
24721
24777
|
if (label) {
|
|
24778
|
+
if (label.onClick) {
|
|
24779
|
+
label.onClick(label);
|
|
24780
|
+
return;
|
|
24781
|
+
}
|
|
24722
24782
|
for (const mesh of this.meshes) {
|
|
24723
24783
|
if (mesh.type !== "connectome" /* CONNECTOME */) {
|
|
24724
24784
|
continue;
|
|
@@ -25033,6 +25093,15 @@ var Niivue = class _Niivue {
|
|
|
25033
25093
|
this.drawScene();
|
|
25034
25094
|
this.uiData.prevX = this.uiData.currX;
|
|
25035
25095
|
this.uiData.prevY = this.uiData.currY;
|
|
25096
|
+
} else if (!this.uiData.mousedown && this.opts.clickToSegment) {
|
|
25097
|
+
const pos = this.getNoPaddingNoBorderCanvasRelativeMousePosition(e, this.gl.canvas);
|
|
25098
|
+
if (!pos) {
|
|
25099
|
+
return;
|
|
25100
|
+
}
|
|
25101
|
+
const x = pos.x * this.uiData.dpr;
|
|
25102
|
+
const y = pos.y * this.uiData.dpr;
|
|
25103
|
+
this.mousePos = [x, y];
|
|
25104
|
+
this.drawScene();
|
|
25036
25105
|
}
|
|
25037
25106
|
}
|
|
25038
25107
|
// not included in public docs
|
|
@@ -25270,6 +25339,11 @@ var Niivue = class _Niivue {
|
|
|
25270
25339
|
if (ext === "GZ") {
|
|
25271
25340
|
ext = re.exec(fullname.slice(0, -3))[1];
|
|
25272
25341
|
ext = ext.toUpperCase();
|
|
25342
|
+
} else if (ext === "CBOR") {
|
|
25343
|
+
const endExt = ext;
|
|
25344
|
+
ext = re.exec(fullname.slice(0, -5))[1];
|
|
25345
|
+
ext = ext.toUpperCase();
|
|
25346
|
+
ext = `${ext}.${endExt}`;
|
|
25273
25347
|
}
|
|
25274
25348
|
return upperCase ? ext : ext.toLowerCase();
|
|
25275
25349
|
}
|
|
@@ -25290,6 +25364,28 @@ var Niivue = class _Niivue {
|
|
|
25290
25364
|
}
|
|
25291
25365
|
async addVolumesFromUrl(imageOptionsArray) {
|
|
25292
25366
|
const promises = imageOptionsArray.map(async (imageItem) => {
|
|
25367
|
+
const ext = this.getFileExt(imageItem.url);
|
|
25368
|
+
if (this.loaders[ext]) {
|
|
25369
|
+
let itemToLoad = imageItem.url;
|
|
25370
|
+
const toExt = this.loaders[ext].toExt;
|
|
25371
|
+
let name = imageItem.name || imageItem.url;
|
|
25372
|
+
name = name.split("/").pop();
|
|
25373
|
+
if (typeof imageItem.url === "string") {
|
|
25374
|
+
const url = imageItem.url;
|
|
25375
|
+
try {
|
|
25376
|
+
const response = await fetch(url);
|
|
25377
|
+
if (!response.ok) {
|
|
25378
|
+
throw new Error(`Failed to load file: ${response.statusText}`);
|
|
25379
|
+
}
|
|
25380
|
+
itemToLoad = await response.arrayBuffer();
|
|
25381
|
+
} catch (error) {
|
|
25382
|
+
throw new Error(`Failed to load url ${url}: ${error}`);
|
|
25383
|
+
}
|
|
25384
|
+
}
|
|
25385
|
+
const buffer = await this.loaders[ext].loader(itemToLoad);
|
|
25386
|
+
imageItem.url = buffer;
|
|
25387
|
+
imageItem.name = `${name}.${toExt}`;
|
|
25388
|
+
}
|
|
25293
25389
|
const imageOptions = {
|
|
25294
25390
|
url: imageItem.url,
|
|
25295
25391
|
headers: imageItem.headers,
|
|
@@ -25429,6 +25525,40 @@ var Niivue = class _Niivue {
|
|
|
25429
25525
|
this.addVolume(volume);
|
|
25430
25526
|
});
|
|
25431
25527
|
}
|
|
25528
|
+
/* useLoader
|
|
25529
|
+
|
|
25530
|
+
registers an external file loader for niivue to use when reading files.
|
|
25531
|
+
|
|
25532
|
+
the loader must return an array buffer of the file contents for niivue
|
|
25533
|
+
to parse and the extension of the file so niivue can infer the file type to load.
|
|
25534
|
+
|
|
25535
|
+
example:
|
|
25536
|
+
|
|
25537
|
+
const myCustomLoader = (File) => {
|
|
25538
|
+
// ... do parsing here ...
|
|
25539
|
+
return {
|
|
25540
|
+
arrayBuffer: ArrayBuffer,
|
|
25541
|
+
fileExt: 'iwi.cbor',
|
|
25542
|
+
positions: Float32Array | [], // allows for mesh loading to mz3
|
|
25543
|
+
indices: Uint32Array | [], // allows for mesh loading to mz3
|
|
25544
|
+
}
|
|
25545
|
+
|
|
25546
|
+
nv.useLoader({
|
|
25547
|
+
loader: myCustomLoader,
|
|
25548
|
+
fileExt: 'iwi.cbor',
|
|
25549
|
+
toExt: 'nii'
|
|
25550
|
+
})
|
|
25551
|
+
|
|
25552
|
+
*/
|
|
25553
|
+
useLoader(loader, fileExt, toExt) {
|
|
25554
|
+
this.loaders = {
|
|
25555
|
+
...this.loaders,
|
|
25556
|
+
[fileExt.toUpperCase()]: {
|
|
25557
|
+
loader,
|
|
25558
|
+
toExt
|
|
25559
|
+
}
|
|
25560
|
+
};
|
|
25561
|
+
}
|
|
25432
25562
|
// not included in public docs
|
|
25433
25563
|
dropListener(e) {
|
|
25434
25564
|
e.stopPropagation();
|
|
@@ -27100,6 +27230,37 @@ var Niivue = class _Niivue {
|
|
|
27100
27230
|
this.document.meshes = this.meshes;
|
|
27101
27231
|
return this.document.download(fileName, compress2);
|
|
27102
27232
|
}
|
|
27233
|
+
// generic loadImages that wraps loadVolumes and loadMeshes
|
|
27234
|
+
async loadImages(images) {
|
|
27235
|
+
const volumes = [];
|
|
27236
|
+
const meshes = [];
|
|
27237
|
+
for (const image of images) {
|
|
27238
|
+
if ("url" in image) {
|
|
27239
|
+
const ext = this.getFileExt(image.url);
|
|
27240
|
+
if (this.loaders[ext]) {
|
|
27241
|
+
const toExt = this.loaders[ext].toExt.toUpperCase();
|
|
27242
|
+
if (MESH_EXTENSIONS.includes(toExt)) {
|
|
27243
|
+
meshes.push(image);
|
|
27244
|
+
} else {
|
|
27245
|
+
volumes.push(image);
|
|
27246
|
+
}
|
|
27247
|
+
continue;
|
|
27248
|
+
}
|
|
27249
|
+
if (MESH_EXTENSIONS.includes(ext.toUpperCase())) {
|
|
27250
|
+
meshes.push(image);
|
|
27251
|
+
} else {
|
|
27252
|
+
volumes.push(image);
|
|
27253
|
+
}
|
|
27254
|
+
}
|
|
27255
|
+
}
|
|
27256
|
+
if (volumes.length > 0) {
|
|
27257
|
+
await this.loadVolumes(volumes);
|
|
27258
|
+
}
|
|
27259
|
+
if (meshes.length > 0) {
|
|
27260
|
+
await this.loadMeshes(meshes);
|
|
27261
|
+
}
|
|
27262
|
+
return this;
|
|
27263
|
+
}
|
|
27103
27264
|
/**
|
|
27104
27265
|
* load an array of volume objects
|
|
27105
27266
|
* @param volumeList - the array of objects to load. each object must have a resolvable "url" property at a minimum
|
|
@@ -27119,27 +27280,7 @@ var Niivue = class _Niivue {
|
|
|
27119
27280
|
this.volumes = [];
|
|
27120
27281
|
this.gl.clearColor(0, 0, 0, 1);
|
|
27121
27282
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
27122
|
-
|
|
27123
|
-
await this.addVolumesFromUrl(volumeList);
|
|
27124
|
-
return this;
|
|
27125
|
-
}
|
|
27126
|
-
const imageOptions = {
|
|
27127
|
-
url: volumeList[0].url,
|
|
27128
|
-
headers: volumeList[0].headers,
|
|
27129
|
-
name: volumeList[0].name,
|
|
27130
|
-
colormap: volumeList[0].colormap ? volumeList[0].colormap : volumeList[0].colorMap,
|
|
27131
|
-
colormapNegative: volumeList[0].colormapNegative ? volumeList[0].colormapNegative : volumeList[0].colorMapNegative,
|
|
27132
|
-
opacity: volumeList[0].opacity,
|
|
27133
|
-
urlImgData: volumeList[0].urlImgData,
|
|
27134
|
-
cal_min: volumeList[0].cal_min,
|
|
27135
|
-
cal_max: volumeList[0].cal_max,
|
|
27136
|
-
trustCalMinMax: this.opts.trustCalMinMax,
|
|
27137
|
-
isManifest: volumeList[0].isManifest,
|
|
27138
|
-
frame4D: volumeList[0].frame4D,
|
|
27139
|
-
limitFrames4D: volumeList[0].limitFrames4D || this.opts.limitFrames4D,
|
|
27140
|
-
colorbarVisible: volumeList[0].colorbarVisible
|
|
27141
|
-
};
|
|
27142
|
-
await this.addVolumeFromUrl(imageOptions);
|
|
27283
|
+
await this.addVolumesFromUrl(volumeList);
|
|
27143
27284
|
return this;
|
|
27144
27285
|
}
|
|
27145
27286
|
/**
|
|
@@ -27170,6 +27311,28 @@ var Niivue = class _Niivue {
|
|
|
27170
27311
|
async addMeshesFromUrl(meshOptions) {
|
|
27171
27312
|
const promises = meshOptions.map(async (meshItem) => {
|
|
27172
27313
|
const ext = this.getFileExt(meshItem.url);
|
|
27314
|
+
if (this.loaders[ext]) {
|
|
27315
|
+
let itemToLoad = meshItem.url;
|
|
27316
|
+
const toExt = this.loaders[ext].toExt;
|
|
27317
|
+
let name = meshItem.name || meshItem.url;
|
|
27318
|
+
name = name.split("/").pop();
|
|
27319
|
+
if (typeof meshItem.url === "string") {
|
|
27320
|
+
const url = meshItem.url;
|
|
27321
|
+
try {
|
|
27322
|
+
const response = await fetch(url);
|
|
27323
|
+
if (!response.ok) {
|
|
27324
|
+
throw new Error(`Failed to load file: ${response.statusText}`);
|
|
27325
|
+
}
|
|
27326
|
+
itemToLoad = await response.arrayBuffer();
|
|
27327
|
+
} catch (error) {
|
|
27328
|
+
throw new Error(`Failed to load url ${url}: ${error}`);
|
|
27329
|
+
}
|
|
27330
|
+
}
|
|
27331
|
+
const { positions, indices } = await this.loaders[ext].loader(itemToLoad);
|
|
27332
|
+
meshItem.name = `${name}.${toExt}`;
|
|
27333
|
+
const mz3 = NVMeshUtilities.createMZ3(positions, indices, false);
|
|
27334
|
+
meshItem.buffer = mz3;
|
|
27335
|
+
}
|
|
27173
27336
|
if (ext === "JCON" || ext === "JSON") {
|
|
27174
27337
|
const response = await fetch(meshItem.url, {});
|
|
27175
27338
|
const json = await response.json();
|
|
@@ -27210,13 +27373,7 @@ var Niivue = class _Niivue {
|
|
|
27210
27373
|
this.meshes = [];
|
|
27211
27374
|
this.gl.clearColor(0, 0, 0, 1);
|
|
27212
27375
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
27213
|
-
|
|
27214
|
-
await this.addMeshesFromUrl(meshList);
|
|
27215
|
-
this.updateGLVolume();
|
|
27216
|
-
this.drawScene();
|
|
27217
|
-
return this;
|
|
27218
|
-
}
|
|
27219
|
-
await this.addMeshFromUrl(meshList[0]);
|
|
27376
|
+
await this.addMeshesFromUrl(meshList);
|
|
27220
27377
|
this.updateGLVolume();
|
|
27221
27378
|
this.drawScene();
|
|
27222
27379
|
return this;
|
|
@@ -29781,6 +29938,7 @@ var Niivue = class _Niivue {
|
|
|
29781
29938
|
* @see {@link https://niivue.github.io/niivue/features/colormaps.html | live demo usage}
|
|
29782
29939
|
*/
|
|
29783
29940
|
setGamma(gamma = 1) {
|
|
29941
|
+
this.scene.gamma = gamma;
|
|
29784
29942
|
cmapper.gamma = gamma;
|
|
29785
29943
|
this.updateGLVolume();
|
|
29786
29944
|
}
|
|
@@ -30144,6 +30302,7 @@ var Niivue = class _Niivue {
|
|
|
30144
30302
|
}
|
|
30145
30303
|
if (this.opts.drawingEnabled) {
|
|
30146
30304
|
const pt = this.frac2vox(this.scene.crosshairPos);
|
|
30305
|
+
const ptMM = this.frac2mm(this.scene.crosshairPos);
|
|
30147
30306
|
if (this.opts.clickToSegment) {
|
|
30148
30307
|
const radius = this.opts.clickToSegmentRadius;
|
|
30149
30308
|
const steps = this.opts.clickToSegmentSteps;
|
|
@@ -30152,11 +30311,12 @@ var Niivue = class _Niivue {
|
|
|
30152
30311
|
this.drawPenAxCorSag = axCorSag;
|
|
30153
30312
|
for (let i2 = 1; i2 <= steps; i2++) {
|
|
30154
30313
|
const angle = i2 / steps * 2 * Math.PI;
|
|
30155
|
-
|
|
30156
|
-
|
|
30157
|
-
const
|
|
30158
|
-
xVox =
|
|
30159
|
-
yVox =
|
|
30314
|
+
const xMM = ptMM[0] + radius * Math.cos(angle);
|
|
30315
|
+
const yMM = ptMM[1] + radius * Math.sin(angle);
|
|
30316
|
+
const zMM = ptMM[2];
|
|
30317
|
+
const xVox = this.back.mm2vox([xMM, yMM, zMM])[0];
|
|
30318
|
+
const yVox = this.back.mm2vox([xMM, yMM, zMM])[1];
|
|
30319
|
+
const zVox = this.back.mm2vox([xMM, yMM, zMM])[2];
|
|
30160
30320
|
this.drawPt(xVox, yVox, zVox, this.opts.penValue);
|
|
30161
30321
|
this.drawPenFillPts.push([xVox, yVox, pt[2]]);
|
|
30162
30322
|
if (i2 === steps) {
|
|
@@ -30440,9 +30600,21 @@ var Niivue = class _Niivue {
|
|
|
30440
30600
|
const labels = [...this.document.labels, ...definedMeshLabels];
|
|
30441
30601
|
return labels;
|
|
30442
30602
|
}
|
|
30603
|
+
getConnectomeLabels() {
|
|
30604
|
+
const connectomes = this.meshes.filter((m) => m.type === "connectome" /* CONNECTOME */);
|
|
30605
|
+
const meshNodes = connectomes.flatMap((m) => m.nodes);
|
|
30606
|
+
const meshLabels = meshNodes.map((n) => n.label);
|
|
30607
|
+
const definedMeshLabels = meshLabels.filter((l) => l !== void 0);
|
|
30608
|
+
const nonAnchoredLabels = this.document.labels.filter((l) => l.anchor == null || l.anchor === 0 /* NONE */);
|
|
30609
|
+
const nonAnchoredLabelSet = new Set(definedMeshLabels);
|
|
30610
|
+
for (const label of nonAnchoredLabels) {
|
|
30611
|
+
nonAnchoredLabelSet.add(label);
|
|
30612
|
+
}
|
|
30613
|
+
return Array.from(nonAnchoredLabelSet);
|
|
30614
|
+
}
|
|
30443
30615
|
getBulletMarginWidth() {
|
|
30444
30616
|
let bulletMargin = 0;
|
|
30445
|
-
const labels = this.
|
|
30617
|
+
const labels = this.getConnectomeLabels();
|
|
30446
30618
|
if (labels.length === 0) {
|
|
30447
30619
|
return 0;
|
|
30448
30620
|
}
|
|
@@ -30459,7 +30631,7 @@ var Niivue = class _Niivue {
|
|
|
30459
30631
|
return bulletMargin;
|
|
30460
30632
|
}
|
|
30461
30633
|
getLegendPanelWidth() {
|
|
30462
|
-
const labels = this.
|
|
30634
|
+
const labels = this.getConnectomeLabels();
|
|
30463
30635
|
if (!this.opts.showLegend || labels.length === 0) {
|
|
30464
30636
|
return 0;
|
|
30465
30637
|
}
|
|
@@ -30485,7 +30657,7 @@ var Niivue = class _Niivue {
|
|
|
30485
30657
|
return width;
|
|
30486
30658
|
}
|
|
30487
30659
|
getLegendPanelHeight() {
|
|
30488
|
-
const labels = this.
|
|
30660
|
+
const labels = this.getConnectomeLabels();
|
|
30489
30661
|
let height = 0;
|
|
30490
30662
|
const scale = 1;
|
|
30491
30663
|
const verticalMargin = this.opts.textHeight * this.gl.canvas.height * scale;
|
|
@@ -30927,7 +31099,7 @@ var Niivue = class _Niivue {
|
|
|
30927
31099
|
}
|
|
30928
31100
|
// not included in public docs
|
|
30929
31101
|
// show text labels for L/R, A/P, I/S dimensions
|
|
30930
|
-
drawSliceOrientationText(leftTopWidthHeight, axCorSag) {
|
|
31102
|
+
drawSliceOrientationText(leftTopWidthHeight, axCorSag, padLeftTop = [NaN, NaN]) {
|
|
30931
31103
|
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
30932
31104
|
let topText = "S";
|
|
30933
31105
|
if (axCorSag === 0 /* AXIAL */) {
|
|
@@ -30941,8 +31113,32 @@ var Niivue = class _Niivue {
|
|
|
30941
31113
|
this.drawTextRightBelow([leftTopWidthHeight[0], leftTopWidthHeight[1]], leftText + topText);
|
|
30942
31114
|
return;
|
|
30943
31115
|
}
|
|
30944
|
-
|
|
30945
|
-
|
|
31116
|
+
let drawBelow = true;
|
|
31117
|
+
let drawRight = true;
|
|
31118
|
+
if (!isNaN(padLeftTop[0])) {
|
|
31119
|
+
const ht = this.opts.textHeight * this.gl.canvas.height + 2;
|
|
31120
|
+
if (padLeftTop[1] > ht) {
|
|
31121
|
+
this.drawTextBelow(
|
|
31122
|
+
[leftTopWidthHeight[0] + leftTopWidthHeight[2] * 0.5, leftTopWidthHeight[1] + padLeftTop[1] - ht],
|
|
31123
|
+
topText
|
|
31124
|
+
);
|
|
31125
|
+
drawBelow = false;
|
|
31126
|
+
}
|
|
31127
|
+
const wid = this.textWidth(ht, leftText) + 2;
|
|
31128
|
+
if (padLeftTop[0] > wid) {
|
|
31129
|
+
this.drawTextRight(
|
|
31130
|
+
[leftTopWidthHeight[0] + padLeftTop[0] - wid, leftTopWidthHeight[1] + leftTopWidthHeight[3] * 0.5],
|
|
31131
|
+
leftText
|
|
31132
|
+
);
|
|
31133
|
+
drawRight = false;
|
|
31134
|
+
}
|
|
31135
|
+
}
|
|
31136
|
+
if (drawBelow) {
|
|
31137
|
+
this.drawTextBelow([leftTopWidthHeight[0] + leftTopWidthHeight[2] * 0.5, leftTopWidthHeight[1]], topText);
|
|
31138
|
+
}
|
|
31139
|
+
if (drawRight) {
|
|
31140
|
+
this.drawTextRight([leftTopWidthHeight[0], leftTopWidthHeight[1] + leftTopWidthHeight[3] * 0.5], leftText);
|
|
31141
|
+
}
|
|
30946
31142
|
}
|
|
30947
31143
|
// not included in public docs
|
|
30948
31144
|
xyMM2xyzMM(axCorSag, sliceFrac) {
|
|
@@ -30980,7 +31176,7 @@ var Niivue = class _Niivue {
|
|
|
30980
31176
|
}
|
|
30981
31177
|
// not included in public docs
|
|
30982
31178
|
// draw 2D tile
|
|
30983
|
-
|
|
31179
|
+
draw2DMain(leftTopWidthHeight, axCorSag, customMM = NaN) {
|
|
30984
31180
|
let frac2mmTexture = this.volumes[0].frac2mm.slice();
|
|
30985
31181
|
let screen = this.screenFieldOfViewExtendedMM(axCorSag);
|
|
30986
31182
|
let mesh2ortho = mat46.create();
|
|
@@ -31009,7 +31205,9 @@ var Niivue = class _Niivue {
|
|
|
31009
31205
|
elevation = isRadiolgical ? -90 : 90;
|
|
31010
31206
|
}
|
|
31011
31207
|
const gl = this.gl;
|
|
31208
|
+
let isStretchToScreen = false;
|
|
31012
31209
|
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0) {
|
|
31210
|
+
isStretchToScreen = true;
|
|
31013
31211
|
const pixPerMMw = gl.canvas.width / screen.fovMM[0];
|
|
31014
31212
|
const pixPerMMh = gl.canvas.height / screen.fovMM[1];
|
|
31015
31213
|
const pixPerMMmin = Math.min(pixPerMMw, pixPerMMh);
|
|
@@ -31156,9 +31354,32 @@ var Niivue = class _Niivue {
|
|
|
31156
31354
|
if (isNaN(customMM)) {
|
|
31157
31355
|
this.drawCrosshairs3D(false, 0.15, obj.modelViewProjectionMatrix, true, this.opts.isSliceMM);
|
|
31158
31356
|
}
|
|
31159
|
-
|
|
31357
|
+
if (isStretchToScreen) {
|
|
31358
|
+
this.drawSliceOrientationText(leftTopWidthHeight, axCorSag);
|
|
31359
|
+
}
|
|
31160
31360
|
this.readyForSync = true;
|
|
31161
31361
|
}
|
|
31362
|
+
draw2D(leftTopWidthHeight, axCorSag, customMM = NaN, imageWidthHeight = [NaN, NaN]) {
|
|
31363
|
+
const padLeftTop = [NaN, NaN];
|
|
31364
|
+
if (isNaN(imageWidthHeight[0])) {
|
|
31365
|
+
this.draw2DMain(leftTopWidthHeight, axCorSag, customMM);
|
|
31366
|
+
} else {
|
|
31367
|
+
const ltwh = leftTopWidthHeight.slice();
|
|
31368
|
+
padLeftTop[0] = Math.floor(0.5 * (ltwh[2] - imageWidthHeight[0]));
|
|
31369
|
+
padLeftTop[1] = Math.floor(0.5 * (ltwh[3] - imageWidthHeight[1]));
|
|
31370
|
+
ltwh[0] += padLeftTop[0];
|
|
31371
|
+
ltwh[1] += padLeftTop[1];
|
|
31372
|
+
ltwh[2] = imageWidthHeight[0];
|
|
31373
|
+
ltwh[3] = imageWidthHeight[1];
|
|
31374
|
+
this.draw2DMain(ltwh, axCorSag, customMM);
|
|
31375
|
+
}
|
|
31376
|
+
if (customMM === Infinity || customMM === -Infinity || axCorSag === 4 /* RENDER */) {
|
|
31377
|
+
return;
|
|
31378
|
+
}
|
|
31379
|
+
if (leftTopWidthHeight[2] !== 0 && leftTopWidthHeight[3] !== 0) {
|
|
31380
|
+
this.drawSliceOrientationText(leftTopWidthHeight, axCorSag, padLeftTop);
|
|
31381
|
+
}
|
|
31382
|
+
}
|
|
31162
31383
|
// not included in public docs
|
|
31163
31384
|
// determine 3D model view projection matrix
|
|
31164
31385
|
calculateMvpMatrix(_unused, leftTopWidthHeight = [0, 0, 0, 0], azimuth, elevation) {
|
|
@@ -31769,7 +31990,7 @@ var Niivue = class _Niivue {
|
|
|
31769
31990
|
* @param style - label style
|
|
31770
31991
|
* @param point - 3D point on the model
|
|
31771
31992
|
*/
|
|
31772
|
-
addLabel(text, style, points) {
|
|
31993
|
+
addLabel(text, style, points, anchor, onClick) {
|
|
31773
31994
|
const defaultStyle = {
|
|
31774
31995
|
textColor: this.opts.legendTextColor,
|
|
31775
31996
|
textScale: 1,
|
|
@@ -31781,7 +32002,7 @@ var Niivue = class _Niivue {
|
|
|
31781
32002
|
bulletColor: this.opts.legendTextColor
|
|
31782
32003
|
};
|
|
31783
32004
|
const labelStyle = style ? { ...defaultStyle, ...style } : { ...defaultStyle };
|
|
31784
|
-
const label = new NVLabel3D(text, labelStyle, points);
|
|
32005
|
+
const label = new NVLabel3D(text, { ...labelStyle }, points, anchor, onClick);
|
|
31785
32006
|
this.document.labels.push(label);
|
|
31786
32007
|
return label;
|
|
31787
32008
|
}
|
|
@@ -31799,6 +32020,60 @@ var Niivue = class _Niivue {
|
|
|
31799
32020
|
return screenPoint;
|
|
31800
32021
|
}
|
|
31801
32022
|
getLabelAtPoint(screenPoint) {
|
|
32023
|
+
const scale = 1;
|
|
32024
|
+
const size = this.opts.textHeight * Math.min(this.gl.canvas.height, this.gl.canvas.width) * scale;
|
|
32025
|
+
const verticalMargin = this.opts.textHeight * this.gl.canvas.height * scale;
|
|
32026
|
+
for (const label of this.document.labels) {
|
|
32027
|
+
if (label.anchor == null || label.anchor === 0 /* NONE */) {
|
|
32028
|
+
continue;
|
|
32029
|
+
}
|
|
32030
|
+
const labelSize = this.opts.textHeight * this.gl.canvas.height * label.style.textScale;
|
|
32031
|
+
const textHeight = this.textHeight(labelSize, label.text);
|
|
32032
|
+
const textWidth = this.textWidth(labelSize, label.text);
|
|
32033
|
+
if (label.anchor & 1 /* LEFT */) {
|
|
32034
|
+
if (screenPoint[0] > textWidth) {
|
|
32035
|
+
continue;
|
|
32036
|
+
}
|
|
32037
|
+
}
|
|
32038
|
+
if (label.anchor & 2 /* CENTER */) {
|
|
32039
|
+
if (screenPoint[0] < (this.gl.canvas.width - textWidth) / 2) {
|
|
32040
|
+
continue;
|
|
32041
|
+
}
|
|
32042
|
+
if (screenPoint[0] > (this.gl.canvas.width + textWidth) / 2) {
|
|
32043
|
+
continue;
|
|
32044
|
+
}
|
|
32045
|
+
}
|
|
32046
|
+
if (label.anchor & 4 /* RIGHT */) {
|
|
32047
|
+
if (screenPoint[0] < this.gl.canvas.width - textWidth) {
|
|
32048
|
+
continue;
|
|
32049
|
+
}
|
|
32050
|
+
}
|
|
32051
|
+
if (label.anchor & 8 /* TOP */) {
|
|
32052
|
+
if (screenPoint[1] < verticalMargin / 2) {
|
|
32053
|
+
continue;
|
|
32054
|
+
}
|
|
32055
|
+
if (screenPoint[1] > textHeight + verticalMargin / 2) {
|
|
32056
|
+
continue;
|
|
32057
|
+
}
|
|
32058
|
+
}
|
|
32059
|
+
if (label.anchor & 16 /* MIDDLE */) {
|
|
32060
|
+
if (screenPoint[1] < (this.gl.canvas.height - textHeight - verticalMargin) / 2) {
|
|
32061
|
+
continue;
|
|
32062
|
+
}
|
|
32063
|
+
if (screenPoint[1] > (this.gl.canvas.height + textHeight - verticalMargin / 2) / 2) {
|
|
32064
|
+
continue;
|
|
32065
|
+
}
|
|
32066
|
+
}
|
|
32067
|
+
if (label.anchor & 32 /* BOTTOM */) {
|
|
32068
|
+
if (screenPoint[1] < this.gl.canvas.height - textHeight - verticalMargin) {
|
|
32069
|
+
continue;
|
|
32070
|
+
}
|
|
32071
|
+
if (screenPoint[1] > this.gl.canvas.height - verticalMargin / 2) {
|
|
32072
|
+
continue;
|
|
32073
|
+
}
|
|
32074
|
+
}
|
|
32075
|
+
return label;
|
|
32076
|
+
}
|
|
31802
32077
|
log.debug("screenPoint", screenPoint);
|
|
31803
32078
|
const panelHeight = this.getLegendPanelHeight();
|
|
31804
32079
|
const panelWidth = this.getLegendPanelWidth();
|
|
@@ -31808,9 +32083,7 @@ var Niivue = class _Niivue {
|
|
|
31808
32083
|
if (screenPoint[0] < left || screenPoint[1] < top || screenPoint[0] > left + panelWidth || screenPoint[1] > top + panelHeight) {
|
|
31809
32084
|
return null;
|
|
31810
32085
|
}
|
|
31811
|
-
const
|
|
31812
|
-
const size = this.opts.textHeight * Math.min(this.gl.canvas.height, this.gl.canvas.width) * scale;
|
|
31813
|
-
const labels = this.getAllLabels();
|
|
32086
|
+
const labels = this.getConnectomeLabels();
|
|
31814
32087
|
for (const label of labels) {
|
|
31815
32088
|
const labelSize = this.opts.textHeight * this.gl.canvas.height * label.style.textScale;
|
|
31816
32089
|
const textHeight = this.textHeight(labelSize, label.text);
|
|
@@ -31839,7 +32112,7 @@ var Niivue = class _Niivue {
|
|
|
31839
32112
|
}
|
|
31840
32113
|
}
|
|
31841
32114
|
// not included in public docs
|
|
31842
|
-
draw3DLabel(label, pos, mvpMatrix, leftTopWidthHeight, bulletMargin
|
|
32115
|
+
draw3DLabel(label, pos, mvpMatrix, leftTopWidthHeight, bulletMargin, legendWidth, secondPass) {
|
|
31843
32116
|
const text = label.text;
|
|
31844
32117
|
const left = pos[0];
|
|
31845
32118
|
const top = pos[1];
|
|
@@ -31856,22 +32129,24 @@ var Niivue = class _Niivue {
|
|
|
31856
32129
|
this.drawCircle([rectLeft, rectTop, bulletSize, bulletSize], label.style.bulletColor);
|
|
31857
32130
|
}
|
|
31858
32131
|
let textLeft = left;
|
|
31859
|
-
if (
|
|
31860
|
-
|
|
31861
|
-
|
|
31862
|
-
|
|
32132
|
+
if (legendWidth) {
|
|
32133
|
+
if (label.style.textAlignment !== "left" /* LEFT */) {
|
|
32134
|
+
const textWidth = this.textWidth(label.style.textScale, label.text) * size;
|
|
32135
|
+
if (label.style.textAlignment === "right" /* RIGHT */) {
|
|
32136
|
+
textLeft = left + legendWidth - size * 1.5 - textWidth;
|
|
32137
|
+
} else {
|
|
32138
|
+
const remaining = legendWidth - (bulletMargin || size);
|
|
32139
|
+
textLeft += (remaining - textWidth) / 2;
|
|
32140
|
+
}
|
|
31863
32141
|
} else {
|
|
31864
|
-
|
|
31865
|
-
textLeft += (remaining - textWidth) / 2;
|
|
32142
|
+
textLeft += bulletMargin;
|
|
31866
32143
|
}
|
|
31867
|
-
} else {
|
|
31868
|
-
textLeft += bulletMargin;
|
|
31869
32144
|
}
|
|
31870
32145
|
this.drawText([textLeft, top], text, label.style.textScale, label.style.textColor);
|
|
31871
32146
|
}
|
|
31872
32147
|
// not included in public docs
|
|
31873
32148
|
draw3DLabels(mvpMatrix, leftTopWidthHeight, secondPass = false) {
|
|
31874
|
-
const labels = this.
|
|
32149
|
+
const labels = this.getConnectomeLabels();
|
|
31875
32150
|
if (!this.opts.showLegend || labels.length === 0) {
|
|
31876
32151
|
return;
|
|
31877
32152
|
}
|
|
@@ -31909,6 +32184,51 @@ var Niivue = class _Niivue {
|
|
|
31909
32184
|
}
|
|
31910
32185
|
}
|
|
31911
32186
|
}
|
|
32187
|
+
drawAnchoredLabels() {
|
|
32188
|
+
const size = this.opts.textHeight * Math.min(this.gl.canvas.height, this.gl.canvas.width) * 1;
|
|
32189
|
+
const anchoredLabels = this.document.labels.filter((l) => l.anchor != null && l.anchor !== 0 /* NONE */);
|
|
32190
|
+
for (const label of anchoredLabels) {
|
|
32191
|
+
const text = label.text;
|
|
32192
|
+
const textHeight = this.textHeight(label.style.textScale, text) * size;
|
|
32193
|
+
const textWidth = this.textWidth(label.style.textScale, text) * size;
|
|
32194
|
+
let left;
|
|
32195
|
+
let top;
|
|
32196
|
+
const scale = 1;
|
|
32197
|
+
const verticalMargin = this.opts.textHeight * this.gl.canvas.height * scale;
|
|
32198
|
+
const rectHeightDiff = verticalMargin;
|
|
32199
|
+
let rectWidthDiff = verticalMargin / 4;
|
|
32200
|
+
let rectHorizontalOffset = 0;
|
|
32201
|
+
let rectVerticalOffset = 0;
|
|
32202
|
+
if (label.anchor & 1 /* LEFT */) {
|
|
32203
|
+
left = 0;
|
|
32204
|
+
}
|
|
32205
|
+
if (label.anchor & 4 /* RIGHT */) {
|
|
32206
|
+
left = this.canvas.width - textWidth;
|
|
32207
|
+
rectHorizontalOffset -= verticalMargin / 4;
|
|
32208
|
+
}
|
|
32209
|
+
if (label.anchor & 2 /* CENTER */) {
|
|
32210
|
+
left = (this.canvas.width - textWidth) / 2;
|
|
32211
|
+
rectHorizontalOffset -= verticalMargin / 4;
|
|
32212
|
+
rectWidthDiff += verticalMargin / 4;
|
|
32213
|
+
}
|
|
32214
|
+
if (label.anchor & 8 /* TOP */) {
|
|
32215
|
+
top = 0;
|
|
32216
|
+
}
|
|
32217
|
+
if (label.anchor & 16 /* MIDDLE */) {
|
|
32218
|
+
top = (this.canvas.height - textHeight - verticalMargin) / 2;
|
|
32219
|
+
rectVerticalOffset -= verticalMargin / 4;
|
|
32220
|
+
}
|
|
32221
|
+
if (label.anchor & 32 /* BOTTOM */) {
|
|
32222
|
+
top = this.canvas.height - textHeight - verticalMargin;
|
|
32223
|
+
rectVerticalOffset -= verticalMargin / 4;
|
|
32224
|
+
}
|
|
32225
|
+
this.drawRect(
|
|
32226
|
+
[left + rectHorizontalOffset, top + rectVerticalOffset, textWidth + rectWidthDiff, textHeight + rectHeightDiff],
|
|
32227
|
+
label.style.backgroundColor
|
|
32228
|
+
);
|
|
32229
|
+
this.draw3DLabel(label, [left, top]);
|
|
32230
|
+
}
|
|
32231
|
+
}
|
|
31912
32232
|
// not included in public docs
|
|
31913
32233
|
draw3D(leftTopWidthHeight = [0, 0, 0, 0], mvpMatrix = null, modelMatrix = null, normalMatrix = null, azimuth = null, elevation = 0) {
|
|
31914
32234
|
const isMosaic = azimuth !== null;
|
|
@@ -32784,6 +33104,12 @@ var Niivue = class _Niivue {
|
|
|
32784
33104
|
}
|
|
32785
33105
|
const isDrawPenDown = isFinite(this.drawPenLocation[0]) && this.opts.drawingEnabled;
|
|
32786
33106
|
const { volScale } = this.sliceScale();
|
|
33107
|
+
const actualScale = volScale.slice();
|
|
33108
|
+
if (this.opts.multiplanarEqualSize) {
|
|
33109
|
+
volScale[0] = 1;
|
|
33110
|
+
volScale[1] = 1;
|
|
33111
|
+
volScale[2] = 1;
|
|
33112
|
+
}
|
|
32787
33113
|
if (typeof this.opts.multiplanarPadPixels !== "number") {
|
|
32788
33114
|
log.debug("multiplanarPadPixels must be numeric");
|
|
32789
33115
|
}
|
|
@@ -32821,38 +33147,43 @@ var Niivue = class _Niivue {
|
|
|
32821
33147
|
isDrawGrid = true;
|
|
32822
33148
|
}
|
|
32823
33149
|
}
|
|
33150
|
+
let ltwh = ltwh2x2;
|
|
32824
33151
|
if (isDrawColumn) {
|
|
32825
|
-
|
|
33152
|
+
ltwh = ltwh1x3;
|
|
32826
33153
|
if (isShowRender || this.opts.multiplanarShowRender === 2 /* AUTO */ && ltwh1x4[4] >= ltwh1x3[4]) {
|
|
32827
33154
|
ltwh = ltwh1x4;
|
|
32828
33155
|
} else {
|
|
32829
33156
|
isDraw3D = false;
|
|
32830
33157
|
}
|
|
32831
|
-
const sX = volScale[0] * ltwh[4];
|
|
32832
|
-
const sY = volScale[1] * ltwh[4];
|
|
32833
|
-
const sZ = volScale[2] * ltwh[4];
|
|
32834
|
-
const sMx = mx * ltwh[4];
|
|
32835
|
-
this.draw2D([ltwh[0], ltwh[1], sX, sY], 0);
|
|
32836
|
-
this.draw2D([ltwh[0], ltwh[1] + sY + pad, sX, sZ], 1);
|
|
32837
|
-
this.draw2D([ltwh[0], ltwh[1] + sY + pad + sZ + pad, sY, sZ], 2);
|
|
32838
|
-
if (isDraw3D) {
|
|
32839
|
-
this.draw3D([ltwh[0], ltwh[1] + sY + sZ + sZ + pad * 3, sMx, sMx]);
|
|
32840
|
-
}
|
|
32841
33158
|
} else if (isDrawRow) {
|
|
32842
|
-
|
|
33159
|
+
ltwh = ltwh3x1;
|
|
32843
33160
|
if (isShowRender || this.opts.multiplanarShowRender === 2 /* AUTO */ && ltwh4x1[4] >= ltwh3x1[4]) {
|
|
32844
33161
|
ltwh = ltwh4x1;
|
|
32845
33162
|
} else {
|
|
32846
33163
|
isDraw3D = false;
|
|
32847
33164
|
}
|
|
32848
|
-
|
|
32849
|
-
|
|
32850
|
-
|
|
32851
|
-
|
|
32852
|
-
|
|
32853
|
-
|
|
33165
|
+
}
|
|
33166
|
+
const sX = volScale[0] * ltwh[4];
|
|
33167
|
+
const sY = volScale[1] * ltwh[4];
|
|
33168
|
+
const sZ = volScale[2] * ltwh[4];
|
|
33169
|
+
const actualX = actualScale[0] * ltwh[4];
|
|
33170
|
+
const actualY = actualScale[1] * ltwh[4];
|
|
33171
|
+
const actualZ = actualScale[2] * ltwh[4];
|
|
33172
|
+
if (isDrawColumn) {
|
|
33173
|
+
this.draw2D([ltwh[0], ltwh[1], sX, sY], 0, NaN, [actualX, actualY]);
|
|
33174
|
+
this.draw2D([ltwh[0], ltwh[1] + sY + pad, sX, sZ], 1, NaN, [actualX, actualZ]);
|
|
33175
|
+
this.draw2D([ltwh[0], ltwh[1] + sY + pad + sZ + pad, sY, sZ], 2, NaN, [actualY, actualZ]);
|
|
33176
|
+
if (isDraw3D) {
|
|
33177
|
+
const sMx = mx * ltwh[4];
|
|
33178
|
+
this.draw3D([ltwh[0], ltwh[1] + sY + sZ + sZ + pad * 3, sMx, sMx]);
|
|
33179
|
+
}
|
|
33180
|
+
} else if (isDrawRow) {
|
|
33181
|
+
this.draw2D([ltwh[0], ltwh[1], sX, sY], 0, NaN, [actualX, actualY]);
|
|
33182
|
+
this.draw2D([ltwh[0] + sX + pad, ltwh[1], sX, sZ], 1, NaN, [actualX, actualZ]);
|
|
33183
|
+
this.draw2D([ltwh[0] + sX + sX + pad * 2, ltwh[1], sY, sZ], 2, NaN, [actualY, actualZ]);
|
|
32854
33184
|
if (isDraw3D) {
|
|
32855
|
-
|
|
33185
|
+
const sMx = mx * ltwh[4];
|
|
33186
|
+
this.draw3D([ltwh[0] + sX + sX + sY + pad * 3, ltwh[1], sMx, sMx]);
|
|
32856
33187
|
}
|
|
32857
33188
|
} else if (isDrawGrid) {
|
|
32858
33189
|
if (!isShowRender) {
|
|
@@ -32861,13 +33192,9 @@ var Niivue = class _Niivue {
|
|
|
32861
33192
|
if (this.opts.multiplanarShowRender === 2 /* AUTO */) {
|
|
32862
33193
|
isDraw3D = true;
|
|
32863
33194
|
}
|
|
32864
|
-
|
|
32865
|
-
|
|
32866
|
-
|
|
32867
|
-
const sZ = volScale[2] * ltwh[4];
|
|
32868
|
-
this.draw2D([ltwh[0], ltwh[1] + sZ + pad, sX, sY], 0);
|
|
32869
|
-
this.draw2D([ltwh[0], ltwh[1], sX, sZ], 1);
|
|
32870
|
-
this.draw2D([ltwh[0] + sX + pad, ltwh[1], sY, sZ], 2);
|
|
33195
|
+
this.draw2D([ltwh[0], ltwh[1] + sZ + pad, sX, sY], 0, NaN, [actualX, actualY]);
|
|
33196
|
+
this.draw2D([ltwh[0], ltwh[1], sX, sZ], 1, NaN, [actualX, actualZ]);
|
|
33197
|
+
this.draw2D([ltwh[0] + sX + pad, ltwh[1], sY, sZ], 2, NaN, [actualY, actualZ]);
|
|
32871
33198
|
if (isDraw3D) {
|
|
32872
33199
|
this.draw3D([ltwh[0] + sX + pad, ltwh[1] + sZ + pad, sY, sY]);
|
|
32873
33200
|
}
|
|
@@ -32932,10 +33259,25 @@ var Niivue = class _Niivue {
|
|
|
32932
33259
|
height
|
|
32933
33260
|
]);
|
|
32934
33261
|
}
|
|
33262
|
+
if (this.opts.clickToSegment) {
|
|
33263
|
+
const x = this.mousePos[0];
|
|
33264
|
+
const y = this.mousePos[1];
|
|
33265
|
+
const tileIdx = this.tileIndex(x, y);
|
|
33266
|
+
if (tileIdx > -1) {
|
|
33267
|
+
const fovMM = this.screenSlices[tileIdx].fovMM;
|
|
33268
|
+
const ltwh = this.screenSlices[tileIdx].leftTopWidthHeight;
|
|
33269
|
+
const pixPerMM = ltwh[2] / fovMM[0];
|
|
33270
|
+
const color = this.opts.crosshairColor;
|
|
33271
|
+
const segmentCursorColor = [color[0], color[1], color[2], 0.4];
|
|
33272
|
+
const radius = this.opts.clickToSegmentRadius * pixPerMM;
|
|
33273
|
+
this.drawCircle([x - radius, y - radius, radius * 2, radius * 2], segmentCursorColor, 1);
|
|
33274
|
+
}
|
|
33275
|
+
}
|
|
32935
33276
|
const pos = this.frac2mm([this.scene.crosshairPos[0], this.scene.crosshairPos[1], this.scene.crosshairPos[2]]);
|
|
32936
33277
|
posString = pos[0].toFixed(2) + "\xD7" + pos[1].toFixed(2) + "\xD7" + pos[2].toFixed(2);
|
|
32937
33278
|
this.readyForSync = true;
|
|
32938
33279
|
this.sync();
|
|
33280
|
+
this.drawAnchoredLabels();
|
|
32939
33281
|
return posString;
|
|
32940
33282
|
}
|
|
32941
33283
|
// not included in public docs
|
|
@@ -32970,6 +33312,7 @@ export {
|
|
|
32970
33312
|
DEFAULT_OPTIONS,
|
|
32971
33313
|
DRAG_MODE,
|
|
32972
33314
|
INITIAL_SCENE_DATA,
|
|
33315
|
+
LabelAnchorPoint,
|
|
32973
33316
|
LabelLineTerminator,
|
|
32974
33317
|
LabelTextAlignment,
|
|
32975
33318
|
MULTIPLANAR_TYPE,
|