-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add neuroglancer url demo (#19)
* add neuroglancer url demo * update readme * export functions, add contact sheet and image series * try to fix up URL creation * remove useless stuff * contact sheet works, woo! * sliders are replaced * some comments
- Loading branch information
1 parent
07e43f1
commit 78753f6
Showing
13 changed files
with
1,213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"printWidth": 120, | ||
"trailingComma": "es5", | ||
"tabWidth": 4, | ||
"singleQuote": true, | ||
"semi": true, | ||
"singleAttributePerLine": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
## Neuroglancer URL Generator | ||
|
||
This is a demo for generating Neuroglancer URLs like the python POC [here](https://github.com/AllenInstitute/ome_zarr_converter/blob/main/src/ome_zarr_converter/neuroglancer/utils.py). | ||
|
||
### How to build / run | ||
1. `pnpm build` from the root directory of this project | ||
2. `pnpm install` in this directory | ||
3. `pnpm run demo` in this directory - this will produce `dst/neuroglancer-url.html` | ||
4. navigate to `dst/neuroglancer-url.html` and open it in your browser | ||
|
||
### The Code | ||
-- TBD -- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"name": "@alleninstitute/neuroglancer-url-demo", | ||
"version": "0.0.1", | ||
"contributors": [ | ||
{ | ||
"name": "Lane Sawyer", | ||
"email": "[email protected]" | ||
}, | ||
{ | ||
"name": "James Gerstenberger", | ||
"email": "[email protected]" | ||
}, | ||
{ | ||
"name": "Noah Shepard", | ||
"email": "[email protected]" | ||
}, | ||
{ | ||
"name": "Skyler Moosman", | ||
"email": "[email protected]" | ||
}, | ||
{ | ||
"name": "Su Li", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"license": "TBD", | ||
"type": "module", | ||
"main": "lib/index.js", | ||
"types": "lib/index.d.ts", | ||
"private": true, | ||
"scripts": { | ||
"preinstall": "npx only-allow pnpm", | ||
"demo": "esbuild src/demo.ts --bundle --outfile=dst/demo.js && cp public/* dst/", | ||
"typecheck": "tsc --noEmit" | ||
}, | ||
"devDependencies": { | ||
"esbuild": "^0.19.12", | ||
"typescript": "^5.3.3" | ||
}, | ||
"dependencies": { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<body style="width: 100vw; height: 100vh; overflow-x: hidden; display: flex; flex-direction: column"> | ||
<div style="width: 70%; height: 100%; display: flex; flex-direction: row"> | ||
<div | ||
id="urlArgEl" | ||
style="padding: 5px" | ||
></div> | ||
<div style="display: flex; flex-direction: column"> | ||
<div style="display: flex; flex-direction: row"> | ||
<button | ||
id="urlBtn" | ||
style="width: 100px; height: 20px" | ||
> | ||
Make URL | ||
</button> | ||
<button | ||
id="copyBtn" | ||
style="width: 100px; height: 20px" | ||
> | ||
Copy | ||
</button> | ||
</div> | ||
<p | ||
id="goodUrl" | ||
style="max-width: 500px; text-wrap: nowrap; overflow: hidden; text-overflow: ellipsis" | ||
></p> | ||
</div> | ||
</div> | ||
<div id="ngUrls"><h3>NG URLS</h3></div> | ||
<div id="contacts"><h3>Contact Sheets</h3></div> | ||
<div id="imageSeries"><h3>Image Series</h3></div> | ||
</body> | ||
</html> | ||
<script src="demo.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { neuroglancerUrl, finalizeConfig, getImageLayer, getShaderCode, urlFromConfig } from './utils'; | ||
|
||
export function getContactSheetUrl( | ||
srcUrl: string, | ||
imgName: string, | ||
omeZarrShape: number[], | ||
xMm: number, | ||
yMm: number, | ||
zMm: number, | ||
redMin: number, | ||
redMax: number, | ||
greenMin: number, | ||
greenMax: number, | ||
blueMin: number, | ||
blueMax: number, | ||
crossSectionScale: number = 0.5 | ||
): string { | ||
const config = getContactSheetConfig( | ||
srcUrl, | ||
imgName, | ||
omeZarrShape, | ||
xMm, | ||
yMm, | ||
zMm, | ||
redMin, | ||
redMax, | ||
greenMin, | ||
greenMax, | ||
blueMin, | ||
blueMax, | ||
crossSectionScale | ||
); | ||
|
||
console.log(config); | ||
return urlFromConfig(neuroglancerUrl, config); | ||
} | ||
|
||
function getContactSheetConfig( | ||
srcUrl: string, | ||
imgName: string, | ||
omeZarrShape: number[], | ||
xMm: number, | ||
yMm: number, | ||
zMm: number, | ||
redMin: number, | ||
redMax: number, | ||
greenMin: number, | ||
greenMax: number, | ||
blueMin: number, | ||
blueMax: number, | ||
crossSectionScale: number = 0.5 | ||
): Record<string, any> { | ||
const [shaderCode, shaderControls] = getShaderCode(redMin, redMax, greenMin, greenMax, blueMin, blueMax); | ||
|
||
const source = createSourceGrid(srcUrl, omeZarrShape, xMm, yMm, zMm); | ||
|
||
const imgLayer = getImageLayer(imgName, source, shaderCode, shaderControls); | ||
|
||
const config = finalizeConfig(imgLayer, imgName, xMm, yMm, zMm); | ||
|
||
config['layout'] = 'xy'; | ||
config['velocity'] = { z: { velocity: -10, atBoundary: 'reverse' } }; | ||
|
||
const nz = omeZarrShape[omeZarrShape.length - 3]; | ||
const sqrtNz = Math.round(Math.sqrt(nz)); | ||
const ny = omeZarrShape[omeZarrShape.length - 2]; | ||
const nx = omeZarrShape[omeZarrShape.length - 1]; | ||
config['position'] = [(nx * sqrtNz) / 2, (ny * sqrtNz) / 2, -(nz - 1)]; | ||
config['crossSectionScale'] = crossSectionScale; | ||
|
||
return config; | ||
} | ||
|
||
function createSourceGrid(srcUrl: string, omeZarrShape: number[], xMm: number, yMm: number, zMm: number): any[] { | ||
const nz = omeZarrShape[omeZarrShape.length - 3]; | ||
const ny = omeZarrShape[omeZarrShape.length - 2]; | ||
const nx = omeZarrShape[omeZarrShape.length - 1]; | ||
const sqrtNz = Math.ceil(Math.sqrt(nz)); | ||
const izToDxDy: { [key: number]: [number, number] } = {}; | ||
let dx = 0; | ||
let dy = 0; | ||
for (let iz = 0; iz < nz; iz++) { | ||
izToDxDy[iz] = [dx, dy]; | ||
dx++; | ||
if (dx >= sqrtNz) { | ||
dy++; | ||
dx = 0; | ||
} | ||
} | ||
|
||
const dimensions = { | ||
'c^': [1, ''], | ||
z: [0.001 * zMm, 'm'], | ||
y: [0.001 * yMm, 'm'], | ||
x: [0.001 * xMm, 'm'], | ||
}; | ||
|
||
const srcList = []; | ||
for (let iz = 0; iz < nz; iz++) { | ||
const src = { url: srcUrl }; | ||
const [dx, dy] = izToDxDy[iz]; | ||
const matrix = [ | ||
[1, 0, 0, 0, 0], | ||
[0, -1, 0, 0, -1 * iz], | ||
[0, 0, 1, 0, dy * ny], | ||
[0, 0, 0, 1, dx * nx], | ||
]; | ||
// @ts-expect-error | ||
src['transform'] = { | ||
outputDimensions: dimensions, | ||
matrix: matrix, | ||
}; | ||
|
||
// @ts-expect-error | ||
src['subsources'] = { | ||
default: true, | ||
}; | ||
// @ts-expect-error | ||
src['enableDefaultSubsources'] = false; | ||
|
||
srcList.push(src); | ||
} | ||
|
||
return srcList; | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import type { ImageSeriesData, NGData, RegularData } from './types'; | ||
import { getNeuroglancerUrl } from './utils'; | ||
import { data } from './data'; | ||
import { getContactSheetUrl } from './contact-sheet'; | ||
import { makeContactSheetElems, makeImageSeriesElems, makeNgUrlElems } from './url-elem-maker'; | ||
|
||
interface NeuroglancerUrl { | ||
srcUrl: string; | ||
imgName: string; | ||
xMm: number; | ||
yMm: number; | ||
zMm: number; | ||
redMin: number; | ||
redMax: number; | ||
greenMin: number; | ||
greenMax: number; | ||
blueMin: number; | ||
blueMax: number; | ||
crossSectionScale?: number; | ||
layout?: string; | ||
} | ||
|
||
const jsonFormat = { | ||
input: { | ||
srcUrl: '', | ||
imgName: '', | ||
xMm: 0, | ||
yMm: 0, | ||
zMm: 0, | ||
redMin: 0, | ||
redMax: 255, | ||
greenMin: 0, | ||
greenMax: 255, | ||
blueMin: 0, | ||
blueMax: 255, | ||
crossSectionScale: 50.0, | ||
layout: '4panel', | ||
}, | ||
output: 'neuroglancer.com/urlstuff', | ||
}; | ||
|
||
const defaultNeuroglancerUrl: NeuroglancerUrl = { | ||
srcUrl: '', | ||
imgName: '', | ||
xMm: 0, | ||
yMm: 0, | ||
zMm: 0, | ||
redMin: 0, | ||
redMax: 255, | ||
greenMin: 0, | ||
greenMax: 255, | ||
blueMin: 0, | ||
blueMax: 255, | ||
crossSectionScale: 50.0, | ||
layout: '4panel', | ||
}; | ||
function demoTime() { | ||
let imageSeries: ImageSeriesData[] = []; | ||
let contactSheets: RegularData[] = []; | ||
let ngURLs: RegularData[] = []; | ||
|
||
data.forEach((item) => { | ||
switch (item.function) { | ||
case 'get_image_series_grid_url': | ||
imageSeries.push(item as ImageSeriesData); | ||
break; | ||
case 'get_contact_sheet_url': | ||
contactSheets.push(item as RegularData); | ||
break; | ||
case 'get_neuroglancer_url': | ||
ngURLs.push(item as RegularData); | ||
break; | ||
} | ||
}); | ||
|
||
const argEl = document.getElementById('urlArgEl'); | ||
const btnEl = document.getElementById('urlBtn'); | ||
const outUrl = document.getElementById('goodUrl'); | ||
const copyBtn = document.getElementById('copyBtn'); | ||
const ngUrlParent = document.getElementById('ngUrls'); | ||
const contactParent = document.getElementById('contacts'); | ||
const imageParent = document.getElementById('imageSeries'); | ||
if (argEl && btnEl && outUrl && copyBtn && ngUrlParent && contactParent && imageParent) { | ||
const fields = document.createElement('ol'); | ||
const fieldItems = Object.entries(defaultNeuroglancerUrl).map(([name, val]) => { | ||
const listEl = document.createElement('li'); | ||
const pEl = document.createElement('p'); | ||
const text = document.createElement('input'); | ||
text.type = 'text'; | ||
text.value = val; | ||
// @ts-expect-error | ||
text.onchange = (e) => (defaultNeuroglancerUrl[name] = val); | ||
|
||
pEl.innerText = name; | ||
listEl.id = name; | ||
|
||
listEl.appendChild(pEl); | ||
listEl.appendChild(text); | ||
fields.appendChild(listEl); | ||
}); | ||
argEl.appendChild(fields); | ||
btnEl.addEventListener('click', (e) => { | ||
const { | ||
srcUrl, | ||
imgName, | ||
xMm, | ||
yMm, | ||
zMm, | ||
redMin, | ||
redMax, | ||
greenMin, | ||
greenMax, | ||
blueMin, | ||
blueMax, | ||
crossSectionScale, | ||
layout, | ||
} = defaultNeuroglancerUrl; | ||
outUrl.innerText = getNeuroglancerUrl( | ||
srcUrl, | ||
imgName, | ||
xMm, | ||
yMm, | ||
zMm, | ||
redMin, | ||
redMax, | ||
greenMin, | ||
greenMax, | ||
blueMin, | ||
blueMax, | ||
crossSectionScale, | ||
layout | ||
); | ||
}); | ||
copyBtn.addEventListener('click', () => { | ||
navigator.clipboard.writeText(outUrl.innerText); | ||
}); | ||
|
||
// These URLs are example URLs that acted as test cases for my formatting | ||
makeNgUrlElems(ngURLs, ngUrlParent); | ||
makeContactSheetElems(contactSheets, contactParent); | ||
makeImageSeriesElems(imageSeries, imageParent); | ||
} | ||
} | ||
|
||
demoTime(); |
Oops, something went wrong.