Skip to content

Commit 81b2d6b

Browse files
authored
feat: React Examples Components (DT-7024) (#49)
1 parent 0c63670 commit 81b2d6b

23 files changed

+1913
-1869
lines changed

examples/dzi.html

Lines changed: 0 additions & 17 deletions
This file was deleted.

examples/index.html

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<!doctype html>
22
<html>
33
<body>
4-
EXAMPLES
5-
<br />
6-
<ul>
7-
<li><a href="/dzi">Deep Zoom Image</a><br /></li>
8-
<li><a href="/omezarr">OMEZARR</a><br /></li>
9-
<li><a href="/layers">Layers</a><br /></li>
10-
</ul>
4+
<div id="app"></div>
5+
<script
6+
type="module"
7+
src="/src/index.tsx"
8+
></script>
119
</body>
1210
</html>

examples/omezarr.html

Lines changed: 0 additions & 17 deletions
This file was deleted.

examples/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@
4242
"vite": "^5.3.5"
4343
},
4444
"dependencies": {
45-
"@alleninstitute/vis-geometry": "workspace:*",
46-
"@alleninstitute/vis-scatterbrain": "workspace:*",
4745
"@alleninstitute/vis-dzi": "workspace:*",
46+
"@alleninstitute/vis-geometry": "workspace:*",
4847
"@alleninstitute/vis-omezarr": "workspace:*",
48+
"@alleninstitute/vis-scatterbrain": "workspace:*",
4949
"@czi-sds/components": "^20.0.1",
5050
"@emotion/css": "^11.11.2",
5151
"@emotion/react": "^11.11.4",
@@ -61,6 +61,7 @@
6161
"lodash": "^4.17.21",
6262
"react": "^18.3.0",
6363
"react-dom": "^18.3.0",
64+
"react-router": "^7.0.2",
6465
"regl": "^2.1.0",
6566
"zarrita": "0.4.0-next.14"
6667
}

examples/public/layers.html

Lines changed: 0 additions & 17 deletions
This file was deleted.

examples/src/app.tsx

Lines changed: 22 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,27 @@
11
import React from 'react';
2-
import { SliceViewLayer } from './ui/slice-ui';
3-
import type { Demo } from './layers';
4-
import { AnnotationGrid } from './ui/annotation-grid';
5-
import { ContactSheetUI } from './ui/contact-sheet';
6-
import { ScatterplotUI } from './ui/scatterplot-ui';
7-
import { Button } from '@czi-sds/components';
2+
import { BrowserRouter, Route, Routes } from 'react-router';
3+
import { Home } from './home';
4+
import { OmezarrDemo } from './omezarr/omezarr-demo';
5+
import { DziDemo } from './dzi/dzi-demo';
86

