@aics/vole-core 3.12.4
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/LICENSE.txt +26 -0
- package/README.md +119 -0
- package/es/Atlas2DSlice.js +224 -0
- package/es/Channel.js +264 -0
- package/es/FileSaver.js +31 -0
- package/es/FusedChannelData.js +192 -0
- package/es/Histogram.js +250 -0
- package/es/ImageInfo.js +127 -0
- package/es/Light.js +74 -0
- package/es/Lut.js +500 -0
- package/es/MarchingCubes.js +507 -0
- package/es/MeshVolume.js +334 -0
- package/es/NaiveSurfaceNets.js +251 -0
- package/es/PathTracedVolume.js +482 -0
- package/es/RayMarchedAtlasVolume.js +250 -0
- package/es/RenderToBuffer.js +31 -0
- package/es/ThreeJsPanel.js +633 -0
- package/es/Timing.js +28 -0
- package/es/TrackballControls.js +538 -0
- package/es/View3d.js +848 -0
- package/es/Volume.js +352 -0
- package/es/VolumeCache.js +161 -0
- package/es/VolumeDims.js +16 -0
- package/es/VolumeDrawable.js +702 -0
- package/es/VolumeMaker.js +101 -0
- package/es/VolumeRenderImpl.js +1 -0
- package/es/VolumeRenderSettings.js +203 -0
- package/es/constants/basicShaders.js +29 -0
- package/es/constants/colors.js +59 -0
- package/es/constants/denoiseShader.js +43 -0
- package/es/constants/lights.js +42 -0
- package/es/constants/materials.js +85 -0
- package/es/constants/pathtraceOutputShader.js +13 -0
- package/es/constants/scaleBarSVG.js +21 -0
- package/es/constants/time.js +34 -0
- package/es/constants/volumePTshader.js +153 -0
- package/es/constants/volumeRayMarchShader.js +123 -0
- package/es/constants/volumeSliceShader.js +115 -0
- package/es/index.js +21 -0
- package/es/loaders/IVolumeLoader.js +131 -0
- package/es/loaders/JsonImageInfoLoader.js +255 -0
- package/es/loaders/OmeZarrLoader.js +495 -0
- package/es/loaders/OpenCellLoader.js +65 -0
- package/es/loaders/RawArrayLoader.js +89 -0
- package/es/loaders/TiffLoader.js +219 -0
- package/es/loaders/VolumeLoadError.js +44 -0
- package/es/loaders/VolumeLoaderUtils.js +221 -0
- package/es/loaders/index.js +40 -0
- package/es/loaders/zarr_utils/ChunkPrefetchIterator.js +143 -0
- package/es/loaders/zarr_utils/WrappedStore.js +51 -0
- package/es/loaders/zarr_utils/types.js +24 -0
- package/es/loaders/zarr_utils/utils.js +225 -0
- package/es/loaders/zarr_utils/validation.js +49 -0
- package/es/test/ChunkPrefetchIterator.test.js +208 -0
- package/es/test/RequestQueue.test.js +442 -0
- package/es/test/SubscribableRequestQueue.test.js +244 -0
- package/es/test/VolumeCache.test.js +118 -0
- package/es/test/VolumeRenderSettings.test.js +71 -0
- package/es/test/lut.test.js +671 -0
- package/es/test/num_utils.test.js +140 -0
- package/es/test/volume.test.js +98 -0
- package/es/test/zarr_utils.test.js +358 -0
- package/es/types/Atlas2DSlice.d.ts +41 -0
- package/es/types/Channel.d.ts +44 -0
- package/es/types/FileSaver.d.ts +6 -0
- package/es/types/FusedChannelData.d.ts +26 -0
- package/es/types/Histogram.d.ts +57 -0
- package/es/types/ImageInfo.d.ts +87 -0
- package/es/types/Light.d.ts +27 -0
- package/es/types/Lut.d.ts +67 -0
- package/es/types/MarchingCubes.d.ts +53 -0
- package/es/types/MeshVolume.d.ts +40 -0
- package/es/types/NaiveSurfaceNets.d.ts +11 -0
- package/es/types/PathTracedVolume.d.ts +65 -0
- package/es/types/RayMarchedAtlasVolume.d.ts +41 -0
- package/es/types/RenderToBuffer.d.ts +17 -0
- package/es/types/ThreeJsPanel.d.ts +107 -0
- package/es/types/Timing.d.ts +11 -0
- package/es/types/TrackballControls.d.ts +51 -0
- package/es/types/View3d.d.ts +357 -0
- package/es/types/Volume.d.ts +152 -0
- package/es/types/VolumeCache.d.ts +43 -0
- package/es/types/VolumeDims.d.ts +28 -0
- package/es/types/VolumeDrawable.d.ts +108 -0
- package/es/types/VolumeMaker.d.ts +49 -0
- package/es/types/VolumeRenderImpl.d.ts +22 -0
- package/es/types/VolumeRenderSettings.d.ts +98 -0
- package/es/types/constants/basicShaders.d.ts +4 -0
- package/es/types/constants/colors.d.ts +2 -0
- package/es/types/constants/denoiseShader.d.ts +40 -0
- package/es/types/constants/lights.d.ts +38 -0
- package/es/types/constants/materials.d.ts +20 -0
- package/es/types/constants/pathtraceOutputShader.d.ts +11 -0
- package/es/types/constants/scaleBarSVG.d.ts +2 -0
- package/es/types/constants/time.d.ts +19 -0
- package/es/types/constants/volumePTshader.d.ts +137 -0
- package/es/types/constants/volumeRayMarchShader.d.ts +117 -0
- package/es/types/constants/volumeSliceShader.d.ts +109 -0
- package/es/types/glsl.d.js +0 -0
- package/es/types/index.d.ts +28 -0
- package/es/types/loaders/IVolumeLoader.d.ts +113 -0
- package/es/types/loaders/JsonImageInfoLoader.d.ts +80 -0
- package/es/types/loaders/OmeZarrLoader.d.ts +87 -0
- package/es/types/loaders/OpenCellLoader.d.ts +9 -0
- package/es/types/loaders/RawArrayLoader.d.ts +33 -0
- package/es/types/loaders/TiffLoader.d.ts +45 -0
- package/es/types/loaders/VolumeLoadError.d.ts +18 -0
- package/es/types/loaders/VolumeLoaderUtils.d.ts +38 -0
- package/es/types/loaders/index.d.ts +22 -0
- package/es/types/loaders/zarr_utils/ChunkPrefetchIterator.d.ts +22 -0
- package/es/types/loaders/zarr_utils/WrappedStore.d.ts +24 -0
- package/es/types/loaders/zarr_utils/types.d.ts +94 -0
- package/es/types/loaders/zarr_utils/utils.d.ts +23 -0
- package/es/types/loaders/zarr_utils/validation.d.ts +7 -0
- package/es/types/test/ChunkPrefetchIterator.test.d.ts +1 -0
- package/es/types/test/RequestQueue.test.d.ts +1 -0
- package/es/types/test/SubscribableRequestQueue.test.d.ts +1 -0
- package/es/types/test/VolumeCache.test.d.ts +1 -0
- package/es/types/test/VolumeRenderSettings.test.d.ts +1 -0
- package/es/types/test/lut.test.d.ts +1 -0
- package/es/types/test/num_utils.test.d.ts +1 -0
- package/es/types/test/volume.test.d.ts +1 -0
- package/es/types/test/zarr_utils.test.d.ts +1 -0
- package/es/types/types.d.ts +115 -0
- package/es/types/utils/RequestQueue.d.ts +112 -0
- package/es/types/utils/SubscribableRequestQueue.d.ts +52 -0
- package/es/types/utils/num_utils.d.ts +43 -0
- package/es/types/workers/VolumeLoaderContext.d.ts +106 -0
- package/es/types/workers/types.d.ts +101 -0
- package/es/types/workers/util.d.ts +3 -0
- package/es/types.js +75 -0
- package/es/typings.d.js +0 -0
- package/es/utils/RequestQueue.js +267 -0
- package/es/utils/SubscribableRequestQueue.js +187 -0
- package/es/utils/num_utils.js +231 -0
- package/es/workers/FetchTiffWorker.js +153 -0
- package/es/workers/VolumeLoadWorker.js +129 -0
- package/es/workers/VolumeLoaderContext.js +271 -0
- package/es/workers/types.js +41 -0
- package/es/workers/util.js +8 -0
- package/package.json +83 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic utility functions to create sample volume data
|
|
3
|
+
* @class
|
|
4
|
+
*/
|
|
5
|
+
export default class VolumeMaker {
|
|
6
|
+
/**
|
|
7
|
+
* Rasterize a signed distance function into a volume of vx * vy * vz dimensions. This is a binary filling operation.
|
|
8
|
+
* @param {number} vx
|
|
9
|
+
* @param {number} vy
|
|
10
|
+
* @param {number} vz
|
|
11
|
+
* @param {function} sdFunc A function f(x,y,z) that returns a distance. f < 0 will be the interior of the volume, and f>=0 will be outside.
|
|
12
|
+
*/
|
|
13
|
+
static createVolume(vx, vy, vz, sdFunc) {
|
|
14
|
+
const data = new Uint8Array(vx * vy * vz).fill(0);
|
|
15
|
+
const cx = vx / 2;
|
|
16
|
+
const cy = vy / 2;
|
|
17
|
+
const cz = vz / 2;
|
|
18
|
+
let offset, px, py, pz;
|
|
19
|
+
for (let i = 0; i < vz; ++i) {
|
|
20
|
+
for (let j = 0; j < vy; ++j) {
|
|
21
|
+
for (let k = 0; k < vx; ++k) {
|
|
22
|
+
offset = i * (vx * vy) + j * vx + k;
|
|
23
|
+
px = k - cx;
|
|
24
|
+
py = j - cy;
|
|
25
|
+
pz = i - cz;
|
|
26
|
+
if (sdFunc(px, py, pz) < 0) {
|
|
27
|
+
data[offset] = 255;
|
|
28
|
+
} else {
|
|
29
|
+
data[offset] = 0;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a volume filled with a sphere in the center
|
|
39
|
+
* @param {number} vx
|
|
40
|
+
* @param {number} vy
|
|
41
|
+
* @param {number} vz
|
|
42
|
+
* @param {number} radius
|
|
43
|
+
*/
|
|
44
|
+
static createSphere(vx, vy, vz, radius) {
|
|
45
|
+
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
46
|
+
return Math.sqrt(px * px + py * py + pz * pz) - radius;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a volume with a cylinder centered inside.
|
|
52
|
+
* @param {number} vx
|
|
53
|
+
* @param {number} vy
|
|
54
|
+
* @param {number} vz
|
|
55
|
+
* @param {number} hx width of cap (?)
|
|
56
|
+
* @param {number} hy depth of cap (?)
|
|
57
|
+
*/
|
|
58
|
+
static createCylinder(vx, vy, vz, hx, hy) {
|
|
59
|
+
let dx, dy, mdx, mdy;
|
|
60
|
+
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
61
|
+
dx = Math.abs(Math.sqrt(px * px + pz * pz)) - hx;
|
|
62
|
+
dy = Math.abs(py) - hy;
|
|
63
|
+
mdx = Math.max(dx, 0.0);
|
|
64
|
+
mdy = Math.max(dy, 0.0);
|
|
65
|
+
return Math.min(Math.max(dx, dy), 0.0) + Math.sqrt(mdx * mdx + mdy * mdy);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a volume with a torus centered inside
|
|
71
|
+
* @param {number} vx
|
|
72
|
+
* @param {number} vy
|
|
73
|
+
* @param {number} vz
|
|
74
|
+
* @param {number} tx inner radius
|
|
75
|
+
* @param {number} ty outer radius
|
|
76
|
+
*/
|
|
77
|
+
static createTorus(vx, vy, vz, tx, ty) {
|
|
78
|
+
let qx, qy;
|
|
79
|
+
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
80
|
+
qx = Math.sqrt(px * px + pz * pz) - tx;
|
|
81
|
+
qy = py;
|
|
82
|
+
return Math.sqrt(qx * qx + qy * qy) - ty;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create a volume with a cone centered inside. cx, cy must be a 2d normalized pair...?
|
|
88
|
+
* @param {number} vx
|
|
89
|
+
* @param {number} vy
|
|
90
|
+
* @param {number} vz
|
|
91
|
+
* @param {number} cx base radius
|
|
92
|
+
* @param {number} cy height
|
|
93
|
+
*/
|
|
94
|
+
static createCone(vx, vy, vz, cx, cy) {
|
|
95
|
+
let q;
|
|
96
|
+
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
97
|
+
q = Math.sqrt(px * px + py * py);
|
|
98
|
+
return cx * q + cy * pz;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Euler, Vector2, Vector3 } from "three";
|
|
2
|
+
/**
|
|
3
|
+
* Marks groups of related settings that may have changed.
|
|
4
|
+
*/
|
|
5
|
+
export let SettingsFlags = /*#__PURE__*/function (SettingsFlags) {
|
|
6
|
+
SettingsFlags[SettingsFlags["TRANSFORM"] = 1] = "TRANSFORM";
|
|
7
|
+
SettingsFlags[SettingsFlags["CAMERA"] = 2] = "CAMERA";
|
|
8
|
+
SettingsFlags[SettingsFlags["BOUNDING_BOX"] = 4] = "BOUNDING_BOX";
|
|
9
|
+
SettingsFlags[SettingsFlags["ROI"] = 8] = "ROI";
|
|
10
|
+
SettingsFlags[SettingsFlags["MASK_ALPHA"] = 16] = "MASK_ALPHA";
|
|
11
|
+
SettingsFlags[SettingsFlags["MATERIAL"] = 32] = "MATERIAL";
|
|
12
|
+
SettingsFlags[SettingsFlags["SAMPLING"] = 64] = "SAMPLING";
|
|
13
|
+
SettingsFlags[SettingsFlags["VIEW"] = 128] = "VIEW";
|
|
14
|
+
SettingsFlags[SettingsFlags["MASK_DATA"] = 256] = "MASK_DATA";
|
|
15
|
+
SettingsFlags[SettingsFlags["ALL"] = 1023] = "ALL";
|
|
16
|
+
return SettingsFlags;
|
|
17
|
+
}({});
|
|
18
|
+
export let Axis = /*#__PURE__*/function (Axis) {
|
|
19
|
+
Axis["X"] = "x";
|
|
20
|
+
Axis["Y"] = "y";
|
|
21
|
+
Axis["Z"] = "z";
|
|
22
|
+
Axis["XYZ"] = "";
|
|
23
|
+
Axis["NONE"] = "";
|
|
24
|
+
return Axis;
|
|
25
|
+
}({});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Holds shared settings for configuring `VolumeRenderImpl` instances.
|
|
29
|
+
*/
|
|
30
|
+
export class VolumeRenderSettings {
|
|
31
|
+
// TRANSFORM
|
|
32
|
+
|
|
33
|
+
// TODO made redundant by `scale`?
|
|
34
|
+
|
|
35
|
+
// VIEW
|
|
36
|
+
|
|
37
|
+
// CAMERA
|
|
38
|
+
|
|
39
|
+
// MASK
|
|
40
|
+
|
|
41
|
+
// MATERIAL
|
|
42
|
+
|
|
43
|
+
// ROI
|
|
44
|
+
|
|
45
|
+
// BOUNDING_BOX
|
|
46
|
+
|
|
47
|
+
// SAMPLING
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a new VolumeRenderSettings object with default fields.
|
|
51
|
+
* @param volume Optional volume data parameter used to initialize size-dependent settings.
|
|
52
|
+
*/
|
|
53
|
+
constructor(volume) {
|
|
54
|
+
this.translation = new Vector3(0, 0, 0);
|
|
55
|
+
this.rotation = new Euler();
|
|
56
|
+
this.scale = new Vector3(1, 1, 1);
|
|
57
|
+
this.isOrtho = false;
|
|
58
|
+
this.viewAxis = Axis.NONE;
|
|
59
|
+
this.orthoScale = 1.0;
|
|
60
|
+
this.flipAxes = new Vector3(1, 1, 1);
|
|
61
|
+
this.maskChannelIndex = -1;
|
|
62
|
+
this.maskAlpha = 1.0;
|
|
63
|
+
this.gammaMin = 0.0;
|
|
64
|
+
this.gammaLevel = 1.0;
|
|
65
|
+
this.gammaMax = 1.0;
|
|
66
|
+
this.density = 0;
|
|
67
|
+
this.brightness = 0;
|
|
68
|
+
this.showBoundingBox = false;
|
|
69
|
+
this.bounds = {
|
|
70
|
+
bmin: new Vector3(-0.5, -0.5, -0.5),
|
|
71
|
+
bmax: new Vector3(0.5, 0.5, 0.5)
|
|
72
|
+
};
|
|
73
|
+
this.boundingBoxColor = [1.0, 1.0, 0.0];
|
|
74
|
+
this.primaryRayStepSize = 1.0;
|
|
75
|
+
this.secondaryRayStepSize = 1.0;
|
|
76
|
+
this.useInterpolation = true;
|
|
77
|
+
this.visible = true;
|
|
78
|
+
this.maxProjectMode = false;
|
|
79
|
+
// volume-dependent properties
|
|
80
|
+
if (volume) {
|
|
81
|
+
this.zSlice = Math.floor(volume.imageInfo.subregionSize.z / 2);
|
|
82
|
+
this.diffuse = new Array(volume.imageInfo.numChannels).fill([255, 255, 255]);
|
|
83
|
+
this.specular = new Array(volume.imageInfo.numChannels).fill([0, 0, 0]);
|
|
84
|
+
this.emissive = new Array(volume.imageInfo.numChannels).fill([0, 0, 0]);
|
|
85
|
+
this.glossiness = new Array(volume.imageInfo.numChannels).fill(0);
|
|
86
|
+
} else {
|
|
87
|
+
this.zSlice = 0;
|
|
88
|
+
this.diffuse = [[255, 255, 255]];
|
|
89
|
+
this.specular = [[0, 0, 0]];
|
|
90
|
+
this.emissive = [[0, 0, 0]];
|
|
91
|
+
this.glossiness = [0];
|
|
92
|
+
}
|
|
93
|
+
this.pixelSamplingRate = 0.75;
|
|
94
|
+
this.resolution = new Vector2(1, 1);
|
|
95
|
+
}
|
|
96
|
+
resizeWithVolume(volume) {
|
|
97
|
+
this.zSlice = Math.floor(volume.imageInfo.subregionSize.z / 2);
|
|
98
|
+
this.diffuse = new Array(volume.imageInfo.numChannels).fill([255, 255, 255]);
|
|
99
|
+
this.specular = new Array(volume.imageInfo.numChannels).fill([0, 0, 0]);
|
|
100
|
+
this.emissive = new Array(volume.imageInfo.numChannels).fill([0, 0, 0]);
|
|
101
|
+
this.glossiness = new Array(volume.imageInfo.numChannels).fill(0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Recursively compares two arrays.
|
|
106
|
+
* Non-array elements are compared using strict equality comparison.
|
|
107
|
+
*/
|
|
108
|
+
static compareArray(a1, a2) {
|
|
109
|
+
if (a1.length !== a2.length) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
for (let i = 0; i < a1.length; i++) {
|
|
113
|
+
const elem1 = a1[i];
|
|
114
|
+
const elem2 = a2[i];
|
|
115
|
+
if (elem1 instanceof Array && elem2 instanceof Array) {
|
|
116
|
+
if (!this.compareArray(elem1, elem2)) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
} else if (elem1 !== elem2) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Compares two VolumeRenderSettings objects.
|
|
128
|
+
* @returns true if both objects have identical settings.
|
|
129
|
+
*/
|
|
130
|
+
isEqual(o2) {
|
|
131
|
+
for (const key of Object.keys(this)) {
|
|
132
|
+
const v1 = this[key];
|
|
133
|
+
const v2 = o2[key];
|
|
134
|
+
if (v1 instanceof Array) {
|
|
135
|
+
if (!VolumeRenderSettings.compareArray(v1, v2)) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
} else if (v1 && v1.bmin !== undefined) {
|
|
139
|
+
// Bounds object
|
|
140
|
+
const bounds1 = v1;
|
|
141
|
+
const bounds2 = v2;
|
|
142
|
+
if (!bounds1.bmin.equals(bounds2.bmin) || !bounds1.bmax.equals(bounds2.bmax)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
} else if (v1 instanceof Vector3 || v1 instanceof Vector2 || v1 instanceof Euler) {
|
|
146
|
+
if (!v1.equals(v2)) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
// number, boolean, string
|
|
151
|
+
if (v1 !== v2) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Recursively creates and returns a deep copy of an array.
|
|
161
|
+
* Note: assumes values in the array are either primitives (numbers) or arrays of primitives.
|
|
162
|
+
*/
|
|
163
|
+
static deepCopyArray(a) {
|
|
164
|
+
const b = new Array(a.length);
|
|
165
|
+
for (let i = 0; i < a.length; i++) {
|
|
166
|
+
const val = a[i];
|
|
167
|
+
if (val instanceof Array) {
|
|
168
|
+
b[i] = this.deepCopyArray(val);
|
|
169
|
+
} else {
|
|
170
|
+
b[i] = val;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return b;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Creates a deep copy of this VolumeRenderSettings object.
|
|
178
|
+
* @param src The object to create a clone of.
|
|
179
|
+
* @returns a new VolumeRenderSettings object with identical fields that do not
|
|
180
|
+
* share references with the original settings object.
|
|
181
|
+
*/
|
|
182
|
+
clone() {
|
|
183
|
+
const dst = new VolumeRenderSettings(); // initialize with empty volume
|
|
184
|
+
for (const key of Object.keys(this)) {
|
|
185
|
+
const val = this[key];
|
|
186
|
+
if (val instanceof Array) {
|
|
187
|
+
dst[key] = VolumeRenderSettings.deepCopyArray(val);
|
|
188
|
+
} else if (key === "bounds") {
|
|
189
|
+
// must use key string here because Bounds is a type alias and not a class
|
|
190
|
+
dst.bounds.bmax = this.bounds.bmax.clone();
|
|
191
|
+
dst.bounds.bmin = this.bounds.bmin.clone();
|
|
192
|
+
} else if (val instanceof Vector3 || val instanceof Vector2 || val instanceof Euler) {
|
|
193
|
+
dst[key] = val.clone();
|
|
194
|
+
} else if (val instanceof String) {
|
|
195
|
+
dst[key] = "" + val;
|
|
196
|
+
} else {
|
|
197
|
+
// boolean, number, other primitives
|
|
198
|
+
dst[key] = val;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return dst;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// "basic" shaders which perform simple functions and appear multiple times in the codebase.
|
|
2
|
+
// These are the only shaders defined outside a dedicated GLSL file to make extra sure they appear
|
|
3
|
+
// only once in the built package.
|
|
4
|
+
|
|
5
|
+
/** Passthrough vertex shader for rendering to a buffer with a fullscreen quad */
|
|
6
|
+
export const renderToBufferVertShader = `
|
|
7
|
+
precision highp float;
|
|
8
|
+
precision highp int;
|
|
9
|
+
out vec2 vUv;
|
|
10
|
+
|
|
11
|
+
void main() {
|
|
12
|
+
vUv = uv;
|
|
13
|
+
gl_Position = vec4(position, 1.0);
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
/** Basic fragment shader that samples its output directly from a texture */
|
|
18
|
+
export const copyImageFragShader = `
|
|
19
|
+
precision highp float;
|
|
20
|
+
precision highp int;
|
|
21
|
+
precision highp sampler2D;
|
|
22
|
+
|
|
23
|
+
in vec2 vUv;
|
|
24
|
+
uniform sampler2D image;
|
|
25
|
+
|
|
26
|
+
void main() {
|
|
27
|
+
gl_FragColor = texture2D(image, vUv);
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const defaultColors = [[255, 0, 255], [255, 255, 255], [0, 255, 255]];
|
|
2
|
+
// 0 <= (h, s, v) <= 1
|
|
3
|
+
// returns 0 <= (r, g, b) <= 255 rounded to nearest integer
|
|
4
|
+
// you can also pass in just one arg as an object of {h, s, v} props.
|
|
5
|
+
function HSVtoRGB(h, s, v) {
|
|
6
|
+
let r, g, b;
|
|
7
|
+
let hh = 0;
|
|
8
|
+
if (arguments.length === 1) {
|
|
9
|
+
const hsv = h;
|
|
10
|
+
s = hsv.s, v = hsv.v, hh = hsv.h;
|
|
11
|
+
} else {
|
|
12
|
+
hh = h;
|
|
13
|
+
}
|
|
14
|
+
const i = Math.floor(hh * 6);
|
|
15
|
+
const f = hh * 6 - i;
|
|
16
|
+
const p = v * (1 - s);
|
|
17
|
+
const q = v * (1 - f * s);
|
|
18
|
+
const t = v * (1 - (1 - f) * s);
|
|
19
|
+
switch (i % 6) {
|
|
20
|
+
case 0:
|
|
21
|
+
r = v, g = t, b = p;
|
|
22
|
+
break;
|
|
23
|
+
case 1:
|
|
24
|
+
r = q, g = v, b = p;
|
|
25
|
+
break;
|
|
26
|
+
case 2:
|
|
27
|
+
r = p, g = v, b = t;
|
|
28
|
+
break;
|
|
29
|
+
case 3:
|
|
30
|
+
r = p, g = q, b = v;
|
|
31
|
+
break;
|
|
32
|
+
case 4:
|
|
33
|
+
r = t, g = p, b = v;
|
|
34
|
+
break;
|
|
35
|
+
case 5:
|
|
36
|
+
r = v, g = p, b = q;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 1993 Park-Miller LCG
|
|
43
|
+
function LCG(s) {
|
|
44
|
+
return function () {
|
|
45
|
+
s = Math.imul(48271, s) | 0 % 2147483647;
|
|
46
|
+
return (s & 2147483647) / 2147483648;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Use it like so:
|
|
50
|
+
const myrand = LCG(123);
|
|
51
|
+
|
|
52
|
+
// if index exceeds defaultColors start choosing random ones
|
|
53
|
+
// returns [r,g,b] 0-255 range
|
|
54
|
+
export const getColorByChannelIndex = index => {
|
|
55
|
+
if (!defaultColors[index]) {
|
|
56
|
+
defaultColors[index] = HSVtoRGB(myrand(), myrand() * 0.5 + 0.5, myrand() * 0.5 + 0.5);
|
|
57
|
+
}
|
|
58
|
+
return defaultColors[index];
|
|
59
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Vector2 } from "three";
|
|
2
|
+
/* babel-plugin-inline-import './shaders/pathtrace_denoise.frag' */
|
|
3
|
+
const denoiseFragmentShader = "precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\nuniform float gInvExposure;\nuniform int gDenoiseWindowRadius;\nuniform float gDenoiseNoise;\nuniform float gDenoiseInvWindowArea;\nuniform float gDenoiseWeightThreshold;\nuniform float gDenoiseLerpThreshold;\nuniform float gDenoiseLerpC;\nuniform vec2 gDenoisePixelSize;\n\nuniform sampler2D tTexture0;\nin vec2 vUv;\n\n// Used to convert from XYZ to linear RGB space\nconst mat3 XYZ_2_RGB = (mat3(\n 3.2404542, -1.5371385, -0.4985314,\n -0.9692660, 1.8760108, 0.0415560,\n 0.0556434, -0.2040259, 1.0572252\n));\n\nvec3 XYZtoRGB(vec3 xyz) {\n return xyz * XYZ_2_RGB;\n}\n\nvoid main()\n{\n vec4 pixelColor = texture(tTexture0, vUv);\n // TODO TONE MAP!!!!!!\n pixelColor.rgb = XYZtoRGB(pixelColor.rgb);\n\n pixelColor.rgb = 1.0-exp(-pixelColor.rgb*gInvExposure);\n pixelColor = clamp(pixelColor, 0.0, 1.0);\n\n /////////////////////\n /////////////////////\n /////////////////////\n /////////////////////\n //// DENOISING FILTER\n /////////////////////\n // see https://developer.download.nvidia.com/compute/cuda/1.1-Beta/x86_website/projects/imageDenoising/doc/imageDenoising.pdf\n /////////////////////\n vec4 clr00 = pixelColor;\n\n float fCount = 0.0;\n float SumWeights = 0.0;\n vec3 clr = vec3(0.0, 0.0, 0.0);\n\n vec2 uvsample = vUv;\n vec3 rgbsample;\n for (int i = -gDenoiseWindowRadius; i <= gDenoiseWindowRadius; i++) {\n for (int j = -gDenoiseWindowRadius; j <= gDenoiseWindowRadius; j++) {\n\n // boundary checking?\n vec3 clrIJ = texture(tTexture0, vUv + vec2(float(i)/gDenoisePixelSize.x, float(j)/gDenoisePixelSize.y)).rgb;\n //vec3 clrIJ = texelFetch(tTexture0, ivec2(gl_FragCoord.xy) + ivec2(i,j), 0).rgb;\n\n rgbsample = XYZtoRGB(clrIJ);\n // tone map!\n rgbsample = 1.0 - exp(-rgbsample * gInvExposure);\n rgbsample = clamp(rgbsample, 0.0, 1.0);\n\n clrIJ = rgbsample;\n\n float distanceIJ = (clr00.x-clrIJ.x)*(clr00.x-clrIJ.x) + (clr00.y-clrIJ.y)*(clr00.y-clrIJ.y) + (clr00.z-clrIJ.z)*(clr00.z-clrIJ.z);\n\n // gDenoiseNoise = 1/h^2\n //\n float weightIJ = exp(-(distanceIJ * gDenoiseNoise + float(i * i + j * j) * gDenoiseInvWindowArea));\n\n clr += (clrIJ * weightIJ);\n\n SumWeights += weightIJ;\n\n fCount += (weightIJ > gDenoiseWeightThreshold) ? gDenoiseInvWindowArea : 0.0;\n }\n }\n\n SumWeights = 1.0 / SumWeights;\n\n clr.rgb *= SumWeights;\n\n float LerpQ = (fCount > gDenoiseLerpThreshold) ? gDenoiseLerpC : 1.0f - gDenoiseLerpC;\n\n clr.rgb = mix(clr.rgb, clr00.rgb, LerpQ);\n clr.rgb = clamp(clr.rgb, 0.0, 1.0);\n\n pc_fragColor = vec4(clr.rgb, clr00.a);\n}\n";
|
|
4
|
+
export const denoiseFragmentShaderSrc = denoiseFragmentShader;
|
|
5
|
+
const DENOISE_WINDOW_RADIUS = 3;
|
|
6
|
+
export const denoiseShaderUniforms = () => ({
|
|
7
|
+
gInvExposure: {
|
|
8
|
+
type: "f",
|
|
9
|
+
value: 1.0 / (1.0 - 0.75)
|
|
10
|
+
},
|
|
11
|
+
gDenoiseWindowRadius: {
|
|
12
|
+
type: "i",
|
|
13
|
+
value: DENOISE_WINDOW_RADIUS
|
|
14
|
+
},
|
|
15
|
+
gDenoiseNoise: {
|
|
16
|
+
type: "f",
|
|
17
|
+
value: 0.05
|
|
18
|
+
},
|
|
19
|
+
gDenoiseInvWindowArea: {
|
|
20
|
+
type: "f",
|
|
21
|
+
value: 1.0 / ((2.0 * DENOISE_WINDOW_RADIUS + 1.0) * (2.0 * DENOISE_WINDOW_RADIUS + 1.0))
|
|
22
|
+
},
|
|
23
|
+
gDenoiseWeightThreshold: {
|
|
24
|
+
type: "f",
|
|
25
|
+
value: 0.1
|
|
26
|
+
},
|
|
27
|
+
gDenoiseLerpThreshold: {
|
|
28
|
+
type: "f",
|
|
29
|
+
value: 0.0
|
|
30
|
+
},
|
|
31
|
+
gDenoiseLerpC: {
|
|
32
|
+
type: "f",
|
|
33
|
+
value: 0.01
|
|
34
|
+
},
|
|
35
|
+
gDenoisePixelSize: {
|
|
36
|
+
type: "v2",
|
|
37
|
+
value: new Vector2(1, 1)
|
|
38
|
+
},
|
|
39
|
+
tTexture0: {
|
|
40
|
+
type: "t",
|
|
41
|
+
value: null
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MathUtils } from "three";
|
|
2
|
+
const spotlightSettings = Object.freeze({
|
|
3
|
+
angle: 36 * MathUtils.DEG2RAD,
|
|
4
|
+
castShadow: false,
|
|
5
|
+
color: 0xffffff,
|
|
6
|
+
intensity: 0.4,
|
|
7
|
+
position: {
|
|
8
|
+
x: -4,
|
|
9
|
+
y: 3.5,
|
|
10
|
+
z: 7
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
const ambientLightSettings = Object.freeze({
|
|
14
|
+
color: 0xffffff,
|
|
15
|
+
intensity: 0.6
|
|
16
|
+
});
|
|
17
|
+
const reflectedLightSettings = Object.freeze({
|
|
18
|
+
castShadow: false,
|
|
19
|
+
color: 0xff88aa,
|
|
20
|
+
intensity: 0.2,
|
|
21
|
+
position: {
|
|
22
|
+
x: 1,
|
|
23
|
+
y: -5,
|
|
24
|
+
z: 0
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const fillLightSettings = Object.freeze({
|
|
28
|
+
castShadow: false,
|
|
29
|
+
color: 0xe8d1a9,
|
|
30
|
+
intensity: 0.15,
|
|
31
|
+
position: {
|
|
32
|
+
x: 2.5,
|
|
33
|
+
y: 0.2,
|
|
34
|
+
z: 1.5
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
export default {
|
|
38
|
+
spotlightSettings,
|
|
39
|
+
ambientLightSettings,
|
|
40
|
+
reflectedLightSettings,
|
|
41
|
+
fillLightSettings
|
|
42
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Color, UniformsUtils, ShaderMaterial } from "three";
|
|
2
|
+
export const fresnelShaderSettings = {
|
|
3
|
+
bias: 0.4,
|
|
4
|
+
power: 2.0,
|
|
5
|
+
scale: 1.0
|
|
6
|
+
};
|
|
7
|
+
export const defaultMaterialSettings = {
|
|
8
|
+
shininess: 1000,
|
|
9
|
+
specularColor: 0x010101
|
|
10
|
+
};
|
|
11
|
+
export const transparentMaterialSettings = {
|
|
12
|
+
shininess: 1000,
|
|
13
|
+
specularColor: 0x010101,
|
|
14
|
+
transparency: {
|
|
15
|
+
bias: -0.2,
|
|
16
|
+
power: 2.0,
|
|
17
|
+
scale: 1.0
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const shaderLibrary = {
|
|
21
|
+
fresnel: {
|
|
22
|
+
uniforms: {
|
|
23
|
+
bias: {
|
|
24
|
+
value: 0.4
|
|
25
|
+
},
|
|
26
|
+
power: {
|
|
27
|
+
value: 2.0
|
|
28
|
+
},
|
|
29
|
+
scale: {
|
|
30
|
+
value: 1.0
|
|
31
|
+
},
|
|
32
|
+
uBaseColor: {
|
|
33
|
+
value: new Color(0xffffff)
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
vertexShader: `varying vec3 vNormal;
|
|
37
|
+
varying vec3 vI;
|
|
38
|
+
|
|
39
|
+
void main() {
|
|
40
|
+
|
|
41
|
+
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
|
|
42
|
+
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
|
43
|
+
|
|
44
|
+
vNormal = normalize( normalMatrix * normal );
|
|
45
|
+
|
|
46
|
+
vec4 I = modelViewMatrix * vec4(position, 1.0);
|
|
47
|
+
vI = normalize(mvPosition.xyz);
|
|
48
|
+
|
|
49
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
50
|
+
|
|
51
|
+
}`,
|
|
52
|
+
fragmentShader: `uniform float bias;
|
|
53
|
+
uniform float power;
|
|
54
|
+
uniform float scale;
|
|
55
|
+
uniform vec3 uBaseColor;
|
|
56
|
+
|
|
57
|
+
varying vec3 vNormal;
|
|
58
|
+
|
|
59
|
+
varying vec3 vI;
|
|
60
|
+
|
|
61
|
+
void main() {
|
|
62
|
+
float edge = 1.0 - max( dot( normalize( vNormal ), normalize( -vI ) ), 0.0);
|
|
63
|
+
edge = max(0.0, min(1.0, -bias + scale * pow((0.0 + edge), power)));
|
|
64
|
+
|
|
65
|
+
float threshold = 0.1;
|
|
66
|
+
if ( edge < threshold ) {
|
|
67
|
+
discard;
|
|
68
|
+
}
|
|
69
|
+
gl_FragColor = vec4( uBaseColor, edge );
|
|
70
|
+
return;
|
|
71
|
+
}`
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export function createShaderMaterial(id) {
|
|
75
|
+
const shader = shaderLibrary[id];
|
|
76
|
+
const u = UniformsUtils.clone(shader.uniforms);
|
|
77
|
+
const vs = shader.vertexShader;
|
|
78
|
+
const fs = shader.fragmentShader;
|
|
79
|
+
const material = new ShaderMaterial({
|
|
80
|
+
fragmentShader: fs,
|
|
81
|
+
uniforms: u,
|
|
82
|
+
vertexShader: vs
|
|
83
|
+
});
|
|
84
|
+
return material;
|
|
85
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* babel-plugin-inline-import './shaders/pathtrace_output.frag' */
|
|
2
|
+
const pathtraceOutputFragmentShader = "precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\nuniform float gInvExposure;\nuniform sampler2D tTexture0;\nin vec2 vUv;\n\n// Used to convert from XYZ to linear RGB space\nconst mat3 XYZ_2_RGB = (mat3(\n 3.2404542, -1.5371385, -0.4985314,\n -0.9692660, 1.8760108, 0.0415560,\n 0.0556434, -0.2040259, 1.0572252\n));\n\nvec3 XYZtoRGB(vec3 xyz) {\n return xyz * XYZ_2_RGB;\n}\n\nvoid main() {\n vec4 pixelColor = texture(tTexture0, vUv);\n\n pixelColor.rgb = XYZtoRGB(pixelColor.rgb);\n\n // pixelColor.rgb = pow(pixelColor.rgb, vec3(1.0/2.2));\n pixelColor.rgb = 1.0-exp(-pixelColor.rgb*gInvExposure);\n pixelColor = clamp(pixelColor, 0.0, 1.0);\n\n pc_fragColor = pixelColor; // sqrt(pixelColor);\n // out_FragColor = pow(pixelColor, vec4(1.0/2.2));\n}\n";
|
|
3
|
+
export const pathtraceOutputFragmentShaderSrc = pathtraceOutputFragmentShader;
|
|
4
|
+
export const pathtraceOutputShaderUniforms = () => ({
|
|
5
|
+
gInvExposure: {
|
|
6
|
+
type: "f",
|
|
7
|
+
value: 1.0 / (1.0 - 0.75)
|
|
8
|
+
},
|
|
9
|
+
tTexture0: {
|
|
10
|
+
type: "t",
|
|
11
|
+
value: null
|
|
12
|
+
}
|
|
13
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const scaleBarSVG = `
|
|
2
|
+
<svg enable-background="new 0 0 150 75.3" viewBox="0 0 150 75.3" xmlns="http://www.w3.org/2000/svg"
|
|
3
|
+
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
4
|
+
<linearGradient id="a" gradientTransform="matrix(1 0 0 -1 0 76)" gradientUnits="userSpaceOnUse" x1=".3035"
|
|
5
|
+
x2="149.6965" y1="19.59" y2="19.59">
|
|
6
|
+
<stop offset="0" stop-color="currentColor" stop-opacity="0" />
|
|
7
|
+
<stop offset=".16" stop-color="currentColor" />
|
|
8
|
+
<stop offset=".84" stop-color="currentColor" />
|
|
9
|
+
<stop offset="1" stop-color="currentColor" stop-opacity="0" />
|
|
10
|
+
</linearGradient>
|
|
11
|
+
<g fill="none">
|
|
12
|
+
<path d="m.5 39.1 149 34.6" stroke="url(#a)" stroke-miterlimit="10" stroke-width="4" />
|
|
13
|
+
<path d="m101.5 73.6 11-8.5" stroke="currentColor" stroke-miterlimit="10" stroke-width="4" />
|
|
14
|
+
<path d="m25.1 55.7 11-8.5" stroke="currentColor" stroke-miterlimit="10" stroke-width="4" />
|
|
15
|
+
<path
|
|
16
|
+
d="m36.2 40c0-12 5.3-12.4 10.5-14.2 5-1.7 17.3 1.9 22.6 1.7s8-5 8.4-11.8c-.4 6.9 3.4 13.8 10 16.3 8 3 17.9 2.7 23.2 7.3 7 6 3.3 12.2 1.6 19.1"
|
|
17
|
+
stroke="white" stroke-dasharray="2 4" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" />
|
|
18
|
+
</g>
|
|
19
|
+
</svg>
|
|
20
|
+
`;
|
|
21
|
+
export default scaleBarSVG;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export let TimeUnit = /*#__PURE__*/function (TimeUnit) {
|
|
2
|
+
TimeUnit[TimeUnit["MILLISECOND"] = 0] = "MILLISECOND";
|
|
3
|
+
TimeUnit[TimeUnit["SECOND"] = 1] = "SECOND";
|
|
4
|
+
TimeUnit[TimeUnit["MINUTE"] = 2] = "MINUTE";
|
|
5
|
+
TimeUnit[TimeUnit["HOUR"] = 3] = "HOUR";
|
|
6
|
+
TimeUnit[TimeUnit["DAY"] = 4] = "DAY";
|
|
7
|
+
return TimeUnit;
|
|
8
|
+
}({});
|
|
9
|
+
const recognizedTimeUnits = {
|
|
10
|
+
[TimeUnit.MILLISECOND]: new Set(["ms", "millisecond", "milliseconds"]),
|
|
11
|
+
[TimeUnit.SECOND]: new Set(["s", "sec", "second", "seconds"]),
|
|
12
|
+
[TimeUnit.MINUTE]: new Set(["m", "min", "minute", "minutes"]),
|
|
13
|
+
[TimeUnit.HOUR]: new Set(["h", "hr", "hour", "hours"]),
|
|
14
|
+
[TimeUnit.DAY]: new Set(["d", "day", "days"])
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parses an OME-compatible time unit into a TimeUnit enum.
|
|
19
|
+
* @param unit string unit
|
|
20
|
+
* @returns
|
|
21
|
+
* - `TimeUnit.MILLISECOND` if unit is "ms", "millisecond", or "milliseconds"
|
|
22
|
+
* - `TimeUnit.SECOND` if unit is "s", "sec", "second", or "seconds"
|
|
23
|
+
* - `TimeUnit.MINUTE` if unit is "m", "min", "minute", or "minutes"
|
|
24
|
+
* - `TimeUnit.HOUR` if unit is "h", "hr", "hour", or "hours"
|
|
25
|
+
* - `TimeUnit.DAY` if unit is "d", "day", or "days"
|
|
26
|
+
* - `undefined` if unit is not recognized
|
|
27
|
+
*/
|
|
28
|
+
export function parseTimeUnit(unit) {
|
|
29
|
+
for (const [timeUnit, recognizedUnits] of Object.entries(recognizedTimeUnits)) {
|
|
30
|
+
if (recognizedUnits.has(unit)) {
|
|
31
|
+
return timeUnit;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|