Skip to content

Commit

Permalink
feat: Port idetik/core
Browse files Browse the repository at this point in the history
  • Loading branch information
tihuan committed Feb 26, 2025
1 parent 83c2417 commit 6103daa
Show file tree
Hide file tree
Showing 92 changed files with 5,805 additions and 14 deletions.
1 change: 1 addition & 0 deletions packages/core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
56 changes: 56 additions & 0 deletions packages/core/examples/image2d_from_omezarr3d_f32/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Web Viewer</title>
<style>
html,
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
#canvas {
width: 100%;
height: 100%;
min-height: 0;
}
#controls-div {
display: flex;
flex-direction: column;
width: 50%;
align-self: center;
margin-top: 2em;
margin-bottom: 2em;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="controls-div">
<label for="slider-min" id="label-min">Min</label>
<input
type="range"
id="slider-min"
min="-1e-5"
max="1e-5"
step="1e-6"
value="-1e-5"
/>
<label for="slider-max" id="label-max">Max</label>
<input
type="range"
id="slider-max"
min="-1e-5"
max="1e-5"
step="1e-6"
value="1e-5"
/>
</div>
<script type="module" src="./main.ts"></script>
</body>
</html>
67 changes: 67 additions & 0 deletions packages/core/examples/image2d_from_omezarr3d_f32/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
LayerManager,
ImageLayer,
OrthographicCamera,
WebGLRenderer,
OmeZarrImageSource,
} from "@";
import { PanZoomControls } from "@/objects/cameras/controls";

const sliderMin = document.getElementById("slider-min") as HTMLInputElement;
const labelMin = document.getElementById("label-min") as HTMLLabelElement;
const sliderMax = document.getElementById("slider-max") as HTMLInputElement;
const labelMax = document.getElementById("label-max") as HTMLLabelElement;

const url =
"https://files.cryoetdataportal.cziscience.com/10444/24apr23a_Position_12/Reconstructions/VoxelSpacing4.990/Tomograms/100/24apr23a_Position_12.zarr";
const pixelScale = 4.99;
const layerManager = new LayerManager();
const renderer = new WebGLRenderer("#canvas");
const camera = new OrthographicCamera(
0,
pixelScale * 1264,
0,
pixelScale * 1264
);
const controls = new PanZoomControls(camera, camera.position);
renderer.setControls(controls);
const region = [{ dimension: "z", index: pixelScale * 120 }];

const source = new OmeZarrImageSource(url);
const layer = new ImageLayer({ source, region });
layerManager.add(layer);

const onMinChange = () => {
const minValue = sliderMin.valueAsNumber;
const maxValue = sliderMax.valueAsNumber;
if (minValue >= maxValue) {
sliderMin.value = (maxValue - Number(sliderMin.step)).toString();
} else {
labelMin.innerText = `Min: ${minValue.toString()}`;
layer.setChannelProps([{ contrastLimits: [minValue, maxValue] }]);
}
};

const onMaxChange = () => {
const maxValue = sliderMax.valueAsNumber;
const minValue = sliderMin.valueAsNumber;
if (maxValue <= minValue) {
sliderMax.value = (minValue + Number(sliderMax.step)).toString();
} else {
labelMax.innerText = `Max: ${maxValue.toString()}`;
layer.setChannelProps([{ contrastLimits: [minValue, maxValue] }]);
}
};

sliderMin.addEventListener("input", onMinChange);
sliderMax.addEventListener("input", onMaxChange);

onMinChange();
onMaxChange();

function animate() {
renderer.render(layerManager, camera);
requestAnimationFrame(animate);
}

animate();
56 changes: 56 additions & 0 deletions packages/core/examples/image2d_from_omezarr4d_hcs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Web Viewer</title>
<style>
html,
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
#main {
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
}
#canvas {
flex: 1;
min-width: 0;
}
#options {
flex: 0;
display: flex;
flex-direction: column;
}
#well-div,
#image-div {
display: flex;
flex-direction: row;
align-items: center;
}
</style>
</head>
<body>
<div id="main">
<div id="options">
<div id="well-div">
<label for="well">Well:</label>
<select id="well"></select>
</div>
<div id="image-div">
<label for="image">Image:</label>
<select id="image"></select>
</div>
</div>
<canvas id="canvas"></canvas>
</div>
<script type="module" src="./main.ts"></script>
</body>
</html>
94 changes: 94 additions & 0 deletions packages/core/examples/image2d_from_omezarr4d_hcs/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
LayerManager,
ImageLayer,
OrthographicCamera,
WebGLRenderer,
OmeZarrImageSource,
} from "@";
import { PanZoomControls } from "@/objects/cameras/controls";
import {
loadOmeZarrPlate,
loadOmeZarrWell,
} from "@/data/ome_zarr_hcs_metadata_loader";

