More universal treatment of highlighting in three

This commit is contained in:
Alexander Nozik 2022-08-19 11:22:10 +03:00
parent 67afa4e45b
commit 40b784f551
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
8 changed files with 79 additions and 31 deletions

View File

@ -67,7 +67,7 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
* List of names of styles applied to this object. Order matters. Not inherited. * List of names of styles applied to this object. Order matters. Not inherited.
*/ */
public var Vision.styles: List<String> public var Vision.styles: List<String>
get() = properties.getValue(Vision.STYLE_KEY, inherit = false, includeStyles = false)?.stringList ?: emptyList() get() = properties.own?.getValue(Vision.STYLE_KEY)?.stringList ?: emptyList()
set(value) { set(value) {
properties.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) properties.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
} }

View File

@ -143,8 +143,11 @@ private class VisionPropertiesItem(
override fun hashCode(): Int = Meta.hashCode(this) override fun hashCode(): Int = Meta.hashCode(this)
} }
/**
* A base implementation of [MutableVisionProperties]
*/
public abstract class AbstractVisionProperties( public abstract class AbstractVisionProperties(
private val vision: Vision, public val vision: Vision,
) : MutableVisionProperties { ) : MutableVisionProperties {
override val descriptor: MetaDescriptor? get() = vision.descriptor override val descriptor: MetaDescriptor? get() = vision.descriptor

View File

@ -58,6 +58,8 @@ public interface Solid : Vision {
public val ROTATION_KEY: Name = "rotation".asName() public val ROTATION_KEY: Name = "rotation".asName()
public val QUATERNION_KEY: Name = "quaternion".asName()
public val X_ROTATION_KEY: Name = ROTATION_KEY + X_KEY public val X_ROTATION_KEY: Name = ROTATION_KEY + X_KEY
public val Y_ROTATION_KEY: Name = ROTATION_KEY + Y_KEY public val Y_ROTATION_KEY: Name = ROTATION_KEY + Y_KEY
public val Z_ROTATION_KEY: Name = ROTATION_KEY + Z_KEY public val Z_ROTATION_KEY: Name = ROTATION_KEY + Z_KEY

View File

@ -20,9 +20,12 @@ import three.external.controls.OrbitControls
import three.external.controls.TrackballControls import three.external.controls.TrackballControls
import three.geometries.EdgesGeometry import three.geometries.EdgesGeometry
import three.helpers.AxesHelper import three.helpers.AxesHelper
import three.materials.Material
import three.math.* import three.math.*
import three.meshline.MeshLine import three.meshline.MeshLine
import three.meshline.MeshLineMaterial import three.meshline.MeshLineMaterial
import three.meshline.isMeshLineMaterial
import three.objects.LineSegments
import three.objects.Mesh import three.objects.Mesh
import three.scenes.Scene import three.scenes.Scene
import kotlin.math.cos import kotlin.math.cos
@ -276,7 +279,7 @@ public class ThreeCanvas(
private fun Object3D.toggleHighlight( private fun Object3D.toggleHighlight(
highlight: Boolean, highlight: Boolean,
edgesName: String, edgesName: String,
material: MeshLineMaterial, material: Material,
) { ) {
if (userData[DO_NOT_HIGHLIGHT_TAG] == true) { if (userData[DO_NOT_HIGHLIGHT_TAG] == true) {
@ -284,16 +287,21 @@ public class ThreeCanvas(
} }
if (isMesh(this)) { if (isMesh(this)) {
val highlightMesh = getObjectByName(edgesName) ?: Mesh( val highlightMesh = getObjectByName(edgesName) ?: if (isMeshLineMaterial(material)) {
Mesh(
MeshLine(EdgesGeometry(geometry)), MeshLine(EdgesGeometry(geometry)),
material material
).also { )
} else {
LineSegments(EdgesGeometry(geometry), material)
}.also {
it.name = edgesName it.name = edgesName
add(it) add(it)
} }
highlightMesh.visible = highlight highlightMesh.visible = highlight
} else { } else {
children.filter { !it.name.startsWith("@") && it.name != edgesName }.forEach { //ignore service objects if they are not statics
children.filter { it.name.startsWith("@static") || !it.name.startsWith("@") }.forEach {
it.toggleHighlight(highlight, edgesName, material) it.toggleHighlight(highlight, edgesName, material)
} }
} }
@ -319,15 +327,25 @@ public class ThreeCanvas(
public companion object { public companion object {
public val SELECTED_MATERIAL: MeshLineMaterial = MeshLineMaterial().apply { public val SELECTED_MATERIAL: MeshLineMaterial = MeshLineMaterial().apply {
color.set(Colors.ivory) color.set(Colors.ivory)
linewidth = 2.0 thickness = 2f
cached = true cached = true
} }
public val HIGHLIGHT_MATERIAL: MeshLineMaterial = MeshLineMaterial().apply { public val HIGHLIGHT_MATERIAL: MeshLineMaterial = MeshLineMaterial().apply {
color.set(Colors.blue) color.set(Colors.blue)
linewidth = 2.0 thickness = 2f
cached = true cached = true
} }
//
// public val SELECTED_MATERIAL: LineBasicMaterial = LineBasicMaterial().apply {
// color.set(Colors.ivory)
// cached = true
// }
//
// public val HIGHLIGHT_MATERIAL: LineBasicMaterial = LineBasicMaterial().apply {
// color.set(Colors.blue)
// cached = true
// }
public const val DO_NOT_HIGHLIGHT_TAG: String = "doNotHighlight" public const val DO_NOT_HIGHLIGHT_TAG: String = "doNotHighlight"

View File

@ -1,14 +1,14 @@
package space.kscience.visionforge.solid.three package space.kscience.visionforge.solid.three
import three.core.Object3D
import three.geometries.TextBufferGeometry
import three.objects.Mesh
import kotlinx.js.jso import kotlinx.js.jso
import space.kscience.dataforge.context.logger import space.kscience.dataforge.context.logger
import space.kscience.dataforge.context.warn import space.kscience.dataforge.context.warn
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.SolidLabel import space.kscience.visionforge.solid.SolidLabel
import three.core.Object3D
import three.geometries.TextBufferGeometry
import three.objects.Mesh
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -25,7 +25,7 @@ public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
curveSegments = 1 curveSegments = 1
}) })
return Mesh(textGeo, ThreeMaterials.DEFAULT).apply { return Mesh(textGeo, ThreeMaterials.DEFAULT).apply {
createMaterial(vision) setMaterial(vision)
updatePosition(vision) updatePosition(vision)
if(observe) { if(observe) {
vision.onPropertyChange(three.context) { vision.onPropertyChange(three.context) {

View File

@ -60,6 +60,7 @@ public object ThreeMaterials {
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
} }
else -> MeshStandardMaterial().apply { else -> MeshStandardMaterial().apply {
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
emissive = meta[SolidMaterial.EMISSIVE_COLOR_KEY]?.threeColor() ?: DEFAULT_EMISSIVE_COLOR emissive = meta[SolidMaterial.EMISSIVE_COLOR_KEY]?.threeColor() ?: DEFAULT_EMISSIVE_COLOR
@ -71,10 +72,18 @@ public object ThreeMaterials {
needsUpdate = true needsUpdate = true
} }
private val materialCache = HashMap<Int, Material>() // private val materialCache = HashMap<Int, Material>()
//
// internal fun cacheMaterial(meta: Meta): Material = materialCache.getOrPut(meta.hashCode()) {
// buildMaterial(meta).apply {
// cached = true
// }
// }
internal fun cacheMaterial(meta: Meta): Material = materialCache.getOrPut(meta.hashCode()) { private val visionMaterialCache = HashMap<Vision, Material>()
buildMaterial(meta).apply {
internal fun cacheMaterial(vision: Vision): Material = visionMaterialCache.getOrPut(vision) {
buildMaterial(vision.properties.getProperty(SolidMaterial.MATERIAL_KEY)).apply {
cached = true cached = true
} }
} }
@ -84,7 +93,7 @@ public object ThreeMaterials {
* Compute color * Compute color
*/ */
public fun Meta.threeColor(): Color? { public fun Meta.threeColor(): Color? {
if(isEmpty()) return null if (isEmpty()) return null
val value = value val value = value
return if (isLeaf) { return if (isLeaf) {
when { when {
@ -118,13 +127,19 @@ internal var Material.cached: Boolean
userData["cached"] = value userData["cached"] = value
} }
public fun Mesh.createMaterial(vision: Vision) { public fun Mesh.setMaterial(vision: Vision) {
val ownMaterialMeta = vision.properties.own?.get(SolidMaterial.MATERIAL_KEY) if (
if (ownMaterialMeta == null) { vision.properties.own?.get(SolidMaterial.MATERIAL_KEY) == null
if (vision is SolidReference && vision.getStyleNodes(SolidMaterial.MATERIAL_KEY).isEmpty()) { && vision.getStyleNodes(SolidMaterial.MATERIAL_KEY).isEmpty()
createMaterial(vision.prototype) ) {
//if this is a reference, use material of the prototype
if (vision is SolidReference) {
ThreeMaterials.cacheMaterial(vision.prototype)
} else { } else {
material = ThreeMaterials.cacheMaterial(vision.properties.getProperty(SolidMaterial.MATERIAL_KEY)) material = vision.parent?.let { parent ->
//TODO cache parent material
ThreeMaterials.buildMaterial(parent.properties.getProperty(SolidMaterial.MATERIAL_KEY))
} ?: ThreeMaterials.cacheMaterial(vision)
} }
} else { } else {
material = ThreeMaterials.buildMaterial(vision.properties.getProperty(SolidMaterial.MATERIAL_KEY)) material = ThreeMaterials.buildMaterial(vision.properties.getProperty(SolidMaterial.MATERIAL_KEY))
@ -138,22 +153,27 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
|| propertyName == SolidMaterial.MATERIAL_KEY + SolidMaterial.TYPE_KEY || propertyName == SolidMaterial.MATERIAL_KEY + SolidMaterial.TYPE_KEY
) { ) {
//generate a new material since cached material should not be changed //generate a new material since cached material should not be changed
createMaterial(vision) setMaterial(vision)
} else { } else {
when (propertyName) { when (propertyName) {
SolidMaterial.MATERIAL_COLOR_KEY -> { SolidMaterial.MATERIAL_COLOR_KEY -> {
material.asDynamic().color = vision.properties.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).threeColor() material.asDynamic().color =
vision.properties.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).threeColor()
?: ThreeMaterials.DEFAULT_COLOR ?: ThreeMaterials.DEFAULT_COLOR
} }
SolidMaterial.SPECULAR_COLOR_KEY -> { SolidMaterial.SPECULAR_COLOR_KEY -> {
material.asDynamic().specular = vision.properties.getProperty(SolidMaterial.SPECULAR_COLOR_KEY).threeColor() material.asDynamic().specular =
vision.properties.getProperty(SolidMaterial.SPECULAR_COLOR_KEY).threeColor()
?: ThreeMaterials.DEFAULT_COLOR ?: ThreeMaterials.DEFAULT_COLOR
} }
SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY -> { SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY -> {
material.asDynamic().emissive = vision.properties.getProperty(SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY) material.asDynamic().emissive = vision.properties.getProperty(SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY)
.threeColor() .threeColor()
?: ThreeMaterials.BLACK_COLOR ?: ThreeMaterials.BLACK_COLOR
} }
SolidMaterial.MATERIAL_OPACITY_KEY -> { SolidMaterial.MATERIAL_OPACITY_KEY -> {
val opacity = vision.properties.getValue( val opacity = vision.properties.getValue(
SolidMaterial.MATERIAL_OPACITY_KEY, SolidMaterial.MATERIAL_OPACITY_KEY,
@ -162,12 +182,14 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
material.opacity = opacity material.opacity = opacity
material.transparent = opacity < 1.0 material.transparent = opacity < 1.0
} }
SolidMaterial.MATERIAL_WIREFRAME_KEY -> { SolidMaterial.MATERIAL_WIREFRAME_KEY -> {
material.asDynamic().wireframe = vision.properties.getValue( material.asDynamic().wireframe = vision.properties.getValue(
SolidMaterial.MATERIAL_WIREFRAME_KEY, SolidMaterial.MATERIAL_WIREFRAME_KEY,
inherit = true, inherit = true,
)?.boolean ?: false )?.boolean ?: false
} }
else -> console.warn("Unrecognized material property: $propertyName") else -> console.warn("Unrecognized material property: $propertyName")
} }
material.needsUpdate = true material.needsUpdate = true

View File

@ -85,7 +85,7 @@ public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit
} }
internal fun Mesh.applyProperties(vision: Solid): Mesh = apply { internal fun Mesh.applyProperties(vision: Solid): Mesh = apply {
createMaterial(vision) setMaterial(vision)
applyEdges(vision) applyEdges(vision)
//applyWireFrame(obj) //applyWireFrame(obj)
layers.set(vision.layer) layers.set(vision.layer)

View File

@ -1,8 +1,11 @@
package three.meshline package three.meshline
import three.core.BufferGeometry import three.core.BufferGeometry
import three.materials.Material
import three.math.Vector3 import three.math.Vector3
public fun MeshLine(geometry: BufferGeometry): MeshLine = MeshLine().apply { setGeometry(geometry) } public fun MeshLine(geometry: BufferGeometry): MeshLine = MeshLine().apply { setGeometry(geometry) }
public fun MeshLine(points: Array<Vector3>): MeshLine = MeshLine().apply { setPoints(points) } public fun MeshLine(points: Array<Vector3>): MeshLine = MeshLine().apply { setPoints(points) }
internal fun isMeshLineMaterial(material: Material): Boolean = material.asDynamic().isMeshLineMaterial == true