Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docs/ui/layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Note, not all control/viewport settings are available in every annotation mode.

- `Keyboard delay (ms)`: The initial delay before an operation will be executed when pressing a keyboard shortcut. A low value will immediately execute a keyboard's associated operation, whereas a high value will delay the execution of an operation. This is useful for preventing an operation being called multiple times when rapidly pressing a key in short succession, e.g., for movement.

- `Move Value (nm/s)`: A high value will speed up movement through the dataset, e.g., when holding down the spacebar. Vice-versa, a low value will slow down the movement allowing for more precision. This setting is especially useful in `Flight mode`.
- `Move Value`: A high value will speed up movement through the dataset, e.g., when holding down the spacebar. Vice-versa, a low value will slow down the movement allowing for more precision. This setting is especially useful in `Flight mode`.

- `d/f-Switching`: If d/f switching is disabled, moving through the dataset with `f` will always go *f*orward by _increasing_ the coordinate orthogonal to the current slice. Correspondingly, `d` will move backwards by decreasing that coordinate. However, if d/f is enabled, the meaning of "forward" and "backward" will change depending on how you create nodes. For example, when a node is placed at z == 100 and afterwards another node is created at z == 90, z will be _decreased_ when going forward.

Expand Down
4 changes: 2 additions & 2 deletions frontend/javascripts/components/fast_tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export type FastTooltipPlacement =
| "left-end";

// See docstring above for context.
const uniqueKeyToDynamicRenderer: Record<string, () => React.ReactElement> = {};
const uniqueKeyToDynamicRenderer: Record<string, () => React.ReactElement | null> = {};

