Skip to content

Commit

Permalink
Merge branch 'master' of github.com:scalableminds/webknossos into tim…
Browse files Browse the repository at this point in the history
…etrack-td-view
  • Loading branch information
daniel-wer committed Oct 19, 2020
2 parents b69dd80 + 1d8f0c3 commit 43bb927
Show file tree
Hide file tree
Showing 31 changed files with 457 additions and 329 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Hybrid tracings can now be imported directly in the tracing view via drag'n'drop. [#4837](https://github.com/scalableminds/webknossos/pull/4837)
- The find data function now works for volume tracings, too. [#4847](https://github.com/scalableminds/webknossos/pull/4847)
- Added admins and dataset managers to dataset access list, as they can access all datasets of the organization. [#4862](https://github.com/scalableminds/webknossos/pull/4862)
- Sped up the NML parsing via dashboard import. [#4872](https://github.com/scalableminds/webknossos/pull/4872)

### Changed
- Brush circles are now connected with rectangles to provide a continuous stroke even if the brush is moved quickly. [#4785](https://github.com/scalableminds/webknossos/pull/4822)
Expand Down
285 changes: 158 additions & 127 deletions app/models/annotation/nml/NmlParser.scala

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ ThisBuild / scalacOptions ++= Seq(
"-language:postfixOps"
)

PlayKeys.devSettings := Seq("play.server.akka.requestTimeout" -> "10000s",
"play.server.http.idleTimeout" -> "10000s")

scapegoatIgnoredFiles := Seq(".*/Tables.scala",
".*/Routes.scala",
".*/ReverseRoutes.scala",
Expand Down
4 changes: 2 additions & 2 deletions conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ play.filters.headers.contentTypeOptions = null
play.filters.headers.frameOptions = null

# Note that these take effect only in production mode (timeouts are shorter in dev)
play.server.http.idleTimeout = 1000s
play.server.akka.requestTimeout = 1000s
play.server.http.idleTimeout = 10000s
play.server.akka.requestTimeout = 10000s

# Avoid the creation of a pid file
pidfile.path = "/dev/null"
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/libs/error_handling.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import window, { document, location } from "libs/window";
const MAX_NUM_ERRORS = 50;
const BLACKLISTED_ERROR_MESSAGES = [
"ResizeObserver loop limit exceeded",
"ResizeObserver loop completed with undelivered notifications.",
"Invariant Violation: Cannot call hover while not dragging.",
// Errors from the sortable-tree when dragging an element onto itself
"Uncaught Invariant Violation: Expected to find a valid target.",
Expand Down
4 changes: 2 additions & 2 deletions frontend/javascripts/libs/mjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ V3.round = function round(v: Vector3 | Float32Array, r: ?Float32Array): Float32A
return r;
};

V3.floor = v => v.map(number => Math.floor(number));
V3.floor = vec => [Math.floor(vec[0]), Math.floor(vec[1]), Math.floor(vec[2])];

V3.ceil = v => v.map(number => Math.ceil(number));
V3.ceil = vec => [Math.ceil(vec[0]), Math.ceil(vec[1]), Math.ceil(vec[2])];

V3.toString = v => v.join(", ");

Expand Down
7 changes: 3 additions & 4 deletions frontend/javascripts/oxalis/controller/camera_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,9 @@ class CameraController extends React.PureComponent<Props> {
const gPos = getPosition(state.flycam);
// camera position's unit is nm, so convert it.
const cPos = voxelToNm(state.dataset.dataSource.scale, gPos);
const cPosVec = new THREE.Vector3(cPos[0], cPos[1], cPos[2]);
this.props.cameras[OrthoViews.PLANE_XY].position.copy(cPosVec);
this.props.cameras[OrthoViews.PLANE_YZ].position.copy(cPosVec);
this.props.cameras[OrthoViews.PLANE_XZ].position.copy(cPosVec);
this.props.cameras[OrthoViews.PLANE_XY].position.set(cPos[0], cPos[1], cPos[2]);
this.props.cameras[OrthoViews.PLANE_YZ].position.set(cPos[0], cPos[1], cPos[2]);
this.props.cameras[OrthoViews.PLANE_XZ].position.set(cPos[0], cPos[1], cPos[2]);
}

bindToEvents() {
Expand Down
10 changes: 4 additions & 6 deletions frontend/javascripts/oxalis/controller/scene_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,13 @@ class SceneController {
if (planeId === id) {
this.planes[planeId].setOriginalCrosshairColor();
this.planes[planeId].setVisible(!hidePlanes);
const pos = _.clone(getPosition(Store.getState().flycam));
const originalPosition = getPosition(Store.getState().flycam);
const pos = _.clone(originalPosition);
ind = Dimensions.getIndices(planeId);
// Offset the plane so the user can see the skeletonTracing behind the plane
pos[ind[2]] +=
planeId === OrthoViews.PLANE_XY ? this.planeShift[ind[2]] : -this.planeShift[ind[2]];
this.planes[planeId].setPosition(new THREE.Vector3(...pos));
this.planes[planeId].setPosition(pos, originalPosition);
} else {
this.planes[planeId].setVisible(false);
}
Expand All @@ -331,7 +332,7 @@ class SceneController {
const { tdViewDisplayPlanes } = Store.getState().userConfiguration;
for (const planeId of OrthoViewValuesWithoutTDView) {
const pos = getPosition(Store.getState().flycam);
this.planes[planeId].setPosition(new THREE.Vector3(pos[0], pos[1], pos[2]));
this.planes[planeId].setPosition(pos);
this.planes[planeId].setGrayCrosshairColor();
this.planes[planeId].setVisible(true);
this.planes[planeId].plane.visible = this.isPlaneVisible[planeId] && tdViewDisplayPlanes;
Expand All @@ -343,7 +344,6 @@ class SceneController {
const state = Store.getState();
const { flycam } = state;
const globalPosition = getPosition(flycam);
const globalPosVec = new THREE.Vector3(...globalPosition);

// 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 @@ -358,11 +358,9 @@ class SceneController {

if (optArbitraryPlane) {
optArbitraryPlane.updateAnchorPoints(anchorPoint);
optArbitraryPlane.setPosition(globalPosVec);
} else {
for (const currentPlane of _.values(this.planes)) {
currentPlane.updateAnchorPoints(anchorPoint);
currentPlane.setPosition(globalPosVec);
const [scaleX, scaleY] = getPlaneScalingFactor(state, flycam, currentPlane.planeID);
const isVisible = scaleX > 0 && scaleY > 0;
if (isVisible) {
Expand Down
4 changes: 2 additions & 2 deletions frontend/javascripts/oxalis/geometries/arbitrary_plane.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ class ArbitraryPlane {
}
}

setPosition = ({ x, y, z }: typeof THREE.Vector3) => {
this.meshes.mainPlane.material.setGlobalPosition([x, y, z]);
setPosition = (x: number, y: number, z: number) => {
this.meshes.mainPlane.material.setGlobalPosition(x, y, z);
};

addToScene(scene: typeof THREE.Scene) {
Expand Down
8 changes: 5 additions & 3 deletions frontend/javascripts/oxalis/geometries/cube.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import BackboneEvents from "backbone-events-standalone";
import * as THREE from "three";
import _ from "lodash";

import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers";
import {
type OrthoView,
type OrthoViewMap,
Expand Down Expand Up @@ -68,9 +69,10 @@ class Cube {
this.setCorners(this.min, this.max);
}

Store.subscribe(() => {
this.updatePosition(getPosition(Store.getState().flycam));
});
listenToStoreProperty(
state => getPosition(state.flycam),
position => this.updatePosition(position),
);
}

setCorners(min: Vector3, max: Vector3) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ class PlaneMaterialFactory {

shaderEditor.addMaterial(this.shaderId, this.material);

this.material.setGlobalPosition = ([x, y, z]) => {
this.material.setGlobalPosition = (x, y, z) => {
this.uniforms.globalPosition.value.set(x, y, z);
};

Expand Down
38 changes: 29 additions & 9 deletions frontend/javascripts/oxalis/geometries/plane.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import * as THREE from "three";
import _ from "lodash";

import { getBaseVoxelFactors } from "oxalis/model/scaleinfo";
import { getPosition } from "oxalis/model/accessors/flycam_accessor";
import Dimensions from "oxalis/model/dimensions";
import PlaneMaterialFactory from "oxalis/geometries/materials/plane_material_factory";
import Store from "oxalis/store";
Expand All @@ -31,10 +30,12 @@ class Plane {
baseScaleVector: typeof THREE.Vector3;
crosshair: Array<typeof THREE.LineSegments>;
TDViewBorders: typeof THREE.Line;
lastScaleFactors: [number, number];

constructor(planeID: OrthoView) {
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 resolution. In all other dimensions, the plane
Expand Down Expand Up @@ -132,6 +133,13 @@ class Plane {
}

setScale(xFactor: number, yFactor: number): void {
if (this.lastScaleFactors[0] !== xFactor || this.lastScaleFactors[1] !== yFactor) {
this.lastScaleFactors[0] = xFactor;
this.lastScaleFactors[1] = yFactor;
} else {
return;
}

const scaleVec = new THREE.Vector3().multiplyVectors(
new THREE.Vector3(xFactor, yFactor, 1),
this.baseScaleVector,
Expand All @@ -148,14 +156,26 @@ class Plane {
);
};

setPosition = (posVec: typeof THREE.Vector3): void => {
this.TDViewBorders.position.copy(posVec);
this.crosshair[0].position.copy(posVec);
this.crosshair[1].position.copy(posVec);
this.plane.position.copy(posVec);

const globalPosition = getPosition(Store.getState().flycam);
this.plane.material.setGlobalPosition(globalPosition);
// In case the plane's position was offset to make geometries
// on the plane visible (by moving the plane to the back), one can
// additionall pass the originalPosition (which is necessary for the
// shader)
setPosition = (pos: Vector3, originalPosition?: Vector3): void => {
const [x, y, z] = pos;
this.TDViewBorders.position.set(x, y, z);
this.crosshair[0].position.set(x, y, z);
this.crosshair[1].position.set(x, y, z);
this.plane.position.set(x, y, z);

if (originalPosition == null) {
this.plane.material.setGlobalPosition(x, y, z);
} else {
this.plane.material.setGlobalPosition(
originalPosition[0],
originalPosition[1],
originalPosition[2],
);
}
};

setVisible = (visible: boolean): void => {
Expand Down
21 changes: 14 additions & 7 deletions frontend/javascripts/oxalis/model/accessors/flycam_accessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,26 +186,26 @@ function getMaximumZoomForAllResolutionsFromStore(state: OxalisState): Array<num
);
}

export function getUp(flycam: Flycam): Vector3 {
function _getUp(flycam: Flycam): Vector3 {
const matrix = flycam.currentMatrix;
return [matrix[4], matrix[5], matrix[6]];
}

export function getLeft(flycam: Flycam): Vector3 {
function _getLeft(flycam: Flycam): Vector3 {
const matrix = flycam.currentMatrix;
return [matrix[0], matrix[1], matrix[2]];
}

export function getPosition(flycam: Flycam): Vector3 {
function _getPosition(flycam: Flycam): Vector3 {
const matrix = flycam.currentMatrix;
return [matrix[12], matrix[13], matrix[14]];
}

export function getFlooredPosition(flycam: Flycam): Vector3 {
return map3(x => Math.floor(x), getPosition(flycam));
function _getFlooredPosition(flycam: Flycam): Vector3 {
return map3(x => Math.floor(x), _getPosition(flycam));
}

export function getRotation(flycam: Flycam): Vector3 {
function _getRotation(flycam: Flycam): Vector3 {
const object = new THREE.Object3D();
const matrix = new THREE.Matrix4().fromArray(flycam.currentMatrix).transpose();
object.applyMatrix(matrix);
Expand All @@ -218,10 +218,17 @@ export function getRotation(flycam: Flycam): Vector3 {
];
}

export function getZoomedMatrix(flycam: Flycam): Matrix4x4 {
function _getZoomedMatrix(flycam: Flycam): Matrix4x4 {
return M4x4.scale1(flycam.zoomStep, flycam.currentMatrix);
}

export const getUp = memoizeOne(_getUp);
export const getLeft = memoizeOne(_getLeft);
export const getPosition = memoizeOne(_getPosition);
export const getFlooredPosition = memoizeOne(_getFlooredPosition);
export const getRotation = memoizeOne(_getRotation);
export const getZoomedMatrix = memoizeOne(_getZoomedMatrix);

export function getRequestLogZoomStep(state: OxalisState): number {
const maximumZoomSteps = getMaximumZoomForAllResolutionsFromStore(state);
const maxLogZoomStep = Math.log2(getMaxZoomStep(state.dataset));
Expand Down
44 changes: 35 additions & 9 deletions frontend/javascripts/oxalis/model/bucket_data_handling/bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
* @flow
*/

import BackboneEvents from "backbone-events-standalone";
import * as THREE from "three";
import _ from "lodash";
import { createNanoEvents } from "nanoevents";

import {
bucketPositionToGlobalAddress,
Expand Down Expand Up @@ -43,6 +43,12 @@ export const bucketDebuggingFlags = {
// Exposing this variable allows debugging on deployed systems
window.bucketDebuggingFlags = bucketDebuggingFlags;

type Emitter = {
on: Function,
events: Object,
emit: Function,
};

export class NullBucket {
type: "null" = "null";
isOutOfBoundingBox: boolean;
Expand Down Expand Up @@ -109,21 +115,19 @@ export class DataBucket {
data: ?BucketDataArray;
temporalBucketManager: TemporalBucketManager;
zoomedAddress: Vector4;
// Copied from backbone events (TODO: handle this better)
trigger: Function;
on: Function;
off: Function;
once: Function;
cube: DataCube;
_fallbackBucket: ?Bucket;
throttledTriggerLabeled: () => void;
emitter: Emitter;

constructor(
elementClass: ElementClass,
zoomedAddress: Vector4,
temporalBucketManager: TemporalBucketManager,
cube: DataCube,
) {
_.extend(this, BackboneEvents);
this.emitter = createNanoEvents();

this.elementClass = elementClass;
this.cube = cube;
this.zoomedAddress = zoomedAddress;
Expand All @@ -134,6 +138,28 @@ export class DataBucket {
this.accessed = false;

this.data = null;

if (this.cube.isSegmentation) {
this.throttledTriggerLabeled = _.throttle(() => this.trigger("bucketLabeled"), 10);
} else {
this.throttledTriggerLabeled = _.noop;
}
}

once(event: string, callback: Function): () => void {
const unbind = this.emitter.on(event, (...args) => {
unbind();
callback(...args);
});
return unbind;
}

on(event: string, cb: Function): () => void {
return this.emitter.on(event, cb);
}

trigger(event: string, ...args: Array<any>): void {
this.emitter.emit(event, ...args);
}

getBoundingBox(): BoundingBoxType {
Expand Down Expand Up @@ -161,6 +187,8 @@ export class DataBucket {
// so that at least the big memory hog is tamed (unfortunately,
// this doesn't help against references which point directly to this.data)
this.data = null;
// Remove all event handlers (see https://github.com/ai/nanoevents#remove-all-listeners)
this.emitter.events = {};
}

needsRequest(): boolean {
Expand Down Expand Up @@ -223,8 +251,6 @@ export class DataBucket {
this.throttledTriggerLabeled();
}

throttledTriggerLabeled = _.throttle(() => this.trigger("bucketLabeled"), 10);

markAndAddBucketForUndo() {
if (!bucketsAlreadyInUndoState.has(this)) {
bucketsAlreadyInUndoState.add(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class DataCube {
return this.getNullBucket(address);
}

let bucket = this.getBucket(address);
let bucket = this.getBucket(address, true);
if (bucket instanceof NullBucket) {
bucket = this.createBucket(address);
}
Expand All @@ -228,8 +228,8 @@ class DataCube {
}

// Returns the Bucket object if it exists, or NULL_BUCKET otherwise.
getBucket(address: Vector4): Bucket {
if (!this.isWithinBounds(address)) {
getBucket(address: Vector4, skipBoundsCheck: boolean = false): Bucket {
if (!skipBoundsCheck && !this.isWithinBounds(address)) {
return this.getNullBucket(address);
}

Expand All @@ -246,9 +246,7 @@ class DataCube {

createBucket(address: Vector4): Bucket {
const bucket = new DataBucket(this.elementClass, address, this.temporalBucketManager, this);
bucket.on({
bucketLoaded: () => this.trigger("bucketLoaded", address),
});
bucket.on("bucketLoaded", () => this.trigger("bucketLoaded", address));
this.addBucketToGarbageCollection(bucket);

const bucketIndex = this.getBucketIndex(address);
Expand Down
Loading

0 comments on commit 43bb927

Please sign in to comment.