Resizable boxes and optimizations

This commit is contained in:
Alexander Nozik 2019-11-04 22:45:00 +03:00
parent 58e396f50c
commit 3b6fd66920
26 changed files with 355 additions and 182 deletions

View File

@ -2,7 +2,7 @@ val dataforgeVersion by extra("0.1.4")
plugins { plugins {
val kotlinVersion = "1.3.50" val kotlinVersion = "1.3.50"
val toolsVersion = "0.2.1" val toolsVersion = "0.2.2"
kotlin("jvm") version kotlinVersion apply false kotlin("jvm") version kotlinVersion apply false
id("kotlin-dce-js") version kotlinVersion apply false id("kotlin-dce-js") version kotlinVersion apply false

View File

@ -1,7 +1,6 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.* import hep.dataforge.names.*
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
@ -145,17 +144,4 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
} else { } else {
set(key.asName(), child) set(key.asName(), child)
} }
// operator fun set(key: String?, child: VisualObject?) = set(key ?: "", child)
protected fun MetaBuilder.updateChildren() {
//adding named children
children.forEach {
"children[${it.key}]" put it.value.toMeta()
}
}
override fun MetaBuilder.updateMeta() {
updateChildren()
}
} }

View File

@ -82,14 +82,6 @@ abstract class AbstractVisualObject : VisualObject {
properties?.get(name) ?: mergedStyles[name] properties?.get(name) ?: mergedStyles[name]
} }
} }
protected open fun MetaBuilder.updateMeta() {}
override fun toMeta(): Meta = buildMeta {
"type" putValue this::class.simpleName
"properties" put properties
updateMeta()
}
} }
fun VisualObject.findStyle(styleName: Name): Meta? { fun VisualObject.findStyle(styleName: Name): Meta? {

View File

@ -3,7 +3,11 @@ package hep.dataforge.vis.spatial
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.meta.* import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.float
import hep.dataforge.meta.get
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.VisualFactory import hep.dataforge.vis.common.VisualFactory
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
@ -27,9 +31,9 @@ class Box(
//TODO add helper for color configuration //TODO add helper for color configuration
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val dx = xSize.toFloat() / 2 val dx = xSize / 2
val dy = ySize.toFloat() / 2 val dy = ySize / 2
val dz = zSize.toFloat() / 2 val dz = zSize / 2
val node1 = Point3D(-dx, -dy, -dz) val node1 = Point3D(-dx, -dy, -dz)
val node2 = Point3D(dx, -dy, -dz) val node2 = Point3D(dx, -dy, -dz)
val node3 = Point3D(dx, dy, -dz) val node3 = Point3D(dx, dy, -dz)
@ -46,16 +50,7 @@ class Box(
geometryBuilder.face4(node8, node5, node6, node7) geometryBuilder.face4(node8, node5, node6, node7)
} }
override fun MetaBuilder.updateMeta() { override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
"xSize" put xSize
"ySize" put ySize
"zSize" put ySize
updatePosition()
}
// override fun toMeta(): Meta {
// return (Visual3DPlugin.json.toJson(Box.serializer(), this) as JsonObject).toMeta()
// }
companion object : VisualFactory<Box> { companion object : VisualFactory<Box> {
const val TYPE = "geometry.3d.box" const val TYPE = "geometry.3d.box"

View File

@ -2,8 +2,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.Meta
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -34,12 +35,7 @@ class Composite(
@Serializable(ConfigSerializer::class) @Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override fun MetaBuilder.updateMeta() { override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
"compositeType" put compositeType
"first" put first.toMeta()
"second" put second.toMeta()
updatePosition()
}
} }
inline fun VisualGroup3D.composite( inline fun VisualGroup3D.composite(

View File

@ -3,7 +3,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -26,6 +28,8 @@ class ConeSegment(
override var position: Point3D? = null override var position: Point3D? = null
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
} }
inline fun VisualGroup3D.cylinder( inline fun VisualGroup3D.cylinder(

View File

@ -3,8 +3,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -19,12 +20,7 @@ class Convex(val points: List<Point3D>) : AbstractVisualObject(), VisualObject3D
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
override fun MetaBuilder.updateMeta() { override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
"points" put {
"point" put points.map { it.toMeta() }
}
updatePosition()
}
companion object { companion object {
const val TYPE = "geometry.3d.convex" const val TYPE = "geometry.3d.convex"

View File

@ -2,7 +2,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -105,6 +107,8 @@ class Extruded(
geometryBuilder.cap(layers.last()) geometryBuilder.cap(layers.last())
} }
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
companion object { companion object {
const val TYPE = "geometry.3d.extruded" const val TYPE = "geometry.3d.extruded"
} }

View File

@ -31,7 +31,7 @@ fun VisualObject.color(rgb: String) {
fun VisualObject.color(rgb: Int) = color(Colors.rgbToString(rgb)) fun VisualObject.color(rgb: Int) = color(Colors.rgbToString(rgb))
fun VisualObject.color(r: UByte, g: UByte, b: UByte) = color( Colors.rgbToString(r,g,b)) fun VisualObject.color(r: UByte, g: UByte, b: UByte) = color(Colors.rgbToString(r, g, b))
var VisualObject.color: String? var VisualObject.color: String?
get() = getProperty(COLOR_KEY).string get() = getProperty(COLOR_KEY).string

View File

@ -3,7 +3,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.number import hep.dataforge.vis.common.number
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -20,6 +22,8 @@ class PolyLine(var points: List<Point3D>) : AbstractVisualObject(), VisualObject
//var lineType by string() //var lineType by string()
var thickness by number(1.0, key = "material.thickness") var thickness by number(1.0, key = "material.thickness")
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
} }
fun VisualGroup3D.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}) = fun VisualGroup3D.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}) =

View File

@ -3,9 +3,12 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.NameSerializer import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.* import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.get
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.asName import hep.dataforge.names.asName
@ -61,10 +64,7 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
} }
} }
override fun MetaBuilder.updateMeta() { override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
//TODO add reference to child
updatePosition()
}
override val children: Map<NameToken, ProxyChild> override val children: Map<NameToken, ProxyChild>
get() = (prototype as? MutableVisualGroup)?.children get() = (prototype as? MutableVisualGroup)?.children
@ -94,6 +94,7 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) }) //override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
@Serializable
inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup { inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup {
val prototype: VisualObject by lazy { val prototype: VisualObject by lazy {
@ -143,6 +144,8 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
} }
} }
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
} }
companion object { companion object {

View File

@ -3,7 +3,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -24,6 +26,8 @@ class Sphere(
override var position: Point3D? = null override var position: Point3D? = null
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
} }
inline fun VisualGroup3D.sphere( inline fun VisualGroup3D.sphere(

View File

@ -2,7 +2,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -123,6 +125,8 @@ class Tube(
} }
} }
} }
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
} }
inline fun VisualGroup3D.tube( inline fun VisualGroup3D.tube(

View File

@ -11,10 +11,9 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.MetaSerializer import hep.dataforge.io.serialization.MetaSerializer
import hep.dataforge.io.serialization.NameSerializer import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.set
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.asName import hep.dataforge.names.asName
@ -98,11 +97,7 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
} }
} }
override fun MetaBuilder.updateMeta() { override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
set(PROTOTYPES_KEY, prototypes?.toMeta())
updatePosition()
updateChildren()
}
companion object { companion object {
const val PROTOTYPES_KEY = "templates" const val PROTOTYPES_KEY = "templates"

View File

@ -40,7 +40,7 @@ interface VisualObject3D : VisualObject {
val LAYER_KEY = "layer".asName() val LAYER_KEY = "layer".asName()
val IGNORE_KEY = "ignore".asName() val IGNORE_KEY = "ignore".asName()
val GEOMETRY_KEY = "geometey".asName() val GEOMETRY_KEY = "geometry".asName()
val x = "x".asName() val x = "x".asName()
val y = "y".asName() val y = "y".asName()

View File

@ -1,96 +0,0 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.*
import hep.dataforge.values.ValueType
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.spatial.Material3D
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material
import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.materials.MeshPhongMaterial
import info.laht.threekt.math.Color
object Materials {
val DEFAULT_COLOR = Color(Colors.darkgreen)
val DEFAULT = MeshPhongMaterial().apply {
color.set(DEFAULT_COLOR)
}
val DEFAULT_LINE_COLOR = Color(Colors.black)
val DEFAULT_LINE = LineBasicMaterial().apply {
color.set(DEFAULT_LINE_COLOR)
}
private val materialCache = HashMap<Meta, Material>()
private val lineMaterialCache = HashMap<Meta, Material>()
fun getMaterial(meta: Meta): Material = materialCache.getOrPut(meta) {
MeshBasicMaterial().apply {
color = meta["color"]?.color() ?: DEFAULT_COLOR
opacity = meta["opacity"]?.double ?: 1.0
transparent = meta["transparent"].boolean ?: (opacity < 1.0)
//node["specularColor"]?.let { specular = it.color() }
//side = 2
}
}
fun getLineMaterial(meta: Meta): Material = lineMaterialCache.getOrPut(meta) {
LineBasicMaterial().apply {
color = meta["color"]?.color() ?: DEFAULT_LINE_COLOR
opacity = meta["opacity"].double ?: 1.0
transparent = meta["transparent"].boolean ?: (opacity < 1.0)
linewidth = meta["thickness"].double ?: 1.0
}
}
}
/**
* Infer color based on meta item
*/
fun MetaItem<*>.color(): Color {
return when (this) {
is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) {
Color(this.value.string)
} else {
val int = value.number.toInt()
// val red = int and 0x00ff0000 shr 16
// val green = int and 0x0000ff00 shr 8
// val blue = int and 0x000000ff
Color(int)
}
is MetaItem.NodeItem -> {
Color(
node["red"]?.int ?: 0,
node["green"]?.int ?: 0,
node["blue"]?.int ?: 0
)
}
}
}
/**
* Infer Three material based on meta item
*/
fun Meta?.jsMaterial(): Material {
return if (this == null) {
Materials.DEFAULT
} else {
Materials.getMaterial(this)
}
}
fun Meta?.jsLineMaterial(): Material {
return if (this == null) {
Materials.DEFAULT_LINE
} else{
Materials.getLineMaterial(this)
}
}
fun Material3D?.jsMaterial(): Material = this?.config.jsMaterial()
fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial()

View File

@ -1,6 +1,8 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.Meta
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import hep.dataforge.meta.get
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
@ -8,10 +10,10 @@ import hep.dataforge.names.startsWith
import hep.dataforge.vis.spatial.Material3D import hep.dataforge.vis.spatial.Material3D
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.layer import hep.dataforge.vis.spatial.layer
import hep.dataforge.vis.spatial.material
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.geometries.EdgesGeometry
import info.laht.threekt.geometries.WireframeGeometry import info.laht.threekt.geometries.WireframeGeometry
import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -28,47 +30,63 @@ abstract class MeshThreeFactory<in T : VisualObject3D>(
abstract fun buildGeometry(obj: T): BufferGeometry abstract fun buildGeometry(obj: T): BufferGeometry
private fun Mesh.applyEdges(obj: T) { private fun Mesh.applyEdges(obj: T) {
children.find { it.name == "edges" }?.let { remove(it) } children.find { it.name == "edges" }?.let {
remove(it)
(it as LineSegments).dispose()
}
//inherited edges definition, enabled by default //inherited edges definition, enabled by default
if (obj.getProperty(EDGES_ENABLED_KEY).boolean != false) { if (obj.getProperty(EDGES_ENABLED_KEY).boolean != false) {
val material = obj.getProperty(EDGES_MATERIAL_KEY).node.jsLineMaterial()
val material = ThreeMaterials.getLineMaterial(obj.getProperty(EDGES_MATERIAL_KEY).node)
add( add(
LineSegments( LineSegments(
EdgesGeometry(geometry as BufferGeometry), EdgesGeometry(geometry as BufferGeometry),
material material
) ).apply {
name = "edges"
}
) )
} }
} }
private fun Mesh.applyWireFrame(obj: T) { private fun Mesh.applyWireFrame(obj: T) {
children.find { it.name == "wireframe" }?.let { remove(it) } children.find { it.name == "wireframe" }?.let {
remove(it)
(it as LineSegments).dispose()
}
//inherited wireframe definition, disabled by default //inherited wireframe definition, disabled by default
if (obj.getProperty(WIREFRAME_ENABLED_KEY).boolean == true) { if (obj.getProperty(WIREFRAME_ENABLED_KEY).boolean == true) {
val material = obj.getProperty(WIREFRAME_MATERIAL_KEY).node.jsLineMaterial() val material = ThreeMaterials.getLineMaterial(obj.getProperty(WIREFRAME_MATERIAL_KEY).node)
add( add(
LineSegments( LineSegments(
WireframeGeometry(geometry as BufferGeometry), WireframeGeometry(geometry as BufferGeometry),
material material
) ).apply {
name = "wireframe"
}
) )
} }
} }
override fun invoke(obj: T): Mesh { override fun invoke(obj: T): Mesh {
//TODO add caching for geometries using templates
val geometry = buildGeometry(obj) val geometry = buildGeometry(obj)
//JS sometimes tries to pass Geometry as BufferGeometry //JS sometimes tries to pass Geometry as BufferGeometry
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
val mesh = Mesh(geometry, obj.material.jsMaterial()).apply { val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
val mesh = Mesh(geometry, MeshBasicMaterial()).apply {
matrixAutoUpdate = false matrixAutoUpdate = false
applyEdges(obj) applyEdges(obj)
applyWireFrame(obj) applyWireFrame(obj)
//set position for mesh //set position for mesh
updatePosition(obj) updatePosition(obj)
//set color for mesh
updateMaterial(obj)
layers.enable(obj.layer) layers.enable(obj.layer)
children.forEach { children.forEach {
it.layers.enable(obj.layer) it.layers.enable(obj.layer)
@ -78,7 +96,14 @@ abstract class MeshThreeFactory<in T : VisualObject3D>(
//add listener to object properties //add listener to object properties
obj.onPropertyChange(this) { name, _, _ -> obj.onPropertyChange(this) { name, _, _ ->
when { when {
name.startsWith(VisualObject3D.GEOMETRY_KEY) -> mesh.geometry = buildGeometry(obj) name.startsWith(VisualObject3D.GEOMETRY_KEY) -> {
val oldGeometry = mesh.geometry as BufferGeometry
val newGeometry = buildGeometry(obj)
oldGeometry.attributes = newGeometry.attributes
mesh.applyWireFrame(obj)
mesh.applyEdges(obj)
newGeometry.dispose()
}
name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj) name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj)
else -> mesh.updateProperty(obj, name) else -> mesh.updateProperty(obj, name)

View File

@ -55,8 +55,7 @@ operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
*/ */
fun Object3D.updateProperty(source: VisualObject, propertyName: Name) { fun Object3D.updateProperty(source: VisualObject, propertyName: Name) {
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) { if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
//updated material updateMaterial(source)
material = source.material.jsMaterial()
} else if ( } else if (
source is VisualObject3D && source is VisualObject3D &&
(propertyName.startsWith(VisualObject3D.position) (propertyName.startsWith(VisualObject3D.position)

View File

@ -1,8 +1,8 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.node
import hep.dataforge.vis.spatial.PolyLine import hep.dataforge.vis.spatial.PolyLine
import hep.dataforge.vis.spatial.layer import hep.dataforge.vis.spatial.layer
import hep.dataforge.vis.spatial.material
import info.laht.threekt.core.Geometry import info.laht.threekt.core.Geometry
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
@ -16,7 +16,8 @@ object ThreeLineFactory : ThreeFactory<PolyLine> {
vertices = obj.points.toTypedArray() vertices = obj.points.toTypedArray()
} }
val material = obj.material.jsLineMaterial() val material =
ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node)
return LineSegments(geometry, material).apply { return LineSegments(geometry, material).apply {
updatePosition(obj) updatePosition(obj)

View File

@ -0,0 +1,103 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.*
import hep.dataforge.values.ValueType
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.Material3D
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material
import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.materials.MeshPhongMaterial
import info.laht.threekt.math.Color
import info.laht.threekt.objects.Mesh
object ThreeMaterials {
val DEFAULT_COLOR = Color(Colors.darkgreen)
val DEFAULT = MeshPhongMaterial().apply {
color.set(DEFAULT_COLOR)
}
val DEFAULT_LINE_COLOR = Color(Colors.black)
val DEFAULT_LINE = LineBasicMaterial().apply {
color.set(DEFAULT_LINE_COLOR)
}
// private val materialCache = HashMap<Meta, Material>()
private val lineMaterialCache = HashMap<Meta?, Material>()
// fun buildMaterial(meta: Meta): Material =
// MeshBasicMaterial().apply {
// color = meta["color"]?.color() ?: DEFAULT_COLOR
// opacity = meta["opacity"]?.double ?: 1.0
// transparent = meta["transparent"].boolean ?: (opacity < 1.0)
// //node["specularColor"]?.let { specular = it.color() }
// //side = 2
// }
fun getLineMaterial(meta: Meta?): Material = lineMaterialCache.getOrPut(meta) {
LineBasicMaterial().apply {
color = meta["color"]?.color() ?: DEFAULT_LINE_COLOR
opacity = meta["opacity"].double ?: 1.0
transparent = meta["transparent"].boolean ?: (opacity < 1.0)
linewidth = meta["thickness"].double ?: 1.0
}
}
}
/**
* Infer color based on meta item
*/
fun MetaItem<*>.color(): Color {
return when (this) {
is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) {
Color(this.value.string)
} else {
val int = value.number.toInt()
Color(int)
}
is MetaItem.NodeItem -> {
Color(
node["red"]?.int ?: 0,
node["green"]?.int ?: 0,
node["blue"]?.int ?: 0
)
}
}
}
///**
// * Infer Three material based on meta item
// */
//fun Meta?.jsMaterial(): Material {
// return if (this == null) {
// ThreeMaterials.DEFAULT
// } else {
// ThreeMaterials.buildMaterial(this)
// }
//}
//
//fun Meta?.jsLineMaterial(): Material {
// return if (this == null) {
// ThreeMaterials.DEFAULT_LINE
// } else {
// ThreeMaterials.buildLineMaterial(this)
// }
//}
//fun Material3D?.jsMaterial(): Material = this?.config.jsMaterial()
//fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial()
fun Mesh.updateMaterial(obj: VisualObject) {
val meta = obj.properties[Material3D.MATERIAL_KEY].node?:EmptyMeta
material = (material as? MeshBasicMaterial ?: MeshBasicMaterial()).apply {
color = meta["color"]?.color() ?: ThreeMaterials.DEFAULT_COLOR
opacity = meta["opacity"]?.double ?: 1.0
transparent = meta["transparent"].boolean ?: (opacity < 1.0)
needsUpdate = true
}
}

View File

@ -36,6 +36,7 @@ class ThreePlugin : AbstractPlugin() {
fun buildObject3D(obj: VisualObject3D): Object3D { fun buildObject3D(obj: VisualObject3D): Object3D {
return when (obj) { return when (obj) {
is ThreeVisualObject -> obj.toObject3D()
is Proxy -> proxyFactory(obj) is Proxy -> proxyFactory(obj)
is VisualGroup3D -> { is VisualGroup3D -> {
val group = ThreeGroup() val group = ThreeGroup()
@ -85,7 +86,7 @@ class ThreePlugin : AbstractPlugin() {
companion object : PluginFactory<ThreePlugin> { companion object : PluginFactory<ThreePlugin> {
override val tag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP) override val tag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP)
override val type = ThreePlugin::class override val type = ThreePlugin::class
override fun invoke(meta: Meta,context: Context) = ThreePlugin() override fun invoke(meta: Meta, context: Context) = ThreePlugin()
} }
} }

View File

@ -0,0 +1,39 @@
@file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vis.spatial.three
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.spatial.Point3D
import hep.dataforge.vis.spatial.Point3DSerializer
import hep.dataforge.vis.spatial.Visual3DPlugin
import hep.dataforge.vis.spatial.VisualObject3D
import info.laht.threekt.core.Object3D
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
/**
* A custom visual object that has its own Three.js renderer
*/
interface ThreeVisualObject : VisualObject3D {
fun toObject3D(): Object3D
}
@Serializable
class CustomThreeVisualObject(val threeFactory: ThreeFactory<VisualObject3D>) : AbstractVisualObject(),
ThreeVisualObject {
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
@Serializable(ConfigSerializer::class)
override var properties: Config? = null
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
override fun toObject3D(): Object3D = threeFactory(this)
}

View File

@ -5,12 +5,13 @@ import hep.dataforge.meta.float
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.*
import info.laht.threekt.core.Face3 import info.laht.threekt.external.controls.OrbitControls
import info.laht.threekt.core.Geometry import info.laht.threekt.materials.Material
import info.laht.threekt.core.Object3D
import info.laht.threekt.math.Euler import info.laht.threekt.math.Euler
import info.laht.threekt.math.Vector3 import info.laht.threekt.math.Vector3
import info.laht.threekt.objects.Mesh
import info.laht.threekt.textures.Texture
/** /**
* Utility methods for three.kt. * Utility methods for three.kt.
@ -65,3 +66,18 @@ fun CSG.toGeometry(): Geometry {
geom.computeBoundingBox() geom.computeBoundingBox()
return geom return geom
} }
internal fun Any.dispose() {
when (this) {
is Geometry -> dispose()
is BufferGeometry -> dispose()
is DirectGeometry -> dispose()
is Material -> dispose()
is Mesh -> {
geometry.dispose()
material.dispose()
}
is OrbitControls -> dispose()
is Texture -> dispose()
}
}

View File

@ -7,3 +7,13 @@ dependencies {
api(project(":dataforge-vis-spatial")) api(project(":dataforge-vis-spatial"))
testCompile(kotlin("test-js")) testCompile(kotlin("test-js"))
} }
kotlin{
target {
browser{
webpackTask {
sourceMaps = false
}
}
}
}

View File

@ -5,6 +5,8 @@ import hep.dataforge.js.Application
import hep.dataforge.js.startApplication import hep.dataforge.js.startApplication
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.three.MeshThreeFactory.Companion.EDGES_ENABLED_KEY
import hep.dataforge.vis.spatial.three.MeshThreeFactory.Companion.WIREFRAME_ENABLED_KEY
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
@ -14,7 +16,6 @@ import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
import kotlin.random.Random import kotlin.random.Random
private class ThreeDemoApp : Application { private class ThreeDemoApp : Application {
override fun start(state: Map<String, Any>) { override fun start(state: Map<String, Any>) {
@ -143,6 +144,28 @@ private class ThreeDemoApp : Application {
} }
} }
} }
demo("dynamicBox", "Dancing boxes") {
val boxes = (-10..10).flatMap { i ->
(-10..10).map { j ->
varBox(10, 10, 0, name = "cell_${i}_${j}") {
x = i * 10
y = j * 10
value = 128
setProperty(EDGES_ENABLED_KEY, false)
setProperty(WIREFRAME_ENABLED_KEY, false)
}
}
}
GlobalScope.launch {
while (isActive) {
delay(200)
boxes.forEach { box ->
box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(0..255)
}
}
}
}
} }

View File

@ -0,0 +1,69 @@
@file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vis.spatial.demo
import hep.dataforge.meta.int
import hep.dataforge.meta.number
import hep.dataforge.names.plus
import hep.dataforge.vis.common.getProperty
import hep.dataforge.vis.common.setProperty
import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.VisualObject3D.Companion.GEOMETRY_KEY
import hep.dataforge.vis.spatial.demo.VariableBoxThreeFactory.X_SIZE_KEY
import hep.dataforge.vis.spatial.demo.VariableBoxThreeFactory.Y_SIZE_KEY
import hep.dataforge.vis.spatial.demo.VariableBoxThreeFactory.Z_SIZE_KEY
import hep.dataforge.vis.spatial.three.CustomThreeVisualObject
import hep.dataforge.vis.spatial.three.MeshThreeFactory
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.BoxBufferGeometry
import kotlinx.serialization.UseSerializers
import kotlin.math.max
private val BOX_Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
internal var VisualObject3D.variableZSize: Number
get() = getProperty(BOX_Z_SIZE_KEY, false).number ?: 0f
set(value) {
setProperty(BOX_Z_SIZE_KEY, value)
}
internal var VisualObject3D.value: Int
get() = getProperty("value", false).int ?: 0
set(value) {
setProperty("value", value)
val size = value.toFloat() / 255f * 20f
variableZSize = size
z = -size / 2
val b = max(0, 255 - value)
val r = max(0, value - 255)
val g = 255 - b - r
color(r.toUByte(), g.toUByte(), b.toUByte())
}
fun VisualGroup3D.varBox(
xSize: Number,
ySize: Number,
zSize: Number,
name: String = "",
action: VisualObject3D.() -> Unit = {}
) = CustomThreeVisualObject(VariableBoxThreeFactory).apply {
setProperty(X_SIZE_KEY, xSize)
setProperty(Y_SIZE_KEY, ySize)
setProperty(Z_SIZE_KEY, zSize)
}.apply(action).also { set(name, it) }
private object VariableBoxThreeFactory : MeshThreeFactory<VisualObject3D>(VisualObject3D::class) {
val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
override fun buildGeometry(obj: VisualObject3D): BufferGeometry {
val xSize = obj.getProperty(X_SIZE_KEY, false).number ?: 0f
val ySize = obj.getProperty(Y_SIZE_KEY, false).number ?: 0f
val zSize = obj.getProperty(Z_SIZE_KEY, false).number ?: 0f
return obj.detail?.let { detail ->
BoxBufferGeometry(xSize, ySize, zSize, detail, detail, detail)
} ?: BoxBufferGeometry(xSize, ySize, zSize)
}
}