export default function FastTooltip({
title,
Expand All @@ -76,7 +76,7 @@ export default function FastTooltip({
className?: string; // class name attached to the wrapper
style?: React.CSSProperties; // style attached to the wrapper
variant?: "dark" | "light" | "success" | "warning" | "error" | "info";
dynamicRenderer?: () => React.ReactElement;
dynamicRenderer?: () => React.ReactElement | null;
}) {
const Tag = wrapper || "span";
const [uniqueKeyForDynamic, setUniqueDynamicId] = useState<string | undefined>(undefined);
Expand Down
26 changes: 24 additions & 2 deletions frontend/javascripts/libs/format_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export function formatScale(scale: VoxelSize | null | undefined, roundTo: number
(value) => Utils.roundTo(value / conversionFactor, roundTo),
scaleFactor,
);
return `${scaleInNmRounded.join(ThinSpace + MultiplicationSymbol + ThinSpace)} ${newUnit}³/voxel`;
return `${scaleInNmRounded.join(ThinSpace + MultiplicationSymbol + ThinSpace)} ${newUnit}³/Vx`;
}

function toOptionalFixed(num: number, decimalPrecision: number): string {
Expand Down Expand Up @@ -399,7 +399,7 @@ function findBestUnitForFormatting(
}
export function formatLengthAsVx(lengthInVx: number, roundTo: number = 2): string {
const roundedLength = Utils.roundTo(lengthInVx, roundTo);
return `${roundedLength} vx`;
return `${roundedLength}${ThinSpace}Vx`;
}
export function formatAreaAsVx(areaInVx: number, roundTo: number = 2): string {
return `${formatLengthAsVx(areaInVx, roundTo)}²`;
Expand Down Expand Up @@ -533,6 +533,28 @@ export function formatVoxels(voxelCount: number) {
return `${voxelCount} Vx`;
}

export function formatVoxelsForHighNumbers(voxelCount: number) {
if (voxelCount == null) {
return "";
}
if (!Number.isFinite(voxelCount)) {
return "Infinity";
}
if (voxelCount > 10 ** 15) {
return `${(voxelCount / 10 ** 15).toPrecision(4)}${ThinSpace}PVx`;
}
if (voxelCount > 10 ** 12) {
return `${(voxelCount / 10 ** 12).toPrecision(4)}${ThinSpace}TVx`;
}
if (voxelCount > 10 ** 9) {
return `${(voxelCount / 10 ** 9).toPrecision(4)}${ThinSpace}GVx`;
}
if (voxelCount > 10 ** 6) {
return `${(voxelCount / 10 ** 6).toPrecision(4)}${ThinSpace}MVx`;
}
return `${voxelCount}${ThinSpace}Vx`;
}

export function formatNumber(num: number): string {
return new Intl.NumberFormat("en-US").format(num);
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const settings: Partial<Record<keyof RecommendedConfiguration, string>> =
displayScalebars: "Show Scalebars",
dynamicSpaceDirection: "d/f-Switching",
keyboardDelay: "Keyboard delay (ms)",
moveValue: "Move Value (nm/s)",
moveValue: "Move Value",
newNodeNewTree: "Single-node-tree mode (Soma clicking)",
centerNewNode: "Auto-center Nodes",
applyNodeRotationOnActivation: "Auto-rotate to Nodes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
ElementClass,
} from "types/api_types";
import type { DataLayer } from "types/schemas/datasource.types";
import { LongUnitToShortUnitMap, type Vector3, type ViewMode } from "viewer/constants";
import { LongUnitToShortUnitMap, Unicode, type Vector3, type ViewMode } from "viewer/constants";
import constants, { ViewModeValues, Vector3Indicies, MappingStatusEnum } from "viewer/constants";
import type {
ActiveMappingInfo,
Expand All @@ -31,6 +31,8 @@ import { getSupportedValueRangeForElementClass } from "../bucket_data_handling/d
import { MagInfo, convertToDenseMags } from "../helpers/mag_info";
import { reuseInstanceOnEquality } from "./accessor_helpers";

const { ThinSpace } = Unicode;

function _getMagInfo(magnifications: Array<{ mag: Vector3 }>): MagInfo {
return new MagInfo(magnifications.map((magObj) => magObj.mag));
}
Expand Down Expand Up @@ -297,7 +299,7 @@ export function getDatasetExtentAsString(

if (inVoxel) {
const extentInVoxel = getDatasetExtentInVoxel(dataset);
return `${formatExtentInUnitWithLength(extentInVoxel, (x) => `${x}`)} voxel`;
return `${formatExtentInUnitWithLength(extentInVoxel, (x) => `${x}`)}${ThinSpace}Vx`;
}

const extent = getDatasetExtentInUnit(dataset);
Expand Down
7 changes: 5 additions & 2 deletions frontend/javascripts/viewer/model/sagas/settings_saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ErrorHandling from "libs/error_handling";
import Toast from "libs/toast";
import messages from "messages";
import { all, call, debounce, put, retry, takeEvery } from "typed-redux-saga";
import { ControlModeEnum } from "viewer/constants";
import { ControlModeEnum, LongUnitToShortUnitMap } from "viewer/constants";
import {
type SetViewModeAction,
type UpdateUserSettingAction,
Expand Down Expand Up @@ -108,7 +108,10 @@ function* showUserSettingToast(action: UpdateUserSettingAction): Saga<void> {

if (propertyName === "moveValue" || propertyName === "moveValue3d") {
const moveValue = yield* select((state) => state.userConfiguration[propertyName]);
const moveValueMessage = messages["tracing.changed_move_value"] + moveValue;
const unit = yield* select(
(state) => LongUnitToShortUnitMap[state.dataset.dataSource.scale.unit],
);
const moveValueMessage = messages["tracing.changed_move_value"] + moveValue + ` ${unit}/s`;
Toast.success(moveValueMessage, {
key: "CHANGED_MOVE_VALUE",
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { PushpinOutlined } from "@ant-design/icons";
import { Space } from "antd";
import { Col, Row, Space } from "antd";
import FastTooltip from "components/fast_tooltip";
import { formatNumberToLength, formatVoxelsForHighNumbers } from "libs/format_utils";
import { V3 } from "libs/mjs";
import { useWkSelector } from "libs/react_hooks";
import Toast from "libs/toast";
import { Vector3Input } from "libs/vector_input";
import message from "messages";
import type React from "react";
import type { Vector3 } from "viewer/constants";
import { useCallback, useRef } from "react";
import { LongUnitToShortUnitMap, type Vector3 } from "viewer/constants";
import { getDatasetExtentInVoxel } from "viewer/model/accessors/dataset_accessor";
import { getPosition } from "viewer/model/accessors/flycam_accessor";
import { setPositionAction } from "viewer/model/actions/flycam_actions";
import { convertVoxelSizeToUnit } from "viewer/model/scaleinfo";
import Store from "viewer/store";
import { ShareButton } from "viewer/view/action-bar/share_modal_view";
import ButtonComponent from "viewer/view/components/button_component";
Expand All @@ -36,7 +39,9 @@ const positionInputErrorStyle: React.CSSProperties = {
function DatasetPositionView() {
const flycam = useWkSelector((state) => state.flycam);
const dataset = useWkSelector((state) => state.dataset);
const voxelSize = useWkSelector((state) => state.dataset.dataSource.scale);
const task = useWkSelector((state) => state.task);
const maybeErrorMessageRef = useRef<string | null>(null);

const copyPositionToClipboard = async () => {
const position = V3.floor(getPosition(flycam)).join(", ");
Expand Down Expand Up @@ -90,6 +95,36 @@ function DatasetPositionView() {
} else if (!maybeErrorMessage && isOutOfTaskBounds) {
maybeErrorMessage = message["tracing.out_of_task_bounds"];
}
maybeErrorMessageRef.current = maybeErrorMessage;

const getPositionTooltipContent = useCallback(() => {
if (maybeErrorMessageRef.current != null) return null;
const currentFlycam = Store.getState().flycam;
const currentPosition = V3.floor(getPosition(currentFlycam));
const shortUnit = LongUnitToShortUnitMap[voxelSize.unit];
const positionInVxStrings = currentPosition.map((coord) => formatVoxelsForHighNumbers(coord));
const voxelSizeInMetricUnit = convertVoxelSizeToUnit(voxelSize, shortUnit);
const positionInMetrics = currentPosition.map(
(coord, index) => coord * voxelSizeInMetricUnit[index],
);
const positionInMetricStrings = positionInMetrics.map((coord) =>
formatNumberToLength(coord, shortUnit),
);
return (
<div>
<Row justify="space-between" gutter={16} wrap={false}>
<Col span={8}>{positionInVxStrings[0]},</Col>
<Col span={8}>{positionInVxStrings[1]},</Col>
<Col span={8}>{positionInVxStrings[2]}</Col>
</Row>
<Row justify="space-between" gutter={16} wrap={false}>
<Col span={8}>{positionInMetricStrings[0]},</Col>
<Col span={8}>{positionInMetricStrings[1]},</Col>
<Col span={8}>{positionInMetricStrings[2]}</Col>
</Row>
</div>
);
}, [voxelSize]);

return (
<FastTooltip title={maybeErrorMessage || null} wrapper="div">
Expand All @@ -107,13 +142,15 @@ function DatasetPositionView() {
<PushpinOutlined style={positionIconStyle} />
</ButtonComponent>
</FastTooltip>
<Vector3Input
value={position}
onChange={handleChangePosition}
autoSize
style={positionInputStyle}
allowDecimals
/>
<FastTooltip dynamicRenderer={getPositionTooltipContent}>
<Vector3Input
value={position}
onChange={handleChangePosition}
autoSize
style={positionInputStyle}
allowDecimals
/>
</FastTooltip>
<DatasetRotationPopoverButtonView style={iconColoringStyle} />
<ShareButton dataset={dataset} style={iconColoringStyle} />
</Space.Compact>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ export default function DistanceMeasurementTooltip() {
pointerEvents: isMeasuring ? "none" : "auto",
}}
>
<DistanceEntry distance={valueInMetricUnit} />
<DistanceEntry distance={valueInVx} />
<DistanceEntry distance={valueInMetricUnit} />
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { APIDataset, APIUser } from "types/api_types";
import type { ArrayElement } from "types/globals";
import { userSettings } from "types/schemas/user_settings.schema";
import type { ViewMode } from "viewer/constants";
import Constants, { BLEND_MODES } from "viewer/constants";
import Constants, { BLEND_MODES, LongUnitToShortUnitMap } from "viewer/constants";
import defaultState from "viewer/default_state";
import { getValidZoomRangeForUser } from "viewer/model/accessors/flycam_accessor";
import { setZoomStepAction } from "viewer/model/actions/flycam_actions";
Expand Down Expand Up @@ -238,11 +238,11 @@ class ControlsAndRenderingSettingsTab extends PureComponent<ControlsAndRendering
};

render() {
const datasetScaleUnit = LongUnitToShortUnitMap[this.props.dataset.dataSource.scale.unit];
const moveValueString = `${settingsLabels.moveValue} (${datasetScaleUnit}/s)`;
const moveValueSetting = Constants.MODES_ARBITRARY.includes(this.props.viewMode) ? (
<NumberSliderSetting
label={
<FastTooltip title={settingsTooltips.moveValue}>{settingsLabels.moveValue}</FastTooltip>
}
label={<FastTooltip title={settingsTooltips.moveValue}>{moveValueString}</FastTooltip>}
min={userSettings.moveValue3d.minimum}
max={userSettings.moveValue3d.maximum}
step={10}
Expand All @@ -252,9 +252,7 @@ class ControlsAndRenderingSettingsTab extends PureComponent<ControlsAndRendering
/>
) : (
<NumberSliderSetting
label={
<FastTooltip title={settingsTooltips.moveValue}>{settingsLabels.moveValue}</FastTooltip>
}
label={<FastTooltip title={settingsTooltips.moveValue}>{moveValueString}</FastTooltip>}
min={userSettings.moveValue.minimum}
max={userSettings.moveValue.maximum}
step={10}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ import {
import { getSpecificDefaultsForLayer } from "types/schemas/dataset_view_configuration_defaults";
import { userSettings } from "types/schemas/user_settings.schema";
import type { Vector3 } from "viewer/constants";
import Constants, { ControlModeEnum, IdentityTransform, MappingStatusEnum } from "viewer/constants";
import Constants, {
ControlModeEnum,
IdentityTransform,
LongUnitToShortUnitMap,
MappingStatusEnum,
} from "viewer/constants";
import defaultState from "viewer/default_state";
import {
getDefaultValueRangeOfLayer,
Expand Down Expand Up @@ -1169,8 +1174,14 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps, State> {
};

getSkeletonLayer = () => {
const { controlMode, annotation, onChangeRadius, userConfiguration, onChangeShowSkeletons } =
this.props;
const {
controlMode,
annotation,
onChangeRadius,
userConfiguration,
onChangeShowSkeletons,
dataset,
} = this.props;
const isPublicViewMode = controlMode === ControlModeEnum.VIEW;

if (isPublicViewMode || annotation.skeleton == null) {
Expand All @@ -1182,6 +1193,7 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps, State> {
const isOnlyAnnotationLayer = annotation.annotationLayers.length === 1;
const { showSkeletons, tracingId } = skeletonTracing;
const activeNodeRadius = getActiveNode(skeletonTracing)?.radius ?? 0;
const unit = LongUnitToShortUnitMap[dataset.dataSource.scale.unit];
return (
<React.Fragment>
<div
Expand Down Expand Up @@ -1247,7 +1259,7 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps, State> {
}}
>
<LogSliderSetting
label="Node Radius"
label={`Node Radius (${unit})`}
min={userSettings.nodeRadius.minimum}
max={userSettings.nodeRadius.maximum}
roundTo={0}
Expand All @@ -1258,9 +1270,9 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps, State> {
/>
<NumberSliderSetting
label={
userConfiguration.overrideNodeRadius
(userConfiguration.overrideNodeRadius
? settings.particleSize
: `Min. ${settings.particleSize}`
: `Min. ${settings.particleSize}`) + ` (${unit})`
}
min={userSettings.particleSize.minimum}
max={userSettings.particleSize.maximum}
Expand All @@ -1280,7 +1292,7 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps, State> {
/>
) : (
<LogSliderSetting
label={settings.clippingDistance}
label={settings.clippingDistance + ` (${unit})`}
roundTo={3}
min={userSettings.clippingDistance.minimum}
max={userSettings.clippingDistance.maximum}
Expand Down
2 changes: 2 additions & 0 deletions unreleased_changes/9127.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
### Added
- Added explicit units to some layer and annotation settings, such as the node radius or the particle size.