From 6f78f3597a4271644573084b9cd48349d1897481 Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Mon, 14 Nov 2022 20:30:53 +0000 Subject: [PATCH 1/3] feat(viewer): adds method to set colors by object ids from userspace (re #1201) --- .../src/modules/filtering/FilteringManager.ts | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/packages/viewer/src/modules/filtering/FilteringManager.ts b/packages/viewer/src/modules/filtering/FilteringManager.ts index a0e927b64..a4767dc68 100644 --- a/packages/viewer/src/modules/filtering/FilteringManager.ts +++ b/packages/viewer/src/modules/filtering/FilteringManager.ts @@ -47,6 +47,7 @@ export class FilteringManager { private ColorNumericFilterState = null private SelectionState = new GenericRvState() private HighlightState = new GenericRvState() + private UserspaceColorState = new UserspaceColorState() public constructor(renderer: SpeckleRenderer) { this.WTI = WorldTree.getInstance() @@ -299,6 +300,43 @@ export class FilteringManager { return this.populateGenericState(objectIds, this.HighlightState) } + public setUserObjectColors(groups: [{ objectIds: string[]; color: string }]) { + this.UserspaceColorState = new UserspaceColorState() + for (const group of groups) { + const state = new ColoredGenericRvState() + state.ids = [ + ...new Set([...group.objectIds, ...this.getDescendantIds(group.objectIds)]) + ] + const nodes = [] + + WorldTree.getInstance().walk((node: TreeNode) => { + if (state.ids.indexOf(node.model.raw.id) !== -1) nodes.push(node) + return true + }) + for (let k = 0; k < nodes.length; k++) { + const rvs = WorldTree.getRenderTree().getRenderViewNodesForNode( + nodes[k], + nodes[k] + ) + if (rvs) { + state.rvs.push(...rvs.map((e) => e.model.renderView)) + } + } + + this.UserspaceColorState.states.push(state) + } + const rampTexture = Assets.generateDiscreetRampTexture( + groups.map((g) => new Color(g.color).getHex()) + ) + this.UserspaceColorState.rampTexture = rampTexture + return this.setFilters() + } + + public removeUserObjectColors() { + this.UserspaceColorState = null + return this.setFilters() + } + private populateGenericState(objectIds, state) { let ids = [...objectIds, ...this.getDescendantIds(objectIds)] /** There's a log of duplicate ids coming in from 'getDescendantIds'. We remove them @@ -351,6 +389,7 @@ export class FilteringManager { this.ColorNumericFilterState = null this.SelectionState = new GenericRvState() this.HighlightState = new GenericRvState() + this.UserspaceColorState = null this.StateKey = null return null } @@ -436,6 +475,19 @@ export class FilteringManager { }) } + if (this.UserspaceColorState) { + let m = -1 + for (const state of this.UserspaceColorState.states) { + m++ + this.Renderer.applyFilter(state.rvs, { + filterType: FilterMaterialType.COLORED, + rampIndex: m / this.UserspaceColorState.states.length, + rampIndexColor: state.color, + rampTexture: this.UserspaceColorState.rampTexture + }) + } + } + if (this.HighlightState.rvs.length !== 0) { this.Renderer.applyFilter(this.HighlightState.rvs, { filterType: this.HighlightState.ghost @@ -540,3 +592,19 @@ class GenericRvState { this.ids = [] } } + +class ColoredGenericRvState extends GenericRvState { + public color: Color = null + public reset(): void { + super.reset() + this.color = null + } +} + +class UserspaceColorState { + public states: ColoredGenericRvState[] = [] + public rampTexture: Texture + public reset() { + this.states = [] + } +} From 5b98fdc574ef8eccd65402570e17d0e1cadc0984 Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Tue, 15 Nov 2022 11:30:52 +0000 Subject: [PATCH 2/3] feat(viewer): drives iteration so we only traverse the world tree once, rather than n times --- packages/viewer/src/modules/Viewer.ts | 8 ++ .../src/modules/filtering/FilteringManager.ts | 77 +++++++++++-------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/packages/viewer/src/modules/Viewer.ts b/packages/viewer/src/modules/Viewer.ts index 3fbf27fd3..20ea70a90 100644 --- a/packages/viewer/src/modules/Viewer.ts +++ b/packages/viewer/src/modules/Viewer.ts @@ -269,6 +269,14 @@ export class Viewer extends EventEmitter implements IViewer { }) } + public setUserObjectColors( + groups: [{ objectIds: string[]; color: string }] + ): Promise { + return new Promise((resolve) => { + resolve(this.filteringManager.setUserObjectColors(groups)) + }) + } + public resetFilters(): Promise { return new Promise((resolve) => { resolve(this.filteringManager.reset()) diff --git a/packages/viewer/src/modules/filtering/FilteringManager.ts b/packages/viewer/src/modules/filtering/FilteringManager.ts index a4767dc68..34bb6f418 100644 --- a/packages/viewer/src/modules/filtering/FilteringManager.ts +++ b/packages/viewer/src/modules/filtering/FilteringManager.ts @@ -11,11 +11,11 @@ import { import SpeckleRenderer from '../SpeckleRenderer' export type FilteringState = { - test?: Record selectedObjects?: string[] hiddenObjects?: string[] isolatedObjects?: string[] colorGroups?: Record[] + userColorGroups?: { ids: string[]; color: string }[] activePropFilterKey?: string passMin?: number | null passMax?: number | null @@ -48,6 +48,7 @@ export class FilteringManager { private SelectionState = new GenericRvState() private HighlightState = new GenericRvState() private UserspaceColorState = new UserspaceColorState() + private ColorStringFilterState2: ColorStringFilterState = null public constructor(renderer: SpeckleRenderer) { this.WTI = WorldTree.getInstance() @@ -302,29 +303,34 @@ export class FilteringManager { public setUserObjectColors(groups: [{ objectIds: string[]; color: string }]) { this.UserspaceColorState = new UserspaceColorState() - for (const group of groups) { - const state = new ColoredGenericRvState() - state.ids = [ - ...new Set([...group.objectIds, ...this.getDescendantIds(group.objectIds)]) - ] - const nodes = [] + // Resetting any other filtering color ops as they're not compatible + this.ColorNumericFilterState = null + this.ColorStringFilterState = null - WorldTree.getInstance().walk((node: TreeNode) => { - if (state.ids.indexOf(node.model.raw.id) !== -1) nodes.push(node) - return true - }) - for (let k = 0; k < nodes.length; k++) { - const rvs = WorldTree.getRenderTree().getRenderViewNodesForNode( - nodes[k], - nodes[k] - ) - if (rvs) { - state.rvs.push(...rvs.map((e) => e.model.renderView)) + const localGroups: { + objectIds: string[] + color: string + nodes: TreeNode[] + rvs: NodeRenderView[] + }[] = groups.map((g) => { + return { ...g, nodes: [], rvs: [] } + }) + + WorldTree.getInstance().walk((node: TreeNode) => { + if (!node.model?.raw?.id) return true + for (const group of localGroups) { + if (group.objectIds.includes(node.model.raw.id)) { + group.nodes.push(node) + const rvsNodes = WorldTree.getRenderTree() + .getRenderViewNodesForNode(node, node) + .map((rvNode) => rvNode.model.renderView) + if (rvsNodes) group.rvs.push(...rvsNodes) } } + return true + }) - this.UserspaceColorState.states.push(state) - } + this.UserspaceColorState.groups = localGroups const rampTexture = Assets.generateDiscreetRampTexture( groups.map((g) => new Color(g.color).getHex()) ) @@ -476,15 +482,21 @@ export class FilteringManager { } if (this.UserspaceColorState) { + returnState.userColorGroups = [] let m = -1 - for (const state of this.UserspaceColorState.states) { + for (const group of this.UserspaceColorState.groups) { m++ - this.Renderer.applyFilter(state.rvs, { + this.Renderer.applyFilter(group.rvs, { filterType: FilterMaterialType.COLORED, - rampIndex: m / this.UserspaceColorState.states.length, - rampIndexColor: state.color, + rampIndex: m / this.UserspaceColorState.groups.length, + rampIndexColor: new Color(group.color), rampTexture: this.UserspaceColorState.rampTexture }) + + returnState.userColorGroups.push({ + ids: group.objectIds, + color: group.color //.getHexString() + }) } } @@ -593,18 +605,15 @@ class GenericRvState { } } -class ColoredGenericRvState extends GenericRvState { - public color: Color = null - public reset(): void { - super.reset() - this.color = null - } -} - class UserspaceColorState { - public states: ColoredGenericRvState[] = [] + public groups: { + objectIds: string[] + color: string + nodes: TreeNode[] + rvs: NodeRenderView[] + }[] = [] public rampTexture: Texture public reset() { - this.states = [] + this.groups = [] } } From 93c5810387fcf379b47c63bfd14b85a8338a3a62 Mon Sep 17 00:00:00 2001 From: AlexandruPopovici Date: Tue, 15 Nov 2022 15:07:43 +0200 Subject: [PATCH 3/3] Added the new API member inside IViewer. Fixed a typo inside a comment --- packages/viewer/src/IViewer.ts | 3 +++ packages/viewer/src/modules/filtering/FilteringManager.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/viewer/src/IViewer.ts b/packages/viewer/src/IViewer.ts index 37ee844ec..081679d6e 100644 --- a/packages/viewer/src/IViewer.ts +++ b/packages/viewer/src/IViewer.ts @@ -187,6 +187,9 @@ export interface IViewer { resetHighlight(): Promise setColorFilter(prop: PropertyInfo, ghost?: boolean): Promise + setUserObjectColors( + groups: [{ objectIds: string[]; color: string }] + ): Promise removeColorFilter(): Promise resetFilters(): Promise diff --git a/packages/viewer/src/modules/filtering/FilteringManager.ts b/packages/viewer/src/modules/filtering/FilteringManager.ts index 34bb6f418..c0d310172 100644 --- a/packages/viewer/src/modules/filtering/FilteringManager.ts +++ b/packages/viewer/src/modules/filtering/FilteringManager.ts @@ -345,7 +345,7 @@ export class FilteringManager { private populateGenericState(objectIds, state) { let ids = [...objectIds, ...this.getDescendantIds(objectIds)] - /** There's a log of duplicate ids coming in from 'getDescendantIds'. We remove them + /** There's a lot of duplicate ids coming in from 'getDescendantIds'. We remove them * to avoid the large redundancy they incurr otherwise. */ ids = [...Array.from(new Set(ids.map((value) => value)))]