wip viewer
This commit is contained in:
Generated
+5
@@ -7658,6 +7658,11 @@
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"dev": true
|
||||
},
|
||||
"rainbowvis.js": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rainbowvis.js/-/rainbowvis.js-1.0.1.tgz",
|
||||
"integrity": "sha1-6JmPuXFhsCVXdIx9oJeWKw1uPgA="
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
"dependencies": {
|
||||
"camera-controls": "^1.33.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"rainbowvis.js": "^1.0.1",
|
||||
"three": "^0.133.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,144 @@
|
||||
import * as THREE from 'three'
|
||||
import Rainbow from 'rainbowvis.js'
|
||||
import { cloneUniforms } from 'three'
|
||||
|
||||
const WireframeMaterial = new THREE.MeshStandardMaterial( {
|
||||
color: 0x7080A0,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.04,
|
||||
wireframe: true
|
||||
} )
|
||||
|
||||
const ColoredMaterial = new THREE.MeshStandardMaterial( {
|
||||
color: 0x7080A0,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: false
|
||||
} )
|
||||
|
||||
|
||||
export function filterAndColorObject( obj, filter ) {
|
||||
if ( !filter )
|
||||
return obj.clone()
|
||||
|
||||
if ( filter.speckle_type && obj.userData?.speckle_type !== filter.speckle_type )
|
||||
if ( !passesAndFilter( obj.userData, filter.and ) )
|
||||
{
|
||||
if ( filter.wireframeFilter && obj.type === 'Mesh' ) {
|
||||
let clone = obj.clone()
|
||||
// clone.material = WireframeMaterial
|
||||
clone.material = obj.material.clone()
|
||||
clone.material.transparent = true
|
||||
clone.material.opacity = 0.05
|
||||
clone.userData = null
|
||||
return clone
|
||||
}
|
||||
return null
|
||||
|
||||
return obj.clone()
|
||||
}
|
||||
|
||||
let clone = obj.clone()
|
||||
if ( filter.colors ) {
|
||||
if ( filter.colors.type === 'category' ) {
|
||||
clone.material = colorWithCategory( obj.userData, filter.colors )
|
||||
} else if ( filter.colors.type === 'gradient' ) {
|
||||
clone.material = colorWithGradient( obj.userData, filter.colors )
|
||||
}
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
function getObjectProperty( obj, property ) {
|
||||
if ( !property ) return
|
||||
let keyParts = property.split( '.' )
|
||||
let crtObj = obj
|
||||
for ( let i = 0; i < keyParts.length - 1; i++ ) {
|
||||
if ( !( keyParts[i] in crtObj ) ) return
|
||||
crtObj = crtObj[ keyParts[i] ]
|
||||
if ( crtObj.constructor !== Object ) return
|
||||
}
|
||||
let attributeName = keyParts[ keyParts.length - 1 ]
|
||||
return crtObj[ attributeName ]
|
||||
}
|
||||
|
||||
function colorWithCategory( obj, colors ) {
|
||||
let defaultValue = colors.default
|
||||
let color = defaultValue
|
||||
let objValue = getObjectProperty( obj, colors.property )
|
||||
let customPallete = colors.values || {}
|
||||
if ( objValue in customPallete ) {
|
||||
color = customPallete[ objValue ]
|
||||
}
|
||||
|
||||
if ( !color ) {
|
||||
// compute value hash
|
||||
let objValueAsString = '' + objValue
|
||||
let hash = 0
|
||||
for( let i = 0; i < objValueAsString.length; i++ ) {
|
||||
let chr = objValueAsString.charCodeAt( i )
|
||||
hash = ( ( hash << 5 ) - hash ) + chr
|
||||
hash |= 0 // Convert to 32bit integer
|
||||
}
|
||||
hash = Math.abs( hash )
|
||||
let colorHue = hash % 360
|
||||
color = `hsl(${colorHue}, 50%, 30%)`
|
||||
}
|
||||
|
||||
let material = ColoredMaterial.clone()
|
||||
material.color = new THREE.Color( color )
|
||||
return material
|
||||
}
|
||||
|
||||
function colorWithGradient( obj, colors ) {
|
||||
let rainbow = new Rainbow( )
|
||||
if ( 'minValue' in colors && 'maxValue' in colors )
|
||||
rainbow.setNumberRange( colors.minValue, colors.maxValue )
|
||||
if ( 'gradientColors' in colors )
|
||||
rainbow.setSpectrum( ...colors.gradientColors )
|
||||
|
||||
let objValue = getObjectProperty( obj, colors.property )
|
||||
objValue = Number( objValue )
|
||||
if ( Number.isNaN( objValue ) ) {
|
||||
// TODO: have different behaviour for missing values?
|
||||
objValue = 0
|
||||
}
|
||||
|
||||
let material = ColoredMaterial.clone()
|
||||
material.color = new THREE.Color( `#${rainbow.colourAt( objValue )}` )
|
||||
return material
|
||||
}
|
||||
|
||||
function passesAndFilter( obj, andFilter ) {
|
||||
if ( !andFilter ) return true
|
||||
for ( let filterKey in andFilter ) {
|
||||
let objValue = getObjectProperty( obj, filterKey )
|
||||
|
||||
// let keyParts = filterKey.split( '.' )
|
||||
// let crtObj = obj
|
||||
// for ( let i = 0; i < keyParts.length - 1; i++ ) {
|
||||
// if ( !( keyParts[i] in crtObj ) ) return false
|
||||
// crtObj = crtObj[ keyParts[i] ]
|
||||
// if ( crtObj.constructor !== Object ) return false
|
||||
// }
|
||||
// let attributeName = keyParts[ keyParts.length - 1 ]
|
||||
let passesFilter = filterValue( objValue, andFilter[ filterKey ] )
|
||||
if ( !passesFilter ) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function filterValue( objValue, valueFilter ) {
|
||||
// Array value filter means it can be any value from the array
|
||||
if ( Array.isArray( valueFilter ) )
|
||||
return valueFilter.includes( objValue )
|
||||
|
||||
// Dictionary value filter can specify ranges with `lte` and `gte` fields (LowerThanOrEqual, GreaterThanOrEqual)
|
||||
if ( valueFilter.constructor === Object ) {
|
||||
if ( 'lte' in valueFilter && objValue > valueFilter.lte )
|
||||
return false
|
||||
if ( 'gte' in valueFilter && objValue < valueFilter.gte )
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
// Can also filter by specific value
|
||||
return objValue === valueFilter
|
||||
}
|
||||
|
||||
@@ -39,22 +39,6 @@ export default class SceneObjects {
|
||||
|
||||
this.filteredObjects = null
|
||||
|
||||
// this.filteredObjects = new THREE.Group()
|
||||
// this.filteredObjects.name = 'filteredObjects'
|
||||
// this.filteredSolidObjects = new THREE.Group()
|
||||
// this.filteredSolidObjects.name = 'filteredSolidObjects'
|
||||
// this.filteredSolidObjects.visible = false // these are grouped later, we never want to display them individually
|
||||
// this.filteredTransparentObjects = new THREE.Group()
|
||||
// this.filteredTransparentObjects.name = 'filteredTransparentObjects'
|
||||
// this.filteredLineObjects = new THREE.Group()
|
||||
// this.filteredLineObjects.name = 'filteredLineObjects'
|
||||
// this.filteredPointObjects = new THREE.Group()
|
||||
// this.filteredPointObjects.name = 'filteredPointObjects'
|
||||
// this.filteredObjects.add( this.filteredSolidObjects )
|
||||
// this.filteredObjects.add( this.filteredTransparentObjects )
|
||||
// this.filteredObjects.add( this.filteredLineObjects )
|
||||
// this.filteredObjects.add( this.filteredPointObjects )
|
||||
|
||||
this.appliedFilter = null
|
||||
|
||||
// When the `appliedFilter` is null, scene will contain `allObjects`. Otherwise, `filteredObjects`
|
||||
@@ -75,6 +59,65 @@ export default class SceneObjects {
|
||||
}
|
||||
}
|
||||
|
||||
getObjectsProperties() {
|
||||
let flattenObject = function( obj ) {
|
||||
let flatten = {}
|
||||
for ( let k in obj ) {
|
||||
if ( [ 'id', '__closure', 'bbox', 'totalChildrenCount' ].includes( k ) )
|
||||
continue
|
||||
let v = obj[ k ]
|
||||
if ( Array.isArray( v ) )
|
||||
continue
|
||||
if ( v.constructor === Object ) {
|
||||
let flattenProp = flattenObject( v )
|
||||
for ( let pk in flattenProp ) {
|
||||
flatten[ `${k}.${pk}` ] = flattenProp[ pk ]
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ( [ 'string', 'number', 'boolean' ].includes( typeof v ) )
|
||||
flatten[ k ] = v
|
||||
}
|
||||
return flatten
|
||||
}
|
||||
|
||||
let propValues = {}
|
||||
for ( let objGroup of this.objectsInScene.children ) {
|
||||
for ( let threeObj of objGroup.children ) {
|
||||
let obj = flattenObject( threeObj.userData )
|
||||
for ( let prop of Object.keys( obj ) ) {
|
||||
if ( !( prop in propValues ) ) {
|
||||
propValues[ prop ] = []
|
||||
}
|
||||
propValues[ prop ].push( obj[ prop ] )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let propInfo = {}
|
||||
for ( let prop in propValues ) {
|
||||
let pinfo = {
|
||||
type: typeof propValues[ prop ][ 0 ],
|
||||
objectCount: propValues[ prop ].length,
|
||||
allValues: propValues[ prop ],
|
||||
uniqueValues: {},
|
||||
minValue: propValues[ prop ][ 0 ],
|
||||
maxValue: propValues[ prop ][ 0 ]
|
||||
}
|
||||
for ( let v of propValues[ prop ] ) {
|
||||
if ( v < pinfo.minValue ) pinfo.minValue = v
|
||||
if ( v > pinfo.maxValue ) pinfo.maxValue = v
|
||||
if ( !( v in pinfo.uniqueValues ) ) {
|
||||
pinfo.uniqueValues[ v ] = 0
|
||||
}
|
||||
pinfo.uniqueValues[ v ] += 1
|
||||
}
|
||||
|
||||
propInfo[ prop ] = pinfo
|
||||
}
|
||||
return propInfo
|
||||
}
|
||||
|
||||
async setFilteredView() {
|
||||
if ( !this.isInitialLoading ) {
|
||||
await this.applyFilter()
|
||||
@@ -156,7 +199,7 @@ export default class SceneObjects {
|
||||
newFilteredObjects.add( filteredPointObjects )
|
||||
|
||||
// group solid objects
|
||||
let groupedFilteredSolidObjects = await this.groupedSolidObjects( filteredSolidObjects )
|
||||
let groupedFilteredSolidObjects = await this.groupSolidObjects( filteredSolidObjects )
|
||||
newFilteredObjects.add( groupedFilteredSolidObjects )
|
||||
|
||||
// Sync update scene
|
||||
@@ -217,6 +260,7 @@ export default class SceneObjects {
|
||||
await this.asyncPause()
|
||||
let groupMaterial = materialIdToMaterial[ materialId ]
|
||||
let groupMesh = new THREE.Mesh( groupGeometry, groupMaterial )
|
||||
groupMesh.userData = null
|
||||
groupedObjects.add( groupMesh )
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,13 @@ export default class SelectionHelper extends EventEmitter {
|
||||
const normalizedPosition = this._getNormalisedClickPosition( e )
|
||||
this.raycaster.setFromCamera( normalizedPosition, this.viewer.camera )
|
||||
|
||||
let intersectedObjects = this.raycaster.intersectObjects( this.subset ? this._getGroupChildren( this.subset ) : this.viewer.sceneManager.filteredObjects )
|
||||
let targetObjects
|
||||
if ( this.subset ) {
|
||||
targetObjects = this._getGroupChildren( this.subset )
|
||||
} else {
|
||||
targetObjects = this.viewer.sceneManager.filteredObjects.filter( obj => !!obj.userData )
|
||||
}
|
||||
let intersectedObjects = this.raycaster.intersectObjects( targetObjects )
|
||||
|
||||
|
||||
if ( this.sectionBox && this.sectionBox.display.visible ) {
|
||||
|
||||
Reference in New Issue
Block a user