feat(filtering): wip colors

This commit is contained in:
Dimitrie Stefanescu
2022-08-06 21:30:46 +03:00
parent 4a29483871
commit 627eb50ec9
6 changed files with 198 additions and 112 deletions
+12 -1
View File
@@ -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`)
})
}
+1 -1
View File
@@ -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"
+122 -19
View File
@@ -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 = {}
+14 -35
View File
@@ -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
}
+8 -1
View File
@@ -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"