Skip to content

Commit bece638

Browse files
authored
Visual Masking Configuration (#420)
* visual mask configuration * update config, remove polygon * add documentation * remove polygon references * remove polygon test * unify config owner/admin testing to configurationManager * remove polygon references * update documentation * default opacity * update visual mask documentation * add relative support for masking * increment version
1 parent 740cae0 commit bece638

21 files changed

Lines changed: 1648 additions & 38 deletions

client/dive-common/components/ConfigurationEditor.vue

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,11 @@ export default defineComponent({
4040
const configMan = useConfiguration();
4141
const getUISetting = (key: UISettingsKey) => (configMan.getUISetting(key));
4242
const girderRest = useGirderRest();
43-
const isAdminOwner = computed(() => {
44-
let ownerAdmin = false;
45-
if (girderRest.user) {
46-
ownerAdmin = girderRest.user.admin;
47-
}
48-
const id = girderRest.user._id;
49-
const groups = girderRest.user.groups as string[];
50-
if (configMan.configOwners.value.users.findIndex((item) => item.id === id) !== -1) {
51-
ownerAdmin = true;
52-
}
53-
groups.forEach((group) => {
54-
if (configMan.configOwners.value.groups.findIndex((item) => item.id === group) !== -1) {
55-
ownerAdmin = true;
56-
}
57-
});
58-
return ownerAdmin;
59-
});
43+
const isAdminOwner = computed(() => configMan.isConfigOwnerAdmin(girderRest.user as ({
44+
admin?: boolean;
45+
_id?: string;
46+
groups?: string[];
47+
} | null)));
6048
const hasConfig = computed(() => !!configMan.configuration.value);
6149
const menuOpen = ref(false);
6250
const additive = ref(false);

client/dive-common/components/DatasetInfoAttributes.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ export default defineComponent({
230230
small
231231
class="ml-2"
232232
v-bind="attrs"
233-
v-on="on"
234233
aria-label="Detection attribute settings"
234+
v-on="on"
235235
@click.stop
236236
>
237237
<v-icon small>

client/dive-common/components/EditorMenu.vue

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { Mousetrap, OverlayPreferences } from 'vue-media-annotator/types';
77
import { EditAnnotationTypes, VisibleAnnotationTypes } from 'vue-media-annotator/layers';
88
import Recipe from 'vue-media-annotator/recipe';
99
import { hexToRgb } from 'vue-media-annotator/utils';
10-
import { useMasks } from 'vue-media-annotator/provides';
10+
import { useMasks, useConfiguration, useVisualMaskManager } from 'vue-media-annotator/provides';
11+
import { useStore } from 'platform/web-girder/store/types';
1112
import MaskTracking from 'dive-common/components/MaskTracking.vue';
1213
1314
interface ButtonData {
@@ -79,6 +80,17 @@ export default defineComponent({
7980
const toolTipForce = ref(false);
8081
let toolTimeTimeout: number | undefined;
8182
const { editorOptions, editorFunctions } = useMasks();
83+
const configMan = useConfiguration();
84+
const visualMaskManager = useVisualMaskManager();
85+
const store = useStore();
86+
const isOwnerAdmin = computed(() => {
87+
const currentUser = store.state.User.user as ({
88+
admin?: boolean;
89+
_id?: string;
90+
groups?: string[];
91+
} | null);
92+
return configMan.isConfigOwnerAdmin(currentUser);
93+
});
8294
const modeToolTips = {
8395
Creating: {
8496
rectangle: 'Drag to draw rectangle. Press ESC to exit.',
@@ -217,6 +229,14 @@ export default defineComponent({
217229
tooltip: 'Tooltip Information about Hovered over annotations',
218230
click: () => toggleVisible('tooltip'),
219231
},
232+
{
233+
id: 'VisualMask',
234+
type: 'VisualMask',
235+
active: isVisible('VisualMask'),
236+
icon: 'mdi-image-filter-center-focus-strong',
237+
tooltip: 'Configuration visual masks',
238+
click: () => toggleVisible('VisualMask'),
239+
},
220240
];
221241
if (props.attributeKey) {
222242
buttons.push({
@@ -228,12 +248,18 @@ export default defineComponent({
228248
click: () => toggleVisible('attributeKey'),
229249
});
230250
}
251+
if (!isOwnerAdmin.value || !visualMaskManager.hasMasks.value) {
252+
return buttons.filter((button) => button.id !== 'VisualMask');
253+
}
231254
return buttons;
232255
});
233256
234257
const isVisible = (mode: VisibleAnnotationTypes) => props.visibleModes.includes(mode);
235258
236259
const toggleVisible = (mode: VisibleAnnotationTypes) => {
260+
if (mode === 'VisualMask' && (!isOwnerAdmin.value || !visualMaskManager.hasMasks.value)) {
261+
return;
262+
}
237263
if (isVisible(mode)) {
238264
emit('set-annotation-state', {
239265
visible: props.visibleModes.filter((m) => m !== mode),

client/dive-common/components/SidebarContext.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,8 @@ export default defineComponent({
8686
overflow-y: hidden;
8787
}
8888
.sidebar-content {
89+
flex: 1 1 auto;
90+
min-height: 0;
91+
overflow: hidden;
8992
}
9093
</style>

client/dive-common/components/Viewer.vue

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
StyleManager, TrackFilterControls, GroupFilterControls,
2323
ConfigurationManager,
2424
} from 'vue-media-annotator/index';
25+
import VisualMaskManager from 'vue-media-annotator/visualMasks';
2526
import { provideAnnotator } from 'vue-media-annotator/provides';
2627
2728
import {
@@ -66,6 +67,7 @@ import { useRoute } from 'vue-router/composables';
6667
import AttributeShortcutToggle from './Attributes/AttributeShortcutToggle.vue';
6768
import GroupSidebarVue from './GroupSidebar.vue';
6869
import MultiCamToolsVue from './MultiCamTools.vue';
70+
import VisualMaskSidebarVue from './VisualMaskSidebar.vue';
6971
import PrevNext from './PrevNext.vue';
7072
import AttributesSideBarVue from './Attributes/AttributesSideBar.vue';
7173
import TypeThresholdVue from './TypeThreshold.vue';
@@ -172,6 +174,14 @@ export default defineComponent({
172174
});
173175
174176
const store = useStore();
177+
const isConfigOwnerAdmin = computed(() => {
178+
const currentUser = store.state.User.user as ({
179+
admin?: boolean;
180+
_id?: string;
181+
groups?: string[];
182+
} | null);
183+
return configurationManager.isConfigOwnerAdmin(currentUser);
184+
});
175185
176186
const {
177187
save: saveToServer,
@@ -208,12 +218,32 @@ export default defineComponent({
208218
const vuetify = inject('vuetify') as Vuetify;
209219
const trackStyleManager = new StyleManager({ markChangesPending, vuetify });
210220
const groupStyleManager = new StyleManager({ markChangesPending, vuetify });
221+
const visualMaskStyleManager = new StyleManager({ markChangesPending, vuetify });
211222
212223
const cameraStore = new CameraStore({ markChangesPending });
213224
// eslint-disable-next-line max-len
214225
const configurationManager = new ConfigurationManager({
215226
configurationId, setConfigurationId, saveConfiguration, transferConfiguration,
216227
});
228+
const visualMaskManager = new VisualMaskManager({
229+
markChangesPending,
230+
styleManager: visualMaskStyleManager,
231+
syncConfiguration: (visualMasks) => {
232+
if (!configurationManager.configuration.value) {
233+
if (!Object.keys(visualMasks).length) {
234+
return;
235+
}
236+
configurationManager.setConfiguration({});
237+
}
238+
if (configurationManager.configuration.value) {
239+
if (Object.keys(visualMasks).length) {
240+
configurationManager.configuration.value.visualMasks = visualMasks;
241+
} else {
242+
delete configurationManager.configuration.value.visualMasks;
243+
}
244+
}
245+
},
246+
});
217247
218248
// This context for removal
219249
const removeGroups = (id: AnnotationId) => {
@@ -507,6 +537,10 @@ export default defineComponent({
507537
handler.trackSelect(selectedTrackId.value, false);
508538
}
509539
try {
540+
await configurationManager.saveConfiguration(
541+
configurationId.value,
542+
{ visualMasks: visualMaskManager.serialize() },
543+
);
510544
await saveToServer({
511545
customTypeStyling: trackStyleManager.getTypeStyles(trackFilters.allTypes),
512546
customGroupStyling: groupStyleManager.getTypeStyles(groupFilters.allTypes),
@@ -526,7 +560,8 @@ export default defineComponent({
526560
}
527561
} catch (err) {
528562
let text = 'Unable to Save Data';
529-
if (err.response && err.response.status === 403) {
563+
const errorResponse = err as { response?: { status?: number } };
564+
if (errorResponse.response && errorResponse.response.status === 403) {
530565
text = 'You do not have permission to Save Data to this Folder.';
531566
}
532567
await prompt({
@@ -635,6 +670,7 @@ export default defineComponent({
635670
configurationManager.setConfiguration(
636671
config.diveConfig.metadata.configuration,
637672
);
673+
visualMaskManager.load(config.diveConfig.metadata.configuration.visualMasks);
638674
639675
if (config.diveConfig.metadata.configuration.general?.baseConfiguration) {
640676
configurationManager.setConfigurationId(
@@ -644,6 +680,8 @@ export default defineComponent({
644680
if (config.diveConfig.metadata.configuration.filterTimelines) {
645681
useTimelineFilters.loadFilterTimelines(config.diveConfig.metadata.configuration.filterTimelines);
646682
}
683+
} else {
684+
visualMaskManager.load();
647685
}
648686
const flatUIMap = configurationManager.getFlatUISettingMap();
649687
ctx.emit('get-ui-settings', flatUIMap);
@@ -836,6 +874,18 @@ export default defineComponent({
836874
component: DatasetInfo,
837875
});
838876
}
877+
if (!configurationManager.getUISetting('UIVisualMasks') || !isConfigOwnerAdmin.value) {
878+
context.unregister({
879+
description: 'Visual Masks',
880+
component: VisualMaskSidebarVue,
881+
});
882+
} else {
883+
context.register({
884+
description: 'Visual Masks',
885+
component: VisualMaskSidebarVue,
886+
width: 340,
887+
});
888+
}
839889
840890
if (!configurationManager.getUISetting('UIThresholdControls')) {
841891
context.unregister({
@@ -861,7 +911,7 @@ export default defineComponent({
861911
progress.loaded = false;
862912
console.error(err);
863913
const errorEl = document.createElement('div');
864-
errorEl.innerHTML = getResponseError(err);
914+
errorEl.innerHTML = getResponseError(err as never);
865915
loadError.value = errorEl.innerText
866916
.concat(". If you don't know how to resolve this, please contact the server administrator.");
867917
throw err;
@@ -979,6 +1029,8 @@ export default defineComponent({
9791029
editingMode,
9801030
groupFilters,
9811031
groupStyleManager,
1032+
visualMaskManager,
1033+
visualMaskStyleManager,
9821034
multiSelectList,
9831035
pendingSaveCount,
9841036
progress,
@@ -1063,6 +1115,7 @@ export default defineComponent({
10631115
selectedTrackId,
10641116
editingGroupId,
10651117
selectedKey,
1118+
visualMaskManager,
10661119
trackFilters,
10671120
videoUrl,
10681121
overlays,
@@ -1168,7 +1221,7 @@ export default defineComponent({
11681221
recipes,
11691222
multiSelectActive,
11701223
editingDetails,
1171-
overlays,
1224+
overlays: overlays ? [...overlays] : [],
11721225
groupEditActive: editingGroupId !== null,
11731226
}"
11741227
:get-u-i-setting="getUISetting"
@@ -1303,7 +1356,7 @@ export default defineComponent({
13031356
v-mousetrap="[
13041357
{ bind: 'n', handler: () => !readonlyState && handler.trackAdd() },
13051358
{ bind: 'r', handler: () => aggregateController.resetZoom() },
1306-
{ bind: 'esc', handler: () => getUISetting('UISelection') && handler.trackAbort() },
1359+
{ bind: 'esc', handler: () => (visualMaskManager.editingMaskId !== null ? visualMaskManager.stopEditing() : (getUISetting('UISelection') && handler.trackAbort())) },
13071360
]"
13081361
class="d-flex flex-column grow"
13091362
>
@@ -1337,7 +1390,7 @@ export default defineComponent({
13371390
<LayerManager
13381391
v-if="progress.loaded"
13391392
:camera="camera"
1340-
:overlays="overlays"
1393+
:overlays="overlays ? [...overlays] : []"
13411394
/>
13421395
</component>
13431396
</div>

0 commit comments

Comments
 (0)