forked from kscience/visionforge
commit
0dafbb2a34
@ -19,6 +19,11 @@
|
|||||||
- VisionGroup builder accepts `null` as name for statics instead of `""`
|
- VisionGroup builder accepts `null` as name for statics instead of `""`
|
||||||
- gdml sphere is rendered as a SphereLayer instead of Sphere (#35)
|
- gdml sphere is rendered as a SphereLayer instead of Sphere (#35)
|
||||||
- Tube is replaced by more general ConeSurface
|
- 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
|
### Deprecated
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.2.0-dev-21"
|
version = "0.2.0-dev-22"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -10,7 +10,7 @@ import kotlin.test.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class GDMLVisualTest {
|
class GDMLVisionTest {
|
||||||
|
|
||||||
// @Test
|
// @Test
|
||||||
// fun testCubesStyles(){
|
// fun testCubesStyles(){
|
@ -10,6 +10,7 @@ import space.kscience.dataforge.context.fetch
|
|||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.gdml.Gdml
|
import space.kscience.gdml.Gdml
|
||||||
import space.kscience.gdml.decodeFromString
|
import space.kscience.gdml.decodeFromString
|
||||||
|
import space.kscience.visionforge.gdml.markLayers
|
||||||
import space.kscience.visionforge.gdml.toVision
|
import space.kscience.visionforge.gdml.toVision
|
||||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||||
import space.kscience.visionforge.ring.tab
|
import space.kscience.visionforge.ring.tab
|
||||||
@ -32,7 +33,11 @@ val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
|
|||||||
val parsedVision = when {
|
val parsedVision = when {
|
||||||
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
||||||
val gdml = Gdml.decodeFromString(data)
|
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)
|
name.endsWith(".json") -> visionManager.decodeFromString(data)
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.css.height
|
import kotlinx.css.*
|
||||||
import kotlinx.css.vh
|
|
||||||
import kotlinx.css.vw
|
|
||||||
import kotlinx.css.width
|
|
||||||
import react.child
|
import react.child
|
||||||
import react.dom.render
|
import react.dom.render
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
@ -32,6 +29,8 @@ private class JsPlaygroundApp : Application {
|
|||||||
render(element) {
|
render(element) {
|
||||||
styledDiv {
|
styledDiv {
|
||||||
css{
|
css{
|
||||||
|
padding(0.pt)
|
||||||
|
margin(0.pt)
|
||||||
height = 100.vh
|
height = 100.vh
|
||||||
width = 100.vw
|
width = 100.vw
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>js-playground</title>
|
<title>js-playground</title>
|
||||||
<script src="js-playground.js"></script>
|
<script src="js-playground.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="application">
|
||||||
<div id="playground"></div>
|
<div id="playground"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -11,7 +11,7 @@ import space.kscience.visionforge.solid.plus
|
|||||||
class SC1(
|
class SC1(
|
||||||
val name: String,
|
val name: String,
|
||||||
val center: Point3D,
|
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(
|
class SC16(
|
||||||
@ -121,12 +121,12 @@ internal expect fun readMonitorConfig(): String
|
|||||||
object Monitor {
|
object Monitor {
|
||||||
|
|
||||||
const val GEOMETRY_TOLERANCE = 0.01
|
const val GEOMETRY_TOLERANCE = 0.01
|
||||||
const val PIXEL_XY_SIZE = 122.0
|
const val PIXEL_XY_SIZE = 122.0f
|
||||||
const val PIXEL_XY_SPACING = 123.2
|
const val PIXEL_XY_SPACING = 123.2f
|
||||||
const val PIXEL_Z_SIZE = 30.0
|
const val PIXEL_Z_SIZE = 30.0f
|
||||||
const val CENTRAL_LAYER_Z = 0.0
|
const val CENTRAL_LAYER_Z = 0.0f
|
||||||
const val UPPER_LAYER_Z = -166.0
|
const val UPPER_LAYER_Z = -166.0f
|
||||||
const val LOWER_LAYER_Z = 180.0
|
const val LOWER_LAYER_Z = 180.0f
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build map for the whole monitor
|
* Build map for the whole monitor
|
||||||
|
@ -17,19 +17,19 @@ import kotlin.random.Random
|
|||||||
internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) {
|
internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) {
|
||||||
// val layer: Layer = findLayer(center.z);
|
// val layer: Layer = findLayer(center.z);
|
||||||
private val upLayer =
|
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 =
|
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 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<Plane> = arrayOf(
|
private val sideLayers: Array<Plane> = 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.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(-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)), 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),
|
||||||
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.0)), Vector3D(0.0, -1.0, 0.0), GEOMETRY_TOLERANCE)
|
||||||
);
|
)
|
||||||
|
|
||||||
//TODO add efficiency
|
//TODO add efficiency
|
||||||
private fun containsPoint(x: Double, y: Double, z: Double, tolerance: Double = GEOMETRY_TOLERANCE): Boolean {
|
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
|
* The layer number from up to bottom
|
||||||
*/
|
*/
|
||||||
fun getLayerNumber(): Int {
|
fun getLayerNumber(): Int {
|
||||||
return when (this.center.z) {
|
return when (this.center.z.toFloat()) {
|
||||||
UPPER_LAYER_Z -> 1;
|
UPPER_LAYER_Z -> 1
|
||||||
CENTRAL_LAYER_Z -> 2;
|
CENTRAL_LAYER_Z -> 2;
|
||||||
LOWER_LAYER_Z -> 3;
|
LOWER_LAYER_Z -> 3;
|
||||||
else -> throw RuntimeException("Unknown layer");
|
else -> throw RuntimeException("Unknown layer");
|
||||||
|
@ -45,7 +45,7 @@ fun makeTrack(start: Vector3D, direction: Vector3D): Line {
|
|||||||
fun makeTrack(x: Double, y: Double, theta: Double, phi: Double): Line {
|
fun makeTrack(x: Double, y: Double, theta: Double, phi: Double): Line {
|
||||||
//TODO check angle definitions
|
//TODO check angle definitions
|
||||||
return makeTrack(
|
return makeTrack(
|
||||||
Vector3D(x, y, CENTRAL_LAYER_Z),
|
Vector3D(x, y, CENTRAL_LAYER_Z.toDouble()),
|
||||||
Vector3D(phi, theta)
|
Vector3D(phi, theta)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,12 @@ import ru.mipt.npm.muon.monitor.readResource
|
|||||||
internal const val MINIMAL_TRACK_LENGTH = 10.0
|
internal const val MINIMAL_TRACK_LENGTH = 10.0
|
||||||
|
|
||||||
|
|
||||||
private val layerCache = HashMap<Double, Plane>()
|
private val layerCache = HashMap<Float, Plane>()
|
||||||
|
|
||||||
fun findLayer(z: Double): Plane = layerCache.getOrPut(z) {
|
fun findLayer(z: Float): Plane = layerCache.getOrPut(z) {
|
||||||
Plane(
|
Plane(
|
||||||
Vector3D(0.0, 0.0, z), Vector3D(0.0, 0.0, 1.0),
|
Vector3D(0.0, 0.0, z.toDouble()), Vector3D(0.0, 0.0, 1.0),
|
||||||
Monitor.GEOMETRY_TOLERANCE
|
Monitor.GEOMETRY_TOLERANCE.toDouble()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ interface TrackGenerator {
|
|||||||
*/
|
*/
|
||||||
class UniformTrackGenerator(
|
class UniformTrackGenerator(
|
||||||
override val rnd: RandomGenerator,
|
override val rnd: RandomGenerator,
|
||||||
val maxX: Double = 4 * PIXEL_XY_SIZE,
|
val maxX: Float = 4 * PIXEL_XY_SIZE,
|
||||||
val maxY: Double = 4 * PIXEL_XY_SIZE
|
val maxY: Float = 4 * PIXEL_XY_SIZE
|
||||||
) :
|
) :
|
||||||
TrackGenerator {
|
TrackGenerator {
|
||||||
override fun generate(): Line {
|
override fun generate(): Line {
|
||||||
@ -44,8 +44,8 @@ class UniformTrackGenerator(
|
|||||||
class FixedAngleGenerator(
|
class FixedAngleGenerator(
|
||||||
override val rnd: RandomGenerator,
|
override val rnd: RandomGenerator,
|
||||||
val phi: Double, val theta: Double,
|
val phi: Double, val theta: Double,
|
||||||
val maxX: Double = 4 * PIXEL_XY_SIZE,
|
val maxX: Float = 4 * PIXEL_XY_SIZE,
|
||||||
val maxY: Double = 4 * PIXEL_XY_SIZE
|
val maxY: Float = 4 * PIXEL_XY_SIZE
|
||||||
) : TrackGenerator {
|
) : TrackGenerator {
|
||||||
override fun generate(): Line {
|
override fun generate(): Line {
|
||||||
val x = (1 - rnd.nextDouble() * 2.0) * maxX
|
val x = (1 - rnd.nextDouble() * 2.0) * maxX
|
||||||
@ -60,8 +60,8 @@ class FixedAngleGenerator(
|
|||||||
class Cos2TrackGenerator(
|
class Cos2TrackGenerator(
|
||||||
override val rnd: RandomGenerator,
|
override val rnd: RandomGenerator,
|
||||||
val power: Double = 2.0,
|
val power: Double = 2.0,
|
||||||
val maxX: Double = 4 * PIXEL_XY_SIZE,
|
val maxX: Float = 4 * PIXEL_XY_SIZE,
|
||||||
val maxY: Double = 4 * PIXEL_XY_SIZE
|
val maxY: Float = 4 * PIXEL_XY_SIZE
|
||||||
) :
|
) :
|
||||||
TrackGenerator {
|
TrackGenerator {
|
||||||
override fun generate(): Line {
|
override fun generate(): Line {
|
||||||
|
@ -57,8 +57,7 @@ fun VisionLayout<Solid>.showcase() {
|
|||||||
rotationX = PI / 4
|
rotationX = PI / 4
|
||||||
color("blue")
|
color("blue")
|
||||||
}
|
}
|
||||||
sphereLayer(50,40){
|
sphereLayer(50, 40, theta = PI / 2) {
|
||||||
theta = (PI/2).toFloat()
|
|
||||||
rotationX = -PI * 3 / 4
|
rotationX = -PI * 3 / 4
|
||||||
z = 110
|
z = 110
|
||||||
color(Colors.pink)
|
color(Colors.pink)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package space.kscience.visionforge.solid.demo
|
package space.kscience.visionforge.solid.demo
|
||||||
|
|
||||||
import info.laht.threekt.core.Object3D
|
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 info.laht.threekt.objects.Mesh
|
||||||
import space.kscience.dataforge.meta.int
|
import space.kscience.dataforge.meta.int
|
||||||
import space.kscience.dataforge.meta.number
|
import space.kscience.dataforge.meta.number
|
||||||
@ -23,10 +23,10 @@ internal fun SolidGroup.varBox(
|
|||||||
action: VariableBox.() -> Unit = {},
|
action: VariableBox.() -> Unit = {},
|
||||||
): VariableBox = VariableBox(xSize, ySize).apply(action).also { set(name, it) }
|
): 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 {
|
override fun render(three: ThreePlugin): Object3D {
|
||||||
val geometry = BoxBufferGeometry(xSize, ySize, 1)
|
val geometry = BoxGeometry(xSize, ySize, 1)
|
||||||
|
|
||||||
val material = ThreeMaterials.DEFAULT.clone()
|
val material = ThreeMaterials.DEFAULT.clone()
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ class FXDemoApp : App(FXDemoGrid::class) {
|
|||||||
stage.height = 600.0
|
stage.height = 600.0
|
||||||
|
|
||||||
view.showcase()
|
view.showcase()
|
||||||
|
view.showcaseCSG()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
docs/images/polyline-points-2.png
Normal file
BIN
docs/images/polyline-points-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
docs/images/polyline-points.png
Normal file
BIN
docs/images/polyline-points.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
230
docs/tutorial.md
230
docs/tutorial.md
@ -1,8 +1,8 @@
|
|||||||
# Tutorial
|
# Tutorial
|
||||||
|
|
||||||
###The main goal of this tutorial is to show all capabilities of ... (this part will be supplemented)
|
#### 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 as well)
|
The simple visualization can be made with function `main`. (this part will be supplemented)
|
||||||
```kotlin
|
```kotlin
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
@ -16,12 +16,20 @@ fun main(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.makeVisionFile (
|
context.makeVisionFile (
|
||||||
Paths.get("customFile.html"),
|
Paths.get("nameFile.html"),
|
||||||
resourceLocation = ResourceLocation.EMBED
|
resourceLocation = ResourceLocation.EMBED
|
||||||
){
|
){
|
||||||
div {
|
div {
|
||||||
|
//first vision
|
||||||
vision {
|
vision {
|
||||||
solid {
|
solid {
|
||||||
|
//solids which you want to visualize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//second vision
|
||||||
|
vision {
|
||||||
|
solid {
|
||||||
|
//solids which you want to visualize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,16 +39,16 @@ fun main(){
|
|||||||
## Solids properties
|
## Solids properties
|
||||||
**We will analyze which basic properties solids have using `box` solid.**
|
**We will analyze which basic properties solids have using `box` solid.**
|
||||||
|
|
||||||
Basic properties:
|
*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.
|
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.
|
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)`
|
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`
|
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.
|
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.
|
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 are doing in more detail. Now, it does not really matter.***
|
***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
|
```kotlin
|
||||||
box(10, 10, 10, name = "small box"){
|
box(10, 10, 10, name = "small box"){
|
||||||
x = 0
|
x = 0
|
||||||
@ -65,7 +73,6 @@ box(40, 40, 40, name = "big box"){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/big-rotated-box.png)
|
![](../docs/images/big-rotated-box.png)
|
||||||
|
|
||||||
If we compare these boxes, we will see all differences.
|
If we compare these boxes, we will see all differences.
|
||||||
|
|
||||||
Here is the function `main` with both boxes.
|
Here is the function `main` with both boxes.
|
||||||
@ -107,51 +114,117 @@ fun main(){
|
|||||||
![](../docs/images/two-boxes-1.png)
|
![](../docs/images/two-boxes-1.png)
|
||||||
![](../docs/images/two-boxes-2.png)
|
![](../docs/images/two-boxes-2.png)
|
||||||
|
|
||||||
###Basic Solids
|
***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:
|
Now, let's see which solids can be visualized:
|
||||||
1) PolyLine
|
### 1) PolyLine
|
||||||
2) Box
|
|
||||||
|
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
|
```kotlin
|
||||||
box(50, 50, 50, name = "box") {
|
box(50, 50, 50, name = "box") {
|
||||||
x = 0
|
|
||||||
y = 0
|
|
||||||
z = 0
|
|
||||||
color("pink")
|
color("pink")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/box.png)
|
![](../docs/images/box.png)
|
||||||
|
|
||||||
|
Now, let's make `box` with bigger `y` value.
|
||||||
```kotlin
|
```kotlin
|
||||||
box(10, 25, 10, name = "high_box") {
|
box(10, 25, 10, name = "high box") {
|
||||||
x = 0
|
|
||||||
y = 0
|
|
||||||
z = 0
|
|
||||||
color("black")
|
color("black")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
As you can see, only rib of `y-axis` differs from other ribs.
|
||||||
|
|
||||||
![](../docs/images/high-box.png)
|
![](../docs/images/high-box.png)
|
||||||
|
|
||||||
|
For final trial, let's create `box` with bigger `x` value.
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
box(65, 40, 40, name = "wide_box") {
|
box(65, 40, 40, name = "wide box") {
|
||||||
x = 0
|
x = 0
|
||||||
y = 0
|
y = 0
|
||||||
z = 0
|
z = 0
|
||||||
color("black")
|
color("black")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
Predictably, only `x-axis` rib bigger than other ribs.
|
||||||
|
|
||||||
![](../docs/images/wide-box.png)
|
![](../docs/images/wide-box.png)
|
||||||
|
|
||||||
3) Sphere
|
### 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
|
```kotlin
|
||||||
sphere(50, name = "sphere") {
|
sphere(50, name = "sphere") {
|
||||||
x = 0
|
x = 0
|
||||||
y = 0
|
y = 0
|
||||||
z = 0
|
z = 0
|
||||||
|
opacity = 0.9
|
||||||
color("blue")
|
color("blue")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/sphere.png)
|
![](../docs/images/sphere.png)
|
||||||
4) Hexagon
|
|
||||||
|
### 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
|
```kotlin
|
||||||
hexagon(
|
hexagon(
|
||||||
Point3D(25, 30, 25),
|
Point3D(25, 30, 25),
|
||||||
@ -162,11 +235,14 @@ Now, let's see which solids can be visualized:
|
|||||||
Point3D(40, 18, 20),
|
Point3D(40, 18, 20),
|
||||||
Point3D(40, 18, 10),
|
Point3D(40, 18, 10),
|
||||||
Point3D(30, 18, 10),
|
Point3D(30, 18, 10),
|
||||||
name = "classic_hexagon"){
|
name = "classic hexagon"){
|
||||||
color("green")
|
color("green")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/classic-hexagon.png)
|
![](../docs/images/classic-hexagon.png)
|
||||||
|
|
||||||
|
Now, let's make a custom hexagon.
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
hexagon(
|
hexagon(
|
||||||
Point3D(5, 30, 5),
|
Point3D(5, 30, 5),
|
||||||
@ -182,28 +258,116 @@ Now, let's see which solids can be visualized:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/custom-hexagon.png)
|
![](../docs/images/custom-hexagon.png)
|
||||||
5) Cone
|
### 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
|
```kotlin
|
||||||
cone(60, 80, name = "cone") {
|
cone(60, 80, name = "cone") {
|
||||||
x = 0
|
|
||||||
y = 0
|
|
||||||
z = 0
|
|
||||||
color("beige")
|
color("beige")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/cone-1.png)
|
![](../docs/images/cone-1.png)
|
||||||
![](../docs/images/cone-2.png)
|
![](../docs/images/cone-2.png)
|
||||||
6) Cone Surface
|
|
||||||
|
First of all, we have to try to build a frustum cone:
|
||||||
```kotlin
|
```kotlin
|
||||||
coneSurface(60, 50, 30, 10, 100, name = "cone_surface") {
|
cone(60, 80, name = "cone") {
|
||||||
x = 0
|
color(0u, 40u, 0u)
|
||||||
y = 0
|
}
|
||||||
z = 0
|
```
|
||||||
|
![](../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")
|
color("red")
|
||||||
rotation = Point3D(2, 50, -9)
|
rotation = Point3D(2, 50, -9)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/cone-surface-1.png)
|
![](../docs/images/cone-surface-1.png)
|
||||||
![](../docs/images/cone-surface-2.png)
|
![](../docs/images/cone-surface-2.png)
|
||||||
7) Extruded
|
|
||||||
|
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<Point2D>` 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*
|
||||||
|
|
||||||
|
@ -25,11 +25,9 @@ kotlin{
|
|||||||
|
|
||||||
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
||||||
dependsOn(jsBrowserDistribution)
|
dependsOn(jsBrowserDistribution)
|
||||||
afterEvaluate {
|
|
||||||
from(jsBrowserDistribution)
|
from(jsBrowserDistribution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
|
@ -16,8 +16,6 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//enableFeaturePreview("GRADLE_METADATA")
|
|
||||||
|
|
||||||
rootProject.name = "visionforge"
|
rootProject.name = "visionforge"
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ dependencies{
|
|||||||
|
|
||||||
implementation(npm("@jetbrains/icons", "3.14.1"))
|
implementation(npm("@jetbrains/icons", "3.14.1"))
|
||||||
implementation(npm("@jetbrains/ring-ui", "4.0.7"))
|
implementation(npm("@jetbrains/ring-ui", "4.0.7"))
|
||||||
implementation(npm("core-js","3.12.1"))
|
|
||||||
implementation(npm("file-saver", "2.0.2"))
|
implementation(npm("file-saver", "2.0.2"))
|
||||||
compileOnly(npm("url-loader","4.1.1"))
|
compileOnly(npm("url-loader","4.1.1"))
|
||||||
compileOnly(npm("postcss-loader","5.2.0"))
|
compileOnly(npm("postcss-loader","5.2.0"))
|
||||||
|
@ -28,11 +28,10 @@ internal data class PropertyListener(
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("vision")
|
@SerialName("vision")
|
||||||
public open class VisionBase : Vision {
|
public open class VisionBase(
|
||||||
|
override @Transient var parent: VisionGroup? = null,
|
||||||
protected var properties: Config? = null
|
protected var properties: Config? = null
|
||||||
|
) : Vision {
|
||||||
@Transient
|
|
||||||
override var parent: VisionGroup? = null
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
protected fun getOrCreateProperties(): Config {
|
protected fun getOrCreateProperties(): Config {
|
||||||
@ -75,11 +74,14 @@ public open class VisionBase : Vision {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
||||||
|
val oldItem = properties?.getItem(name)
|
||||||
|
if(oldItem!= item) {
|
||||||
getOrCreateProperties().setItem(name, item)
|
getOrCreateProperties().setItem(name, item)
|
||||||
if (notify) {
|
if (notify) {
|
||||||
invalidateProperty(name)
|
invalidateProperty(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override val descriptor: NodeDescriptor? get() = null
|
override val descriptor: NodeDescriptor? get() = null
|
||||||
|
|
||||||
|
@ -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 vision a new value for vision content
|
||||||
* @param properties updated properties
|
* @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
|
@Serializable
|
||||||
public data class VisionChange(
|
public data class VisionChange(
|
||||||
public val void: Boolean = false,
|
public val delete: Boolean = false,
|
||||||
public val vision: Vision? = null,
|
public val vision: Vision? = null,
|
||||||
@Serializable(MetaSerializer::class) public val properties: Meta? = null,
|
@Serializable(MetaSerializer::class) public val properties: Meta? = null,
|
||||||
public val children: Map<Name, VisionChange>? = null,
|
public val children: Map<Name, VisionChange>? = null,
|
||||||
|
@ -58,6 +58,7 @@ public operator fun VisionGroup.iterator(): Iterator<Vision> = children.values.i
|
|||||||
public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty()
|
public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty()
|
||||||
|
|
||||||
public interface VisionContainerBuilder<in V : Vision> {
|
public interface VisionContainerBuilder<in V : Vision> {
|
||||||
|
//TODO add documentation
|
||||||
public operator fun set(name: Name?, child: V?)
|
public operator fun set(name: Name?, child: V?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import space.kscience.dataforge.names.*
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("vision.group")
|
@SerialName("vision.group")
|
||||||
public open class VisionGroupBase(
|
public open class VisionGroupBase(
|
||||||
@SerialName("children") internal val childrenInternal: MutableMap<NameToken, Vision> = LinkedHashMap(),
|
@SerialName("children") protected val childrenInternal: MutableMap<NameToken, Vision> = LinkedHashMap(),
|
||||||
) : VisionBase(), MutableVisionGroup {
|
) : VisionBase(), MutableVisionGroup {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,7 +134,7 @@ public open class VisionGroupBase(
|
|||||||
override fun update(change: VisionChange) {
|
override fun update(change: VisionChange) {
|
||||||
change.children?.forEach { (name, change) ->
|
change.children?.forEach { (name, change) ->
|
||||||
when {
|
when {
|
||||||
change.void -> set(name, null)
|
change.delete -> set(name, null)
|
||||||
change.vision != null -> set(name, change.vision)
|
change.vision != null -> set(name, change.vision)
|
||||||
else -> get(name)?.update(change)
|
else -> get(name)?.update(change)
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(visionSerializer, string)
|
public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(visionSerializer, string)
|
||||||
|
|
||||||
public fun encodeToString(vision: Vision): String = jsonFormat.encodeToString(visionSerializer, vision)
|
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)
|
public fun decodeFromJson(json: JsonElement): Vision = jsonFormat.decodeFromJsonElement(visionSerializer, json)
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package space.kscience.visionforge
|
package space.kscience.visionforge
|
||||||
|
|
||||||
|
import space.kscience.dataforge.meta.Config
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.set
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property containers are used to create a symmetric behaviors for vision properties and style builders
|
* Property containers are used to create a symmetric behaviors for vision properties and style builders
|
||||||
*/
|
*/
|
||||||
public interface VisionPropertyContainer<out T> {
|
public interface VisionPropertyContainer<out V: Vision> {
|
||||||
public fun getProperty(
|
public fun getProperty(
|
||||||
name: Name,
|
name: Name,
|
||||||
inherit: Boolean = false,
|
inherit: Boolean = false,
|
||||||
@ -16,3 +19,17 @@ public interface VisionPropertyContainer<out T> {
|
|||||||
|
|
||||||
public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true)
|
public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public open class SimpleVisionPropertyContainer<out V: Vision>(protected val config: Config): VisionPropertyContainer<V>{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,9 @@ package space.kscience.visionforge
|
|||||||
|
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.browser.window
|
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.*
|
||||||
import org.w3c.dom.url.URL
|
import org.w3c.dom.url.URL
|
||||||
import space.kscience.dataforge.context.*
|
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_ENDPOINT_ATTRIBUTE
|
||||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_FETCH_ATTRIBUTE
|
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_FETCH_ATTRIBUTE
|
||||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_NAME_ATTRIBUTE
|
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_NAME_ATTRIBUTE
|
||||||
import kotlin.collections.set
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.time.Duration
|
||||||
|
|
||||||
public class VisionClient : AbstractPlugin() {
|
public class VisionClient : AbstractPlugin() {
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
private val visionManager: VisionManager by require(VisionManager)
|
private val visionManager: VisionManager by require(VisionManager)
|
||||||
|
|
||||||
private val visionMap = HashMap<Element, Vision>()
|
//private val visionMap = HashMap<Element, Vision>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Up-going tree traversal in search for endpoint attribute
|
* 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 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) {
|
if (vision != null) {
|
||||||
visionMap[element] = vision
|
|
||||||
val renderer = findRendererFor(vision) ?: error("Could nof find renderer for $vision")
|
val renderer = findRendererFor(vision) ?: error("Could nof find renderer for $vision")
|
||||||
renderer.render(element, vision, outputMeta)
|
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,12 +144,13 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
visionManager.decodeFromString(it)
|
visionManager.decodeFromString(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (embeddedVision != null) {
|
when {
|
||||||
|
embeddedVision != null -> {
|
||||||
logger.info { "Found embedded vision for output with name $name" }
|
logger.info { "Found embedded vision for output with name $name" }
|
||||||
renderVision(element, embeddedVision, outputMeta)
|
renderVision(name, element, embeddedVision, outputMeta)
|
||||||
}
|
}
|
||||||
|
element.attributes[OUTPUT_FETCH_ATTRIBUTE] != null -> {
|
||||||
element.attributes[OUTPUT_FETCH_ATTRIBUTE]?.let { attr ->
|
val attr = element.attributes[OUTPUT_FETCH_ATTRIBUTE]!!
|
||||||
|
|
||||||
val fetchUrl = if (attr.value.isBlank() || attr.value == "auto") {
|
val fetchUrl = if (attr.value.isBlank() || attr.value == "auto") {
|
||||||
val endpoint = resolveEndpoint(element)
|
val endpoint = resolveEndpoint(element)
|
||||||
@ -103,61 +169,14 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
response.text().then { text ->
|
response.text().then { text ->
|
||||||
val vision = visionManager.decodeFromString(text)
|
val vision = visionManager.decodeFromString(text)
|
||||||
renderVision(element, vision, outputMeta)
|
renderVision(name, element, vision, outputMeta)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error { "Failed to fetch initial vision state from $fetchUrl" }
|
logger.error { "Failed to fetch initial vision state from $fetchUrl" }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ dependencies {
|
|||||||
exclude(group = "org.openjfx")
|
exclude(group = "org.openjfx")
|
||||||
}
|
}
|
||||||
|
|
||||||
api("org.fxyz3d:fxyz3d:0.5.2") {
|
api("org.fxyz3d:fxyz3d:0.5.4") {
|
||||||
exclude(module = "slf4j-simple")
|
exclude(module = "slf4j-simple")
|
||||||
}
|
}
|
||||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}")
|
api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}")
|
||||||
|
@ -15,7 +15,7 @@ import space.kscience.dataforge.names.asName
|
|||||||
import space.kscience.dataforge.values.*
|
import space.kscience.dataforge.values.*
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
class TextValueChooser : ValueChooserBase<TextField>() {
|
public class TextValueChooser : ValueChooserBase<TextField>() {
|
||||||
|
|
||||||
private val displayText: String
|
private val displayText: String
|
||||||
get() = currentValue().let {
|
get() = currentValue().let {
|
||||||
|
@ -14,7 +14,6 @@ import space.kscience.dataforge.meta.descriptors.ValueDescriptor
|
|||||||
import space.kscience.dataforge.misc.Named
|
import space.kscience.dataforge.misc.Named
|
||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.toName
|
import space.kscience.dataforge.names.toName
|
||||||
import space.kscience.dataforge.provider.provideByType
|
|
||||||
import space.kscience.dataforge.values.Null
|
import space.kscience.dataforge.values.Null
|
||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
import space.kscience.visionforge.widget
|
import space.kscience.visionforge.widget
|
||||||
@ -63,7 +62,7 @@ public interface ValueChooser {
|
|||||||
|
|
||||||
public fun setCallback(callback: ValueCallback)
|
public fun setCallback(callback: ValueCallback)
|
||||||
|
|
||||||
@Type("space.kscience.dataforge.vis.fx.valueChooserFactory")
|
@Type("space.kscience..fx.valueChooserFactory")
|
||||||
public interface Factory : Named {
|
public interface Factory : Named {
|
||||||
public operator fun invoke(meta: Meta = Meta.EMPTY): ValueChooser
|
public operator fun invoke(meta: Meta = Meta.EMPTY): ValueChooser
|
||||||
}
|
}
|
||||||
@ -75,7 +74,7 @@ public interface ValueChooser {
|
|||||||
TextValueChooser.name -> TextValueChooser
|
TextValueChooser.name -> TextValueChooser
|
||||||
ColorValueChooser.name -> ColorValueChooser
|
ColorValueChooser.name -> ColorValueChooser
|
||||||
ComboBoxValueChooser.name -> ComboBoxValueChooser
|
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,
|
context: Context,
|
||||||
value: ObservableValue<Value?>,
|
value: ObservableValue<Value?>,
|
||||||
descriptor: ValueDescriptor? = null,
|
descriptor: ValueDescriptor? = null,
|
||||||
|
@ -18,16 +18,18 @@ import tornadofx.*
|
|||||||
*
|
*
|
||||||
* @author Alexander Nozik
|
* @author Alexander Nozik
|
||||||
*/
|
*/
|
||||||
abstract class ValueChooserBase<out T : Node> : ValueChooser {
|
public abstract class ValueChooserBase<out T : Node> : ValueChooser {
|
||||||
|
|
||||||
override val node by lazy { buildNode() }
|
override val node: T by lazy { buildNode() }
|
||||||
final override val valueProperty = SimpleObjectProperty<Value>(Null)
|
final override val valueProperty: SimpleObjectProperty<Value> =
|
||||||
final override val descriptorProperty = SimpleObjectProperty<ValueDescriptor>()
|
SimpleObjectProperty<Value>(Null)
|
||||||
|
final override val descriptorProperty: SimpleObjectProperty<ValueDescriptor> =
|
||||||
|
SimpleObjectProperty<ValueDescriptor>()
|
||||||
|
|
||||||
override var descriptor: ValueDescriptor? by descriptorProperty
|
override var descriptor: ValueDescriptor? by descriptorProperty
|
||||||
override var value: Value? by valueProperty
|
override var value: Value? by valueProperty
|
||||||
|
|
||||||
fun resetValue() {
|
public fun resetValue() {
|
||||||
setDisplayValue(currentValue())
|
setDisplayValue(currentValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@ import space.kscience.dataforge.meta.update
|
|||||||
import space.kscience.visionforge.*
|
import space.kscience.visionforge.*
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
|
public class VisualObjectEditorFragment(public val selector: (Vision) -> Meta) : Fragment() {
|
||||||
|
|
||||||
val itemProperty = SimpleObjectProperty<Vision>()
|
public val itemProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
|
||||||
var item: Vision? by itemProperty
|
public var item: Vision? by itemProperty
|
||||||
val descriptorProperty = SimpleObjectProperty<NodeDescriptor>()
|
public val descriptorProperty: SimpleObjectProperty<NodeDescriptor> = SimpleObjectProperty<NodeDescriptor>()
|
||||||
|
|
||||||
constructor(
|
public constructor(
|
||||||
item: Vision?,
|
item: Vision?,
|
||||||
descriptor: NodeDescriptor?,
|
descriptor: NodeDescriptor?,
|
||||||
selector: (Vision) -> MutableItemProvider = { it.allProperties() },
|
selector: (Vision) -> MutableItemProvider = { it.allProperties() },
|
||||||
@ -30,13 +30,13 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
|
|||||||
|
|
||||||
private var currentConfig: Config? = null
|
private var currentConfig: Config? = null
|
||||||
|
|
||||||
private val configProperty: Binding<Config?> = itemProperty.objectBinding { visualObject ->
|
private val configProperty: Binding<Config?> = itemProperty.objectBinding { vision ->
|
||||||
if (visualObject == null) return@objectBinding null
|
if (vision == null) return@objectBinding null
|
||||||
val meta = selector(visualObject)
|
val meta = selector(vision)
|
||||||
val config = Config().apply {
|
val config = Config().apply {
|
||||||
update(meta)
|
update(meta)
|
||||||
onChange(this@VisualObjectEditorFragment) { key, _, after ->
|
onChange(this@VisualObjectEditorFragment) { key, _, after ->
|
||||||
visualObject.setProperty(key, after)
|
vision.setProperty(key, after)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//remember old config reference to cleanup listeners
|
//remember old config reference to cleanup listeners
|
||||||
@ -51,7 +51,7 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val styleBoxProperty: Binding<Node?> = configProperty.objectBinding() {
|
private val styleBoxProperty: Binding<Node?> = configProperty.objectBinding {
|
||||||
VBox().apply {
|
VBox().apply {
|
||||||
item?.styles?.forEach { styleName ->
|
item?.styles?.forEach { styleName ->
|
||||||
val styleMeta = item?.getStyle(styleName)
|
val styleMeta = item?.getStyle(styleName)
|
||||||
|
@ -3,6 +3,7 @@ package space.kscience.visionforge.editor
|
|||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.scene.control.SelectionMode
|
import javafx.scene.control.SelectionMode
|
||||||
import javafx.scene.control.TreeItem
|
import javafx.scene.control.TreeItem
|
||||||
|
import javafx.scene.layout.VBox
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.VisionGroup
|
import space.kscience.visionforge.VisionGroup
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
@ -29,13 +30,13 @@ private fun toTreeItem(vision: Vision, title: String): TreeItem<Pair<String, Vis
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VisualObjectTreeFragment : Fragment() {
|
public class VisualObjectTreeFragment : Fragment() {
|
||||||
val itemProperty = SimpleObjectProperty<Vision>()
|
public val itemProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
|
||||||
var item: Vision? by itemProperty
|
public var item: Vision? by itemProperty
|
||||||
|
|
||||||
val selectedProperty = SimpleObjectProperty<Vision>()
|
public val selectedProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
|
||||||
|
|
||||||
override val root = vbox {
|
override val root: VBox = vbox {
|
||||||
titledpane("Object tree", collapsible = false) {
|
titledpane("Object tree", collapsible = false) {
|
||||||
treeview<Pair<String, Vision>> {
|
treeview<Pair<String, Vision>> {
|
||||||
cellFormat {
|
cellFormat {
|
||||||
@ -47,7 +48,9 @@ class VisualObjectTreeFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
selectionModel.selectionMode = SelectionMode.SINGLE
|
selectionModel.selectionMode = SelectionMode.SINGLE
|
||||||
val selectedValue = selectionModel.selectedItemProperty().objectBinding { it?.value?.second }
|
val selectedValue = selectionModel.selectedItemProperty().objectBinding {
|
||||||
|
it?.value?.second
|
||||||
|
}
|
||||||
selectedProperty.bind(selectedValue)
|
selectedProperty.bind(selectedValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import kotlin.collections.set
|
|||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class FX3DPlugin : AbstractPlugin() {
|
public class FX3DPlugin : AbstractPlugin() {
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
private val objectFactories = HashMap<KClass<out Solid>, FX3DFactory<*>>()
|
private val objectFactories = HashMap<KClass<out Solid>, FX3DFactory<*>>()
|
||||||
@ -42,7 +42,7 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
as FX3DFactory<Solid>?
|
as FX3DFactory<Solid>?
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildNode(obj: Solid): Node {
|
public fun buildNode(obj: Solid): Node {
|
||||||
val binding = VisualObjectFXBinding(this, obj)
|
val binding = VisualObjectFXBinding(this, obj)
|
||||||
return when (obj) {
|
return when (obj) {
|
||||||
is SolidReferenceGroup -> referenceFactory(obj, binding)
|
is SolidReferenceGroup -> referenceFactory(obj, binding)
|
||||||
@ -65,6 +65,8 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
}
|
}
|
||||||
is SolidLabel -> Text(obj.text).apply {
|
is SolidLabel -> Text(obj.text).apply {
|
||||||
font = Font.font(obj.fontFamily, obj.fontSize)
|
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
|
x = -layoutBounds.width / 2
|
||||||
y = layoutBounds.height / 2
|
y = layoutBounds.height / 2
|
||||||
}
|
}
|
||||||
@ -129,10 +131,10 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : PluginFactory<FX3DPlugin> {
|
public companion object : PluginFactory<FX3DPlugin> {
|
||||||
override val tag = PluginTag("vision.fx3D", PluginTag.DATAFORGE_GROUP)
|
override val tag: PluginTag = PluginTag("vision.fx3D", PluginTag.DATAFORGE_GROUP)
|
||||||
override val type = FX3DPlugin::class
|
override val type: KClass<FX3DPlugin> = FX3DPlugin::class
|
||||||
override fun invoke(meta: Meta, context: Context) = FX3DPlugin()
|
override fun invoke(meta: Meta, context: Context): FX3DPlugin = FX3DPlugin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,14 +142,14 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
* Builder and updater for three.js object
|
* Builder and updater for three.js object
|
||||||
*/
|
*/
|
||||||
@Type(TYPE)
|
@Type(TYPE)
|
||||||
interface FX3DFactory<in T : Solid> {
|
public interface FX3DFactory<in T : Solid> {
|
||||||
|
|
||||||
val type: KClass<in T>
|
public val type: KClass<in T>
|
||||||
|
|
||||||
operator fun invoke(obj: T, binding: VisualObjectFXBinding): Node
|
public operator fun invoke(obj: T, binding: VisualObjectFXBinding): Node
|
||||||
|
|
||||||
companion object {
|
public companion object {
|
||||||
const val TYPE = "fx3DFactory"
|
public const val TYPE: String = "fx3DFactory"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import javafx.application.Platform
|
|||||||
import javafx.beans.property.ObjectProperty
|
import javafx.beans.property.ObjectProperty
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.scene.*
|
import javafx.scene.*
|
||||||
|
import javafx.scene.layout.BorderPane
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import org.fxyz3d.scene.Axes
|
import org.fxyz3d.scene.Axes
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
@ -11,31 +12,32 @@ import space.kscience.dataforge.context.ContextAware
|
|||||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
class FXCanvas3D(
|
public class FXCanvas3D(
|
||||||
val plugin: FX3DPlugin,
|
public val fx3d: FX3DPlugin,
|
||||||
val spec: Canvas3DOptions = Canvas3DOptions.empty(),
|
public val options: Canvas3DOptions = Canvas3DOptions.empty(),
|
||||||
) : Fragment(), ContextAware {
|
) : 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))
|
//transforms.add(Rotate(180.0, Rotate.Z_AXIS))
|
||||||
}
|
}
|
||||||
|
|
||||||
val axes = Axes().also {
|
public val axes: Axes = Axes().also {
|
||||||
it.setHeight(spec.axes.size)
|
it.setHeight(options.axes.size)
|
||||||
it.setRadius(spec.axes.width)
|
it.setRadius(options.axes.width)
|
||||||
it.isVisible = spec.axes.visible
|
it.isVisible = options.axes.visible
|
||||||
world.add(it)
|
world.add(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val light = AmbientLight()
|
public val light: AmbientLight = AmbientLight()
|
||||||
|
|
||||||
private val camera = PerspectiveCamera().apply {
|
private val camera = PerspectiveCamera().apply {
|
||||||
nearClip = spec.camera.nearClip
|
nearClip = options.camera.nearClip
|
||||||
farClip = spec.camera.farClip
|
farClip = options.camera.farClip
|
||||||
fieldOfView = spec.camera.fov.toDouble()
|
fieldOfView = options.camera.fov.toDouble()
|
||||||
this.add(light)
|
|
||||||
|
add(light)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val canvas = SubScene(
|
private val canvas = SubScene(
|
||||||
@ -49,19 +51,19 @@ class FXCanvas3D(
|
|||||||
scene.camera = camera
|
scene.camera = camera
|
||||||
}
|
}
|
||||||
|
|
||||||
override val root = borderpane {
|
override val root: BorderPane = borderpane {
|
||||||
center = canvas
|
center = canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
val controls = camera.orbitControls(canvas, spec.camera).also {
|
public val controls: OrbitControls = camera.orbitControls(canvas, options.camera).also {
|
||||||
world.add(it.centerMarker)
|
world.add(it.centerMarker)
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootObjectProperty: ObjectProperty<Solid> = SimpleObjectProperty()
|
public val rootObjectProperty: ObjectProperty<Solid> = SimpleObjectProperty()
|
||||||
var rootObject: Solid? by rootObjectProperty
|
public var rootObject: Solid? by rootObjectProperty
|
||||||
|
|
||||||
private val rootNodeProperty = rootObjectProperty.objectBinding {
|
private val rootNodeProperty = rootObjectProperty.objectBinding {
|
||||||
it?.let { plugin.buildNode(it) }
|
it?.let { fx3d.buildNode(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -79,7 +81,7 @@ class FXCanvas3D(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(vision: Solid) {
|
public fun render(vision: Solid) {
|
||||||
rootObject = vision
|
rootObject = vision
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -38,7 +38,7 @@ private fun MeshView.toCSG(): CSG {
|
|||||||
return CSG.fromPolygons(polygons)
|
return CSG.fromPolygons(polygons)
|
||||||
}
|
}
|
||||||
|
|
||||||
class FXCompositeFactory(val plugin: FX3DPlugin) : FX3DFactory<Composite> {
|
public class FXCompositeFactory(public val plugin: FX3DPlugin) : FX3DFactory<Composite> {
|
||||||
override val type: KClass<in Composite>
|
override val type: KClass<in Composite>
|
||||||
get() = Composite::class
|
get() = Composite::class
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class FXCompositeFactory(val plugin: FX3DPlugin) : FX3DFactory<Composite> {
|
|||||||
val firstCSG = first.toCSG()
|
val firstCSG = first.toCSG()
|
||||||
val secondCSG = second.toCSG()
|
val secondCSG = second.toCSG()
|
||||||
val resultCSG = when (obj.compositeType) {
|
val resultCSG = when (obj.compositeType) {
|
||||||
CompositeType.UNION -> firstCSG.union(secondCSG)
|
CompositeType.SUM, CompositeType.UNION -> firstCSG.union(secondCSG)
|
||||||
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
||||||
CompositeType.SUBTRACT -> firstCSG.difference(secondCSG)
|
CompositeType.SUBTRACT -> firstCSG.difference(secondCSG)
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,14 @@ import javafx.scene.Node
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
|
||||||
object FXConvexFactory : FX3DFactory<Convex> {
|
public object FXConvexFactory : FX3DFactory<Convex> {
|
||||||
override val type: KClass<in Convex> get() = Convex::class
|
override val type: KClass<in Convex> get() = Convex::class
|
||||||
|
|
||||||
override fun invoke(obj: Convex, binding: VisualObjectFXBinding): Node {
|
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()
|
return hull.toNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import space.kscience.visionforge.Vision
|
|||||||
import space.kscience.visionforge.onPropertyChange
|
import space.kscience.visionforge.onPropertyChange
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGroup> {
|
public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGroup> {
|
||||||
override val type: KClass<in SolidReferenceGroup> get() = SolidReferenceGroup::class
|
override val type: KClass<in SolidReferenceGroup> get() = SolidReferenceGroup::class
|
||||||
|
|
||||||
override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node {
|
override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node {
|
||||||
|
@ -8,7 +8,7 @@ import org.fxyz3d.geometry.Face3
|
|||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
object FXShapeFactory : FX3DFactory<GeometrySolid> {
|
public object FXShapeFactory : FX3DFactory<GeometrySolid> {
|
||||||
override val type: KClass<in GeometrySolid> get() = GeometrySolid::class
|
override val type: KClass<in GeometrySolid> get() = GeometrySolid::class
|
||||||
|
|
||||||
override fun invoke(obj: GeometrySolid, binding: VisualObjectFXBinding): MeshView {
|
override fun invoke(obj: GeometrySolid, binding: VisualObjectFXBinding): MeshView {
|
||||||
|
@ -1,127 +1,110 @@
|
|||||||
package space.kscience.visionforge.solid
|
package space.kscience.visionforge.solid
|
||||||
|
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.property.SimpleBooleanProperty
|
||||||
import javafx.beans.property.SimpleDoubleProperty
|
import javafx.beans.property.SimpleDoubleProperty
|
||||||
import javafx.event.EventHandler
|
import javafx.event.EventHandler
|
||||||
import javafx.geometry.Point3D
|
|
||||||
import javafx.scene.Camera
|
import javafx.scene.Camera
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.SubScene
|
import javafx.scene.SubScene
|
||||||
import javafx.scene.input.MouseEvent
|
|
||||||
import javafx.scene.input.ScrollEvent
|
|
||||||
import javafx.scene.shape.Sphere
|
import javafx.scene.shape.Sphere
|
||||||
import javafx.scene.transform.Rotate
|
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 javafx.scene.transform.Translate
|
||||||
|
import space.kscience.dataforge.meta.useProperty
|
||||||
import tornadofx.*
|
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
|
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)
|
public var zenith: Double by zenithProperty
|
||||||
var azimuth by azimuthProperty
|
|
||||||
|
|
||||||
val zenithProperty = SimpleDoubleProperty(PI / 2 - spec.latitude)
|
|
||||||
var zenith by zenithProperty
|
|
||||||
|
|
||||||
val latitudeProperty = zenithProperty.unaryMinus().plus(PI / 2)
|
private val baseTranslate = Translate(0.0, 0.0, 0.0)
|
||||||
val latitude by latitudeProperty
|
|
||||||
|
|
||||||
val baseXProperty = SimpleDoubleProperty(0.0)
|
public var x: Double by baseTranslate.xProperty()
|
||||||
var x by baseXProperty
|
public var y: Double by baseTranslate.yProperty()
|
||||||
val baseYProperty = SimpleDoubleProperty(0.0)
|
public var z: Double by baseTranslate.zProperty()
|
||||||
var y by baseYProperty
|
|
||||||
val baseZProperty = SimpleDoubleProperty(0.0)
|
|
||||||
var z by baseZProperty
|
|
||||||
|
|
||||||
private val baseTranslate = Translate()
|
private val distanceProperty = SimpleDoubleProperty().apply {
|
||||||
|
spec.useProperty(CameraSpec::distance) {
|
||||||
|
set(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// val basePositionProperty: ObjectBinding<Point3D> =
|
private val distanceTranslation = Translate().apply {
|
||||||
// nonNullObjectBinding(baseXProperty, baseYProperty, baseZProperty) {
|
zProperty().bind(-distanceProperty)
|
||||||
// Point3D(x, y, z)
|
}
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val basePosition by basePositionProperty
|
|
||||||
|
|
||||||
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 {
|
Sphere(10.0).also {
|
||||||
it.transforms.setAll(baseTranslate)
|
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 {
|
init {
|
||||||
camera.transforms.setAll(ry, rx, translate)
|
camera.transforms.setAll(
|
||||||
update()
|
baseTranslate,
|
||||||
val listener = InvalidationListener {
|
yUpRotation,
|
||||||
update()
|
azimuthRotation,
|
||||||
}
|
zenithRotation,
|
||||||
distanceProperty.addListener(listener)
|
distanceTranslation,
|
||||||
azimuthProperty.addListener(listener)
|
centering,
|
||||||
zenithProperty.addListener(listener)
|
)
|
||||||
baseXProperty.addListener(listener)
|
|
||||||
baseYProperty.addListener(listener)
|
|
||||||
baseZProperty.addListener(listener)
|
|
||||||
|
|
||||||
canvas.apply {
|
canvas.apply {
|
||||||
// center.xProperty().bind(widthProperty().divide(2))
|
|
||||||
// center.zProperty().bind(heightProperty().divide(2))
|
|
||||||
handleMouse()
|
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() {
|
private fun Node.handleMouse() {
|
||||||
|
|
||||||
var mousePosX = 0.0
|
var mousePosX = 0.0
|
||||||
@ -131,20 +114,21 @@ class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec:
|
|||||||
var mouseDeltaX: Double
|
var mouseDeltaX: Double
|
||||||
var mouseDeltaY: Double
|
var mouseDeltaY: Double
|
||||||
|
|
||||||
onMousePressed = EventHandler<MouseEvent> { me ->
|
onMousePressed = EventHandler { me ->
|
||||||
mousePosX = me.sceneX
|
mousePosX = me.sceneX
|
||||||
mousePosY = me.sceneY
|
mousePosY = me.sceneY
|
||||||
mouseOldX = me.sceneX
|
mouseOldX = me.sceneX
|
||||||
mouseOldY = me.sceneY
|
mouseOldY = me.sceneY
|
||||||
|
inProgressProperty.set(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseDragged = EventHandler<MouseEvent> { me ->
|
onMouseDragged = EventHandler { me ->
|
||||||
mouseOldX = mousePosX
|
mouseOldX = mousePosX
|
||||||
mouseOldY = mousePosY
|
mouseOldY = mousePosY
|
||||||
mousePosX = me.sceneX
|
mousePosX = me.sceneX
|
||||||
mousePosY = me.sceneY
|
mousePosY = me.sceneY
|
||||||
mouseDeltaX = mousePosX - mouseOldX
|
mouseDeltaX = mouseOldX - mousePosX
|
||||||
mouseDeltaY = mousePosY - mouseOldY
|
mouseDeltaY = mouseOldY - mousePosY
|
||||||
|
|
||||||
val modifier = when {
|
val modifier = when {
|
||||||
me.isControlDown -> CONTROL_MULTIPLIER
|
me.isControlDown -> CONTROL_MULTIPLIER
|
||||||
@ -153,19 +137,24 @@ class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (me.isPrimaryButtonDown) {
|
if (me.isPrimaryButtonDown) {
|
||||||
azimuth = (azimuth - mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED).coerceIn(0.0, 2 * PI)
|
azimuth = (azimuth - mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED)
|
||||||
zenith = (zenith - mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED).coerceIn(0.0,PI)
|
zenith = (zenith - mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED).coerceIn(-PI/2, PI/2)
|
||||||
} else if (me.isSecondaryButtonDown) {
|
} else if (me.isSecondaryButtonDown) {
|
||||||
x += MOUSE_SPEED * modifier * TRACK_SPEED * (mouseDeltaX * cos(azimuth) + mouseDeltaY * sin(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))
|
z += MOUSE_SPEED * modifier * TRACK_SPEED * ( mouseDeltaX * sin(azimuth) + mouseDeltaY * cos(azimuth))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onScroll = EventHandler<ScrollEvent> { event ->
|
|
||||||
|
onMouseReleased = EventHandler {
|
||||||
|
inProgressProperty.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll = EventHandler { event ->
|
||||||
distance = max(1.0, distance - MOUSE_SPEED * event.deltaY * RESIZE_SPEED)
|
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 CONTROL_MULTIPLIER = 0.1
|
||||||
private const val SHIFT_MULTIPLIER = 10.0
|
private const val SHIFT_MULTIPLIER = 10.0
|
||||||
private const val MOUSE_SPEED = 0.1
|
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) =
|
public fun Camera.orbitControls(canvas: SubScene, spec: CameraSpec): OrbitControls =
|
||||||
OrbitControls(this, canvas, spec)
|
OrbitControls(this, canvas, spec)
|
@ -14,7 +14,7 @@ import tornadofx.*
|
|||||||
/**
|
/**
|
||||||
* A caching binding collection for [Vision] properties
|
* 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<Name, ObjectBinding<MetaItem?>>()
|
private val bindings = HashMap<Name, ObjectBinding<MetaItem?>>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -33,15 +33,13 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public operator fun get(key: Name): ObjectBinding<MetaItem?> {
|
public operator fun get(key: Name): ObjectBinding<MetaItem?> = bindings.getOrPut(key) {
|
||||||
return bindings.getOrPut(key) {
|
|
||||||
object : ObjectBinding<MetaItem?>() {
|
object : ObjectBinding<MetaItem?>() {
|
||||||
override fun computeValue(): MetaItem? = obj.getProperty(key)
|
override fun computeValue(): MetaItem? = obj.getProperty(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public operator fun get(key: String) = get(key.toName())
|
public operator fun get(key: String): ObjectBinding<TypedMetaItem<*>?> = get(key.toName())
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ObjectBinding<MetaItem?>.value(): Binding<Value?> = objectBinding { it.value }
|
public fun ObjectBinding<MetaItem?>.value(): Binding<Value?> = objectBinding { it.value }
|
||||||
|
@ -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<Int, Counter>()
|
|
||||||
//
|
|
||||||
// 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)
|
|
||||||
//}
|
|
@ -41,7 +41,7 @@ public class GdmlTransformer {
|
|||||||
|
|
||||||
internal val styleCache = HashMap<Name, Meta>()
|
internal val styleCache = HashMap<Name, Meta>()
|
||||||
|
|
||||||
public fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
public fun Solid.registerAndUseStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
||||||
styleCache.getOrPut(name.toName()) {
|
styleCache.getOrPut(name.toName()) {
|
||||||
Meta(builder)
|
Meta(builder)
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ public class GdmlTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun Solid.transparent() {
|
public fun Solid.transparent() {
|
||||||
useStyle("transparent") {
|
registerAndUseStyle("transparent") {
|
||||||
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
||||||
"edges.enabled" put true
|
"edges.enabled" put true
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ public class GdmlTransformer {
|
|||||||
|
|
||||||
if (parent.physVolumes.isNotEmpty()) transparent()
|
if (parent.physVolumes.isNotEmpty()) transparent()
|
||||||
|
|
||||||
useStyle(styleName) {
|
registerAndUseStyle(styleName) {
|
||||||
val vfMaterial = SolidMaterial().apply {
|
val vfMaterial = SolidMaterial().apply {
|
||||||
configurePaint(material, solid)
|
configurePaint(material, solid)
|
||||||
}
|
}
|
||||||
@ -125,7 +125,11 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
|
|
||||||
fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) {
|
fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) {
|
||||||
val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref)
|
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 {
|
private fun proxySolid(root: Gdml, group: SolidGroup, solid: GdmlSolid, name: String): SolidReferenceGroup {
|
||||||
@ -159,26 +163,27 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
newScale: GdmlScale? = null,
|
newScale: GdmlScale? = null,
|
||||||
): T = apply {
|
): T = apply {
|
||||||
newPos?.let {
|
newPos?.let {
|
||||||
val point = Point3D(it.x(settings.lUnit), it.y(settings.lUnit), it.z(settings.lUnit))
|
val gdmlX = it.x(settings.lUnit)
|
||||||
if (point != Point3D.ZERO) {
|
if (gdmlX != 0f) x = gdmlX
|
||||||
position = point
|
val gdmlY = it.y(settings.lUnit)
|
||||||
}
|
if (gdmlY != 0f) y = gdmlY
|
||||||
|
val gdmlZ = it.z(settings.lUnit)
|
||||||
|
if (gdmlZ != 0f) z = gdmlZ
|
||||||
}
|
}
|
||||||
newRotation?.let {
|
newRotation?.let {
|
||||||
val point = Point3D(it.x(settings.aUnit), it.y(settings.aUnit), it.z(settings.aUnit))
|
val gdmlX = it.x(settings.aUnit)
|
||||||
if (point != Point3D.ZERO) {
|
if (gdmlX != 0f) rotationX = gdmlX
|
||||||
rotation = point
|
val gdmlY = it.y(settings.aUnit)
|
||||||
}
|
if (gdmlY != 0f) rotationY = gdmlY
|
||||||
//this@withPosition.rotationOrder = RotationOrder.ZXY
|
val gdmlZ = it.z(settings.aUnit)
|
||||||
|
if (gdmlZ != 0f) rotationZ = gdmlZ
|
||||||
}
|
}
|
||||||
newScale?.let {
|
newScale?.let {
|
||||||
val point = Point3D(it.x, it.y, it.z)
|
if (it.x != 1f) scaleX = it.x
|
||||||
if (point != Point3D.ONE) {
|
if (it.y != 1f) scaleY = it.y
|
||||||
scale = point
|
if (it.z != 1f) scaleZ = it.z
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO convert units if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : Solid> T.withPosition(root: Gdml, physVolume: GdmlPhysVolume): T = withPosition(
|
fun <T : Solid> T.withPosition(root: Gdml, physVolume: GdmlPhysVolume): T = withPosition(
|
||||||
physVolume.resolvePosition(root),
|
physVolume.resolvePosition(root),
|
||||||
@ -227,11 +232,10 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
bottomRadius = solid.rmax1 * lScale,
|
bottomRadius = solid.rmax1 * lScale,
|
||||||
height = solid.z * lScale,
|
height = solid.z * lScale,
|
||||||
upperRadius = solid.rmax2 * lScale,
|
upperRadius = solid.rmax2 * lScale,
|
||||||
|
startAngle = solid.startphi * aScale,
|
||||||
|
angle = solid.deltaphi * aScale,
|
||||||
name = name
|
name = name
|
||||||
) {
|
)
|
||||||
startAngle = solid.startphi * aScale
|
|
||||||
angle = solid.deltaphi * aScale
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
coneSurface(
|
coneSurface(
|
||||||
bottomOuterRadius = solid.rmax1 * lScale,
|
bottomOuterRadius = solid.rmax1 * lScale,
|
||||||
@ -239,11 +243,10 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
height = solid.z * lScale,
|
height = solid.z * lScale,
|
||||||
topOuterRadius = solid.rmax2 * lScale,
|
topOuterRadius = solid.rmax2 * lScale,
|
||||||
topInnerRadius = solid.rmin2 * lScale,
|
topInnerRadius = solid.rmin2 * lScale,
|
||||||
|
startAngle = solid.startphi * aScale,
|
||||||
|
angle = solid.deltaphi * aScale,
|
||||||
name = name
|
name = name
|
||||||
) {
|
)
|
||||||
startAngle = solid.startphi * aScale
|
|
||||||
angle = solid.deltaphi * aScale
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is GdmlXtru -> extrude(name) {
|
is GdmlXtru -> extrude(name) {
|
||||||
shape {
|
shape {
|
||||||
@ -271,12 +274,15 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
scaleZ = solid.scale.z.toFloat()
|
scaleZ = solid.scale.z.toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is GdmlSphere -> sphereLayer(solid.rmax * lScale, solid.rmin * lScale, name) {
|
is GdmlSphere -> sphereLayer(
|
||||||
phi = solid.deltaphi * aScale
|
outerRadius = solid.rmax * lScale,
|
||||||
theta = solid.deltatheta * aScale
|
innerRadius = solid.rmin * lScale,
|
||||||
phiStart = solid.startphi * aScale
|
phi = solid.deltaphi * aScale,
|
||||||
thetaStart = solid.starttheta * aScale
|
theta = solid.deltatheta * aScale,
|
||||||
}
|
phiStart = solid.startphi * aScale,
|
||||||
|
thetaStart = solid.starttheta * aScale,
|
||||||
|
name = name,
|
||||||
|
)
|
||||||
is GdmlOrb -> sphere(solid.r * lScale, name = name)
|
is GdmlOrb -> sphere(solid.r * lScale, name = name)
|
||||||
is GdmlPolyhedra -> extrude(name) {
|
is GdmlPolyhedra -> extrude(name) {
|
||||||
//getting the radius of first
|
//getting the radius of first
|
||||||
@ -297,7 +303,7 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
|||||||
val first: GdmlSolid = solid.first.resolve(root) ?: error("")
|
val first: GdmlSolid = solid.first.resolve(root) ?: error("")
|
||||||
val second: GdmlSolid = solid.second.resolve(root) ?: error("")
|
val second: GdmlSolid = solid.second.resolve(root) ?: error("")
|
||||||
val type: CompositeType = when (solid) {
|
val type: CompositeType = when (solid) {
|
||||||
is GdmlUnion -> CompositeType.UNION
|
is GdmlUnion -> CompositeType.SUM // dumb sum for better performance
|
||||||
is GdmlSubtraction -> CompositeType.SUBTRACT
|
is GdmlSubtraction -> CompositeType.SUBTRACT
|
||||||
is GdmlIntersection -> CompositeType.INTERSECT
|
is GdmlIntersection -> CompositeType.INTERSECT
|
||||||
}
|
}
|
@ -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<Name, VisionCounterTree>
|
||||||
|
) {
|
||||||
|
|
||||||
|
// self count for prototypes
|
||||||
|
var selfCount = 1
|
||||||
|
|
||||||
|
val children: Map<NameToken, VisionCounterTree> 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<VisionCounterTree> = sequence {
|
||||||
|
yield(this@topToBottom)
|
||||||
|
children.values.forEach {
|
||||||
|
yieldAll(it.topToBottom())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun SolidGroup.markLayers(thresholds: List<Int> = 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<VisionCounterTree>(
|
||||||
|
{ 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" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ class TestCubes {
|
|||||||
val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box
|
val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box
|
||||||
assertNotNull(smallBoxPrototype)
|
assertNotNull(smallBoxPrototype)
|
||||||
assertEquals(30.0, smallBoxPrototype.xSize.toDouble())
|
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)
|
assertNotNull(smallBoxVision)
|
||||||
assertEquals(30.0, smallBoxVision.xSize.toDouble())
|
assertEquals(30.0, smallBoxVision.xSize.toDouble())
|
||||||
}
|
}
|
||||||
|
@ -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++
|
|
||||||
}
|
|
@ -4,9 +4,6 @@ import space.kscience.gdml.Gdml
|
|||||||
import space.kscience.gdml.decodeFromFile
|
import space.kscience.gdml.decodeFromFile
|
||||||
import space.kscience.visionforge.solid.SolidGroup
|
import space.kscience.visionforge.solid.SolidGroup
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
|
|
||||||
public actual typealias Counter = AtomicInteger
|
|
||||||
|
|
||||||
public fun SolidGroup.gdml(
|
public fun SolidGroup.gdml(
|
||||||
file: Path,
|
file: Path,
|
||||||
|
@ -5,7 +5,6 @@ import kotlinx.coroutines.withContext
|
|||||||
import nl.adaptivity.xmlutil.StAXReader
|
import nl.adaptivity.xmlutil.StAXReader
|
||||||
import space.kscience.gdml.Gdml
|
import space.kscience.gdml.Gdml
|
||||||
import space.kscience.gdml.decodeFromReader
|
import space.kscience.gdml.decodeFromReader
|
||||||
import space.kscience.visionforge.solid.prototype
|
|
||||||
import space.kscience.visionforge.visitor.countDistinct
|
import space.kscience.visionforge.visitor.countDistinct
|
||||||
import space.kscience.visionforge.visitor.flowStatistics
|
import space.kscience.visionforge.visitor.flowStatistics
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -23,7 +22,7 @@ suspend fun main() {
|
|||||||
|
|
||||||
|
|
||||||
vision.flowStatistics<KClass<*>>{ _, child ->
|
vision.flowStatistics<KClass<*>>{ _, child ->
|
||||||
child.prototype::class
|
child::class
|
||||||
}.countDistinct().forEach { (depth, size) ->
|
}.countDistinct().forEach { (depth, size) ->
|
||||||
println("$depth\t$size")
|
println("$depth\t$size")
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@ import io.ktor.server.engine.embeddedServer
|
|||||||
import io.ktor.websocket.WebSockets
|
import io.ktor.websocket.WebSockets
|
||||||
import io.ktor.websocket.webSocket
|
import io.ktor.websocket.webSocket
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.channels.consumeEach
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
@ -131,6 +133,15 @@ public class VisionServer internal constructor(
|
|||||||
application.log.debug("Opened server socket for $name")
|
application.log.debug("Opened server socket for $name")
|
||||||
val vision: Vision = visions[name.toName()] ?: error("Plot with id='$name' not registered")
|
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 {
|
try {
|
||||||
withContext(visionManager.context.coroutineContext) {
|
withContext(visionManager.context.coroutineContext) {
|
||||||
vision.flowChanges(visionManager, Duration.milliseconds(updateInterval)).collect { update ->
|
vision.flowChanges(visionManager, Duration.milliseconds(updateInterval)).collect { update ->
|
||||||
|
@ -6,7 +6,8 @@ import space.kscience.dataforge.meta.update
|
|||||||
import space.kscience.visionforge.*
|
import space.kscience.visionforge.*
|
||||||
|
|
||||||
public enum class CompositeType {
|
public enum class CompositeType {
|
||||||
UNION,
|
SUM, // Dumb sum of meshes
|
||||||
|
UNION, //CSG union
|
||||||
INTERSECT,
|
INTERSECT,
|
||||||
SUBTRACT
|
SUBTRACT
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@ import kotlin.math.sin
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.cone")
|
@SerialName("solid.cone")
|
||||||
public class ConeSegment(
|
public class ConeSegment(
|
||||||
public var bottomRadius: Float,
|
public val bottomRadius: Float,
|
||||||
public var height: Float,
|
public val height: Float,
|
||||||
public var topRadius: Float,
|
public val topRadius: Float,
|
||||||
public var startAngle: Float = 0f,
|
public val startAngle: Float = 0f,
|
||||||
public var angle: Float = PI2
|
public val angle: Float = PI2
|
||||||
) : SolidBase(), GeometrySolid {
|
) : SolidBase(), GeometrySolid {
|
||||||
|
|
||||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||||
@ -83,10 +83,14 @@ public inline fun VisionContainerBuilder<Solid>.cone(
|
|||||||
bottomRadius: Number,
|
bottomRadius: Number,
|
||||||
height: Number,
|
height: Number,
|
||||||
upperRadius: Number = 0.0,
|
upperRadius: Number = 0.0,
|
||||||
|
startAngle: Number = 0f,
|
||||||
|
angle: Number = PI2,
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
block: ConeSegment.() -> Unit = {}
|
block: ConeSegment.() -> Unit = {}
|
||||||
): ConeSegment = ConeSegment(
|
): ConeSegment = ConeSegment(
|
||||||
bottomRadius.toFloat(),
|
bottomRadius.toFloat(),
|
||||||
height.toFloat(),
|
height.toFloat(),
|
||||||
topRadius = upperRadius.toFloat()
|
topRadius = upperRadius.toFloat(),
|
||||||
|
startAngle = startAngle.toFloat(),
|
||||||
|
angle = angle.toFloat()
|
||||||
).apply(block).also { set(name, it) }
|
).apply(block).also { set(name, it) }
|
@ -16,13 +16,13 @@ import kotlin.math.sin
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.coneSurface")
|
@SerialName("solid.coneSurface")
|
||||||
public class ConeSurface(
|
public class ConeSurface(
|
||||||
public var bottomRadius: Float,
|
public val bottomRadius: Float,
|
||||||
public var bottomInnerRadius: Float,
|
public val bottomInnerRadius: Float,
|
||||||
public var height: Float,
|
public val height: Float,
|
||||||
public var topRadius: Float,
|
public val topRadius: Float,
|
||||||
public var topInnerRadius: Float,
|
public val topInnerRadius: Float,
|
||||||
public var startAngle: Float = 0f,
|
public val startAngle: Float = 0f,
|
||||||
public var angle: Float = PI2,
|
public val angle: Float = PI2,
|
||||||
) : SolidBase(), GeometrySolid {
|
) : SolidBase(), GeometrySolid {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -148,6 +148,8 @@ public inline fun VisionContainerBuilder<Solid>.coneSurface(
|
|||||||
height: Number,
|
height: Number,
|
||||||
topOuterRadius: Number,
|
topOuterRadius: Number,
|
||||||
topInnerRadius: Number,
|
topInnerRadius: Number,
|
||||||
|
startAngle: Number = 0f,
|
||||||
|
angle: Number = PI2,
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
block: ConeSurface.() -> Unit = {},
|
block: ConeSurface.() -> Unit = {},
|
||||||
): ConeSurface = ConeSurface(
|
): ConeSurface = ConeSurface(
|
||||||
@ -156,4 +158,6 @@ public inline fun VisionContainerBuilder<Solid>.coneSurface(
|
|||||||
height = height.toFloat(),
|
height = height.toFloat(),
|
||||||
topRadius = topOuterRadius.toFloat(),
|
topRadius = topOuterRadius.toFloat(),
|
||||||
topInnerRadius = topInnerRadius.toFloat(),
|
topInnerRadius = topInnerRadius.toFloat(),
|
||||||
|
startAngle = startAngle.toFloat(),
|
||||||
|
angle = angle.toFloat()
|
||||||
).apply(block).also { set(name, it) }
|
).apply(block).also { set(name, it) }
|
@ -2,9 +2,8 @@ package space.kscience.visionforge.solid
|
|||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.visionforge.VisionBuilder
|
import space.kscience.dataforge.meta.Config
|
||||||
import space.kscience.visionforge.VisionContainerBuilder
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.set
|
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
@ -13,7 +12,7 @@ import kotlin.math.sin
|
|||||||
public typealias Shape2D = List<Point2D>
|
public typealias Shape2D = List<Point2D>
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public class Shape2DBuilder(private val points: MutableList<Point2D> = ArrayList()) {
|
public class Shape2DBuilder(private val points: ArrayList<Point2D> = ArrayList()) {
|
||||||
|
|
||||||
public fun point(x: Number, y: Number) {
|
public fun point(x: Number, y: Number) {
|
||||||
points.add(Point2D(x, y))
|
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
|
@Serializable
|
||||||
@SerialName("solid.extrude")
|
@SerialName("solid.extrude")
|
||||||
public class Extruded(
|
public class Extruded(
|
||||||
public var shape: List<Point2D> = ArrayList(),
|
public val shape: List<Point2D>,
|
||||||
public var layers: MutableList<Layer> = ArrayList()
|
public val layers: List<Layer>
|
||||||
) : SolidBase(), GeometrySolid {
|
) : SolidBase(), GeometrySolid, VisionPropertyContainer<Extruded> {
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||||
val shape: Shape2D = shape
|
val shape: Shape2D = shape
|
||||||
@ -103,6 +92,26 @@ public class Extruded(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ExtrudeBuilder(
|
||||||
|
public var shape: List<Point2D> = emptyList(),
|
||||||
|
|
||||||
|
public var layers: MutableList<Layer> = ArrayList(),
|
||||||
|
|
||||||
|
config: Config = Config()
|
||||||
|
) : SimpleVisionPropertyContainer<Extruded>(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
|
@VisionBuilder
|
||||||
public fun VisionContainerBuilder<Solid>.extrude(name: String? = null, action: Extruded.() -> Unit = {}): Extruded =
|
public fun VisionContainerBuilder<Solid>.extruded(
|
||||||
Extruded().apply(action).also { set(name, it) }
|
name: String? = null,
|
||||||
|
action: ExtrudeBuilder.() -> Unit = {}
|
||||||
|
): Extruded = ExtrudeBuilder().apply(action).build().also { set(name, it) }
|
@ -13,7 +13,7 @@ import space.kscience.visionforge.set
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.line")
|
@SerialName("solid.line")
|
||||||
public class PolyLine(public var points: List<Point3D>) : SolidBase(), Solid {
|
public class PolyLine(public val points: List<Point3D>) : SolidBase(), Solid {
|
||||||
|
|
||||||
//var lineType by string()
|
//var lineType by string()
|
||||||
public var thickness: Number by allProperties(inherit = false).number(1.0,
|
public var thickness: Number by allProperties(inherit = false).number(1.0,
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package space.kscience.visionforge.solid
|
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.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.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.names.plus
|
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.DETAIL_KEY
|
||||||
import space.kscience.visionforge.solid.Solid.Companion.IGNORE_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.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]
|
* Interface for 3-dimensional [Vision]
|
||||||
*/
|
*/
|
||||||
public interface Solid : Vision {
|
public interface Solid : Vision {
|
||||||
public var position: Point3D?
|
|
||||||
public var rotation: Point3D?
|
|
||||||
public var scale: Point3D?
|
|
||||||
|
|
||||||
override val descriptor: NodeDescriptor get() = Companion.descriptor
|
override val descriptor: NodeDescriptor get() = Companion.descriptor
|
||||||
|
|
||||||
@ -37,7 +49,7 @@ public interface Solid : Vision {
|
|||||||
public val Y_KEY: Name = "y".asName()
|
public val Y_KEY: Name = "y".asName()
|
||||||
public val Z_KEY: Name = "z".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 X_POSITION_KEY: Name = POSITION_KEY + X_KEY
|
||||||
public val Y_POSITION_KEY: Name = POSITION_KEY + Y_KEY
|
public val Y_POSITION_KEY: Name = POSITION_KEY + Y_KEY
|
||||||
@ -72,6 +84,23 @@ public interface Solid : Vision {
|
|||||||
hide()
|
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)
|
item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial.descriptor)
|
||||||
|
|
||||||
enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) {
|
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
|
// get() = getProperty(SELECTED_KEY).boolean
|
||||||
// set(value) = setProperty(SELECTED_KEY, value)
|
// set(value) = setProperty(SELECTED_KEY, value)
|
||||||
|
|
||||||
private fun Solid.position(): Point3D =
|
internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number> =
|
||||||
position ?: Point3D(0.0, 0.0, 0.0).also { position = it }
|
object : ReadWriteProperty<Solid, Number> {
|
||||||
|
override fun getValue(thisRef: Solid, property: KProperty<*>): Number {
|
||||||
public var Solid.x: Number
|
return thisRef.getOwnProperty(name)?.number ?: default
|
||||||
get() = position?.x ?: 0f
|
|
||||||
set(value) {
|
|
||||||
position().x = value.toDouble()
|
|
||||||
invalidateProperty(Solid.X_POSITION_KEY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.y: Number
|
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Number) {
|
||||||
get() = position?.y ?: 0f
|
thisRef.setProperty(name, value)
|
||||||
set(value) {
|
}
|
||||||
position().y = value.toDouble()
|
|
||||||
invalidateProperty(Solid.Y_POSITION_KEY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.z: Number
|
internal fun point(name: Name, default: Float): ReadWriteProperty<Solid, Point3D?> =
|
||||||
get() = position?.z ?: 0f
|
object : ReadWriteProperty<Solid, Point3D?> {
|
||||||
set(value) {
|
override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? {
|
||||||
position().z = value.toDouble()
|
val item = thisRef.getOwnProperty(name) ?: return null
|
||||||
invalidateProperty(Solid.Z_POSITION_KEY)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.rotation(): Point3D =
|
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) {
|
||||||
rotation ?: Point3D(0.0, 0.0, 0.0).also { rotation = it }
|
if (value == null) {
|
||||||
|
thisRef.setProperty(name, null)
|
||||||
public var Solid.rotationX: Number
|
} else {
|
||||||
get() = rotation?.x ?: 0f
|
thisRef.setProperty(name + X_KEY, value.x)
|
||||||
set(value) {
|
thisRef.setProperty(name + Y_KEY, value.y)
|
||||||
rotation().x = value.toDouble()
|
thisRef.setProperty(name + Z_KEY, value.z)
|
||||||
invalidateProperty(Solid.X_ROTATION_KEY)
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.rotationY: Number
|
public var Solid.position: Point3D? by point(POSITION_KEY, 0f)
|
||||||
get() = rotation?.y ?: 0f
|
public var Solid.rotation: Point3D? by point(ROTATION_KEY, 0f)
|
||||||
set(value) {
|
public var Solid.scale: Point3D? by point(SCALE_KEY, 1f)
|
||||||
rotation().y = value.toDouble()
|
|
||||||
invalidateProperty(Solid.Y_ROTATION_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
public var Solid.rotationZ: Number
|
public var Solid.x: Number by float(X_POSITION_KEY, 0f)
|
||||||
get() = rotation?.z ?: 0f
|
public var Solid.y: Number by float(Y_POSITION_KEY, 0f)
|
||||||
set(value) {
|
public var Solid.z: Number by float(Z_POSITION_KEY, 0f)
|
||||||
rotation().z = value.toDouble()
|
|
||||||
invalidateProperty(Solid.Z_ROTATION_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Solid.scale(): Point3D =
|
public var Solid.rotationX: Number by float(X_ROTATION_KEY, 0f)
|
||||||
scale ?: Point3D(1.0, 1.0, 1.0).also { scale = it }
|
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.scaleX: Number
|
public var Solid.scaleX: Number by float(X_SCALE_KEY, 1f)
|
||||||
get() = scale?.x ?: 1f
|
public var Solid.scaleY: Number by float(Y_SCALE_KEY, 1f)
|
||||||
set(value) {
|
public var Solid.scaleZ: Number by float(Z_SCALE_KEY, 1f)
|
||||||
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)
|
|
||||||
}
|
|
@ -2,21 +2,13 @@ package space.kscience.visionforge.solid
|
|||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.Meta
|
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
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.VisionBase
|
||||||
import space.kscience.visionforge.VisionChange
|
import space.kscience.visionforge.VisionChange
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid")
|
@SerialName("solid")
|
||||||
public open class SolidBase(
|
public open class SolidBase : VisionBase(), Solid {
|
||||||
override var position: Point3D? = null,
|
|
||||||
override var rotation: Point3D? = null,
|
|
||||||
override var scale: Point3D? = null,
|
|
||||||
) : VisionBase(), Solid {
|
|
||||||
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
override fun update(change: VisionChange) {
|
||||||
@ -24,15 +16,3 @@ public open class SolidBase(
|
|||||||
super.update(change)
|
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 }
|
|
||||||
}
|
|
@ -1,23 +1,25 @@
|
|||||||
package space.kscience.visionforge.solid
|
package space.kscience.visionforge.solid
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
|
||||||
import kotlinx.serialization.PolymorphicSerializer
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
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.meta.descriptors.NodeDescriptor
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.visionforge.*
|
import space.kscience.visionforge.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container with prototype support
|
||||||
|
*/
|
||||||
public interface PrototypeHolder {
|
public interface PrototypeHolder {
|
||||||
|
/**
|
||||||
|
* Build or update prototype tree
|
||||||
|
*/
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit)
|
public fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a prototype from this container. Should never return a ref.
|
||||||
|
*/
|
||||||
public fun getPrototype(name: Name): Solid?
|
public fun getPrototype(name: Name): Solid?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,40 +29,35 @@ public interface PrototypeHolder {
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("group.solid")
|
@SerialName("group.solid")
|
||||||
public class SolidGroup(
|
public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
|
||||||
@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 {
|
|
||||||
|
|
||||||
init {
|
override val children: Map<NameToken, Vision> get() = super.childrenInternal.filter { it.key != PROTOTYPES_TOKEN }
|
||||||
prototypes?.parent = this
|
|
||||||
|
private var prototypes: MutableVisionGroup?
|
||||||
|
get() = childrenInternal[PROTOTYPES_TOKEN] as? MutableVisionGroup
|
||||||
|
set(value) {
|
||||||
|
set(PROTOTYPES_TOKEN, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
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? =
|
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
|
* Create or edit prototype node as a group
|
||||||
*/
|
*/
|
||||||
override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
|
override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
|
||||||
(prototypes ?: Prototypes().also {
|
(prototypes ?: SolidGroup().also {
|
||||||
prototypes = it
|
prototypes = it
|
||||||
it.parent = this
|
|
||||||
}).run(builder)
|
}).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 createGroup(): SolidGroup = SolidGroup()
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
override fun update(change: VisionChange) {
|
||||||
@ -90,50 +87,3 @@ public fun VisionContainerBuilder<Vision>.group(
|
|||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup =
|
public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup =
|
||||||
SolidGroup().apply(action).also { set(name, it) }
|
SolidGroup().apply(action).also { set(name, it) }
|
||||||
|
|
||||||
/**
|
|
||||||
* A special class which works as a holder for prototypes
|
|
||||||
*/
|
|
||||||
internal class Prototypes(
|
|
||||||
children: MutableMap<NameToken, Vision> = 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<Solid>.() -> Unit) {
|
|
||||||
apply(builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPrototype(name: Name): Solid? = get(name) as? Solid
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class PrototypeSerializer : KSerializer<MutableVisionGroup> {
|
|
||||||
|
|
||||||
private val mapSerializer: KSerializer<Map<NameToken, Vision>> =
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,9 +9,9 @@ import space.kscience.visionforge.set
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.label")
|
@SerialName("solid.label")
|
||||||
public class SolidLabel(
|
public class SolidLabel(
|
||||||
public var text: String,
|
public val text: String,
|
||||||
public var fontSize: Double,
|
public val fontSize: Double,
|
||||||
public var fontFamily: String,
|
public val fontFamily: String,
|
||||||
) : SolidBase(), Solid
|
) : SolidBase(), Solid
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
|
@ -11,16 +11,36 @@ import space.kscience.dataforge.misc.DFExperimental
|
|||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.*
|
||||||
import space.kscience.visionforge.*
|
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
|
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(
|
private fun SolidReference.getRefProperty(
|
||||||
name: Name,
|
name: Name,
|
||||||
inherit: Boolean,
|
inherit: Boolean,
|
||||||
includeStyles: Boolean,
|
includeStyles: Boolean,
|
||||||
includeDefaults: Boolean,
|
includeDefaults: Boolean,
|
||||||
): MetaItem? = buildList {
|
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) {
|
||||||
|
getOwnProperty(name)
|
||||||
|
} else {
|
||||||
|
buildList {
|
||||||
add(getOwnProperty(name))
|
add(getOwnProperty(name))
|
||||||
if (includeStyles) {
|
if (includeStyles) {
|
||||||
addAll(getStyleItems(name))
|
addAll(getStyleItems(name))
|
||||||
@ -30,6 +50,13 @@ private fun SolidReference.getRefProperty(
|
|||||||
add(parent?.getProperty(name, inherit))
|
add(parent?.getProperty(name, inherit))
|
||||||
}
|
}
|
||||||
}.merge()
|
}.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
|
* A reference [Solid] to reuse a template object
|
||||||
@ -38,48 +65,24 @@ private fun SolidReference.getRefProperty(
|
|||||||
@SerialName("solid.ref")
|
@SerialName("solid.ref")
|
||||||
public class SolidReferenceGroup(
|
public class SolidReferenceGroup(
|
||||||
public val refName: Name,
|
public val refName: Name,
|
||||||
) : SolidBase(), SolidReference, VisionGroup {
|
) : VisionBase(), SolidReference, VisionGroup, Solid {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively search for defined template in the parent
|
* Recursively search for defined template in the parent
|
||||||
*/
|
*/
|
||||||
override val prototype: Solid
|
override val prototype: Solid by lazy {
|
||||||
get() {
|
|
||||||
if (parent == null) error("No parent is present for SolidReferenceGroup")
|
if (parent == null) error("No parent is present for SolidReferenceGroup")
|
||||||
if (parent !is SolidGroup) error("Reference parent is not a group")
|
if (parent !is PrototypeHolder) error("Parent does not hold prototypes")
|
||||||
return (parent as? SolidGroup)?.getPrototype(refName)
|
(parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found")
|
||||||
?: error("Prototype with name $refName not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val children: Map<NameToken, Vision>
|
override val children: Map<NameToken, Vision>
|
||||||
get() = (prototype as? VisionGroup)?.children
|
get() = (prototype as? VisionGroup)?.children
|
||||||
?.filter { !it.key.toString().startsWith("@") }
|
?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
|
||||||
?.mapValues {
|
?.mapValues {
|
||||||
ReferenceChild(it.key.asName())
|
ReferenceChild(this, it.key.asName())
|
||||||
} ?: emptyMap()
|
} ?: 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(
|
override fun getProperty(
|
||||||
name: Name,
|
name: Name,
|
||||||
inherit: Boolean,
|
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
|
* 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).
|
* (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 val prototype: Solid by lazy {
|
||||||
override var position: Point3D?
|
if (refName.isEmpty()) owner.prototype else {
|
||||||
get() = prototype.position
|
val proto = (owner.prototype as? VisionGroup)?.get(refName)
|
||||||
set(_) {
|
?: error("Prototype with name $refName not found in SolidReferenceGroup ${owner.refName}")
|
||||||
error("Can't set position of reference")
|
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<NameToken, Vision>
|
override val children: Map<NameToken, Vision>
|
||||||
get() = (prototype as? VisionGroup)?.children
|
get() = (prototype as? VisionGroup)?.children
|
||||||
?.filter { !it.key.toString().startsWith("@") }
|
?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
|
||||||
?.mapValues { (key, _) ->
|
?.mapValues { (key, _) ->
|
||||||
ReferenceChild(childName + key.asName())
|
ReferenceChild(owner, refName + key.asName())
|
||||||
} ?: emptyMap()
|
} ?: 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) {
|
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
||||||
setChildProperty(childName, name, item, notify)
|
owner.setProperty(childPropertyName(refName, name), item, notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProperty(
|
override fun getProperty(
|
||||||
@ -132,16 +130,12 @@ public class SolidReferenceGroup(
|
|||||||
inherit: Boolean,
|
inherit: Boolean,
|
||||||
includeStyles: Boolean,
|
includeStyles: Boolean,
|
||||||
includeDefaults: Boolean,
|
includeDefaults: Boolean,
|
||||||
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) {
|
): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults)
|
||||||
getOwnProperty(name)
|
|
||||||
} else {
|
|
||||||
getRefProperty(name, inherit, includeStyles, includeDefaults)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var parent: VisionGroup?
|
override var parent: VisionGroup?
|
||||||
get() {
|
get() {
|
||||||
val parentName = childName.cutLast()
|
val parentName = refName.cutLast()
|
||||||
return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName)
|
return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName)
|
||||||
}
|
}
|
||||||
set(_) {
|
set(_) {
|
||||||
error("Setting a parent for a reference child is not possible")
|
error("Setting a parent for a reference child is not possible")
|
||||||
@ -149,8 +143,8 @@ public class SolidReferenceGroup(
|
|||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
override val propertyChanges: Flow<Name>
|
override val propertyChanges: Flow<Name>
|
||||||
get() = this@SolidReferenceGroup.propertyChanges.mapNotNull { name ->
|
get() = owner.propertyChanges.mapNotNull { name ->
|
||||||
if (name.startsWith(childToken(childName))) {
|
if (name.startsWith(childToken(refName))) {
|
||||||
name.cutFirst()
|
name.cutFirst()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
@ -158,7 +152,7 @@ public class SolidReferenceGroup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidateProperty(propertyName: Name) {
|
override fun invalidateProperty(propertyName: Name) {
|
||||||
this@SolidReferenceGroup.invalidateProperty(childPropertyName(childName, propertyName))
|
owner.invalidateProperty(childPropertyName(refName, propertyName))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
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
|
* Create ref for existing prototype
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.visionforge.VisionBuilder
|
import space.kscience.visionforge.VisionBuilder
|
||||||
import space.kscience.visionforge.VisionContainerBuilder
|
import space.kscience.visionforge.VisionContainerBuilder
|
||||||
|
import space.kscience.visionforge.VisionPropertyContainer
|
||||||
import space.kscience.visionforge.set
|
import space.kscience.visionforge.set
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
@ -12,12 +13,12 @@ import kotlin.math.sin
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.sphere")
|
@SerialName("solid.sphere")
|
||||||
public class Sphere(
|
public class Sphere(
|
||||||
public var radius: Float,
|
public val radius: Float,
|
||||||
public var phiStart: Float = 0f,
|
public val phiStart: Float = 0f,
|
||||||
public var phi: Float = PI2,
|
public val phi: Float = PI2,
|
||||||
public var thetaStart: Float = 0f,
|
public val thetaStart: Float = 0f,
|
||||||
public var theta: Float = PI.toFloat(),
|
public val theta: Float = PI .toFloat(),
|
||||||
) : SolidBase(), GeometrySolid {
|
) : SolidBase(), GeometrySolid, VisionPropertyContainer<Sphere> {
|
||||||
|
|
||||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||||
fun point3DfromSphCoord(r: Float, theta: Float, phi: Float): Point3D {
|
fun point3DfromSphCoord(r: Float, theta: Float, phi: Float): Point3D {
|
||||||
|
@ -15,12 +15,12 @@ import kotlin.math.sin
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.sphereLayer")
|
@SerialName("solid.sphereLayer")
|
||||||
public class SphereLayer(
|
public class SphereLayer(
|
||||||
public var outerRadius: Float,
|
public val outerRadius: Float,
|
||||||
public var innerRadius: Float,
|
public val innerRadius: Float,
|
||||||
public var phiStart: Float = 0f,
|
public val phiStart: Float = 0f,
|
||||||
public var phi: Float = PI2,
|
public val phi: Float = PI2,
|
||||||
public var thetaStart: Float = 0f,
|
public val thetaStart: Float = 0f,
|
||||||
public var theta: Float = PI.toFloat(),
|
public val theta: Float = PI.toFloat(),
|
||||||
) : SolidBase(), GeometrySolid {
|
) : SolidBase(), GeometrySolid {
|
||||||
|
|
||||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>): Unit = geometryBuilder.run {
|
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>): Unit = geometryBuilder.run {
|
||||||
@ -72,9 +72,17 @@ public class SphereLayer(
|
|||||||
public inline fun VisionContainerBuilder<Solid>.sphereLayer(
|
public inline fun VisionContainerBuilder<Solid>.sphereLayer(
|
||||||
outerRadius: Number,
|
outerRadius: Number,
|
||||||
innerRadius: Number,
|
innerRadius: Number,
|
||||||
|
phiStart: Number = 0f,
|
||||||
|
phi: Number = PI2,
|
||||||
|
thetaStart: Number = 0f,
|
||||||
|
theta: Number = PI.toFloat(),
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
action: SphereLayer.() -> Unit = {},
|
action: SphereLayer.() -> Unit = {},
|
||||||
): SphereLayer = SphereLayer(
|
): SphereLayer = SphereLayer(
|
||||||
outerRadius.toFloat(),
|
outerRadius.toFloat(),
|
||||||
innerRadius.toFloat(),
|
innerRadius.toFloat(),
|
||||||
|
phiStart.toFloat(),
|
||||||
|
phi.toFloat(),
|
||||||
|
thetaStart.toFloat(),
|
||||||
|
theta.toFloat()
|
||||||
).apply(action).also { set(name, it) }
|
).apply(action).also { set(name, it) }
|
@ -1,39 +1,68 @@
|
|||||||
package space.kscience.visionforge.solid
|
package space.kscience.visionforge.solid
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.Meta
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
import space.kscience.dataforge.meta.MetaBuilder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import space.kscience.dataforge.meta.double
|
import kotlinx.serialization.encoding.Encoder
|
||||||
import space.kscience.dataforge.meta.get
|
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.PI
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
public const val PI2: Float = 2 * PI.toFloat()
|
public const val PI2: Float = 2 * PI.toFloat()
|
||||||
|
|
||||||
@Serializable
|
@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 {
|
public fun Point2D.toMeta(): Meta = Meta {
|
||||||
Solid.X_KEY put x
|
X_KEY put x
|
||||||
Solid.Y_KEY put y
|
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 companion object {
|
||||||
public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0)
|
public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0)
|
||||||
public val ONE: Point3D = Point3D(1.0, 1.0, 1.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<Point3D> {
|
||||||
|
|
||||||
|
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(
|
public operator fun Point3D.plus(other: Point3D): Point3D = Point3D(
|
||||||
this.x + other.x,
|
this.x + other.x,
|
||||||
@ -41,10 +70,52 @@ public operator fun Point3D.plus(other: Point3D): Point3D = Point3D(
|
|||||||
this.z + other.z
|
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 {
|
public fun Point3D.toMeta(): MetaBuilder = Meta {
|
||||||
Solid.X_KEY put x
|
X_KEY put x
|
||||||
Solid.Y_KEY put y
|
Y_KEY put y
|
||||||
Solid.Z_KEY put z
|
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 }
|
||||||
}
|
}
|
@ -18,7 +18,6 @@ public class Camera : Scheme() {
|
|||||||
public var distance: Double by double(INITIAL_DISTANCE)
|
public var distance: Double by double(INITIAL_DISTANCE)
|
||||||
public var azimuth: Double by double(INITIAL_AZIMUTH)
|
public var azimuth: Double by double(INITIAL_AZIMUTH)
|
||||||
public var latitude: Double by double(INITIAL_LATITUDE)
|
public var latitude: Double by double(INITIAL_LATITUDE)
|
||||||
public val zenith: Double get() = PI / 2 - latitude
|
|
||||||
|
|
||||||
public companion object : SchemeSpec<Camera>(::Camera) {
|
public companion object : SchemeSpec<Camera>(::Camera) {
|
||||||
public const val INITIAL_DISTANCE: Double = 300.0
|
public const val INITIAL_DISTANCE: Double = 300.0
|
||||||
@ -52,3 +51,5 @@ public class Camera : Scheme() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public val Camera.zenith: Double get() = PI / 2 - latitude
|
@ -1,35 +1,38 @@
|
|||||||
package space.kscience.visionforge.solid.transform
|
package space.kscience.visionforge.solid.transform
|
||||||
|
|
||||||
|
import space.kscience.dataforge.meta.itemSequence
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.asName
|
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.*
|
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
|
@DFExperimental
|
||||||
internal fun mergeChild(parent: VisionGroup, child: Vision): Vision {
|
internal fun Vision.updateFrom(other: Vision): Vision {
|
||||||
return child.apply {
|
if (this is Solid && other is Solid) {
|
||||||
|
x += other.x
|
||||||
configure(parent.meta)
|
y += other.y
|
||||||
|
z += other.y
|
||||||
//parent.properties?.let { config.update(it) }
|
rotationX += other.rotationX
|
||||||
|
rotationY += other.rotationY
|
||||||
if (this is Solid && parent is Solid) {
|
rotationZ += other.rotationZ
|
||||||
position = (position ?: Point3D.ZERO) + (parent.position ?: Point3D.ZERO)
|
scaleX *= other.scaleX
|
||||||
rotation = (parent.rotation ?: Point3D.ZERO) + (parent.rotation ?: Point3D.ZERO)
|
scaleY *= other.scaleY
|
||||||
scale = when {
|
scaleZ *= other.scaleZ
|
||||||
scale == null && parent.scale == null -> null
|
other.meta.itemSequence().forEach { (name, item) ->
|
||||||
scale == null -> parent.scale
|
if (getProperty(name) == null) {
|
||||||
parent.scale == null -> scale
|
setProperty(name, item)
|
||||||
else -> Point3D(
|
|
||||||
scale!!.x * parent.scale!!.x,
|
|
||||||
scale!!.y * parent.scale!!.y,
|
|
||||||
scale!!.z * parent.scale!!.z
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
|
internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
|
||||||
@ -43,7 +46,7 @@ internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
|
|||||||
}
|
}
|
||||||
if (parent is VisionGroup && parent.children.size == 1) {
|
if (parent is VisionGroup && parent.children.size == 1) {
|
||||||
val child = parent.children.values.first()
|
val child = parent.children.values.first()
|
||||||
val newParent = mergeChild(parent, child)
|
val newParent = child.updateFrom(parent)
|
||||||
newParent.parent = null
|
newParent.parent = null
|
||||||
set(childName.asName(), newParent)
|
set(childName.asName(), newParent)
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ internal object UnRef : VisualTreeTransform<SolidGroup>() {
|
|||||||
}
|
}
|
||||||
children.filter { (it.value as? SolidReferenceGroup)?.refName == name }.forEach { (key, value) ->
|
children.filter { (it.value as? SolidReferenceGroup)?.refName == name }.forEach { (key, value) ->
|
||||||
val reference = value as SolidReferenceGroup
|
val reference = value as SolidReferenceGroup
|
||||||
val newChild = mergeChild(reference, reference.prototype)
|
val newChild = reference.prototype.updateFrom(reference)
|
||||||
newChild.parent = null
|
newChild.parent = null
|
||||||
set(key.asName(), newChild) // replace proxy with merged object
|
set(key.asName(), newChild) // replace proxy with merged object
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,6 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":visionforge-solid"))
|
api(project(":visionforge-solid"))
|
||||||
implementation(npm("three", "0.124.0"))
|
implementation(npm("three", "0.130.1"))
|
||||||
implementation(npm("three-csg-ts", "2.2.2"))
|
implementation(npm("three-csg-ts", "3.1.6"))
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
@file:JsModule("three")
|
@file:JsModule("three")
|
||||||
@file:JsNonModule
|
@file:JsNonModule
|
||||||
|
@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", "unused")
|
||||||
|
|
||||||
package info.laht.threekt
|
package info.laht.threekt
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", "unused")
|
||||||
@file:JsModule("three")
|
@file:JsModule("three")
|
||||||
@file:JsNonModule
|
@file:JsNonModule
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ abstract external class BufferAttribute protected constructor(
|
|||||||
* Default is 0. Position at whcih to start update.
|
* Default is 0. Position at whcih to start update.
|
||||||
*/
|
*/
|
||||||
var offset: Int
|
var offset: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default is -1, which means don't use update ranges.
|
* 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.
|
* UUID of this object instance. This gets automatically assigned and this shouldn't be edited.
|
||||||
*/
|
*/
|
||||||
val uuid: String
|
val uuid: String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional name for this attribute instance. Default is an empty string.
|
* Optional name for this attribute instance. Default is an empty string.
|
||||||
*/
|
*/
|
||||||
var name: String
|
var name: String
|
||||||
|
|
||||||
var array: dynamic
|
var array: dynamic
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of vectors that are being stored in the array.
|
* The length of vectors that are being stored in the array.
|
||||||
*/
|
*/
|
||||||
val itemSize: Int
|
val itemSize: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the array's length divided by the itemSize.
|
* 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.
|
* then this will count the number of such vectors stored.
|
||||||
*/
|
*/
|
||||||
val count: Int
|
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.
|
* 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.
|
* This corresponds to the gl.DYNAMIC_DRAW flag.
|
||||||
*/
|
*/
|
||||||
var dynamic: Boolean
|
var dynamic: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This can be used to only update some components of stored vectors (
|
* This can be used to only update some components of stored vectors (
|
||||||
* for example, just the component related to color).
|
* for example, just the component related to color).
|
||||||
@ -99,8 +105,16 @@ abstract external class BufferAttribute protected constructor(
|
|||||||
*/
|
*/
|
||||||
var needsUpdate: Boolean
|
var needsUpdate: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback function that is executed after the Renderer has transferred the attribute array data to the GPU.
|
||||||
|
*/
|
||||||
var onUploadCallback: () -> Unit
|
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.
|
* 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 getW(index: Int): Number
|
||||||
|
|
||||||
fun copy(source: BufferAttribute): BufferAttribute
|
fun copy(source: BufferAttribute): BufferAttribute
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy a vector from bufferAttribute[index2] to array[index1].
|
* 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)
|
fun setXYZW(index: Int, x: Number, y: Number, z: Number, w: Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
external class Float32BufferAttribute(
|
||||||
|
array: Array<Float>,
|
||||||
|
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
|
@ -85,14 +85,14 @@ open external class BufferGeometry {
|
|||||||
fun clearGroups()
|
fun clearGroups()
|
||||||
fun addGroup(start: Int, count: Int, materialIndex: Int = definedExternally)
|
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 getAttribute(name: String): BufferAttribute
|
||||||
fun removeAttribute(name: String): BufferGeometry
|
fun deleteAttribute(name: String): BufferGeometry
|
||||||
|
|
||||||
fun setIndex(index: BufferAttribute)
|
fun setIndex(index: BufferAttribute)
|
||||||
|
fun setIndex(index: Array<Short>)
|
||||||
fun setDrawRange(start: Int, count: Int)
|
fun setDrawRange(start: Int, count: Int)
|
||||||
|
|
||||||
fun fromGeometry(geometry: Geometry)
|
|
||||||
fun setFromObject(`object`: Object3D): BufferGeometry
|
fun setFromObject(`object`: Object3D): BufferGeometry
|
||||||
fun updateFromObject(`object`: Object3D): BufferGeometry
|
fun updateFromObject(`object`: Object3D): BufferGeometry
|
||||||
fun setFromPoints(points: Array<Vector3>): BufferGeometry
|
fun setFromPoints(points: Array<Vector3>): BufferGeometry
|
||||||
|
@ -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)
|
|
||||||
|
|
||||||
}
|
|
@ -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<Vector3>
|
|
||||||
}
|
|
||||||
|
|
||||||
external interface MorphNormal {
|
|
||||||
val name: String
|
|
||||||
val normals: Array<Vector3>
|
|
||||||
}
|
|
||||||
|
|
||||||
open external class Geometry {
|
|
||||||
|
|
||||||
val id: Int
|
|
||||||
|
|
||||||
var vertices: Array<Vector3>
|
|
||||||
var colors: Array<Color>
|
|
||||||
var faces: Array<Face3>
|
|
||||||
var faceVertexUvs: Array<Array<Vector2>>
|
|
||||||
|
|
||||||
var morphTargets: Array<MorphTarget>
|
|
||||||
var morphNormals: Array<MorphNormal>
|
|
||||||
|
|
||||||
var skinWeights: Array<Vector4>
|
|
||||||
var skinIndices: Array<Vector4>
|
|
||||||
|
|
||||||
var lineDistances: List<Double>
|
|
||||||
|
|
||||||
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<Vector3>): Geometry
|
|
||||||
|
|
||||||
fun sortFacesByMaterialIndex()
|
|
||||||
|
|
||||||
fun toJSON(): Any
|
|
||||||
|
|
||||||
open fun clone(): Geometry
|
|
||||||
fun copy(geometry: Geometry): Geometry
|
|
||||||
|
|
||||||
fun dispose()
|
|
||||||
|
|
||||||
}
|
|
@ -4,7 +4,6 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
|
|
||||||
|
|
||||||
external class BoxGeometry(
|
external class BoxGeometry(
|
||||||
@ -14,14 +13,4 @@ external class BoxGeometry(
|
|||||||
widthSegments: Int = definedExternally,
|
widthSegments: Int = definedExternally,
|
||||||
heightSegments: Int = definedExternally,
|
heightSegments: Int = definedExternally,
|
||||||
depthSegments: 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
|
) : BufferGeometry
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
|
|
||||||
external class ConeGeometry(
|
external class ConeGeometry(
|
||||||
radius: Number = definedExternally,
|
radius: Number = definedExternally,
|
||||||
@ -14,14 +14,4 @@ external class ConeGeometry(
|
|||||||
openEnded: Boolean = definedExternally,
|
openEnded: Boolean = definedExternally,
|
||||||
thetaStart: Boolean = definedExternally,
|
thetaStart: Boolean = definedExternally,
|
||||||
thetaLength: 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
|
) : BufferGeometry
|
@ -4,7 +4,6 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
|
|
||||||
external class CylinderGeometry(
|
external class CylinderGeometry(
|
||||||
radiusTop: Number,
|
radiusTop: Number,
|
||||||
@ -15,15 +14,4 @@ external class CylinderGeometry(
|
|||||||
openEnded: Boolean = definedExternally,
|
openEnded: Boolean = definedExternally,
|
||||||
thetaStart: Number = definedExternally,
|
thetaStart: Number = definedExternally,
|
||||||
thetaLength: 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
|
) : BufferGeometry
|
@ -4,8 +4,5 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
|
|
||||||
external class EdgesGeometry(geometry: Geometry, thresholdAngle: Int = definedExternally) : BufferGeometry {
|
public external class EdgesGeometry(geometry: BufferGeometry, thresholdAngle: Int = definedExternally) : BufferGeometry
|
||||||
constructor(geometry: BufferGeometry, thresholdAngle: Int = definedExternally)
|
|
||||||
}
|
|
@ -11,7 +11,6 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
import info.laht.threekt.extras.core.Shape
|
import info.laht.threekt.extras.core.Shape
|
||||||
import info.laht.threekt.math.Vector2
|
import info.laht.threekt.math.Vector2
|
||||||
|
|
||||||
@ -78,15 +77,3 @@ external open class ExtrudeBufferGeometry : BufferGeometry {
|
|||||||
var WorldUVGenerator: UVGenerator
|
var WorldUVGenerator: UVGenerator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
external open class ExtrudeGeometry : Geometry {
|
|
||||||
constructor(shapes: Shape, options: ExtrudeGeometryOptions?)
|
|
||||||
constructor(shapes: Array<Shape>, options: ExtrudeGeometryOptions?)
|
|
||||||
|
|
||||||
open fun addShapeList(shapes: Array<Shape>, options: Any? = definedExternally)
|
|
||||||
open fun addShape(shape: Shape, options: Any? = definedExternally)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
var WorldUVGenerator: UVGenerator
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,6 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
|
|
||||||
external class PlaneGeometry(
|
external class PlaneGeometry(
|
||||||
|
|
||||||
@ -13,13 +12,4 @@ external class PlaneGeometry(
|
|||||||
widthSegments: Int = definedExternally,
|
widthSegments: Int = definedExternally,
|
||||||
heightSegments: Int = definedExternally
|
heightSegments: Int = definedExternally
|
||||||
|
|
||||||
) : Geometry
|
|
||||||
|
|
||||||
external class PlaneBufferGeometry(
|
|
||||||
|
|
||||||
width: Number,
|
|
||||||
height: Number,
|
|
||||||
widthSegments: Int = definedExternally,
|
|
||||||
heightSegments: Int = definedExternally
|
|
||||||
|
|
||||||
) : BufferGeometry
|
) : BufferGeometry
|
@ -4,7 +4,6 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
|
|
||||||
external class SphereGeometry(
|
external class SphereGeometry(
|
||||||
radius: Number,
|
radius: Number,
|
||||||
@ -14,14 +13,4 @@ external class SphereGeometry(
|
|||||||
phiLength: Number = definedExternally,
|
phiLength: Number = definedExternally,
|
||||||
thetaStart: Number = definedExternally,
|
thetaStart: Number = definedExternally,
|
||||||
thetaLength: 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
|
) : BufferGeometry
|
@ -34,10 +34,7 @@ external interface TextGeometryParameters {
|
|||||||
set(value) = definedExternally
|
set(value) = definedExternally
|
||||||
}
|
}
|
||||||
|
|
||||||
external class TextBufferGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) : ExtrudeBufferGeometry {
|
external class TextBufferGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) :
|
||||||
val parameters: TextGeometryParameters
|
ExtrudeBufferGeometry {
|
||||||
}
|
|
||||||
|
|
||||||
external class TextGeometry(text: String, parameters: TextGeometryParameters? = definedExternally) : ExtrudeGeometry {
|
|
||||||
val parameters: TextGeometryParameters
|
val parameters: TextGeometryParameters
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
|
|
||||||
external class TorusGeometry(
|
external class TorusGeometry(
|
||||||
radius: Number = definedExternally,
|
radius: Number = definedExternally,
|
||||||
@ -12,12 +12,4 @@ external class TorusGeometry(
|
|||||||
radialSegments: Int = definedExternally,
|
radialSegments: Int = definedExternally,
|
||||||
tubularSegments: Int = definedExternally,
|
tubularSegments: Int = definedExternally,
|
||||||
arc: Number = definedExternally
|
arc: Number = definedExternally
|
||||||
) : Geometry
|
|
||||||
|
|
||||||
external class TorusBufferGeometry(
|
|
||||||
radius: Number = definedExternally,
|
|
||||||
tube: Number = definedExternally,
|
|
||||||
radialSegments: Int = definedExternally,
|
|
||||||
tubularSegments: Int = definedExternally,
|
|
||||||
arc: Number = definedExternally
|
|
||||||
) : BufferGeometry
|
) : BufferGeometry
|
@ -1,10 +1,10 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
import info.laht.threekt.extras.core.Curve
|
import info.laht.threekt.extras.core.Curve
|
||||||
import info.laht.threekt.math.Vector3
|
import info.laht.threekt.math.Vector3
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a tube that extrudes along a 3d curve.
|
* Creates a tube that extrudes along a 3d curve.
|
||||||
*/
|
*/
|
||||||
@ -16,25 +16,6 @@ external class TubeGeometry(
|
|||||||
radiusSegments: Int = definedExternally,
|
radiusSegments: Int = definedExternally,
|
||||||
closed: Boolean = definedExternally
|
closed: Boolean = definedExternally
|
||||||
|
|
||||||
) : Geometry {
|
|
||||||
|
|
||||||
var tangents: Array<Vector3>
|
|
||||||
var normals: Array<Vector3>
|
|
||||||
var binormals: Array<Vector3>
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a tube that extrudes along a 3d curve.
|
|
||||||
*/
|
|
||||||
external class TubeBufferGeometry(
|
|
||||||
|
|
||||||
path: Curve<Vector3>,
|
|
||||||
tubularSegments: Int = definedExternally,
|
|
||||||
radius: Number = definedExternally,
|
|
||||||
radiusSegments: Int = definedExternally,
|
|
||||||
closed: Boolean = definedExternally
|
|
||||||
|
|
||||||
) : BufferGeometry {
|
) : BufferGeometry {
|
||||||
|
|
||||||
val parameters: dynamic
|
val parameters: dynamic
|
||||||
|
@ -4,14 +4,8 @@
|
|||||||
package info.laht.threekt.geometries
|
package info.laht.threekt.geometries
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
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.
|
* This can be used as a helper object to view a Geometry object as a wireframe.
|
||||||
*/
|
*/
|
||||||
external class WireframeGeometry : BufferGeometry {
|
external class WireframeGeometry(geometry: BufferGeometry) : BufferGeometry
|
||||||
|
|
||||||
constructor(geometry: Geometry)
|
|
||||||
constructor(geometry: BufferGeometry)
|
|
||||||
|
|
||||||
}
|
|
@ -28,12 +28,10 @@
|
|||||||
package info.laht.threekt.objects
|
package info.laht.threekt.objects
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.materials.Material
|
import info.laht.threekt.materials.Material
|
||||||
|
|
||||||
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D {
|
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D {
|
||||||
constructor(geometry: Geometry, material: Material)
|
|
||||||
|
|
||||||
var geometry: BufferGeometry
|
var geometry: BufferGeometry
|
||||||
var material: Material
|
var material: Material
|
||||||
|
@ -27,16 +27,16 @@
|
|||||||
|
|
||||||
package info.laht.threekt.objects
|
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
|
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?)
|
var geometry: BufferGeometry
|
||||||
constructor(geometry: BufferGeometry?, material: Material?)
|
|
||||||
|
|
||||||
var geometry: dynamic
|
|
||||||
var material: Material
|
var material: Material
|
||||||
|
|
||||||
var drawMode: Int
|
var drawMode: Int
|
||||||
|
@ -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<BufferGeometry>, useGroups: Boolean): BufferGeometry
|
||||||
|
}
|
@ -30,16 +30,14 @@ public abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
override fun invoke(three: ThreePlugin, obj: T): Mesh {
|
override fun invoke(three: ThreePlugin, obj: T): Mesh {
|
||||||
val geometry = buildGeometry(obj)
|
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 meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
||||||
|
|
||||||
val mesh = Mesh(geometry, ThreeMaterials.DEFAULT).apply {
|
val mesh = Mesh(geometry, ThreeMaterials.DEFAULT).apply {
|
||||||
matrixAutoUpdate = false
|
matrixAutoUpdate = false
|
||||||
//set position for mesh
|
//set position for mesh
|
||||||
updatePosition(obj)
|
updatePosition(obj)
|
||||||
}.applyProperties(obj)
|
applyProperties(obj)
|
||||||
|
}
|
||||||
|
|
||||||
//add listener to object properties
|
//add listener to object properties
|
||||||
obj.onPropertyChange(three.updateScope) { name ->
|
obj.onPropertyChange(three.updateScope) { name ->
|
||||||
@ -76,9 +74,9 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply {
|
|||||||
updateMaterial(obj)
|
updateMaterial(obj)
|
||||||
applyEdges(obj)
|
applyEdges(obj)
|
||||||
//applyWireFrame(obj)
|
//applyWireFrame(obj)
|
||||||
layers.enable(obj.layer)
|
layers.set(obj.layer)
|
||||||
children.forEach {
|
children.forEach {
|
||||||
it.layers.enable(obj.layer)
|
it.layers.set(obj.layer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
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.Box
|
||||||
import space.kscience.visionforge.solid.detail
|
import space.kscience.visionforge.solid.detail
|
||||||
|
|
||||||
public object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
|
public object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
|
||||||
override fun buildGeometry(obj: Box): BoxBufferGeometry =
|
override fun buildGeometry(obj: Box): BoxGeometry =
|
||||||
obj.detail?.let { detail ->
|
obj.detail?.let { detail ->
|
||||||
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail)
|
BoxGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail)
|
||||||
} ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize)
|
} ?: BoxGeometry(obj.xSize, obj.ySize, obj.zSize)
|
||||||
}
|
}
|
@ -250,8 +250,11 @@ public class ThreeCanvas(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun render(vision: Solid) {
|
public fun render(vision: Solid) {
|
||||||
|
if (root != null) {
|
||||||
three.logger.info { "Replacing root node in three canvas" }
|
three.logger.info { "Replacing root node in three canvas" }
|
||||||
scene.findChild("@root".asName())?.let { scene.remove(it) }
|
scene.findChild("@root".asName())?.let { scene.remove(it) }
|
||||||
|
root?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
val object3D = three.buildObject3D(vision)
|
val object3D = three.buildObject3D(vision)
|
||||||
object3D.name = "@root"
|
object3D.name = "@root"
|
||||||
|
@ -2,7 +2,7 @@ package space.kscience.visionforge.solid.three
|
|||||||
|
|
||||||
import info.laht.threekt.DoubleSide
|
import info.laht.threekt.DoubleSide
|
||||||
import info.laht.threekt.core.Object3D
|
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.materials.MeshBasicMaterial
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
import info.laht.threekt.textures.Texture
|
import info.laht.threekt.textures.Texture
|
||||||
@ -46,7 +46,7 @@ public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mesh = Mesh(
|
val mesh = Mesh(
|
||||||
PlaneBufferGeometry(canvas.width, canvas.height),
|
PlaneGeometry(canvas.width, canvas.height),
|
||||||
material
|
material
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,29 +1,60 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
package space.kscience.visionforge.solid.three
|
||||||
|
|
||||||
import CSG
|
import CSG
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.objects.Mesh
|
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.Composite
|
||||||
import space.kscience.visionforge.solid.CompositeType
|
import space.kscience.visionforge.solid.CompositeType
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This should be inner, because it uses object builder
|
* This should be inner, because it uses object builder
|
||||||
*/
|
*/
|
||||||
public class ThreeCompositeFactory(public val three: ThreePlugin) : MeshThreeFactory<Composite>(Composite::class) {
|
public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory<Composite> {
|
||||||
|
|
||||||
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<in Composite> 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")
|
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")
|
val second = three.buildObject3D(obj.second) as? Mesh ?: error("Second part of composite is not a mesh")
|
||||||
second.updateMatrix()
|
return when (obj.compositeType) {
|
||||||
val firstCSG = CSG.fromMesh(first)
|
CompositeType.SUM, CompositeType.UNION -> CSG.union(first, second)
|
||||||
val secondCSG = CSG.fromMesh(second)
|
CompositeType.INTERSECT -> CSG.intersect(first, second)
|
||||||
val resultCSG = when (obj.compositeType) {
|
CompositeType.SUBTRACT -> CSG.subtract(first, second)
|
||||||
CompositeType.UNION -> firstCSG.union(secondCSG)
|
}.apply {
|
||||||
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
updatePosition(obj)
|
||||||
CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG)
|
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
package space.kscience.visionforge.solid.three
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
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.ConeSegment
|
||||||
import space.kscience.visionforge.solid.detail
|
import space.kscience.visionforge.solid.detail
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
@ -11,7 +11,7 @@ public object ThreeConeFactory : MeshThreeFactory<ConeSegment>(ConeSegment::clas
|
|||||||
override fun buildGeometry(obj: ConeSegment): BufferGeometry {
|
override fun buildGeometry(obj: ConeSegment): BufferGeometry {
|
||||||
val cylinder = obj.detail?.let {
|
val cylinder = obj.detail?.let {
|
||||||
val segments = it.toDouble().pow(0.5).toInt()
|
val segments = it.toDouble().pow(0.5).toInt()
|
||||||
CylinderBufferGeometry(
|
CylinderGeometry(
|
||||||
radiusTop = obj.topRadius,
|
radiusTop = obj.topRadius,
|
||||||
radiusBottom = obj.bottomRadius,
|
radiusBottom = obj.bottomRadius,
|
||||||
height = obj.height,
|
height = obj.height,
|
||||||
@ -21,7 +21,7 @@ public object ThreeConeFactory : MeshThreeFactory<ConeSegment>(ConeSegment::clas
|
|||||||
thetaStart = obj.startAngle,
|
thetaStart = obj.startAngle,
|
||||||
thetaLength = obj.angle
|
thetaLength = obj.angle
|
||||||
)
|
)
|
||||||
} ?: CylinderBufferGeometry(
|
} ?: CylinderGeometry(
|
||||||
radiusTop = obj.topRadius,
|
radiusTop = obj.topRadius,
|
||||||
radiusBottom = obj.bottomRadius,
|
radiusBottom = obj.bottomRadius,
|
||||||
height = obj.height,
|
height = obj.height,
|
||||||
|
@ -63,9 +63,7 @@ public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
|||||||
* Generic factory for elements which provide inside geometry builder
|
* Generic factory for elements which provide inside geometry builder
|
||||||
*/
|
*/
|
||||||
public object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) {
|
public object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) {
|
||||||
override fun buildGeometry(obj: GeometrySolid): BufferGeometry {
|
override fun buildGeometry(obj: GeometrySolid): BufferGeometry = ThreeGeometryBuilder().apply {
|
||||||
return obj.run {
|
obj.toGeometry(this)
|
||||||
ThreeGeometryBuilder().apply { toGeometry(this) }.build()
|
}.build()
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,52 +1,63 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
package space.kscience.visionforge.solid.three
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Face3
|
import info.laht.threekt.core.Float32BufferAttribute
|
||||||
import info.laht.threekt.core.Geometry
|
|
||||||
import info.laht.threekt.math.Vector3
|
import info.laht.threekt.math.Vector3
|
||||||
import space.kscience.dataforge.meta.Meta
|
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.GeometryBuilder
|
||||||
import space.kscience.visionforge.solid.Point3D
|
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 Point3D.toVector() = Vector3(x, y, z)
|
||||||
|
|
||||||
|
internal fun <T> MutableList<T>.add(vararg values: T) {
|
||||||
|
values.forEach {
|
||||||
|
add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of geometry builder for Three.js [BufferGeometry]
|
* An implementation of geometry builder for Three.js [BufferGeometry]
|
||||||
*/
|
*/
|
||||||
public class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
|
public class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
|
||||||
|
|
||||||
private val vertices = ArrayList<Vector3>()
|
private val indices = ArrayList<Short>()
|
||||||
private val faces = ArrayList<Face3>()
|
private val positions = ArrayList<Float>()
|
||||||
|
private val normals = ArrayList<Float>()
|
||||||
|
// private val colors = ArrayList<Float>()
|
||||||
|
|
||||||
private val vertexCache = HashMap<Point3D, Int>()
|
private val vertexCache = HashMap<Point3D, Short>()
|
||||||
|
private var counter: Short = -1
|
||||||
|
|
||||||
private fun append(vertex: Point3D): Int {
|
private fun vertex(vertex: Point3D, normal: Point3D): Short = vertexCache.getOrPut(vertex) {
|
||||||
val index = vertexCache[vertex] ?: -1//vertices.indexOf(vertex)
|
//add vertex and update cache if needed
|
||||||
return if (index > 0) {
|
positions.add(vertex.x, vertex.y, vertex.z)
|
||||||
index
|
normals.add(normal.x, vertex.y, vertex.z)
|
||||||
} else {
|
//colors.add(1f, 1f, 1f)
|
||||||
vertices.add(vertex.toVector())
|
counter++
|
||||||
vertexCache[vertex] = vertices.size - 1
|
counter
|
||||||
vertices.size - 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) {
|
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))
|
val actualNormal: Point3D = normal ?: (vertex3 - vertex2) cross (vertex1 - vertex2)
|
||||||
meta["materialIndex"].int?.let { face.materialIndex = it }
|
indices.add(
|
||||||
meta["color"]?.getColor()?.let { face.color = it }
|
vertex(vertex1, actualNormal),
|
||||||
faces.add(face)
|
vertex(vertex2, actualNormal),
|
||||||
|
vertex(vertex3, actualNormal)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun build(): BufferGeometry {
|
override fun build(): BufferGeometry = BufferGeometry().apply {
|
||||||
return Geometry().apply {
|
setIndex(indices.toTypedArray())
|
||||||
vertices = this@ThreeGeometryBuilder.vertices.toTypedArray()
|
setAttribute("position", Float32BufferAttribute(positions.toTypedArray(), 3))
|
||||||
faces = this@ThreeGeometryBuilder.faces.toTypedArray()
|
setAttribute("normal", Float32BufferAttribute(normals.toTypedArray(), 3))
|
||||||
|
//setAttribute("color", Float32BufferAttribute(colors.toFloatArray(), 3))
|
||||||
|
//a temporary fix for CSG problem
|
||||||
|
val uvsArray = Array<Float>((counter+1)*2){0f}
|
||||||
|
setAttribute("uv", Float32BufferAttribute(uvsArray, 2))
|
||||||
|
|
||||||
computeBoundingSphere()
|
computeBoundingSphere()
|
||||||
computeFaceNormals()
|
|
||||||
}.toBufferGeometry()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ import space.kscience.visionforge.solid.SolidBase
|
|||||||
/**
|
/**
|
||||||
* A custom visual object that has its own Three.js renderer
|
* 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
|
public abstract fun render(three: ThreePlugin): Object3D
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
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.core.Object3D
|
||||||
import info.laht.threekt.math.Color
|
import info.laht.threekt.math.Color
|
||||||
import info.laht.threekt.objects.LineSegments
|
import info.laht.threekt.objects.LineSegments
|
||||||
@ -16,8 +16,8 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
|
|||||||
override val type: KClass<PolyLine> get() = PolyLine::class
|
override val type: KClass<PolyLine> get() = PolyLine::class
|
||||||
|
|
||||||
override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D {
|
override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D {
|
||||||
val geometry = Geometry().apply {
|
val geometry = BufferGeometry().apply {
|
||||||
vertices = Array(obj.points.size) { obj.points[it].toVector() }
|
setFromPoints(Array(obj.points.size) { obj.points[it].toVector() })
|
||||||
}
|
}
|
||||||
|
|
||||||
val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true)
|
val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true)
|
||||||
|
@ -49,7 +49,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun buildObject3D(obj: Solid): Object3D = when (obj) {
|
public fun buildObject3D(obj: Solid): Object3D = when (obj) {
|
||||||
is ThreeVision -> obj.render(this)
|
is ThreeJsVision -> obj.render(this)
|
||||||
is SolidReferenceGroup -> ThreeReferenceFactory(this, obj)
|
is SolidReferenceGroup -> ThreeReferenceFactory(this, obj)
|
||||||
is SolidGroup -> {
|
is SolidGroup -> {
|
||||||
val group = ThreeGroup()
|
val group = ThreeGroup()
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
package space.kscience.visionforge.solid.three
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
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.Sphere
|
||||||
import space.kscience.visionforge.solid.detail
|
import space.kscience.visionforge.solid.detail
|
||||||
|
|
||||||
public object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
|
public object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
|
||||||
override fun buildGeometry(obj: Sphere): BufferGeometry {
|
override fun buildGeometry(obj: Sphere): BufferGeometry {
|
||||||
return obj.detail?.let {detail ->
|
return obj.detail?.let {detail ->
|
||||||
SphereBufferGeometry(
|
SphereGeometry(
|
||||||
radius = obj.radius,
|
radius = obj.radius,
|
||||||
phiStart = obj.phiStart,
|
phiStart = obj.phiStart,
|
||||||
phiLength = obj.phi,
|
phiLength = obj.phi,
|
||||||
@ -17,7 +17,7 @@ public object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
|
|||||||
widthSegments = detail,
|
widthSegments = detail,
|
||||||
heightSegments = detail
|
heightSegments = detail
|
||||||
)
|
)
|
||||||
}?: SphereBufferGeometry(
|
}?: SphereGeometry(
|
||||||
radius = obj.radius,
|
radius = obj.radius,
|
||||||
phiStart = obj.phiStart,
|
phiStart = obj.phiStart,
|
||||||
phiLength = obj.phi,
|
phiLength = obj.phi,
|
||||||
|
@ -1,49 +1,52 @@
|
|||||||
@file:Suppress("INTERFACE_WITH_SUPERCLASS",
|
@file:Suppress(
|
||||||
|
"INTERFACE_WITH_SUPERCLASS",
|
||||||
"OVERRIDING_FINAL_MEMBER",
|
"OVERRIDING_FINAL_MEMBER",
|
||||||
"RETURN_TYPE_MISMATCH_ON_OVERRIDE",
|
"RETURN_TYPE_MISMATCH_ON_OVERRIDE",
|
||||||
"CONFLICTING_OVERLOADS",
|
"CONFLICTING_OVERLOADS",
|
||||||
"EXTERNAL_DELEGATION")
|
"EXTERNAL_DELEGATION"
|
||||||
|
)
|
||||||
|
|
||||||
@file:JsModule("three-csg-ts")
|
@file:JsModule("three-csg-ts")
|
||||||
@file:JsNonModule
|
@file:JsNonModule
|
||||||
|
|
||||||
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.math.Matrix4
|
import info.laht.threekt.math.Matrix4
|
||||||
import info.laht.threekt.math.Vector3
|
import info.laht.threekt.math.Vector3
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
|
|
||||||
external open class CSG {
|
public external class CSG {
|
||||||
open fun clone(): CSG
|
public fun clone(): CSG
|
||||||
open fun toPolygons(): Array<Polygon>
|
public fun toPolygons(): Array<Polygon>
|
||||||
open fun union(csg: CSG): CSG
|
public fun toGeometry(toMatrix: Matrix4): BufferGeometry
|
||||||
open fun subtract(csg: CSG): CSG
|
public fun union(csg: CSG): CSG
|
||||||
open fun intersect(csg: CSG): CSG
|
public fun subtract(csg: CSG): CSG
|
||||||
open fun inverse(): CSG
|
public fun intersect(csg: CSG): CSG
|
||||||
|
public fun inverse(): CSG
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
public companion object {
|
||||||
fun fromPolygons(polygons: Array<Polygon>): CSG
|
fun fromPolygons(polygons: Array<Polygon>): CSG
|
||||||
fun fromGeometry(geom: Any): CSG
|
fun fromGeometry(geom: BufferGeometry, objectIndex: dynamic = definedExternally): CSG
|
||||||
fun fromMesh(mesh: Mesh): CSG
|
fun fromMesh(mesh: Mesh, objectIndex: dynamic = definedExternally): CSG
|
||||||
|
fun toGeometry(csg: CSG, toMatrix: Matrix4): BufferGeometry
|
||||||
fun toMesh(csg: CSG, toMatrix: Matrix4): Mesh
|
fun toMesh(csg: CSG, toMatrix: Matrix4): Mesh
|
||||||
fun iEval(tokens: Mesh, index: Number? = definedExternally)
|
fun iEval(tokens: Mesh, index: Number? = definedExternally)
|
||||||
fun eval(tokens: Mesh, doRemove: Boolean): Mesh
|
fun eval(tokens: Mesh, doRemove: Boolean): Mesh
|
||||||
var _tmpm3: Any
|
fun union(meshA: Mesh, meshB: Mesh): Mesh
|
||||||
var doRemove: Any
|
fun subtract(meshA: Mesh, meshB: Mesh): Mesh
|
||||||
var currentOp: Any
|
fun intersect(meshA: Mesh, meshB: Mesh): Mesh
|
||||||
var currentPrim: Any
|
|
||||||
var nextPrim: Any
|
|
||||||
var sourceMesh: Any
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
external open class Vector(x: Number, y: Number, z: Number) : Vector3 {
|
external class Vector(x: Number, y: Number, z: Number) : Vector3 {
|
||||||
open fun negated(): Vector
|
fun negated(): Vector
|
||||||
open fun plus(a: Vector): Vector
|
fun plus(a: Vector): Vector
|
||||||
open fun minus(a: Vector): Vector
|
fun minus(a: Vector): Vector
|
||||||
open fun times(a: Number): Vector
|
fun times(a: Number): Vector
|
||||||
open fun dividedBy(a: Number): Vector
|
fun dividedBy(a: Number): Vector
|
||||||
open fun lerp(a: Vector, t: Number): Any
|
fun lerp(a: Vector, t: Number): Any
|
||||||
open fun unit(): Vector
|
fun unit(): Vector
|
||||||
open fun cross(a: Vector): Any
|
fun cross(a: Vector): Any
|
||||||
}
|
}
|
||||||
|
|
||||||
external interface IVector {
|
external interface IVector {
|
||||||
@ -52,21 +55,21 @@ external interface IVector {
|
|||||||
var z: Number
|
var z: Number
|
||||||
}
|
}
|
||||||
|
|
||||||
external open class Vertex(pos: IVector, normal: IVector, uv: IVector? = definedExternally) {
|
external class Vertex(pos: IVector, normal: IVector, uv: IVector? = definedExternally) {
|
||||||
open var pos: Vector
|
var pos: Vector
|
||||||
open var normal: Vector
|
var normal: Vector
|
||||||
open var uv: Vector
|
var uv: Vector
|
||||||
open fun clone(): Vertex
|
fun clone(): Vertex
|
||||||
open fun flip()
|
fun flip()
|
||||||
open fun interpolate(other: Vertex, t: Number): Vertex
|
fun interpolate(other: Vertex, t: Number): Vertex
|
||||||
}
|
}
|
||||||
|
|
||||||
external open class Plane(normal: Vector, w: Number) {
|
external class Plane(normal: Vector, w: Number) {
|
||||||
open var normal: Vector
|
var normal: Vector
|
||||||
open var w: Number
|
var w: Number
|
||||||
open fun clone(): Plane
|
fun clone(): Plane
|
||||||
open fun flip()
|
fun flip()
|
||||||
open fun splitPolygon(
|
fun splitPolygon(
|
||||||
polygon: Polygon,
|
polygon: Polygon,
|
||||||
coplanarFront: Array<Polygon>,
|
coplanarFront: Array<Polygon>,
|
||||||
coplanarBack: Array<Polygon>,
|
coplanarBack: Array<Polygon>,
|
||||||
@ -80,10 +83,10 @@ external open class Plane(normal: Vector, w: Number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
external open class Polygon(vertices: Array<Vertex>, shared: Any? = definedExternally) {
|
external class Polygon(vertices: Array<Vertex>, shared: Any? = definedExternally) {
|
||||||
open var plane: Plane
|
var plane: Plane
|
||||||
open var vertices: Array<Vertex>
|
var vertices: Array<Vertex>
|
||||||
open var shared: Any
|
var shared: Any
|
||||||
open fun clone(): Polygon
|
fun clone(): Polygon
|
||||||
open fun flip()
|
fun flip()
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package space.kscience.visionforge.solid.three
|
package space.kscience.visionforge.solid.three
|
||||||
|
|
||||||
import CSG
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.*
|
import info.laht.threekt.core.Layers
|
||||||
import info.laht.threekt.external.controls.OrbitControls
|
import info.laht.threekt.external.controls.OrbitControls
|
||||||
import info.laht.threekt.materials.Material
|
import info.laht.threekt.materials.Material
|
||||||
import info.laht.threekt.math.Euler
|
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 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
|
internal fun Double.toRadians() = this * PI / 180
|
||||||
|
|
||||||
public fun CSG.toGeometry(): Geometry {
|
|
||||||
val geom = Geometry()
|
|
||||||
|
|
||||||
val vertices = ArrayList<Vector3>()
|
|
||||||
val faces = ArrayList<Face3>()
|
|
||||||
|
|
||||||
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() {
|
internal fun Any.dispose() {
|
||||||
when (this) {
|
when (this) {
|
||||||
is Geometry -> dispose()
|
|
||||||
is BufferGeometry -> dispose()
|
is BufferGeometry -> dispose()
|
||||||
is DirectGeometry -> dispose()
|
|
||||||
is Material -> dispose()
|
is Material -> dispose()
|
||||||
is Mesh -> {
|
is Mesh -> {
|
||||||
geometry.dispose()
|
geometry.dispose()
|
||||||
|
Loading…
Reference in New Issue
Block a user