diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ea6fc9b..d638f0c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ - VisionGroup builder accepts `null` as name for statics instead of `""` - gdml sphere is rendered as a SphereLayer instead of Sphere (#35) - Tube is replaced by more general ConeSurface +- position, rotation and size moved to properties +- prototypes moved to children +- Immutable Solid instances +- Property listeners are not triggered if there are no changes. +- Feedback websocket connection in the client. ### Deprecated diff --git a/build.gradle.kts b/build.gradle.kts index 8236106e..4ab2a7d5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,7 +20,7 @@ allprojects { } group = "space.kscience" - version = "0.2.0-dev-21" + version = "0.2.0-dev-22" } subprojects { diff --git a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisualTest.kt b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt similarity index 97% rename from demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisualTest.kt rename to demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt index c12c7215..17bdc014 100644 --- a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisualTest.kt +++ b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt @@ -10,7 +10,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull -class GDMLVisualTest { +class GDMLVisionTest { // @Test // fun testCubesStyles(){ diff --git a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt index 1d619dfb..4f6560b6 100644 --- a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt +++ b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt @@ -10,6 +10,7 @@ import space.kscience.dataforge.context.fetch import space.kscience.dataforge.names.Name import space.kscience.gdml.Gdml import space.kscience.gdml.decodeFromString +import space.kscience.visionforge.gdml.markLayers import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.tab @@ -32,7 +33,11 @@ val GDMLApp = functionalComponent("GDMLApp") { props -> val parsedVision = when { name.endsWith(".gdml") || name.endsWith(".xml") -> { val gdml = Gdml.decodeFromString(data) - gdml.toVision() + gdml.toVision().apply { + root(visionManager) + console.info("Marking layers for file $name") + markLayers() + } } name.endsWith(".json") -> visionManager.decodeFromString(data) else -> { diff --git a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt index b43ce062..ec28649c 100644 --- a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt +++ b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt @@ -1,8 +1,5 @@ import kotlinx.browser.document -import kotlinx.css.height -import kotlinx.css.vh -import kotlinx.css.vw -import kotlinx.css.width +import kotlinx.css.* import react.child import react.dom.render import space.kscience.dataforge.context.Context @@ -32,6 +29,8 @@ private class JsPlaygroundApp : Application { render(element) { styledDiv { css{ + padding(0.pt) + margin(0.pt) height = 100.vh width = 100.vw } diff --git a/demo/js-playground/src/main/resources/index.html b/demo/js-playground/src/main/resources/index.html index 7a777ef1..83a9016e 100644 --- a/demo/js-playground/src/main/resources/index.html +++ b/demo/js-playground/src/main/resources/index.html @@ -2,10 +2,11 @@ + js-playground - +
\ No newline at end of file diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt index e87a2abc..17d5ac86 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt @@ -11,7 +11,7 @@ import space.kscience.visionforge.solid.plus class SC1( val name: String, val center: Point3D, - val xSize: Double = PIXEL_XY_SIZE, val ySize: Double = PIXEL_XY_SIZE, val zSize: Double = PIXEL_Z_SIZE + val xSize: Float = PIXEL_XY_SIZE, val ySize: Float = PIXEL_XY_SIZE, val zSize: Float = PIXEL_Z_SIZE ) class SC16( @@ -121,12 +121,12 @@ internal expect fun readMonitorConfig(): String object Monitor { const val GEOMETRY_TOLERANCE = 0.01 - const val PIXEL_XY_SIZE = 122.0 - const val PIXEL_XY_SPACING = 123.2 - const val PIXEL_Z_SIZE = 30.0 - const val CENTRAL_LAYER_Z = 0.0 - const val UPPER_LAYER_Z = -166.0 - const val LOWER_LAYER_Z = 180.0 + const val PIXEL_XY_SIZE = 122.0f + const val PIXEL_XY_SPACING = 123.2f + const val PIXEL_Z_SIZE = 30.0f + const val CENTRAL_LAYER_Z = 0.0f + const val UPPER_LAYER_Z = -166.0f + const val LOWER_LAYER_Z = 180.0f /** * Build map for the whole monitor diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt index 71a96647..579bca15 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt @@ -17,19 +17,19 @@ import kotlin.random.Random internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) { // val layer: Layer = findLayer(center.z); private val upLayer = - findLayer(sc.center.z + sc.zSize / 2.0)//Layer("${name}_up", center.z + zSize / 2.0); + findLayer(sc.center.z + sc.zSize / 2f)//Layer("${name}_up", center.z + zSize / 2.0); private val bottomLayer = - findLayer(sc.center.z - sc.zSize / 2.0)//Layer("${name}_bottom", center.z - zSize / 2.0); + findLayer(sc.center.z - sc.zSize / 2f)//Layer("${name}_bottom", center.z - zSize / 2.0); private val centralLayer = findLayer(sc.center.z) - private val center = Vector3D(sc.center.x, sc.center.y, sc.center.z) + private val center = Vector3D(sc.center.x.toDouble(), sc.center.y.toDouble(), sc.center.z.toDouble()) private val sideLayers: Array = arrayOf( - Plane(center.add(Vector3D(PIXEL_XY_SIZE / 2, 0.0, 0.0)), Vector3D(1.0, 0.0, 0.0), GEOMETRY_TOLERANCE), - Plane(center.add(Vector3D(-PIXEL_XY_SIZE / 2, 0.0, 0.0)), Vector3D(-1.0, 0.0, 0.0), GEOMETRY_TOLERANCE), - Plane(center.add(Vector3D(0.0, PIXEL_XY_SIZE / 2, 0.0)), Vector3D(0.0, 1.0, 0.0), GEOMETRY_TOLERANCE), - Plane(center.add(Vector3D(0.0, -PIXEL_XY_SIZE / 2, 0.0)), Vector3D(0.0, -1.0, 0.0), GEOMETRY_TOLERANCE) - ); + Plane(center.add(Vector3D(PIXEL_XY_SIZE / 2.0, 0.0, 0.0)), Vector3D(1.0, 0.0, 0.0), GEOMETRY_TOLERANCE), + Plane(center.add(Vector3D(-PIXEL_XY_SIZE / 2.0, 0.0, 0.0)), Vector3D(-1.0, 0.0, 0.0), GEOMETRY_TOLERANCE), + Plane(center.add(Vector3D(0.0, PIXEL_XY_SIZE / 2.0, 0.0)), Vector3D(0.0, 1.0, 0.0), GEOMETRY_TOLERANCE), + Plane(center.add(Vector3D(0.0, -PIXEL_XY_SIZE / 2.0, 0.0)), Vector3D(0.0, -1.0, 0.0), GEOMETRY_TOLERANCE) + ) //TODO add efficiency private fun containsPoint(x: Double, y: Double, z: Double, tolerance: Double = GEOMETRY_TOLERANCE): Boolean { @@ -63,8 +63,8 @@ internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) { * The layer number from up to bottom */ fun getLayerNumber(): Int { - return when (this.center.z) { - UPPER_LAYER_Z -> 1; + return when (this.center.z.toFloat()) { + UPPER_LAYER_Z -> 1 CENTRAL_LAYER_Z -> 2; LOWER_LAYER_Z -> 3; else -> throw RuntimeException("Unknown layer"); diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/line.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/line.kt index de704441..c2578783 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/line.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/line.kt @@ -45,7 +45,7 @@ fun makeTrack(start: Vector3D, direction: Vector3D): Line { fun makeTrack(x: Double, y: Double, theta: Double, phi: Double): Line { //TODO check angle definitions return makeTrack( - Vector3D(x, y, CENTRAL_LAYER_Z), + Vector3D(x, y, CENTRAL_LAYER_Z.toDouble()), Vector3D(phi, theta) ) } diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/monitor.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/monitor.kt index a4e83c8b..d2ec7235 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/monitor.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/monitor.kt @@ -13,12 +13,12 @@ import ru.mipt.npm.muon.monitor.readResource internal const val MINIMAL_TRACK_LENGTH = 10.0 -private val layerCache = HashMap() +private val layerCache = HashMap() -fun findLayer(z: Double): Plane = layerCache.getOrPut(z) { +fun findLayer(z: Float): Plane = layerCache.getOrPut(z) { Plane( - Vector3D(0.0, 0.0, z), Vector3D(0.0, 0.0, 1.0), - Monitor.GEOMETRY_TOLERANCE + Vector3D(0.0, 0.0, z.toDouble()), Vector3D(0.0, 0.0, 1.0), + Monitor.GEOMETRY_TOLERANCE.toDouble() ) } diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/simulation.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/simulation.kt index 6019a94a..f8d45fd4 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/simulation.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/simulation.kt @@ -28,8 +28,8 @@ interface TrackGenerator { */ class UniformTrackGenerator( override val rnd: RandomGenerator, - val maxX: Double = 4 * PIXEL_XY_SIZE, - val maxY: Double = 4 * PIXEL_XY_SIZE + val maxX: Float = 4 * PIXEL_XY_SIZE, + val maxY: Float = 4 * PIXEL_XY_SIZE ) : TrackGenerator { override fun generate(): Line { @@ -44,8 +44,8 @@ class UniformTrackGenerator( class FixedAngleGenerator( override val rnd: RandomGenerator, val phi: Double, val theta: Double, - val maxX: Double = 4 * PIXEL_XY_SIZE, - val maxY: Double = 4 * PIXEL_XY_SIZE + val maxX: Float = 4 * PIXEL_XY_SIZE, + val maxY: Float = 4 * PIXEL_XY_SIZE ) : TrackGenerator { override fun generate(): Line { val x = (1 - rnd.nextDouble() * 2.0) * maxX @@ -60,8 +60,8 @@ class FixedAngleGenerator( class Cos2TrackGenerator( override val rnd: RandomGenerator, val power: Double = 2.0, - val maxX: Double = 4 * PIXEL_XY_SIZE, - val maxY: Double = 4 * PIXEL_XY_SIZE + val maxX: Float = 4 * PIXEL_XY_SIZE, + val maxY: Float = 4 * PIXEL_XY_SIZE ) : TrackGenerator { override fun generate(): Line { diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index bfa90827..2385fef1 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -27,7 +27,7 @@ fun VisionLayout.demo(name: String, title: String = name, block: SolidGro } val canvasOptions = Canvas3DOptions { - size{ + size { minSize = 400 } axes { @@ -57,9 +57,8 @@ fun VisionLayout.showcase() { rotationX = PI / 4 color("blue") } - sphereLayer(50,40){ - theta = (PI/2).toFloat() - rotationX = - PI * 3 / 4 + sphereLayer(50, 40, theta = PI / 2) { + rotationX = -PI * 3 / 4 z = 110 color(Colors.pink) } diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt index e3961775..22cac2c6 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge.solid.demo import info.laht.threekt.core.Object3D -import info.laht.threekt.geometries.BoxBufferGeometry +import info.laht.threekt.geometries.BoxGeometry import info.laht.threekt.objects.Mesh import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.number @@ -23,10 +23,10 @@ internal fun SolidGroup.varBox( action: VariableBox.() -> Unit = {}, ): VariableBox = VariableBox(xSize, ySize).apply(action).also { set(name, it) } -internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeVision() { +internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() { override fun render(three: ThreePlugin): Object3D { - val geometry = BoxBufferGeometry(xSize, ySize, 1) + val geometry = BoxGeometry(xSize, ySize, 1) val material = ThreeMaterials.DEFAULT.clone() diff --git a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt index 1ed557ea..3ce92f0f 100644 --- a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt +++ b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt @@ -14,6 +14,7 @@ class FXDemoApp : App(FXDemoGrid::class) { stage.height = 600.0 view.showcase() + view.showcaseCSG() } } diff --git a/docs/images/polyline-points-2.png b/docs/images/polyline-points-2.png new file mode 100644 index 00000000..716ebb18 Binary files /dev/null and b/docs/images/polyline-points-2.png differ diff --git a/docs/images/polyline-points.png b/docs/images/polyline-points.png new file mode 100644 index 00000000..68d5d141 Binary files /dev/null and b/docs/images/polyline-points.png differ diff --git a/docs/tutorial.md b/docs/tutorial.md index 9de1432b..d70d306d 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1,209 +1,373 @@ -#Tutorial - -###The main goal of this tutorial is to show all capabilities of ... (this part will be supplemented) - -The simple visualization can be made with function `main`. (this part will be supplemented as well) -```kotlin -import kotlinx.html.div -import space.kscience.dataforge.context.Context -import space.kscience.visionforge.html.ResourceLocation -import space.kscience.visionforge.solid.* -import java.nio.file.Paths - -fun main(){ - val context = Context{ - plugin(Solids) - } - - context.makeVisionFile ( - Paths.get("customFile.html"), - resourceLocation = ResourceLocation.EMBED - ){ - div { - vision { - solid { - } - } - } - } -} -``` -##Solids properties -**We will analyze which basic properties solids have using `box` solid.** - -Basic properties: -1. `opacity` - It is set in `float`. It takes on values from 0 to 1, which represent percents of solid opacity. It's initial value is 1. -2. `color` - It can be specified as `Int`, `String`, or as three `Ubytes`, which represent color in `rgb`. Elementally, the solid will have `green` color. -3. `rotation` - it's the point, around which the solid will be rotated. Initially, the value is `Point3D(0, 0, 0)` -4. position, which is given by values `x`, `y`, `z`. Initial values are `x = 0`, `y = 0`, `z = 0` - -Let's see how properties are set in solids. -The `small box` will have elemental values of properties. If you will not set properties, it will have the same `position`, `color`, `rotation`, and `opacity` values. - -***You can see that `box` take four values. Later, we will discuss what they are doing in more detail. Now, it does not really matter.*** -```kotlin -box(10, 10, 10, name = "small box"){ - x = 0 - y = 0 - z = 0 - opacity = 1 //100% opacity - color("red") //as string - rotation = Point3D(0, 0, 0) -} -``` -![](../docs/images/small-box.png) - -The `big box` will have properties with custom values. -```kotlin -box(40, 40, 40, name = "big box"){ - x = 20 - y = 10 - z = 60 - opacity = 0.5 //50% opacity - color(0u, 179u, 179u) //color in rgb - rotation = Point3D(60, 80, 0) -} -``` -![](../docs/images/big-rotated-box.png) - -If we compare these boxes, we will see all differences. - -Here is the function `main` with both boxes. -```kotlin -fun main(){ - val context = Context{ - plugin(Solids) - } - - context.makeVisionFile ( - Paths.get("customFile.html"), - resourceLocation = ResourceLocation.EMBED - ){ - div { - vision { - solid { - box(10, 10, 10, name = "small box"){ - x = 0 - y = 0 - z = 0 - opacity = 1 //100% opacity - color("red") //as string - rotation = Point3D(0, 0, 0) - } - box(40, 40, 40, name = "big box"){ - x = 20 - y = 10 - z = 60 - opacity = 0.5 //50% opacity - color(0u, 179u, 179u) //rgb - rotation = Point3D(60, 80, 0) - } - } - } - } - } -} -``` -![](../docs/images/two-boxes-1.png) -![](../docs/images/two-boxes-2.png) - -###Basic Solids - -Now, let's see which solids can be visualized: -1) PolyLine -2) Box - ```kotlin - box(50, 50, 50, name = "box") { - x = 0 - y = 0 - z = 0 - color("pink") - } - ``` - ![](../docs/images/box.png) - ```kotlin - box(10, 25, 10, name = "high_box") { - x = 0 - y = 0 - z = 0 - color("black") - } - ``` - ![](../docs/images/high-box.png) - - ```kotlin - box(65, 40, 40, name = "wide_box") { - x = 0 - y = 0 - z = 0 - color("black") - } - ``` - ![](../docs/images/wide-box.png) - -3) Sphere - ```kotlin - sphere(50, name = "sphere") { - x = 0 - y = 0 - z = 0 - color("blue") - } - ``` - ![](../docs/images/sphere.png) -4) Hexagon - ```kotlin - hexagon( - Point3D(25, 30, 25), - Point3D(35, 30, 25), - Point3D(35, 30, 15), - Point3D(25, 30, 15), - Point3D(30, 18, 20), - Point3D(40, 18, 20), - Point3D(40, 18, 10), - Point3D(30, 18, 10), - name = "classic_hexagon"){ - color("green") - } - ``` - ![](../docs/images/classic-hexagon.png) - ```kotlin - hexagon( - Point3D(5, 30, 5), - Point3D(24, 30, 8), - Point3D(20, 30, -10), - Point3D(5, 30, -7), - Point3D(8, 16, 0), - Point3D(12, 16, 0), - Point3D(10, 16, -5), - Point3D(6.5, 12, -3), - name = "custom_hexagon"){ - color("brown") - } - ``` - ![](../docs/images/custom-hexagon.png) -5) Cone - ```kotlin - cone(60, 80, name = "cone") { - x = 0 - y = 0 - z = 0 - color("beige") - } - ``` - ![](../docs/images/cone-1.png) - ![](../docs/images/cone-2.png) -6) Cone Surface - ```kotlin - coneSurface(60, 50, 30, 10, 100, name = "cone_surface") { - x = 0 - y = 0 - z = 0 - color("red") - rotation = Point3D(2, 50, -9) - } - ``` - ![](../docs/images/cone-surface-1.png) - ![](../docs/images/cone-surface-2.png) -7) Extruded - +# Tutorial + +#### The main goal of this tutorial is to show main capabilities of the visualization instrument. + +The simple visualization can be made with function `main`. (this part will be supplemented) +```kotlin +import kotlinx.html.div +import space.kscience.dataforge.context.Context +import space.kscience.visionforge.html.ResourceLocation +import space.kscience.visionforge.solid.* +import java.nio.file.Paths + +fun main(){ + val context = Context{ + plugin(Solids) + } + + context.makeVisionFile ( + Paths.get("nameFile.html"), + resourceLocation = ResourceLocation.EMBED + ){ + div { + //first vision + vision { + solid { + //solids which you want to visualize + } + } + //second vision + vision { + solid { + //solids which you want to visualize + } + } + } + } +} +``` +## Solids properties +**We will analyze which basic properties solids have using `box` solid.** + +*Basic properties:* +1. `opacity` - It is set in `float`. It takes on values from 0 to 1, which represent percents of solid opacity. It's initial value is 1. +2. `color` - It can be specified as `Int`, `String`, or as three `Ubytes`, which represent color in `rgb`. Elementally, the solid will have `green` color. +3. `rotation` - it's the point, which set rotations along axes. Initially, the value is `Point3D(0, 0, 0)`. Changing `x` coordinate of the point, you make pivot around `x axis`. The same for other coordinates: changing `y` - pivot around `y axis`, changing `z` - pivot around `z axis`. +4. position, which is given by values `x`, `y`, `z`. Initial values are `x = 0`, `y = 0`, `z = 0`. The coordinate system is Cartesian. It's elemental position is this - vertical `y` axis and horizontal `Oxz` plane. + +Let's see how properties are set in solids. +The `small box` will have elemental values of properties. If you don't set properties, it will have the same `position`, `color`, `rotation`, and `opacity` values. + +***You can see that `box` take four values. Later, we will discuss what they do in more detail. Now, it does not really matter.*** +```kotlin +box(10, 10, 10, name = "small box"){ + x = 0 + y = 0 + z = 0 + opacity = 1 //100% opacity + color("red") //as string + rotation = Point3D(0, 0, 0) +} +``` +![](../docs/images/small-box.png) + +The `big box` will have properties with custom values. +```kotlin +box(40, 40, 40, name = "big box"){ + x = 20 + y = 10 + z = 60 + opacity = 0.5 //50% opacity + color(0u, 179u, 179u) //color in rgb + rotation = Point3D(60, 80, 0) +} +``` +![](../docs/images/big-rotated-box.png) +If we compare these boxes, we will see all differences. + +Here is the function `main` with both boxes. +```kotlin +fun main(){ + val context = Context{ + plugin(Solids) + } + + context.makeVisionFile ( + Paths.get("customFile.html"), + resourceLocation = ResourceLocation.EMBED + ){ + div { + vision { + solid { + box(10, 10, 10, name = "small box"){ + x = 0 + y = 0 + z = 0 + opacity = 1 //100% opacity + color("red") //as string + rotation = Point3D(0, 0, 0) + } + box(40, 40, 40, name = "big box"){ + x = 20 + y = 10 + z = 60 + opacity = 0.5 //50% opacity + color(0u, 179u, 179u) //rgb + rotation = Point3D(60, 80, 0) + } + } + } + } + } +} +``` +![](../docs/images/two-boxes-1.png) +![](../docs/images/two-boxes-2.png) + +***There is plenty of other properties, especially of those, which you can create by yourself. Here we mention just small part.*** + +## Basic Solids +Now, let's see which solids can be visualized: +### 1) PolyLine + +It's scarcely a solid, but it can be visualized, so we mention it. +`polyline` build lines, obviously. Let's take a look at it's work. + +`polyline` requires two values - `points`, and `name`: +* `points` is a `vararg` with `Point3D` type. It takes pairs of points, which you want to connect. +* `name` is an identifier of *any solid*, but in this case it is an identifier of `polyline`. +It's type is `String`. **This value can be required by any solid; +you can set it, you can not to set it, but without you won't be able to control solid, since it won't be inherited.** + +This is an example of polyline with other solid `box`: +```kotlin +box(100, 100, 100, name = "box"){ + x = -10 + y = -10 + z = -10 + opacity = 0.4 +} +polyline(Point3D(30, 20, 10), Point3D(30, -100, 30), Point3D(30, -100, 30), Point3D(50, -100, 30), name = "polyline"){ + color("red") +} +``` + +![](../docs/images/polyline-points.png) +![](../docs/images/polyline-points-2.png) + +### 2) Box + +First thing which has to be mentioned is that `box` takes four values: `box(x, y, z, name)` +* `x` - x-axis length of the `box` +* `y` - y-axis length of the `box` +* `z` - z-axis length of the `box` + +These values have `Float` type. + +*`x`, `y`, and `z` are necessary values, which cannot be ignored. You have to set them.* + +* `name` - `box`'es identifier. You've already met it. + +Let's create just usual `box` with equal ribs. + +```kotlin + box(50, 50, 50, name = "box") { + color("pink") + } +``` + ![](../docs/images/box.png) + +Now, let's make `box` with bigger `y` value. + ```kotlin + box(10, 25, 10, name = "high box") { + color("black") + } + ``` +As you can see, only rib of `y-axis` differs from other ribs. + + ![](../docs/images/high-box.png) + +For final trial, let's create `box` with bigger `x` value. + + ```kotlin + box(65, 40, 40, name = "wide box") { + x = 0 + y = 0 + z = 0 + color("black") + } + ``` +Predictably, only `x-axis` rib bigger than other ribs. + + ![](../docs/images/wide-box.png) + +### 3) Sphere + +It takes in two values: `radius`, and `name`. +We bring you to mind that `name` is a general value for all solids, so do not wonder, since all solids need their own identifier. + +As for `radius`, it has `Float` type, and, as you can guess, it sets radius of the sphere, which will be created. + ```kotlin + sphere(50, name = "sphere") { + x = 0 + y = 0 + z = 0 + opacity = 0.9 + color("blue") + } + ``` + ![](../docs/images/sphere.png) + +### 4) Hexagon + +It is solid which has six edges. It is set by eight values: `node1`,..., `node8`. They all have `Point3D` type, so they are just points, vertices. + +*Six edges are these:* +1) Edge with vertices `node1`, `node4`, `node3`, `node2` +2) Edge with vertices `node1`, `node2`, `node6`, `node5` +3) Edge with vertices `node2`, `node3`, `node7`, `node6` +4) Edge with vertices `node4`, `node8`, `node7`, `node3` +5) Edge with vertices `node1`, `node5`, `node8`, `node4` +6) Edge with vertices `node8`, `node5`, `node6`, `node7` + +![](../docs/images/scheme.png) + +As hexagon takes in specific points, we understand that this solid cannot be moved, it fixed in space, and it can't make pivots. + +Let's make classic parallelepiped. +```kotlin + hexagon( + Point3D(25, 30, 25), + Point3D(35, 30, 25), + Point3D(35, 30, 15), + Point3D(25, 30, 15), + Point3D(30, 18, 20), + Point3D(40, 18, 20), + Point3D(40, 18, 10), + Point3D(30, 18, 10), + name = "classic hexagon"){ + color("green") + } +``` + ![](../docs/images/classic-hexagon.png) + +Now, let's make a custom hexagon. + + ```kotlin + hexagon( + Point3D(5, 30, 5), + Point3D(24, 30, 8), + Point3D(20, 30, -10), + Point3D(5, 30, -7), + Point3D(8, 16, 0), + Point3D(12, 16, 0), + Point3D(10, 16, -5), + Point3D(6.5, 12, -3), + name = "custom_hexagon"){ + color("brown") + } + ``` + ![](../docs/images/custom-hexagon.png) +### 3) Cone +It takes in six values: `bottomRadius`, `height`, `upperRadius`, `startAngle`, `angle`, and `name`. + +Obviously, `bottomRadius` is responsible for radius of a bottom base, and `height` sets height of a cone along the `z-axis`. + +As it takes such values as `upperRadius`, `startAngle`, `angle`, `cone` can build not only usual cones, but also cone segments. Initially, `upperRadius` will have `0.0` value, `startAngle` - `0f`, `angle` - `PI2`, so if you don't set them, you'll get just a simple cone. + +Setting `upperRadius`, you make a frustum cone, since it sets a radius of the upper base of a cone. Set `startAngle`, and `angle` let to cut off segments by planes perpendicular to the base. `startAngle` - an angle, starting with which segment will be left, `angle` - an angle of cone, which will be set from `startAngle`. + +Let's build a classic cone: +```kotlin + cone(60, 80, name = "cone") { + color("beige") + } + ``` + ![](../docs/images/cone-1.png) + ![](../docs/images/cone-2.png) + +First of all, we have to try to build a frustum cone: +```kotlin +cone(60, 80, name = "cone") { + color(0u, 40u, 0u) +} +``` +![](../docs/images/frustum-cone.png) + +Now, we need to make a try to build a cone segment: + +```kotlin +cone(60, 80, angle = PI, name = "cone") { + color(0u, 0u, 200u) +} +``` +![](../docs/images/cone-segment-1.png) +![](../docs/images/cone-segment-2.png) + +Finally, the segment of frustum cone is left for a try: +```kotlin +cone(60, 100, 20, PI*3/4, angle = PI/3, name = "cone") { + color(190u, 0u, 0u) +} +``` +![](../docs/images/frustum-cone-segment.png) + +### 4) Cone Surface +This solid is set by seven values:`bottomOuterRadius`, `bottomInnerRadius`, `height`, `topOuterRadius`, `topInnerRadius`, `startAngle`, and `angle`. + +In addition to `height`, `startAngle`, and `angle`, which work as they work in `cone`, there are some new values. +`bottomOuterRadius`, and `bottomInnerRadius` set properties of the bottom circle, `topOuterRadius`, `topInnerRadius` - of the upper circle. They have no initial value, so that means they have to be set. + +Generally, `cone`, and `coneSurface` buildings work in the same way, it's possible to make `coneSurface`'s fragments as in `cone` + +Let's build usual cone surface with almost all properties set: +```kotlin + coneSurface(60, 50, 30, 10, 100, name = "cone surface") { + color("red") + rotation = Point3D(2, 50, -9) + } + ``` +![](../docs/images/cone-surface-1.png) +![](../docs/images/cone-surface-2.png) + +Now, let's create a cone surface and set all it's properties: + +```kotlin +coneSurface(30, 25, 10, 10, 8,0f, pi*3/4, name = "cone surface") { + color("fuchsia") + rotation = Point3D(2, 50, -9) +} +``` +![](../docs/images/cone-surface-fragment.png) +![](../docs/images/cone-surface-fragment-2.png) + +### 5) Cylinder + +This solid is set by `radius`, and `height`. As you can see by accepting values, there's no option of building fragments of cylinders. + +Here's a demonstration of a cylinder: + +```kotlin +cylinder(40, 100, "cylinder"){ + rotation = Point3D(40, 0, 0) + color("indigo") +} +``` +![](../docs/images/cylinder-1.png) +![](../docs/images/cylinder-2.png) +### 6) Tube + +`tube` takes in `radius`, `height`, `innerRadius`, `startAngle`, `angle`, and `name`. *All values are familiar from `cone`, and `coneSurface` solids.* + +Here is an example of classic tube: +```kotlin +tube(50, 40, 20, name = "usual tube"){ + opacity = 0.4 +} +``` +![](../docs/images/tube.png) + +This is an example of tube fragment: + +```kotlin +tube(50, 40, 20, 0f, PI, name = "fragmented tube"){ + color("white") +} +``` +![](../docs/images/tube-fragment.png) +### 7) Extruded + +`extruded` is set by two values: `shape`, and `layer`. +* `shape` is a value of `List` type. It' s just a list of all points of the solid. *`shape` has to consist of not less than two points!* +* `layer` is `MutableList` types variable. (here is a sentence with description of the work of this function). *The amount of `layer`-s has to be more than one* + diff --git a/jupyter/visionforge-gdml-jupyter/build.gradle.kts b/jupyter/visionforge-gdml-jupyter/build.gradle.kts index e2d1db03..896b44a4 100644 --- a/jupyter/visionforge-gdml-jupyter/build.gradle.kts +++ b/jupyter/visionforge-gdml-jupyter/build.gradle.kts @@ -4,9 +4,9 @@ plugins { description = "Jupyter api artifact for GDML rendering" -kotlin{ +kotlin { explicitApi = null - js{ + js { useCommonJs() browser { webpackTask { @@ -25,19 +25,17 @@ kotlin{ tasks.getByName("jvmProcessResources") { dependsOn(jsBrowserDistribution) - afterEvaluate { - from(jsBrowserDistribution) - } + from(jsBrowserDistribution) } } - sourceSets{ + sourceSets { commonMain { dependencies { api(project(":visionforge-solid")) } } - jvmMain{ + jvmMain { dependencies { implementation(project(":visionforge-gdml")) } @@ -52,10 +50,10 @@ kotlin{ } } -kscience{ +kscience { useJupyter() } -readme{ +readme { maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index bc62b31e..6311841e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,8 +16,6 @@ pluginManagement { } } -//enableFeaturePreview("GRADLE_METADATA") - rootProject.name = "visionforge" diff --git a/ui/ring/build.gradle.kts b/ui/ring/build.gradle.kts index 84d33b43..4469b8cf 100644 --- a/ui/ring/build.gradle.kts +++ b/ui/ring/build.gradle.kts @@ -22,7 +22,6 @@ dependencies{ implementation(npm("@jetbrains/icons", "3.14.1")) implementation(npm("@jetbrains/ring-ui", "4.0.7")) - implementation(npm("core-js","3.12.1")) implementation(npm("file-saver", "2.0.2")) compileOnly(npm("url-loader","4.1.1")) compileOnly(npm("postcss-loader","5.2.0")) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index c9b68219..78b75124 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -28,11 +28,10 @@ internal data class PropertyListener( */ @Serializable @SerialName("vision") -public open class VisionBase : Vision { +public open class VisionBase( + override @Transient var parent: VisionGroup? = null, protected var properties: Config? = null - - @Transient - override var parent: VisionGroup? = null +) : Vision { @Synchronized protected fun getOrCreateProperties(): Config { @@ -75,9 +74,12 @@ public open class VisionBase : Vision { } override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - getOrCreateProperties().setItem(name, item) - if (notify) { - invalidateProperty(name) + val oldItem = properties?.getItem(name) + if(oldItem!= item) { + getOrCreateProperties().setItem(name, item) + if (notify) { + invalidateProperty(name) + } } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index 85b147ab..06ccb7bc 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -65,14 +65,14 @@ private fun Vision.isolate(manager: VisionManager): Vision { } /** - * @param void flag showing that this vision child should be removed + * @param delete flag showing that this vision child should be removed * @param vision a new value for vision content * @param properties updated properties - * @param children a map of children changed in ths [VisionChange]. If a child to be removed, set [void] flag to true. + * @param children a map of children changed in ths [VisionChange]. If a child to be removed, set [delete] flag to true. */ @Serializable public data class VisionChange( - public val void: Boolean = false, + public val delete: Boolean = false, public val vision: Vision? = null, @Serializable(MetaSerializer::class) public val properties: Meta? = null, public val children: Map? = null, diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index 4f16982b..6b35d971 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -58,6 +58,7 @@ public operator fun VisionGroup.iterator(): Iterator = children.values.i public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty() public interface VisionContainerBuilder { + //TODO add documentation public operator fun set(name: Name?, child: V?) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt index 44800673..933b55cd 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt @@ -17,7 +17,7 @@ import space.kscience.dataforge.names.* @Serializable @SerialName("vision.group") public open class VisionGroupBase( - @SerialName("children") internal val childrenInternal: MutableMap = LinkedHashMap(), + @SerialName("children") protected val childrenInternal: MutableMap = LinkedHashMap(), ) : VisionBase(), MutableVisionGroup { /** @@ -134,7 +134,7 @@ public open class VisionGroupBase( override fun update(change: VisionChange) { change.children?.forEach { (name, change) -> when { - change.void -> set(name, null) + change.delete -> set(name, null) change.vision != null -> set(name, change.vision) else -> get(name)?.update(change) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt index b3e9faf4..4f01e839 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt @@ -39,6 +39,8 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) { public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(visionSerializer, string) public fun encodeToString(vision: Vision): String = jsonFormat.encodeToString(visionSerializer, vision) + public fun encodeToString(change: VisionChange): String = + jsonFormat.encodeToString(VisionChange.serializer(), change) public fun decodeFromJson(json: JsonElement): Vision = jsonFormat.decodeFromJsonElement(visionSerializer, json) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt index 68567ee3..112295c9 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt @@ -1,12 +1,15 @@ package space.kscience.visionforge +import space.kscience.dataforge.meta.Config import space.kscience.dataforge.meta.MetaItem +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.set import space.kscience.dataforge.names.Name /** * Property containers are used to create a symmetric behaviors for vision properties and style builders */ -public interface VisionPropertyContainer { +public interface VisionPropertyContainer { public fun getProperty( name: Name, inherit: Boolean = false, @@ -15,4 +18,18 @@ public interface VisionPropertyContainer { ): MetaItem? public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true) +} + +public open class SimpleVisionPropertyContainer(protected val config: Config): VisionPropertyContainer{ + override fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean + ): MetaItem? = config[name] + + override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { + config[name] = item + } + } \ No newline at end of file diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt index 98803115..017c615d 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -2,6 +2,9 @@ package space.kscience.visionforge import kotlinx.browser.document import kotlinx.browser.window +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.w3c.dom.* import org.w3c.dom.url.URL import space.kscience.dataforge.context.* @@ -13,14 +16,14 @@ import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_CONNEC import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_FETCH_ATTRIBUTE import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_NAME_ATTRIBUTE -import kotlin.collections.set import kotlin.reflect.KClass +import kotlin.time.Duration public class VisionClient : AbstractPlugin() { override val tag: PluginTag get() = Companion.tag private val visionManager: VisionManager by require(VisionManager) - private val visionMap = HashMap() + //private val visionMap = HashMap() /** * Up-going tree traversal in search for endpoint attribute @@ -53,11 +56,73 @@ public class VisionClient : AbstractPlugin() { private fun Element.getFlag(attribute: String): Boolean = attributes[attribute]?.value != null - private fun renderVision(element: Element, vision: Vision?, outputMeta: Meta) { + private fun renderVision(name: String, element: Element, vision: Vision?, outputMeta: Meta) { if (vision != null) { - visionMap[element] = vision val renderer = findRendererFor(vision) ?: error("Could nof find renderer for $vision") renderer.render(element, vision, outputMeta) + + element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { attr -> + val wsUrl = if (attr.value.isBlank() || attr.value == "auto") { + val endpoint = resolveEndpoint(element) + logger.info { "Vision server is resolved to $endpoint" } + URL(endpoint).apply { + pathname += "/ws" + } + } else { + URL(attr.value) + }.apply { + protocol = "ws" + searchParams.append("name", name) + } + + logger.info { "Updating vision data from $wsUrl" } + + //Individual websocket for this element + WebSocket(wsUrl.toString()).apply { + onmessage = { messageEvent -> + val stringData: String? = messageEvent.data as? String + if (stringData != null) { + val change: VisionChange = visionManager.jsonFormat.decodeFromString( + VisionChange.serializer(), + stringData + ) + + if (change.vision != null) { + renderer.render(element, vision, outputMeta) + } + + logger.debug { "Got update $change for output with name $name" } + vision.update(change) + } else { + console.error("WebSocket message data is not a string") + } + } + + + //Backward change propagation + var feedbackJob: Job? = null + + onopen = { + feedbackJob = vision.flowChanges( + visionManager, + Duration.Companion.milliseconds(300) + ).onEach { change -> + send(visionManager.encodeToString(change)) + }.launchIn(visionManager.context) + + console.info("WebSocket update channel established for output '$name'") + } + + onclose = { + feedbackJob?.cancel() + console.info("WebSocket update channel closed for output '$name'") + } + onerror = { + feedbackJob?.cancel() + console.error("WebSocket update channel error for output '$name'") + } + } + } } } @@ -79,85 +144,39 @@ public class VisionClient : AbstractPlugin() { visionManager.decodeFromString(it) } - if (embeddedVision != null) { - logger.info { "Found embedded vision for output with name $name" } - renderVision(element, embeddedVision, outputMeta) - } - - element.attributes[OUTPUT_FETCH_ATTRIBUTE]?.let { attr -> - - val fetchUrl = if (attr.value.isBlank() || attr.value == "auto") { - val endpoint = resolveEndpoint(element) - logger.info { "Vision server is resolved to $endpoint" } - URL(endpoint).apply { - pathname += "/vision" - } - } else { - URL(attr.value) - }.apply { - searchParams.append("name", name) + when { + embeddedVision != null -> { + logger.info { "Found embedded vision for output with name $name" } + renderVision(name, element, embeddedVision, outputMeta) } + element.attributes[OUTPUT_FETCH_ATTRIBUTE] != null -> { + val attr = element.attributes[OUTPUT_FETCH_ATTRIBUTE]!! - logger.info { "Fetching vision data from $fetchUrl" } - window.fetch(fetchUrl).then { response -> - if (response.ok) { - response.text().then { text -> - val vision = visionManager.decodeFromString(text) - renderVision(element, vision, outputMeta) + val fetchUrl = if (attr.value.isBlank() || attr.value == "auto") { + val endpoint = resolveEndpoint(element) + logger.info { "Vision server is resolved to $endpoint" } + URL(endpoint).apply { + pathname += "/vision" } } else { - logger.error { "Failed to fetch initial vision state from $fetchUrl" } + URL(attr.value) + }.apply { + searchParams.append("name", name) } - } - } - - element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { attr -> - val wsUrl = if (attr.value.isBlank() || attr.value == "auto") { - val endpoint = resolveEndpoint(element) - logger.info { "Vision server is resolved to $endpoint" } - URL(endpoint).apply { - pathname += "/ws" - } - } else { - URL(attr.value) - }.apply { - protocol = "ws" - searchParams.append("name", name) - } - - logger.info { "Updating vision data from $wsUrl" } - - WebSocket(wsUrl.toString()).apply { - onmessage = { messageEvent -> - val stringData: String? = messageEvent.data as? String - if (stringData != null) { - val change = visionManager.jsonFormat.decodeFromString( - VisionChange.serializer(), - stringData - ) - - if (change.vision != null) { - renderVision(element, change.vision, outputMeta) + logger.info { "Fetching vision data from $fetchUrl" } + window.fetch(fetchUrl).then { response -> + if (response.ok) { + response.text().then { text -> + val vision = visionManager.decodeFromString(text) + renderVision(name, element, vision, outputMeta) } - - logger.debug { "Got update $change for output with name $name" } - visionMap[element]?.update(change) - ?: console.info("Target vision for element $element with name $name not found") } else { - console.error("WebSocket message data is not a string") + logger.error { "Failed to fetch initial vision state from $fetchUrl" } } } - onopen = { - console.info("WebSocket update channel established for output '$name'") - } - onclose = { - console.info("WebSocket update channel closed for output '$name'") - } - onerror = { - console.error("WebSocket update channel error for output '$name'") - } } + else -> error("No embedded vision data / fetch url for $name") } } diff --git a/visionforge-fx/build.gradle.kts b/visionforge-fx/build.gradle.kts index 391517d7..09adb66a 100644 --- a/visionforge-fx/build.gradle.kts +++ b/visionforge-fx/build.gradle.kts @@ -22,7 +22,7 @@ dependencies { exclude(group = "org.openjfx") } - api("org.fxyz3d:fxyz3d:0.5.2") { + api("org.fxyz3d:fxyz3d:0.5.4") { exclude(module = "slf4j-simple") } api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}") diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt index ea55fc83..e7beac2b 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt @@ -15,7 +15,7 @@ import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.* import tornadofx.* -class TextValueChooser : ValueChooserBase() { +public class TextValueChooser : ValueChooserBase() { private val displayText: String get() = currentValue().let { diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt index c3796fa0..8816a250 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt @@ -14,7 +14,6 @@ import space.kscience.dataforge.meta.descriptors.ValueDescriptor import space.kscience.dataforge.misc.Named import space.kscience.dataforge.misc.Type import space.kscience.dataforge.names.toName -import space.kscience.dataforge.provider.provideByType import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Value import space.kscience.visionforge.widget @@ -63,7 +62,7 @@ public interface ValueChooser { public fun setCallback(callback: ValueCallback) - @Type("space.kscience.dataforge.vis.fx.valueChooserFactory") + @Type("space.kscience..fx.valueChooserFactory") public interface Factory : Named { public operator fun invoke(meta: Meta = Meta.EMPTY): ValueChooser } @@ -75,7 +74,7 @@ public interface ValueChooser { TextValueChooser.name -> TextValueChooser ColorValueChooser.name -> ColorValueChooser ComboBoxValueChooser.name -> ComboBoxValueChooser - else -> context.provideByType(type)//Search for additional factories in the plugin + else -> null//context.provideByType(type)//Search for additional factories in the plugin } } @@ -101,7 +100,7 @@ public interface ValueChooser { } } - fun build( + public fun build( context: Context, value: ObservableValue, descriptor: ValueDescriptor? = null, diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt index 81dc42ac..98790f96 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt @@ -18,16 +18,18 @@ import tornadofx.* * * @author Alexander Nozik */ -abstract class ValueChooserBase : ValueChooser { +public abstract class ValueChooserBase : ValueChooser { - override val node by lazy { buildNode() } - final override val valueProperty = SimpleObjectProperty(Null) - final override val descriptorProperty = SimpleObjectProperty() + override val node: T by lazy { buildNode() } + final override val valueProperty: SimpleObjectProperty = + SimpleObjectProperty(Null) + final override val descriptorProperty: SimpleObjectProperty = + SimpleObjectProperty() override var descriptor: ValueDescriptor? by descriptorProperty override var value: Value? by valueProperty - fun resetValue() { + public fun resetValue() { setDisplayValue(currentValue()) } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt index 929c7e85..a918a3a5 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt @@ -13,13 +13,13 @@ import space.kscience.dataforge.meta.update import space.kscience.visionforge.* import tornadofx.* -class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { +public class VisualObjectEditorFragment(public val selector: (Vision) -> Meta) : Fragment() { - val itemProperty = SimpleObjectProperty() - var item: Vision? by itemProperty - val descriptorProperty = SimpleObjectProperty() + public val itemProperty: SimpleObjectProperty = SimpleObjectProperty() + public var item: Vision? by itemProperty + public val descriptorProperty: SimpleObjectProperty = SimpleObjectProperty() - constructor( + public constructor( item: Vision?, descriptor: NodeDescriptor?, selector: (Vision) -> MutableItemProvider = { it.allProperties() }, @@ -30,13 +30,13 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { private var currentConfig: Config? = null - private val configProperty: Binding = itemProperty.objectBinding { visualObject -> - if (visualObject == null) return@objectBinding null - val meta = selector(visualObject) + private val configProperty: Binding = itemProperty.objectBinding { vision -> + if (vision == null) return@objectBinding null + val meta = selector(vision) val config = Config().apply { update(meta) onChange(this@VisualObjectEditorFragment) { key, _, after -> - visualObject.setProperty(key, after) + vision.setProperty(key, after) } } //remember old config reference to cleanup listeners @@ -51,7 +51,7 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { } } - private val styleBoxProperty: Binding = configProperty.objectBinding() { + private val styleBoxProperty: Binding = configProperty.objectBinding { VBox().apply { item?.styles?.forEach { styleName -> val styleMeta = item?.getStyle(styleName) diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectTreeFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectTreeFragment.kt index f1faaf1a..2fa6cee1 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectTreeFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectTreeFragment.kt @@ -3,6 +3,7 @@ package space.kscience.visionforge.editor import javafx.beans.property.SimpleObjectProperty import javafx.scene.control.SelectionMode import javafx.scene.control.TreeItem +import javafx.scene.layout.VBox import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionGroup import tornadofx.* @@ -29,13 +30,13 @@ private fun toTreeItem(vision: Vision, title: String): TreeItem() - var item: Vision? by itemProperty +public class VisualObjectTreeFragment : Fragment() { + public val itemProperty: SimpleObjectProperty = SimpleObjectProperty() + public var item: Vision? by itemProperty - val selectedProperty = SimpleObjectProperty() + public val selectedProperty: SimpleObjectProperty = SimpleObjectProperty() - override val root = vbox { + override val root: VBox = vbox { titledpane("Object tree", collapsible = false) { treeview> { cellFormat { @@ -47,7 +48,9 @@ class VisualObjectTreeFragment : Fragment() { } } selectionModel.selectionMode = SelectionMode.SINGLE - val selectedValue = selectionModel.selectedItemProperty().objectBinding { it?.value?.second } + val selectedValue = selectionModel.selectedItemProperty().objectBinding { + it?.value?.second + } selectedProperty.bind(selectedValue) } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt index 6bd56fcf..d46bab32 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt @@ -23,7 +23,7 @@ import kotlin.collections.set import kotlin.math.PI import kotlin.reflect.KClass -class FX3DPlugin : AbstractPlugin() { +public class FX3DPlugin : AbstractPlugin() { override val tag: PluginTag get() = Companion.tag private val objectFactories = HashMap, FX3DFactory<*>>() @@ -42,7 +42,7 @@ class FX3DPlugin : AbstractPlugin() { as FX3DFactory? } - fun buildNode(obj: Solid): Node { + public fun buildNode(obj: Solid): Node { val binding = VisualObjectFXBinding(this, obj) return when (obj) { is SolidReferenceGroup -> referenceFactory(obj, binding) @@ -65,6 +65,8 @@ class FX3DPlugin : AbstractPlugin() { } is SolidLabel -> Text(obj.text).apply { font = Font.font(obj.fontFamily, obj.fontSize) + transforms.add(Rotate(180.0, Rotate.Y_AXIS)) + transforms.add(Rotate(180.0, Rotate.Z_AXIS)) x = -layoutBounds.width / 2 y = layoutBounds.height / 2 } @@ -129,10 +131,10 @@ class FX3DPlugin : AbstractPlugin() { } } - companion object : PluginFactory { - override val tag = PluginTag("vision.fx3D", PluginTag.DATAFORGE_GROUP) - override val type = FX3DPlugin::class - override fun invoke(meta: Meta, context: Context) = FX3DPlugin() + public companion object : PluginFactory { + override val tag: PluginTag = PluginTag("vision.fx3D", PluginTag.DATAFORGE_GROUP) + override val type: KClass = FX3DPlugin::class + override fun invoke(meta: Meta, context: Context): FX3DPlugin = FX3DPlugin() } } @@ -140,14 +142,14 @@ class FX3DPlugin : AbstractPlugin() { * Builder and updater for three.js object */ @Type(TYPE) -interface FX3DFactory { +public interface FX3DFactory { - val type: KClass + public val type: KClass - operator fun invoke(obj: T, binding: VisualObjectFXBinding): Node + public operator fun invoke(obj: T, binding: VisualObjectFXBinding): Node - companion object { - const val TYPE = "fx3DFactory" + public companion object { + public const val TYPE: String = "fx3DFactory" } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCanvas3D.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCanvas3D.kt index 171bc5a3..a05c844a 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCanvas3D.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCanvas3D.kt @@ -4,6 +4,7 @@ import javafx.application.Platform import javafx.beans.property.ObjectProperty import javafx.beans.property.SimpleObjectProperty import javafx.scene.* +import javafx.scene.layout.BorderPane import javafx.scene.paint.Color import org.fxyz3d.scene.Axes import space.kscience.dataforge.context.Context @@ -11,31 +12,32 @@ import space.kscience.dataforge.context.ContextAware import space.kscience.visionforge.solid.specifications.Canvas3DOptions import tornadofx.* -class FXCanvas3D( - val plugin: FX3DPlugin, - val spec: Canvas3DOptions = Canvas3DOptions.empty(), +public class FXCanvas3D( + public val fx3d: FX3DPlugin, + public val options: Canvas3DOptions = Canvas3DOptions.empty(), ) : Fragment(), ContextAware { - override val context: Context get() = plugin.context + override val context: Context get() = fx3d.context - val world = Group().apply { + public val world: Group = Group().apply { //transforms.add(Rotate(180.0, Rotate.Z_AXIS)) } - val axes = Axes().also { - it.setHeight(spec.axes.size) - it.setRadius(spec.axes.width) - it.isVisible = spec.axes.visible + public val axes: Axes = Axes().also { + it.setHeight(options.axes.size) + it.setRadius(options.axes.width) + it.isVisible = options.axes.visible world.add(it) } - val light = AmbientLight() + public val light: AmbientLight = AmbientLight() private val camera = PerspectiveCamera().apply { - nearClip = spec.camera.nearClip - farClip = spec.camera.farClip - fieldOfView = spec.camera.fov.toDouble() - this.add(light) + nearClip = options.camera.nearClip + farClip = options.camera.farClip + fieldOfView = options.camera.fov.toDouble() + + add(light) } private val canvas = SubScene( @@ -49,19 +51,19 @@ class FXCanvas3D( scene.camera = camera } - override val root = borderpane { + override val root: BorderPane = borderpane { center = canvas } - val controls = camera.orbitControls(canvas, spec.camera).also { + public val controls: OrbitControls = camera.orbitControls(canvas, options.camera).also { world.add(it.centerMarker) } - val rootObjectProperty: ObjectProperty = SimpleObjectProperty() - var rootObject: Solid? by rootObjectProperty + public val rootObjectProperty: ObjectProperty = SimpleObjectProperty() + public var rootObject: Solid? by rootObjectProperty private val rootNodeProperty = rootObjectProperty.objectBinding { - it?.let { plugin.buildNode(it) } + it?.let { fx3d.buildNode(it) } } init { @@ -79,7 +81,7 @@ class FXCanvas3D( } } - fun render(vision: Solid) { + public fun render(vision: Solid) { rootObject = vision } } \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt index 5aa7619e..1cdcf914 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt @@ -38,7 +38,7 @@ private fun MeshView.toCSG(): CSG { return CSG.fromPolygons(polygons) } -class FXCompositeFactory(val plugin: FX3DPlugin) : FX3DFactory { +public class FXCompositeFactory(public val plugin: FX3DPlugin) : FX3DFactory { override val type: KClass get() = Composite::class @@ -48,7 +48,7 @@ class FXCompositeFactory(val plugin: FX3DPlugin) : FX3DFactory { val firstCSG = first.toCSG() val secondCSG = second.toCSG() val resultCSG = when (obj.compositeType) { - CompositeType.UNION -> firstCSG.union(secondCSG) + CompositeType.SUM, CompositeType.UNION -> firstCSG.union(secondCSG) CompositeType.INTERSECT -> firstCSG.intersect(secondCSG) CompositeType.SUBTRACT -> firstCSG.difference(secondCSG) } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt index f2bbba54..7bc44207 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt @@ -7,11 +7,14 @@ import javafx.scene.Node import kotlin.reflect.KClass -object FXConvexFactory : FX3DFactory { +public object FXConvexFactory : FX3DFactory { override val type: KClass get() = Convex::class override fun invoke(obj: Convex, binding: VisualObjectFXBinding): Node { - val hull = HullUtil.hull(obj.points.map { Vector3d.xyz(it.x, it.y, it.z) }, PropertyStorage()) + val hull = HullUtil.hull( + obj.points.map { Vector3d.xyz(it.x.toDouble(), it.y.toDouble(), it.z.toDouble()) }, + PropertyStorage() + ) return hull.toNode() } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt index dc72f7f1..022900df 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt @@ -7,7 +7,7 @@ import space.kscience.visionforge.Vision import space.kscience.visionforge.onPropertyChange import kotlin.reflect.KClass -class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory { +public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory { override val type: KClass get() = SolidReferenceGroup::class override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node { diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt index ad88aac0..116075ce 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt @@ -8,7 +8,7 @@ import org.fxyz3d.geometry.Face3 import space.kscience.dataforge.meta.Meta import kotlin.reflect.KClass -object FXShapeFactory : FX3DFactory { +public object FXShapeFactory : FX3DFactory { override val type: KClass get() = GeometrySolid::class override fun invoke(obj: GeometrySolid, binding: VisualObjectFXBinding): MeshView { diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/OrbitControls.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/OrbitControls.kt index 47c3c8e8..2fe573e0 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/OrbitControls.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/OrbitControls.kt @@ -1,127 +1,110 @@ package space.kscience.visionforge.solid -import javafx.beans.InvalidationListener +import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleDoubleProperty import javafx.event.EventHandler -import javafx.geometry.Point3D import javafx.scene.Camera import javafx.scene.Node import javafx.scene.SubScene -import javafx.scene.input.MouseEvent -import javafx.scene.input.ScrollEvent import javafx.scene.shape.Sphere import javafx.scene.transform.Rotate +import javafx.scene.transform.Rotate.X_AXIS +import javafx.scene.transform.Rotate.Y_AXIS import javafx.scene.transform.Translate +import space.kscience.dataforge.meta.useProperty import tornadofx.* -import kotlin.math.* +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.max +import kotlin.math.sin import space.kscience.visionforge.solid.specifications.Camera as CameraSpec +public class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: CameraSpec) { -class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: CameraSpec) { + /** + * Azimuth angle in radians + */ + public val azimuthProperty: SimpleDoubleProperty = SimpleDoubleProperty().apply { + spec.useProperty(CameraSpec::azimuth){ + set(spec.azimuth) + } + } + public var azimuth: Double by azimuthProperty - val distanceProperty = SimpleDoubleProperty(spec.distance) - var distance by distanceProperty + /** + * Zenith angle in radians + */ + public val zenithProperty: SimpleDoubleProperty = SimpleDoubleProperty().apply { + spec.useProperty(CameraSpec::latitude){ + set(PI / 2 - spec.latitude) + } + } - val azimuthProperty = SimpleDoubleProperty(spec.azimuth) - var azimuth by azimuthProperty + public var zenith: Double by zenithProperty - val zenithProperty = SimpleDoubleProperty(PI / 2 - spec.latitude) - var zenith by zenithProperty - val latitudeProperty = zenithProperty.unaryMinus().plus(PI / 2) - val latitude by latitudeProperty + private val baseTranslate = Translate(0.0, 0.0, 0.0) - val baseXProperty = SimpleDoubleProperty(0.0) - var x by baseXProperty - val baseYProperty = SimpleDoubleProperty(0.0) - var y by baseYProperty - val baseZProperty = SimpleDoubleProperty(0.0) - var z by baseZProperty + public var x: Double by baseTranslate.xProperty() + public var y: Double by baseTranslate.yProperty() + public var z: Double by baseTranslate.zProperty() - private val baseTranslate = Translate() + private val distanceProperty = SimpleDoubleProperty().apply { + spec.useProperty(CameraSpec::distance) { + set(it) + } + } -// val basePositionProperty: ObjectBinding = -// nonNullObjectBinding(baseXProperty, baseYProperty, baseZProperty) { -// Point3D(x, y, z) -// } -// -// val basePosition by basePositionProperty + private val distanceTranslation = Translate().apply { + zProperty().bind(-distanceProperty) + } - val centerMarker by lazy { + public var distance: Double by distanceProperty + + private val centering = Translate().apply { + xProperty().bind(-canvas.widthProperty() / 2) + yProperty().bind(-canvas.heightProperty() / 2) + } + + private val yUpRotation = Rotate(180.0, X_AXIS) + + private val azimuthRotation = Rotate().apply { + axis = Y_AXIS + angleProperty().bind(azimuthProperty * (180.0 / PI)) + } + + private val zenithRotation = Rotate().apply { + axisProperty().bind(objectBinding(azimuthProperty) { + azimuthRotation.inverseTransform(X_AXIS) + }) + angleProperty().bind(-zenithProperty * (180.0 / PI)) + } + + private val inProgressProperty = SimpleBooleanProperty(false) + + + public val centerMarker: Node by lazy { Sphere(10.0).also { it.transforms.setAll(baseTranslate) + it.visibleProperty().bind(inProgressProperty) } } - //private val center = Translate() - - private val rx = Rotate(0.0, Rotate.X_AXIS) - - private val ry = Rotate(0.0, Rotate.Y_AXIS) - - private val translate = Translate() - - //private val rz = Rotate(180.0, Rotate.Z_AXIS) - - init { - camera.transforms.setAll(ry, rx, translate) - update() - val listener = InvalidationListener { - update() - } - distanceProperty.addListener(listener) - azimuthProperty.addListener(listener) - zenithProperty.addListener(listener) - baseXProperty.addListener(listener) - baseYProperty.addListener(listener) - baseZProperty.addListener(listener) + camera.transforms.setAll( + baseTranslate, + yUpRotation, + azimuthRotation, + zenithRotation, + distanceTranslation, + centering, + ) canvas.apply { -// center.xProperty().bind(widthProperty().divide(2)) -// center.zProperty().bind(heightProperty().divide(2)) handleMouse() } -// coordinateContainer?.vbox { -// label(distanceProperty.asString()) -// label(azimuthProperty.asString()) -// label(zenithProperty.asString()) -// } } - private fun update() { - val spherePosition = Point3D( - sin(zenith) * sin(azimuth), - cos(zenith), - sin(zenith) * cos(azimuth) - ).times(distance) - val basePosition = Point3D(x, y, z) - baseTranslate.x = x - baseTranslate.y = y - baseTranslate.z = z - //Create direction vector - val cameraPosition = basePosition + spherePosition - val camDirection: Point3D = (-spherePosition).normalize() - - val xRotation = Math.toDegrees(asin(-camDirection.y)) - val yRotation = Math.toDegrees(atan2(camDirection.x, camDirection.z)) - - rx.pivotX = cameraPosition.x - rx.pivotY = cameraPosition.y - rx.pivotZ = cameraPosition.z - rx.angle = xRotation - - ry.pivotX = cameraPosition.x - ry.pivotY = cameraPosition.y - ry.pivotZ = cameraPosition.z - ry.angle = yRotation - - translate.x = cameraPosition.x - translate.y = cameraPosition.y - translate.z = cameraPosition.z - } - - private fun Node.handleMouse() { var mousePosX = 0.0 @@ -131,20 +114,21 @@ class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: var mouseDeltaX: Double var mouseDeltaY: Double - onMousePressed = EventHandler { me -> + onMousePressed = EventHandler { me -> mousePosX = me.sceneX mousePosY = me.sceneY mouseOldX = me.sceneX mouseOldY = me.sceneY + inProgressProperty.set(true) } - onMouseDragged = EventHandler { me -> + onMouseDragged = EventHandler { me -> mouseOldX = mousePosX mouseOldY = mousePosY mousePosX = me.sceneX mousePosY = me.sceneY - mouseDeltaX = mousePosX - mouseOldX - mouseDeltaY = mousePosY - mouseOldY + mouseDeltaX = mouseOldX - mousePosX + mouseDeltaY = mouseOldY - mousePosY val modifier = when { me.isControlDown -> CONTROL_MULTIPLIER @@ -153,19 +137,24 @@ class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: } if (me.isPrimaryButtonDown) { - azimuth = (azimuth - mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED).coerceIn(0.0, 2 * PI) - zenith = (zenith - mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED).coerceIn(0.0,PI) + azimuth = (azimuth - mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED) + zenith = (zenith - mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED).coerceIn(-PI/2, PI/2) } else if (me.isSecondaryButtonDown) { - x += MOUSE_SPEED * modifier * TRACK_SPEED * (mouseDeltaX * cos(azimuth) + mouseDeltaY * sin(azimuth)) - z += MOUSE_SPEED * modifier * TRACK_SPEED * (-mouseDeltaX * sin(azimuth) + mouseDeltaY * cos(azimuth)) + x += MOUSE_SPEED * modifier * TRACK_SPEED * (mouseDeltaX * cos(azimuth) - mouseDeltaY * sin(azimuth)) + z += MOUSE_SPEED * modifier * TRACK_SPEED * ( mouseDeltaX * sin(azimuth) + mouseDeltaY * cos(azimuth)) } } - onScroll = EventHandler { event -> + + onMouseReleased = EventHandler { + inProgressProperty.set(false) + } + + onScroll = EventHandler { event -> distance = max(1.0, distance - MOUSE_SPEED * event.deltaY * RESIZE_SPEED) } } - companion object { + public companion object { private const val CONTROL_MULTIPLIER = 0.1 private const val SHIFT_MULTIPLIER = 10.0 private const val MOUSE_SPEED = 0.1 @@ -175,5 +164,5 @@ class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: } } -fun Camera.orbitControls(canvas: SubScene, spec: CameraSpec) = - OrbitControls(this, canvas, spec) \ No newline at end of file +public fun Camera.orbitControls(canvas: SubScene, spec: CameraSpec): OrbitControls = + OrbitControls(this, canvas, spec) diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt index 5045a375..a2f41fbe 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt @@ -14,7 +14,7 @@ import tornadofx.* /** * A caching binding collection for [Vision] properties */ -public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vision) { +public class VisualObjectFXBinding(private val fx: FX3DPlugin, public val obj: Vision) { private val bindings = HashMap>() init { @@ -33,15 +33,13 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi } } - public operator fun get(key: Name): ObjectBinding { - return bindings.getOrPut(key) { - object : ObjectBinding() { - override fun computeValue(): MetaItem? = obj.getProperty(key) - } + public operator fun get(key: Name): ObjectBinding = bindings.getOrPut(key) { + object : ObjectBinding() { + override fun computeValue(): MetaItem? = obj.getProperty(key) } } - public operator fun get(key: String) = get(key.toName()) + public operator fun get(key: String): ObjectBinding?> = get(key.toName()) } public fun ObjectBinding.value(): Binding = objectBinding { it.value } diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlOptimizer.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlOptimizer.kt deleted file mode 100644 index a35e845c..00000000 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlOptimizer.kt +++ /dev/null @@ -1,83 +0,0 @@ -package space.kscience.visionforge.gdml - -import space.kscience.dataforge.meta.itemSequence -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.visionforge.Vision -import space.kscience.visionforge.meta -import space.kscience.visionforge.solid.* - -public expect class Counter() { - public fun get(): Int - public fun incrementAndGet(): Int -} - -private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && other == null) { - null -} else { - (this ?: Point3D(0, 0, 0)) + (other ?: Point3D(0, 0, 0)) -} - -@DFExperimental -internal fun Vision.updateFrom(other: Vision): Vision { - if (this is Solid && other is Solid) { - position = position.safePlus(other.position) - rotation = rotation.safePlus(other.rotation) - if (this.scale != null || other.scale != null) { - scaleX = scaleX.toDouble() * other.scaleX.toDouble() - scaleY = scaleY.toDouble() * other.scaleY.toDouble() - scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble() - } - other.meta.itemSequence().forEach { (name, item) -> - if (getProperty(name) == null) { - setProperty(name, item) - } - } - } - return this -} -// -//@DFExperimental -//private class GdmlOptimizer() : VisionVisitor { -// val logger = KotlinLogging.logger("SingleChildReducer") -// -// private val depthCount = HashMap() -// -// override suspend fun visit(name: Name, vision: Vision) { -// val depth = name.length -// depthCount.getOrPut(depth) { Counter() }.incrementAndGet() -// } -// -// override fun skip(name: Name, vision: Vision): Boolean = vision is Proxy.ProxyChild -// -// override suspend fun visitChildren(name: Name, group: VisionGroup) { -// if (name == "volumes".toName()) return -// if (group !is MutableVisionGroup) return -// -// val newChildren = group.children.entries.associate { (visionToken, vision) -> -// //Reduce single child groups -// if (vision is VisionGroup && vision !is Proxy && vision.children.size == 1) { -// val (token, child) = vision.children.entries.first() -// child.parent = null -// if (token != visionToken) { -// child.config["solidName"] = token.toString() -// } -// visionToken to child.updateFrom(vision) -// } else { -// visionToken to vision -// } -// } -// if (newChildren != group.children) { -// group.removeAll() -// newChildren.forEach { (token, child) -> -// group[token] = child -// } -// } -// } -//} -// -//@DFExperimental -//suspend fun SolidGroup.optimizeGdml(): Job = coroutineScope { -// prototypes?.let { -// VisionVisitor.visitTree(GdmlOptimizer(), this, it) -// } ?: CompletableDeferred(Unit) -//} \ No newline at end of file diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt similarity index 90% rename from visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt rename to visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt index ef41a214..98b4509e 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt @@ -41,7 +41,7 @@ public class GdmlTransformer { internal val styleCache = HashMap() - public fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) { + public fun Solid.registerAndUseStyle(name: String, builder: MetaBuilder.() -> Unit) { styleCache.getOrPut(name.toName()) { Meta(builder) } @@ -49,7 +49,7 @@ public class GdmlTransformer { } public fun Solid.transparent() { - useStyle("transparent") { + registerAndUseStyle("transparent") { SolidMaterial.MATERIAL_OPACITY_KEY put 0.3 "edges.enabled" put true } @@ -75,7 +75,7 @@ public class GdmlTransformer { if (parent.physVolumes.isNotEmpty()) transparent() - useStyle(styleName) { + registerAndUseStyle(styleName) { val vfMaterial = SolidMaterial().apply { configurePaint(material, solid) } @@ -125,7 +125,11 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) { val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref) - settings.run { configureSolid(parent, solid, material) } + with(settings) { + with(this@configureSolid) { + configureSolid(parent, solid, material) + } + } } private fun proxySolid(root: Gdml, group: SolidGroup, solid: GdmlSolid, name: String): SolidReferenceGroup { @@ -159,25 +163,26 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { newScale: GdmlScale? = null, ): T = apply { newPos?.let { - val point = Point3D(it.x(settings.lUnit), it.y(settings.lUnit), it.z(settings.lUnit)) - if (point != Point3D.ZERO) { - position = point - } + val gdmlX = it.x(settings.lUnit) + if (gdmlX != 0f) x = gdmlX + val gdmlY = it.y(settings.lUnit) + if (gdmlY != 0f) y = gdmlY + val gdmlZ = it.z(settings.lUnit) + if (gdmlZ != 0f) z = gdmlZ } newRotation?.let { - val point = Point3D(it.x(settings.aUnit), it.y(settings.aUnit), it.z(settings.aUnit)) - if (point != Point3D.ZERO) { - rotation = point - } - //this@withPosition.rotationOrder = RotationOrder.ZXY + val gdmlX = it.x(settings.aUnit) + if (gdmlX != 0f) rotationX = gdmlX + val gdmlY = it.y(settings.aUnit) + if (gdmlY != 0f) rotationY = gdmlY + val gdmlZ = it.z(settings.aUnit) + if (gdmlZ != 0f) rotationZ = gdmlZ } newScale?.let { - val point = Point3D(it.x, it.y, it.z) - if (point != Point3D.ONE) { - scale = point - } + if (it.x != 1f) scaleX = it.x + if (it.y != 1f) scaleY = it.y + if (it.z != 1f) scaleZ = it.z } - //TODO convert units if needed } fun T.withPosition(root: Gdml, physVolume: GdmlPhysVolume): T = withPosition( @@ -227,11 +232,10 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { bottomRadius = solid.rmax1 * lScale, height = solid.z * lScale, upperRadius = solid.rmax2 * lScale, + startAngle = solid.startphi * aScale, + angle = solid.deltaphi * aScale, name = name - ) { - startAngle = solid.startphi * aScale - angle = solid.deltaphi * aScale - } + ) } else { coneSurface( bottomOuterRadius = solid.rmax1 * lScale, @@ -239,11 +243,10 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { height = solid.z * lScale, topOuterRadius = solid.rmax2 * lScale, topInnerRadius = solid.rmin2 * lScale, + startAngle = solid.startphi * aScale, + angle = solid.deltaphi * aScale, name = name - ) { - startAngle = solid.startphi * aScale - angle = solid.deltaphi * aScale - } + ) } is GdmlXtru -> extrude(name) { shape { @@ -271,12 +274,15 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { scaleZ = solid.scale.z.toFloat() } } - is GdmlSphere -> sphereLayer(solid.rmax * lScale, solid.rmin * lScale, name) { - phi = solid.deltaphi * aScale - theta = solid.deltatheta * aScale - phiStart = solid.startphi * aScale - thetaStart = solid.starttheta * aScale - } + is GdmlSphere -> sphereLayer( + outerRadius = solid.rmax * lScale, + innerRadius = solid.rmin * lScale, + phi = solid.deltaphi * aScale, + theta = solid.deltatheta * aScale, + phiStart = solid.startphi * aScale, + thetaStart = solid.starttheta * aScale, + name = name, + ) is GdmlOrb -> sphere(solid.r * lScale, name = name) is GdmlPolyhedra -> extrude(name) { //getting the radius of first @@ -297,7 +303,7 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { val first: GdmlSolid = solid.first.resolve(root) ?: error("") val second: GdmlSolid = solid.second.resolve(root) ?: error("") val type: CompositeType = when (solid) { - is GdmlUnion -> CompositeType.UNION + is GdmlUnion -> CompositeType.SUM // dumb sum for better performance is GdmlSubtraction -> CompositeType.SUBTRACT is GdmlIntersection -> CompositeType.INTERSECT } diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/markLayers.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/markLayers.kt new file mode 100644 index 00000000..befc69bb --- /dev/null +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/markLayers.kt @@ -0,0 +1,89 @@ +package space.kscience.visionforge.gdml + +import space.kscience.dataforge.context.info +import space.kscience.dataforge.context.logger +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.length +import space.kscience.dataforge.names.plus +import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.solid.Solid +import space.kscience.visionforge.solid.SolidGroup +import space.kscience.visionforge.solid.SolidReferenceGroup +import space.kscience.visionforge.solid.layer + + +private class VisionCounterTree( + val name: Name, + val vision: Solid, + val prototypes: HashMap +) { + + // self count for prototypes + var selfCount = 1 + + val children: Map by lazy { + (vision as? VisionGroup)?.children?.mapValues { (key, vision) -> + if (vision is SolidReferenceGroup) { + prototypes.getOrPut(vision.refName) { + VisionCounterTree(vision.refName, vision.prototype, prototypes) + }.apply { + selfCount += 1 + } + } else { + VisionCounterTree(name + key, vision as Solid, prototypes) + } + } ?: emptyMap() + } + + val childrenCount: Int by lazy { + children.values.sumOf { it.childrenCount + 1 } + } + +} + + +private fun VisionCounterTree.topToBottom(): Sequence = sequence { + yield(this@topToBottom) + children.values.forEach { + yieldAll(it.topToBottom()) + } +} + +public fun SolidGroup.markLayers(thresholds: List = listOf(500, 1000, 20000, 50000)) { + val logger = manager?.context?.logger + val counterTree = VisionCounterTree(Name.EMPTY, this, hashMapOf()) + val totalCount = counterTree.childrenCount + if (totalCount > thresholds.firstOrNull() ?: 0) { + val allNodes = counterTree.topToBottom().distinct().toMutableList() + //println("tree construction finished") + allNodes.sortWith( + compareBy( + { it.name.length }, + { (it.children.size + 1) * it.selfCount } + ).reversed() + ) + + //mark layers + var remaining = totalCount + + for (node in allNodes) { + val layerIndex = if (remaining > thresholds.last()) + thresholds.size + else + thresholds.indexOfLast { remaining < it } + + if (layerIndex == 0) break + + node.vision.layer = layerIndex + remaining -= node.selfCount * (node.children.size + 1) + logger?.apply { + if (node.selfCount > 1) { + info { "Prototype with name ${node.name} moved to layer $layerIndex. $remaining nodes remains" } + } else { + info { "Vision with name ${node.name} moved to layer $layerIndex. $remaining nodes remains" } + } + } + } + } +} \ No newline at end of file diff --git a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt index a364996c..9fb80095 100644 --- a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt +++ b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt @@ -26,7 +26,7 @@ class TestCubes { val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box assertNotNull(smallBoxPrototype) assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) - val smallBoxVision = vision["composite-111.smallBox"]?.prototype as? Box + val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box assertNotNull(smallBoxVision) assertEquals(30.0, smallBoxVision.xSize.toDouble()) } diff --git a/visionforge-gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/gdmlJs.kt b/visionforge-gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/gdmlJs.kt deleted file mode 100644 index f90eec29..00000000 --- a/visionforge-gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/gdmlJs.kt +++ /dev/null @@ -1,8 +0,0 @@ -package space.kscience.visionforge.gdml - -public actual class Counter { - private var count: Int = 0 - public actual fun get(): Int = count - - public actual fun incrementAndGet(): Int = count++ -} \ No newline at end of file diff --git a/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt b/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt index 7bd17a5c..b67e231e 100644 --- a/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt +++ b/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt @@ -4,9 +4,6 @@ import space.kscience.gdml.Gdml import space.kscience.gdml.decodeFromFile import space.kscience.visionforge.solid.SolidGroup import java.nio.file.Path -import java.util.concurrent.atomic.AtomicInteger - -public actual typealias Counter = AtomicInteger public fun SolidGroup.gdml( file: Path, diff --git a/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt b/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt index eccac343..1f1da074 100644 --- a/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt +++ b/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.withContext import nl.adaptivity.xmlutil.StAXReader import space.kscience.gdml.Gdml import space.kscience.gdml.decodeFromReader -import space.kscience.visionforge.solid.prototype import space.kscience.visionforge.visitor.countDistinct import space.kscience.visionforge.visitor.flowStatistics import java.io.File @@ -23,7 +22,7 @@ suspend fun main() { vision.flowStatistics>{ _, child -> - child.prototype::class + child::class }.countDistinct().forEach { (depth, size) -> println("$depth\t$size") } diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt index 6f0a3959..2c9ed631 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt @@ -19,7 +19,9 @@ import io.ktor.server.engine.embeddedServer import io.ktor.websocket.WebSockets import io.ktor.websocket.webSocket import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.html.* import kotlinx.html.stream.createHTML @@ -131,6 +133,15 @@ public class VisionServer internal constructor( application.log.debug("Opened server socket for $name") val vision: Vision = visions[name.toName()] ?: error("Plot with id='$name' not registered") + launch { + incoming.consumeEach { + val change = visionManager.jsonFormat.decodeFromString( + VisionChange.serializer(), it.data.decodeToString() + ) + vision.update(change) + } + } + try { withContext(visionManager.context.coroutineContext) { vision.flowChanges(visionManager, Duration.milliseconds(updateInterval)).collect { update -> diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt index 35cf7ab6..d1d41d09 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt @@ -6,7 +6,8 @@ import space.kscience.dataforge.meta.update import space.kscience.visionforge.* public enum class CompositeType { - UNION, + SUM, // Dumb sum of meshes + UNION, //CSG union INTERSECT, SUBTRACT } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSegment.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSegment.kt index 211e3af2..560ec496 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSegment.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSegment.kt @@ -15,11 +15,11 @@ import kotlin.math.sin @Serializable @SerialName("solid.cone") public class ConeSegment( - public var bottomRadius: Float, - public var height: Float, - public var topRadius: Float, - public var startAngle: Float = 0f, - public var angle: Float = PI2 + public val bottomRadius: Float, + public val height: Float, + public val topRadius: Float, + public val startAngle: Float = 0f, + public val angle: Float = PI2 ) : SolidBase(), GeometrySolid { override fun toGeometry(geometryBuilder: GeometryBuilder) { @@ -83,10 +83,14 @@ public inline fun VisionContainerBuilder.cone( bottomRadius: Number, height: Number, upperRadius: Number = 0.0, + startAngle: Number = 0f, + angle: Number = PI2, name: String? = null, block: ConeSegment.() -> Unit = {} ): ConeSegment = ConeSegment( bottomRadius.toFloat(), height.toFloat(), - topRadius = upperRadius.toFloat() + topRadius = upperRadius.toFloat(), + startAngle = startAngle.toFloat(), + angle = angle.toFloat() ).apply(block).also { set(name, it) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSurface.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSurface.kt index caa8c746..a9c5622b 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSurface.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ConeSurface.kt @@ -16,13 +16,13 @@ import kotlin.math.sin @Serializable @SerialName("solid.coneSurface") public class ConeSurface( - public var bottomRadius: Float, - public var bottomInnerRadius: Float, - public var height: Float, - public var topRadius: Float, - public var topInnerRadius: Float, - public var startAngle: Float = 0f, - public var angle: Float = PI2, + public val bottomRadius: Float, + public val bottomInnerRadius: Float, + public val height: Float, + public val topRadius: Float, + public val topInnerRadius: Float, + public val startAngle: Float = 0f, + public val angle: Float = PI2, ) : SolidBase(), GeometrySolid { init { @@ -148,6 +148,8 @@ public inline fun VisionContainerBuilder.coneSurface( height: Number, topOuterRadius: Number, topInnerRadius: Number, + startAngle: Number = 0f, + angle: Number = PI2, name: String? = null, block: ConeSurface.() -> Unit = {}, ): ConeSurface = ConeSurface( @@ -156,4 +158,6 @@ public inline fun VisionContainerBuilder.coneSurface( height = height.toFloat(), topRadius = topOuterRadius.toFloat(), topInnerRadius = topInnerRadius.toFloat(), + startAngle = startAngle.toFloat(), + angle = angle.toFloat() ).apply(block).also { set(name, it) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt index d5943e3a..ded328b6 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt @@ -2,9 +2,8 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.visionforge.VisionBuilder -import space.kscience.visionforge.VisionContainerBuilder -import space.kscience.visionforge.set +import space.kscience.dataforge.meta.Config +import space.kscience.visionforge.* import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin @@ -13,7 +12,7 @@ import kotlin.math.sin public typealias Shape2D = List @Serializable -public class Shape2DBuilder(private val points: MutableList = ArrayList()) { +public class Shape2DBuilder(private val points: ArrayList = ArrayList()) { public fun point(x: Number, y: Number) { points.add(Point2D(x, y)) @@ -38,19 +37,9 @@ public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Flo @Serializable @SerialName("solid.extrude") public class Extruded( - public var shape: List = ArrayList(), - public var layers: MutableList = ArrayList() -) : SolidBase(), GeometrySolid { - - public fun shape(block: Shape2DBuilder.() -> Unit) { - this.shape = Shape2DBuilder().apply(block).build() - //TODO send invalidation signal - } - - public 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 - } + public val shape: List, + public val layers: List +) : SolidBase(), GeometrySolid, VisionPropertyContainer { override fun toGeometry(geometryBuilder: GeometryBuilder) { val shape: Shape2D = shape @@ -103,6 +92,26 @@ public class Extruded( } } +public class ExtrudeBuilder( + public var shape: List = emptyList(), + + public var layers: MutableList = ArrayList(), + + config: Config = Config() +) : SimpleVisionPropertyContainer(config) { + public fun shape(block: Shape2DBuilder.() -> Unit) { + this.shape = Shape2DBuilder().apply(block).build() + } + + public 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())) + } + + internal fun build(): Extruded = Extruded(shape, layers).apply { configure(config) } +} + @VisionBuilder -public fun VisionContainerBuilder.extrude(name: String? = null, action: Extruded.() -> Unit = {}): Extruded = - Extruded().apply(action).also { set(name, it) } \ No newline at end of file +public fun VisionContainerBuilder.extruded( + name: String? = null, + action: ExtrudeBuilder.() -> Unit = {} +): Extruded = ExtrudeBuilder().apply(action).build().also { set(name, it) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt index 41fe9903..cd9f2cbd 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt @@ -13,7 +13,7 @@ import space.kscience.visionforge.set @Serializable @SerialName("solid.line") -public class PolyLine(public var points: List) : SolidBase(), Solid { +public class PolyLine(public val points: List) : SolidBase(), Solid { //var lineType by string() public var thickness: Number by allProperties(inherit = false).number(1.0, diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt index d7d03c90..a4c8e69c 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt @@ -1,9 +1,7 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.boolean +import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.enum -import space.kscience.dataforge.meta.int import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus @@ -14,14 +12,28 @@ import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY import space.kscience.visionforge.solid.Solid.Companion.IGNORE_KEY import space.kscience.visionforge.solid.Solid.Companion.LAYER_KEY +import space.kscience.visionforge.solid.Solid.Companion.POSITION_KEY +import space.kscience.visionforge.solid.Solid.Companion.ROTATION_KEY +import space.kscience.visionforge.solid.Solid.Companion.SCALE_KEY +import space.kscience.visionforge.solid.Solid.Companion.X_KEY +import space.kscience.visionforge.solid.Solid.Companion.X_POSITION_KEY +import space.kscience.visionforge.solid.Solid.Companion.X_ROTATION_KEY +import space.kscience.visionforge.solid.Solid.Companion.X_SCALE_KEY +import space.kscience.visionforge.solid.Solid.Companion.Y_KEY +import space.kscience.visionforge.solid.Solid.Companion.Y_POSITION_KEY +import space.kscience.visionforge.solid.Solid.Companion.Y_ROTATION_KEY +import space.kscience.visionforge.solid.Solid.Companion.Y_SCALE_KEY +import space.kscience.visionforge.solid.Solid.Companion.Z_KEY +import space.kscience.visionforge.solid.Solid.Companion.Z_POSITION_KEY +import space.kscience.visionforge.solid.Solid.Companion.Z_ROTATION_KEY +import space.kscience.visionforge.solid.Solid.Companion.Z_SCALE_KEY +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty /** * Interface for 3-dimensional [Vision] */ public interface Solid : Vision { - public var position: Point3D? - public var rotation: Point3D? - public var scale: Point3D? override val descriptor: NodeDescriptor get() = Companion.descriptor @@ -37,7 +49,7 @@ public interface Solid : Vision { public val Y_KEY: Name = "y".asName() public val Z_KEY: Name = "z".asName() - public val POSITION_KEY: Name = "pos".asName() + public val POSITION_KEY: Name = "position".asName() public val X_POSITION_KEY: Name = POSITION_KEY + X_KEY public val Y_POSITION_KEY: Name = POSITION_KEY + Y_KEY @@ -72,6 +84,23 @@ public interface Solid : Vision { hide() } + node(POSITION_KEY){ + hide() + } + + node(ROTATION_KEY){ + hide() + } + + node(SCALE_KEY){ + hide() + } + + value(DETAIL_KEY) { + type(ValueType.NUMBER) + hide() + } + item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial.descriptor) enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) { @@ -79,22 +108,6 @@ public interface Solid : Vision { } } } - - internal fun solidEquals(first: Solid, second: Solid): Boolean { - if (first.position != second.position) return false - if (first.rotation != second.rotation) return false - if (first.scale != second.scale) return false - if (first.meta != second.meta) return false - return true - } - - internal fun solidHashCode(solid: Solid): Int { - var result = +(solid.position?.hashCode() ?: 0) - result = 31 * result + (solid.rotation?.hashCode() ?: 0) - result = 31 * result + (solid.scale?.hashCode() ?: 0) - result = 31 * result + solid.allProperties().hashCode() - return result - } } } @@ -145,74 +158,51 @@ public var Vision.ignore: Boolean? // get() = getProperty(SELECTED_KEY).boolean // set(value) = setProperty(SELECTED_KEY, value) -private fun Solid.position(): Point3D = - position ?: Point3D(0.0, 0.0, 0.0).also { position = it } +internal fun float(name: Name, default: Number): ReadWriteProperty = + object : ReadWriteProperty { + override fun getValue(thisRef: Solid, property: KProperty<*>): Number { + return thisRef.getOwnProperty(name)?.number ?: default + } -public var Solid.x: Number - get() = position?.x ?: 0f - set(value) { - position().x = value.toDouble() - invalidateProperty(Solid.X_POSITION_KEY) + override fun setValue(thisRef: Solid, property: KProperty<*>, value: Number) { + thisRef.setProperty(name, value) + } } -public var Solid.y: Number - get() = position?.y ?: 0f - set(value) { - position().y = value.toDouble() - invalidateProperty(Solid.Y_POSITION_KEY) +internal fun point(name: Name, default: Float): ReadWriteProperty = + object : ReadWriteProperty { + override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? { + val item = thisRef.getOwnProperty(name) ?: return null + return object : Point3D { + override val x: Float get() = item[X_KEY]?.float ?: default + override val y: Float get() = item[Y_KEY]?.float ?: default + override val z: Float get() = item[Z_KEY]?.float ?: default + } + } + + override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) { + if (value == null) { + thisRef.setProperty(name, null) + } else { + thisRef.setProperty(name + X_KEY, value.x) + thisRef.setProperty(name + Y_KEY, value.y) + thisRef.setProperty(name + Z_KEY, value.z) + } + } } -public var Solid.z: Number - get() = position?.z ?: 0f - set(value) { - position().z = value.toDouble() - invalidateProperty(Solid.Z_POSITION_KEY) - } +public var Solid.position: Point3D? by point(POSITION_KEY, 0f) +public var Solid.rotation: Point3D? by point(ROTATION_KEY, 0f) +public var Solid.scale: Point3D? by point(SCALE_KEY, 1f) -private fun Solid.rotation(): Point3D = - rotation ?: Point3D(0.0, 0.0, 0.0).also { rotation = it } +public var Solid.x: Number by float(X_POSITION_KEY, 0f) +public var Solid.y: Number by float(Y_POSITION_KEY, 0f) +public var Solid.z: Number by float(Z_POSITION_KEY, 0f) -public var Solid.rotationX: Number - get() = rotation?.x ?: 0f - set(value) { - rotation().x = value.toDouble() - invalidateProperty(Solid.X_ROTATION_KEY) - } +public var Solid.rotationX: Number by float(X_ROTATION_KEY, 0f) +public var Solid.rotationY: Number by float(Y_ROTATION_KEY, 0f) +public var Solid.rotationZ: Number by float(Z_ROTATION_KEY, 0f) -public var Solid.rotationY: Number - get() = rotation?.y ?: 0f - set(value) { - rotation().y = value.toDouble() - invalidateProperty(Solid.Y_ROTATION_KEY) - } - -public var Solid.rotationZ: Number - get() = rotation?.z ?: 0f - set(value) { - rotation().z = value.toDouble() - invalidateProperty(Solid.Z_ROTATION_KEY) - } - -private fun Solid.scale(): Point3D = - scale ?: Point3D(1.0, 1.0, 1.0).also { scale = it } - -public var Solid.scaleX: Number - get() = scale?.x ?: 1f - set(value) { - scale().x = value.toDouble() - invalidateProperty(Solid.X_SCALE_KEY) - } - -public var Solid.scaleY: Number - get() = scale?.y ?: 1f - set(value) { - scale().y = value.toDouble() - invalidateProperty(Solid.Y_SCALE_KEY) - } - -public var Solid.scaleZ: Number - get() = scale?.z ?: 1f - set(value) { - scale().z = value.toDouble() - invalidateProperty(Solid.Z_SCALE_KEY) - } \ No newline at end of file +public var Solid.scaleX: Number by float(X_SCALE_KEY, 1f) +public var Solid.scaleY: Number by float(Y_SCALE_KEY, 1f) +public var Solid.scaleZ: Number by float(Z_SCALE_KEY, 1f) \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index e69f729c..d2fee33a 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -2,21 +2,13 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.float -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.node import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionChange @Serializable @SerialName("solid") -public open class SolidBase( - override var position: Point3D? = null, - override var rotation: Point3D? = null, - override var scale: Point3D? = null, -) : VisionBase(), Solid { +public open class SolidBase : VisionBase(), Solid { override val descriptor: NodeDescriptor get() = Solid.descriptor override fun update(change: VisionChange) { @@ -24,15 +16,3 @@ public open class SolidBase( super.update(change) } } - -internal fun Meta.toVector(default: Float = 0f) = Point3D( - this[Solid.X_KEY].float ?: default, - this[Solid.Y_KEY].float ?: default, - this[Solid.Z_KEY].float ?: default -) - -internal fun Solid.updatePosition(meta: Meta?) { - meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it } - meta[Solid.ROTATION_KEY].node?.toVector()?.let { rotation = it } - meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it } -} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt index 20ca8afc..b78ec4a5 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt @@ -1,23 +1,25 @@ package space.kscience.visionforge.solid -import kotlinx.serialization.KSerializer -import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import space.kscience.dataforge.meta.MetaItem import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.visionforge.* +/** + * A container with prototype support + */ public interface PrototypeHolder { + /** + * Build or update prototype tree + */ @VisionBuilder public fun prototypes(builder: VisionContainerBuilder.() -> Unit) + /** + * Resolve a prototype from this container. Should never return a ref. + */ public fun getPrototype(name: Name): Solid? } @@ -27,40 +29,35 @@ public interface PrototypeHolder { */ @Serializable @SerialName("group.solid") -public class SolidGroup( - @Serializable(PrototypeSerializer::class) internal var prototypes: MutableVisionGroup? = null, - override var position: Point3D? = null, - override var rotation: Point3D? = null, - override var scale: Point3D? = null, -) : VisionGroupBase(), Solid, PrototypeHolder { +public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { + + override val children: Map get() = super.childrenInternal.filter { it.key != PROTOTYPES_TOKEN } + + private var prototypes: MutableVisionGroup? + get() = childrenInternal[PROTOTYPES_TOKEN] as? MutableVisionGroup + set(value) { + set(PROTOTYPES_TOKEN, value) + } - init { - prototypes?.parent = this - } override val descriptor: NodeDescriptor get() = Solid.descriptor /** - * Ger a prototype redirecting the request to the parent if prototype is not found + * Get a prototype redirecting the request to the parent if prototype is not found. + * If prototype is a ref, then it is unfolded automatically. */ override fun getPrototype(name: Name): Solid? = - (prototypes?.get(name) as? Solid) ?: (parent as? PrototypeHolder)?.getPrototype(name) + prototypes?.get(name)?.unref ?: (parent as? PrototypeHolder)?.getPrototype(name) /** * Create or edit prototype node as a group */ override fun prototypes(builder: VisionContainerBuilder.() -> Unit): Unit { - (prototypes ?: Prototypes().also { + (prototypes ?: SolidGroup().also { prototypes = it - it.parent = this }).run(builder) } -// /** -// * TODO add special static group to hold statics without propagation -// */ -// override fun addStatic(child: VisualObject) = setChild(NameToken("@static(${child.hashCode()})"), child) - override fun createGroup(): SolidGroup = SolidGroup() override fun update(change: VisionChange) { @@ -90,50 +87,3 @@ public fun VisionContainerBuilder.group( @VisionBuilder public fun VisionContainerBuilder.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup = SolidGroup().apply(action).also { set(name, it) } - -/** - * A special class which works as a holder for prototypes - */ -internal class Prototypes( - children: MutableMap = hashMapOf(), -) : VisionGroupBase(children), PrototypeHolder { - - override fun getProperty( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, - ): MetaItem? = null - - override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - error("Can't set property of a prototypes container") - } - - override val descriptor: NodeDescriptor? = null - - override fun prototypes(builder: VisionContainerBuilder.() -> Unit) { - apply(builder) - } - - override fun getPrototype(name: Name): Solid? = get(name) as? Solid -} - -internal class PrototypeSerializer : KSerializer { - - private val mapSerializer: KSerializer> = - MapSerializer( - NameToken.serializer(), - PolymorphicSerializer(Vision::class) - ) - - override val descriptor: SerialDescriptor get() = mapSerializer.descriptor - - override fun deserialize(decoder: Decoder): MutableVisionGroup { - val map = mapSerializer.deserialize(decoder) - return Prototypes(map.toMutableMap()) - } - - override fun serialize(encoder: Encoder, value: MutableVisionGroup) { - mapSerializer.serialize(encoder, value.children) - } -} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidLabel.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidLabel.kt index ac45e22e..3e725d0d 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidLabel.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidLabel.kt @@ -9,9 +9,9 @@ import space.kscience.visionforge.set @Serializable @SerialName("solid.label") public class SolidLabel( - public var text: String, - public var fontSize: Double, - public var fontFamily: String, + public val text: String, + public val fontSize: Double, + public val fontFamily: String, ) : SolidBase(), Solid @VisionBuilder diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 17ee9141..1cb5eac8 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -11,25 +11,52 @@ import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.* import space.kscience.visionforge.* -public interface SolidReference : Solid { + +public interface SolidReference : VisionGroup { + /** + * The prototype for this reference. Always returns a "real" prototype, not a reference + */ public val prototype: Solid } + +/** + * Get a vision prototype if it is a [SolidReference] or vision itself if it is not. + * Unref is recursive, so it always returns a non-reference. + */ +public val Vision.unref: Solid + get() = when (this) { + is SolidReference -> prototype.unref + is Solid -> this + else -> error("This Vision is neither Solid nor SolidReference") + } + + private fun SolidReference.getRefProperty( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, -): MetaItem? = buildList { - add(getOwnProperty(name)) - if (includeStyles) { - addAll(getStyleItems(name)) - } - add(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit) { - add(parent?.getProperty(name, inherit)) - } -}.merge() +): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) { + getOwnProperty(name) +} else { + buildList { + add(getOwnProperty(name)) + if (includeStyles) { + addAll(getStyleItems(name)) + } + add(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) + if (inherit) { + add(parent?.getProperty(name, inherit)) + } + }.merge() +} + +private fun childToken(childName: Name): NameToken = + NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) + +private fun childPropertyName(childName: Name, propertyName: Name): Name = + childToken(childName) + propertyName /** * A reference [Solid] to reuse a template object @@ -38,48 +65,24 @@ private fun SolidReference.getRefProperty( @SerialName("solid.ref") public class SolidReferenceGroup( public val refName: Name, -) : SolidBase(), SolidReference, VisionGroup { +) : VisionBase(), SolidReference, VisionGroup, Solid { /** * Recursively search for defined template in the parent */ - override val prototype: Solid - get() { - if (parent == null) error("No parent is present for SolidReferenceGroup") - if (parent !is SolidGroup) error("Reference parent is not a group") - return (parent as? SolidGroup)?.getPrototype(refName) - ?: error("Prototype with name $refName not found") - } + override val prototype: Solid by lazy { + if (parent == null) error("No parent is present for SolidReferenceGroup") + if (parent !is PrototypeHolder) error("Parent does not hold prototypes") + (parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found") + } override val children: Map get() = (prototype as? VisionGroup)?.children - ?.filter { !it.key.toString().startsWith("@") } + ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } ?.mapValues { - ReferenceChild(it.key.asName()) + ReferenceChild(this, it.key.asName()) } ?: emptyMap() - private fun childToken(childName: Name): NameToken = - NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) - - private fun childPropertyName(childName: Name, propertyName: Name): Name = - childToken(childName) + propertyName - - private fun getChildProperty(childName: Name, propertyName: Name): MetaItem? { - return getOwnProperty(childPropertyName(childName, propertyName)) - } - - private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem?, notify: Boolean) { - setProperty(childPropertyName(childName, propertyName), item, notify) - } - - private fun prototypeFor(name: Name): Solid { - return if (name.isEmpty()) prototype else { - val proto = (prototype as? SolidGroup)?.get(name) - ?: error("Prototype with name $name not found in SolidReferenceGroup $refName") - proto as? Solid ?: error("Prototype with name $name is ${proto::class} but expected Solid") - } - } - override fun getProperty( name: Name, inherit: Boolean, @@ -94,37 +97,32 @@ public class SolidReferenceGroup( * A ProxyChild is created temporarily only to interact with properties, it does not store any values * (properties are stored in external cache) and created and destroyed on-demand). */ - private inner class ReferenceChild(private val childName: Name) : SolidReference, VisionGroup { + private class ReferenceChild( + val owner: SolidReferenceGroup, + private val refName: Name + ) : SolidReference, VisionGroup, Solid { - //TODO replace by properties - override var position: Point3D? - get() = prototype.position - set(_) { - error("Can't set position of reference") + override val prototype: Solid by lazy { + if (refName.isEmpty()) owner.prototype else { + val proto = (owner.prototype as? VisionGroup)?.get(refName) + ?: error("Prototype with name $refName not found in SolidReferenceGroup ${owner.refName}") + proto.unref as? Solid + ?: error("Prototype with name $refName is ${proto::class} but expected Solid") } - override var rotation: Point3D? - get() = prototype.rotation - set(_) { - error("Can't set position of reference") - } - override var scale: Point3D? - get() = prototype.scale - set(_) { - error("Can't set position of reference") - } - override val prototype: Solid get() = prototypeFor(childName) + } override val children: Map get() = (prototype as? VisionGroup)?.children - ?.filter { !it.key.toString().startsWith("@") } + ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } ?.mapValues { (key, _) -> - ReferenceChild(childName + key.asName()) + ReferenceChild(owner, refName + key.asName()) } ?: emptyMap() - override fun getOwnProperty(name: Name): MetaItem? = getChildProperty(childName, name) + override fun getOwnProperty(name: Name): MetaItem? = + owner.getOwnProperty(childPropertyName(refName, name)) override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - setChildProperty(childName, name, item, notify) + owner.setProperty(childPropertyName(refName, name), item, notify) } override fun getProperty( @@ -132,16 +130,12 @@ public class SolidReferenceGroup( inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) { - getOwnProperty(name) - } else { - getRefProperty(name, inherit, includeStyles, includeDefaults) - } + ): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults) override var parent: VisionGroup? get() { - val parentName = childName.cutLast() - return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName) + val parentName = refName.cutLast() + return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName) } set(_) { error("Setting a parent for a reference child is not possible") @@ -149,8 +143,8 @@ public class SolidReferenceGroup( @DFExperimental override val propertyChanges: Flow - get() = this@SolidReferenceGroup.propertyChanges.mapNotNull { name -> - if (name.startsWith(childToken(childName))) { + get() = owner.propertyChanges.mapNotNull { name -> + if (name.startsWith(childToken(refName))) { name.cutFirst() } else { null @@ -158,7 +152,7 @@ public class SolidReferenceGroup( } override fun invalidateProperty(propertyName: Name) { - this@SolidReferenceGroup.invalidateProperty(childPropertyName(childName, propertyName)) + owner.invalidateProperty(childPropertyName(refName, propertyName)) } override fun update(change: VisionChange) { @@ -176,12 +170,6 @@ public class SolidReferenceGroup( } } -/** - * Get a vision prototype if it is a [SolidReferenceGroup] or vision itself if it is not - */ -public val Vision.prototype: Vision - get() = if (this is SolidReference) prototype.prototype else this - /** * Create ref for existing prototype */ diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt index c4d67f97..bae8d83d 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionContainerBuilder +import space.kscience.visionforge.VisionPropertyContainer import space.kscience.visionforge.set import kotlin.math.PI import kotlin.math.cos @@ -12,12 +13,12 @@ import kotlin.math.sin @Serializable @SerialName("solid.sphere") public class Sphere( - public var radius: Float, - public var phiStart: Float = 0f, - public var phi: Float = PI2, - public var thetaStart: Float = 0f, - public var theta: Float = PI.toFloat(), -) : SolidBase(), GeometrySolid { + public val radius: Float, + public val phiStart: Float = 0f, + public val phi: Float = PI2, + public val thetaStart: Float = 0f, + public val theta: Float = PI .toFloat(), +) : SolidBase(), GeometrySolid, VisionPropertyContainer { override fun toGeometry(geometryBuilder: GeometryBuilder) { fun point3DfromSphCoord(r: Float, theta: Float, phi: Float): Point3D { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt index 83e358e2..05149e2e 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt @@ -15,12 +15,12 @@ import kotlin.math.sin @Serializable @SerialName("solid.sphereLayer") public class SphereLayer( - public var outerRadius: Float, - public var innerRadius: Float, - public var phiStart: Float = 0f, - public var phi: Float = PI2, - public var thetaStart: Float = 0f, - public var theta: Float = PI.toFloat(), + public val outerRadius: Float, + public val innerRadius: Float, + public val phiStart: Float = 0f, + public val phi: Float = PI2, + public val thetaStart: Float = 0f, + public val theta: Float = PI.toFloat(), ) : SolidBase(), GeometrySolid { override fun toGeometry(geometryBuilder: GeometryBuilder): Unit = geometryBuilder.run { @@ -72,9 +72,17 @@ public class SphereLayer( public inline fun VisionContainerBuilder.sphereLayer( outerRadius: Number, innerRadius: Number, + phiStart: Number = 0f, + phi: Number = PI2, + thetaStart: Number = 0f, + theta: Number = PI.toFloat(), name: String? = null, action: SphereLayer.() -> Unit = {}, ): SphereLayer = SphereLayer( outerRadius.toFloat(), innerRadius.toFloat(), + phiStart.toFloat(), + phi.toFloat(), + thetaStart.toFloat(), + theta.toFloat() ).apply(action).also { set(name, it) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt index 35fc731f..f5aa2d4a 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt @@ -1,39 +1,68 @@ package space.kscience.visionforge.solid +import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MetaBuilder -import space.kscience.dataforge.meta.double -import space.kscience.dataforge.meta.get +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import space.kscience.dataforge.meta.* +import space.kscience.visionforge.solid.Solid.Companion.X_KEY +import space.kscience.visionforge.solid.Solid.Companion.Y_KEY +import space.kscience.visionforge.solid.Solid.Companion.Z_KEY import kotlin.math.PI +import kotlin.math.pow +import kotlin.math.sqrt public const val PI2: Float = 2 * PI.toFloat() @Serializable -public data class Point2D(public var x: Double, public var y: Double) +public data class Point2D(public var x: Float, public var y: Float) -public fun Point2D(x: Number, y: Number): Point2D = Point2D(x.toDouble(), y.toDouble()) +public fun Point2D(x: Number, y: Number): Point2D = Point2D(x.toFloat(), y.toFloat()) public fun Point2D.toMeta(): Meta = Meta { - Solid.X_KEY put x - Solid.Y_KEY put y + X_KEY put x + Y_KEY put y } -internal fun Meta.point2D(): Point2D = Point2D(this["x"].double ?: 0.0, this["y"].double ?: 0.0) +internal fun Meta.point2D(): Point2D = Point2D(this["x"].float ?: 0f, this["y"].float ?: 0f) + +@Serializable(Point3DSerializer::class) +public interface Point3D { + public val x: Float + public val y: Float + public val z: Float -@Serializable -public data class Point3D( - public var x: Double, - public var y: Double, - public var z: Double, -) { public companion object { public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0) public val ONE: Point3D = Point3D(1.0, 1.0, 1.0) } } -public fun Point3D(x: Number, y: Number, z: Number): Point3D = Point3D(x.toDouble(), y.toDouble(), z.toDouble()) +@Serializable(Point3DSerializer::class) +public interface MutablePoint3D : Point3D { + override var x: Float + override var y: Float + override var z: Float +} + +@Serializable +private class Point3DImpl(override var x: Float, override var y: Float, override var z: Float) : MutablePoint3D + +internal object Point3DSerializer : KSerializer { + + override val descriptor: SerialDescriptor = Point3DImpl.serializer().descriptor + + + override fun deserialize(decoder: Decoder): Point3D = decoder.decodeSerializableValue(Point3DImpl.serializer()) + + override fun serialize(encoder: Encoder, value: Point3D) { + val impl: Point3DImpl = (value as? Point3DImpl) ?: Point3DImpl(value.x, value.y, value.z) + encoder.encodeSerializableValue(Point3DImpl.serializer(), impl) + } +} + +public fun Point3D(x: Number, y: Number, z: Number): Point3D = Point3DImpl(x.toFloat(), y.toFloat(), z.toFloat()) public operator fun Point3D.plus(other: Point3D): Point3D = Point3D( this.x + other.x, @@ -41,10 +70,52 @@ public operator fun Point3D.plus(other: Point3D): Point3D = Point3D( this.z + other.z ) -internal fun Meta.point3D() = Point3D(this["x"].double ?: 0.0, this["y"].double ?: 0.0, this["y"].double ?: 0.0) +public operator fun Point3D.minus(other: Point3D): Point3D = Point3D( + this.x - other.x, + this.y - other.y, + this.z - other.z +) + +public operator fun Point3D.unaryMinus(): Point3D = Point3D( + -x, + -y, + -z +) + +public infix fun Point3D.cross(other: Point3D): Point3D = Point3D( + y * other.z - z * other.y, + z * other.x - x * other.z, + x * other.y - y * other.x +) + +public fun MutablePoint3D.normalizeInPlace() { + val norm = sqrt(x.pow(2) + y.pow(2) + z.pow(2)) + x /= norm + y /= norm + z /= norm +} + +internal fun ItemProvider.point3D(default: Float = 0f) = object : Point3D { + override val x: Float by float(default) + override val y: Float by float(default) + override val z: Float by float(default) +} public fun Point3D.toMeta(): MetaBuilder = Meta { - Solid.X_KEY put x - Solid.Y_KEY put y - Solid.Z_KEY put z + X_KEY put x + Y_KEY put y + Z_KEY put z +} + + +internal fun Meta.toVector(default: Float = 0f) = Point3D( + this[Solid.X_KEY].float ?: default, + this[Solid.Y_KEY].float ?: default, + this[Solid.Z_KEY].float ?: default +) + +internal fun Solid.updatePosition(meta: Meta?) { + meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it } + meta[Solid.ROTATION_KEY].node?.toVector()?.let { rotation = it } + meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it } } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt index b6209b54..905fa234 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt @@ -18,7 +18,6 @@ public class Camera : Scheme() { public var distance: Double by double(INITIAL_DISTANCE) public var azimuth: Double by double(INITIAL_AZIMUTH) public var latitude: Double by double(INITIAL_LATITUDE) - public val zenith: Double get() = PI / 2 - latitude public companion object : SchemeSpec(::Camera) { public const val INITIAL_DISTANCE: Double = 300.0 @@ -51,4 +50,6 @@ public class Camera : Scheme() { } } } -} \ No newline at end of file +} + +public val Camera.zenith: Double get() = PI / 2 - latitude \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt index 5d07303e..a51aaa25 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt @@ -1,36 +1,39 @@ package space.kscience.visionforge.solid.transform +import space.kscience.dataforge.meta.itemSequence import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.asName -import space.kscience.visionforge.* +import space.kscience.visionforge.MutableVisionGroup +import space.kscience.visionforge.Vision +import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.meta import space.kscience.visionforge.solid.* +private operator fun Number.plus(other: Number) = toFloat() + other.toFloat() +private operator fun Number.times(other: Number) = toFloat() * other.toFloat() + @DFExperimental -internal fun mergeChild(parent: VisionGroup, child: Vision): Vision { - return child.apply { - - configure(parent.meta) - - //parent.properties?.let { config.update(it) } - - if (this is Solid && parent is Solid) { - position = (position ?: Point3D.ZERO) + (parent.position ?: Point3D.ZERO) - rotation = (parent.rotation ?: Point3D.ZERO) + (parent.rotation ?: Point3D.ZERO) - scale = when { - scale == null && parent.scale == null -> null - scale == null -> parent.scale - parent.scale == null -> scale - else -> Point3D( - scale!!.x * parent.scale!!.x, - scale!!.y * parent.scale!!.y, - scale!!.z * parent.scale!!.z - ) +internal fun Vision.updateFrom(other: Vision): Vision { + if (this is Solid && other is Solid) { + x += other.x + y += other.y + z += other.y + rotationX += other.rotationX + rotationY += other.rotationY + rotationZ += other.rotationZ + scaleX *= other.scaleX + scaleY *= other.scaleY + scaleZ *= other.scaleZ + other.meta.itemSequence().forEach { (name, item) -> + if (getProperty(name) == null) { + setProperty(name, item) } } - } + return this } + @DFExperimental internal object RemoveSingleChild : VisualTreeTransform() { @@ -43,7 +46,7 @@ internal object RemoveSingleChild : VisualTreeTransform() { } if (parent is VisionGroup && parent.children.size == 1) { val child = parent.children.values.first() - val newParent = mergeChild(parent, child) + val newParent = child.updateFrom(parent) newParent.parent = null set(childName.asName(), newParent) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt index b6d7c2e6..890c8291 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt @@ -31,7 +31,7 @@ internal object UnRef : VisualTreeTransform() { } children.filter { (it.value as? SolidReferenceGroup)?.refName == name }.forEach { (key, value) -> val reference = value as SolidReferenceGroup - val newChild = mergeChild(reference, reference.prototype) + val newChild = reference.prototype.updateFrom(reference) newChild.parent = null set(key.asName(), newChild) // replace proxy with merged object } diff --git a/visionforge-threejs/build.gradle.kts b/visionforge-threejs/build.gradle.kts index 60c00061..a6407d13 100644 --- a/visionforge-threejs/build.gradle.kts +++ b/visionforge-threejs/build.gradle.kts @@ -4,6 +4,6 @@ plugins { dependencies { api(project(":visionforge-solid")) - implementation(npm("three", "0.124.0")) - implementation(npm("three-csg-ts", "2.2.2")) + implementation(npm("three", "0.130.1")) + implementation(npm("three-csg-ts", "3.1.6")) } diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/THREE.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/THREE.kt index d3844561..3f8028cb 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/THREE.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/THREE.kt @@ -24,6 +24,7 @@ @file:JsModule("three") @file:JsNonModule +@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", "unused") package info.laht.threekt diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/animation/AnimationAction.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/animation/AnimationAction.kt index 8abbc8ae..d0e9b073 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/animation/AnimationAction.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/animation/AnimationAction.kt @@ -1,3 +1,4 @@ +@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", "unused") @file:JsModule("three") @file:JsNonModule diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferAttribute.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferAttribute.kt index 001aa20c..6a773330 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferAttribute.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferAttribute.kt @@ -44,6 +44,7 @@ abstract external class BufferAttribute protected constructor( * Default is 0. Position at whcih to start update. */ var offset: Int + /** * Default is -1, which means don't use update ranges. @@ -56,16 +57,19 @@ abstract external class BufferAttribute protected constructor( * UUID of this object instance. This gets automatically assigned and this shouldn't be edited. */ val uuid: String + /** * Optional name for this attribute instance. Default is an empty string. */ var name: String var array: dynamic + /** * The length of vectors that are being stored in the array. */ val itemSize: Int + /** * Stores the array's length divided by the itemSize. * @@ -73,6 +77,7 @@ abstract external class BufferAttribute protected constructor( * then this will count the number of such vectors stored. */ val count: Int + /** * Indicates how the underlying data in the buffer maps to the values in the GLSL shader code. See the constructor above for details. */ @@ -86,6 +91,7 @@ abstract external class BufferAttribute protected constructor( * This corresponds to the gl.DYNAMIC_DRAW flag. */ var dynamic: Boolean + /** * This can be used to only update some components of stored vectors ( * for example, just the component related to color). @@ -99,8 +105,16 @@ abstract external class BufferAttribute protected constructor( */ var needsUpdate: Boolean + /** + * A callback function that is executed after the Renderer has transferred the attribute array data to the GPU. + */ var onUploadCallback: () -> Unit + /** + * Sets the value of the [onUploadCallback] property. + */ + fun onUpload(callback: () -> Unit) + /** * A version number, incremented every time the needsUpdate property is set to true. */ @@ -119,6 +133,7 @@ abstract external class BufferAttribute protected constructor( fun getW(index: Int): Number fun copy(source: BufferAttribute): BufferAttribute + /** * Copy a vector from bufferAttribute[index2] to array[index1]. */ @@ -205,3 +220,22 @@ abstract external class BufferAttribute protected constructor( */ fun setXYZW(index: Int, x: Number, y: Number, z: Number, w: Number) } + + +external class Float32BufferAttribute( + array: Array, + itemSize: Int, + normalized: Boolean = definedExternally +) : BufferAttribute + +external class Int32BufferAttribute( + array: IntArray, + itemSize: Int, + normalized: Boolean = definedExternally +) : BufferAttribute + +external class Int16BufferAttribute( + array: ShortArray, + itemSize: Int, + normalized: Boolean = definedExternally +) : BufferAttribute \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferGeometry.kt index c3fd093b..251627a7 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/BufferGeometry.kt @@ -85,14 +85,14 @@ open external class BufferGeometry { fun clearGroups() fun addGroup(start: Int, count: Int, materialIndex: Int = definedExternally) - fun addAttribute(name: String, attribute: BufferAttribute) + fun setAttribute(name: String, attribute: BufferAttribute) fun getAttribute(name: String): BufferAttribute - fun removeAttribute(name: String): BufferGeometry + fun deleteAttribute(name: String): BufferGeometry fun setIndex(index: BufferAttribute) + fun setIndex(index: Array) fun setDrawRange(start: Int, count: Int) - fun fromGeometry(geometry: Geometry) fun setFromObject(`object`: Object3D): BufferGeometry fun updateFromObject(`object`: Object3D): BufferGeometry fun setFromPoints(points: Array): BufferGeometry diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/DirectGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/DirectGeometry.kt deleted file mode 100644 index bae8ce28..00000000 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/DirectGeometry.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * The MIT License - * - * Copyright 2017-2018 Lars Ivar Hatledal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -@file:JsModule("three") -@file:JsNonModule - -package info.laht.threekt.core - -import info.laht.threekt.math.Box3 -import info.laht.threekt.math.Sphere - -external class DirectGeometry { - - var verticesNeedUpdate: Boolean - var normalsNeedUpdate: Boolean - var colorsNeedUpdate: Boolean - var uvsNeedUpdate: Boolean - var groupsNeedUpdate: Boolean - - fun computeBoundingBox(): Box3 - fun computeBoundingSphere(): Sphere - - fun dispose() - - fun fromGeometry(geometry: Geometry) - -} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/Geometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/Geometry.kt deleted file mode 100644 index 522ab07c..00000000 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/core/Geometry.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * The MIT License - * - * Copyright 2017-2018 Lars Ivar Hatledal - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -@file:JsModule("three") -@file:JsNonModule - -package info.laht.threekt.core - -import info.laht.threekt.math.* -import info.laht.threekt.objects.Mesh - -external interface MorphTarget { - val name: String - val vertices: Array -} - -external interface MorphNormal { - val name: String - val normals: Array -} - -open external class Geometry { - - val id: Int - - var vertices: Array - var colors: Array - var faces: Array - var faceVertexUvs: Array> - - var morphTargets: Array - var morphNormals: Array - - var skinWeights: Array - var skinIndices: Array - - var lineDistances: List - - var boundingBox: Box3? - var boundingSphere: Sphere? - - // update flags - - var elementsNeedUpdate: Boolean - var verticesNeedUpdate: Boolean - var uvsNeedUpdate: Boolean - var normalsNeedUpdate: Boolean - var colorsNeedUpdate: Boolean - var lineDistancesNeedUpdate: Boolean - var groupsNeedUpdate: Boolean - - fun applyMatrix4(matrix: Matrix4): Geometry - fun rotateX(angle: Number): Geometry - fun rotateY(angle: Number): Geometry - fun rotateZ(angle: Number): Geometry - fun translate(x: Number, y: Number, z: Number): Geometry - fun scale(x: Number, y: Number, z: Number): Geometry - fun lookAt(vector: Vector3): Geometry - fun fromBufferGeometry(geometry: BufferGeometry): Geometry - fun addFace(a: Int, b: Int, c: Int, materialIndexOffset: Int = definedExternally) - fun center(): Vector3 - fun normalize(): Geometry - fun computeFaceNormals() - fun computeVertexNormals(areaWeighted: Boolean = definedExternally) - fun computeFlatVertexNormals() - fun computeMorphNormals() - fun computeLineDistances() - fun computeBoundingBox() - fun computeBoundingSphere() - - fun merge(geometry: Geometry, matrix: Matrix4 = definedExternally, materialIndexOffset: Int = definedExternally) - - fun mergeMesh(mesh: Mesh) - - fun mergeVertices() - - fun setFromPoint(points: Array): Geometry - - fun sortFacesByMaterialIndex() - - fun toJSON(): Any - - open fun clone(): Geometry - fun copy(geometry: Geometry): Geometry - - fun dispose() - -} diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/BoxGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/BoxGeometry.kt index 6cf5d5a7..8d0e5ec5 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/BoxGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/BoxGeometry.kt @@ -4,7 +4,6 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry external class BoxGeometry( @@ -14,14 +13,4 @@ external class BoxGeometry( widthSegments: Int = definedExternally, heightSegments: Int = definedExternally, depthSegments: Int = definedExternally -) : Geometry - - -external class BoxBufferGeometry( - width: Number, - height: Number, - depth: Number, - widthSegments: Int = definedExternally, - heightSegments: Int = definedExternally, - depthSegments: Int = definedExternally ) : BufferGeometry diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ConeGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ConeGeometry.kt index ae143fa8..f98ad2dd 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ConeGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ConeGeometry.kt @@ -4,7 +4,7 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry + external class ConeGeometry( radius: Number = definedExternally, @@ -14,14 +14,4 @@ external class ConeGeometry( openEnded: Boolean = definedExternally, thetaStart: Boolean = definedExternally, thetaLength: Boolean = definedExternally -) : Geometry - -external class ConeBufferGeometry( - radius: Number = definedExternally, - height: Number = definedExternally, - radialSegments: Int = definedExternally, - heightSegments: Int = definedExternally, - openEnded: Boolean = definedExternally, - thetaStart: Boolean = definedExternally, - thetaLength: Boolean = definedExternally ) : BufferGeometry \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/CylinderGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/CylinderGeometry.kt index a4801453..b10e5c11 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/CylinderGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/CylinderGeometry.kt @@ -4,7 +4,6 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry external class CylinderGeometry( radiusTop: Number, @@ -15,15 +14,4 @@ external class CylinderGeometry( openEnded: Boolean = definedExternally, thetaStart: Number = definedExternally, thetaLength: Number = definedExternally -) : Geometry - -external class CylinderBufferGeometry( - radiusTop: Number, - radiusBottom: Number, - height: Number, - radialSegments: Int = definedExternally, - heightSegments: Int = definedExternally, - openEnded: Boolean = definedExternally, - thetaStart: Number = definedExternally, - thetaLength: Number = definedExternally ) : BufferGeometry \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/EdgesGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/EdgesGeometry.kt index 8e6568da..b0a5c91b 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/EdgesGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/EdgesGeometry.kt @@ -4,8 +4,5 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry -external class EdgesGeometry(geometry: Geometry, thresholdAngle: Int = definedExternally) : BufferGeometry { - constructor(geometry: BufferGeometry, thresholdAngle: Int = definedExternally) -} \ No newline at end of file +public external class EdgesGeometry(geometry: BufferGeometry, thresholdAngle: Int = definedExternally) : BufferGeometry \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ExtrudedGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ExtrudeGeometry.kt similarity index 83% rename from visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ExtrudedGeometry.kt rename to visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ExtrudeGeometry.kt index ab17c116..4b30e7af 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ExtrudedGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/ExtrudeGeometry.kt @@ -11,7 +11,6 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry import info.laht.threekt.extras.core.Shape import info.laht.threekt.math.Vector2 @@ -78,15 +77,3 @@ external open class ExtrudeBufferGeometry : BufferGeometry { var WorldUVGenerator: UVGenerator } } - -external open class ExtrudeGeometry : Geometry { - constructor(shapes: Shape, options: ExtrudeGeometryOptions?) - constructor(shapes: Array, options: ExtrudeGeometryOptions?) - - open fun addShapeList(shapes: Array, options: Any? = definedExternally) - open fun addShape(shape: Shape, options: Any? = definedExternally) - - companion object { - var WorldUVGenerator: UVGenerator - } -} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/PlaneGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/PlaneGeometry.kt index ad758af4..0b1072ef 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/PlaneGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/PlaneGeometry.kt @@ -4,7 +4,6 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry external class PlaneGeometry( @@ -13,13 +12,4 @@ external class PlaneGeometry( widthSegments: Int = definedExternally, heightSegments: Int = definedExternally -) : Geometry - -external class PlaneBufferGeometry( - - width: Number, - height: Number, - widthSegments: Int = definedExternally, - heightSegments: Int = definedExternally - ) : BufferGeometry \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/SphereGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/SphereGeometry.kt index 8117ff4c..5b0c9fc6 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/SphereGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/SphereGeometry.kt @@ -4,7 +4,6 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry external class SphereGeometry( radius: Number, @@ -14,14 +13,4 @@ external class SphereGeometry( phiLength: Number = definedExternally, thetaStart: Number = definedExternally, thetaLength: Number = definedExternally -) : Geometry - -external class SphereBufferGeometry( - radius: Number, - widthSegments: Int = definedExternally, - heightSegments: Int = definedExternally, - phiStart: Number = definedExternally, - phiLength: Number = definedExternally, - thetaStart: Number = definedExternally, - thetaLength: Number = definedExternally ) : BufferGeometry \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TextBufferGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TextGeometry.kt similarity index 83% rename from visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TextBufferGeometry.kt rename to visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TextGeometry.kt index ca9f93b3..b8c935f3 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TextBufferGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TextGeometry.kt @@ -34,10 +34,7 @@ external interface TextGeometryParameters { set(value) = definedExternally } -external class TextBufferGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) : ExtrudeBufferGeometry { - val parameters: TextGeometryParameters -} - -external class TextGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) : ExtrudeGeometry { +external class TextBufferGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) : + ExtrudeBufferGeometry { val parameters: TextGeometryParameters } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TorusGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TorusGeometry.kt index 7406afb3..0a53525f 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TorusGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TorusGeometry.kt @@ -4,7 +4,7 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry + external class TorusGeometry( radius: Number = definedExternally, @@ -12,12 +12,4 @@ external class TorusGeometry( radialSegments: Int = definedExternally, tubularSegments: Int = definedExternally, arc: Number = definedExternally -) : Geometry - -external class TorusBufferGeometry( - radius: Number = definedExternally, - tube: Number = definedExternally, - radialSegments: Int = definedExternally, - tubularSegments: Int = definedExternally, - arc: Number = definedExternally ) : BufferGeometry \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TubeGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TubeGeometry.kt index 38bc560a..36afa6ae 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TubeGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/TubeGeometry.kt @@ -1,10 +1,10 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry import info.laht.threekt.extras.core.Curve import info.laht.threekt.math.Vector3 + /** * Creates a tube that extrudes along a 3d curve. */ @@ -16,25 +16,6 @@ external class TubeGeometry( radiusSegments: Int = definedExternally, closed: Boolean = definedExternally -) : Geometry { - - var tangents: Array - var normals: Array - var binormals: Array - -} - -/** - * Creates a tube that extrudes along a 3d curve. - */ -external class TubeBufferGeometry( - - path: Curve, - tubularSegments: Int = definedExternally, - radius: Number = definedExternally, - radiusSegments: Int = definedExternally, - closed: Boolean = definedExternally - ) : BufferGeometry { val parameters: dynamic diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/WireframeGeometry.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/WireframeGeometry.kt index 4c0a72ee..f52c61cc 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/WireframeGeometry.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/geometries/WireframeGeometry.kt @@ -4,14 +4,8 @@ package info.laht.threekt.geometries import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry /** * This can be used as a helper object to view a Geometry object as a wireframe. */ -external class WireframeGeometry : BufferGeometry { - - constructor(geometry: Geometry) - constructor(geometry: BufferGeometry) - -} \ No newline at end of file +external class WireframeGeometry(geometry: BufferGeometry) : BufferGeometry \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/LineSegments.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/LineSegments.kt index b5a83ff0..03bef34e 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/LineSegments.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/LineSegments.kt @@ -28,12 +28,10 @@ package info.laht.threekt.objects import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry import info.laht.threekt.core.Object3D import info.laht.threekt.materials.Material open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D { - constructor(geometry: Geometry, material: Material) var geometry: BufferGeometry var material: Material diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/Mesh.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/Mesh.kt index 78dcb9f6..fc91e333 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/Mesh.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/objects/Mesh.kt @@ -27,16 +27,16 @@ package info.laht.threekt.objects -import info.laht.threekt.core.* +import info.laht.threekt.core.BufferGeometry +import info.laht.threekt.core.Intersect +import info.laht.threekt.core.Object3D +import info.laht.threekt.core.Raycaster import info.laht.threekt.materials.Material -open external class Mesh : Object3D { +open external class Mesh(geometry: BufferGeometry?, material: Material?) : Object3D { - constructor(geometry: Geometry?, material: Material?) - constructor(geometry: BufferGeometry?, material: Material?) - - var geometry: dynamic + var geometry: BufferGeometry var material: Material var drawMode: Int diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/utils/BufferGeometryUtils.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/utils/BufferGeometryUtils.kt new file mode 100644 index 00000000..dd15b514 --- /dev/null +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/utils/BufferGeometryUtils.kt @@ -0,0 +1,15 @@ +@file:JsModule("three/examples/jsm/utils/BufferGeometryUtils") +@file:JsNonModule +package info.laht.threekt.utils + +import info.laht.threekt.core.BufferGeometry + + +public external object BufferGeometryUtils { + /** + * Merges a set of geometries into a single instance. All geometries must have compatible attributes. If merge does not succeed, the method returns null. + * @param geometries -- Array of BufferGeometry instances. + * @param useGroups -- Whether groups should be generated for the merged geometry or not. + */ + public fun mergeBufferGeometries(geometries: Array, useGroups: Boolean): BufferGeometry +} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt index b01dbd1a..7cbd4d3d 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt @@ -30,16 +30,14 @@ public abstract class MeshThreeFactory( override fun invoke(three: ThreePlugin, obj: T): Mesh { val geometry = buildGeometry(obj) - //JS sometimes tries to pass Geometry as BufferGeometry - @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") - //val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty val mesh = Mesh(geometry, ThreeMaterials.DEFAULT).apply { matrixAutoUpdate = false //set position for mesh updatePosition(obj) - }.applyProperties(obj) + applyProperties(obj) + } //add listener to object properties obj.onPropertyChange(three.updateScope) { name -> @@ -76,9 +74,9 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { updateMaterial(obj) applyEdges(obj) //applyWireFrame(obj) - layers.enable(obj.layer) + layers.set(obj.layer) children.forEach { - it.layers.enable(obj.layer) + it.layers.set(obj.layer) } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt index dad57066..a5b50688 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt @@ -1,12 +1,12 @@ package space.kscience.visionforge.solid.three -import info.laht.threekt.geometries.BoxBufferGeometry +import info.laht.threekt.geometries.BoxGeometry import space.kscience.visionforge.solid.Box import space.kscience.visionforge.solid.detail public object ThreeBoxFactory : MeshThreeFactory(Box::class) { - override fun buildGeometry(obj: Box): BoxBufferGeometry = + override fun buildGeometry(obj: Box): BoxGeometry = obj.detail?.let { detail -> - BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail) - } ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) + BoxGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail) + } ?: BoxGeometry(obj.xSize, obj.ySize, obj.zSize) } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt index 7dd89a32..3afb6776 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt @@ -250,8 +250,11 @@ public class ThreeCanvas( } public fun render(vision: Solid) { - three.logger.info { "Replacing root node in three canvas" } - scene.findChild("@root".asName())?.let { scene.remove(it) } + if (root != null) { + three.logger.info { "Replacing root node in three canvas" } + scene.findChild("@root".asName())?.let { scene.remove(it) } + root?.dispose() + } val object3D = three.buildObject3D(vision) object3D.name = "@root" diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt index bbce8f19..4855abcd 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt @@ -2,7 +2,7 @@ package space.kscience.visionforge.solid.three import info.laht.threekt.DoubleSide import info.laht.threekt.core.Object3D -import info.laht.threekt.geometries.PlaneBufferGeometry +import info.laht.threekt.geometries.PlaneGeometry import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.objects.Mesh import info.laht.threekt.textures.Texture @@ -46,7 +46,7 @@ public object ThreeCanvasLabelFactory : ThreeFactory { } val mesh = Mesh( - PlaneBufferGeometry(canvas.width, canvas.height), + PlaneGeometry(canvas.width, canvas.height), material ) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt index 1f8c9328..2cdd12d3 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt @@ -1,29 +1,60 @@ package space.kscience.visionforge.solid.three import CSG -import info.laht.threekt.core.BufferGeometry +import info.laht.threekt.core.Object3D import info.laht.threekt.objects.Mesh +import space.kscience.dataforge.names.startsWith +import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.Composite import space.kscience.visionforge.solid.CompositeType +import kotlin.reflect.KClass /** * This should be inner, because it uses object builder */ -public class ThreeCompositeFactory(public val three: ThreePlugin) : MeshThreeFactory(Composite::class) { +public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory { - override fun buildGeometry(obj: Composite): BufferGeometry { +// override fun buildGeometry(obj: Composite): BufferGeometry { +// val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh") +// //first.updateMatrix() +// val second = three.buildObject3D(obj.second) as? Mesh ?: error("Second part of composite is not a mesh") +// //second.updateMatrix() +// val firstCSG = CSG.fromMesh(first) +// val secondCSG = CSG.fromMesh(second) +//// val resultCSG = when (obj.compositeType) { +//// CompositeType.UNION -> firstCSG.union(secondCSG) +//// CompositeType.INTERSECT -> firstCSG.intersect(secondCSG) +//// CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG) +//// } +//// return resultCSG.toGeometry(second.matrix) +// +// val resultMesh: Mesh = when (obj.compositeType) { +// CompositeType.UNION -> CSG.union(first,second) +// CompositeType.INTERSECT -> CSG.intersect(first,second) +// CompositeType.SUBTRACT -> CSG.subtract(first,second) +// } +// return resultMesh.geometry +// } + + override val type: KClass get() = Composite::class + + override fun invoke(three: ThreePlugin, obj: Composite): Object3D { val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh") - first.updateMatrix() val second = three.buildObject3D(obj.second) as? Mesh ?: error("Second part of composite is not a mesh") - second.updateMatrix() - val firstCSG = CSG.fromMesh(first) - val secondCSG = CSG.fromMesh(second) - val resultCSG = when (obj.compositeType) { - CompositeType.UNION -> firstCSG.union(secondCSG) - CompositeType.INTERSECT -> firstCSG.intersect(secondCSG) - CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG) + return when (obj.compositeType) { + CompositeType.SUM, CompositeType.UNION -> CSG.union(first, second) + CompositeType.INTERSECT -> CSG.intersect(first, second) + CompositeType.SUBTRACT -> CSG.subtract(first, second) + }.apply { + updatePosition(obj) + applyProperties(obj) + obj.onPropertyChange(three.updateScope) { name -> + when { + //name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) + name.startsWith(MeshThreeFactory.EDGES_KEY) -> applyEdges(obj) + else -> updateProperty(obj, name) + } + } } - return resultCSG.toGeometry().toBufferGeometry() } - } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt index c8eece9a..5789c30e 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge.solid.three import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.geometries.CylinderBufferGeometry +import info.laht.threekt.geometries.CylinderGeometry import space.kscience.visionforge.solid.ConeSegment import space.kscience.visionforge.solid.detail import kotlin.math.PI @@ -11,7 +11,7 @@ public object ThreeConeFactory : MeshThreeFactory(ConeSegment::clas override fun buildGeometry(obj: ConeSegment): BufferGeometry { val cylinder = obj.detail?.let { val segments = it.toDouble().pow(0.5).toInt() - CylinderBufferGeometry( + CylinderGeometry( radiusTop = obj.topRadius, radiusBottom = obj.bottomRadius, height = obj.height, @@ -21,7 +21,7 @@ public object ThreeConeFactory : MeshThreeFactory(ConeSegment::clas thetaStart = obj.startAngle, thetaLength = obj.angle ) - } ?: CylinderBufferGeometry( + } ?: CylinderGeometry( radiusTop = obj.topRadius, radiusBottom = obj.bottomRadius, height = obj.height, diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt index e231c4ce..c62abd77 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt @@ -63,9 +63,7 @@ public fun Object3D.updateProperty(source: Vision, propertyName: Name) { * Generic factory for elements which provide inside geometry builder */ public object ThreeShapeFactory : MeshThreeFactory(GeometrySolid::class) { - override fun buildGeometry(obj: GeometrySolid): BufferGeometry { - return obj.run { - ThreeGeometryBuilder().apply { toGeometry(this) }.build() - } - } + override fun buildGeometry(obj: GeometrySolid): BufferGeometry = ThreeGeometryBuilder().apply { + obj.toGeometry(this) + }.build() } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt index c1568ffd..c1c5aa72 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt @@ -1,52 +1,63 @@ package space.kscience.visionforge.solid.three import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Face3 -import info.laht.threekt.core.Geometry +import info.laht.threekt.core.Float32BufferAttribute import info.laht.threekt.math.Vector3 import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.int import space.kscience.visionforge.solid.GeometryBuilder import space.kscience.visionforge.solid.Point3D +import space.kscience.visionforge.solid.cross +import space.kscience.visionforge.solid.minus internal fun Point3D.toVector() = Vector3(x, y, z) +internal fun MutableList.add(vararg values: T) { + values.forEach { + add(it) + } +} + /** * An implementation of geometry builder for Three.js [BufferGeometry] */ public class ThreeGeometryBuilder : GeometryBuilder { - private val vertices = ArrayList() - private val faces = ArrayList() + private val indices = ArrayList() + private val positions = ArrayList() + private val normals = ArrayList() +// private val colors = ArrayList() - private val vertexCache = HashMap() + private val vertexCache = HashMap() + private var counter: Short = -1 - private fun append(vertex: Point3D): Int { - val index = vertexCache[vertex] ?: -1//vertices.indexOf(vertex) - return if (index > 0) { - index - } else { - vertices.add(vertex.toVector()) - vertexCache[vertex] = vertices.size - 1 - vertices.size - 1 - } + private fun vertex(vertex: Point3D, normal: Point3D): Short = vertexCache.getOrPut(vertex) { + //add vertex and update cache if needed + positions.add(vertex.x, vertex.y, vertex.z) + normals.add(normal.x, vertex.y, vertex.z) + //colors.add(1f, 1f, 1f) + counter++ + counter } override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) { - val face = Face3(append(vertex1), append(vertex2), append(vertex3), normal?.toVector() ?: Vector3(0, 0, 0)) - meta["materialIndex"].int?.let { face.materialIndex = it } - meta["color"]?.getColor()?.let { face.color = it } - faces.add(face) + val actualNormal: Point3D = normal ?: (vertex3 - vertex2) cross (vertex1 - vertex2) + indices.add( + vertex(vertex1, actualNormal), + vertex(vertex2, actualNormal), + vertex(vertex3, actualNormal) + ) } - override fun build(): BufferGeometry { - return Geometry().apply { - vertices = this@ThreeGeometryBuilder.vertices.toTypedArray() - faces = this@ThreeGeometryBuilder.faces.toTypedArray() - computeBoundingSphere() - computeFaceNormals() - }.toBufferGeometry() + override fun build(): BufferGeometry = BufferGeometry().apply { + setIndex(indices.toTypedArray()) + setAttribute("position", Float32BufferAttribute(positions.toTypedArray(), 3)) + setAttribute("normal", Float32BufferAttribute(normals.toTypedArray(), 3)) + //setAttribute("color", Float32BufferAttribute(colors.toFloatArray(), 3)) + //a temporary fix for CSG problem + val uvsArray = Array((counter+1)*2){0f} + setAttribute("uv", Float32BufferAttribute(uvsArray, 2)) + + computeBoundingSphere() } } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeVision.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeJsVision.kt similarity index 83% rename from visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeVision.kt rename to visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeJsVision.kt index 16d1bd4f..27a5da44 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeVision.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeJsVision.kt @@ -6,6 +6,6 @@ import space.kscience.visionforge.solid.SolidBase /** * A custom visual object that has its own Three.js renderer */ -public abstract class ThreeVision : SolidBase() { +public abstract class ThreeJsVision : SolidBase() { public abstract fun render(three: ThreePlugin): Object3D } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt index cc9c58ce..b2858fdb 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt @@ -1,6 +1,6 @@ package space.kscience.visionforge.solid.three -import info.laht.threekt.core.Geometry +import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.math.Color import info.laht.threekt.objects.LineSegments @@ -16,8 +16,8 @@ public object ThreeLineFactory : ThreeFactory { override val type: KClass get() = PolyLine::class override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D { - val geometry = Geometry().apply { - vertices = Array(obj.points.size) { obj.points[it].toVector() } + val geometry = BufferGeometry().apply { + setFromPoints(Array(obj.points.size) { obj.points[it].toVector() }) } val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index 68fa692a..e6d85dc5 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -49,7 +49,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } public fun buildObject3D(obj: Solid): Object3D = when (obj) { - is ThreeVision -> obj.render(this) + is ThreeJsVision -> obj.render(this) is SolidReferenceGroup -> ThreeReferenceFactory(this, obj) is SolidGroup -> { val group = ThreeGroup() diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt index 8f117516..8932a6c8 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt @@ -1,14 +1,14 @@ package space.kscience.visionforge.solid.three import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.geometries.SphereBufferGeometry +import info.laht.threekt.geometries.SphereGeometry import space.kscience.visionforge.solid.Sphere import space.kscience.visionforge.solid.detail public object ThreeSphereFactory : MeshThreeFactory(Sphere::class) { override fun buildGeometry(obj: Sphere): BufferGeometry { return obj.detail?.let {detail -> - SphereBufferGeometry( + SphereGeometry( radius = obj.radius, phiStart = obj.phiStart, phiLength = obj.phi, @@ -17,7 +17,7 @@ public object ThreeSphereFactory : MeshThreeFactory(Sphere::class) { widthSegments = detail, heightSegments = detail ) - }?: SphereBufferGeometry( + }?: SphereGeometry( radius = obj.radius, phiStart = obj.phiStart, phiLength = obj.phi, diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt index af99f66e..50e48ee9 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt @@ -1,49 +1,52 @@ -@file:Suppress("INTERFACE_WITH_SUPERCLASS", +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS", - "EXTERNAL_DELEGATION") + "EXTERNAL_DELEGATION" +) @file:JsModule("three-csg-ts") @file:JsNonModule +import info.laht.threekt.core.BufferGeometry import info.laht.threekt.math.Matrix4 import info.laht.threekt.math.Vector3 import info.laht.threekt.objects.Mesh -external open class CSG { - open fun clone(): CSG - open fun toPolygons(): Array - open fun union(csg: CSG): CSG - open fun subtract(csg: CSG): CSG - open fun intersect(csg: CSG): CSG - open fun inverse(): CSG +public external class CSG { + public fun clone(): CSG + public fun toPolygons(): Array + public fun toGeometry(toMatrix: Matrix4): BufferGeometry + public fun union(csg: CSG): CSG + public fun subtract(csg: CSG): CSG + public fun intersect(csg: CSG): CSG + public fun inverse(): CSG - companion object { + + public companion object { fun fromPolygons(polygons: Array): CSG - fun fromGeometry(geom: Any): CSG - fun fromMesh(mesh: Mesh): CSG + fun fromGeometry(geom: BufferGeometry, objectIndex: dynamic = definedExternally): CSG + fun fromMesh(mesh: Mesh, objectIndex: dynamic = definedExternally): CSG + fun toGeometry(csg: CSG, toMatrix: Matrix4): BufferGeometry fun toMesh(csg: CSG, toMatrix: Matrix4): Mesh fun iEval(tokens: Mesh, index: Number? = definedExternally) fun eval(tokens: Mesh, doRemove: Boolean): Mesh - var _tmpm3: Any - var doRemove: Any - var currentOp: Any - var currentPrim: Any - var nextPrim: Any - var sourceMesh: Any + fun union(meshA: Mesh, meshB: Mesh): Mesh + fun subtract(meshA: Mesh, meshB: Mesh): Mesh + fun intersect(meshA: Mesh, meshB: Mesh): Mesh } } -external open 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 - open fun times(a: Number): Vector - open fun dividedBy(a: Number): Vector - open fun lerp(a: Vector, t: Number): Any - open fun unit(): Vector - open fun cross(a: Vector): Any +external class Vector(x: Number, y: Number, z: Number) : Vector3 { + fun negated(): Vector + fun plus(a: Vector): Vector + fun minus(a: Vector): Vector + fun times(a: Number): Vector + fun dividedBy(a: Number): Vector + fun lerp(a: Vector, t: Number): Any + fun unit(): Vector + fun cross(a: Vector): Any } external interface IVector { @@ -52,21 +55,21 @@ external interface IVector { var z: Number } -external open class Vertex(pos: IVector, normal: IVector, uv: IVector? = definedExternally) { - open var pos: Vector - open var normal: Vector - open var uv: Vector - open fun clone(): Vertex - open fun flip() - open fun interpolate(other: Vertex, t: Number): Vertex +external class Vertex(pos: IVector, normal: IVector, uv: IVector? = definedExternally) { + var pos: Vector + var normal: Vector + var uv: Vector + fun clone(): Vertex + fun flip() + fun interpolate(other: Vertex, t: Number): Vertex } -external open class Plane(normal: Vector, w: Number) { - open var normal: Vector - open var w: Number - open fun clone(): Plane - open fun flip() - open fun splitPolygon( +external class Plane(normal: Vector, w: Number) { + var normal: Vector + var w: Number + fun clone(): Plane + fun flip() + fun splitPolygon( polygon: Polygon, coplanarFront: Array, coplanarBack: Array, @@ -80,10 +83,10 @@ external open class Plane(normal: Vector, w: Number) { } } -external open class Polygon(vertices: Array, shared: Any? = definedExternally) { - open var plane: Plane - open var vertices: Array - open var shared: Any - open fun clone(): Polygon - open fun flip() +external class Polygon(vertices: Array, shared: Any? = definedExternally) { + var plane: Plane + var vertices: Array + var shared: Any + fun clone(): Polygon + fun flip() } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt index fa13e91d..9e78800b 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge.solid.three -import CSG -import info.laht.threekt.core.* +import info.laht.threekt.core.BufferGeometry +import info.laht.threekt.core.Layers import info.laht.threekt.external.controls.OrbitControls import info.laht.threekt.materials.Material import info.laht.threekt.math.Euler @@ -19,53 +19,13 @@ public val Solid.euler: Euler get() = Euler(rotationX, rotationY, rotationZ, rot public val MetaItem.vector: Vector3 get() = Vector3(node["x"].float ?: 0f, node["y"].float ?: 0f, node["z"].float ?: 0f) -public fun Geometry.toBufferGeometry(): BufferGeometry = BufferGeometry().apply { fromGeometry(this@toBufferGeometry) } internal fun Double.toRadians() = this * PI / 180 -public fun CSG.toGeometry(): Geometry { - val geom = Geometry() - - val vertices = ArrayList() - val faces = ArrayList() - - for (polygon in toPolygons()) { - 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, Vector3()) - 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 -} internal fun Any.dispose() { when (this) { - is Geometry -> dispose() is BufferGeometry -> dispose() - is DirectGeometry -> dispose() is Material -> dispose() is Mesh -> { geometry.dispose()