diff --git a/build.gradle.kts b/build.gradle.kts index edf19391..a8d57674 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ val dataforgeVersion by extra("0.1.4") plugins { val kotlinVersion = "1.3.50" - val toolsVersion = "0.2.1" + val toolsVersion = "0.2.2" kotlin("jvm") version kotlinVersion apply false id("kotlin-dce-js") version kotlinVersion apply false diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt index c8637618..921617c8 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt @@ -1,7 +1,6 @@ package hep.dataforge.vis.common import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaItem import hep.dataforge.names.* import kotlinx.serialization.Transient @@ -145,17 +144,4 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup } else { 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() - } } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt index 5a4a0639..e0808e75 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt @@ -82,14 +82,6 @@ abstract class AbstractVisualObject : VisualObject { 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? { diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt index 8b78f991..571a1ff8 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt @@ -3,7 +3,11 @@ package hep.dataforge.vis.spatial import hep.dataforge.context.Context 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.VisualFactory import hep.dataforge.vis.common.VisualObject @@ -27,9 +31,9 @@ class Box( //TODO add helper for color configuration override fun toGeometry(geometryBuilder: GeometryBuilder) { - val dx = xSize.toFloat() / 2 - val dy = ySize.toFloat() / 2 - val dz = zSize.toFloat() / 2 + val dx = xSize / 2 + val dy = ySize / 2 + val dz = zSize / 2 val node1 = Point3D(-dx, -dy, -dz) val node2 = Point3D(dx, -dy, -dz) val node3 = Point3D(dx, dy, -dz) @@ -46,16 +50,7 @@ class Box( geometryBuilder.face4(node8, node5, node6, node7) } - override fun MetaBuilder.updateMeta() { - "xSize" put xSize - "ySize" put ySize - "zSize" put ySize - updatePosition() - } - -// override fun toMeta(): Meta { -// return (Visual3DPlugin.json.toJson(Box.serializer(), this) as JsonObject).toMeta() -// } + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() companion object : VisualFactory { const val TYPE = "geometry.3d.box" diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt index ba20b4b3..4e920800 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt @@ -2,8 +2,9 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer +import hep.dataforge.io.toMeta import hep.dataforge.meta.Config -import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.Meta import hep.dataforge.meta.update import hep.dataforge.vis.common.AbstractVisualObject import kotlinx.serialization.Serializable @@ -34,12 +35,7 @@ class Composite( @Serializable(ConfigSerializer::class) override var properties: Config? = null - override fun MetaBuilder.updateMeta() { - "compositeType" put compositeType - "first" put first.toMeta() - "second" put second.toMeta() - updatePosition() - } + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() } inline fun VisualGroup3D.composite( diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt index db87acc7..b1533a83 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt @@ -3,7 +3,9 @@ package hep.dataforge.vis.spatial 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 kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -26,6 +28,8 @@ class ConeSegment( override var position: Point3D? = null override var rotation: Point3D? = null override var scale: Point3D? = null + + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() } inline fun VisualGroup3D.cylinder( diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt index 7231f6c2..3d9f03d5 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt @@ -3,8 +3,9 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer +import hep.dataforge.io.toMeta import hep.dataforge.meta.Config -import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.Meta import hep.dataforge.vis.common.AbstractVisualObject import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -19,12 +20,7 @@ class Convex(val points: List) : AbstractVisualObject(), VisualObject3D override var rotation: Point3D? = null override var scale: Point3D? = null - override fun MetaBuilder.updateMeta() { - "points" put { - "point" put points.map { it.toMeta() } - } - updatePosition() - } + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() companion object { const val TYPE = "geometry.3d.convex" diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt index 7255b7ef..c0bb0fb1 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt @@ -2,7 +2,9 @@ package hep.dataforge.vis.spatial 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 kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -105,6 +107,8 @@ class Extruded( geometryBuilder.cap(layers.last()) } + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() + companion object { const val TYPE = "geometry.3d.extruded" } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt index e3947546..645c0cf5 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt @@ -31,7 +31,7 @@ fun VisualObject.color(rgb: String) { 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? get() = getProperty(COLOR_KEY).string diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt index 3e0e554b..001c1e40 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt @@ -3,7 +3,9 @@ package hep.dataforge.vis.spatial 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.common.number import kotlinx.serialization.Serializable @@ -20,6 +22,8 @@ class PolyLine(var points: List) : AbstractVisualObject(), VisualObject //var lineType by string() 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 = {}) = diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt index 60756f7a..e5ea3be6 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt @@ -3,9 +3,12 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer - 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.NameToken import hep.dataforge.names.asName @@ -61,10 +64,7 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua } } - override fun MetaBuilder.updateMeta() { - //TODO add reference to child - updatePosition() - } + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() override val children: Map 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) }) + @Serializable inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup { 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 { diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt index ab7b79f9..1b210c75 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt @@ -3,7 +3,9 @@ package hep.dataforge.vis.spatial 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 kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -24,6 +26,8 @@ class Sphere( override var position: Point3D? = null override var rotation: Point3D? = null override var scale: Point3D? = null + + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() } inline fun VisualGroup3D.sphere( diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt index 384f3cfb..ccf6a62f 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt @@ -2,7 +2,9 @@ package hep.dataforge.vis.spatial 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 kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -123,6 +125,8 @@ class Tube( } } } + + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() } inline fun VisualGroup3D.tube( diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt index 622f345d..071623b7 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt @@ -11,10 +11,9 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.MetaSerializer import hep.dataforge.io.serialization.NameSerializer +import hep.dataforge.io.toMeta import hep.dataforge.meta.Config import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder -import hep.dataforge.meta.set import hep.dataforge.names.Name import hep.dataforge.names.NameToken import hep.dataforge.names.asName @@ -98,11 +97,7 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { } } - override fun MetaBuilder.updateMeta() { - set(PROTOTYPES_KEY, prototypes?.toMeta()) - updatePosition() - updateChildren() - } + override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta() companion object { const val PROTOTYPES_KEY = "templates" diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt index 1f26ff0b..6992e0ec 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt @@ -40,7 +40,7 @@ interface VisualObject3D : VisualObject { val LAYER_KEY = "layer".asName() val IGNORE_KEY = "ignore".asName() - val GEOMETRY_KEY = "geometey".asName() + val GEOMETRY_KEY = "geometry".asName() val x = "x".asName() val y = "y".asName() diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/Materials.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/Materials.kt deleted file mode 100644 index 8e575169..00000000 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/Materials.kt +++ /dev/null @@ -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() - private val lineMaterialCache = HashMap() - - 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() - diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt index 19745507..35898398 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt @@ -1,6 +1,8 @@ package hep.dataforge.vis.spatial.three +import hep.dataforge.meta.Meta import hep.dataforge.meta.boolean +import hep.dataforge.meta.get import hep.dataforge.meta.node import hep.dataforge.names.asName 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.VisualObject3D import hep.dataforge.vis.spatial.layer -import hep.dataforge.vis.spatial.material import info.laht.threekt.core.BufferGeometry import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.geometries.WireframeGeometry +import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh import kotlin.reflect.KClass @@ -28,47 +30,63 @@ abstract class MeshThreeFactory( abstract fun buildGeometry(obj: T): BufferGeometry 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 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( LineSegments( EdgesGeometry(geometry as BufferGeometry), material - ) + ).apply { + name = "edges" + } ) } } 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 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( LineSegments( WireframeGeometry(geometry as BufferGeometry), material - ) + ).apply { + name = "wireframe" + } ) } } override fun invoke(obj: T): Mesh { - //TODO add caching for geometries using templates val geometry = buildGeometry(obj) //JS sometimes tries to pass Geometry as BufferGeometry @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 applyEdges(obj) applyWireFrame(obj) + //set position for mesh updatePosition(obj) + //set color for mesh + updateMaterial(obj) + layers.enable(obj.layer) children.forEach { it.layers.enable(obj.layer) @@ -78,7 +96,14 @@ abstract class MeshThreeFactory( //add listener to object properties obj.onPropertyChange(this) { name, _, _ -> 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(EDGES_KEY) -> mesh.applyEdges(obj) else -> mesh.updateProperty(obj, name) diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt index 23339ac7..bfd273c7 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt @@ -55,8 +55,7 @@ operator fun ThreeFactory.invoke(obj: Any): Object3D { */ fun Object3D.updateProperty(source: VisualObject, propertyName: Name) { if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) { - //updated material - material = source.material.jsMaterial() + updateMaterial(source) } else if ( source is VisualObject3D && (propertyName.startsWith(VisualObject3D.position) diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt index f3a46e1b..f2fba9ec 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt @@ -1,8 +1,8 @@ package hep.dataforge.vis.spatial.three +import hep.dataforge.meta.node import hep.dataforge.vis.spatial.PolyLine import hep.dataforge.vis.spatial.layer -import hep.dataforge.vis.spatial.material import info.laht.threekt.core.Geometry import info.laht.threekt.core.Object3D import info.laht.threekt.objects.LineSegments @@ -16,7 +16,8 @@ object ThreeLineFactory : ThreeFactory { 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 { updatePosition(obj) diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt new file mode 100644 index 00000000..4b4309d8 --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt @@ -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() + private val lineMaterialCache = HashMap() + + +// 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 + } +} diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt index 2831a155..b3d6db64 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt @@ -36,6 +36,7 @@ class ThreePlugin : AbstractPlugin() { fun buildObject3D(obj: VisualObject3D): Object3D { return when (obj) { + is ThreeVisualObject -> obj.toObject3D() is Proxy -> proxyFactory(obj) is VisualGroup3D -> { val group = ThreeGroup() @@ -85,7 +86,7 @@ class ThreePlugin : AbstractPlugin() { companion object : PluginFactory { override val tag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP) override val type = ThreePlugin::class - override fun invoke(meta: Meta,context: Context) = ThreePlugin() + override fun invoke(meta: Meta, context: Context) = ThreePlugin() } } diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeVisualObject.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeVisualObject.kt new file mode 100644 index 00000000..36095377 --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeVisualObject.kt @@ -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) : 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) + +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/three.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/three.kt index 730e7550..34e67764 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/three.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/three.kt @@ -5,12 +5,13 @@ import hep.dataforge.meta.float import hep.dataforge.meta.get import hep.dataforge.meta.node import hep.dataforge.vis.spatial.* -import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Face3 -import info.laht.threekt.core.Geometry -import info.laht.threekt.core.Object3D +import info.laht.threekt.core.* +import info.laht.threekt.external.controls.OrbitControls +import info.laht.threekt.materials.Material import info.laht.threekt.math.Euler import info.laht.threekt.math.Vector3 +import info.laht.threekt.objects.Mesh +import info.laht.threekt.textures.Texture /** * Utility methods for three.kt. @@ -64,4 +65,19 @@ fun CSG.toGeometry(): Geometry { geom.computeBoundingSphere() geom.computeBoundingBox() 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() + } } \ No newline at end of file diff --git a/spatial-js-demo/build.gradle.kts b/spatial-js-demo/build.gradle.kts index fa333f2d..e5021379 100644 --- a/spatial-js-demo/build.gradle.kts +++ b/spatial-js-demo/build.gradle.kts @@ -7,3 +7,13 @@ dependencies { api(project(":dataforge-vis-spatial")) testCompile(kotlin("test-js")) } + +kotlin{ + target { + browser{ + webpackTask { + sourceMaps = false + } + } + } +} \ No newline at end of file diff --git a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt b/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt index 41751d9d..01e6f690 100644 --- a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt +++ b/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt @@ -5,6 +5,8 @@ import hep.dataforge.js.Application import hep.dataforge.js.startApplication import hep.dataforge.vis.common.Colors 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.delay import kotlinx.coroutines.isActive @@ -14,7 +16,6 @@ import kotlin.math.cos import kotlin.math.sin import kotlin.random.Random - private class ThreeDemoApp : Application { override fun start(state: Map) { @@ -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) + } + } + } + } } diff --git a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt b/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt new file mode 100644 index 00000000..c1d8be33 --- /dev/null +++ b/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt @@ -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::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) + } +} \ No newline at end of file