Skip to content

Commit

Permalink
toggle and resize detection and behaviour bars
Browse files Browse the repository at this point in the history
  • Loading branch information
reinhrst committed Feb 27, 2024
1 parent 30592a9 commit f435bbe
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 32 deletions.
5 changes: 3 additions & 2 deletions src/lib/typeCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export class NumberChecker extends Checker<number> {
constructor(
options?: {
isInt?: boolean,
isFinite?: boolean,
min?: number,
max?: number,
valid?: (n: number) => boolean
Expand All @@ -154,12 +155,12 @@ export class NumberChecker extends Checker<number> {
}
}
if (options?.min !== undefined) {
if (n > options.min) {
if (n < options.min) {
return false
}
}
if (options?.max !== undefined) {
if (n < options.max) {
if (n > options.max) {
return false
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/viewer/DetectionBarDetections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const SCALES = [
] as const;

const TOP_SCALING_FACTOR = 2;
const BETWEEN_LAYERS_HEIGHT = 10;
const BETWEEN_LAYERS_HEIGHT_FACTOR = 0.1;

export const DetectionBarDetections: FunctionComponent = () => {
const currentFrameNumber = useSelector(selectCurrentFrameNumber)
Expand Down Expand Up @@ -200,7 +200,7 @@ export const DetectionBarDetections: FunctionComponent = () => {
style={{"--svgWidth": svgRect.width, "--svgHeight": svgRect.height,
"--totalNumberOfFrames": totalFrames, "--maxCount": scalingInfo[0],
"--currentFrame": currentFrameNumber,
"--between-layers-height": BETWEEN_LAYERS_HEIGHT,
"--between-layers-height": svgRect.height * BETWEEN_LAYERS_HEIGHT_FACTOR,
"--top-scaling-factor": TOP_SCALING_FACTOR
}}
onMouseDown={ev => {
Expand Down
3 changes: 3 additions & 0 deletions src/viewer/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const SideBar: FunctionComponent = () => {
<Button controlInfo={CONTROLS.show_controls} />
<Button controlInfo={CONTROLS.class_sliders} />
<Button controlInfo={CONTROLS.key_shortcut_help_toggle} />
<Button controlInfo={CONTROLS.show_detection_bar} />
<Button controlInfo={CONTROLS.show_behaviour_bar} />
<Button controlInfo={CONTROLS.sizer} />
</div>
</div>
}
46 changes: 46 additions & 0 deletions src/viewer/Sizer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { FunctionComponent } from "preact"
import { useSelector } from "react-redux"
import { behaviourBarSizeSet, behaviourBarToggled, controlPaneToggled, detectionBarSizeSet, detectionBarToggled, selectBehaviourBarShown, selectBehaviourBarSize, selectControlPanelShown, selectDetectionBarShown, selectDetectionBarSize } from "./generalSettingsSlice"
import { useAppDispatch } from "./store"
import { Icon } from "../lib/Icon"
import * as css from "./sizer.module.css"

export const Sizer: FunctionComponent = () => {
const controlPaneShown = useSelector(selectControlPanelShown)
const detectionBarShown = useSelector(selectDetectionBarShown)
const detectionBarSize = useSelector(selectDetectionBarSize)
const behaviourBarShown = useSelector(selectBehaviourBarShown)
const behaviourBarSize = useSelector(selectBehaviourBarSize)
const dispatch = useAppDispatch()
return <div>
<h3>control panel</h3>
<div>
<div className={css.on_off} onClick={() => dispatch(controlPaneToggled())}>
<Icon iconName={controlPaneShown ? "check_box" : "check_box_outline_blank"}
/> visible
</div>
</div>
<h3>detection bar</h3>
<div>
<div className={css.on_off} onClick={() => dispatch(detectionBarToggled())}>
<Icon iconName={detectionBarShown ? "check_box" : "check_box_outline_blank"}
/> visible
</div>
<input type="range" min={5} max={45} disabled={!detectionBarShown}
className={css.slider} value={detectionBarSize} onChange={
e => dispatch(detectionBarSizeSet(e.currentTarget.valueAsNumber))}/>
</div>
<h3>behaviour bar</h3>
<div>
<div className={css.on_off} onClick={() => dispatch(behaviourBarToggled())}>
<Icon iconName={behaviourBarShown ? "check_box" : "check_box_outline_blank"}
/> visible
</div>
<input type="range" min={5} max={45} disabled={!behaviourBarShown}
className={css.slider} value={behaviourBarSize} onChange={
e => dispatch(behaviourBarSizeSet(e.currentTarget.valueAsNumber))}/>
</div>

</div>
}

27 changes: 20 additions & 7 deletions src/viewer/Viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ControlPanel } from "./ControlPanel";
import { KeyShortcuts } from "./KeyShortcuts";
import { useEffect } from "react";
import { assert, exhausted, isCompatibleBrowser, joinedStringFromDict, mayBeUndefined } from "../lib/util";
import { selectControlPanelShown } from "./generalSettingsSlice";
import { selectBehaviourBarShown, selectBehaviourBarSize, selectControlPanelShown, selectDetectionBarShown, selectDetectionBarSize } from "./generalSettingsSlice";
import { MultipleActionsAssignedToPressedKeyException, appErrorSet, lastKeyPressedSet, selectAppError, selectSidebarPopup, sidebarPopupWasClosed} from "./appSlice"
import { ClassSliders } from "./ClassSliders"
import { Uploader } from "./Uploader"
Expand All @@ -20,9 +20,14 @@ import { keyFromEvent, keyToString } from "../lib/key";
import { selectActionByKeyString } from "./shortcutsSlice";
import { executeShortcutAction } from "./reducers";
import { ErrorPopup } from "./Error";
import { Sizer} from "./Sizer"

export const Viewer: FunctionComponent = () => {
const playerInfoShown = useSelector(selectControlPanelShown)
const controlPaneShown = useSelector(selectControlPanelShown)
const detectionBarShown = useSelector(selectDetectionBarShown)
const detectionBarSize = useSelector(selectDetectionBarSize)
const behaviourBarShown = useSelector(selectBehaviourBarShown)
const behaviourBarSize = useSelector(selectBehaviourBarSize)
const error = useSelector(selectAppError)
useEffect(() => {
if (!isCompatibleBrowser()) {
Expand All @@ -36,16 +41,21 @@ export const Viewer: FunctionComponent = () => {

return <div className={joinedStringFromDict({
[css.viewer]: true,
[css.no_controlpanel]: !playerInfoShown,
})}>
[css.no_controlpanel]: !controlPaneShown,
[css.no_detectionbar]: !detectionBarShown,
[css.no_behaviourbar]: !behaviourBarShown,
})} style={{
"--behaviourbar-height": `${behaviourBarSize.toFixed(1)}vh`,
"--detectionbar-height": `${detectionBarSize.toFixed(1)}vh`,
}}>
{error && <ErrorPopup error={error} />}
<ShortcutsHandler />
<Popup />
<SideBar />
<VideoPlayer />
{playerInfoShown && <ControlPanel />}
<DetectionBar />
<Behaviour />
{controlPaneShown && <ControlPanel />}
{detectionBarShown && <DetectionBar />}
{behaviourBarShown && <Behaviour />}
</div>
}

Expand All @@ -54,6 +64,7 @@ const Popup: FunctionComponent = () => {
const dispatch = useAppDispatch()
const noSuppressShortcuts = popup === "keyShortcuts"
return popup && <Dialog noSuppressShortcuts={noSuppressShortcuts}
className={css[`popup_${popup}`]}
onRequestClose={() => dispatch(sidebarPopupWasClosed())} onClose={() => dispatch(sidebarPopupWasClosed())}>
{(() => {
switch (popup) {
Expand All @@ -65,6 +76,8 @@ const Popup: FunctionComponent = () => {
return <KeyShortcuts onRequestClose={() => dispatch(sidebarPopupWasClosed())} />
case "uploader":
return <Uploader onRequestClose={() => dispatch(sidebarPopupWasClosed())} />
case "sizer":
return <Sizer />
default: {
exhausted(popup)
}
Expand Down
2 changes: 1 addition & 1 deletion src/viewer/appSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { type ActionAlreadyInUseException, createOrUpdateAction, exportPreset, i
import { Key } from '../lib/key'
import { addBehaviourInfoLine, editBehaviourInfoLineField, NoWritableBehaviourFileException, removeBehaviourInfoLine, setCurrentlyEditingFieldIndex, toggleBehaviourInfoCurrentlySelectedSubject } from './behaviourSlice'

export type SidebarPopup = "info" | "classSliders" | "keyShortcuts" | "uploader"
export type SidebarPopup = "info" | "classSliders" | "keyShortcuts" | "uploader" | "sizer"
export const zoomLevels = [1, 2, 3, 5] as const
export type ZoomLevel = number

Expand Down
45 changes: 35 additions & 10 deletions src/viewer/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { selectDetectionInfoPotentiallyNull } from "./detectionsSlice";
import { SidebarPopup, hideDetectionBoxesToggled, selectHideDetectionBoxes, selectSidebarPopup, selectZoom, sidebarPopupWasToggled, zoomToggled } from "./appSlice";
import { currentlySelectedLineUpdated, removeBehaviourInfoLine, selectBehaviourInfo, selectCurrentlySelectedSubject, setCurrentlyEditingFieldIndex} from "./behaviourSlice";
import { selectSelectedBehaviourLine } from "./selectors";
import { playerInfoToggled, selectFramenumberIndexInLayout, selectControlPanelShown } from "./generalSettingsSlice";
import { selectFramenumberIndexInLayout, selectControlPanelShown, controlPaneToggled, selectBehaviourBarShown, behaviourBarToggled, detectionBarToggled, selectDetectionBarShown } from "./generalSettingsSlice";

export type ControlInfo<T> = {
iconName: ValidIconName
Expand Down Expand Up @@ -78,6 +78,12 @@ export const CONTROLS = {
name: "Upload files and start a new detection",
}),

sizer: createPopupControl({
iconName: "height",
popupName: "sizer",
name: "Set the sizes of different elements",
}),

key_shortcut_help_toggle: createPopupControl({
iconName: "indeterminate_question_box",
name: "key shortcuts help and customization",
Expand Down Expand Up @@ -161,9 +167,8 @@ export const CONTROLS = {
iconName: "vertical_align_top",
description: "previous behaviour line",
selectIsDisabled: state => {
const behaviourInfo = selectBehaviourInfo(state)
if (!behaviourInfo) return true
const selectedBehaviourLine = selectSelectedBehaviourLine(state)!
const selectedBehaviourLine = selectSelectedBehaviourLine(state)
if (!selectedBehaviourLine) return true
return (selectedBehaviourLine.index === 1 && selectedBehaviourLine.rel === "at") || selectedBehaviourLine.index === 0
},
selectActionArgument: state => ({
Expand All @@ -188,8 +193,10 @@ export const CONTROLS = {
description: "next behaviour line",
selectIsDisabled: state => {
const behaviourInfo = selectBehaviourInfo(state)
if (!behaviourInfo) return true
const selectedBehaviourLine = selectSelectedBehaviourLine(state)!
const selectedBehaviourLine = selectSelectedBehaviourLine(state)
if (!behaviourInfo || !selectedBehaviourLine) {
return true
}
return selectedBehaviourLine.index === behaviourInfo.lines.length - 1
},
selectActionArgument: state => ({
Expand All @@ -213,9 +220,10 @@ export const CONTROLS = {
iconName: "delete",
description: "remove the selected behaviour line",
selectIsDisabled: state => {
const behaviourInfo = selectBehaviourInfo(state)
if (!behaviourInfo) return true
const selectedBehaviourLine = selectSelectedBehaviourLine(state)!
const selectedBehaviourLine = selectSelectedBehaviourLine(state)
if (!selectedBehaviourLine) {
return true
}
return selectedBehaviourLine.rel !== "at"
},
selectActionArgument: state => ({
Expand Down Expand Up @@ -264,7 +272,24 @@ export const CONTROLS = {
description: "Toggle whether controls and info is shown next to the player",
selectIsDisabled: () => false,
selectIsActivated: state => selectControlPanelShown(state),
action: dispatch => dispatch(playerInfoToggled())
action: dispatch => dispatch(controlPaneToggled())
}),

show_detection_bar: fillAndWrapDefaultControlInfo({
iconName: "bar_chart_4_bars",
description: "Toggle whether detection bar is shown",
selectIsDisabled: () => false,
selectIsActivated: state => selectDetectionBarShown(state),
action: dispatch => dispatch(detectionBarToggled())
}),


show_behaviour_bar: fillAndWrapDefaultControlInfo({
iconName: "data_table",
description: "Toggle whether behaviour bar is shown",
selectIsDisabled: () => false,
selectIsActivated: state => selectBehaviourBarShown(state),
action: dispatch => dispatch(behaviourBarToggled())
}),

zoom_toggle: fillAndWrapDefaultControlInfo({
Expand Down
40 changes: 36 additions & 4 deletions src/viewer/generalSettingsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from './store'
import { assert, mayBeUndefined } from '../lib/util'
import {HSL} from "../lib/colour"
import { Checker, LiteralChecker, RecordChecker, StringChecker, getCheckerFromObject } from '../lib/typeCheck';
import { Checker, LiteralChecker, NumberChecker, RecordChecker, StringChecker, getCheckerFromObject } from '../lib/typeCheck';
import { DetectionInfo } from '../lib/detections';


Expand All @@ -26,12 +26,16 @@ export type GeneralSettingsState = {
settingsByDetectionClassByKey: Record<string, Record<`${number}`, SettingsForDetectionClass>>
confidenceLocation: ConfidenceLocation
showControlPanel: boolean
detectionBar: {visible: boolean, size: number}
behaviourBar: {visible: boolean, size: number}
}

const defaultGeneralSettings: GeneralSettingsState = {
settingsByDetectionClassByKey: {},
confidenceLocation: "outer-right-bottom",
showControlPanel: true,
detectionBar: {visible: true, size: 10},
behaviourBar: {visible: true, size: 10},
}

function isJSON(s: string): boolean {
Expand Down Expand Up @@ -63,6 +67,8 @@ export const generalSettingsChecker: Checker<GeneralSettingsState> = getCheckerF
"outer-left-bottom", "outer-center-bottom", "outer-right-bottom",
]),
showControlPanel: true,
detectionBar: {visible: true, size: new NumberChecker({min: 5, max: 45, isFinite: true,})},
behaviourBar: {visible: true, size: new NumberChecker({min: 5, max: 45, isFinite: true,})},
})

const LOCAL_STORAGE_GENERAL_SETTINGS_KEY = "Behave_General_Settings"
Expand Down Expand Up @@ -160,9 +166,23 @@ export const generalSettingsSlice = createSlice({
confidenceLocationUpdated: (state, action: PayloadAction<ConfidenceLocation>) => {
state.confidenceLocation = action.payload
},
playerInfoToggled: state => {
controlPaneToggled: state => {
state.showControlPanel = !state.showControlPanel
}
},
detectionBarToggled: state => {
state.detectionBar.visible = !state.detectionBar.visible
},
detectionBarSizeSet: (state, {payload: newSize}: PayloadAction<number>) => {
assert(Number.isFinite(newSize))
state.detectionBar.size = Math.min(45, Math.max(5, newSize))
},
behaviourBarToggled: state => {
state.behaviourBar.visible = !state.behaviourBar.visible
},
behaviourBarSizeSet: (state, {payload: newSize}: PayloadAction<number>) => {
assert(Number.isFinite(newSize))
state.behaviourBar.size = Math.min(45, Math.max(5, newSize))
},
}
})

Expand All @@ -173,14 +193,26 @@ export const {
hideToggled,
colourUpdated,
confidenceLocationUpdated,
playerInfoToggled,
controlPaneToggled,
detectionBarToggled,
detectionBarSizeSet,
behaviourBarToggled,
behaviourBarSizeSet,
} = generalSettingsSlice.actions

export const selectSettingsByDetectionClassByKey = (state: RootState) => state.settings.general.settingsByDetectionClassByKey
export const selectConfidenceLocation = (state: RootState) => state.settings.general.confidenceLocation

export const selectControlPanelShown = (state: RootState) => state.settings.general.showControlPanel

export const selectDetectionBarSize = (state: RootState) => state.settings.general.detectionBar.size

export const selectDetectionBarShown = (state: RootState) => state.settings.general.detectionBar.visible

export const selectBehaviourBarSize = (state: RootState) => state.settings.general.behaviourBar.size

export const selectBehaviourBarShown = (state: RootState) => state.settings.general.behaviourBar.visible

export type BehaviourColoumnType = "frameNumber" | "pts" | `dateTime:${string}` | "subject" | "behaviour" | `comments:${string}`
export type BehaveLayout = Array<{width: number | "*", type: BehaviourColoumnType}>

Expand Down
6 changes: 6 additions & 0 deletions src/viewer/sizer.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.on_off {
cursor: pointer;
}
.slider {
width: 10em;
}
21 changes: 15 additions & 6 deletions src/viewer/viewer.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ body {
padding: 0;
margin: 0;
--sidebar-width: 2.1em;
--detectionbar-height: min(5em, 10vh);
--behaviour-height: 5em;
--controlpanel-width: 10em;
position: fixed;
}
Expand Down Expand Up @@ -39,11 +37,17 @@ kbd + kbd {
display: grid;
grid:
"sidebar videoplayer controlpanel" 1fr
"sidebar detectionbar detectionbar" var(--detectionbar-height)
"sidebar behaviour behaviour" var(--behaviour-height)
/ var(--sidebar-width) 1fr var(--controlpanel-width);
"sidebar detectionbar detectionbar" var(--force-detectionbar-height, var(--detectionbar-height))
"sidebar behaviour behaviour" var(--force-behaviour-height, var(--behaviourbar-height))
/ var(--sidebar-width) 1fr var(--force-controlpanel-width, var(--controlpanel-width));
&.no_controlpanel {
--controlpanel-width: 0;
--force-controlpanel-width: 0;
}
&.no_detectionbar {
--force-detectionbar-height: 0;
}
&.no_behaviourbar {
--force-behaviour-height: 0;
}
height: 100vh;
width: 100vw;
Expand Down Expand Up @@ -73,3 +77,8 @@ kbd + kbd {
background-color: hsl(140, 50%, 95%);
overflow: scroll;
}

.popup_sizer {
margin-left: calc(max(var(--sidebar-width) + 1em, 3em));
max-width: fit-content;
}

0 comments on commit f435bbe

Please sign in to comment.