Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions .github/workflows/build_test_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,29 @@ jobs:
run: yarn typecheck
- name: Check for cyclic dependencies in frontend
run: yarn check-cyclic-dependencies
- name: Run frontend tests
run: yarn run vitest run --config vitest_spec.config.ts --coverage.enabled true
- name: Download Coverage Artifacts
# Can not use actions/download-artifact@v4 because it does not support downloading artifacts from other GA runs
run: |
gh_last_success_run_id=$(gh run list --workflow "CI Pipeline" --json conclusion,headBranch,databaseId --branch master --jq 'first(.[] | select(.conclusion | contains("success"))) | .databaseId')
[ -z "$gh_last_success_run_id" ] && echo "No successful run found" && exit 1 || true
gh run download $gh_last_success_run_id -n $ARTIFACT_NAME -D $OUTPUT_DIR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARTIFACT_NAME: vitest-coverage-master
OUTPUT_DIR: coverage-master
- name: 'Report Coverage'
uses: davelosert/vitest-coverage-report-action@v2
with:
comment-on: none # alternative: pr
json-summary-compare-path: coverage-master/coverage-summary.json
- name: "Upload Coverage"
if: github.ref == 'refs/heads/master'
uses: actions/upload-artifact@v4
with:
name: vitest-coverage-master
path: coverage
# - name: Run frontend tests
# run: yarn run vitest run --config vitest_spec.config.ts --coverage.enabled true
# - name: Download Coverage Artifacts
# # Can not use actions/download-artifact@v4 because it does not support downloading artifacts from other GA runs
# run: |
# gh_last_success_run_id=$(gh run list --workflow "CI Pipeline" --json conclusion,headBranch,databaseId --branch master --jq 'first(.[] | select(.conclusion | contains("success"))) | .databaseId')
# [ -z "$gh_last_success_run_id" ] && echo "No successful run found" && exit 1 || true
# gh run download $gh_last_success_run_id -n $ARTIFACT_NAME -D $OUTPUT_DIR
# env:
# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ARTIFACT_NAME: vitest-coverage-master
# OUTPUT_DIR: coverage-master
# - name: 'Report Coverage'
# uses: davelosert/vitest-coverage-report-action@v2
# with:
# comment-on: none # alternative: pr
# json-summary-compare-path: coverage-master/coverage-summary.json
# - name: "Upload Coverage"
# if: github.ref == 'refs/heads/master'
# uses: actions/upload-artifact@v4
# with:
# name: vitest-coverage-master
# path: coverage

backend-tests:
runs-on: ubuntu-22.04
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const datasetNames = [
"Multi-Channel-Test",
"connectome_file_test_dataset",
"kiwi", // This dataset is rotated and translated.
"test-extreme-anisotropy",
];

