FX backend in progress

This commit is contained in:
Alexander Nozik 2019-12-14 21:45:41 +03:00
parent c964f80d73
commit cef1a1ee6d
32 changed files with 406 additions and 146 deletions

View File

@ -32,8 +32,7 @@ kotlin {
jsMain{ jsMain{
dependencies { dependencies {
api("hep.dataforge:dataforge-output-html:$dataforgeVersion") api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0") api(npm("bootstrap","4.4.1"))
api(npm("bootstrap","4.3.1"))
} }
} }
} }

View File

@ -1,6 +1,9 @@
package hep.dataforge.vis.common 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.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
@ -15,7 +18,7 @@ import kotlinx.serialization.Transient
* A root type for display hierarchy * A root type for display hierarchy
*/ */
@Type(TYPE) @Type(TYPE)
interface VisualObject : MetaRepr, Configurable { interface VisualObject : Configurable {
/** /**
* The parent object of this one. If null, this one is a root. * 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?) 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> var styles: List<Name>

View File

@ -21,7 +21,8 @@ kotlin {
} }
jvmMain { jvmMain {
dependencies { 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 { jsMain {

View File

@ -3,7 +3,6 @@ package hep.dataforge.vis.spatial
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.float import hep.dataforge.meta.float
@ -50,8 +49,6 @@ class Box(
geometryBuilder.face4(node8, node5, node6, node7) geometryBuilder.face4(node8, node5, node6, node7)
} }
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
companion object : VisualFactory<Box> { companion object : VisualFactory<Box> {
const val TYPE = "geometry.3d.box" const val TYPE = "geometry.3d.box"

View File

@ -2,9 +2,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -34,8 +32,6 @@ class Composite(
@Serializable(ConfigSerializer::class) @Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
} }
inline fun VisualGroup3D.composite( inline fun VisualGroup3D.composite(

View File

@ -3,12 +3,12 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlin.math.cos
import kotlin.math.sin
/** /**
* A cylinder or cut cone segment * A cylinder or cut cone segment
@ -20,7 +20,7 @@ class ConeSegment(
var upperRadius: Float, var upperRadius: Float,
var startAngle: Float = 0f, var startAngle: Float = 0f,
var angle: Float = PI2 var angle: Float = PI2
) : AbstractVisualObject(), VisualObject3D { ) : AbstractVisualObject(), VisualObject3D, Shape {
@Serializable(ConfigSerializer::class) @Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
@ -29,7 +29,48 @@ class ConeSegment(
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: 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( inline fun VisualGroup3D.cylinder(

View File

@ -3,9 +3,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -20,8 +18,6 @@ class Convex(val points: List<Point3D>) : AbstractVisualObject(), VisualObject3D
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
companion object { companion object {
const val TYPE = "geometry.3d.convex" const val TYPE = "geometry.3d.convex"
} }

View File

@ -2,9 +2,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -107,8 +105,6 @@ class Extruded(
geometryBuilder.cap(layers.last()) geometryBuilder.cap(layers.last())
} }
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
companion object { companion object {
const val TYPE = "geometry.3d.extruded" const val TYPE = "geometry.3d.extruded"
} }

View File

@ -3,9 +3,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.number import hep.dataforge.vis.common.number
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -23,7 +21,6 @@ class PolyLine(var points: List<Point3D>) : AbstractVisualObject(), VisualObject
//var lineType by string() //var lineType by string()
var thickness by number(1.0, key = "material.thickness") 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 = {}) = fun VisualGroup3D.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}) =

View File

@ -4,7 +4,6 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.NameSerializer import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem 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> override val children: Map<NameToken, ProxyChild>
get() = (prototype as? MutableVisualGroup)?.children get() = (prototype as? MutableVisualGroup)?.children
?.filter { !it.key.toString().startsWith("@") } ?.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 { companion object {

View File

@ -3,9 +3,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -18,7 +16,7 @@ class Sphere(
var phi: Float = PI2, var phi: Float = PI2,
var thetaStart: Float = 0f, var thetaStart: Float = 0f,
var theta: Float = PI.toFloat() var theta: Float = PI.toFloat()
) : AbstractVisualObject(), VisualObject3D { ) : AbstractVisualObject(), VisualObject3D, Shape {
@Serializable(ConfigSerializer::class) @Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
@ -27,7 +25,9 @@ class Sphere(
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: 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( inline fun VisualGroup3D.sphere(

View File

@ -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) }

View File

@ -2,9 +2,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -126,7 +124,6 @@ class Tube(
} }
} }
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
} }
inline fun VisualGroup3D.tube( inline fun VisualGroup3D.tube(

View File

@ -11,7 +11,6 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.MetaSerializer import hep.dataforge.io.serialization.MetaSerializer
import hep.dataforge.io.serialization.NameSerializer import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.Name 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 { companion object {
const val PROTOTYPES_KEY = "templates" const val PROTOTYPES_KEY = "templates"
} }

View File

@ -1,8 +1,8 @@
package hep.dataforge.vis.spatial 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.getIndexed
import hep.dataforge.meta.node
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -25,12 +25,11 @@ class ConvexTest {
val convex = group.first() as Convex 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 val points = meta.getIndexed("points").values.map { (it as MetaItem.NodeItem<*>).node.point3D()}
assertEquals(8, points.count())
assertEquals(8, pointsNode?.items?.count())
val points = pointsNode?.getIndexed("points")
assertEquals(8, convex.points.size) assertEquals(8, convex.points.size)
} }

View File

@ -17,6 +17,6 @@ class SerializationTest {
val string = json.stringify(Box.serializer(), cube) val string = json.stringify(Box.serializer(), cube)
println(string) println(string)
val newCube = json.parse(Box.serializer(), string) val newCube = json.parse(Box.serializer(), string)
assertEquals(cube.toMeta(),newCube.toMeta()) assertEquals(cube.config, newCube.config)
} }
} }

View File

@ -1,6 +1,6 @@
package hep.dataforge.vis.spatial.editor 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.InputType
import kotlinx.html.dom.append import kotlinx.html.dom.append
import kotlinx.html.js.div import kotlinx.html.js.div
@ -9,7 +9,7 @@ import kotlinx.html.js.label
import org.w3c.dom.Element import org.w3c.dom.Element
import kotlin.dom.clear import kotlin.dom.clear
fun Element.threeOutputConfig(output: ThreeOutput) { fun Element.threeOutputConfig(canvas: ThreeCanvas) {
clear() clear()
append { append {
card("Layers"){ card("Layers"){
@ -23,9 +23,9 @@ fun Element.threeOutputConfig(output: ThreeOutput) {
} }
onchange = { onchange = {
if (checked) { if (checked) {
output.camera.layers.enable(layer) canvas.camera.layers.enable(layer)
} else { } else {
output.camera.layers.disable(layer) canvas.camera.layers.disable(layer)
} }
} }
} }

View File

@ -19,7 +19,7 @@ import kotlin.browser.window
import kotlin.dom.clear import kotlin.dom.clear
import kotlin.math.max 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 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 = {}) = 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) { if (element != null) {
attach(element) attach(element)
} }

View File

@ -38,17 +38,17 @@ fun Object3D.updatePosition(obj: VisualObject3D) {
updateMatrix() updateMatrix()
} }
/** ///**
* Unsafe invocation of a factory // * Unsafe invocation of a factory
*/ // */
operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D { //operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
if (type.isInstance(obj)) { // if (type.isInstance(obj)) {
@Suppress("UNCHECKED_CAST") // @Suppress("UNCHECKED_CAST")
return invoke(obj as T) // return invoke(obj as T)
} else { // } else {
error("The object of type ${obj::class} could not be rendered by this factory") // error("The object of type ${obj::class} could not be rendered by this factory")
} // }
} //}
/** /**
* Update non-position non-geometry property * Update non-position non-geometry property

View File

@ -16,7 +16,7 @@ import info.laht.threekt.objects.Group as ThreeGroup
class ThreePlugin : AbstractPlugin() { class ThreePlugin : AbstractPlugin() {
override val tag: PluginTag get() = Companion.tag 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 compositeFactory = ThreeCompositeFactory(this)
private val proxyFactory = ThreeProxyFactory(this) private val proxyFactory = ThreeProxyFactory(this)
@ -29,9 +29,11 @@ class ThreePlugin : AbstractPlugin() {
objectFactories[PolyLine::class] = ThreeLineFactory objectFactories[PolyLine::class] = ThreeLineFactory
} }
private fun findObjectFactory(type: KClass<out VisualObject>): ThreeFactory<*>? { @Suppress("UNCHECKED_CAST")
return objectFactories[type] private fun findObjectFactory(type: KClass<out VisualObject>): ThreeFactory<VisualObject3D>? {
?: context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type } return (objectFactories[type]
?: context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type })
as ThreeFactory<VisualObject3D>?
} }
fun buildObject3D(obj: VisualObject3D): Object3D { fun buildObject3D(obj: VisualObject3D): Object3D {
@ -73,7 +75,7 @@ class ThreePlugin : AbstractPlugin() {
is Composite -> compositeFactory(obj) is Composite -> compositeFactory(obj)
else -> { else -> {
//find specialized factory for this type if it is present //find specialized factory for this type if it is present
val factory = findObjectFactory(obj::class) val factory: ThreeFactory<VisualObject3D>? = findObjectFactory(obj::class)
when { when {
factory != null -> factory(obj) factory != null -> factory(obj)
obj is Shape -> ThreeShapeFactory(obj) obj is Shape -> ThreeShapeFactory(obj)

View File

@ -3,13 +3,10 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.toMeta
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.spatial.Point3D import hep.dataforge.vis.spatial.Point3D
import hep.dataforge.vis.spatial.Point3DSerializer import hep.dataforge.vis.spatial.Point3DSerializer
import hep.dataforge.vis.spatial.Visual3DPlugin
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -32,8 +29,6 @@ class CustomThreeVisualObject(val threeFactory: ThreeFactory<VisualObject3D>) :
@Serializable(ConfigSerializer::class) @Serializable(ConfigSerializer::class)
override var properties: Config? = null override var properties: Config? = null
override fun toMeta(): Meta = Visual3DPlugin.json.toJson(serializer(), this).toMeta()
override fun toObject3D(): Object3D = threeFactory(this) override fun toObject3D(): Object3D = threeFactory(this)
} }

View File

@ -1,5 +1,11 @@
package hep.dataforge.vis.spatial.fx 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_FAR_CLIP
import hep.dataforge.vis.spatial.World.CAMERA_INITIAL_DISTANCE import hep.dataforge.vis.spatial.World.CAMERA_INITIAL_DISTANCE
import hep.dataforge.vis.spatial.World.CAMERA_INITIAL_X_ANGLE 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 org.fxyz3d.utils.CameraTransformer
import tornadofx.* 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() val world: Group = Group()
private val camera = PerspectiveCamera().apply { 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 { companion object {
private const val AXIS_LENGTH = 2000.0 private const val AXIS_LENGTH = 2000.0
private const val CONTROL_MULTIPLIER = 0.1 private const val CONTROL_MULTIPLIER = 0.1

View File

@ -10,7 +10,7 @@ import tornadofx.*
/** /**
* A caching binding collection for [VisualObject] properties * A caching binding collection for [VisualObject] properties
*/ */
class DisplayObjectFXListener(val obj: VisualObject) { class DisplayObjectFXBinding(val obj: VisualObject) {
private val binndings = HashMap<Name, ObjectBinding<MetaItem<*>?>>() private val binndings = HashMap<Name, ObjectBinding<MetaItem<*>?>>()
init { init {

View File

@ -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) }
}
}

View File

@ -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"
}
}

