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

Bounding Box tool improvements #7892

Merged
merged 16 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4fb4596
enforce default newly create bounding boxes to be within dataset bounds
MichaelBuessemeyer Jun 20, 2024
7eb1230
add first version bounding box moving
MichaelBuessemeyer Jun 20, 2024
84e53f1
Merge branch 'master' of github.com:scalableminds/webknossos into bbo…
MichaelBuessemeyer Jul 2, 2024
8b15773
move bounding box by global data to be zoom independent
MichaelBuessemeyer Jul 2, 2024
8b9320d
move bbox on ctrl / meta
MichaelBuessemeyer Jul 2, 2024
d383ac2
add changelog entry
MichaelBuessemeyer Jul 2, 2024
d8c5018
in case newly create bbox is outside of dataset bounds
MichaelBuessemeyer Jul 2, 2024
a78af8a
Merge branch 'master' into bbox-tool-improvements
MichaelBuessemeyer Jul 3, 2024
ff2cf0b
Merge branch 'master' of github.com:scalableminds/webknossos into bbo…
MichaelBuessemeyer Jul 5, 2024
322eef4
add move hint to statusbar on ctrl pressed
MichaelBuessemeyer Jul 5, 2024
6d5541a
use move mouse curser when in bbox tool and ctrl / meta is pressed
MichaelBuessemeyer Jul 5, 2024
5adacbf
Merge branch 'bbox-tool-improvements' of github.com:scalableminds/web…
MichaelBuessemeyer Jul 5, 2024
598651c
remove messages files
MichaelBuessemeyer Jul 5, 2024
deae8d8
remove accidental pushed changes
MichaelBuessemeyer Jul 15, 2024
ceec60d
Merge branch 'master' of github.com:scalableminds/webknossos into bbo…
MichaelBuessemeyer Jul 15, 2024
c717229
Merge branch 'master' into bbox-tool-improvements
MichaelBuessemeyer Jul 17, 2024
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
2 changes: 2 additions & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Uploading an annotation into a dataset that it was not created for now also works if the dataset is in a different organization. [#7816](https://github.com/scalableminds/webknossos/pull/7816)
- When downloading + reuploading an annotation that is based on a segmentation layer with active mapping, that mapping is now still be selected after the reupload. [#7822](https://github.com/scalableminds/webknossos/pull/7822)
- Added the ability to change the unit of the dataset voxel size to any supported unit of the [ome/ngff standard](https://github.com/ome/ngff/blob/39605eec64ceff481bb3a98f0adeaa330ab1ef26/latest/index.bs#L192). This allows users to upload and work with low-resolution datasets with a different base unit than nanometer. [#7783](https://github.com/scalableminds/webknossos/pull/7783)
- Added the option to move a bounding box via dragging and pressing ctrl / meta. [#7892](https://github.com/scalableminds/webknossos/pull/7892)
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
- In the Voxelytics workflow list, the name of the WEBKNOSSOS user who started the job is displayed. [#7794](https://github.com/scalableminds/webknossos/pull/7795)
- Start an alignment job (aligns the section in a dataset) via the "AI Analysis" button. [#7820](https://github.com/scalableminds/webknossos/pull/7820)
- Added additional validation for the animation job modal. Bounding boxes must be larger then zero. [#7883](https://github.com/scalableminds/webknossos/pull/7883)
Expand All @@ -32,6 +33,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Fixed
- Fixed a bug where the warning to zoom in to see the agglomerate mapping was shown to the user even when the 3D viewport was maximized and no volume data was shown. [#7865](https://github.com/scalableminds/webknossos/issues/7865)
- Fixed a bug that prevented saving new dataset settings. [#7903](https://github.com/scalableminds/webknossos/pull/7903)
- Fixed a bug that allowed the default newly created bounding box to appear outside the dataset. In case the whole bounding box would be outside it is created regardless. [#7892](https://github.com/scalableminds/webknossos/pull/7892)
- Fixed that on large screens the login forms were not horizontally centered. [#7909](https://github.com/scalableminds/webknossos/pull/7909)
- Fixed a bug where brushing on a fallback segmentation with active mapping and with segment index file would lead to failed saves. [#7833](https://github.com/scalableminds/webknossos/pull/7833)
- Fixed a bug where the "Hide Meshes" / "Show Meshes" options of the context menu for segment groups were not available although at leas one mesh was set to visible / invisible. [#7890](https://github.com/scalableminds/webknossos/pull/7890)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
calculateGlobalDelta,
calculateGlobalPos,
calculateMaybeGlobalPos,
} from "oxalis/model/accessors/view_mode_accessor";
import _ from "lodash";
import type { OrthoView, Point2, Vector3, BoundingBoxType, Vector2 } from "oxalis/constants";
import Store from "oxalis/store";
import Store, { OxalisState, UserBoundingBox } from "oxalis/store";
import { getSomeTracing } from "oxalis/model/accessors/tracing_accessor";
import type { DimensionMap, DimensionIndices } from "oxalis/model/dimensions";
import Dimension from "oxalis/model/dimensions";
Expand Down Expand Up @@ -295,6 +296,15 @@ export const highlightAndSetCursorOnHoveredBoundingBox = _.throttle(
},
BOUNDING_BOX_HOVERING_THROTTLE_TIME,
);

function getBoundingBoxOfPrimaryEdge(
primaryEdge: SelectedEdge,
state: OxalisState,
): UserBoundingBox | undefined {
const { userBoundingBoxes } = getSomeTracing(state.tracing);
return userBoundingBoxes.find((bbox) => bbox.id === primaryEdge.boxId);
}

export function handleResizingBoundingBox(
mousePosition: Point2,
planeId: OrthoView,
Expand All @@ -303,8 +313,7 @@ export function handleResizingBoundingBox(
) {
const state = Store.getState();
const globalMousePosition = calculateGlobalPos(state, mousePosition, planeId);
const { userBoundingBoxes } = getSomeTracing(state.tracing);
const bboxToResize = userBoundingBoxes.find((bbox) => bbox.id === primaryEdge.boxId);
const bboxToResize = getBoundingBoxOfPrimaryEdge(primaryEdge, state);

if (!bboxToResize) {
return;
Expand Down Expand Up @@ -364,3 +373,28 @@ export function handleResizingBoundingBox(
}),
);
}

export function handleMovingBoundingBox(
delta: Point2,
planeId: OrthoView,
primaryEdge: SelectedEdge,
) {
const state = Store.getState();
const globalDelta = calculateGlobalDelta(state, delta, planeId);
const bboxToResize = getBoundingBoxOfPrimaryEdge(primaryEdge, state);

if (!bboxToResize) {
return;
}

const updatedBounds = {
min: V3.toArray(V3.add(bboxToResize.boundingBox.min, globalDelta)),
max: V3.toArray(V3.add(bboxToResize.boundingBox.max, globalDelta)),
};

Store.dispatch(
changeUserBoundingBoxAction(primaryEdge.boxId, {
boundingBox: updatedBounds,
}),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import PlaneView from "oxalis/view/plane_view";
import * as SkeletonHandlers from "oxalis/controller/combinations/skeleton_handlers";
import {
createBoundingBoxAndGetEdges,
handleMovingBoundingBox,
SelectedEdge,
} from "oxalis/controller/combinations/bounding_box_handlers";
import {
Expand Down Expand Up @@ -561,12 +562,16 @@ export class BoundingBoxTool {
delta: Point2,
pos: Point2,
_id: string | null | undefined,
_event: MouseEvent,
event: MouseEvent,
) => {
if (primarySelectedEdge != null) {
handleResizingBoundingBox(pos, planeId, primarySelectedEdge, secondarySelectedEdge);
} else {
if (primarySelectedEdge == null) {
MoveHandlers.handleMovePlane(delta);
return;
}
if (event.ctrlKey || event.metaKey) {
handleMovingBoundingBox(delta, planeId, primarySelectedEdge);
} else {
handleResizingBoundingBox(pos, planeId, primarySelectedEdge, secondarySelectedEdge);
}
},
leftMouseDown: (pos: Point2, _plane: OrthoView, _event: MouseEvent) => {
Expand Down
50 changes: 50 additions & 0 deletions frontend/javascripts/oxalis/model/accessors/view_mode_accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,40 @@ function _calculateMaybePlaneScreenPos(
return point;
}

function _calculateMaybeGlobalDelta(
state: OxalisState,
delta: Point2,
planeId?: OrthoView | null | undefined,
): Vector3 | null | undefined {
let position: Vector3;
planeId = planeId || state.viewModeData.plane.activeViewport;
const planeRatio = getBaseVoxelFactorsInUnit(state.dataset.dataSource.scale);
const diffX = delta.x * state.flycam.zoomStep;
const diffY = delta.y * state.flycam.zoomStep;

switch (planeId) {
case OrthoViews.PLANE_XY: {
position = [Math.round(diffX * planeRatio[0]), Math.round(diffY * planeRatio[1]), 0];
break;
}

case OrthoViews.PLANE_YZ: {
position = [0, Math.round(diffY * planeRatio[1]), Math.round(diffX * planeRatio[2])];
break;
}

case OrthoViews.PLANE_XZ: {
position = [Math.round(diffX * planeRatio[0]), 0, Math.round(diffY * planeRatio[2])];
break;
}

default:
return null;
}

return position;
}

function _calculateGlobalPos(
state: OxalisState,
clickPos: Point2,
Expand All @@ -206,6 +240,21 @@ function _calculateGlobalPos(
return position;
}

function _calculateGlobalDelta(
state: OxalisState,
delta: Point2,
planeId?: OrthoView | null | undefined,
): Vector3 {
const position = _calculateMaybeGlobalDelta(state, delta, planeId);

if (!position) {
console.error("Trying to calculate the global position, but no data viewport is active.");
return [0, 0, 0];
}

return position;
}

export function getDisplayedDataExtentInPlaneMode(state: OxalisState) {
const planeRatio = getBaseVoxelFactorsInUnit(state.dataset.dataSource.scale);
const curGlobalCenterPos = getPosition(state.flycam);
Expand Down Expand Up @@ -238,6 +287,7 @@ export function getDisplayedDataExtentInPlaneMode(state: OxalisState) {
}
export const calculateMaybeGlobalPos = reuseInstanceOnEquality(_calculateMaybeGlobalPos);
export const calculateGlobalPos = reuseInstanceOnEquality(_calculateGlobalPos);
export const calculateGlobalDelta = reuseInstanceOnEquality(_calculateGlobalDelta);
export const calculateMaybePlaneScreenPos = reuseInstanceOnEquality(_calculateMaybePlaneScreenPos);
export function getViewMode(state: OxalisState): ViewMode {
return state.temporaryConfiguration.viewMode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ class BoundingBox {
const size = this.getSize();
return { topLeft: this.min, width: size[0], height: size[1], depth: size[2] };
}

toBoundingBoxType(): BoundingBoxType {
return {
min: this.min,
max: this.max,
};
}
}

export default BoundingBox;
20 changes: 16 additions & 4 deletions frontend/javascripts/oxalis/model/reducers/annotation_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import _ from "lodash";
import { getAdditionalCoordinatesAsString } from "../accessors/flycam_accessor";
import { getMeshesForAdditionalCoordinates } from "../accessors/volumetracing_accessor";
import { AdditionalCoordinate } from "types/api_flow_types";
import { getDatasetBoundingBox } from "../accessors/dataset_accessor";
import BoundingBox from "../bucket_data_handling/bounding_box";

const updateTracing = (state: OxalisState, shape: Partial<OxalisState["tracing"]>): OxalisState =>
updateKey(state, "tracing", shape);
Expand Down Expand Up @@ -178,17 +180,27 @@ function AnnotationReducer(state: OxalisState, action: Action): OxalisState {
max: V3.toArray(V3.round(V3.add(action.center, halfBoxExtent))),
};
}
let newBoundingBox: UserBoundingBox;
let newUserBoundingBox: UserBoundingBox;
if (action.newBoundingBox != null) {
newBoundingBox = {
newUserBoundingBox = {
...newBoundingBoxTemplate,
...action.newBoundingBox,
};
} else {
newBoundingBox = newBoundingBoxTemplate;
newUserBoundingBox = newBoundingBoxTemplate;
}

const updatedUserBoundingBoxes = [...userBoundingBoxes, newBoundingBox];
// Ensure the new bounding box is within the dataset bounding box.
const datasetBoundingBox = getDatasetBoundingBox(state.dataset);
const newBoundingBox = new BoundingBox(newUserBoundingBox.boundingBox);
const newBoundingBoxWithinDataset = newBoundingBox.intersectedWith(datasetBoundingBox);
// Only update the bounding box if the bounding box overlaps with the dataset bounds.
// Else the bounding box is completely outside the dataset bounds -> in that case just keep the bounding box and let the user cook.
if (newBoundingBoxWithinDataset.getVolume() > 0) {
newUserBoundingBox.boundingBox = newBoundingBoxWithinDataset.toBoundingBoxType();
}
philippotto marked this conversation as resolved.
Show resolved Hide resolved

const updatedUserBoundingBoxes = [...userBoundingBoxes, newUserBoundingBox];
return updateUserBoundingBoxes(state, updatedUserBoundingBoxes);
}

Expand Down