type DatasetName = string;
Expand All @@ -63,6 +64,7 @@ const viewOverrides: Record<string, string> = {
connectome_file_test_dataset:
'{"position":[102,109,60],"mode":"orthogonal","zoomStep":0.734,"stateByLayer":{"segmentation":{"connectomeInfo":{"connectomeName":"connectome","agglomerateIdsToImport":[1]}}}}',
kiwi: "1191,1112,21,0,8.746",
"test-extreme-anisotropy": "100,100,75,0,6.727",
};
const datasetConfigOverrides: Record<string, PartialDatasetConfiguration> = {
ROI2017_wkw: {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions frontend/javascripts/viewer/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,14 @@ export enum TreeTypeEnum {
export type TreeType = keyof typeof TreeTypeEnum;
export const NODE_ID_REF_REGEX = /#([0-9]+)/g;
export const POSITION_REF_REGEX = /#\(([0-9]+,[0-9]+,[0-9]+)\)/g;
const VIEWPORT_WIDTH = 376;
const VIEWPORT_WIDTH = 1;
const ARBITRARY_VIEWPORT_WIDTH = 376;

// ARBITRARY_CAM_DISTANCE has to be calculated such that with cam
// angle 45°, the plane of width Constants.VIEWPORT_WIDTH fits exactly in the
// viewport.
export const ARBITRARY_CAM_DISTANCE = VIEWPORT_WIDTH / 2 / Math.tan(((Math.PI / 180) * 45) / 2);
export const ARBITRARY_CAM_DISTANCE =
ARBITRARY_VIEWPORT_WIDTH / 2 / Math.tan(((Math.PI / 180) * 45) / 2);

export const ensureSmallerEdge = false;
export const Unicode = {
Expand Down Expand Up @@ -307,6 +309,7 @@ const Constants = {
BUCKET_WIDTH: 32,
BUCKET_SIZE: 32 ** 3,
VIEWPORT_WIDTH,
ARBITRARY_VIEWPORT_WIDTH,
DEFAULT_NAVBAR_HEIGHT: 48,
BANNER_HEIGHT: 38,
// For reference, the area of a large brush size (let's say, 300px) corresponds to
Expand Down
14 changes: 12 additions & 2 deletions frontend/javascripts/viewer/geometries/arbitrary_plane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@ class ArbitraryPlane {
const textureMaterial = this.materialFactory.setup().getMaterial();
this.plane = adaptPlane(
new Mesh(
new PlaneGeometry(constants.VIEWPORT_WIDTH, constants.VIEWPORT_WIDTH, 1, 1),
new PlaneGeometry(
constants.ARBITRARY_VIEWPORT_WIDTH,
constants.ARBITRARY_VIEWPORT_WIDTH,
1,
1,
),
textureMaterial,
),
);
Expand Down Expand Up @@ -176,7 +181,12 @@ class ArbitraryPlane {
debuggerMaterial.transparent = true;
shaderEditor.addMaterial(99, debuggerMaterial);
const debuggerPlane = new Mesh(
new PlaneGeometry(constants.VIEWPORT_WIDTH, constants.VIEWPORT_WIDTH, 50, 50),
new PlaneGeometry(
constants.ARBITRARY_VIEWPORT_WIDTH,
constants.ARBITRARY_VIEWPORT_WIDTH,
50,
50,
),
debuggerMaterial,
);
return debuggerPlane;
Expand Down
13 changes: 7 additions & 6 deletions frontend/javascripts/viewer/geometries/plane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import PlaneMaterialFactory, {
type PlaneShaderMaterial,
} from "viewer/geometries/materials/plane_material_factory";
import { listenToStoreProperty } from "viewer/model/helpers/listener_helpers";
import { getBaseVoxelInUnit } from "viewer/model/scaleinfo";

// A subdivision of 100 means that there will be 100 segments per axis
// and thus 101 vertices per axis (i.e., the vertex shader is executed 101**2).
Expand Down Expand Up @@ -68,10 +69,6 @@ class Plane {
this.planeID = planeID;
this.displayCrosshair = true;
this.lastScaleFactors = [-1, -1];
// VIEWPORT_WIDTH means that the plane should be that many voxels wide in the
// dimension with the highest mag. In all other dimensions, the plane
// is smaller in voxels, so that it is squared in nm.
// --> scaleInfo.baseVoxel
this.baseRotation = new Euler(0, 0, 0);
this.bindToEvents();
this.createMeshes();
Expand Down Expand Up @@ -163,8 +160,12 @@ class Plane {
}
this.lastScaleFactors[0] = xFactor;
this.lastScaleFactors[1] = yFactor;
// Account for the dataset scale to match one world space coordinate to one dataset scale unit.
const scaleVector: Vector3 = V3.multiply([xFactor, yFactor, 1], this.datasetScaleFactor);
// Scale to base voxel space which is the same coordinate space the cameras use
const baseVoxelUnit = getBaseVoxelInUnit(this.datasetScaleFactor);
const scaleVector: Vector3 = V3.multiply(
[xFactor, yFactor, 1],
[baseVoxelUnit, baseVoxelUnit, baseVoxelUnit],
);
this.getMeshes().map((mesh) => mesh.scale.set(...scaleVector));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function determineBucketsForFlight(
abortLimit?: number,
): void {
const queryMatrix = M4x4.scale1(1, matrix);
const width = constants.VIEWPORT_WIDTH;
const width = constants.ARBITRARY_VIEWPORT_WIDTH;
const halfWidth = width / 2;
const cameraVertex: Vector3 = [0, 0, -sphericalCapRadius];
const fallbackZoomStep = logZoomStep + 1;
Expand Down
14 changes: 10 additions & 4 deletions frontend/javascripts/viewer/model/reducers/flycam_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,17 @@ function rotateReducer(
});
}

// export function getMatrixScale(voxelSize: Vector3): Vector3 {
// const scale = [1 / voxelSize[0], 1 / voxelSize[1], 1 / voxelSize[2]];
// const maxScale = Math.max(scale[0], scale[1], scale[2]);
// const multi = 1 / maxScale;
// return [multi * scale[0], multi * scale[1], multi * scale[2]];
// }

export function getMatrixScale(voxelSize: Vector3): Vector3 {
const scale = [1 / voxelSize[0], 1 / voxelSize[1], 1 / voxelSize[2]];
const maxScale = Math.max(scale[0], scale[1], scale[2]);
const multi = 1 / maxScale;
return [multi * scale[0], multi * scale[1], multi * scale[2]];
const baseVoxelSize = 1;
// const baseVoxelSize = Math.min(...voxelSize);
return [baseVoxelSize / voxelSize[0], baseVoxelSize / voxelSize[1], baseVoxelSize / voxelSize[2]];
}

function resetMatrix(matrix: Matrix4x4, voxelSize: Vector3) {
Expand Down
7 changes: 4 additions & 3 deletions frontend/javascripts/viewer/model/scaleinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { UnitsMap } from "libs/format_utils";
import type { VoxelSize } from "types/api_types";
import { LongUnitToShortUnitMap, type UnitShort, type Vector3 } from "viewer/constants";

export function getBaseVoxelInUnit(voxelSizeFactor: Vector3): number {
// base voxel should be a cube with highest mag
return Math.min(...voxelSizeFactor);
export function getBaseVoxelInUnit(_voxelSizeFactor: Vector3): number {
// base voxel should be a cube with highest resolution
return 1;
// return Math.min(...voxelSizeFactor);
}

export function voxelToVolumeInUnit(
Expand Down
14 changes: 14 additions & 0 deletions frontend/javascripts/viewer/shaders/main_data_shaders.glsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ flat in vec2 index;
flat in uint outputMagIdx[<%= globalLayerCount %>];
flat in uint outputSeed[<%= globalLayerCount %>];
flat in float outputAddress[<%= globalLayerCount %>];
flat in float useBucketBorderVertexOptimization;
in vec4 worldCoord;
in vec4 modelCoord;
in mat4 savedModelMatrix;
Expand Down Expand Up @@ -438,6 +439,8 @@ flat out vec2 index;
flat out uint outputMagIdx[<%= globalLayerCount %>];
flat out uint outputSeed[<%= globalLayerCount %>];
flat out float outputAddress[<%= globalLayerCount %>];
// bool varyings are not supported
flat out float useBucketBorderVertexOptimization;

uniform bool is3DViewBeingRendered;
uniform vec3 representativeMagForVertexAlignment;
Expand Down Expand Up @@ -476,6 +479,8 @@ void main() {
<% }
}) %>

useBucketBorderVertexOptimization = 1.0;

vUv = uv;
modelCoord = vec4(position, 1.0);
savedModelMatrix = modelMatrix;
Expand All @@ -486,6 +491,7 @@ void main() {
// The same goes when all layers of the dataset are transformed.
// This shouldn't really impact the performance as isFlycamRotated is a uniform.
if(isFlycamRotated || !<%= isOrthogonal %> || doAllLayersHaveTransforms) {
useBucketBorderVertexOptimization = 0.0;
return;
}
// Remember the original z position, since it can subtly diverge in the
Expand Down Expand Up @@ -522,6 +528,14 @@ void main() {
vec2 d = transDim(vec3(bucketWidth) * representativeMagForVertexAlignment).xy;

vec3 voxelSizeFactorUVW = transDim(voxelSizeFactor);
vec2 viewportWidthInVoxelsUV = abs(worldCoordBottomRight.xy - worldCoordTopLeft.xy) / voxelSizeFactorUVW.xy;
// If the plane subdivision vertices cannot possibly cover all bucket borders, the optimization must not be used.
// Otherwise, rendering artifacts will occur (partially rendered planes).
if ((d * PLANE_SUBDIVISION).x < viewportWidthInVoxelsUV.x || (d * PLANE_SUBDIVISION).y < viewportWidthInVoxelsUV.y) {
useBucketBorderVertexOptimization = 0.0;
return;
}

vec3 voxelSizeFactorInvertedUVW = transDim(voxelSizeFactorInverted);
vec3 transWorldCoord = transDim(worldCoord.xyz);

Expand Down
6 changes: 3 additions & 3 deletions frontend/javascripts/viewer/shaders/segmentation.glsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export const convertCellIdToRGB: ShaderModule = {
float zoomAdaption = ceil(zoomValue);
vec3 worldCoordUVW = coordScaling * getUnrotatedWorldCoordUVW() / zoomAdaption;

float baseVoxelSize = min(min(voxelSizeFactor.x, voxelSizeFactor.y), voxelSizeFactor.z);
float baseVoxelSize = 1.; // min(min(voxelSizeFactor.x, voxelSizeFactor.y), voxelSizeFactor.z);
vec3 anisotropyFactorUVW = transDim(voxelSizeFactor) / baseVoxelSize;
worldCoordUVW.x = worldCoordUVW.x * anisotropyFactorUVW.x;
worldCoordUVW.y = worldCoordUVW.y * anisotropyFactorUVW.y;
Expand Down Expand Up @@ -333,7 +333,7 @@ export const getBrushOverlay: ShaderModule = {

// Compute the anisotropy of the dataset so that the brush looks the same in
// each viewport
float baseVoxelSize = min(min(voxelSizeFactor.x, voxelSizeFactor.y), voxelSizeFactor.z);
float baseVoxelSize = 1.; // min(min(voxelSizeFactor.x, voxelSizeFactor.y), voxelSizeFactor.z);
vec3 anisotropyFactorUVW = transDim(voxelSizeFactor) / baseVoxelSize;

float dist = length((floor(worldCoordUVW.xy) - transDim(flooredMousePos).xy) * anisotropyFactorUVW.xy);
Expand Down Expand Up @@ -361,7 +361,7 @@ export const getProofreadingCrossHairOverlay: ShaderModule = {

// Compute the anisotropy of the dataset so that the cross hair looks the same in
// each viewport
float baseVoxelSize = min(min(voxelSizeFactor.x, voxelSizeFactor.y), voxelSizeFactor.z);
float baseVoxelSize = 1.; // min(min(voxelSizeFactor.x, voxelSizeFactor.y), voxelSizeFactor.z);
vec3 anisotropyFactorUVW = transDim(voxelSizeFactor) / baseVoxelSize;

// Compute the distance in screen coordinate space to show a zoom-independent cross hair
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/viewer/shaders/texture_access.glsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export const getColorForCoords: ShaderModule = {

// To avoid rare rendering artifacts, don't use the precomputed
// bucket address when being at the border of buckets.
bool beSafe = isFlycamRotated || !<%= isOrthogonal %>;
bool beSafe = useBucketBorderVertexOptimization < 0.5;
renderedMagIdx = outputMagIdx[globalLayerIndex];
vec3 coords = floor(getAbsoluteCoords(worldPositionUVW, renderedMagIdx, globalLayerIndex));
vec3 absoluteBucketPosition = div(coords, bucketWidth);
Expand Down
2 changes: 2 additions & 0 deletions unreleased_changes/9114.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
### Fixed
- Fixed rendering issues for extremely anisotropic datasets.