@niivue/niivue 0.55.0 → 0.56.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 +50 -0
- package/build/index.js +128 -5
- package/build/index.js.map +1 -1
- package/build/index.min.js +1 -1
- package/build/metafile-esm.json +1 -1
- package/dist/index.d.ts +50 -0
- package/dist/index.js +128 -5
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/niivue.umd.js +19 -19
- package/package.json +1 -1
- package/src/index.html +38 -3
- package/src/niivue/index.ts +186 -4
- package/src/niivue/types.ts +0 -0
package/build/index.d.ts
CHANGED
|
@@ -1702,6 +1702,11 @@ declare class Niivue {
|
|
|
1702
1702
|
VOLUME_ID: number;
|
|
1703
1703
|
DISTANCE_FROM_CAMERA: number;
|
|
1704
1704
|
graph: Graph;
|
|
1705
|
+
customLayout: Array<{
|
|
1706
|
+
sliceType: SLICE_TYPE;
|
|
1707
|
+
position: [number, number, number, number];
|
|
1708
|
+
sliceMM?: number;
|
|
1709
|
+
}>;
|
|
1705
1710
|
meshShaders: Array<{
|
|
1706
1711
|
Name: string;
|
|
1707
1712
|
Frag: string;
|
|
@@ -2127,6 +2132,50 @@ declare class Niivue {
|
|
|
2127
2132
|
* @see {@link https://niivue.github.io/niivue/features/layout.html | live demo usage}
|
|
2128
2133
|
*/
|
|
2129
2134
|
setHeroImage(fraction: number): void;
|
|
2135
|
+
/**
|
|
2136
|
+
* Set a custom slice layout. This overrides the built-in layouts.
|
|
2137
|
+
* @param layout - Array of layout specifications for each slice view
|
|
2138
|
+
* @example
|
|
2139
|
+
* niivue.setCustomLayout([
|
|
2140
|
+
* // Left 50% - Sag
|
|
2141
|
+
* {sliceType: 2, position: [0, 0, 0.5, 1.0]},
|
|
2142
|
+
* // Top right - Cor
|
|
2143
|
+
* {sliceType: 1, position: [0.5, 0, 0.5, 0.5]},
|
|
2144
|
+
* // Bottom right - Ax
|
|
2145
|
+
* {sliceType: 0, position: [0.5, 0.5, 0.5, 0.5]}
|
|
2146
|
+
* ])
|
|
2147
|
+
*
|
|
2148
|
+
* produces:
|
|
2149
|
+
* +----------------+----------------+
|
|
2150
|
+
* | | |
|
|
2151
|
+
* | | coronal |
|
|
2152
|
+
* | | |
|
|
2153
|
+
* | | |
|
|
2154
|
+
* | sagittal +----------------+
|
|
2155
|
+
* | | |
|
|
2156
|
+
* | | axial |
|
|
2157
|
+
* | | |
|
|
2158
|
+
* | | |
|
|
2159
|
+
* +----------------+----------------+
|
|
2160
|
+
*/
|
|
2161
|
+
setCustomLayout(layout: Array<{
|
|
2162
|
+
sliceType: SLICE_TYPE;
|
|
2163
|
+
position: [number, number, number, number];
|
|
2164
|
+
sliceMM?: number;
|
|
2165
|
+
}>): void;
|
|
2166
|
+
/**
|
|
2167
|
+
* Clear custom layout and rely on built-in layouts
|
|
2168
|
+
*/
|
|
2169
|
+
clearCustomLayout(): void;
|
|
2170
|
+
/**
|
|
2171
|
+
* Get the current custom layout if set
|
|
2172
|
+
* @returns The current custom layout or null if using built-in layouts
|
|
2173
|
+
*/
|
|
2174
|
+
getCustomLayout(): Array<{
|
|
2175
|
+
sliceType: SLICE_TYPE;
|
|
2176
|
+
position: [number, number, number, number];
|
|
2177
|
+
sliceMM?: number;
|
|
2178
|
+
}> | null;
|
|
2130
2179
|
/**
|
|
2131
2180
|
* control whether 2D slices use radiological or neurological convention.
|
|
2132
2181
|
* @param isRadiologicalConvention - new display convention
|
|
@@ -3069,6 +3118,7 @@ declare class Niivue {
|
|
|
3069
3118
|
* @see {@link https://niivue.github.io/niivue/features/mosaics.html | live demo usage}
|
|
3070
3119
|
*/
|
|
3071
3120
|
drawMosaic(mosaicStr: string): void;
|
|
3121
|
+
calculateWidthHeight(sliceType: number, volScale: number[], containerWidth: number, containerHeight: number): [number, number];
|
|
3072
3122
|
drawSceneCore(): string | void;
|
|
3073
3123
|
drawScene(): string | void;
|
|
3074
3124
|
get gl(): WebGL2RenderingContext;
|
package/build/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
17
17
|
import { mat4 as mat49, vec2, vec3 as vec314, vec4 as vec49 } from "gl-matrix";
|
|
18
18
|
|
|
19
19
|
// package.json
|
|
20
|
-
var version = "0.
|
|
20
|
+
var version = "0.56.0";
|
|
21
21
|
|
|
22
22
|
// src/logger.ts
|
|
23
23
|
var _Log = class _Log {
|
|
@@ -25166,6 +25166,7 @@ var Niivue = class {
|
|
|
25166
25166
|
normalizeValues: false,
|
|
25167
25167
|
isRangeCalMinMax: false
|
|
25168
25168
|
});
|
|
25169
|
+
__publicField(this, "customLayout", []);
|
|
25169
25170
|
__publicField(this, "meshShaders", [
|
|
25170
25171
|
{
|
|
25171
25172
|
Name: "Phong",
|
|
@@ -27120,6 +27121,65 @@ var Niivue = class {
|
|
|
27120
27121
|
this.opts.heroImageFraction = fraction;
|
|
27121
27122
|
this.drawScene();
|
|
27122
27123
|
}
|
|
27124
|
+
/**
|
|
27125
|
+
* Set a custom slice layout. This overrides the built-in layouts.
|
|
27126
|
+
* @param layout - Array of layout specifications for each slice view
|
|
27127
|
+
* @example
|
|
27128
|
+
* niivue.setCustomLayout([
|
|
27129
|
+
* // Left 50% - Sag
|
|
27130
|
+
* {sliceType: 2, position: [0, 0, 0.5, 1.0]},
|
|
27131
|
+
* // Top right - Cor
|
|
27132
|
+
* {sliceType: 1, position: [0.5, 0, 0.5, 0.5]},
|
|
27133
|
+
* // Bottom right - Ax
|
|
27134
|
+
* {sliceType: 0, position: [0.5, 0.5, 0.5, 0.5]}
|
|
27135
|
+
* ])
|
|
27136
|
+
*
|
|
27137
|
+
* produces:
|
|
27138
|
+
* +----------------+----------------+
|
|
27139
|
+
* | | |
|
|
27140
|
+
* | | coronal |
|
|
27141
|
+
* | | |
|
|
27142
|
+
* | | |
|
|
27143
|
+
* | sagittal +----------------+
|
|
27144
|
+
* | | |
|
|
27145
|
+
* | | axial |
|
|
27146
|
+
* | | |
|
|
27147
|
+
* | | |
|
|
27148
|
+
* +----------------+----------------+
|
|
27149
|
+
*/
|
|
27150
|
+
setCustomLayout(layout) {
|
|
27151
|
+
for (let i = 0; i < layout.length; i++) {
|
|
27152
|
+
const [left1, top1, width1, height1] = layout[i].position;
|
|
27153
|
+
const right1 = left1 + width1;
|
|
27154
|
+
const bottom1 = top1 + height1;
|
|
27155
|
+
for (let j = i + 1; j < layout.length; j++) {
|
|
27156
|
+
const [left2, top2, width2, height2] = layout[j].position;
|
|
27157
|
+
const right2 = left2 + width2;
|
|
27158
|
+
const bottom2 = top2 + height2;
|
|
27159
|
+
const horizontallyOverlaps = left1 < right2 && right1 > left2;
|
|
27160
|
+
const verticallyOverlaps = top1 < bottom2 && bottom1 > top2;
|
|
27161
|
+
if (horizontallyOverlaps && verticallyOverlaps) {
|
|
27162
|
+
throw new Error(`Custom layout is invalid. Tile ${i} overlaps with tile ${j}.`);
|
|
27163
|
+
}
|
|
27164
|
+
}
|
|
27165
|
+
}
|
|
27166
|
+
this.customLayout = layout;
|
|
27167
|
+
this.drawScene();
|
|
27168
|
+
}
|
|
27169
|
+
/**
|
|
27170
|
+
* Clear custom layout and rely on built-in layouts
|
|
27171
|
+
*/
|
|
27172
|
+
clearCustomLayout() {
|
|
27173
|
+
this.customLayout = null;
|
|
27174
|
+
this.drawScene();
|
|
27175
|
+
}
|
|
27176
|
+
/**
|
|
27177
|
+
* Get the current custom layout if set
|
|
27178
|
+
* @returns The current custom layout or null if using built-in layouts
|
|
27179
|
+
*/
|
|
27180
|
+
getCustomLayout() {
|
|
27181
|
+
return this.customLayout;
|
|
27182
|
+
}
|
|
27123
27183
|
/**
|
|
27124
27184
|
* control whether 2D slices use radiological or neurological convention.
|
|
27125
27185
|
* @param isRadiologicalConvention - new display convention
|
|
@@ -35183,6 +35243,36 @@ var Niivue = class {
|
|
|
35183
35243
|
}
|
|
35184
35244
|
this.opts.textHeight = labelSize;
|
|
35185
35245
|
}
|
|
35246
|
+
calculateWidthHeight(sliceType, volScale, containerWidth, containerHeight) {
|
|
35247
|
+
let xScale, yScale;
|
|
35248
|
+
switch (sliceType) {
|
|
35249
|
+
case 0 /* AXIAL */:
|
|
35250
|
+
xScale = volScale[0];
|
|
35251
|
+
yScale = volScale[1];
|
|
35252
|
+
break;
|
|
35253
|
+
case 1 /* CORONAL */:
|
|
35254
|
+
xScale = volScale[0];
|
|
35255
|
+
yScale = volScale[2];
|
|
35256
|
+
break;
|
|
35257
|
+
case 2 /* SAGITTAL */:
|
|
35258
|
+
xScale = volScale[1];
|
|
35259
|
+
yScale = volScale[2];
|
|
35260
|
+
break;
|
|
35261
|
+
default:
|
|
35262
|
+
return [containerWidth, containerHeight];
|
|
35263
|
+
}
|
|
35264
|
+
const aspectRatio = xScale / yScale;
|
|
35265
|
+
const containerAspect = containerWidth / containerHeight;
|
|
35266
|
+
let actualWidth, actualHeight;
|
|
35267
|
+
if (aspectRatio > containerAspect) {
|
|
35268
|
+
actualWidth = containerWidth;
|
|
35269
|
+
actualHeight = containerWidth / aspectRatio;
|
|
35270
|
+
} else {
|
|
35271
|
+
actualHeight = containerHeight;
|
|
35272
|
+
actualWidth = containerHeight * aspectRatio;
|
|
35273
|
+
}
|
|
35274
|
+
return [actualWidth, actualHeight];
|
|
35275
|
+
}
|
|
35186
35276
|
// not included in public docs
|
|
35187
35277
|
drawSceneCore() {
|
|
35188
35278
|
if (!this.initialized) {
|
|
@@ -35208,9 +35298,6 @@ var Niivue = class {
|
|
|
35208
35298
|
this.drawLoadingText(this.opts.loadingText);
|
|
35209
35299
|
return;
|
|
35210
35300
|
}
|
|
35211
|
-
if (this.back === null) {
|
|
35212
|
-
return;
|
|
35213
|
-
}
|
|
35214
35301
|
if (this.uiData.isDragging && this.scene.clipPlaneDepthAziElev[0] < 1.8 && this.inRenderTile(this.uiData.dragStart[0], this.uiData.dragStart[1]) >= 0) {
|
|
35215
35302
|
const x = this.uiData.dragStart[0] - this.uiData.dragEnd[0];
|
|
35216
35303
|
const y = this.uiData.dragStart[1] - this.uiData.dragEnd[1];
|
|
@@ -35246,7 +35333,43 @@ var Niivue = class {
|
|
|
35246
35333
|
let isHeroImage = false;
|
|
35247
35334
|
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
35248
35335
|
this.screenSlices = [];
|
|
35249
|
-
if (this.
|
|
35336
|
+
if (this.customLayout && this.customLayout.length > 0) {
|
|
35337
|
+
this.screenSlices = [];
|
|
35338
|
+
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
35339
|
+
const { volScale } = this.sliceScale();
|
|
35340
|
+
const canvasWH = [this.effectiveCanvasWidth(), this.effectiveCanvasHeight()];
|
|
35341
|
+
for (const view of this.customLayout) {
|
|
35342
|
+
const { sliceType, position, sliceMM } = view;
|
|
35343
|
+
const leftTopWidthHeight = position.slice();
|
|
35344
|
+
if (position[0] >= 0 && position[0] <= 1 && position[2] <= 1) {
|
|
35345
|
+
leftTopWidthHeight[0] = position[0] * canvasWH[0];
|
|
35346
|
+
leftTopWidthHeight[2] = position[2] * canvasWH[0];
|
|
35347
|
+
}
|
|
35348
|
+
if (position[1] >= 0 && position[1] <= 1 && position[3] <= 1) {
|
|
35349
|
+
leftTopWidthHeight[1] = position[1] * canvasWH[1];
|
|
35350
|
+
leftTopWidthHeight[3] = position[3] * canvasWH[1];
|
|
35351
|
+
}
|
|
35352
|
+
if (leftTopWidthHeight[0] + leftTopWidthHeight[2] > canvasWH[0]) {
|
|
35353
|
+
log.warn("adjusting slice width because it would have been clipped");
|
|
35354
|
+
leftTopWidthHeight[2] = canvasWH[0] - leftTopWidthHeight[0];
|
|
35355
|
+
}
|
|
35356
|
+
if (leftTopWidthHeight[1] + leftTopWidthHeight[3] > canvasWH[1]) {
|
|
35357
|
+
log.warn("adjusting slice height because it would have been clipped");
|
|
35358
|
+
leftTopWidthHeight[3] = canvasWH[1] - leftTopWidthHeight[1];
|
|
35359
|
+
}
|
|
35360
|
+
if (sliceType === 4 /* RENDER */) {
|
|
35361
|
+
this.draw3D(leftTopWidthHeight);
|
|
35362
|
+
} else if (sliceType === 0 /* AXIAL */ || sliceType === 1 /* CORONAL */ || sliceType === 2 /* SAGITTAL */) {
|
|
35363
|
+
const actualDimensions = this.calculateWidthHeight(
|
|
35364
|
+
sliceType,
|
|
35365
|
+
volScale,
|
|
35366
|
+
leftTopWidthHeight[2],
|
|
35367
|
+
leftTopWidthHeight[3]
|
|
35368
|
+
);
|
|
35369
|
+
this.draw2D(leftTopWidthHeight, sliceType, sliceMM ?? NaN, actualDimensions);
|
|
35370
|
+
}
|
|
35371
|
+
}
|
|
35372
|
+
} else if (this.opts.sliceType === 0 /* AXIAL */) {
|
|
35250
35373
|
this.draw2D([0, 0, 0, 0], 0);
|
|
35251
35374
|
} else if (this.opts.sliceType === 1 /* CORONAL */) {
|
|
35252
35375
|
this.draw2D([0, 0, 0, 0], 1);
|