#827. Generalizes all gradient based filtering. We can now filter by any property which is numerical from the sandbox. Implemented ramp texture generation. We will need this to generate the ramp texture for discreet colored filters like filterByType, filterByFamily ,etc
This commit is contained in:
@@ -3,6 +3,7 @@ import { GeometryConverter } from '@speckle/viewer'
|
||||
import { Viewer, IViewer, WorldTree } from '@speckle/viewer'
|
||||
import SpeckleLineMaterial from '@speckle/viewer/dist/modules/materials/SpeckleLineMaterial'
|
||||
import { Object3D } from '@speckle/viewer/node_modules/@types/three'
|
||||
import { Pane } from 'tweakpane'
|
||||
import UrlHelper from './UrlHelper'
|
||||
export default class Sandbox {
|
||||
private viewer: IViewer
|
||||
@@ -29,9 +30,10 @@ export default class Sandbox {
|
||||
}
|
||||
|
||||
public static filterParams = {
|
||||
volumeData: {},
|
||||
minVolume: 0,
|
||||
maxVolume: 10000
|
||||
filterBy: 'Volume',
|
||||
data: {},
|
||||
minValue: 0,
|
||||
maxValue: 10000
|
||||
}
|
||||
|
||||
public constructor(viewer: Viewer) {
|
||||
@@ -233,68 +235,60 @@ export default class Sandbox {
|
||||
title: 'Filtering',
|
||||
expanded: true
|
||||
})
|
||||
// filteringFolder
|
||||
// .addButton({
|
||||
// title: 'Select Random'
|
||||
// })
|
||||
// .on('click', () => {
|
||||
// this.viewer.speckleRenderer.clearFilter()
|
||||
// this.viewer.speckleRenderer.beginFilter()
|
||||
// this.viewer.speckleRenderer.applyFilter(
|
||||
// this.getRandomNodeIds(0.25),
|
||||
// FilterMaterial.GRADIENT
|
||||
// )
|
||||
// this.viewer.speckleRenderer.endFilter()
|
||||
// })
|
||||
filteringFolder.addInput(Sandbox.filterParams, 'filterBy', {
|
||||
options: {
|
||||
Volume: 'Volume',
|
||||
Area: 'Area',
|
||||
SpeckleType: 'speckle_type'
|
||||
}
|
||||
})
|
||||
|
||||
filteringFolder
|
||||
.addButton({
|
||||
title: 'Filter By Volume'
|
||||
title: 'Apply Filter'
|
||||
})
|
||||
.on('click', () => {
|
||||
Sandbox.filterParams.volumeData = this.viewer.debugGetVolumeNodes()
|
||||
Sandbox.filterParams.minVolume = Sandbox.filterParams.volumeData.min
|
||||
Sandbox.filterParams.maxVolume = Sandbox.filterParams.volumeData.max
|
||||
Sandbox.filterParams.data = this.viewer.debugGetFilterByPropetyNodes(
|
||||
Sandbox.filterParams.filterBy
|
||||
)
|
||||
Sandbox.filterParams.minValue = Sandbox.filterParams.data.min
|
||||
Sandbox.filterParams.maxValue = Sandbox.filterParams.data.max
|
||||
|
||||
this.viewer.debugApplyVolumeFilter(Sandbox.filterParams.volumeData)
|
||||
this.minVolumeControl.controller_.valueController.value.constraint_.constraints[0].minValue =
|
||||
Sandbox.filterParams.minVolume
|
||||
this.minVolumeControl.controller_.valueController.value.constraint_.constraints[0].maxValue =
|
||||
Sandbox.filterParams.maxVolume
|
||||
this.maxVolumeControl.controller_.valueController.value.constraint_.constraints[0].minValue =
|
||||
Sandbox.filterParams.minVolume
|
||||
this.maxVolumeControl.controller_.valueController.value.constraint_.constraints[0].maxValue =
|
||||
Sandbox.filterParams.maxVolume
|
||||
this.maxVolumeControl.disabled = false
|
||||
this.minVolumeControl.disabled = false
|
||||
this.viewer.debugApplyByPropetyFilter(
|
||||
Sandbox.filterParams.data,
|
||||
Sandbox.filterParams.filterBy
|
||||
)
|
||||
if (this.maxVolumeControl) this.maxVolumeControl.dispose()
|
||||
if (this.minVolumeControl) this.minVolumeControl.dispose()
|
||||
|
||||
this.minVolumeControl = filteringFolder
|
||||
.addInput(Sandbox.filterParams, 'minValue', {
|
||||
min: Sandbox.filterParams.minValue,
|
||||
max: Sandbox.filterParams.maxValue
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.debugApplyByPropetyFilter(
|
||||
Sandbox.filterParams.data,
|
||||
Sandbox.filterParams.filterBy,
|
||||
Sandbox.filterParams.minValue,
|
||||
Sandbox.filterParams.maxValue
|
||||
)
|
||||
})
|
||||
this.maxVolumeControl = filteringFolder
|
||||
.addInput(Sandbox.filterParams, 'maxValue', {
|
||||
min: Sandbox.filterParams.minValue,
|
||||
max: Sandbox.filterParams.maxValue
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.debugApplyByPropetyFilter(
|
||||
Sandbox.filterParams.data,
|
||||
Sandbox.filterParams.filterBy,
|
||||
Sandbox.filterParams.minValue,
|
||||
Sandbox.filterParams.maxValue
|
||||
)
|
||||
})
|
||||
this.pane.refresh()
|
||||
})
|
||||
this.minVolumeControl = filteringFolder
|
||||
.addInput(Sandbox.filterParams, 'minVolume', {
|
||||
min: 0,
|
||||
max: 100,
|
||||
disabled: true
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.debugApplyVolumeFilter(
|
||||
Sandbox.filterParams.volumeData,
|
||||
Sandbox.filterParams.minVolume,
|
||||
Sandbox.filterParams.maxVolume
|
||||
)
|
||||
})
|
||||
this.maxVolumeControl = filteringFolder
|
||||
.addInput(Sandbox.filterParams, 'maxVolume', {
|
||||
min: 0,
|
||||
max: 100,
|
||||
disabled: true
|
||||
})
|
||||
.on('change', () => {
|
||||
this.viewer.debugApplyVolumeFilter(
|
||||
Sandbox.filterParams.volumeData,
|
||||
Sandbox.filterParams.minVolume,
|
||||
Sandbox.filterParams.maxVolume
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
public async loadUrl(url: string) {
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Texture, PMREMGenerator, WebGLRenderer, TextureLoader } from 'three'
|
||||
import {
|
||||
Texture,
|
||||
PMREMGenerator,
|
||||
WebGLRenderer,
|
||||
TextureLoader,
|
||||
Color,
|
||||
DataTexture
|
||||
} from 'three'
|
||||
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js'
|
||||
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
|
||||
import { Asset, AssetType } from '../IViewer'
|
||||
@@ -96,4 +103,37 @@ export class Assets {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public static generateGradientRampTexture(
|
||||
fromColor: string,
|
||||
toColor: string,
|
||||
steps: number
|
||||
) {
|
||||
fromColor
|
||||
toColor
|
||||
steps
|
||||
// NOT NECESSARY AT THE MOMENT. USING STATIC GRADIENT RAMP
|
||||
}
|
||||
|
||||
public static generateDiscreetRampTexture(hexColors: string[]): Texture {
|
||||
const width = hexColors.length
|
||||
const height = 1
|
||||
|
||||
const size = width * height
|
||||
const data = new Uint8Array(4 * size)
|
||||
|
||||
for (let k = 0; k < hexColors.length; k++) {
|
||||
const stride = k * 4
|
||||
const color = new Color(hexColors[k])
|
||||
color.convertSRGBToLinear()
|
||||
data[stride] = Math.floor(color.r * 255)
|
||||
data[stride + 1] = Math.floor(color.g * 255)
|
||||
data[stride + 2] = Math.floor(color.b * 255)
|
||||
data[stride + 3] = 255
|
||||
}
|
||||
|
||||
const texture = new DataTexture(data, width, height)
|
||||
texture.needsUpdate = true
|
||||
return texture
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { Texture } from 'three'
|
||||
|
||||
export enum FilterMaterialType {
|
||||
SELECT,
|
||||
GHOST,
|
||||
GRADIENT
|
||||
GRADIENT,
|
||||
COLORED
|
||||
}
|
||||
|
||||
export interface FilterMaterial {
|
||||
filterType: FilterMaterialType
|
||||
gradientIndex?: number
|
||||
rampIndex?: number
|
||||
rampTexture?: Texture
|
||||
}
|
||||
|
||||
export class FilteringManager {
|
||||
|
||||
@@ -322,7 +322,11 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
}
|
||||
}
|
||||
|
||||
public debugGetVolumeNodes(): { min: number; max: number; nodes: TreeNode[] } {
|
||||
public debugGetFilterByPropetyNodes(propertyName: string): {
|
||||
min: number
|
||||
max: number
|
||||
nodes: TreeNode[]
|
||||
} {
|
||||
const volumeNodes = []
|
||||
let minVolume = Infinity
|
||||
let maxVolume = 0
|
||||
@@ -331,7 +335,7 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
if (params) {
|
||||
for (const k in params) {
|
||||
if (!(params[k] instanceof Object)) continue
|
||||
if (params[k].name === 'Volume') {
|
||||
if (params[k].name === propertyName) {
|
||||
minVolume = Math.min(minVolume, params[k].value)
|
||||
maxVolume = Math.max(maxVolume, params[k].value)
|
||||
volumeNodes.push(node)
|
||||
@@ -348,21 +352,23 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
}
|
||||
}
|
||||
|
||||
public debugApplyVolumeFilter(
|
||||
public debugApplyByPropetyFilter(
|
||||
data: { min: number; max: number; nodes: TreeNode[] },
|
||||
propertyName: string,
|
||||
min?: number,
|
||||
max?: number
|
||||
) {
|
||||
const nodesGradient = []
|
||||
const nodesGhost = []
|
||||
const volumeValues = []
|
||||
const values = []
|
||||
|
||||
/** This is the lazy approach */
|
||||
WorldTree.getInstance().walk((node: TreeNode) => {
|
||||
const params = node.model.raw.parameters
|
||||
if (params) {
|
||||
for (const k in params) {
|
||||
if (!(params[k] instanceof Object)) continue
|
||||
if (params[k].name === 'Volume') {
|
||||
if (params[k].name === propertyName) {
|
||||
const volumeValue = params[k].value
|
||||
const pasMin = min !== undefined ? volumeValue >= min : true
|
||||
const pasMax = max !== undefined ? volumeValue <= max : true
|
||||
@@ -373,7 +379,7 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
!nodesGradient.includes(node)
|
||||
) {
|
||||
nodesGradient.push(node)
|
||||
volumeValues.push(volumeValue)
|
||||
values.push(volumeValue)
|
||||
}
|
||||
} else {
|
||||
if (!nodesGhost.includes(node)) nodesGhost.push(node)
|
||||
@@ -397,10 +403,10 @@ export class Viewer extends EventEmitter implements IViewer {
|
||||
const ids = WorldTree.getRenderTree()
|
||||
.getRenderViewsForNode(nodesGradient[k], nodesGradient[k])
|
||||
.map((value) => value.renderData.id)
|
||||
const t = (volumeValues[k] - data.min) / (data.max - data.min)
|
||||
const t = (values[k] - data.min) / (data.max - data.min)
|
||||
this.speckleRenderer.applyFilter(ids, {
|
||||
filterType: FilterMaterialType.GRADIENT,
|
||||
gradientIndex: t
|
||||
rampIndex: t
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -385,9 +385,9 @@ export default class Materials {
|
||||
}
|
||||
|
||||
public getFilterMaterialOptions(filterMaterial: FilterMaterial) {
|
||||
return filterMaterial.gradientIndex
|
||||
return filterMaterial.rampIndex
|
||||
? {
|
||||
gradientIndex: filterMaterial.gradientIndex
|
||||
gradientIndex: filterMaterial.rampIndex
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user