9-
export function AppUi(props: { demo: Demo }) {
10-
const { demo } = props;
7+
export function App() {
118
return (
12-
<div>
13-
<Button
14-
onClick={() => {
15-
demo.requestSnapshot(3000);
16-
}}
17-
>
18-
{'📸'}
19-
</Button>
20-
<label>{`Layer ${demo.selectedLayer}`}</label>
21-
<Button
22-
onClick={() => {
23-
demo.selectLayer(demo.selectedLayer - 1);
24-
}}
25-
>
26-
{'<-'}
27-
</Button>
28-
<Button
29-
onClick={() => {
30-
demo.selectLayer(demo.selectedLayer + 1);
31-
}}
32-
>
33-
{'->'}
34-
</Button>
35-
<LayerUi demo={demo} />
36-
</div>
9+
<BrowserRouter>
10+
<Routes>
11+
<Route
12+
index
13+
element={<Home />}
14+
/>
15+
<Route
16+
path="/dzi"
17+
element={<DziDemo />}
18+
/>
19+
<Route
20+
path="/omezarr"
21+
element={<OmezarrDemo />}
22+
/>
23+
<Route path="/layers" />
24+
</Routes>
25+
</BrowserRouter>
3726
);
3827
}
39-
function LayerUi(props: { demo: Demo }) {
40-
const { demo } = props;
41-
const layer = demo.layers[demo.selectedLayer];
42-
if (layer) {
43-
switch (layer.type) {
44-
case 'annotationGrid':
45-
return <AnnotationGrid demo={demo} />;
46-
case 'volumeGrid':
47-
return <ContactSheetUI demo={demo} />;
48-
case 'volumeSlice':
49-
return <SliceViewLayer demo={demo} />;
50-
case 'scatterplot':
51-
case 'scatterplotGrid':
52-
return <ScatterplotUI demo={demo} />;
53-
default:
54-
return null;
55-
}
56-
}
57-
return <SliceViewLayer demo={props.demo} />;
58-
}

examples/src/common/camera.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,43 @@
1-
import type { box2D, vec2 } from '@alleninstitute/vis-geometry';
1+
import { Box2D, Vec2, type box2D, type vec2 } from '@alleninstitute/vis-geometry';
22

33
// a basic camera, for viewing slices
44
export type Camera = {
55
readonly view: box2D; // a view in 'data space'
66
readonly screen: vec2; // what that view projects to in display space, aka pixels
77
readonly projection: 'webImage' | 'cartesian';
88
};
9+
/**
10+
* Zooms relative to your current mouse position
11+
* @param view box2d in dataspace that is mapped to the canvas
12+
* @param screenSize in pixels
13+
* @param zoomScale
14+
* @param mousePos mouse position in pixels
15+
*/
16+
export function zoom(view: box2D, screenSize: vec2, zoomScale: number, mousePos: vec2): box2D {
17+
// translate mouse pos to data space
18+
// offset divided by screen size gives us a percentage of the canvas where the mouse is
19+
// multiply percentage by view size to make it data space
20+
// add offset of the min corner so that the position takes into account any box offset
21+
const zoomPoint: vec2 = Vec2.add(view.minCorner, Vec2.mul(Vec2.div(mousePos, screenSize), Box2D.size(view)));
22+
23+
// scale the box with our new zoom point as the center
24+
const newView = Box2D.translate(
25+
Box2D.scale(Box2D.translate(view, Vec2.scale(zoomPoint, -1)), [zoomScale, zoomScale]),
26+
zoomPoint
27+
);
28+
29+
return newView;
30+
}
31+
32+
/**
33+
*
34+
* @param view box2d in dataspace that is mapped to the canvas
35+
* @param screenSize
36+
* @param mousePos mouse position in pixels
37+
*/
38+
export function pan(view: box2D, screenSize: vec2, mousePos: vec2): box2D {
39+
const relativePos = Vec2.div(Vec2.mul(mousePos, [-1, -1]), screenSize);
40+
const scaledOffset = Vec2.mul(relativePos, Box2D.size(view));
41+
const newView = Box2D.translate(view, scaledOffset);
42+
return newView;
43+
}

examples/src/dzi/app.tsx

Lines changed: 0 additions & 6 deletions
This file was deleted.

examples/src/dzi/decode-dzi.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import type { DziImage } from '@alleninstitute/vis-dzi';
2+
3+
// DZI files will come with XML or JSON to give you important information such as the width, height, and format.
4+
// Below is a function for parsing an xml with that data, althought sometimes it comes in JSON format.
5+
// At the end of the file you can see two examples of the metadata format you might see, one as XML and another as JSON
6+
/**
7+
* This function helps decode xml metadata for a dzi file.
8+
* @param s the contents of the url param - expected to be an XML doc describing the DZI image
9+
* @param url location of the .dzi file
10+
* @returns formatted dzi image data
11+
*/
12+
function decodeDziXml(s: string, url: string): DziImage | undefined {
13+
const parser = new DOMParser();
14+
const doc = parser.parseFromString(s, 'text/xml');
15+
// catch any errors if the xml is malformed
16+
const err = doc.querySelector('Error');
17+
if (err) return undefined;
18+
19+
if (doc) {
20+
const img = doc.getElementsByTagName('Image')[0];
21+
const size = doc.getElementsByTagName('Size')[0];
22+
// format: as jpg/png
23+
// overlap: how much overlap there is between images so that we can compensate the rendering
24+
// tile size: how big in pixels each tile is
25+
const [format, overlap, tileSize] = [
26+
img.getAttribute('Format'),
27+
img.getAttribute('Overlap'),
28+
img.getAttribute('TileSize'),
29+
];
30+
if (size && format && overlap && tileSize) {
31+
// width and height of the image, so we can properly size the view
32+
const width = size.getAttribute('Width');
33+
const height = size.getAttribute('Height');
34+
35+
// the url ends with .dzi to denote that we're reaching for a dzi file
36+
// in order to get the images from that url we need to remove the .dzi
37+
// and replace it with _files/ so that the image viewer knows where to look
38+
const dataLoc = url.split('.dzi')[0];
39+
if (width && height && dataLoc) {
40+
return {
41+
imagesUrl: `${dataLoc}_files/`,
42+
format: format as 'jpeg' | 'png' | 'jpg' | 'JPG' | 'PNG',
43+
overlap: Number.parseInt(overlap, 10),
44+
tileSize: Number.parseInt(tileSize, 10),
45+
size: {
46+
width: Number.parseInt(width, 10),
47+
height: Number.parseInt(height, 10),
48+
},
49+
};
50+
}
51+
}
52+
}
53+
54+
return undefined;
55+
}
56+
57+
/* Example XML
58+
<?xml version="1.0" encoding="UTF-8"?>
59+
<Image xmlns="http://schemas.microsoft.com/deepzoom/2008"
60+
Format="jpg"
61+
Overlap="2"
62+
TileSize="256" >
63+
<Size Height="9221"
64+
Width="7026"/>
65+
</Image>
66+
*/
67+
68+
/* Example JSON
69+
{
70+
"Image": {
71+
"xmlns": "http://schemas.microsoft.com/deepzoom/2008",
72+
"Format": "jpg",
73+
"Overlap": "2",
74+
"TileSize": "256",
75+
"Size": {
76+
"Height": "9221",
77+
"Width": "7026"
78+
}
79+
}
80+
}
81+
*/

examples/src/dzi/double.tsx

Lines changed: 0 additions & 81 deletions
This file was deleted.

0 commit comments

Comments
 (0)