Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f87ca4db70 | |||
| a70e8385ea | |||
| a910ba1447 | |||
| b7a5692167 |
+2
-2
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<main class="relative">
|
||||
<SpeckleViewer />
|
||||
<ControlPanel />
|
||||
<SelectionPanel />
|
||||
<!-- <ControlPanel />
|
||||
<SelectionPanel /> -->
|
||||
</main>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
</div>
|
||||
<div class="py-3 px-4">
|
||||
<div class="flex gap-x-2">
|
||||
<BaseButton @click="getLevels">Get levels</BaseButton>
|
||||
<BaseButton @click="categorizeLevels">Categorize levels</BaseButton>
|
||||
<BaseButton @click="uncategorizeLevels">Uncategorize levels</BaseButton>
|
||||
<!-- <BaseButton @click="getLevels">Get levels</BaseButton> -->
|
||||
<!-- <BaseButton @click="categorizeLevels">Categorize levels</BaseButton> -->
|
||||
<!-- <BaseButton @click="uncategorizeLevels">Uncategorize levels</BaseButton> -->
|
||||
</div>
|
||||
<div v-if="levels" class="flex flex-col gap-y-2 items-start text-sm mt-4 mb-2">
|
||||
<!-- <div v-if="levels" class="flex flex-col gap-y-2 items-start text-sm mt-4 mb-2">
|
||||
<button
|
||||
v-for="(property, index) in levels?.valueGroups"
|
||||
:key="`level-${index}`"
|
||||
@@ -22,7 +22,7 @@
|
||||
{{ property.value }}
|
||||
</button>
|
||||
<button class="font-medium hover:text-blue-600 mt-2" @click="resetFilters">Show all</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -39,23 +39,20 @@ const { categorize, isolate, animate, resetFilters } = useViewer()
|
||||
|
||||
const levels = ref()
|
||||
|
||||
// Get all the properties with the key 'properties.Instance Parameters.Constraints.Level.value'
|
||||
const getLevels = async () => {
|
||||
if (!properties) return
|
||||
levels.value = properties.find((property: PropertyInfo) => property.key === 'properties.Instance Parameters.Constraints.Level.value')
|
||||
const getLevels = () => {
|
||||
// if (!properties) return
|
||||
// levels.value = properties.find((property: PropertyInfo) => property.key === 'properties.Instance Parameters.Constraints.Level.value')
|
||||
}
|
||||
|
||||
// Categorize the levels and isolate the objects
|
||||
const categorizeLevels = async () => {
|
||||
await getLevels()
|
||||
categorize(levels.value.valueGroups)
|
||||
isolate(levels.value.valueGroups.flatMap((group: StringPropertyInfo['valueGroups']) => group.ids))
|
||||
animate()
|
||||
// getLevels()
|
||||
// categorize(levels.value.valueGroups)
|
||||
// isolate(levels.value.valueGroups.flatMap((group: StringPropertyInfo['valueGroups']) => group.ids))
|
||||
// animate()
|
||||
}
|
||||
|
||||
// Reset filters and reverse the animation
|
||||
const uncategorizeLevels = async () => {
|
||||
resetFilters()
|
||||
animate({ reverse: true })
|
||||
// resetFilters()
|
||||
// animate({ reverse: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<h2 class="text-sm font-medium text-gray-800">Selection info</h2>
|
||||
</div>
|
||||
<div class="py-3 px-4 text-sm flex flex-col gap-y-1">
|
||||
<template v-if="selectionInfo">
|
||||
<!-- <template v-if="selectionInfo">
|
||||
<div>
|
||||
<span class="font-medium">ID:</span> {{ selectionInfo.id }}
|
||||
</div>
|
||||
@@ -18,7 +18,7 @@
|
||||
<span class="font-medium">Level:</span> {{ selectionInfo.level.name }}
|
||||
</div>
|
||||
</template>
|
||||
<p v-else class="text-sm text-gray-500">No selection</p>
|
||||
<p v-else class="text-sm text-gray-500">No selection</p> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,18 +11,16 @@ import useViewer from '@/composables/viewer'
|
||||
const canvas = useTemplateRef('canvas')
|
||||
const { init, addExtensions,loadModelFromUrl } = useViewer()
|
||||
|
||||
// For demo purposes we will load two models
|
||||
// You can replace these with your own as well
|
||||
const MODELS = {
|
||||
ONE: 'https://app.speckle.systems/projects/7744b171ca/models/e32f5e5416',
|
||||
TWO: 'https://app.speckle.systems/projects/7744b171ca/models/7fee46df4b'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!canvas.value) return
|
||||
// if (!canvas.value) return
|
||||
|
||||
await init(canvas.value)
|
||||
addExtensions()
|
||||
await loadModelFromUrl(MODELS.ONE)
|
||||
// await init(canvas.value)
|
||||
// addExtensions()
|
||||
// await loadModelFromUrl(MODELS.ONE)
|
||||
})
|
||||
</script>
|
||||
|
||||
+47
-78
@@ -19,107 +19,76 @@ export let properties: PropertyInfo[] | undefined = undefined
|
||||
const selectionInfo = ref(null)
|
||||
|
||||
export default function useViewer() {
|
||||
/**
|
||||
* Initialize the viewer
|
||||
* @param element - DOM element to initialize the viewer on
|
||||
*/
|
||||
async function init(element: HTMLDivElement) {
|
||||
const params = {
|
||||
...DefaultViewerParams,
|
||||
showStats: false,
|
||||
verbose: true
|
||||
}
|
||||
const init = async (element: HTMLDivElement) => {
|
||||
// const params = {
|
||||
// ...DefaultViewerParams,
|
||||
// showStats: false,
|
||||
// verbose: true
|
||||
// }
|
||||
|
||||
viewer = new Viewer(element, params)
|
||||
await viewer.init()
|
||||
// viewer = new Viewer(element, params)
|
||||
// await viewer.init()
|
||||
|
||||
// Get the object properties after the model has loaded
|
||||
// This will cache them and allow faster access later
|
||||
viewer.on(ViewerEvent.LoadComplete, async() => {
|
||||
properties = await viewer.getObjectProperties()
|
||||
})
|
||||
// viewer.on(ViewerEvent.LoadComplete, async() => {
|
||||
// properties = await viewer.getObjectProperties()
|
||||
// })
|
||||
|
||||
// Handle object clicks in the viewer
|
||||
viewer.on(ViewerEvent.ObjectClicked, (event: SelectionEvent | null) => {
|
||||
// If there are no nodes, the click was not on an object
|
||||
if (event) {
|
||||
const nodes = viewer.getWorldTree().findId(event.hits[0].node.model.id)
|
||||
if (nodes && nodes.length > 0) {
|
||||
selectionInfo.value = nodes[0].model.raw
|
||||
}
|
||||
} else {
|
||||
selectionInfo.value = null
|
||||
}
|
||||
})
|
||||
// viewer.on(ViewerEvent.ObjectClicked, (event: SelectionEvent | null) => {
|
||||
// if (event) {
|
||||
// const nodes = viewer.getWorldTree().findId(event.hits[0].node.model.id)
|
||||
// if (nodes && nodes.length > 0) {
|
||||
// selectionInfo.value = nodes[0].model.raw
|
||||
// }
|
||||
// } else {
|
||||
// selectionInfo.value = null
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
/**
|
||||
* The viewer can be extended with additional functionality by adding extensions
|
||||
* You can use extensions provided by the viewer, or create your own
|
||||
*/
|
||||
function addExtensions() {
|
||||
if (!viewer) return
|
||||
viewer.createExtension(CameraController)
|
||||
viewer.createExtension(SelectionExtension)
|
||||
viewer.createExtension(FilteringExtension)
|
||||
viewer.createExtension(Catalogue)
|
||||
const addExtensions = () => {
|
||||
// if (!viewer) return
|
||||
// viewer.createExtension(CameraController)
|
||||
// viewer.createExtension(SelectionExtension)
|
||||
// viewer.createExtension(FilteringExtension)
|
||||
// viewer.createExtension(Catalogue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a model from a Speckle URL
|
||||
* @param url - The URL of the Speckle model
|
||||
* @param authToken - This is required if the model is private
|
||||
*/
|
||||
const loadModelFromUrl = async (url: string, authToken?: string) => {
|
||||
const urls = await UrlHelper.getResourceUrls(url, authToken)
|
||||
urls.forEach(async (url) => {
|
||||
if (!viewer) return
|
||||
const loader = new SpeckleLoader(viewer.getWorldTree(), url, '')
|
||||
await viewer.loadObject(loader, true)
|
||||
})
|
||||
// const urls = await UrlHelper.getResourceUrls(url, authToken)
|
||||
// urls.forEach(async (url) => {
|
||||
// if (!viewer) return
|
||||
// const loader = new SpeckleLoader(viewer.getWorldTree(), url, '')
|
||||
// await viewer.loadObject(loader, true)
|
||||
// })
|
||||
}
|
||||
|
||||
/**
|
||||
* Isolate objects in the viewer
|
||||
* @param ids - List of IDs to isolate
|
||||
*/
|
||||
const isolate = async (ids: Array<string>) => {
|
||||
if (!viewer) return
|
||||
const filter = viewer.getExtension(FilteringExtension)
|
||||
filter.resetFilters()
|
||||
filter.isolateObjects(ids)
|
||||
// if (!viewer) return
|
||||
// const filter = viewer.getExtension(FilteringExtension)
|
||||
// filter.resetFilters()
|
||||
// filter.isolateObjects(ids)
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize a certain property in the viewer
|
||||
* @param input - ids to categorize
|
||||
* @param options - options for the catalogue
|
||||
*/
|
||||
const categorize = async (
|
||||
input: Array<{ ids: Array<string>; value: string }>,
|
||||
options?: CatalogueOptions
|
||||
) => {
|
||||
if (!viewer) return
|
||||
const catalogue = viewer.getExtension(Catalogue)
|
||||
catalogue.categorize(input, options)
|
||||
// if (!viewer) return
|
||||
// const catalogue = viewer.getExtension(Catalogue)
|
||||
// catalogue.categorize(input, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the catalogue extension to animate the objects
|
||||
* @param options - option to reverse the animation
|
||||
*/
|
||||
const animate = async (options?:{ reverse?: boolean }) => {
|
||||
const { reverse } = options || {}
|
||||
if (!viewer) return
|
||||
const catalogue = viewer.getExtension(Catalogue)
|
||||
catalogue.animate(reverse)
|
||||
// const { reverse } = options || {}
|
||||
// if (!viewer) return
|
||||
// const catalogue = viewer.getExtension(Catalogue)
|
||||
// catalogue.animate(reverse)
|
||||
}
|
||||
|
||||
// Reset the filtering extension
|
||||
const resetFilters = async () => {
|
||||
if (!viewer) return
|
||||
const filter = viewer.getExtension(FilteringExtension)
|
||||
filter.resetFilters()
|
||||
// if (!viewer) return
|
||||
// const filter = viewer.getExtension(FilteringExtension)
|
||||
// filter.resetFilters()
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user