diff --git a/build.gradle.kts b/build.gradle.kts index 1fde55c2..ada9bd4c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -val dataforgeVersion by extra("0.1.3-dev-10") +val dataforgeVersion by extra("0.1.3") plugins{ val kotlinVersion = "1.3.50-eap-5" diff --git a/dataforge-vis-common/build.gradle.kts b/dataforge-vis-common/build.gradle.kts index e5c6d413..e57c78bb 100644 --- a/dataforge-vis-common/build.gradle.kts +++ b/dataforge-vis-common/build.gradle.kts @@ -18,6 +18,7 @@ kotlin { val jsMain by getting { dependencies { api("hep.dataforge:dataforge-output-html:$dataforgeVersion") + api(npm("text-encoding")) } } } diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt index 4adeeabc..144f26e6 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt @@ -1,17 +1,20 @@ package hep.dataforge.vis.common +import hep.dataforge.meta.Config import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaItem import hep.dataforge.names.Name -import hep.dataforge.names.get +import hep.dataforge.names.toName import hep.dataforge.provider.Provider import kotlinx.serialization.Transient import kotlin.collections.set open class VisualGroup : AbstractVisualObject(), Iterable, Provider { - protected val namedChildren = HashMap() - protected val unnamedChildren = ArrayList() + protected open val namedChildren: MutableMap = HashMap() + protected open val unnamedChildren: MutableList = ArrayList() + + override var properties: Config? = null override val defaultTarget: String get() = VisualObject.TYPE @@ -51,27 +54,25 @@ open class VisualGroup : AbstractVisualObject(), Iterable, listeners.removeAll { it.owner === owner } } - operator fun set(name: Name, child: T?) { - if (child == null) { - namedChildren.remove(name) - } else { - if (child.parent == null) { - child.parent = this - } else { - error("Can't reassign existing parent for $child") - } - namedChildren[name] = child - } - listeners.forEach { it.callback(name, child) } - } - /** * Add named or unnamed child to the group. If key is [null] the child is considered unnamed. Both key and value are not * allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed. */ operator fun set(name: Name?, child: T?) { when { - name != null -> set(name, child) + name != null -> { + if (child == null) { + namedChildren.remove(name) + } else { + if (child.parent == null) { + child.parent = this + } else { + error("Can't reassign existing parent for $child") + } + namedChildren[name] = child + } + listeners.forEach { it.callback(name, child) } + } child != null -> add(child) else -> error("Both key and child element are empty") } @@ -87,7 +88,7 @@ open class VisualGroup : AbstractVisualObject(), Iterable, /** * Get named child by string */ - operator fun get(key: String): T? = namedChildren.get(key) + operator fun get(key: String): T? = namedChildren[key.toName()] /** * Get an unnamed child diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualLeaf.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualLeaf.kt deleted file mode 100644 index 5f17dd33..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualLeaf.kt +++ /dev/null @@ -1,26 +0,0 @@ -package hep.dataforge.vis.common - -import hep.dataforge.meta.* -import hep.dataforge.names.Name - -/** - * Basic [VisualObject] leaf element - */ -open class VisualLeaf(meta: Meta = EmptyMeta) : AbstractVisualObject(), Configurable { - - val properties = Styled(meta) - - override val config: Config = properties.style - - override fun setProperty(name: Name, value: Any?) { - config[name] = value - } - - override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { - return if (inherit) { - properties[name] ?: parent?.getProperty(name, inherit) - } else { - properties[name] - } - } -} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt index b0e278d9..6f131fe2 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt @@ -1,12 +1,9 @@ package hep.dataforge.vis.common -import hep.dataforge.io.ConfigSerializer import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.provider.Type import hep.dataforge.vis.common.VisualObject.Companion.TYPE -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable import kotlinx.serialization.Transient private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) @@ -18,8 +15,6 @@ private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta) @Type(TYPE) interface VisualObject : MetaRepr, Configurable { - val type: String get() = this::class.simpleName ?: TYPE - /** * The parent object of this one. If null, this one is a root. */ @@ -64,7 +59,9 @@ internal data class MetaListener( val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit ) -abstract class AbstractVisualObject : VisualObject { +abstract class AbstractVisualObject: VisualObject { + + @Transient override var parent: VisualObject? = null @Transient @@ -84,12 +81,10 @@ abstract class AbstractVisualObject : VisualObject { listeners.removeAll { it.owner == owner } } - @Serializable(ConfigSerializer::class) - @SerialName("properties") - private var _config: Config? = null + abstract var properties: Config? override val config: Config - get() = _config ?: Config().also { config -> - _config = config + get() = properties ?: Config().also { config -> + properties = config config.onChange(this, ::propertyChanged) } @@ -99,19 +94,17 @@ abstract class AbstractVisualObject : VisualObject { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { return if (inherit) { - _config?.get(name) ?: parent?.getProperty(name, inherit) + properties?.get(name) ?: parent?.getProperty(name, inherit) } else { - _config?.get(name) + properties?.get(name) } } protected open fun MetaBuilder.updateMeta() {} override fun toMeta(): Meta = buildMeta { - "type" to type - "properties" to config + "type" to this::class.simpleName + "properties" to properties updateMeta() } -} - - +} \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCoil.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCoil.kt new file mode 100644 index 00000000..567f0e7c --- /dev/null +++ b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCoil.kt @@ -0,0 +1,26 @@ +package hep.dataforge.vis.spatial.gdml + +import hep.dataforge.vis.spatial.Visual3DPlugin +import hep.dataforge.vis.spatial.VisualGroup3D +import nl.adaptivity.xmlutil.StAXReader +import scientifik.gdml.GDML +import java.io.File + + +fun main() { + val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N_coil.gdml") + + val xmlReader = StAXReader(file.inputStream(), "UTF-8") + val xml = GDML.format.parse(GDML.serializer(), xmlReader) + val visual = xml.toVisual { + lUnit = LUnit.CM + } + + //val meta = visual.toMeta() + + val str = Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual) + + println(str) + + //println(Json.indented.stringify(meta.toJson())) +} \ No newline at end of file diff --git a/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis-spatial/build.gradle.kts index 3b1660bd..17715a94 100644 --- a/dataforge-vis-spatial/build.gradle.kts +++ b/dataforge-vis-spatial/build.gradle.kts @@ -29,7 +29,7 @@ kotlin { dependencies { api("info.laht.threekt:threejs-wrapper:0.106-npm-3") implementation(npm("three", "0.106.2")) - implementation(npm("@hi-level/three-csg")) + implementation(npm("@hi-level/three-csg", "1.0.6")) implementation(npm("style-loader")) implementation(npm("element-resize-event")) } 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 adc96842..7f362248 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 @@ -1,19 +1,29 @@ +@file:UseSerializers(Point3DSerializer::class) package hep.dataforge.vis.spatial import hep.dataforge.context.Context -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder -import hep.dataforge.meta.float -import hep.dataforge.meta.get +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.* +import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.VisualFactory import hep.dataforge.vis.common.VisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers import kotlin.reflect.KClass +@Serializable data class Box( val xSize: Float, val ySize: Float, val zSize: Float -) : VisualLeaf3D(), Shape { +) : AbstractVisualObject(), VisualObject3D, Shape { + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null + + @Serializable(ConfigSerializer::class) + override var properties: Config? = null //TODO add helper for color configuration override fun toGeometry(geometryBuilder: GeometryBuilder) { @@ -40,8 +50,13 @@ data class Box( "xSize" to xSize "ySize" to ySize "zSize" to ySize + updatePosition() } +// override fun toMeta(): Meta { +// return (Visual3DPlugin.json.toJson(Box.serializer(), this) as JsonObject).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 079ed5c9..8e6970ad 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 @@ -1,7 +1,13 @@ +@file:UseSerializers(Point3DSerializer::class) package hep.dataforge.vis.spatial -import hep.dataforge.meta.isEmpty +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.Config +import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.update +import hep.dataforge.vis.common.AbstractVisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers enum class CompositeType { UNION, @@ -9,11 +15,27 @@ enum class CompositeType { SUBTRACT } -open class Composite( +@Serializable +class Composite( + val compositeType: CompositeType, val first: VisualObject3D, - val second: VisualObject3D, - val compositeType: CompositeType = CompositeType.UNION -) : VisualLeaf3D() + val second: VisualObject3D +) : AbstractVisualObject(), VisualObject3D { + + 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 MetaBuilder.updateMeta() { + "compositeType" to compositeType + "first" to first.toMeta() + "second" to second.toMeta() + updatePosition() + } +} inline fun VisualGroup3D.composite( type: CompositeType, @@ -23,14 +45,14 @@ inline fun VisualGroup3D.composite( val group = VisualGroup3D().apply(builder) val children = group.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") - return Composite(children[0], children[1], type).also { - if (!group.config.isEmpty()) { + return Composite(type, children[0], children[1]).also { + if (group.properties != null) { it.config.update(group.config) + it.material = group.material } it.position = group.position it.rotation = group.rotation it.scale = group.scale - it.material = group.material set(name, it) } } 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 new file mode 100644 index 00000000..fdecbf3c --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt @@ -0,0 +1,40 @@ +@file:UseSerializers(Point3DSerializer::class) + +package hep.dataforge.vis.spatial + +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.Config +import hep.dataforge.vis.common.AbstractVisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers + +/** + * A cylinder or cut cone segment + */ +@Serializable +class ConeSegment( + var radius: Float, + var height: Float, + var upperRadius: Float, + var startAngle: Float = 0f, + var angle: Float = PI2 +) : AbstractVisualObject(), VisualObject3D { + + @Serializable(ConfigSerializer::class) + override var properties: Config? = null + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null +} + +inline fun VisualGroup3D.cylinder( + r: Number, + height: Number, + name: String? = null, + block: ConeSegment.() -> Unit = {} +): ConeSegment = ConeSegment( + r.toFloat(), + height.toFloat(), + r.toFloat() +).apply(block).also { set(name, it) } \ No newline at end of file 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 19d5a23b..1bfa5e7b 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 @@ -1,15 +1,29 @@ +@file:UseSerializers(Point3DSerializer::class) + package hep.dataforge.vis.spatial +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.Config import hep.dataforge.meta.MetaBuilder +import hep.dataforge.vis.common.AbstractVisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers -class Convex( - val points: List -) : VisualLeaf3D() { +@Serializable +class Convex(val points: List) : AbstractVisualObject(), VisualObject3D { + + @Serializable(ConfigSerializer::class) + override var properties: Config? = null + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null override fun MetaBuilder.updateMeta() { "points" to { - "point" to points.map{it.toMeta()} + "point" to points.map { it.toMeta() } } + updatePosition() } companion object { diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Cylinder.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Cylinder.kt deleted file mode 100644 index f76e9d43..00000000 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Cylinder.kt +++ /dev/null @@ -1,22 +0,0 @@ -package hep.dataforge.vis.spatial - -/** - * A cylinder or cut cone segment - */ -class Cylinder( - var radius: Float, - var height: Float, - var upperRadius: Float = radius, - var startAngle: Float = 0f, - var angle: Float = PI2 -) : VisualLeaf3D() - -inline fun VisualGroup3D.cylinder( - r: Number, - height: Number, - name: String? = null, - block: Cylinder.() -> Unit = {} -): Cylinder = Cylinder( - r.toFloat(), - height.toFloat() -).apply(block).also { set(name, it) } \ No newline at end of file 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 0c6d887f..5a464521 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 @@ -1,5 +1,11 @@ +@file:UseSerializers(Point2DSerializer::class, Point3DSerializer::class) package hep.dataforge.vis.spatial +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.Config +import hep.dataforge.vis.common.AbstractVisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin @@ -7,16 +13,16 @@ import kotlin.math.sin typealias Shape2D = List -class Shape2DBuilder { - private val list = ArrayList() +@Serializable +class Shape2DBuilder(private val points: MutableList = ArrayList()) { fun point(x: Number, y: Number) { - list.add(Point2D(x, y)) + points.add(Point2D(x, y)) } infix fun Number.to(y: Number) = point(this, y) - fun build(): Shape2D = list + fun build(): Shape2D = points } fun Shape2DBuilder.polygon(vertices: Int, radius: Number) { @@ -27,19 +33,27 @@ fun Shape2DBuilder.polygon(vertices: Int, radius: Number) { } } +@Serializable data class Layer(var x: Float, var y: Float, var z: Float, var scale: Float) -class Extruded : VisualLeaf3D(), Shape { +@Serializable +class Extruded( + var shape: List = ArrayList(), + var layers: MutableList = ArrayList() +) : AbstractVisualObject(), VisualObject3D, Shape { - var shape: List = ArrayList() + @Serializable(ConfigSerializer::class) + override var properties: Config? = null + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null fun shape(block: Shape2DBuilder.() -> Unit) { this.shape = Shape2DBuilder().apply(block).build() //TODO send invalidation signal } - val layers: MutableList = ArrayList() - fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) { layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat())) //TODO send invalidation signal 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 fb4bf8e5..15ea2f28 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 @@ -1,25 +1,37 @@ +@file:UseSerializers(Point3DSerializer::class, NameSerializer::class) + package hep.dataforge.vis.spatial +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.io.NameSerializer +import hep.dataforge.meta.Config import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaItem import hep.dataforge.names.Name import hep.dataforge.vis.common.AbstractVisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers /** * A proxy [VisualObject3D] to reuse a template object */ +@Serializable class Proxy(val templateName: Name) : AbstractVisualObject(), VisualObject3D { + override var position: Point3D? = null override var rotation: Point3D? = null override var scale: Point3D? = null + @Serializable(ConfigSerializer::class) + override var properties: Config? = null + /** * Recursively search for defined template in the parent */ - val template by lazy { - (parent as? VisualGroup3D)?.getTemplate(templateName) + val template: VisualObject3D + get() = (parent as? VisualGroup3D)?.getTemplate(templateName) ?: error("Template with name $templateName not found in $parent") - } + override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { return if (inherit) { 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 f633b002..28f83960 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 @@ -1,14 +1,30 @@ +@file:UseSerializers(Point3DSerializer::class) + package hep.dataforge.vis.spatial +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.Config +import hep.dataforge.vis.common.AbstractVisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers import kotlin.math.PI +@Serializable class Sphere( var radius: Float, var phiStart: Float = 0f, var phi: Float = PI2, var thetaStart: Float = 0f, var theta: Float = PI.toFloat() -) : VisualLeaf3D() +) : AbstractVisualObject(), VisualObject3D { + + @Serializable(ConfigSerializer::class) + override var properties: Config? = null + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null +} inline fun VisualGroup3D.sphere( radius: Number, 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 bf64ffc9..d9c96864 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 @@ -1,5 +1,11 @@ +@file:UseSerializers(Point3DSerializer::class) package hep.dataforge.vis.spatial +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.Config +import hep.dataforge.vis.common.AbstractVisualObject +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin @@ -7,13 +13,21 @@ import kotlin.math.sin /** * Straight tube segment */ +@Serializable class Tube( var radius: Float, var height: Float, var innerRadius: Float = 0f, var startAngle: Float = 0f, var angle: Float = PI2 -) : VisualLeaf3D(), Shape { +) : AbstractVisualObject(), VisualObject3D, Shape { + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null + + @Serializable(ConfigSerializer::class) + override var properties: Config? = null init { require(radius > 0) @@ -37,8 +51,8 @@ class Tube( geometryBuilder.apply { //creating shape in x-y plane with z = 0 - val bottomOuterPoints = shape(radius, 0f) - val upperOuterPoints = shape(radius, height) + val bottomOuterPoints = shape(radius, -height/2) + val upperOuterPoints = shape(radius, height/2) //outer face (1 until segments).forEach { face4(bottomOuterPoints[it - 1], bottomOuterPoints[it], upperOuterPoints[it], upperOuterPoints[it - 1]) @@ -62,8 +76,8 @@ class Tube( face4(zeroTop, zeroBottom, bottomOuterPoints.last(), upperOuterPoints.last()) } } else { - val bottomInnerPoints = shape(innerRadius, 0f) - val upperInnerPoints = shape(innerRadius, height) + val bottomInnerPoints = shape(innerRadius, -height/2) + val upperInnerPoints = shape(innerRadius, height/2) //outer face (1 until segments).forEach { // inner surface @@ -97,9 +111,14 @@ class Tube( bottomOuterPoints.last() ) face4(upperInnerPoints[0], upperInnerPoints.last(), upperOuterPoints.last(), upperOuterPoints[0]) - } else{ - face4(bottomInnerPoints[0],bottomOuterPoints[0],upperOuterPoints[0],upperInnerPoints[0]) - face4(bottomOuterPoints.last(),bottomInnerPoints.last(),upperInnerPoints.last(),upperOuterPoints.last()) + } else { + face4(bottomInnerPoints[0], bottomOuterPoints[0], upperOuterPoints[0], upperInnerPoints[0]) + face4( + bottomOuterPoints.last(), + bottomInnerPoints.last(), + upperInnerPoints.last(), + upperOuterPoints.last() + ) } } } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3DPlugin.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3DPlugin.kt index 77177070..2c9a0595 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3DPlugin.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3DPlugin.kt @@ -6,6 +6,9 @@ import hep.dataforge.context.PluginTag import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.vis.common.VisualPlugin +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import kotlinx.serialization.modules.SerializersModule import kotlin.reflect.KClass class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) { @@ -23,6 +26,26 @@ class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) { override val tag: PluginTag = PluginTag(name = "visual.spatial", group = PluginTag.DATAFORGE_GROUP) override val type: KClass = Visual3DPlugin::class override fun invoke(meta: Meta): Visual3DPlugin = Visual3DPlugin(meta) + + val serialModule = SerializersModule { + polymorphic(VisualObject3D::class) { + VisualGroup3D::class with VisualGroup3D.serializer() + Proxy::class with Proxy.serializer() + Composite::class with Composite.serializer() + Tube::class with Tube.serializer() + Box::class with Box.serializer() + Convex::class with Convex.serializer() + } + } + + val json = Json( + JsonConfiguration( + prettyPrint = true, + useArrayPolymorphism = false, + encodeDefaults = false + ), + context = serialModule + ) } } 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 65d3e47c..fae9605c 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 @@ -1,16 +1,21 @@ +@file:UseSerializers(Point3DSerializer::class, NameSerializer::class) + package hep.dataforge.vis.spatial +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.io.NameSerializer import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.plus import hep.dataforge.output.Output -import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.asName import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers interface VisualObject3D : VisualObject { var position: Point3D? @@ -60,18 +65,8 @@ interface VisualObject3D : VisualObject { } } -abstract class VisualLeaf3D : AbstractVisualObject(), VisualObject3D, Configurable { - override var position: Point3D? = null - override var rotation: Point3D? = null - override var scale: Point3D? = null -} - +@Serializable class VisualGroup3D : VisualGroup(), VisualObject3D, Configurable { - - override var position: Point3D? = null - override var rotation: Point3D? = null - override var scale: Point3D? = null - /** * A container for templates visible inside this group */ @@ -81,12 +76,27 @@ class VisualGroup3D : VisualGroup(), VisualObject3D, Configurabl field = value } + @Serializable(ConfigSerializer::class) + override var properties: Config? = null + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null + + override val namedChildren: MutableMap = HashMap() + override val unnamedChildren: MutableList = ArrayList() + fun getTemplate(name: Name): VisualObject3D? = templates?.get(name) ?: (parent as? VisualGroup3D)?.getTemplate(name) override fun MetaBuilder.updateMeta() { + set(TEMPLATES_KEY, templates?.toMeta()) updatePosition() updateChildren() } + + companion object { + const val TEMPLATES_KEY = "templates" + } } fun VisualGroup3D.group(key: String? = null, action: VisualGroup3D.() -> Unit = {}): VisualGroup3D = @@ -213,5 +223,4 @@ var VisualObject3D.scaleZ: Number set(value) { scale().z = value.toDouble() propertyChanged(VisualObject3D.zScale) - } - + } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serilization.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serilization.kt new file mode 100644 index 00000000..e353d655 --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serilization.kt @@ -0,0 +1,41 @@ +package hep.dataforge.vis.spatial + +import kotlinx.serialization.* + +@Serializable +private data class Point2DSerial(val x: Double, val y: Double) + +@Serializable +private data class Point3DSerial(val x: Double, val y: Double, val z: Double) + +@Serializer(Point3D::class) +object Point3DSerializer : KSerializer { + private val serializer = Point3DSerial.serializer() + override val descriptor: SerialDescriptor get() = serializer.descriptor + + override fun deserialize(decoder: Decoder): Point3D { + return serializer.deserialize(decoder).let { + Point3D(it.x, it.y, it.z) + } + } + + override fun serialize(encoder: Encoder, obj: Point3D) { + serializer.serialize(encoder, Point3DSerial(obj.x, obj.y, obj.z) ) + } +} + +@Serializer(Point2D::class) +object Point2DSerializer : KSerializer { + private val serializer = Point2DSerial.serializer() + override val descriptor: SerialDescriptor get() = serializer.descriptor + + override fun deserialize(decoder: Decoder): Point2D { + return serializer.deserialize(decoder).let { + Point2D(it.x, it.y) + } + } + + override fun serialize(encoder: Encoder, obj: Point2D) { + serializer.serialize(encoder, Point2DSerial(obj.x, obj.y)) + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt index 810e4772..6d1c5152 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt @@ -25,6 +25,8 @@ class ConvexTest { val convex = group.first() as Convex + val meta = convex.toMeta() + val pointsNode = convex.toMeta()["points"].node assertEquals(8, pointsNode?.items?.count()) diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt index e36c6d42..87668852 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt @@ -1,13 +1,15 @@ package hep.dataforge.vis.spatial import hep.dataforge.context.Global +import kotlinx.serialization.ImplicitReflectionSerializer import kotlin.test.Test import kotlin.test.assertEquals class SerializationTest { + @ImplicitReflectionSerializer @Test fun testCubeSerialization(){ - val cube = Box(null,100f,100f,100f).apply{ + val cube = Box(100f,100f,100f).apply{ color(222) } val meta = cube.toMeta() diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCompositeFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCompositeFactory.kt index 51089d35..b6ee8852 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCompositeFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCompositeFactory.kt @@ -3,7 +3,6 @@ package hep.dataforge.vis.spatial.three import hep.dataforge.vis.spatial.Composite import hep.dataforge.vis.spatial.CompositeType import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry import info.laht.threekt.objects.Mesh /** @@ -23,9 +22,7 @@ class ThreeCompositeFactory(val three: ThreePlugin) : MeshThreeFactory firstCSG.intersect(secondCSG) CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG) } - - val mesh = CSG.toMesh(resultCSG, second.matrix) - return (mesh.geometry as Geometry).toBufferGeometry() + return resultCSG.toGeometry().toBufferGeometry() } } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt index 78765e08..9b9e17e9 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt @@ -1,14 +1,14 @@ package hep.dataforge.vis.spatial.three -import hep.dataforge.vis.spatial.Cylinder +import hep.dataforge.vis.spatial.ConeSegment import hep.dataforge.vis.spatial.detail import info.laht.threekt.core.BufferGeometry import info.laht.threekt.geometries.CylinderBufferGeometry import kotlin.math.PI import kotlin.math.pow -object ThreeCylinderFactory : MeshThreeFactory(Cylinder::class) { - override fun buildGeometry(obj: Cylinder): BufferGeometry { +object ThreeCylinderFactory : MeshThreeFactory(ConeSegment::class) { + override fun buildGeometry(obj: ConeSegment): BufferGeometry { val cylinder = obj.detail?.let { val segments = it.toDouble().pow(0.5).toInt() CylinderBufferGeometry( 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 1b93b1bc..ce3dec9d 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 @@ -37,6 +37,12 @@ interface ThreeFactory { */ internal fun Object3D.updatePosition(obj: VisualObject3D) { visible = obj.visible ?: true +// Matrix4().apply { +// makeRotationFromEuler(obj.euler) +// applyMatrix(this) +// makeTranslation(obj.x.toDouble(), obj.y.toDouble(), obj.z.toDouble()) +// applyMatrix(this) +// } position.set(obj.x, obj.y, obj.z) setRotationFromEuler(obj.euler) scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) 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 16bffea0..50de48f4 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 @@ -27,7 +27,7 @@ class ThreePlugin : AbstractPlugin() { objectFactories[Box::class] = ThreeBoxFactory objectFactories[Convex::class] = ThreeConvexFactory objectFactories[Sphere::class] = ThreeSphereFactory - objectFactories[Cylinder::class] = ThreeCylinderFactory + objectFactories[ConeSegment::class] = ThreeCylinderFactory } private fun findObjectFactory(type: KClass): ThreeFactory<*>? { diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/csg.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/csg.kt index 1e91e3f0..fa9a1c5b 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/csg.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/csg.kt @@ -13,10 +13,11 @@ package hep.dataforge.vis.spatial.three import info.laht.threekt.math.Matrix4 +import info.laht.threekt.math.Vector3 import info.laht.threekt.objects.Mesh open external class CSG { - open var polygons: Any + open var polygons: Array open fun clone(): CSG open fun toPolygons(): Array open fun union(csg: CSG): CSG @@ -40,8 +41,7 @@ open external class CSG { } } -open external class Vector(x: Number, y: Number, z: Number) { - open fun clone(): Any +open external class Vector(x: Number, y: Number, z: Number): Vector3 { open fun negated(): Vector open fun plus(a: Vector): Vector open fun minus(a: Vector): Vector 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 20639b70..730e7550 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 @@ -6,6 +6,7 @@ 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.math.Euler @@ -25,4 +26,42 @@ val VisualObject3D.euler get() = Euler(rotationX, rotationY, rotationZ, rotation val MetaItem<*>.vector get() = Vector3(node["x"].float ?: 0f, node["y"].float ?: 0f, node["z"].float ?: 0f) -fun Geometry.toBufferGeometry(): BufferGeometry = BufferGeometry().apply { fromGeometry(this@toBufferGeometry) } \ No newline at end of file +fun Geometry.toBufferGeometry(): BufferGeometry = BufferGeometry().apply { fromGeometry(this@toBufferGeometry) } + +fun CSG.toGeometry(): Geometry { + val geom = Geometry() + + val vertices = ArrayList() + val faces = ArrayList() + + for (polygon in polygons) { + val v0 = vertices.size + val pvs = polygon.vertices + + for (pv in pvs) { + vertices.add(Vector3().copy(pv.pos)) + } + + for (j in 3..polygon.vertices.size) { + val fc = Face3(v0, v0 + j - 2, v0 + j - 1, zero) + fc.vertexNormals = arrayOf( + Vector3().copy(pvs[0].normal), + Vector3().copy(pvs[j - 2].normal), + Vector3().copy(pvs[j - 1].normal) + ) + + fc.normal = Vector3().copy(polygon.plane.normal) + faces.add(fc) + } + } + geom.vertices = vertices.toTypedArray() + geom.faces = faces.toTypedArray() +// val inv: Matrix4 = Matrix4().apply { getInverse(toMatrix) } +// geom.applyMatrix(toMatrix) + geom.verticesNeedUpdate = true + geom.elementsNeedUpdate = true + geom.normalsNeedUpdate = true + geom.computeBoundingSphere() + geom.computeBoundingBox() + return geom +} \ No newline at end of file diff --git a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt b/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt index 044eab05..17c8e330 100644 --- a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt +++ b/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt @@ -45,6 +45,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { element.append(gridRoot) } + @Suppress("UNCHECKED_CAST") override fun get(type: KClass, name: Name, stage: Name, meta: Meta): Output { val three = context.plugins.get()!!