Extruded shape demo
This commit is contained in:
parent
aa867af3c3
commit
397cc4b679
@ -9,6 +9,9 @@ import kotlinx.coroutines.GlobalScope
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
@ -68,6 +71,17 @@ class ThreeDemoApp : ApplicationBase() {
|
|||||||
color(12285)
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,9 +50,9 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
|||||||
gridRoot.append {
|
gridRoot.append {
|
||||||
span("border") {
|
span("border") {
|
||||||
div("col-4") {
|
div("col-4") {
|
||||||
h2 { +(meta["title"].string ?: name.toString()) }
|
output.attach(div { id = "output-$name" }){300}
|
||||||
hr()
|
hr()
|
||||||
output.attach(div { id = "output-$name" })
|
h2 { +(meta["title"].string ?: name.toString()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,123 @@
|
|||||||
package hep.dataforge.vis.spatial
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.toName
|
|
||||||
import hep.dataforge.vis.common.DisplayLeaf
|
import hep.dataforge.vis.common.DisplayLeaf
|
||||||
import hep.dataforge.vis.common.DisplayObject
|
import hep.dataforge.vis.common.DisplayObject
|
||||||
import hep.dataforge.vis.common.DisplayObjectList
|
import hep.dataforge.vis.common.DisplayObjectList
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
|
|
||||||
typealias Shape2D = List<Point2D>
|
typealias Shape2D = List<Point2D>
|
||||||
|
|
||||||
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<Point2D>()
|
||||||
|
|
||||||
class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) {
|
fun point(x: Number, y: Number) {
|
||||||
|
list.add(Point2D(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): Shape2D = list
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Layer> {
|
||||||
|
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
|
val shape
|
||||||
get() = shape(properties["shape"] ?: error("Shape not defined"))
|
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
|
val layers
|
||||||
get() = properties.getAll("layer").values.map {
|
get() = properties.getAll("layer").values.map {
|
||||||
layer(it.node ?: error("layer item is not a node"))
|
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 <T : Any> GeometryBuilder<T>.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<List<Point3D>> = 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<Point3D>
|
||||||
|
|
||||||
|
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 {
|
companion object {
|
||||||
const val TYPE = "geometry.3d.extruded"
|
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 DisplayObjectList.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
|
fun DisplayObjectList.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
|
||||||
Extruded(this, meta).apply(action).also { addChild(it) }
|
Extruded(this, meta).apply(action).also { addChild(it) }
|
@ -6,7 +6,12 @@ import hep.dataforge.meta.MetaRepr
|
|||||||
import hep.dataforge.meta.buildMeta
|
import hep.dataforge.meta.buildMeta
|
||||||
import hep.dataforge.vis.common.DisplayObject
|
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 {
|
data class Point3D(val x: Number, val y: Number, val z: Number) : MetaRepr {
|
||||||
override fun toMeta(): Meta = buildMeta {
|
override fun toMeta(): Meta = buildMeta {
|
||||||
|
Loading…
Reference in New Issue
Block a user