wip viewer

This commit is contained in:
cristi8
2021-11-03 13:11:15 +02:00
parent 9a84766722
commit ce7d26a35b
5 changed files with 212 additions and 22 deletions
+5
View File
@@ -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",
+1
View File
@@ -56,6 +56,7 @@
"dependencies": {
"camera-controls": "^1.33.0",
"lodash.debounce": "^4.0.8",
"rainbowvis.js": "^1.0.1",
"three": "^0.133.1"
}
}
+138 -4
View File
@@ -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
}
+61 -17
View File
@@ -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 ) {