From 01ae988971ac4de9d5d994798f8444e6aeaaa2e0 Mon Sep 17 00:00:00 2001
From: Skyler Moosman <8845503+TheMooseman@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:29:15 -0700
Subject: [PATCH 1/8] chore: Update Scatterbrain Version (#32)
---
packages/scatterbrain/package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/scatterbrain/package.json b/packages/scatterbrain/package.json
index 682be44..4486ad7 100644
--- a/packages/scatterbrain/package.json
+++ b/packages/scatterbrain/package.json
@@ -1,6 +1,6 @@
{
"name": "@alleninstitute/vis-scatterbrain",
- "version": "0.0.3",
+ "version": "0.0.4",
"contributors": [
{
"name": "Lane Sawyer",
@@ -58,4 +58,4 @@
"lodash": "^4.17.21",
"regl": "^2.1.0"
}
-}
\ No newline at end of file
+}
From 7107135a73e515bd1383b7386d1eadfd481b0513 Mon Sep 17 00:00:00 2001
From: Lane Sawyer
Date: Fri, 1 Nov 2024 10:02:08 -0700
Subject: [PATCH 2/8] chore: CI workflow [DT-5996] (#25)
---
.github/workflows/ci.yml | 129 +++++++++++++++++++++++++++++++++
LICENSE.md | 4 +-
examples/package.json | 2 +-
package.json | 4 +-
packages/dzi/package.json | 2 +-
packages/dzi/src/loader.ts | 16 ++--
packages/dzi/src/renderer.ts | 2 +-
packages/geometry/package.json | 2 +-
8 files changed, 146 insertions(+), 15 deletions(-)
create mode 100644 .github/workflows/ci.yml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..7176b50
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,129 @@
+name: CI
+
+on: [pull_request]
+
+concurrency:
+ # Cancels workflows that are already running when a new one is triggered for a branch
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 9
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build
+ run: pnpm build
+
+ format:
+ name: Format
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 9
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Format
+ run: pnpm fmt:check
+
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+ # Skip job until we get a linter added
+ if: false
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 9
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Lint
+ run: echo "Add pnpm lint command here"
+
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 9
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Test
+ run: pnpm test
+
+ typecheck:
+ name: Typecheck
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 9
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build
+ run: pnpm build
+
+ - name: Typecheck
+ run: pnpm typecheck
diff --git a/LICENSE.md b/LICENSE.md
index 0745566..45200b7 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright 2024 Allen Institute
+Copyright 2024 Allen Institute
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
@@ -8,4 +8,4 @@ Redistribution and use in source and binary forms, with or without modification,
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/examples/package.json b/examples/package.json
index fb913ea..19aa863 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -64,4 +64,4 @@
"regl": "^2.1.0",
"zarrita": "0.4.0-next.14"
}
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index d5007ae..e9950d9 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,9 @@
"build": "pnpm -r run --no-bail build",
"watch": "pnpm -r run watch",
"fmt": "npx prettier . --write",
- "fmt:check": "npx prettier . --check"
+ "fmt:check": "npx prettier . --check",
+ "test": "pnpm -r run --no-bail test:ci",
+ "typecheck": "pnpm -r --no-bail run typecheck"
},
"devDependencies": {
"@parcel/packager-ts": "2.12.0",
diff --git a/packages/dzi/package.json b/packages/dzi/package.json
index dd7e021..dfdc05b 100644
--- a/packages/dzi/package.json
+++ b/packages/dzi/package.json
@@ -55,4 +55,4 @@
"lodash": "^4.17.21",
"regl": "^2.1.0"
}
-}
\ No newline at end of file
+}
diff --git a/packages/dzi/src/loader.ts b/packages/dzi/src/loader.ts
index 0edda84..1ebb9b3 100644
--- a/packages/dzi/src/loader.ts
+++ b/packages/dzi/src/loader.ts
@@ -54,13 +54,13 @@ export function getVisibleTiles(dzi: DziImage, camera: { view: box2D; screenSize
const availableTiles = tilesInLayer(dzi, layer);
const baseLayer = findLargestSingleTileLayer(dzi);
- const baseIndex: TileIndex = { col: 0, row: 0 }
+ const baseIndex: TileIndex = { col: 0, row: 0 };
const baseTile: DziTile = {
index: baseIndex,
layer: baseLayer,
relativeLocation: Box2D.create([0, 0], [1, 1]),
- url: tileUrl(dzi, baseLayer, baseIndex)
- }
+ url: tileUrl(dzi, baseLayer, baseIndex),
+ };
// note that the tile boxes are in pixels relative to the layer in which they reside
// the given view is assumed to be a parameter (in the space [0:1]) of the image as a whole
@@ -82,7 +82,7 @@ export function getVisibleTiles(dzi: DziImage, camera: { view: box2D; screenSize
// filter out tiles which are not in view
})
.filter((t) => !!Box2D.intersection(t.relativeLocation, camera.view));
- return (baseLayer < layer) ? [baseTile, ...tiles] : tiles;
+ return baseLayer < layer ? [baseTile, ...tiles] : tiles;
}
/**
* NOTE: THE REMAINDER OF THIS FILE IS EXPORTED ONLY FOR TESTING PURPOSES
@@ -97,13 +97,13 @@ export function firstSuitableLayer(imageWidth: number, screenWidth: number) {
}
/**
- *
- * @param dzi
+ *
+ * @param dzi
* @returns the index of the largest layer which contains only a single tile
- *
+ *
*/
function findLargestSingleTileLayer(dzi: DziImage): number {
- return Math.floor(Math.log2(dzi.tileSize))
+ return Math.floor(Math.log2(dzi.tileSize));
}
export function tileWithOverlap(total: number, step: number, overlap: number): Interval[] {
const blocks: Interval[] = [];
diff --git a/packages/dzi/src/renderer.ts b/packages/dzi/src/renderer.ts
index 4962a41..1937ad0 100644
--- a/packages/dzi/src/renderer.ts
+++ b/packages/dzi/src/renderer.ts
@@ -45,7 +45,7 @@ export function buildDziRenderer(regl: REGL.Regl): Renderer { }, // no private resources to destroy
+ destroy: () => {}, // no private resources to destroy
cacheKey: (item, _requestKey, _data, _settings) => `${item.url}`,
fetchItemContent: fetchDziTile,
getVisibleItems: (dzi, settings) => {
diff --git a/packages/geometry/package.json b/packages/geometry/package.json
index f7ed500..00783d6 100644
--- a/packages/geometry/package.json
+++ b/packages/geometry/package.json
@@ -52,4 +52,4 @@
"typescript": "^5.3.3",
"vitest": "^1.4.0"
}
-}
\ No newline at end of file
+}
From 0e3f6759748c219206dbad797dac1fcf2714ff4b Mon Sep 17 00:00:00 2001
From: noah
Date: Tue, 12 Nov 2024 11:07:39 -0800
Subject: [PATCH 3/8] Fix: flickery frames due to leaky event handling (#36)
---
packages/scatterbrain/package.json | 4 +--
.../scatterbrain/src/abstract/async-frame.ts | 36 ++++++++-----------
.../src/abstract/render-server.ts | 9 ++++-
packages/scatterbrain/src/dataset-cache.ts | 1 +
4 files changed, 26 insertions(+), 24 deletions(-)
diff --git a/packages/scatterbrain/package.json b/packages/scatterbrain/package.json
index 4486ad7..07935cd 100644
--- a/packages/scatterbrain/package.json
+++ b/packages/scatterbrain/package.json
@@ -1,6 +1,6 @@
{
"name": "@alleninstitute/vis-scatterbrain",
- "version": "0.0.4",
+ "version": "0.0.5",
"contributors": [
{
"name": "Lane Sawyer",
@@ -58,4 +58,4 @@
"lodash": "^4.17.21",
"regl": "^2.1.0"
}
-}
+}
\ No newline at end of file
diff --git a/packages/scatterbrain/src/abstract/async-frame.ts b/packages/scatterbrain/src/abstract/async-frame.ts
index 2879831..a2e0eb3 100644
--- a/packages/scatterbrain/src/abstract/async-frame.ts
+++ b/packages/scatterbrain/src/abstract/async-frame.ts
@@ -92,7 +92,7 @@ export function beginFrame<
renderItem(itemToRender, dataset, settings, maybe);
}
};
- const reportStatus = (event: AsyncFrameEvent, synchronous: boolean = false) => {
+ const reportStatus = (event: AsyncFrameEvent, synchronous: boolean) => {
// we want to report our status, however the flow of events can be confusing -
// our callers anticipate an asynchronous (long running) frame to be started,
// but there are scenarios in which the whole thing is completely synchronous
@@ -102,11 +102,11 @@ export function beginFrame<
if (synchronous) {
lifecycleCallback(event);
} else {
- Promise.resolve().then(() => lifecycleCallback(event));
+ Promise.resolve().then(() => lifecycleCallback(event))
}
};
- const doWorkOnQueue = (intervalId: number) => {
+ const doWorkOnQueue = (intervalId: number, synchronous: boolean = false) => {
// try our best to cleanup if something goes awry
const startWorkTime = performance.now();
const cleanupOnError = (err: unknown) => {
@@ -117,7 +117,7 @@ export function beginFrame<
abort.abort(err);
clearInterval(intervalId);
// pass the error somewhere better:
- reportStatus({ status: 'error', error: err }, true);
+ reportStatus({ status: 'error', error: err }, synchronous);
};
while (mutableCache.getNumPendingTasks() < Math.max(maximumInflightAsyncTasks, 1)) {
// We know there are items in the queue because of the check above, so we assert the type exist
@@ -131,7 +131,7 @@ export function beginFrame<
requestsForItem(itemToRender, dataset, settings, abort.signal),
partial(fancy, itemToRender),
toCacheKey,
- () => reportStatus({ status: 'progress', dataset, renderedItems: [itemToRender] }, true)
+ () => reportStatus({ status: 'progress', dataset, renderedItems: [itemToRender] }, synchronous)
);
if (result !== undefined) {
// put this cancel callback in a list where we can invoke if something goes wrong
@@ -152,30 +152,24 @@ export function beginFrame<
if (mutableCache.getNumPendingTasks() < 1) {
// we do want to wait for that last in-flight task to actually finish though:
clearInterval(intervalId);
- reportStatus({ status: 'finished' });
+ reportStatus({ status: 'finished' }, synchronous);
}
return;
}
};
- const interval = setInterval(() => doWorkOnQueue(interval), queueProcessingIntervalMS);
- // do some work right now...
+ reportStatus({ status: 'begin' }, true)
+ const interval = setInterval(() => doWorkOnQueue(interval), queueProcessingIntervalMS);
if (queue.length > 0) {
- reportStatus({ status: 'begin' }, true);
- doWorkOnQueue(interval);
- // return a function to allow our caller to cancel the frame - guaranteed that no settings/data will be
- // touched/referenced after cancellation, unless the author of render() did some super weird bad things
- return {
- cancelFrame: (reason?: string) => {
- taskCancelCallbacks.forEach((cancelMe) => cancelMe());
- abort.abort(new DOMException(reason, 'AbortError'));
- clearInterval(interval);
- reportStatus({ status: 'cancelled' });
- },
- };
+ doWorkOnQueue(interval, false)
}
return {
- cancelFrame: () => {},
+ cancelFrame: (reason?: string) => {
+ taskCancelCallbacks.forEach((cancelMe) => cancelMe());
+ abort.abort(new DOMException(reason, 'AbortError'));
+ clearInterval(interval);
+ reportStatus({ status: 'cancelled' }, true);
+ }
};
}
diff --git a/packages/scatterbrain/src/abstract/render-server.ts b/packages/scatterbrain/src/abstract/render-server.ts
index 0f1acda..664b538 100644
--- a/packages/scatterbrain/src/abstract/render-server.ts
+++ b/packages/scatterbrain/src/abstract/render-server.ts
@@ -168,12 +168,19 @@ export class RenderServer {
}
};
this.clients.set(client, {
- frame: renderFn(image, this.cache, hijack),
+ frame: null,
image,
copyBuffer,
resolution,
updateRequested: null,
});
+ // this is worded rather awkwardly, because sometimes the frameLifecycle object returned by renderFn() represents
+ // a frame that is already finished!
+ // this is a good thing for performance, but potentially confusing - so we do our book-keeping before we actually start rendering:
+ const aboutToStart = this.clients.get(client); // this is the record we just put into the clients map - TS just wants to be sure it really exists:
+ if (aboutToStart) {
+ aboutToStart.frame = renderFn(image, this.cache, hijack)
+ }
}
}
}
diff --git a/packages/scatterbrain/src/dataset-cache.ts b/packages/scatterbrain/src/dataset-cache.ts
index 6cab4c8..8ff6cbb 100644
--- a/packages/scatterbrain/src/dataset-cache.ts
+++ b/packages/scatterbrain/src/dataset-cache.ts
@@ -225,6 +225,7 @@ export class AsyncDataCache Promise>,
use: (items: Record) => void,
toCacheKey: (semanticKey: SemanticKey) => CacheKey,
+ // TODO: consider removing taskFinished - it would be more simple to let the caller handle this in their use() function
taskFinished?: () => void
): cancelFn | undefined {
const keys: SemanticKey[] = Object.keys(workingSet) as SemanticKey[];
From 1fe8fbdbb83a7d75368aa1bb8512c309d1b83256 Mon Sep 17 00:00:00 2001
From: Lane Sawyer
Date: Tue, 12 Nov 2024 15:13:20 -0800
Subject: [PATCH 4/8] chore: Pull request template [DT-5996] (#38)
---
.github/PULL_REQUEST_TEMPLATE.md | 38 +++++++++++++++++++
packages/scatterbrain/package.json | 2 +-
.../scatterbrain/src/abstract/async-frame.ts | 8 ++--
.../src/abstract/render-server.ts | 2 +-
4 files changed, 44 insertions(+), 6 deletions(-)
create mode 100644 .github/PULL_REQUEST_TEMPLATE.md
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..971bd0b
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,38 @@
+# WIP Steps
+
+_This section is optional if the PR is fully ready for review_
+
+- Create the PR as a "draft" PR
+- Add [WIP] to the title
+- Use the template below to ask for specific feedback
+- [ ] @name: Could you look at \_\_\_? I'm trying to do... (etc.)
+
+# What
+
+Replace this with 1-2 line Description of what feature, bugfix, chore does the PR contain the code for.
+
+# How
+
+Replace this txt describing what kind of technical overlaying code changes were introduced here.
+
+# Screenshots
+
+_This section is optional if there are no visible changes_
+
+- If possible add screenshots of the visible additions in the UI.
+- If there are changes in the UI, add **Before** and **After** Screenshots for quick overview.
+- If there was a Figma design, add a link to that here as well.
+- Hint : Drag and Drop any images you want to add to the PR. Also you can create a gif of an interactive version and add that!
+
+# PR Checklist
+
+- [ ] Is your PR title following our conventional commit naming recommendations?
+- [ ] Have you filled in the PR Description Template?
+- [ ] Is your branch up to date with the latest in `main`?
+- [ ] Do the CI checks pass successfully?
+- [ ] Have you smoke tested the example applications?
+- [ ] Did you check that the changes meet accessibility standards?
+- [ ] Have you tested the application on these browsers?
+ - [ ] Chrome (Fully supported)
+ - [ ] Firefox (Major bug fixes supported)
+ - [ ] Safari (Major bug fixes supported)
diff --git a/packages/scatterbrain/package.json b/packages/scatterbrain/package.json
index 07935cd..5376029 100644
--- a/packages/scatterbrain/package.json
+++ b/packages/scatterbrain/package.json
@@ -58,4 +58,4 @@
"lodash": "^4.17.21",
"regl": "^2.1.0"
}
-}
\ No newline at end of file
+}
diff --git a/packages/scatterbrain/src/abstract/async-frame.ts b/packages/scatterbrain/src/abstract/async-frame.ts
index a2e0eb3..ea32ba4 100644
--- a/packages/scatterbrain/src/abstract/async-frame.ts
+++ b/packages/scatterbrain/src/abstract/async-frame.ts
@@ -102,7 +102,7 @@ export function beginFrame<
if (synchronous) {
lifecycleCallback(event);
} else {
- Promise.resolve().then(() => lifecycleCallback(event))
+ Promise.resolve().then(() => lifecycleCallback(event));
}
};
@@ -158,10 +158,10 @@ export function beginFrame<
}
};
- reportStatus({ status: 'begin' }, true)
+ reportStatus({ status: 'begin' }, true);
const interval = setInterval(() => doWorkOnQueue(interval), queueProcessingIntervalMS);
if (queue.length > 0) {
- doWorkOnQueue(interval, false)
+ doWorkOnQueue(interval, false);
}
return {
cancelFrame: (reason?: string) => {
@@ -169,7 +169,7 @@ export function beginFrame<
abort.abort(new DOMException(reason, 'AbortError'));
clearInterval(interval);
reportStatus({ status: 'cancelled' }, true);
- }
+ },
};
}
diff --git a/packages/scatterbrain/src/abstract/render-server.ts b/packages/scatterbrain/src/abstract/render-server.ts
index 664b538..2bf3b32 100644
--- a/packages/scatterbrain/src/abstract/render-server.ts
+++ b/packages/scatterbrain/src/abstract/render-server.ts
@@ -179,7 +179,7 @@ export class RenderServer {
// this is a good thing for performance, but potentially confusing - so we do our book-keeping before we actually start rendering:
const aboutToStart = this.clients.get(client); // this is the record we just put into the clients map - TS just wants to be sure it really exists:
if (aboutToStart) {
- aboutToStart.frame = renderFn(image, this.cache, hijack)
+ aboutToStart.frame = renderFn(image, this.cache, hijack);
}
}
}
From c925c2854ac34f9c5c775feebbdf75f9a81e3d42 Mon Sep 17 00:00:00 2001
From: noah
Date: Tue, 12 Nov 2024 16:16:56 -0800
Subject: [PATCH 5/8] Fix: still flickery (#37)
---
packages/scatterbrain/package.json | 2 +-
packages/scatterbrain/src/abstract/async-frame.ts | 11 +++++++----
packages/scatterbrain/src/abstract/render-server.ts | 13 ++++++++++++-
3 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/packages/scatterbrain/package.json b/packages/scatterbrain/package.json
index 5376029..2b27a38 100644
--- a/packages/scatterbrain/package.json
+++ b/packages/scatterbrain/package.json
@@ -1,6 +1,6 @@
{
"name": "@alleninstitute/vis-scatterbrain",
- "version": "0.0.5",
+ "version": "0.0.6",
"contributors": [
{
"name": "Lane Sawyer",
diff --git a/packages/scatterbrain/src/abstract/async-frame.ts b/packages/scatterbrain/src/abstract/async-frame.ts
index ea32ba4..4e0a5a9 100644
--- a/packages/scatterbrain/src/abstract/async-frame.ts
+++ b/packages/scatterbrain/src/abstract/async-frame.ts
@@ -87,12 +87,15 @@ export function beginFrame<
const abort = new AbortController();
const queue: Item[] = [...items];
const taskCancelCallbacks: Array<() => void> = [];
- const fancy = (itemToRender: Item, maybe: Record) => {
- if (isPrepared(maybe)) {
+ const renderItemWrapper = (itemToRender: Item, maybe: Record) => {
+ if (isPrepared(maybe) && !abort.signal.aborted) {
renderItem(itemToRender, dataset, settings, maybe);
}
};
const reportStatus = (event: AsyncFrameEvent, synchronous: boolean) => {
+ if (event.status !== 'cancelled' && abort.signal.aborted) {
+ return;
+ }
// we want to report our status, however the flow of events can be confusing -
// our callers anticipate an asynchronous (long running) frame to be started,
// but there are scenarios in which the whole thing is completely synchronous
@@ -129,7 +132,7 @@ export function beginFrame<
try {
const result = mutableCache.cacheAndUse(
requestsForItem(itemToRender, dataset, settings, abort.signal),
- partial(fancy, itemToRender),
+ partial(renderItemWrapper, itemToRender),
toCacheKey,
() => reportStatus({ status: 'progress', dataset, renderedItems: [itemToRender] }, synchronous)
);
@@ -165,8 +168,8 @@ export function beginFrame<
}
return {
cancelFrame: (reason?: string) => {
- taskCancelCallbacks.forEach((cancelMe) => cancelMe());
abort.abort(new DOMException(reason, 'AbortError'));
+ taskCancelCallbacks.forEach((cancelMe) => cancelMe());
clearInterval(interval);
reportStatus({ status: 'cancelled' }, true);
},
diff --git a/packages/scatterbrain/src/abstract/render-server.ts b/packages/scatterbrain/src/abstract/render-server.ts
index 2bf3b32..e2db538 100644
--- a/packages/scatterbrain/src/abstract/render-server.ts
+++ b/packages/scatterbrain/src/abstract/render-server.ts
@@ -131,6 +131,7 @@ export class RenderServer {
private prepareToRenderToClient(client: Client) {
const previousEntry = this.clients.get(client);
if (previousEntry) {
+ previousEntry.updateRequested = null;
// the client is mutable - so every time we get a request, we have to check to see if it got resized
if (client.width !== previousEntry.resolution[0] || client.height !== previousEntry.resolution[1]) {
// handle resizing by deleting previously allocated resources:
@@ -151,6 +152,8 @@ export class RenderServer {
const clientFrame = this.clients.get(client);
if (clientFrame && clientFrame.frame) {
clientFrame.frame.cancelFrame();
+ this.regl.clear({ framebuffer: clientFrame.image, color: [0, 0, 0, 0], depth: 0 });
+ clientFrame.updateRequested = null;
}
const { image, resolution, copyBuffer } = this.prepareToRenderToClient(client);
const hijack: RenderCallback = (e) => {
@@ -179,7 +182,15 @@ export class RenderServer {
// this is a good thing for performance, but potentially confusing - so we do our book-keeping before we actually start rendering:
const aboutToStart = this.clients.get(client); // this is the record we just put into the clients map - TS just wants to be sure it really exists:
if (aboutToStart) {
- aboutToStart.frame = renderFn(image, this.cache, hijack);
+ const frame = renderFn(image, this.cache, hijack);
+ if (frame) {
+ aboutToStart.frame = {
+ cancelFrame: (reason?: string) => {
+ frame.cancelFrame(reason);
+ aboutToStart.updateRequested = null;
+ },
+ };
+ }
}
}
}
From 9631bf7b3c45f404770b29290d35ff0ef8b0bebe Mon Sep 17 00:00:00 2001
From: noah
Date: Wed, 13 Nov 2024 13:58:12 -0800
Subject: [PATCH 6/8] chore: Update version (#39)
---
packages/dzi/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/dzi/package.json b/packages/dzi/package.json
index dfdc05b..80b1cfd 100644
--- a/packages/dzi/package.json
+++ b/packages/dzi/package.json
@@ -1,6 +1,6 @@
{
"name": "@alleninstitute/vis-dzi",
- "version": "0.0.3",
+ "version": "0.0.4",
"contributors": [
{
"name": "Lane Sawyer",
From 45e717503f999384bce6599483115e472f5854c4 Mon Sep 17 00:00:00 2001
From: noah
Date: Wed, 20 Nov 2024 12:37:48 -0800
Subject: [PATCH 7/8] ome-zarr slice-view renderer package (#34)
Co-authored-by: Lane Sawyer
Co-authored-by: Skyler Moosman <8845503+TheMooseman@users.noreply.github.com>
---
examples/index.html | 1 +
examples/omezarr.html | 17 ++
examples/package.json | 1 +
.../loaders/ome-zarr/fetchSlice.worker.ts | 2 +-
.../loaders/ome-zarr/sliceWorkerPool.ts | 2 +-
.../react}/render-server-provider.tsx | 3 +-
examples/src/data-renderers/versa-renderer.ts | 2 +-
.../src/data-renderers/volumeSliceRenderer.ts | 2 +-
.../src/data-sources/ome-zarr/planar-slice.ts | 4 +-
.../src/data-sources/ome-zarr/slice-grid.ts | 4 +-
examples/src/dzi/double.tsx | 2 +-
examples/src/dzi/dziView.tsx | 2 +-
examples/src/layers.ts | 2 +-
examples/src/omezarr/app.tsx | 26 +++
examples/src/omezarr/omezarr.ts | 5 +
examples/src/omezarr/sliceview.tsx | 99 ++++++++
packages/omezarr/package.json | 58 +++++
packages/omezarr/src/index.ts | 21 ++
packages/omezarr/src/sliceview/loader.ts | 107 +++++++++
.../omezarr/src/sliceview/slice-renderer.ts | 139 ++++++++++++
.../omezarr/src/sliceview/tile-renderer.ts | 99 ++++++++
.../omezarr/src}/zarr-data.ts | 16 +-
packages/omezarr/tsconfig.json | 13 ++
pnpm-lock.yaml | 212 ++++--------------
24 files changed, 649 insertions(+), 190 deletions(-)
create mode 100644 examples/omezarr.html
rename examples/src/{dzi => common/react}/render-server-provider.tsx (81%)
create mode 100644 examples/src/omezarr/app.tsx
create mode 100644 examples/src/omezarr/omezarr.ts
create mode 100644 examples/src/omezarr/sliceview.tsx
create mode 100644 packages/omezarr/package.json
create mode 100644 packages/omezarr/src/index.ts
create mode 100644 packages/omezarr/src/sliceview/loader.ts
create mode 100644 packages/omezarr/src/sliceview/slice-renderer.ts
create mode 100644 packages/omezarr/src/sliceview/tile-renderer.ts
rename {examples/src/common/loaders/ome-zarr => packages/omezarr/src}/zarr-data.ts (93%)
create mode 100644 packages/omezarr/tsconfig.json
diff --git a/examples/index.html b/examples/index.html
index 4b9835d..46832e4 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -5,6 +5,7 @@
+
+
+
+