0ef0a13979
* feat(viewer-lib): WIP on area measurement * feat(viewer-lib): WIP on area measurement. Basics are working. Needs more bling * feat(viewer-lib): Implemented polygon triangulation for measured area * chore(shared): Updated MeasurementType * Add Area control to measure panel * feat(viewer-lib): Real time updates of the fill polygon and area value * feat(viewer-lib): Added pole of innacessibility as area label anchor using the polylabel libraryh * feat(viewer-lib): Fixed fill polygon material * feat(viewer-lib): Changed the surface normal indicator from the faulty disc to a outlined rectangle. Looks and works much better now * fix(viewer-lib): Measurements get clipped by sections planes * faet(viewer-lib): Measurements can now optionally define their own snapping method. Area measurement snaps to first point in screen space. Generic vertex snap still applies if enabled * fix(viewer-lib): Forgot to project the measured point * feat(viewer-lib): Double click auto-finishes the area measurmenet by instantly joining with the first point. Right clikc removes current area measurement point so you can 'undo' * fix(viewer-lib: Fixed a stupid bug relatedto text because somebody thought that making it 'async' would be sooooo cool... * fix(frontend): Prevent zoom on double click when using area measurement * chore(viewer-lib): Refined and fomralized a bit now that the general idea of a measurement has got more complex * chore(viewer-lib): Moved state switching a bit * chore(viewer-lib): Replaced the old disc normal indicator with the new one and made it standard. Added an option to the gizmo's style that determines the pixels size of the normal indicator * chore(viewer-lib): Documented the area measurement tool * chore(viewer-lib): Some updates: - Implemented proper bounds getter for area measurement - Got rid of the static vector buffers in Measurement and replaced them with consts where needed - Reduced the min click timing from 250ms to 150ms - Other small adjustments * feat(viewer-lib): Added the option to chain measurements * chore(frontend-2): Added toggle for measurement chaining * chore(viewer-lib): Perpendicular measurement chaining now align on the same line as requested * feat(viewer-lib): Implemented point (coordinate) measurement: - Added support for billboard offseting in NDC in the shader via vec2 offset uniform. Not a dream come true, but required mostly because of how troika works - SpeckleText background now follows text anchoring - Implemented new POINT measurement type * chore(viewer-lib): Separated label position calculation. We now update only the label transform each frame, instead of updating the entire label redunantly * chore(viewer-lib): Offsets are now constants. Removed redundant vector and matrix creation * chore(frontend-2): Placeholder radio button for point measurement type. Fixed compile errors * fix(viewer-lib): Fixes WEB-3105. Export all measurement types * updated icons * Update description --------- Co-authored-by: andrewwallacespeckle <andrew@speckle.systems> Co-authored-by: Mike Tasset <mike.tasset@gmail.com>
126 lines
4.4 KiB
TypeScript
126 lines
4.4 KiB
TypeScript
import { MeasurementType } from '@speckle/viewer'
|
|
import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
|
|
import { useMixpanel } from '~~/lib/core/composables/mp'
|
|
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
|
|
import { useCameraUtilities, useSelectionUtilities } from '~~/lib/viewer/composables/ui'
|
|
import { useSelectionEvents } from '~~/lib/viewer/composables/viewer'
|
|
|
|
function useCollectSelection() {
|
|
const {
|
|
ui: { selection }
|
|
} = useInjectedViewerState()
|
|
|
|
const selectionCallback: Parameters<
|
|
typeof useSelectionEvents
|
|
>[0]['singleClickCallback'] = (_event, { firstVisibleSelectionHit }) => {
|
|
if (!firstVisibleSelectionHit) return (selection.value = null) // reset selection location
|
|
selection.value = firstVisibleSelectionHit.point
|
|
}
|
|
useSelectionEvents({
|
|
singleClickCallback: selectionCallback,
|
|
doubleClickCallback: selectionCallback
|
|
})
|
|
}
|
|
|
|
function useSelectOrZoomOnSelection() {
|
|
const state = useInjectedViewerState()
|
|
const { clearSelection, addToSelection } = useSelectionUtilities()
|
|
const { zoom } = useCameraUtilities()
|
|
const mp = useMixpanel()
|
|
const logger = useLogger()
|
|
|
|
const trackAndClearSelection = () => {
|
|
clearSelection()
|
|
mp.track('Viewer Action', {
|
|
type: 'action',
|
|
name: 'selection',
|
|
action: 'clear',
|
|
source: 'viewer'
|
|
})
|
|
}
|
|
useSelectionEvents(
|
|
{
|
|
singleClickCallback: (args, { firstVisibleSelectionHit }) => {
|
|
if (!args) return trackAndClearSelection()
|
|
if (args.hits.length === 0) return trackAndClearSelection()
|
|
if (!args.multiple) clearSelection() // note we're not tracking selectino clearing here
|
|
|
|
if (!firstVisibleSelectionHit) return clearSelection()
|
|
addToSelection(firstVisibleSelectionHit.node.model.raw as SpeckleObject)
|
|
// Expands default viewer selection behaviour with a special case in diff mode.
|
|
// In diff mode, if we select via a mouse click an object, and that object is
|
|
// "modified", we want to select its pair as well.
|
|
if (
|
|
state.ui.diff.enabled.value &&
|
|
state.ui.diff.result.value &&
|
|
firstVisibleSelectionHit.node.model.raw.applicationId
|
|
) {
|
|
const modifiedObjectPairs = state.ui.diff.result.value.modified
|
|
const obj = firstVisibleSelectionHit.node.model.raw as SpeckleObject
|
|
const pairedItems = modifiedObjectPairs.find((item) => {
|
|
if (
|
|
(item[0].model.raw as SpeckleObject).id === obj.id ||
|
|
(item[1].model.raw as SpeckleObject).id === obj.id
|
|
) {
|
|
return true
|
|
}
|
|
})
|
|
if (!pairedItems) return
|
|
|
|
const pair =
|
|
(pairedItems[0].model.raw as SpeckleObject).id === obj.id
|
|
? (pairedItems[1].model.raw as SpeckleObject)
|
|
: (pairedItems[0].model.raw as SpeckleObject)
|
|
if (!pair) return
|
|
addToSelection(pair)
|
|
}
|
|
mp.track('Viewer Action', {
|
|
type: 'action',
|
|
name: 'selection',
|
|
action: 'select',
|
|
multiple: args.multiple
|
|
})
|
|
},
|
|
doubleClickCallback: (args, { firstVisibleSelectionHit }) => {
|
|
const isMeasureMode = state.ui.measurement.enabled.value
|
|
const measurementType = state.ui.measurement.options.value.type
|
|
|
|
if (
|
|
isMeasureMode &&
|
|
(measurementType === MeasurementType.PERPENDICULAR ||
|
|
measurementType === MeasurementType.AREA)
|
|
) {
|
|
return
|
|
}
|
|
if (!args) return zoom()
|
|
if (!args.hits) return zoom()
|
|
if (args.hits.length === 0) return zoom()
|
|
|
|
const firstVisHit = firstVisibleSelectionHit
|
|
if (!firstVisHit) return clearSelection()
|
|
|
|
if (state.ui.filters.selectedObjects.value.length !== 0) {
|
|
const ids = state.ui.filters.selectedObjects.value.map((o) => o.id as string)
|
|
zoom(ids)
|
|
} // else somethingn is weird.
|
|
else {
|
|
logger.warn(
|
|
"Got a double click event but there's no selected object in the state - this should be impossible :)"
|
|
)
|
|
}
|
|
mp.track('Viewer Action', {
|
|
type: 'action',
|
|
name: 'zoom',
|
|
source: 'object-double-click'
|
|
})
|
|
}
|
|
},
|
|
{ state }
|
|
)
|
|
}
|
|
|
|
export function useViewerSelectionEventHandler() {
|
|
useCollectSelection()
|
|
useSelectOrZoomOnSelection()
|
|
}
|