Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support non-quadratic viewports #3634

Merged
merged 36 commits into from
Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4b56e7a
allow viewports to be non-quadratic (3D viewport still a bit broken; …
philippotto Jan 16, 2019
0e6c7a6
zoom into viewports so that data cannot be cropped
philippotto Jan 16, 2019
f38a139
fix interactions with 3D viewport
philippotto Jan 16, 2019
d6c75af
make arbitrary viewport square to avoid scaling problems
philippotto Jan 18, 2019
60cd536
Merge branch 'master' into non-quadratic-viewports
philippotto Jan 18, 2019
c255453
fix node selection and other rectangular-viewport-related bugs
philippotto Jan 21, 2019
a5fa735
fix panning
philippotto Jan 21, 2019
3c8e2e8
iterate on brush
philippotto Jan 21, 2019
4b9a24f
fix brush preview
philippotto Jan 21, 2019
85deff7
clean up
philippotto Jan 21, 2019
ea3aba3
remove viewport width from info tab view and clean up info tab UI
philippotto Jan 21, 2019
586e6e3
remove unused constants
philippotto Jan 21, 2019
61f1820
use larger viewport area to render more data instead of upscaling dat…
philippotto Jan 23, 2019
b9ce445
Merge branch 'master' of github.com:scalableminds/webknossos into non…
philippotto Jan 28, 2019
4c374ec
adapt mouse interactions to true-viewport-scaling; fix fallback rende…
philippotto Jan 28, 2019
cd5b18b
clean up
philippotto Jan 29, 2019
abec2f0
disable some CI checks
philippotto Jan 29, 2019
5992bc7
Merge branch 'master' of github.com:scalableminds/webknossos into non…
philippotto Feb 1, 2019
e5d48d9
Merge branch 'master' of github.com:scalableminds/webknossos into non…
philippotto Feb 7, 2019
134cd42
clean up according to PR feedback
philippotto Feb 8, 2019
0e7eb19
update changelog
philippotto Feb 8, 2019
9df2531
clean up
philippotto Feb 8, 2019
cb7b5d0
fix scalebars and implement tooltips for complete viewport extent
philippotto Feb 8, 2019
887afb6
Refactor bucket counting (#3750)
philippotto Feb 11, 2019
13dd17d
Merge branch 'master' of github.com:scalableminds/webknossos into non…
philippotto Feb 12, 2019
9d13e6a
remove obsolete comment
philippotto Feb 12, 2019
1c4a207
Merge branch 'master' of github.com:scalableminds/webknossos into non…
philippotto Feb 12, 2019
38d4249
fix import
philippotto Feb 12, 2019
7e22dc6
Merge branch 'master' of github.com:scalableminds/webknossos into non…
philippotto Feb 18, 2019
613a64d
revert not-on-master.sh
philippotto Feb 18, 2019
49173d5
remove unnecessary import
philippotto Feb 18, 2019
f41e386
fix tests and refactor to remove cyclic dependencies in accessors
philippotto Feb 18, 2019
7d93921
extract constant
philippotto Feb 18, 2019
02ce143
update screenshots
philippotto Feb 18, 2019
4e7e3d7
adapt screenshot view value
philippotto Feb 18, 2019
40270c4
Merge branch 'master' of github.com:scalableminds/webknossos into non…
philippotto Feb 18, 2019
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
4 changes: 4 additions & 0 deletions app/assets/javascripts/libs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export function map3<A, B>(fn: (A, number) => B, tuple: [A, A, A]): [B, B, B] {
return [fn(x, 0), fn(y, 1), fn(z, 2)];
}

export function isVec2Equal(a: Vector3, b: Vector3): boolean {
return a[0] === b[0] && a[1] === b[1];
}

function swap(arr, a, b) {
let tmp;
if (arr[a] > arr[b]) {
Expand Down
7 changes: 2 additions & 5 deletions app/assets/javascripts/oxalis/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,9 @@ export const POSITION_REF_REGEX = /#\(([0-9]+,[0-9]+,[0-9]+)\)/g;
// There is an outer yellow CSS border and an inner (red/green/blue) border
// that is a result of the plane being smaller than the renderer viewport
export const OUTER_CSS_BORDER = 2;
const INNER_RENDERER_BORDER = 2;
export const ORTHOGONAL_BORDER = OUTER_CSS_BORDER + INNER_RENDERER_BORDER;
const PLANE_WIDTH = 376;
const VIEWPORT_WIDTH = PLANE_WIDTH + ORTHOGONAL_BORDER * 2;
const VIEWPORT_WIDTH = PLANE_WIDTH;
export const ensureSmallerEdge = true;

const Constants = {
ARBITRARY_VIEW: 4,
Expand All @@ -133,8 +132,6 @@ const Constants = {
BUCKET_SIZE: 32 ** 3,
PLANE_WIDTH,
VIEWPORT_WIDTH,
// The size of the gap between the 4 viewports in the orthogonal mode
VIEWPORT_GAP_WIDTH: 20,

// We require at least 3 * 512 === 1536 buckets per data layer to fit onto the GPU.
// This number is used during setup to pick appropriate data texture sizes.
Expand Down
55 changes: 42 additions & 13 deletions app/assets/javascripts/oxalis/controller/camera_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import TWEEN from "tween.js";
import _ from "lodash";

import { getBoundaries } from "oxalis/model/accessors/dataset_accessor";
import {
getInputCatcherAspectRatio,
applyAspectRatioToWidth,
} from "oxalis/model/accessors/view_mode_accessor";
import { getPosition } from "oxalis/model/accessors/flycam_accessor";
import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers";
import { setTDCameraAction } from "oxalis/model/actions/view_mode_actions";
Expand Down Expand Up @@ -57,17 +61,23 @@ class CameraController extends React.PureComponent<Props> {

updateCamViewport(): void {
const state = Store.getState();
const clippingDistance = state.userConfiguration.clippingDistance;
const { clippingDistance } = state.userConfiguration;
const scaleFactor = getBaseVoxel(state.dataset.dataSource.scale);
const zoom = state.flycam.zoomStep;
const halfBoundary = (constants.VIEWPORT_WIDTH / 2) * zoom;
for (const planeId of OrthoViewValuesWithoutTDView) {
const [scaledWidth, scaledHeight] = applyAspectRatioToWidth(
getInputCatcherAspectRatio(planeId),
halfBoundary * scaleFactor,
);

this.props.cameras[planeId].left = -scaledWidth;
this.props.cameras[planeId].right = scaledWidth;

this.props.cameras[planeId].bottom = -scaledHeight;
this.props.cameras[planeId].top = scaledHeight;

this.props.cameras[planeId].near = -clippingDistance;
const scaledBoundary = halfBoundary * scaleFactor;
this.props.cameras[planeId].left = -scaledBoundary;
this.props.cameras[planeId].bottom = -scaledBoundary;
this.props.cameras[planeId].right = scaledBoundary;
this.props.cameras[planeId].top = scaledBoundary;
this.props.cameras[planeId].updateProjectionMatrix();
}
}
Expand All @@ -94,6 +104,10 @@ class CameraController extends React.PureComponent<Props> {
storeState => storeState.flycam.zoomStep,
() => this.updateCamViewport(),
),
listenToStoreProperty(
storeState => storeState.viewModeData.plane.inputCatcherRects,
() => this.updateCamViewport(),
),
listenToStoreProperty(
storeState => storeState.flycam.currentMatrix,
() => this.update(),
Expand Down Expand Up @@ -149,8 +163,11 @@ export function rotate3DViewTo(id: OrthoView, animate: boolean = true): void {
const b = voxelToNm(dataset.dataSource.scale, getBoundaries(dataset).upperBoundary);
const pos = voxelToNm(dataset.dataSource.scale, getPosition(state.flycam));

const aspectRatio = getInputCatcherAspectRatio(OrthoViews.TDView);

let to: TweenState;
if (id === OrthoViews.TDView) {
// TODO use height
const diagonal = Math.sqrt(b[0] * b[0] + b[1] * b[1]);
const padding = 0.05 * diagonal;

Expand All @@ -175,21 +192,33 @@ export function rotate3DViewTo(id: OrthoView, animate: boolean = true): void {
// Calulate the x coordinate so that the vector from the camera to the cube's middle point is
// perpendicular to the vector going from (0, b[1], 0) to (b[0], 0, 0).

const squareLeft = -distance - padding;
const squareRight = diagonal - distance + padding;
const squareTop = diagonal / 2 + padding + yOffset;
const squareBottom = -diagonal / 2 - padding + yOffset;
const squareCenterX = (squareLeft + squareRight) / 2;
const squareCenterY = (squareTop + squareBottom) / 2;
const squareWidth = Math.abs(squareLeft - squareRight);

const height = squareWidth / aspectRatio;

to = {
dx: b[1] / diagonal,
dy: b[0] / diagonal,
dz: -1 / 2,
upX: 0,
upY: 0,
upZ: -1,
l: -distance - padding,
r: diagonal - distance + padding,
t: diagonal / 2 + padding + yOffset,
b: -diagonal / 2 - padding + yOffset,
l: squareCenterX - squareWidth / 2,
r: squareCenterX + squareWidth / 2,
t: squareCenterY + height / 2,
b: squareCenterY - height / 2,
};
} else {
const ind = Dimensions.getIndices(id);
const width = Math.max(b[ind[0]], b[ind[1]] * 1.12) * 1.1;
const height = width / aspectRatio;

const paddingTop = width * 0.12;
const padding = ((width / 1.1) * 0.1) / 2;
const offsetX = pos[ind[0]] + padding + (width - b[ind[0]]) / 2;
Expand Down Expand Up @@ -219,11 +248,11 @@ export function rotate3DViewTo(id: OrthoView, animate: boolean = true): void {
l,
t,
r: l + width,
b: t - width,
b: t - height,
};
}

const updateCameraTDView = function(tweenState: TweenState): void {
const updateCameraTDView = (tweenState: TweenState) => {
const p = voxelToNm(
Store.getState().dataset.dataSource.scale,
getPosition(Store.getState().flycam),
Expand Down Expand Up @@ -265,7 +294,7 @@ export function rotate3DViewTo(id: OrthoView, animate: boolean = true): void {
.to(to, time)
.onUpdate(function updater() {
// TweenJS passes the current state via the `this` object.
// However, for easier type checking, we pass it as an explicit
// However, for better type checking, we pass it as an explicit
// parameter.
updateCameraTDView(this);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ function onClick(
const borderWidth = OUTER_CSS_BORDER;
const [x, y] = [Math.round(position.x) - borderWidth, Math.round(position.y) - borderWidth];
// compute the index of the pixel under the cursor,
// while inverting along the y-axis, because OpenGL has its origin bottom-left :/
const index = (x + (width - y) * height) * 4;
// while inverting along the y-axis, because WebGL has its origin bottom-left :/
const index = (x + (height - y) * width) * 4;
philippotto marked this conversation as resolved.
Show resolved Hide resolved
// the nodeId can be reconstructed by interpreting the RGB values of the pixel as a base-255 number
const nodeId = buffer.subarray(index, index + 3).reduce((a, b) => a * 255 + b, 0);
SceneController.skeleton.stopPicking();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ export function getPlaneMouseControls(planeId: OrthoView): * {

if (tool === VolumeToolEnum.MOVE) {
const state = Store.getState();
const viewportScale = getViewportScale(planeId);
const [viewportScaleX, viewportScaleY] = getViewportScale(planeId);
const { activeViewport } = state.viewModeData.plane;
const v = [(delta.x * -1) / viewportScale, (delta.y * -1) / viewportScale, 0];
const v = [(delta.x * -1) / viewportScaleX, (delta.y * -1) / viewportScaleY, 0];
Store.dispatch(movePlaneFlycamOrthoAction(v, activeViewport, true));
}

Expand Down
25 changes: 15 additions & 10 deletions app/assets/javascripts/oxalis/controller/scene_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import _ from "lodash";
import type { MeshMetaData } from "admin/api_flow_types";
import { V3 } from "libs/mjs";
import { getBoundaries } from "oxalis/model/accessors/dataset_accessor";
import { getInputCatcherAspectRatio } from "oxalis/model/accessors/view_mode_accessor";
import {
getPosition,
getPlaneScalingFactor,
Expand All @@ -31,6 +32,7 @@ import Store from "oxalis/store";
import * as Utils from "libs/utils";
import app from "app";
import constants, {
ArbitraryViewport,
type BoundingBoxType,
type OrthoView,
type OrthoViewMap,
Expand Down Expand Up @@ -268,7 +270,7 @@ class SceneController {
}
}

updateSceneForCam = (id: OrthoView): void => {
updateSceneForCam = (id: OrthoView, hidePlanes: boolean = false): void => {
philippotto marked this conversation as resolved.
Show resolved Hide resolved
// This method is called for each of the four cams. Even
// though they are all looking at the same scene, some
// things have to be changed for each cam.
Expand All @@ -283,7 +285,7 @@ class SceneController {
for (const planeId of OrthoViewValuesWithoutTDView) {
if (planeId === id) {
this.planes[planeId].setOriginalCrosshairColor();
this.planes[planeId].setVisible(true);
this.planes[planeId].setVisible(!hidePlanes);
const pos = _.clone(getPosition(Store.getState().flycam));
ind = Dimensions.getIndices(planeId);
// Offset the plane so the user can see the skeletonTracing behind the plane
Expand All @@ -305,10 +307,10 @@ class SceneController {
}
};

update = (optPlane?: ArbitraryPlane): void => {
const gPos = getPosition(Store.getState().flycam);
update(optArbitraryPlane?: ArbitraryPlane): void {
const { flycam } = Store.getState();
const gPos = getPosition(flycam);
const globalPosVec = new THREE.Vector3(...gPos);
const planeScale = getPlaneScalingFactor(Store.getState().flycam);

// The anchor point refers to the top-left-front bucket of the bounding box
// which covers all three rendered planes. Relative to this anchor point,
Expand All @@ -327,17 +329,20 @@ class SceneController {
);
}

if (optPlane) {
optPlane.updateAnchorPoints(anchorPoint, fallbackAnchorPoint);
optPlane.setPosition(globalPosVec);
if (optArbitraryPlane) {
optArbitraryPlane.updateAnchorPoints(anchorPoint, fallbackAnchorPoint);
optArbitraryPlane.setPosition(globalPosVec);
const aspectRatio = getInputCatcherAspectRatio(ArbitraryViewport);
optArbitraryPlane.setAspectRatio(aspectRatio);
} else {
for (const currentPlane of _.values(this.planes)) {
currentPlane.updateAnchorPoints(anchorPoint, fallbackAnchorPoint);
currentPlane.setPosition(globalPosVec);
currentPlane.setScale(planeScale);
const [scaleX, scaleY] = getPlaneScalingFactor(flycam, currentPlane.planeID);
currentPlane.setScale(scaleX, scaleY);
}
}
};
}

setDisplayCrosshair(value: boolean): void {
for (const plane of _.values(this.planes)) {
Expand Down
10 changes: 5 additions & 5 deletions app/assets/javascripts/oxalis/controller/td_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,14 @@ class TDController extends React.PureComponent<Props> {
if (zoomToMouse && this.mouseController) {
zoomToPosition = this.mouseController.position;
}
const { width } = getInputCatcherRect(OrthoViews.TDView);
Store.dispatch(zoomTDViewAction(value, zoomToPosition, width));
const { width, height } = getInputCatcherRect(OrthoViews.TDView);
Store.dispatch(zoomTDViewAction(value, zoomToPosition, width, height));
}

moveTDView(delta: Point2): void {
const scale = getViewportScale(OrthoViews.TDView);
Store.dispatch(moveTDViewXAction((delta.x / scale) * -1));
Store.dispatch(moveTDViewYAction((delta.y / scale) * -1));
const [scaleX, scaleY] = getViewportScale(OrthoViews.TDView);
Store.dispatch(moveTDViewXAction((delta.x / scaleX) * -1));
Store.dispatch(moveTDViewYAction((delta.y / scaleY) * -1));
}

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,10 @@ class ArbitraryController extends React.PureComponent<Props> {
),
);
} else if (this.props.viewMode === constants.MODE_ARBITRARY_PLANE) {
const f = Store.getState().flycam.zoomStep / getViewportScale(ArbitraryViewport);
Store.dispatch(moveFlycamAction([delta.x * f, delta.y * f, 0]));
const [scaleX, scaleY] = getViewportScale(ArbitraryViewport);
const fx = Store.getState().flycam.zoomStep / scaleX;
const fy = Store.getState().flycam.zoomStep / scaleY;
Store.dispatch(moveFlycamAction([delta.x * fx, delta.y * fy, 0]));
}
},
scroll: this.scroll,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import {
getPlaneScalingFactor,
} from "oxalis/model/accessors/flycam_accessor";
import { getResolutions } from "oxalis/model/accessors/dataset_accessor";
import { getViewportScale, getInputCatcherRect } from "oxalis/model/accessors/view_mode_accessor";
import {
getDominantViewportScale,
getViewportScale,
getInputCatcherRect,
} from "oxalis/model/accessors/view_mode_accessor";
import { getVolumeTool } from "oxalis/model/accessors/volumetracing_accessor";
import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers";
import {
Expand Down Expand Up @@ -145,8 +149,8 @@ class PlaneController extends React.PureComponent<Props> {
getPlaneMouseControls(planeId: OrthoView): Object {
const baseControls = {
leftDownMove: (delta: Point2) => {
const viewportScale = getViewportScale(planeId);
return this.movePlane([(delta.x * -1) / viewportScale, (delta.y * -1) / viewportScale, 0]);
const scale = getDominantViewportScale(planeId);
return this.movePlane([(delta.x * -1) / scale, (delta.y * -1) / scale, 0]);
},

scroll: this.scrollPlanes.bind(this),
Expand Down Expand Up @@ -394,8 +398,8 @@ class PlaneController extends React.PureComponent<Props> {

zoomTDView(value: number): void {
const zoomToPosition = null;
const { width } = getInputCatcherRect(OrthoViews.TDView);
Store.dispatch(zoomTDViewAction(value, zoomToPosition, width));
const { width, height } = getInputCatcherRect(OrthoViews.TDView);
Store.dispatch(zoomTDViewAction(value, zoomToPosition, width, height));
}

finishZoom = (): void => {
Expand Down Expand Up @@ -529,13 +533,13 @@ export function calculateGlobalPos(clickPos: Point2): Vector3 {
const state = Store.getState();
const { activeViewport } = state.viewModeData.plane;
const curGlobalPos = getPosition(state.flycam);
const zoomFactor = getPlaneScalingFactor(state.flycam);
const zoomFactors = getPlaneScalingFactor(state.flycam, activeViewport);
const viewportScale = getViewportScale(activeViewport);
const planeRatio = getBaseVoxelFactors(state.dataset.dataSource.scale);

const center = (constants.VIEWPORT_WIDTH * viewportScale) / 2;
const diffX = ((center - clickPos.x) / viewportScale) * zoomFactor;
const diffY = ((center - clickPos.y) / viewportScale) * zoomFactor;
const center = [0, 1].map(dim => (constants.VIEWPORT_WIDTH * viewportScale[dim]) / 2);
const diffX = ((center[0] - clickPos.x) / viewportScale[0]) * zoomFactors[0];
const diffY = ((center[1] - clickPos.y) / viewportScale[1]) * zoomFactors[1];

switch (activeViewport) {
case OrthoViews.PLANE_XY:
Expand Down
Loading