FX backend in progress
This commit is contained in:
parent
c964f80d73
commit
cef1a1ee6d
@ -32,8 +32,7 @@ kotlin {
|
||||
jsMain{
|
||||
dependencies {
|
||||
api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
|
||||
api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
|
||||
api(npm("bootstrap","4.3.1"))
|
||||
api(npm("bootstrap","4.4.1"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Configurable
|
||||
import hep.dataforge.meta.Laminate
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.toName
|
||||
@ -15,7 +18,7 @@ import kotlinx.serialization.Transient
|
||||
* A root type for display hierarchy
|
||||
*/
|
||||
@Type(TYPE)
|
||||
interface VisualObject : MetaRepr, Configurable {
|
||||
interface VisualObject : Configurable {
|
||||
|
||||
/**
|
||||
* The parent object of this one. If null, this one is a root.
|
||||
@ -54,7 +57,7 @@ interface VisualObject : MetaRepr, Configurable {
|
||||
fun removeChangeListener(owner: Any?)
|
||||
|
||||
/**
|
||||
* List of names of styles applied to this object
|
||||
* List of names of styles applied to this object. Order matters.
|
||||
*/
|
||||
var styles: List<Name>
|
||||
|
||||
|
@ -21,7 +21,8 @@ kotlin {
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation("org.fxyz3d:fxyz3d:0.5.2")
|
||||
api("org.fxyz3d:fxyz3d:0.5.2")
|
||||
implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7")
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
|
@ -3,7 +3,6 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.io.toMeta
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.float
|
||||
@ -50,8 +49,6 @@ class Box(
|
||||
geometryBuilder.face4(node8, node5, node6, node7)
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
|
||||
companion object : VisualFactory<Box> {
|
||||
const val TYPE = "geometry.3d.box"
|
||||
|
||||
|
@ -2,9 +2,7 @@
|
||||
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.meta.update
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import kotlinx.serialization.Serializable
|
||||
@ -34,8 +32,6 @@ class Composite(
|
||||
|
||||
@Serializable(ConfigSerializer::class)
|
||||
override var properties: Config? = null
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
}
|
||||
|
||||
inline fun VisualGroup3D.composite(
|
||||
|
@ -3,12 +3,12 @@
|
||||
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
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* A cylinder or cut cone segment
|
||||
@ -20,7 +20,7 @@ class ConeSegment(
|
||||
var upperRadius: Float,
|
||||
var startAngle: Float = 0f,
|
||||
var angle: Float = PI2
|
||||
) : AbstractVisualObject(), VisualObject3D {
|
||||
) : AbstractVisualObject(), VisualObject3D, Shape {
|
||||
|
||||
@Serializable(ConfigSerializer::class)
|
||||
override var properties: Config? = null
|
||||
@ -29,7 +29,48 @@ class ConeSegment(
|
||||
override var rotation: Point3D? = null
|
||||
override var scale: Point3D? = null
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
val segments = detail ?: 8
|
||||
require(segments >= 4) { "The number of segments in cone segment is too small" }
|
||||
val angleStep = angle / (segments - 1)
|
||||
|
||||
fun shape(r: Float, z: Float): List<Point3D> {
|
||||
return (0 until segments).map { i ->
|
||||
Point3D(r * cos(startAngle + angleStep * i), r * sin(startAngle + angleStep * i), z)
|
||||
}
|
||||
}
|
||||
|
||||
geometryBuilder.apply {
|
||||
|
||||
//creating shape in x-y plane with z = 0
|
||||
val bottomOuterPoints = shape(upperRadius, -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])
|
||||
}
|
||||
|
||||
if (angle == PI2) {
|
||||
face4(bottomOuterPoints.last(), bottomOuterPoints[0], upperOuterPoints[0], upperOuterPoints.last())
|
||||
}
|
||||
|
||||
val zeroBottom = Point3D(0f, 0f, 0f)
|
||||
val zeroTop = Point3D(0f, 0f, height)
|
||||
(1 until segments).forEach {
|
||||
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it])
|
||||
face(upperOuterPoints[it - 1], upperOuterPoints[it], zeroTop)
|
||||
}
|
||||
if (angle == PI2) {
|
||||
face(bottomOuterPoints.last(), zeroBottom, bottomOuterPoints[0])
|
||||
face(upperOuterPoints.last(), upperOuterPoints[0], zeroTop)
|
||||
} else {
|
||||
face4(zeroTop, zeroBottom, bottomOuterPoints[0], upperOuterPoints[0])
|
||||
face4(zeroTop, zeroBottom, bottomOuterPoints.last(), upperOuterPoints.last())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline fun VisualGroup3D.cylinder(
|
||||
|
@ -3,9 +3,7 @@
|
||||
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
|
||||
@ -20,8 +18,6 @@ class Convex(val points: List<Point3D>) : AbstractVisualObject(), VisualObject3D
|
||||
override var rotation: Point3D? = null
|
||||
override var scale: Point3D? = null
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
|
||||
companion object {
|
||||
const val TYPE = "geometry.3d.convex"
|
||||
}
|
||||
|
@ -2,9 +2,7 @@
|
||||
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
|
||||
@ -107,8 +105,6 @@ class Extruded(
|
||||
geometryBuilder.cap(layers.last())
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
|
||||
companion object {
|
||||
const val TYPE = "geometry.3d.extruded"
|
||||
}
|
||||
|
@ -3,9 +3,7 @@
|
||||
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
|
||||
@ -23,7 +21,6 @@ class PolyLine(var points: List<Point3D>) : 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 = {}) =
|
||||
|
@ -4,7 +4,6 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
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.MetaItem
|
||||
@ -64,8 +63,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
||||
}
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
|
||||
override val children: Map<NameToken, ProxyChild>
|
||||
get() = (prototype as? MutableVisualGroup)?.children
|
||||
?.filter { !it.key.toString().startsWith("@") }
|
||||
@ -144,8 +141,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
||||
}
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -3,9 +3,7 @@
|
||||
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
|
||||
@ -18,7 +16,7 @@ class Sphere(
|
||||
var phi: Float = PI2,
|
||||
var thetaStart: Float = 0f,
|
||||
var theta: Float = PI.toFloat()
|
||||
) : AbstractVisualObject(), VisualObject3D {
|
||||
) : AbstractVisualObject(), VisualObject3D, Shape {
|
||||
|
||||
@Serializable(ConfigSerializer::class)
|
||||
override var properties: Config? = null
|
||||
@ -27,7 +25,9 @@ class Sphere(
|
||||
override var rotation: Point3D? = null
|
||||
override var scale: Point3D? = null
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
inline fun VisualGroup3D.sphere(
|
||||
|
@ -0,0 +1,23 @@
|
||||
@file:UseSerializers(Point3DSerializer::class)
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
|
||||
@Serializable
|
||||
class Text3D(var text: String, var fontSize: Int) : 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
|
||||
|
||||
}
|
||||
|
||||
fun VisualGroup3D.text(text: String, fontSize: Int, name: String = "", action: Text3D.() -> Unit = {}) =
|
||||
Text3D(text, fontSize).apply(action).also { set(name, it) }
|
@ -2,9 +2,7 @@
|
||||
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
|
||||
@ -126,7 +124,6 @@ class Tube(
|
||||
}
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
}
|
||||
|
||||
inline fun VisualGroup3D.tube(
|
||||
|
@ -11,7 +11,6 @@ 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.names.Name
|
||||
@ -97,8 +96,6 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
|
||||
}
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
|
||||
companion object {
|
||||
const val PROTOTYPES_KEY = "templates"
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.io.toMeta
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.getIndexed
|
||||
import hep.dataforge.meta.node
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -25,12 +25,11 @@ class ConvexTest {
|
||||
|
||||
val convex = group.first() as Convex
|
||||
|
||||
val meta = convex.toMeta()
|
||||
val json = Visual3DPlugin.json.toJson(Convex.serializer(), convex)
|
||||
val meta = json.toMeta()
|
||||
|
||||
val pointsNode = convex.toMeta()["points"].node
|
||||
|
||||
assertEquals(8, pointsNode?.items?.count())
|
||||
val points = pointsNode?.getIndexed("points")
|
||||
val points = meta.getIndexed("points").values.map { (it as MetaItem.NodeItem<*>).node.point3D()}
|
||||
assertEquals(8, points.count())
|
||||
|
||||
assertEquals(8, convex.points.size)
|
||||
}
|
||||
|
@ -8,15 +8,15 @@ import kotlin.test.assertEquals
|
||||
class SerializationTest {
|
||||
@ImplicitReflectionSerializer
|
||||
@Test
|
||||
fun testCubeSerialization(){
|
||||
val cube = Box(100f,100f,100f).apply{
|
||||
fun testCubeSerialization() {
|
||||
val cube = Box(100f, 100f, 100f).apply {
|
||||
color(222)
|
||||
x = 100
|
||||
z = -100
|
||||
}
|
||||
val string = json.stringify(Box.serializer(),cube)
|
||||
val string = json.stringify(Box.serializer(), cube)
|
||||
println(string)
|
||||
val newCube = json.parse(Box.serializer(),string)
|
||||
assertEquals(cube.toMeta(),newCube.toMeta())
|
||||
val newCube = json.parse(Box.serializer(), string)
|
||||
assertEquals(cube.config, newCube.config)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package hep.dataforge.vis.spatial.editor
|
||||
|
||||
import hep.dataforge.vis.spatial.three.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.three.ThreeCanvas
|
||||
import kotlinx.html.InputType
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.js.div
|
||||
@ -9,7 +9,7 @@ import kotlinx.html.js.label
|
||||
import org.w3c.dom.Element
|
||||
import kotlin.dom.clear
|
||||
|
||||
fun Element.threeOutputConfig(output: ThreeOutput) {
|
||||
fun Element.threeOutputConfig(canvas: ThreeCanvas) {
|
||||
clear()
|
||||
append {
|
||||
card("Layers"){
|
||||
@ -23,9 +23,9 @@ fun Element.threeOutputConfig(output: ThreeOutput) {
|
||||
}
|
||||
onchange = {
|
||||
if (checked) {
|
||||
output.camera.layers.enable(layer)
|
||||
canvas.camera.layers.enable(layer)
|
||||
} else {
|
||||
output.camera.layers.disable(layer)
|
||||
canvas.camera.layers.disable(layer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import kotlin.browser.window
|
||||
import kotlin.dom.clear
|
||||
import kotlin.math.max
|
||||
|
||||
class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Renderer<VisualObject3D> {
|
||||
class ThreeCanvas(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Renderer<VisualObject3D> {
|
||||
|
||||
override val context: Context get() = three.context
|
||||
|
||||
@ -95,7 +95,7 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Renderer
|
||||
}
|
||||
|
||||
fun ThreePlugin.output(element: HTMLElement? = null, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit = {}) =
|
||||
ThreeOutput(this, buildMeta(meta, override)).apply {
|
||||
ThreeCanvas(this, buildMeta(meta, override)).apply {
|
||||
if (element != null) {
|
||||
attach(element)
|
||||
}
|
@ -38,17 +38,17 @@ fun Object3D.updatePosition(obj: VisualObject3D) {
|
||||
updateMatrix()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsafe invocation of a factory
|
||||
*/
|
||||
operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
||||
if (type.isInstance(obj)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return invoke(obj as T)
|
||||
} else {
|
||||
error("The object of type ${obj::class} could not be rendered by this factory")
|
||||
}
|
||||
}
|
||||
///**
|
||||
// * Unsafe invocation of a factory
|
||||
// */
|
||||
//operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
||||
// if (type.isInstance(obj)) {
|
||||
// @Suppress("UNCHECKED_CAST")
|
||||
// return invoke(obj as T)
|
||||
// } else {
|
||||
// error("The object of type ${obj::class} could not be rendered by this factory")
|
||||
// }
|
||||
//}
|
||||
|
||||
/**
|
||||
* Update non-position non-geometry property
|
||||
|
@ -16,7 +16,7 @@ import info.laht.threekt.objects.Group as ThreeGroup
|
||||
class ThreePlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
private val objectFactories = HashMap<KClass<out VisualObject>, ThreeFactory<*>>()
|
||||
private val objectFactories = HashMap<KClass<out VisualObject3D>, ThreeFactory<*>>()
|
||||
private val compositeFactory = ThreeCompositeFactory(this)
|
||||
private val proxyFactory = ThreeProxyFactory(this)
|
||||
|
||||
@ -29,9 +29,11 @@ class ThreePlugin : AbstractPlugin() {
|
||||
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||
}
|
||||
|
||||
private fun findObjectFactory(type: KClass<out VisualObject>): ThreeFactory<*>? {
|
||||
return objectFactories[type]
|
||||
?: context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type }
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun findObjectFactory(type: KClass<out VisualObject>): ThreeFactory<VisualObject3D>? {
|
||||
return (objectFactories[type]
|
||||
?: context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type })
|
||||
as ThreeFactory<VisualObject3D>?
|
||||
}
|
||||
|
||||
fun buildObject3D(obj: VisualObject3D): Object3D {
|
||||
@ -73,7 +75,7 @@ class ThreePlugin : AbstractPlugin() {
|
||||
is Composite -> compositeFactory(obj)
|
||||
else -> {
|
||||
//find specialized factory for this type if it is present
|
||||
val factory = findObjectFactory(obj::class)
|
||||
val factory: ThreeFactory<VisualObject3D>? = findObjectFactory(obj::class)
|
||||
when {
|
||||
factory != null -> factory(obj)
|
||||
obj is Shape -> ThreeShapeFactory(obj)
|
||||
|
@ -3,13 +3,10 @@
|
||||
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
|
||||
@ -32,8 +29,6 @@ class CustomThreeVisualObject(val threeFactory: ThreeFactory<VisualObject3D>) :
|
||||
@Serializable(ConfigSerializer::class)
|
||||
override var properties: Config? = null
|
||||
|
||||
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
|
||||
|
||||
override fun toObject3D(): Object3D = threeFactory(this)
|
||||
|
||||
}
|
@ -1,5 +1,11 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.ContextAware
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.output.Renderer
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import hep.dataforge.vis.spatial.World.CAMERA_FAR_CLIP
|
||||
import hep.dataforge.vis.spatial.World.CAMERA_INITIAL_DISTANCE
|
||||
import hep.dataforge.vis.spatial.World.CAMERA_INITIAL_X_ANGLE
|
||||
@ -16,7 +22,9 @@ import javafx.scene.paint.Color
|
||||
import org.fxyz3d.utils.CameraTransformer
|
||||
import tornadofx.*
|
||||
|
||||
class Canvas3D : Fragment() {
|
||||
class Canvas3D(val plugin: FX3DPlugin, val meta: Meta = EmptyMeta) : Fragment(), Renderer<VisualObject3D>, ContextAware {
|
||||
|
||||
override val context: Context get() = plugin.context
|
||||
val world: Group = Group()
|
||||
|
||||
private val camera = PerspectiveCamera().apply {
|
||||
@ -153,6 +161,10 @@ class Canvas3D : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(obj: VisualObject3D, meta: Meta) {
|
||||
plugin.buildNode(obj)?.let { world.children.add(it) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val AXIS_LENGTH = 2000.0
|
||||
private const val CONTROL_MULTIPLIER = 0.1
|
||||
|
@ -10,7 +10,7 @@ import tornadofx.*
|
||||
/**
|
||||
* A caching binding collection for [VisualObject] properties
|
||||
*/
|
||||
class DisplayObjectFXListener(val obj: VisualObject) {
|
||||
class DisplayObjectFXBinding(val obj: VisualObject) {
|
||||
private val binndings = HashMap<Name, ObjectBinding<MetaItem<*>?>>()
|
||||
|
||||
init {
|
@ -1,50 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.output.Renderer
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.Box
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
import org.fxyz3d.shapes.primitives.CuboidMesh
|
||||
import tornadofx.*
|
||||
|
||||
/**
|
||||
* https://github.com/miho/JCSG for operations
|
||||
*
|
||||
*/
|
||||
class FX3DOutput(override val context: Context) : Renderer<VisualObject> {
|
||||
val canvas by lazy { Canvas3D() }
|
||||
|
||||
|
||||
private fun buildNode(obj: VisualObject): Node? {
|
||||
val listener = DisplayObjectFXListener(obj)
|
||||
val x = listener["pos.x"].float()
|
||||
val y = listener["pos.y"].float()
|
||||
val z = listener["pos.z"].float()
|
||||
val center = objectBinding(x, y, z) {
|
||||
org.fxyz3d.geometry.Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f)
|
||||
}
|
||||
return when (obj) {
|
||||
is VisualGroup3D -> Group(obj.map { buildNode(it) }).apply {
|
||||
this.translateXProperty().bind(x)
|
||||
this.translateYProperty().bind(y)
|
||||
this.translateZProperty().bind(z)
|
||||
}
|
||||
is Box -> CuboidMesh(obj.xSize.toDouble(), obj.ySize.toDouble(), obj.zSize.toDouble()).apply {
|
||||
this.centerProperty().bind(center)
|
||||
this.materialProperty().bind(listener["color"].transform { it.material() })
|
||||
}
|
||||
else -> {
|
||||
logger.error { "No renderer defined for ${obj::class}" }
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(obj: VisualObject, meta: Meta) {
|
||||
buildNode(obj)?.let { canvas.world.children.add(it) }
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import hep.dataforge.context.*
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.provider.Type
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.fx.FX3DFactory.Companion.TYPE
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.shape.Shape3D
|
||||
import javafx.scene.transform.Rotate
|
||||
import org.fxyz3d.shapes.composites.PolyLine3D
|
||||
import org.fxyz3d.shapes.primitives.CuboidMesh
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class FX3DPlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
private val objectFactories = HashMap<KClass<out VisualObject3D>, FX3DFactory<*>>()
|
||||
private val compositeFactory = FXCompositeFactory(this)
|
||||
private val proxyFactory = FXProxyFactory(this)
|
||||
|
||||
init {
|
||||
//Add specialized factories here
|
||||
objectFactories[Convex::class] = FXConvexFactory
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun findObjectFactory(type: KClass<out VisualObject3D>): FX3DFactory<VisualObject3D>? {
|
||||
return (objectFactories[type] ?: context.content<FX3DFactory<*>>(TYPE).values.find { it.type == type })
|
||||
as FX3DFactory<VisualObject3D>?
|
||||
}
|
||||
|
||||
fun buildNode(obj: VisualObject3D): Node? {
|
||||
val binding = DisplayObjectFXBinding(obj)
|
||||
return when (obj) {
|
||||
is Proxy -> proxyFactory(obj, binding)
|
||||
is VisualGroup3D -> Group(obj.filterIsInstance<VisualObject3D>().map { buildNode(it) })
|
||||
is Composite -> compositeFactory(obj, binding)
|
||||
is Box -> CuboidMesh(obj.xSize.toDouble(), obj.ySize.toDouble(), obj.zSize.toDouble())
|
||||
is PolyLine -> PolyLine3D(
|
||||
obj.points.map { it.point },
|
||||
obj.thickness.toFloat(),
|
||||
obj.material?.get("color")?.color()
|
||||
)
|
||||
else -> {
|
||||
//find specialized factory for this type if it is present
|
||||
val factory: FX3DFactory<VisualObject3D>? = findObjectFactory(obj::class)
|
||||
when {
|
||||
factory != null -> factory(obj, binding)
|
||||
obj is Shape -> FXShapeFactory(obj, binding)
|
||||
else -> error("Renderer for ${obj::class} not found")
|
||||
}
|
||||
}
|
||||
}.apply {
|
||||
translateXProperty().bind(binding[VisualObject3D.xPos].float())
|
||||
translateYProperty().bind(binding[VisualObject3D.yPos].float())
|
||||
translateZProperty().bind(binding[VisualObject3D.zPos].float())
|
||||
scaleXProperty().bind(binding[VisualObject3D.xScale].float())
|
||||
scaleYProperty().bind(binding[VisualObject3D.yScale].float())
|
||||
scaleZProperty().bind(binding[VisualObject3D.zScale].float())
|
||||
|
||||
val rotateX = Rotate(0.0, Rotate.X_AXIS).apply {
|
||||
angleProperty().bind(binding[VisualObject3D.xRotation].float())
|
||||
}
|
||||
|
||||
val rotateY = Rotate(0.0, Rotate.Y_AXIS).apply {
|
||||
angleProperty().bind(binding[VisualObject3D.yRotation].float())
|
||||
}
|
||||
|
||||
val rotateZ = Rotate(0.0, Rotate.Z_AXIS).apply {
|
||||
angleProperty().bind(binding[VisualObject3D.zRotation].float())
|
||||
}
|
||||
|
||||
when (obj.rotationOrder) {
|
||||
RotationOrder.ZYX -> transforms.addAll(rotateZ, rotateY, rotateX)
|
||||
RotationOrder.XZY -> transforms.addAll(rotateY, rotateZ, rotateX)
|
||||
RotationOrder.YXZ -> transforms.addAll(rotateZ, rotateX, rotateY)
|
||||
RotationOrder.YZX -> transforms.addAll(rotateX, rotateZ, rotateY)
|
||||
RotationOrder.ZXY -> transforms.addAll(rotateY, rotateX, rotateZ)
|
||||
RotationOrder.XYZ -> transforms.addAll(rotateZ, rotateY, rotateX)
|
||||
}
|
||||
|
||||
if (this is Shape3D) {
|
||||
materialProperty().bind(binding["color"].transform { it.material() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : PluginFactory<FX3DPlugin> {
|
||||
override val tag = PluginTag("visual.fx3D", PluginTag.DATAFORGE_GROUP)
|
||||
override val type = FX3DPlugin::class
|
||||
override fun invoke(meta: Meta, context: Context) = FX3DPlugin()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder and updater for three.js object
|
||||
*/
|
||||
@Type(TYPE)
|
||||
interface FX3DFactory<in T : VisualObject3D> {
|
||||
|
||||
val type: KClass<in T>
|
||||
|
||||
operator fun invoke(obj: T, binding: DisplayObjectFXBinding): Node
|
||||
|
||||
companion object {
|
||||
const val TYPE = "fx3DFactory"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import eu.mihosoft.jcsg.CSG
|
||||
import hep.dataforge.vis.spatial.Composite
|
||||
import hep.dataforge.vis.spatial.CompositeType
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.shape.MeshView
|
||||
import org.fxyz3d.utils.MeshUtils
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class FXCompositeFactory(val plugin: FX3DPlugin) :
|
||||
FX3DFactory<Composite> {
|
||||
override val type: KClass<in Composite>
|
||||
get() = Composite::class
|
||||
|
||||
override fun invoke(obj: Composite, binding: DisplayObjectFXBinding): Node {
|
||||
val first = plugin.buildNode(obj.first) as? MeshView ?: error("Can't build node")
|
||||
val second = plugin.buildNode(obj.second) as? MeshView ?: error("Can't build node")
|
||||
val firstCSG = MeshUtils.mesh2CSG(first)
|
||||
val secondCSG = MeshUtils.mesh2CSG(second)
|
||||
val resultCSG = when(obj.compositeType){
|
||||
CompositeType.UNION -> firstCSG.union(secondCSG)
|
||||
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
||||
CompositeType.SUBTRACT -> firstCSG.difference(secondCSG)
|
||||
}
|
||||
return resultCSG.toNode()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun CSG.toNode(): Node{
|
||||
val meshes = toJavaFXMesh().asMeshViews
|
||||
return if(meshes.size == 1){
|
||||
meshes.first()
|
||||
} else {
|
||||
Group(meshes.map { it })
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import eu.mihosoft.jcsg.PropertyStorage
|
||||
import eu.mihosoft.jcsg.ext.quickhull3d.HullUtil
|
||||
import eu.mihosoft.vvecmath.Vector3d
|
||||
import hep.dataforge.vis.spatial.Convex
|
||||
import javafx.scene.Node
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
object FXConvexFactory : FX3DFactory<Convex> {
|
||||
override val type: KClass<in Convex> get() = Convex::class
|
||||
|
||||
override fun invoke(obj: Convex, binding: DisplayObjectFXBinding): Node {
|
||||
val hull = HullUtil.hull(obj.points.map { Vector3d.xyz(it.x, it.y, it.z) }, PropertyStorage())
|
||||
return hull.toNode()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import hep.dataforge.vis.spatial.Proxy
|
||||
import javafx.scene.Node
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class FXProxyFactory(val plugin: FX3DPlugin) :
|
||||
FX3DFactory<Proxy> {
|
||||
override val type: KClass<in Proxy> get() = Proxy::class
|
||||
|
||||
override fun invoke(obj: Proxy, binding: DisplayObjectFXBinding): Node {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.spatial.GeometryBuilder
|
||||
import hep.dataforge.vis.spatial.Point3D
|
||||
import hep.dataforge.vis.spatial.Shape
|
||||
import javafx.scene.shape.Mesh
|
||||
import javafx.scene.shape.MeshView
|
||||
import javafx.scene.shape.TriangleMesh
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
object FXShapeFactory : FX3DFactory<Shape> {
|
||||
override val type: KClass<in Shape> get() = Shape::class
|
||||
|
||||
override fun invoke(obj: Shape, binding: DisplayObjectFXBinding): MeshView {
|
||||
val mesh = FXGeometryBuilder().apply { obj.toGeometry(this) }.build()
|
||||
return MeshView(mesh)
|
||||
}
|
||||
}
|
||||
|
||||
private typealias Face = IntArray
|
||||
|
||||
private class FXGeometryBuilder : GeometryBuilder<Mesh> {
|
||||
val vertices = ArrayList<Point3D>()
|
||||
val faces = ArrayList<Face>()
|
||||
private val vertexCache = HashMap<Point3D, Int>()
|
||||
|
||||
private fun append(vertex: Point3D): Int {
|
||||
val index = vertexCache[vertex] ?: -1//vertices.indexOf(vertex)
|
||||
return if (index > 0) {
|
||||
index
|
||||
} else {
|
||||
vertices.add(vertex)
|
||||
vertexCache[vertex] = vertices.size - 1
|
||||
vertices.size - 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) {
|
||||
//adding vertices
|
||||
val face: Face = intArrayOf(append(vertex1), append(vertex2), append(vertex3))
|
||||
faces.add(face)
|
||||
}
|
||||
|
||||
override fun build(): Mesh {
|
||||
val mesh = TriangleMesh()
|
||||
vertices.forEach {
|
||||
//TODO optimize copy
|
||||
mesh.points.addAll(it.x.toFloat(), it.y.toFloat(), it.z.toFloat())
|
||||
}
|
||||
faces.forEach {
|
||||
mesh.faces.addAll(it[0], it[1], it[2])
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ class RendererDemoApp : App(RendererDemoView::class)
|
||||
|
||||
|
||||
class RendererDemoView : View() {
|
||||
val renderer = FX3DOutput(Global)
|
||||
val plugin = Global.plugins.fetch(FX3DPlugin)
|
||||
val renderer = Canvas3D(plugin)
|
||||
override val root: Parent = borderpane {
|
||||
center = renderer.canvas.root
|
||||
center = renderer.root
|
||||
}
|
||||
|
||||
lateinit var group: VisualGroup3D
|
||||
@ -44,7 +45,7 @@ class RendererDemoView : View() {
|
||||
}
|
||||
}
|
||||
|
||||
renderer.canvas.apply {
|
||||
renderer.apply {
|
||||
angleY = -30.0
|
||||
angleX = -15.0
|
||||
}
|
||||
|
@ -5,8 +5,28 @@ actual class Point2D actual constructor(x: Number, y: Number) {
|
||||
actual var y = y.toDouble()
|
||||
}
|
||||
|
||||
actual class Point3D actual constructor(x: Number, y: Number, z: Number) {
|
||||
actual var x = x.toDouble()
|
||||
actual var y = y.toDouble()
|
||||
actual var z = z.toDouble()
|
||||
actual class Point3D(val point: org.fxyz3d.geometry.Point3D) {
|
||||
actual constructor(x: Number, y: Number, z: Number) : this(
|
||||
org.fxyz3d.geometry.Point3D(
|
||||
x.toFloat(),
|
||||
y.toFloat(),
|
||||
z.toFloat()
|
||||
)
|
||||
)
|
||||
|
||||
actual var x: Double
|
||||
inline get() = point.x.toDouble()
|
||||
inline set(value) {
|
||||
point.x = value.toFloat()
|
||||
}
|
||||
actual var y: Double
|
||||
inline get() = point.y.toDouble()
|
||||
inline set(value) {
|
||||
point.y = value.toFloat()
|
||||
}
|
||||
actual var z: Double
|
||||
inline get() = point.z.toDouble()
|
||||
inline set(value) {
|
||||
point.z = value.toFloat()
|
||||
}
|
||||
}
|
@ -10,12 +10,12 @@ import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.string
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.output.Renderer
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import hep.dataforge.vis.spatial.three.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.three.ThreeCanvas
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
import kotlinx.html.dom.append
|
||||
@ -33,7 +33,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
private val gridRoot = document.create.div("row")
|
||||
private val outputs: MutableMap<Name, ThreeOutput> = HashMap()
|
||||
private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
|
||||
|
||||
init {
|
||||
require(ThreePlugin)
|
||||
@ -48,7 +48,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Output<T> {
|
||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
||||
val three = context.plugins.get<ThreePlugin>()!!
|
||||
|
||||
return outputs.getOrPut(name) {
|
||||
@ -73,7 +73,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
}
|
||||
|
||||
output
|
||||
} as Output<T>
|
||||
} as Renderer<T>
|
||||
}
|
||||
|
||||
companion object : PluginFactory<ThreeDemoGrid> {
|
||||
|
Loading…
Reference in New Issue
Block a user