9427686d42
* fix(fe2): unfollow on camera move * WIP new state hydration function * WIP sync state * minor cleanup * fix coloring not being tracked * fix for post thread close camera pos restore * supporting duplicate users * preventing guest commenting + state reset fixes * fixed guests not receiving viewer comment updates * post-thread creation opens new thread * removing gap between 'X is typing' and bubble appearing * reset filters will also reset colors now * fixed thread full context * camera reset fix * thread reset fix * fixed router concurrency issues * followed user avatar fix * TONS OF DEBUGGING FOR ROUTER QUEUING * removing queued routing debugging stuff + disabling spotlight cancelation * WIP async URL updates * missing authLogger fixed * fix for broken projection * fix for bubbles positions not updating correctly * queued routing cleanup * fixed spotlight mode disabling unnecessarily * added back stoplight stop on ctrl * undid spotlight debugging
274 lines
7.0 KiB
TypeScript
274 lines
7.0 KiB
TypeScript
import { timeoutAt } from '@speckle/shared'
|
|
import { PropertyInfo } from '@speckle/viewer'
|
|
import { until } from '@vueuse/shared'
|
|
import { difference, isString, uniq } from 'lodash-es'
|
|
import { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
|
|
import { isNonNullable } from '~~/lib/common/helpers/utils'
|
|
import {
|
|
useInjectedViewer,
|
|
useInjectedViewerInterfaceState,
|
|
useInjectedViewerState
|
|
} from '~~/lib/viewer/composables/setup'
|
|
|
|
export function useSectionBoxUtilities() {
|
|
const { instance } = useInjectedViewer()
|
|
const {
|
|
sectionBox,
|
|
filters: { selectedObjects }
|
|
} = useInjectedViewerInterfaceState()
|
|
|
|
const isSectionBoxEnabled = computed(() => !!sectionBox.value)
|
|
const toggleSectionBox = () => {
|
|
if (isSectionBoxEnabled.value) {
|
|
sectionBox.value = null
|
|
return
|
|
}
|
|
|
|
const objectIds = selectedObjects.value.map((o) => o.id).filter(isNonNullable)
|
|
const box = instance.getSectionBoxFromObjects(objectIds)
|
|
sectionBox.value = box
|
|
}
|
|
const sectionBoxOn = () => {
|
|
if (!isSectionBoxEnabled.value) {
|
|
toggleSectionBox()
|
|
}
|
|
}
|
|
const sectionBoxOff = () => {
|
|
sectionBox.value = null
|
|
}
|
|
|
|
return {
|
|
isSectionBoxEnabled,
|
|
toggleSectionBox,
|
|
sectionBoxOn,
|
|
sectionBoxOff,
|
|
sectionBox
|
|
}
|
|
}
|
|
|
|
export function useCameraUtilities() {
|
|
const { instance } = useInjectedViewer()
|
|
const {
|
|
filters: { selectedObjects, isolatedObjectIds },
|
|
camera
|
|
} = useInjectedViewerInterfaceState()
|
|
|
|
const zoom = (...args: Parameters<typeof instance.zoom>) => instance.zoom(...args)
|
|
|
|
const setView = (...args: Parameters<typeof instance.setView>) => {
|
|
instance.setView(...args)
|
|
}
|
|
|
|
const truck = (...args: Parameters<typeof instance.cameraHandler.controls.truck>) =>
|
|
instance.cameraHandler.controls.truck(...args)
|
|
|
|
const zoomExtentsOrSelection = () => {
|
|
const ids = selectedObjects.value.map((o) => o.id).filter(isNonNullable)
|
|
|
|
if (ids.length > 0) {
|
|
return instance.zoom(ids)
|
|
}
|
|
|
|
if (isolatedObjectIds.value.length) {
|
|
return instance.zoom(isolatedObjectIds.value)
|
|
}
|
|
|
|
instance.zoom()
|
|
}
|
|
|
|
const toggleProjection = () => {
|
|
camera.isOrthoProjection.value = !camera.isOrthoProjection.value
|
|
}
|
|
|
|
const forceViewToViewerSync = () => {
|
|
setView({
|
|
position: camera.position.value,
|
|
target: camera.target.value
|
|
})
|
|
}
|
|
|
|
return {
|
|
zoomExtentsOrSelection,
|
|
toggleProjection,
|
|
camera,
|
|
truck,
|
|
setView,
|
|
zoom,
|
|
forceViewToViewerSync
|
|
}
|
|
}
|
|
|
|
export function useFilterUtilities() {
|
|
// const { instance } = useInjectedViewer()
|
|
const { filters, explodeFactor } = useInjectedViewerInterfaceState()
|
|
const {
|
|
viewer: {
|
|
metadata: { availableFilters }
|
|
}
|
|
} = useInjectedViewerState()
|
|
|
|
const isolateObjects = (
|
|
objectIds: string[],
|
|
options?: Partial<{
|
|
replace: boolean
|
|
}>
|
|
) => {
|
|
filters.isolatedObjectIds.value = uniq([
|
|
...(options?.replace ? [] : filters.isolatedObjectIds.value),
|
|
...objectIds
|
|
])
|
|
// instance.isolateObjects(objectIds, 'utilities', true)
|
|
}
|
|
|
|
const unIsolateObjects = (objectIds: string[]) => {
|
|
filters.isolatedObjectIds.value = difference(
|
|
filters.isolatedObjectIds.value,
|
|
objectIds
|
|
)
|
|
// instance.unIsolateObjects(objectIds, 'utilities', true)
|
|
}
|
|
|
|
const hideObjects = (
|
|
objectIds: string[],
|
|
options?: Partial<{
|
|
replace: boolean
|
|
}>
|
|
) => {
|
|
filters.hiddenObjectIds.value = uniq([
|
|
...(options?.replace ? [] : filters.hiddenObjectIds.value),
|
|
...objectIds
|
|
])
|
|
// instance.hideObjects(objectIds, 'utilities', true)
|
|
}
|
|
|
|
const showObjects = (objectIds: string[]) => {
|
|
filters.hiddenObjectIds.value = difference(filters.hiddenObjectIds.value, objectIds)
|
|
// instance.showObjects(objectIds, 'utilities', true)
|
|
}
|
|
|
|
/**
|
|
* Sets the current filter property. Does not apply it (instruct viewer to color objects).
|
|
*/
|
|
const setPropertyFilter = (property: PropertyInfo) => {
|
|
filters.propertyFilter.filter.value = property
|
|
}
|
|
|
|
/**
|
|
* Instructs the viewer to apply the current property filter (color objects).
|
|
*/
|
|
const applyPropertyFilter = () => {
|
|
filters.propertyFilter.isApplied.value = true
|
|
}
|
|
|
|
/**
|
|
* Unsets the current property filter.
|
|
*/
|
|
const removePropertyFilter = () => {
|
|
filters.propertyFilter.isApplied.value = false
|
|
filters.propertyFilter.filter.value = null
|
|
}
|
|
|
|
/**
|
|
* Unapplies the current property filter - removes object colouring
|
|
*/
|
|
const unApplyPropertyFilter = () => {
|
|
filters.propertyFilter.isApplied.value = false
|
|
}
|
|
|
|
const resetFilters = () => {
|
|
filters.hiddenObjectIds.value = []
|
|
filters.isolatedObjectIds.value = []
|
|
filters.propertyFilter.filter.value = null
|
|
filters.propertyFilter.isApplied.value = false
|
|
explodeFactor.value = 0
|
|
// filters.selectedObjects.value = []
|
|
}
|
|
|
|
const waitForAvailableFilter = async (
|
|
key: string,
|
|
options?: Partial<{ timeout: number }>
|
|
) => {
|
|
const timeout = options?.timeout || 10000
|
|
|
|
const res = await Promise.race([
|
|
until(availableFilters).toMatch(
|
|
(filters) => !!filters?.find((p) => p.key === key)
|
|
),
|
|
timeoutAt(timeout, 'Waiting for available filter timed out')
|
|
])
|
|
|
|
const filter = res?.find((p) => p.key === key)
|
|
return filter as NonNullable<typeof filter>
|
|
}
|
|
|
|
return {
|
|
isolateObjects,
|
|
unIsolateObjects,
|
|
hideObjects,
|
|
showObjects,
|
|
filters,
|
|
setPropertyFilter,
|
|
applyPropertyFilter,
|
|
removePropertyFilter,
|
|
unApplyPropertyFilter,
|
|
resetFilters,
|
|
waitForAvailableFilter
|
|
}
|
|
}
|
|
|
|
export function useSelectionUtilities() {
|
|
const {
|
|
filters: { selectedObjects }
|
|
} = useInjectedViewerInterfaceState()
|
|
const {
|
|
metadata: { worldTree }
|
|
} = useInjectedViewer()
|
|
|
|
const setSelectionFromObjectIds = (objectIds: string[]) => {
|
|
const res = worldTree.value
|
|
? worldTree.value.findAll((node) => {
|
|
const t = node.model as Record<string, unknown>
|
|
const raw = t.raw as Record<string, unknown>
|
|
const id = raw.id as string
|
|
if (!raw || !id) return false
|
|
if (objectIds.includes(id)) return true
|
|
return false
|
|
})
|
|
: []
|
|
|
|
const objs = res.map(
|
|
(node) => (node.model as Record<string, unknown>).raw as SpeckleObject
|
|
)
|
|
selectedObjects.value = objs
|
|
}
|
|
|
|
const addToSelection = (object: SpeckleObject) => {
|
|
const idx = selectedObjects.value.findIndex((o) => o.id === object.id)
|
|
if (idx !== -1) return
|
|
|
|
selectedObjects.value = [...selectedObjects.value, object]
|
|
}
|
|
|
|
const removeFromSelection = (objectOrId: SpeckleObject | string) => {
|
|
const oid = isString(objectOrId) ? objectOrId : objectOrId.id
|
|
const idx = selectedObjects.value.findIndex((o) => o.id === oid)
|
|
if (idx === -1) return
|
|
|
|
const newObjects = selectedObjects.value.slice()
|
|
newObjects.splice(idx, 1)
|
|
selectedObjects.value = newObjects
|
|
}
|
|
|
|
const clearSelection = () => {
|
|
selectedObjects.value = []
|
|
}
|
|
|
|
return {
|
|
addToSelection,
|
|
removeFromSelection,
|
|
clearSelection,
|
|
setSelectionFromObjectIds,
|
|
objects: selectedObjects
|
|
}
|
|
}
|