View File

@ -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 })
}
}

View File

@ -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()
}
}

View File

@ -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.
}
}

View File

@ -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
}
}

View File

@ -16,9 +16,10 @@ class RendererDemoApp : App(RendererDemoView::class)
class RendererDemoView : View() { class RendererDemoView : View() {
val renderer = FX3DOutput(Global) val plugin = Global.plugins.fetch(FX3DPlugin)
val renderer = Canvas3D(plugin)
override val root: Parent = borderpane { override val root: Parent = borderpane {
center = renderer.canvas.root center = renderer.root
} }
lateinit var group: VisualGroup3D lateinit var group: VisualGroup3D
@ -44,7 +45,7 @@ class RendererDemoView : View() {
} }
} }
renderer.canvas.apply { renderer.apply {
angleY = -30.0 angleY = -30.0
angleX = -15.0 angleX = -15.0
} }

View File

@ -5,8 +5,28 @@ actual class Point2D actual constructor(x: Number, y: Number) {
actual var y = y.toDouble() actual var y = y.toDouble()
} }
actual class Point3D actual constructor(x: Number, y: Number, z: Number) { actual class Point3D(val point: org.fxyz3d.geometry.Point3D) {
actual var x = x.toDouble() actual constructor(x: Number, y: Number, z: Number) : this(
actual var y = y.toDouble() org.fxyz3d.geometry.Point3D(
actual var z = z.toDouble() 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()
}
} }

View File

@ -10,12 +10,12 @@ import hep.dataforge.meta.get
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.output.Output
import hep.dataforge.output.OutputManager import hep.dataforge.output.OutputManager
import hep.dataforge.output.Renderer
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.render 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.ThreePlugin
import hep.dataforge.vis.spatial.three.output import hep.dataforge.vis.spatial.three.output
import kotlinx.html.dom.append import kotlinx.html.dom.append
@ -33,7 +33,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
private val gridRoot = document.create.div("row") private val gridRoot = document.create.div("row")
private val outputs: MutableMap<Name, ThreeOutput> = HashMap() private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
init { init {
require(ThreePlugin) require(ThreePlugin)
@ -48,7 +48,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
} }
@Suppress("UNCHECKED_CAST") @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>()!! val three = context.plugins.get<ThreePlugin>()!!
return outputs.getOrPut(name) { return outputs.getOrPut(name) {
@ -73,7 +73,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
} }
output output
} as Output<T> } as Renderer<T>
} }
companion object : PluginFactory<ThreeDemoGrid> { companion object : PluginFactory<ThreeDemoGrid> {