@niivue/niivue 0.44.0 → 0.44.2-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/nvmesh.ts CHANGED
@@ -1313,6 +1313,102 @@ export class NVMesh {
1313
1313
  this.updateMesh(gl) // apply the new properties...
1314
1314
  }
1315
1315
 
1316
+ hierarchicalOrder(): number {
1317
+ const V0 = 12
1318
+ const F0 = 20
1319
+ const nF = this.tris.length / 3
1320
+ const order = Math.log(nF / F0) / Math.log(4)
1321
+ // Sanity checks
1322
+ if (nF !== Math.pow(4, order) * F0) {
1323
+ return NaN
1324
+ }
1325
+ const nV = this.pts.length / 3
1326
+ if (nV !== Math.pow(4, order) * (V0 - 2) + 2) {
1327
+ return NaN
1328
+ }
1329
+ // next checks are in case FreeSurfer was optimized with more local face indices
1330
+ // for an example see BrainMesh_ICBM152.lh.mz3
1331
+ for (let i = 0; i < 15; i += 3) {
1332
+ if (this.tris[i] !== 0) {
1333
+ return NaN
1334
+ }
1335
+ }
1336
+ for (let i = 15; i < 24; i += 3) {
1337
+ if (this.tris[i] !== 3) {
1338
+ return NaN
1339
+ }
1340
+ }
1341
+ for (let i = 24; i < 30; i += 3) {
1342
+ if (this.tris[i] !== 4) {
1343
+ return NaN
1344
+ }
1345
+ }
1346
+ return order
1347
+ }
1348
+
1349
+ decimateFaces(n: number, ntarget: number): void {
1350
+ let fac = this.tris
1351
+ // Constants for the icosahedron
1352
+ const V0 = 12
1353
+ const F0 = 20
1354
+ for (let j = n - 1; j >= ntarget; j--) {
1355
+ const nVjprev = Math.pow(4, j + 1) * (V0 - 2) + 2
1356
+ const nVj = Math.pow(4, j) * (V0 - 2) + 2
1357
+ const nFjprev = fac.length / 3 // = 4^(j+1)*F0
1358
+ const nFj = Math.pow(4, j) * F0
1359
+
1360
+ console.log(`order ${j + 1} -> ${j} vertices ${nVjprev} -> ${nVj} faces ${nFjprev} -> ${nFj}`)
1361
+
1362
+ const remap = Array.from({ length: nVjprev }, (_, i) => i + 1)
1363
+
1364
+ for (let i = 0; i < nFjprev; i++) {
1365
+ const v1 = fac[3 * i]
1366
+ const v2 = fac[3 * i + 1]
1367
+ const v3 = fac[3 * i + 2]
1368
+ remap[v1 - 1] = Math.min(remap[v1 - 1], v2, v3)
1369
+ }
1370
+
1371
+ const facJ = new Uint32Array(nFj * 3)
1372
+ for (let i = 0; i < nFj; i++) {
1373
+ facJ[3 * i] = remap[fac[3 * i] - 1]
1374
+ facJ[3 * i + 1] = remap[fac[3 * i + 1] - 1]
1375
+ facJ[3 * i + 2] = remap[fac[3 * i + 2] - 1]
1376
+ }
1377
+ fac = facJ
1378
+ }
1379
+ this.tris = new Uint32Array(fac)
1380
+ }
1381
+
1382
+ // internal function simplifies FreeSurfer triangulated mesh and overlays
1383
+ decimateHierarchicalMesh(gl: WebGL2RenderingContext, order: number = 4): boolean {
1384
+ const inputOrder = this.hierarchicalOrder()
1385
+ if (isNaN(inputOrder)) {
1386
+ log.warn('Unable to decimate mesh: it does not have a hierarchical structure')
1387
+ return false
1388
+ }
1389
+ if (order >= inputOrder) {
1390
+ log.warn(`Unable to decimate mesh: input order (${inputOrder}) must be larger than downsampled order (${order})`)
1391
+ return false
1392
+ }
1393
+ const inputVLength = this.pts.length / 3
1394
+ const V0 = 12
1395
+ const nV = Math.pow(4, order) * (V0 - 2) + 2
1396
+ this.pts = new Float32Array(this.pts.slice(0, nV * 3))
1397
+ this.decimateFaces(inputOrder, order)
1398
+ if (this.layers && this.layers.length > 0) {
1399
+ for (let i = 0; i < this.layers.length; i++) {
1400
+ const layer = this.layers[i]
1401
+ if (layer.values instanceof Float32Array || layer.values.length !== inputVLength) {
1402
+ layer.values = new Float32Array(layer.values.slice(0, nV))
1403
+ } else {
1404
+ log.warn(`decimation logic needs to be updated`)
1405
+ }
1406
+ }
1407
+ }
1408
+ this.updateMesh(gl) // apply the new properties...
1409
+ return true
1410
+ }
1411
+
1316
1412
  // adjust attributes of a mesh layer. invoked by niivue.setMeshLayerProperty()
1317
1413
  // TODO this method is a bit too generic
