feat(filtering): wip colors
This commit is contained in:
@@ -151,8 +151,19 @@ export default class Sandbox {
|
||||
const darkModeToggle = this.tabs.pages[0].addButton({
|
||||
title: '🌞 / 🌛'
|
||||
})
|
||||
const dark = localStorage.getItem('dark') === 'dark'
|
||||
if (dark) {
|
||||
const dark = document
|
||||
.getElementById('renderer')
|
||||
?.classList.toggle('background-dark')
|
||||
}
|
||||
|
||||
darkModeToggle.on('click', () => {
|
||||
document.getElementById('renderer')?.classList.toggle('background-dark')
|
||||
const dark = document
|
||||
.getElementById('renderer')
|
||||
?.classList.toggle('background-dark')
|
||||
|
||||
localStorage.setItem('dark', dark ? `dark` : `light`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"@speckle/objectloader": "workspace:^",
|
||||
"camera-controls": "^1.33.1",
|
||||
"hold-event": "^0.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash-es": "^4.17.21",
|
||||
"rainbowvis.js": "^1.0.1",
|
||||
"three": "^0.140.0",
|
||||
"tree-model": "1.0.7"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Color, Texture } from 'three'
|
||||
import { get } from 'lodash'
|
||||
import { Color, Texture, MathUtils } from 'three'
|
||||
import { TreeNode, WorldTree } from './tree/WorldTree'
|
||||
|
||||
export enum FilterMaterialType {
|
||||
@@ -27,10 +28,12 @@ enum IsolateCommand {
|
||||
}
|
||||
|
||||
export class FilteringManager {
|
||||
private viewer: any
|
||||
private renderer: any
|
||||
|
||||
constructor(renderer: any) {
|
||||
this.renderer = renderer
|
||||
constructor(viewer: any) {
|
||||
this.viewer = viewer
|
||||
this.renderer = viewer.speckleRenderer
|
||||
}
|
||||
|
||||
private setFilters() {
|
||||
@@ -86,14 +89,52 @@ export class FilteringManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides a bunch of objects. The opposite of `showObjects`.
|
||||
* @param objectIds objects to hide.
|
||||
* @param filterKey the "ui scope" this command is coming from.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @param ghost whether to ghost instead of completely hide the objects.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public hideObjects(objectIds: string[], filterKey: string = null, resourceUrl: string = null, ghost = false) {
|
||||
return this.toggleObjectsVisibility(objectIds, VisibilityCommand.HIDE, filterKey, resourceUrl, ghost)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a bunch of objects. The opposite of `hideObjects`.
|
||||
* @param objectIds objects to hide.
|
||||
* @param filterKey the "ui scope" this command is coming from.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public showObjects(objectIds: string[], filterKey: string = null, resourceUrl: string = null) {
|
||||
return this.toggleObjectsVisibility(objectIds, VisibilityCommand.SHOW, filterKey, resourceUrl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides all the descendants of the provided object's id. The opposite of `showTree`.
|
||||
* @param objectId the root object id.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @param ghost whether to ghost instead of completely hide the objects.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public hideTree(objectId: string, resourceUrl: string = null, ghost = false) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.hideObjects(ids, null, resourceUrl, ghost)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows all the descendants of the provided object's id. The opposite of `hideTree`.
|
||||
* @param objectId the root object id.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public showTree(objectId: string, resourceUrl: string = null) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.showObjects(ids, null, resourceUrl)
|
||||
}
|
||||
|
||||
private toggleObjectsVisibility(
|
||||
objectIds: string[],
|
||||
command = VisibilityCommand.HIDE,
|
||||
@@ -162,13 +203,56 @@ export class FilteringManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Isolates a bunch of objects - all other objects in the scene, besides the ones provided, are ghosted or hidden. The opposite of `unIsolateObjects`.
|
||||
* @param objectIds objects to isolate.
|
||||
* @param filterKey the "ui scope" this command is coming from.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @param ghost whether to ghost instead of completely hide the objects.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public isolateObjects(objectIds: string[], filterKey: string = null, resourceUrl: string = null, ghost = true) {
|
||||
return this.toggleObjectsIsolation(objectIds, IsolateCommand.ISOLATE, filterKey, resourceUrl, ghost)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unisolates a bunch of objects - if previously isolated, the provided objects will be either hidden or ghosted. The opposite of `isolateObjects`.
|
||||
* @param objectIds objects to unisolate.
|
||||
* @param filterKey the "ui scope" this command is coming from.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @param ghost whether to ghost instead of completely hide the objects.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public unIsolateObjects(objectIds: string[], filterKey: string = null, resourceUrl: string = null) {
|
||||
return this.toggleObjectsIsolation(objectIds, IsolateCommand.UNISOLATE, filterKey, resourceUrl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Isolates the descendants of the provided object. All other objects in the scene, besides the descendants of the one provided, are ghosted or hidden. The opposite of `unIsolateTree`.
|
||||
* @param objectId the parent object's id.
|
||||
* @param filterKey the "ui scope" this command is coming from.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @param ghost whether to ghost instead of completely hide the objects.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public isolateTree(objectId: string, resourceUrl: string = null, ghost = true) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.isolateObjects(ids, null, resourceUrl, ghost)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unisolates the descendants of the provided object. All other objects in the scene, besides the descendants of the one provided, are ghosted or hidden. The opposite of `isolateTree`.
|
||||
* @param objectId the parent object's id.
|
||||
* @param filterKey the "ui scope" this command is coming from.
|
||||
* @param resourceUrl the resource url to limit searching to.
|
||||
* @param ghost whether to ghost instead of completely hide the objects.
|
||||
* @returns the current applied filter state.
|
||||
*/
|
||||
public unIsolateTree(objectId: string, resourceUrl: string = null) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.unIsolateObjects(ids, null, resourceUrl)
|
||||
}
|
||||
|
||||
private toggleObjectsIsolation(
|
||||
objectIds: string[],
|
||||
command = IsolateCommand.ISOLATE,
|
||||
@@ -210,24 +294,43 @@ export class FilteringManager {
|
||||
return this.setFilters()
|
||||
}
|
||||
|
||||
public showTree(objectId: string, resourceUrl: string = null) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.showObjects(ids, null, resourceUrl)
|
||||
public setColorFilter(property: any, resourceUrl: string = null) {
|
||||
if (property.type === 'numeric') {
|
||||
// do something
|
||||
}
|
||||
if (property.type === 'string') {
|
||||
// do something else
|
||||
console.log(Object.keys(property.uniqueValues))
|
||||
const keys = Object.keys(property.uniqueValues)
|
||||
|
||||
const colors: { value: string; color: any; rvs: any[] }[] = []
|
||||
|
||||
for (const key of keys) {
|
||||
colors.push({
|
||||
color: new Color(MathUtils.randInt(0, 0xffffff)).getHex(),
|
||||
value: key,
|
||||
rvs: []
|
||||
})
|
||||
}
|
||||
|
||||
WorldTree.getInstance().walk((node: TreeNode) => {
|
||||
if (!node.model.atomic) return true
|
||||
|
||||
const propertyValue = get(node.model.raw, property.key, null)
|
||||
if (!propertyValue) return true
|
||||
|
||||
const colorData = colors.find((c) => c.value === propertyValue)
|
||||
if (!colorData) return true
|
||||
|
||||
const rvs = WorldTree.getRenderTree(resourceUrl).getRenderViewsForNode(node)
|
||||
colorData.rvs.push(...rvs)
|
||||
})
|
||||
console.log(colors)
|
||||
}
|
||||
}
|
||||
|
||||
public hideTree(objectId: string, resourceUrl: string = null) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.hideObjects(ids, null, resourceUrl)
|
||||
}
|
||||
|
||||
public isolateTree(objectId: string, resourceUrl: string = null, ghost = true) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.isolateObjects(ids, null, resourceUrl, ghost)
|
||||
}
|
||||
|
||||
public unIsolateTree(objectId: string, resourceUrl: string = null, ghost = true) {
|
||||
const ids = this.getDescendantIds(objectId)
|
||||
return this.unIsolateObjects(ids, null, resourceUrl, ghost)
|
||||
public removeColorFilter() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private lookupCache = {}
|
||||
|
||||
@@ -104,9 +104,11 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
console.warn('Built stuff')
|
||||
})
|
||||
|
||||
this.FilterManager = new FilteringManager(this.speckleRenderer)
|
||||
;(window as any).WorldTree = WorldTree
|
||||
this.FilterManager = new FilteringManager(this)
|
||||
;(window as any).WT = WorldTree
|
||||
;(window as any).FilterManager = this.FilterManager
|
||||
;(window as any).FM = this.FilterManager
|
||||
;(window as any).R = this
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
@@ -261,8 +263,8 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
|
||||
WorldTree.getInstance().walk((node: TreeNode) => {
|
||||
if (!node.model.atomic) return true
|
||||
console.log(node)
|
||||
const obj = flattenObject(node.model.raw)
|
||||
|
||||
for (const prop of Object.keys(obj)) {
|
||||
if (!(prop in propValues)) {
|
||||
propValues[prop] = []
|
||||
@@ -272,7 +274,7 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
return true
|
||||
})
|
||||
|
||||
const propInfo = {}
|
||||
const propInfo = []
|
||||
for (const prop in propValues) {
|
||||
const pinfo = {
|
||||
type: typeof propValues[prop][0],
|
||||
@@ -280,7 +282,8 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
allValues: propValues[prop],
|
||||
uniqueValues: {},
|
||||
minValue: propValues[prop][0],
|
||||
maxValue: propValues[prop][0]
|
||||
maxValue: propValues[prop][0],
|
||||
key: null as string
|
||||
}
|
||||
for (const v of propValues[prop]) {
|
||||
if (v < pinfo.minValue) pinfo.minValue = v
|
||||
@@ -290,9 +293,11 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
}
|
||||
pinfo.uniqueValues[v] += 1
|
||||
}
|
||||
|
||||
propInfo[prop] = pinfo
|
||||
pinfo.key = prop
|
||||
propInfo.push(pinfo)
|
||||
}
|
||||
|
||||
return propInfo
|
||||
}
|
||||
|
||||
public debugGetFilterByNumericPropetyData(propertyName: string): {
|
||||
@@ -398,9 +403,11 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
const rgb = new Color(`hsl(${colorHue}, 50%, 30%)`)
|
||||
return rgb.getHex()
|
||||
}
|
||||
|
||||
const data: {
|
||||
color?: { name: string; color: string; colorIndex: number; nodes: [] }
|
||||
} = {}
|
||||
|
||||
let colorCount = 0
|
||||
/** This is the lazy approach */
|
||||
WorldTree.getInstance().walk((node: TreeNode) => {
|
||||
@@ -453,34 +460,6 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
console.warn(`Filter time: ${performance.now() - start}`)
|
||||
}
|
||||
|
||||
// private isObject(value) {
|
||||
// return !!(value && typeof value === 'object' && !Array.isArray(value))
|
||||
// }
|
||||
|
||||
// private findObjectProperty(object = {}, keyToMatch = '') {
|
||||
// if (this.isObject(object)) {
|
||||
// const entries = Object.entries(object)
|
||||
|
||||
// for (let i = 0; i < entries.length; i += 1) {
|
||||
// const [objectKey, objectValue] = entries[i]
|
||||
|
||||
// if (objectKey === keyToMatch) {
|
||||
// return object[objectKey]
|
||||
// }
|
||||
|
||||
// if (this.isObject(objectValue)) {
|
||||
// const child = this.findObjectProperty(objectValue, keyToMatch)
|
||||
|
||||
// if (child !== null) {
|
||||
// return child
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return null
|
||||
// }
|
||||
|
||||
public dispose() {
|
||||
// TODO: currently it's easier to simply refresh the page :)
|
||||
}
|
||||
|
||||
@@ -41,9 +41,7 @@ export default class Materials {
|
||||
renderMaterial = {
|
||||
id: node.model.raw.renderMaterial.id,
|
||||
color: node.model.raw.renderMaterial.diffuse,
|
||||
opacity: node.model.raw.renderMaterial.opacity
|
||||
? node.model.raw.renderMaterial.opacity
|
||||
: 1,
|
||||
opacity: node.model.raw.renderMaterial.opacity ? node.model.raw.renderMaterial.opacity : 1,
|
||||
vertexColors: node.model.raw.colors && node.model.raw.colors.length > 0
|
||||
}
|
||||
}
|
||||
@@ -222,31 +220,29 @@ export default class Materials {
|
||||
;(<SpeckleLineMaterial>this.lineHiddenMaterial).resolution = new Vector2()
|
||||
this.lineHiddenMaterial.visible = false
|
||||
|
||||
this.materialMap[NodeRenderView.NullRenderMaterialHash] =
|
||||
new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0x7f7f7f,
|
||||
emissive: 0x0,
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
side: DoubleSide // TBD,
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
['USE_RTE']
|
||||
)
|
||||
this.materialMap[NodeRenderView.NullRenderMaterialVertexColorsHash] =
|
||||
new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0x7f7f7f,
|
||||
emissive: 0x0,
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
side: DoubleSide, // TBD
|
||||
vertexColors: true
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
['USE_RTE']
|
||||
)
|
||||
this.materialMap[NodeRenderView.NullRenderMaterialHash] = new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0x7f7f7f,
|
||||
emissive: 0x0,
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
side: DoubleSide // TBD,
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
['USE_RTE']
|
||||
)
|
||||
this.materialMap[NodeRenderView.NullRenderMaterialVertexColorsHash] = new SpeckleStandardMaterial(
|
||||
{
|
||||
color: 0x7f7f7f,
|
||||
emissive: 0x0,
|
||||
roughness: 1,
|
||||
metalness: 0,
|
||||
side: DoubleSide, // TBD
|
||||
vertexColors: true
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
},
|
||||
['USE_RTE']
|
||||
)
|
||||
|
||||
const hash = NodeRenderView.NullDisplayStyleHash // So prettier doesn't fuck up everything
|
||||
this.materialMap[hash] = new SpeckleLineMaterial({
|
||||
@@ -272,22 +268,20 @@ export default class Materials {
|
||||
sizeAttenuation: false
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
this.materialMap[NodeRenderView.NullPointCloudVertexColorsMaterialHash] =
|
||||
new SpecklePointMaterial({
|
||||
color: 0xffffff,
|
||||
vertexColors: true,
|
||||
size: 2,
|
||||
sizeAttenuation: false
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
this.materialMap[NodeRenderView.NullPointCloudMaterialHash] =
|
||||
new SpecklePointMaterial({
|
||||
color: 0xffffff,
|
||||
vertexColors: false,
|
||||
size: 2,
|
||||
sizeAttenuation: false
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
this.materialMap[NodeRenderView.NullPointCloudVertexColorsMaterialHash] = new SpecklePointMaterial({
|
||||
color: 0xffffff,
|
||||
vertexColors: true,
|
||||
size: 2,
|
||||
sizeAttenuation: false
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
this.materialMap[NodeRenderView.NullPointCloudMaterialHash] = new SpecklePointMaterial({
|
||||
color: 0xffffff,
|
||||
vertexColors: false,
|
||||
size: 2,
|
||||
sizeAttenuation: false
|
||||
// clippingPlanes: this.viewer.sectionBox.planes
|
||||
})
|
||||
}
|
||||
|
||||
private makeMeshMaterial(materialData: RenderMaterial): Material {
|
||||
@@ -344,11 +338,7 @@ export default class Materials {
|
||||
return mat
|
||||
}
|
||||
|
||||
public updateMaterialMap(
|
||||
hash: number,
|
||||
material: RenderMaterial | DisplayStyle,
|
||||
type: GeometryType
|
||||
): Material {
|
||||
public updateMaterialMap(hash: number, material: RenderMaterial | DisplayStyle, type: GeometryType): Material {
|
||||
// console.log(this.materialMap)
|
||||
if (this.materialMap[hash]) {
|
||||
// console.warn(`Duplicate material hash found: ${hash}`)
|
||||
@@ -488,10 +478,7 @@ export default class Materials {
|
||||
}
|
||||
}
|
||||
|
||||
public getFilterMaterial(
|
||||
renderView: NodeRenderView,
|
||||
filterMaterial: FilterMaterialType
|
||||
) {
|
||||
public getFilterMaterial(renderView: NodeRenderView, filterMaterial: FilterMaterialType) {
|
||||
switch (filterMaterial) {
|
||||
case FilterMaterialType.SELECT:
|
||||
return this.getHighlightMaterial(renderView)
|
||||
@@ -508,8 +495,7 @@ export default class Materials {
|
||||
|
||||
public getFilterMaterialOptions(filterMaterial: FilterMaterial) {
|
||||
return {
|
||||
rampIndex:
|
||||
filterMaterial.rampIndex !== undefined ? filterMaterial.rampIndex : undefined,
|
||||
rampIndex: filterMaterial.rampIndex !== undefined ? filterMaterial.rampIndex : undefined,
|
||||
rampIndexColor: filterMaterial.rampIndexColor,
|
||||
rampTexture: filterMaterial.rampTexture ? filterMaterial.rampTexture : undefined
|
||||
}
|
||||
|
||||
@@ -5088,7 +5088,7 @@ __metadata:
|
||||
eslint: ^8.11.0
|
||||
eslint-config-prettier: ^8.5.0
|
||||
hold-event: ^0.1.0
|
||||
lodash.debounce: ^4.0.8
|
||||
lodash-es: ^4.17.21
|
||||
prettier: ^2.5.1
|
||||
rainbowvis.js: ^1.0.1
|
||||
regenerator-runtime: ^0.13.7
|
||||
@@ -18163,6 +18163,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash-es@npm:^4.17.21":
|
||||
version: 4.17.21
|
||||
resolution: "lodash-es@npm:4.17.21"
|
||||
checksum: 05cbffad6e2adbb331a4e16fbd826e7faee403a1a04873b82b42c0f22090f280839f85b95393f487c1303c8a3d2a010048bf06151a6cbe03eee4d388fb0a12d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash-webpack-plugin@npm:^0.11.6":
|
||||
version: 0.11.6
|
||||
resolution: "lodash-webpack-plugin@npm:0.11.6"
|
||||
|
||||
Reference in New Issue
Block a user