From 397cc4b6796ccefe18b172e900f11e877730444d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Jul 2019 14:50:02 +0300 Subject: [PATCH] Extruded shape demo --- .../vis/spatial/demo/ThreeDemoApp.kt | 16 ++- .../vis/spatial/demo/ThreeDemoGrid.kt | 4 +- .../hep/dataforge/vis/spatial/Extruded.kt | 130 ++++++++++++++---- .../dataforge/vis/spatial/GeometryBuilder.kt | 7 +- 4 files changed, 127 insertions(+), 30 deletions(-) diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt index 9a0c9253..4abf0938 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt @@ -9,6 +9,9 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.sin import kotlin.random.Random @@ -61,13 +64,24 @@ class ThreeDemoApp : ApplicationBase() { } } - demo("jsroot", "JSROOT cube"){ + demo("jsroot", "JSROOT cube") { jsRootGeometry { y = 110.0 shape = box(50, 50, 50) color(12285) } } + + demo("extrude", "extruded shape") { + extrude { + shape { + polygon(8, 50) + } + for(i in 0..100) { + layer(i*5, 20*sin(2*PI/100*i), 20*cos(2*PI/100*i)) + } + } + } } diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt index fd962164..771cb0ab 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt @@ -50,9 +50,9 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { gridRoot.append { span("border") { div("col-4") { - h2 { +(meta["title"].string ?: name.toString()) } + output.attach(div { id = "output-$name" }){300} hr() - output.attach(div { id = "output-$name" }) + h2 { +(meta["title"].string ?: name.toString()) } } } } 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 79c00cb9..78d7569b 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,45 +1,123 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.* -import hep.dataforge.names.toName import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObjectList +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.sin typealias Shape2D = List -data class Layer(val z: Number, val x: Number = 0.0, val y: Number = 0.0, val scale: Number = 1.0) +class Shape2DBuilder { + private val list = ArrayList() -class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) { + fun point(x: Number, y: Number) { + list.add(Point2D(x, y)) + } - val shape - get() = shape(properties["shape"] ?: error("Shape not defined")) + fun build(): Shape2D = list +} - val layers - get() = properties.getAll("layer").values.map { - layer(it.node ?: error("layer item is not a node")) - } - - companion object { - const val TYPE = "geometry.3d.extruded" - - private fun shape(item: MetaItem<*>): Shape2D { - return item.node?.getAll("xyPoint".toName())?.map { (_, value) -> - Point2D(value.node["x"].number ?: 0, value.node["y"].number ?: 0) - } ?: emptyList() - } - - private fun layer(meta: Meta): Layer { - val x by meta.number(0.0) - val y by meta.number(0.0) - val z by meta.number { error("z is undefined in layer") } - val scale by meta.number(1.0) - return Layer(z, x, y, scale) - } +fun Shape2DBuilder.polygon(vertices: Int, radius: Number) { + require(vertices > 2) { "Polygon must have more than 2 vertices" } + val angle = 2 * PI / vertices + for (i in 0 until vertices) { + point(radius.toDouble() * cos(angle * i), radius.toDouble() * sin(angle * i)) } } +class Layer(override val config: Config) : Specific { + var z by number(0.0) + var x by number(0.0) + var y by number(0.0) + var scale by number(1.0) + + companion object : Specification { + override fun wrap(config: Config): Layer = Layer(config) + } +} + +//class Layer(val z: Number, val x: Number = 0.0, val y: Number = 0.0, val scale: Number = 1.0) + +class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape { + + val shape + get() = properties.getAll("shape.point").map { (_, value) -> + Point2D(value.node["x"].number ?: 0, value.node["y"].number ?: 0) + } + + fun shape(block: Shape2DBuilder.() -> Unit) { + val points = Shape2DBuilder().apply(block).build().map { it.toMeta() } + properties["shape.point"] = points + } + + val layers + get() = properties.getAll("layer").values.map { + Layer.wrap(it.node ?: error("layer item is not a node")) + } + + fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0): Layer { + val layer = Layer.build { + this.x = x + this.y = y + this.z = z + this.scale = scale + } + properties.append("layer", layer) + return layer + } + + override fun GeometryBuilder.buildGeometry() { + val shape: Shape2D = shape + + if (shape.size < 3) error("Extruded shape requires more than points per layer") + + /** + * Expand the shape for specific layers + */ + val layers: List> = layers.map { layer -> + shape.map { (x, y) -> + val newX = layer.x.toDouble() + x.toDouble() * layer.scale.toDouble() + val newY = layer.y.toDouble() + y.toDouble() * layer.scale.toDouble() + Point3D(newX, newY, layer.z) + } + } + + if (layers.size < 2) error("Extruded shape requires more than one layer") + + var lowerLayer = layers.first() + var upperLayer: List + + for (i in (1 until layers.size)) { + upperLayer = layers[i] + for (j in (0 until shape.size - 1)) { + //counter clockwise + face4( + lowerLayer[j], + lowerLayer[j + 1], + upperLayer[j + 1], + upperLayer[j] + ) + } + + // final face + face4( + lowerLayer[shape.size - 1], + lowerLayer[0], + upperLayer[0], + upperLayer[shape.size - 1] + ) + lowerLayer = upperLayer + } + } + + companion object { + const val TYPE = "geometry.3d.extruded" + } +} fun DisplayObjectList.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) = Extruded(this, meta).apply(action).also { addChild(it) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt index f3c0fa9a..36a2ddc6 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt @@ -6,7 +6,12 @@ import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.buildMeta import hep.dataforge.vis.common.DisplayObject -data class Point2D(val x: Number, val y: Number) +data class Point2D(val x: Number, val y: Number): MetaRepr{ + override fun toMeta(): Meta = buildMeta { + "x" to x + "y" to y + } +} data class Point3D(val x: Number, val y: Number, val z: Number) : MetaRepr { override fun toMeta(): Meta = buildMeta {