1318
1414
  setLayerProperty(
@@ -719,6 +719,47 @@ void main() {
719
719
  color = lineColor;
720
720
  }`
721
721
 
722
+ export const fragRectOutlineShader = `#version 300 es
723
+ #line 723
724
+ precision highp int;
725
+ precision highp float;
726
+
727
+ uniform vec4 lineColor;
728
+ uniform vec4 leftTopWidthHeight;
729
+ uniform float thickness; // line thickness in pixels
730
+ uniform vec2 canvasWidthHeight;
731
+
732
+ out vec4 color;
733
+
734
+ void main() {
735
+ // fragment position in screen coordinates
736
+ vec2 fragCoord = gl_FragCoord.xy;
737
+
738
+ // canvas height
739
+ float canvasHeight = canvasWidthHeight.y;
740
+
741
+ // 'top' and 'bottom' to match gl_FragCoord.y coordinate system
742
+ float top = canvasHeight - leftTopWidthHeight.y;
743
+ float bottom = top - leftTopWidthHeight.w;
744
+
745
+ // left and right edges
746
+ float left = leftTopWidthHeight.x;
747
+ float right = left + leftTopWidthHeight.z;
748
+
749
+ bool withinLeft = fragCoord.x >= left && fragCoord.x <= left + thickness;
750
+ bool withinRight = fragCoord.x <= right && fragCoord.x >= right - thickness;
751
+ bool withinTop = fragCoord.y <= top && fragCoord.y >= top - thickness;
752
+ bool withinBottom = fragCoord.y >= bottom && fragCoord.y <= bottom + thickness;
753
+
754
+ bool isOutline = withinLeft || withinRight || withinTop || withinBottom;
755
+
756
+ if (isOutline) {
757
+ color = lineColor;
758
+ } else {
759
+ discard;
760
+ }
761
+ }`
762
+
722
763
  export const vertColorbarShader = `#version 300 es
723
764
  #line 490
724
765
  layout(location=0) in vec3 pos;
@@ -1207,11 +1248,11 @@ uniform mat4 mvpMtx;
1207
1248
  uniform mat4 normMtx;
1208
1249
  out vec4 vClr;
1209
1250
  out vec3 vN;
1210
- out vec4 vP;
1251
+ out vec4 vPc;
1211
1252
  void main(void) {
1212
1253
  vec3 lightPosition = vec3(0.0, 0.0, -10.0);
1213
- vP = vec4(pos, 1.0);
1214
- gl_Position = mvpMtx * vP;
1254
+ vPc = mvpMtx * vec4(pos, 1.0);
1255
+ gl_Position = vPc;
1215
1256
  vN = normalize((normMtx * vec4(norm.xyz,1.0)).xyz);
1216
1257
  //vV = -vec3(modelMtx*vec4(pos,1.0));
1217
1258
  vClr = clr;
@@ -1368,6 +1409,46 @@ void main() {
1368
1409
  color = vec4(d3 + s3 + s, opacity);
1369
1410
  }`
1370
1411
 
1412
+ // https://thorium.rocks/media/curves/curvaceous.html
1413
+ export const fragMeshShaderCrevice = `#version 300 es
1414
+ precision highp int;
1415
+ precision highp float;
1416
+ uniform float opacity;
1417
+ in vec4 vClr;
1418
+ in vec3 vN;
1419
+ in vec4 vPc;
1420
+ out vec4 color;
1421
+ void main() {
1422
+ vec3 n = normalize(vN);
1423
+ // Compute curvature
1424
+ vec3 dx = dFdx(n);
1425
+ vec3 dy = dFdy(n);
1426
+ vec3 xneg = n - dx;
1427
+ vec3 xpos = n + dx;
1428
+ vec3 yneg = n - dy;
1429
+ vec3 ypos = n + dy;
1430
+ float depth = length(vPc.xyz);
1431
+ float curv = (cross(xneg, xpos).y - cross(yneg, ypos).x) / depth;
1432
+ //at this stage 0.5 for flat, with valleys dark and ridges bright
1433
+ curv = 1.0 - (curv + 0.5);
1434
+ //clamp
1435
+ curv = min(max(curv, 0.0), 1.0);
1436
+ // easing function
1437
+ curv = pow(curv, 0.5);
1438
+ //modulate ambient and diffuse with curvature
1439
+ vec3 r = vec3(0.0, 0.0, 1.0); //rayDir: for orthographic projections moving in Z direction (no need for normal matrix)
1440
+ float ambient = 0.6;
1441
+ float diffuse = 0.6;
1442
+ float specular = 0.2;
1443
+ float shininess = 10.0;
1444
+ vec3 lightPosition = vec3(0.0, 10.0, -2.0);
1445
+ vec3 l = normalize(lightPosition);
1446
+ float lightNormDot = dot(n, l);
1447
+ vec3 a = vClr.rgb * ambient * curv;
1448
+ vec3 d = max(lightNormDot, 0.0) * vClr.rgb * diffuse;
1449
+ float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1450
+ color = vec4(a + d + s, opacity);
1451
+ }`
1371
1452
  // Phong: default
1372
1453
  export const fragMeshShader = `#version 300 es
1373
1454
  precision highp int;
package/src/types.ts CHANGED
@@ -152,3 +152,16 @@ export type NiiVueLocation = {
152
152
  vox: vec3
153
153
  xy: [number, number]
154
154
  }
155
+
156
+ export type SyncOpts = {
157
+ '3d'?: boolean
158
+ '2d'?: boolean
159
+ zoomPan?: boolean
160
+ cal_min?: boolean
161
+ cal_max?: boolean
162
+ gamma?: boolean
163
+ useSliceOffset?: boolean
164
+ sliceType?: boolean
165
+ crosshair?: boolean
166
+ clipPlane?: boolean
167
+ }