// The following data comes from Zenodo: https://zenodo.org/records/11262587
// First download and unpack it. Then host the containing directory locally
// with something like:
// http-server --cors -p 8080
const plateUrl =
"http://localhost:8080/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr";
const plate = await loadOmeZarrPlate(plateUrl);
console.debug("plate", plate);
if (plate.plate === undefined) {
throw new Error(`No plate found: ${plate}`);
}
const wellPaths = plate.plate.wells.map((well) => well.path);
const wellSelector = document.querySelector("#well") as HTMLSelectElement;
wellPaths.forEach((path) => {
const option = document.createElement("option");
option.value = path;
option.text = path;
wellSelector.appendChild(option);
});

const layerManager = new LayerManager();
const renderer = new WebGLRenderer("#canvas");
const camera = new OrthographicCamera(0, 840, 0, 360);
const controls = new PanZoomControls(camera, camera.position);
renderer.setControls(controls);
const region = [
{ dimension: "c", index: { start: 0, stop: 3 } },
{ dimension: "z", index: 0 },
];

const imageSelector = document.querySelector("#image") as HTMLSelectElement;

const onImageChange = async () => {
console.debug("onImageChange: ", imageSelector.value);
layerManager.layers.length = 0;
const imageUrl =
plateUrl + "/" + wellSelector.value + "/" + imageSelector.value;
const source = new OmeZarrImageSource(imageUrl);
const layer = new ImageLayer({
source,
region,
channelProps: [
// color and contrast limits manually looked up in the zarr omero metadata
{ color: [0, 1, 1], contrastLimits: [110, 800] },
{ color: [1, 0, 1], contrastLimits: [110, 250] },
{ color: [1, 1, 0], contrastLimits: [110, 800] },
],
});
layerManager.add(layer);
};

const onWellChange = async () => {
console.debug("onWellChange: ", wellSelector.value);
layerManager.layers.length = 0;
const path = wellSelector.value;
const well = await loadOmeZarrWell(plateUrl, path);
console.debug("well", well);
if (well.well === undefined) {
throw new Error(`No well found: ${well}`);
}
const imagePaths = well.well.images.map((image) => image.path);
imageSelector.innerHTML = "";
imagePaths.forEach((path) => {
const option = document.createElement("option");
option.value = path;
option.text = path;
imageSelector.appendChild(option);
});
await onImageChange();
};

wellSelector.addEventListener("change", onWellChange);
imageSelector.addEventListener("change", onImageChange);
await onWellChange();

function animate() {
renderer.render(layerManager, camera);
requestAnimationFrame(animate);
}

animate();
26 changes: 26 additions & 0 deletions packages/core/examples/image2d_from_omezarr5d_u16/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Web Viewer</title>
<style>
html,
body {
margin: 0;
padding: 0;
display: flex;
width: 100%;
height: 100%;
}
canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="module" src="./main.ts"></script>
</body>
</html>
44 changes: 44 additions & 0 deletions packages/core/examples/image2d_from_omezarr5d_u16/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
LayerManager,
ImageLayer,
WebGLRenderer,
OmeZarrImageSource,
OrthographicCamera,
} from "@";
import { AxesLayer } from "@/layers/axes_layer";
import { PanZoomControls } from "@/objects/cameras/controls";

const url =
"https://public.czbiohub.org/royerlab/zebrahub/imaging/single-objective/ZSNS001.ome.zarr/";
const layerManager = new LayerManager();
const renderer = new WebGLRenderer("#canvas");
const left = 150;
const right = 950;
const top = 100;
const bottom = 900;
const camera = new OrthographicCamera(left, right, top, bottom);
const controls = new PanZoomControls(camera, camera.position);
renderer.setControls(controls);

// Source is 5D, so provide indices at 3 dimensions to project to 2D.
// Also specify a subregion in x and y to exercise that part of the API.
const source = new OmeZarrImageSource(url);
const region = [
{ dimension: "t", index: 400 },
{ dimension: "c", index: 0 },
{ dimension: "z", index: 300 },
{ dimension: "y", index: { start: top, stop: bottom } },
{ dimension: "x", index: { start: left, stop: right } },
];
const channelProps = [{ contrastLimits: [0, 255] as [number, number] }];
const layer = new ImageLayer({ source, region, channelProps });
const axes = new AxesLayer({ length: 2000, width: 0.01 });
layerManager.add(layer);
layerManager.add(axes);

function animate() {
renderer.render(layerManager, camera);
requestAnimationFrame(animate);
}

animate();
26 changes: 26 additions & 0 deletions packages/core/examples/image2d_from_omezarr5d_u8/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Web Viewer</title>
<style>
html,
body {
margin: 0;
padding: 0;
display: flex;
width: 100%;
height: 100%;
}
canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="module" src="./main.ts"></script>
</body>
</html>
Loading

0 comments on commit 6103daa

Please sign in to comment.