forked from kscience/visionforge
Added satelite detector demo
This commit is contained in:
parent
6b4bc6912f
commit
5b8d298ac6
24
demo/sat-demo/build.gradle.kts
Normal file
24
demo/sat-demo/build.gradle.kts
Normal file
@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.mpp")
|
||||
}
|
||||
|
||||
val kvisionVersion: String = "3.16.2"
|
||||
|
||||
kscience{
|
||||
application()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(project(":visionforge-solid"))
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package ru.mipt.npm.sat
|
||||
|
||||
import hep.dataforge.vision.solid.*
|
||||
import kotlin.math.PI
|
||||
|
||||
internal fun visionOfSatellite(
|
||||
layers: Int = 10,
|
||||
layerHeight: Number = 10,
|
||||
xSegments: Int = 3,
|
||||
ySegments: Int = xSegments,
|
||||
xSegmentSize: Number = 30,
|
||||
ySegmentSize: Number = xSegmentSize,
|
||||
fiberDiameter: Number = 1.0,
|
||||
): SolidGroup = SolidGroup {
|
||||
opacity = 0.3
|
||||
val totalXSize = xSegments * xSegmentSize.toDouble()
|
||||
val totalYSize = ySegments * ySegmentSize.toDouble()
|
||||
for (layer in 1..layers) {
|
||||
group("layer[$layer]") {
|
||||
for (i in 1..xSegments) {
|
||||
for (j in 1..ySegments) {
|
||||
box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") {
|
||||
z = (layer - 0.5) * layerHeight.toDouble()
|
||||
x = (i - 0.5) * xSegmentSize.toDouble()
|
||||
y = (j - 0.5) * ySegmentSize.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
||||
group("fibers") {
|
||||
for (i in 1..xSegments) {
|
||||
cylinder(fiberDiameter, totalYSize) {
|
||||
rotationX = PI / 2
|
||||
z = (layer - 1.0) * layerHeight.toDouble() + fiberDiameter.toDouble()
|
||||
x = (i - 0.5) * xSegmentSize.toDouble()
|
||||
y = totalYSize/2
|
||||
|
||||
color("red")
|
||||
}
|
||||
}
|
||||
|
||||
for (j in 1..ySegments) {
|
||||
cylinder(fiberDiameter, totalXSize) {
|
||||
rotationY = PI / 2
|
||||
z = (layer) * layerHeight.toDouble() - fiberDiameter.toDouble()
|
||||
y = (j - 0.5) * xSegmentSize.toDouble()
|
||||
x = totalXSize/2
|
||||
|
||||
color("blue")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package ru.mipt.npm.sat
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import hep.dataforge.vision.solid.three.ThreePlugin
|
||||
import hep.dataforge.vision.solid.three.render
|
||||
import kotlinx.browser.document
|
||||
import hep.dataforge.meta.invoke
|
||||
import org.w3c.dom.HTMLElement
|
||||
|
||||
private class SatDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
val element = document.getElementById("canvas") as? HTMLElement
|
||||
?: error("Element with id 'canvas' not found on page")
|
||||
val three = Global.plugins.fetch(ThreePlugin)
|
||||
val sat = visionOfSatellite(
|
||||
ySegments = 5,
|
||||
)
|
||||
three.render(element, sat){
|
||||
minSize = 500
|
||||
axes {
|
||||
size = 500.0
|
||||
visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::SatDemoApp)
|
||||
}
|
12
demo/sat-demo/src/jsMain/resources/index.html
Normal file
12
demo/sat-demo/src/jsMain/resources/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Three js demo for particle physics</title>
|
||||
<script type="text/javascript" src="sat-demo.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div id="canvas"></div>
|
||||
</body>
|
||||
</html>
|
@ -6,6 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
kscience {
|
||||
useCoroutines()
|
||||
val fxVersion: String by rootProject.extra
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
application()
|
||||
|
@ -2,7 +2,14 @@ package hep.dataforge.vision.solid.demo
|
||||
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import kotlin.browser.document
|
||||
import hep.dataforge.vision.solid.x
|
||||
import hep.dataforge.vision.solid.y
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.random.Random
|
||||
|
||||
private class ThreeDemoApp : Application {
|
||||
|
||||
@ -13,27 +20,25 @@ private class ThreeDemoApp : Application {
|
||||
ThreeDemoGrid(element).run {
|
||||
showcase()
|
||||
showcaseCSG()
|
||||
// demo("dynamicBox", "Dancing boxes") {
|
||||
// val boxes = (-10..10).flatMap { i ->
|
||||
// (-10..10).map { j ->
|
||||
// varBox(10, 10, 0, name = "cell_${i}_${j}") {
|
||||
// x = i * 10
|
||||
// y = j * 10
|
||||
// value = 128
|
||||
// setProperty(EDGES_ENABLED_KEY, false)
|
||||
// setProperty(WIREFRAME_ENABLED_KEY, false)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// GlobalScope.launch {
|
||||
// while (isActive) {
|
||||
// delay(500)
|
||||
// boxes.forEach { box ->
|
||||
// box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(0..255)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
demo("dynamicBox", "Dancing boxes") {
|
||||
val boxes = (-10..10).flatMap { i ->
|
||||
(-10..10).map { j ->
|
||||
varBox(10, 10, 0, name = "cell_${i}_${j}") {
|
||||
x = i * 10
|
||||
y = j * 10
|
||||
value = 128
|
||||
}
|
||||
}
|
||||
}
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
boxes.forEach { box ->
|
||||
box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(1..255)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,6 +11,8 @@ import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||
import hep.dataforge.vision.solid.three.ThreePlugin
|
||||
import hep.dataforge.vision.solid.three.output
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.dom.clear
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.dom.create
|
||||
import kotlinx.html.h2
|
||||
@ -19,8 +21,6 @@ import kotlinx.html.id
|
||||
import kotlinx.html.js.div
|
||||
import kotlinx.html.span
|
||||
import org.w3c.dom.Element
|
||||
import kotlin.browser.document
|
||||
import kotlin.dom.clear
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class ThreeDemoGrid(element: Element, meta: Meta = Meta.EMPTY) : OutputManager {
|
||||
|
@ -4,6 +4,7 @@ package hep.dataforge.vision.solid.demo
|
||||
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.meta.number
|
||||
import hep.dataforge.meta.set
|
||||
import hep.dataforge.meta.setItem
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.names.startsWith
|
||||
@ -12,7 +13,6 @@ import hep.dataforge.vision.getProperty
|
||||
import hep.dataforge.vision.set
|
||||
import hep.dataforge.vision.solid.*
|
||||
import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY
|
||||
import hep.dataforge.vision.solid.demo.VariableBoxThreeFactory.Z_SIZE_KEY
|
||||
import hep.dataforge.vision.solid.three.*
|
||||
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
@ -21,86 +21,89 @@ import info.laht.threekt.geometries.BoxBufferGeometry
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlin.math.max
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal var Solid.variableZSize: Number
|
||||
get() = getProperty(Z_SIZE_KEY, false).number ?: 0f
|
||||
set(value) {
|
||||
setItem(Z_SIZE_KEY, value.asValue())
|
||||
}
|
||||
|
||||
internal var Solid.value: Int
|
||||
get() = getProperty("value", false).int ?: 0
|
||||
set(value) {
|
||||
setItem("value", value.asValue())
|
||||
val size = value.toFloat() / 255f * 20f
|
||||
scaleZ = size
|
||||
z = -size / 2
|
||||
|
||||
val b = max(0, 255 - value)
|
||||
val r = max(0, value - 255)
|
||||
val g = 255 - b - r
|
||||
color(r.toUByte(), g.toUByte(), b.toUByte())
|
||||
}
|
||||
|
||||
fun SolidGroup.varBox(
|
||||
internal fun SolidGroup.varBox(
|
||||
xSize: Number,
|
||||
ySize: Number,
|
||||
zSize: Number,
|
||||
name: String = "",
|
||||
action: Solid.() -> Unit = {}
|
||||
) = CustomThreeVision(VariableBoxThreeFactory).apply {
|
||||
scaleX = xSize
|
||||
scaleY = ySize
|
||||
scaleZ = zSize
|
||||
}.apply(action).also { set(name, it) }
|
||||
action: VariableBox.() -> Unit = {},
|
||||
): VariableBox = VariableBox(xSize, ySize, zSize).apply(action).also { set(name, it) }
|
||||
|
||||
private object VariableBoxThreeFactory : ThreeFactory<Solid> {
|
||||
val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
|
||||
val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
|
||||
val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
|
||||
internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeVision() {
|
||||
init {
|
||||
scaleX = xSize
|
||||
scaleY = ySize
|
||||
scaleZ = zSize
|
||||
config[MeshThreeFactory.EDGES_ENABLED_KEY] = false
|
||||
config[MeshThreeFactory.WIREFRAME_ENABLED_KEY] = false
|
||||
}
|
||||
|
||||
override val type: KClass<in Solid> get() = Solid::class
|
||||
|
||||
override fun invoke(obj: Solid): Object3D {
|
||||
val xSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val ySize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val zSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
override fun render(): Object3D {
|
||||
val xSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val ySize = getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val zSize = getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val geometry = BoxBufferGeometry(1, 1, 1)
|
||||
|
||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
||||
|
||||
val mesh = Mesh(geometry, getMaterial(obj,true)).apply {
|
||||
applyEdges(obj)
|
||||
applyWireFrame(obj)
|
||||
val mesh = Mesh(geometry, getMaterial(this@VariableBox, true)).apply {
|
||||
applyEdges(this@VariableBox)
|
||||
applyWireFrame(this@VariableBox)
|
||||
|
||||
//set position for mesh
|
||||
updatePosition(obj)
|
||||
updatePosition(this@VariableBox)
|
||||
|
||||
layers.enable(obj.layer)
|
||||
layers.enable(this@VariableBox.layer)
|
||||
children.forEach {
|
||||
it.layers.enable(obj.layer)
|
||||
it.layers.enable(this@VariableBox.layer)
|
||||
}
|
||||
}
|
||||
|
||||
mesh.scale.set(xSize, ySize, zSize)
|
||||
|
||||
//add listener to object properties
|
||||
obj.onPropertyChange(this) { name ->
|
||||
onPropertyChange(mesh) { name ->
|
||||
when {
|
||||
// name.startsWith(GEOMETRY_KEY) -> {
|
||||
// val newXSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
// val newYSize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
// val newZSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
// mesh.scale.set(newXSize, newYSize, newZSize)
|
||||
// mesh.updateMatrix()
|
||||
// }
|
||||
name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
|
||||
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(obj)
|
||||
else -> mesh.updateProperty(obj, name)
|
||||
name.startsWith(GEOMETRY_KEY) -> {
|
||||
val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val newYSize = getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val newZSize = getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
mesh.scale.set(newXSize, newYSize, newZSize)
|
||||
mesh.updateMatrix()
|
||||
}
|
||||
name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox)
|
||||
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox)
|
||||
else -> mesh.updateProperty(this@VariableBox, name)
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
|
||||
var variableZSize: Number
|
||||
get() = getProperty(Z_SIZE_KEY, false).number ?: 0f
|
||||
set(value) {
|
||||
setItem(Z_SIZE_KEY, value.asValue())
|
||||
}
|
||||
|
||||
var value: Int
|
||||
get() = getProperty("value", false).int ?: 0
|
||||
set(value) {
|
||||
setItem("value", value.asValue())
|
||||
val size = value.toFloat() / 255f * 20f
|
||||
scaleZ = size
|
||||
z = size / 2
|
||||
|
||||
val b = max(0, 128 - value)
|
||||
val r = max(0, value - 128)
|
||||
val g = 255 - b - r
|
||||
color(r.toUByte(), g.toUByte(), b.toUByte())
|
||||
}
|
||||
|
||||
companion object{
|
||||
private val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
|
||||
private val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
|
||||
private val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
|
||||
}
|
||||
}
|
@ -43,5 +43,6 @@ include(
|
||||
":demo:spatial-showcase",
|
||||
":demo:gdml",
|
||||
":demo:muon-monitor",
|
||||
":demo:sat-demo",
|
||||
":playground"
|
||||
)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package hep.dataforge.vision.bootstrap
|
||||
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.vision.solid.SolidGroup
|
||||
import hep.dataforge.vision.solid.SolidManager
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||
@ -15,8 +14,7 @@ import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.files.Blob
|
||||
import org.w3c.files.BlobPropertyBag
|
||||
import react.RBuilder
|
||||
import react.ReactElement
|
||||
import react.*
|
||||
import react.dom.button
|
||||
import react.dom.div
|
||||
import react.dom.input
|
||||
@ -31,37 +29,51 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl
|
||||
fileSaver.saveAs(blob, fileName)
|
||||
}
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
public fun RBuilder.canvasControls(canvas: ThreeCanvas): ReactElement = accordion("controls") {
|
||||
entry("Settings") {
|
||||
div("row") {
|
||||
div("col-2") {
|
||||
label("checkbox-inline") {
|
||||
input(type = InputType.checkBox) {
|
||||
attrs {
|
||||
defaultChecked = canvas.axes.visible
|
||||
onChangeFunction = {
|
||||
canvas.axes.visible = (it.target as HTMLInputElement).checked
|
||||
public fun RBuilder.canvasControls(canvas: ThreeCanvas): ReactElement {
|
||||
return child(CanvasControls){
|
||||
attrs{
|
||||
this.canvas = canvas
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public external interface CanvasControlsProps : RProps {
|
||||
public var canvas: ThreeCanvas
|
||||
}
|
||||
|
||||
public val CanvasControls: FunctionalComponent<CanvasControlsProps> = functionalComponent ("CanvasControls") { props ->
|
||||
val visionManager = useMemo(
|
||||
{ props.canvas.context.plugins.fetch(SolidManager).visionManager },
|
||||
arrayOf(props.canvas)
|
||||
)
|
||||
accordion("controls") {
|
||||
entry("Settings") {
|
||||
div("row") {
|
||||
div("col-2") {
|
||||
label("checkbox-inline") {
|
||||
input(type = InputType.checkBox) {
|
||||
attrs {
|
||||
defaultChecked = props.canvas.axes.visible
|
||||
onChangeFunction = {
|
||||
props.canvas.axes.visible = (it.target as HTMLInputElement).checked
|
||||
}
|
||||
}
|
||||
}
|
||||
+"Axes"
|
||||
}
|
||||
+"Axes"
|
||||
}
|
||||
}
|
||||
div("col-1") {
|
||||
button {
|
||||
+"Export"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
val json = (canvas.content as? SolidGroup)?.let { group ->
|
||||
SolidManager.jsonForSolids.encodeToString(
|
||||
SolidGroup.serializer(),
|
||||
group
|
||||
)
|
||||
}
|
||||
if (json != null) {
|
||||
saveData(it, "object.json", "text/json") {
|
||||
json
|
||||
div("col-1") {
|
||||
button {
|
||||
+"Export"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
val json = (props.canvas.content as? SolidGroup)?.let { group ->
|
||||
visionManager.encodeToString(group)
|
||||
}
|
||||
if (json != null) {
|
||||
saveData(it, "object.json", "text/json") {
|
||||
json
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,22 +81,22 @@ public fun RBuilder.canvasControls(canvas: ThreeCanvas): ReactElement = accordio
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
entry("Layers") {
|
||||
div("row") {
|
||||
(0..11).forEach { layer ->
|
||||
div("col-1") {
|
||||
label { +layer.toString() }
|
||||
input(type = InputType.checkBox) {
|
||||
attrs {
|
||||
if (layer == 0) {
|
||||
defaultChecked = true
|
||||
}
|
||||
onChangeFunction = {
|
||||
if ((it.target as HTMLInputElement).checked) {
|
||||
canvas.camera.layers.enable(layer)
|
||||
} else {
|
||||
canvas.camera.layers.disable(layer)
|
||||
entry("Layers") {
|
||||
div("row") {
|
||||
(0..11).forEach { layer ->
|
||||
div("col-1") {
|
||||
label { +layer.toString() }
|
||||
input(type = InputType.checkBox) {
|
||||
attrs {
|
||||
if (layer == 0) {
|
||||
defaultChecked = true
|
||||
}
|
||||
onChangeFunction = {
|
||||
if ((it.target as HTMLInputElement).checked) {
|
||||
props.canvas.camera.layers.enable(layer)
|
||||
} else {
|
||||
props.canvas.camera.layers.disable(layer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,10 +130,8 @@ public fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer
|
||||
+"Export"
|
||||
onClickFunction = {
|
||||
val json = (canvas.content as? SolidGroup)?.let { group ->
|
||||
SolidManager.jsonForSolids.encodeToString(
|
||||
SolidGroup.serializer(),
|
||||
group
|
||||
)
|
||||
val visionManager = canvas.context.plugins.fetch(SolidManager).visionManager
|
||||
visionManager.encodeToString(group)
|
||||
}
|
||||
if (json != null) {
|
||||
saveData(it, "object.json", "text/json") {
|
||||
|
@ -1,5 +1,8 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.names.*
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@ -7,7 +10,7 @@ import kotlinx.serialization.Transient
|
||||
/**
|
||||
* Abstract implementation of mutable group of [Vision]
|
||||
*/
|
||||
abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
|
||||
//protected abstract val _children: MutableMap<NameToken, T>
|
||||
|
||||
@ -22,30 +25,31 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
/**
|
||||
* Update or create stylesheet
|
||||
*/
|
||||
fun styleSheet(block: StyleSheet.() -> Unit) {
|
||||
val res = styleSheet ?: StyleSheet(this).also { styleSheet = it }
|
||||
res.block()
|
||||
public fun styleSheet(block: StyleSheet.() -> Unit) {
|
||||
if (styleSheet == null) {
|
||||
styleSheet = StyleSheet(this@AbstractVisionGroup)
|
||||
}
|
||||
styleSheet!!.block()
|
||||
}
|
||||
|
||||
override fun propertyChanged(name: Name) {
|
||||
super.propertyChanged(name)
|
||||
for(obj in this) {
|
||||
for (obj in this) {
|
||||
obj.propertyChanged(name)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Consider renaming to `StructureChangeListener` (singular)
|
||||
private data class StructureChangeListeners(val owner: Any?, val callback: (Name, Vision?) -> Unit)
|
||||
private data class StructureChangeListener(val owner: Any?, val callback: (NameToken, Vision?) -> Unit)
|
||||
|
||||
@Transient
|
||||
private val structureChangeListeners = HashSet<StructureChangeListeners>()
|
||||
private val structureChangeListeners = HashSet<StructureChangeListener>()
|
||||
|
||||
/**
|
||||
* Add listener for children change
|
||||
*/
|
||||
override fun onChildrenChange(owner: Any?, action: (Name, Vision?) -> Unit) {
|
||||
override fun onChildrenChange(owner: Any?, action: (NameToken, Vision?) -> Unit) {
|
||||
structureChangeListeners.add(
|
||||
StructureChangeListeners(
|
||||
StructureChangeListener(
|
||||
owner,
|
||||
action
|
||||
)
|
||||
@ -62,7 +66,7 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
/**
|
||||
* Propagate children change event upwards
|
||||
*/
|
||||
protected fun childrenChanged(name: Name, child: Vision?) {
|
||||
protected fun childrenChanged(name: NameToken, child: Vision?) {
|
||||
structureChangeListeners.forEach { it.callback(name, child) }
|
||||
}
|
||||
|
||||
@ -77,10 +81,11 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
protected abstract fun setChild(token: NameToken, child: Vision)
|
||||
|
||||
/**
|
||||
* Add a static child. Statics could not be found by name, removed or replaced
|
||||
* Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events.
|
||||
*/
|
||||
protected open fun addStatic(child: Vision) =
|
||||
set(NameToken("@static(${child.hashCode()})").asName(), child)
|
||||
protected open fun addStatic(child: Vision): Unit {
|
||||
setChild(NameToken("@static", index = child.hashCode().toString()), child)
|
||||
}
|
||||
|
||||
protected abstract fun createGroup(): AbstractVisionGroup
|
||||
|
||||
@ -124,6 +129,7 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
when {
|
||||
name.isEmpty() -> {
|
||||
if (child != null) {
|
||||
attach(child)
|
||||
addStatic(child)
|
||||
}
|
||||
}
|
||||
@ -135,6 +141,7 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
attach(child)
|
||||
setChild(token, child)
|
||||
}
|
||||
childrenChanged(token, child)
|
||||
}
|
||||
else -> {
|
||||
//TODO add safety check
|
||||
@ -142,7 +149,13 @@ abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
parent[name.tokens.last().asName()] = child
|
||||
}
|
||||
}
|
||||
childrenChanged(name, child)
|
||||
}
|
||||
|
||||
override fun update(meta: Meta) {
|
||||
val styleMeta = meta.get("styleSheet").node
|
||||
if (styleMeta != null) {
|
||||
this.styleSheet?.update(styleMeta)
|
||||
}
|
||||
super.update(meta)
|
||||
}
|
||||
}
|
@ -235,12 +235,12 @@ public object Colors {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert three bytes representing color to Meta
|
||||
*/
|
||||
fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = Meta {
|
||||
RED_KEY put r.toInt()
|
||||
GREEN_KEY put g.toInt()
|
||||
BLUE_KEY put b.toInt()
|
||||
}
|
||||
// /**
|
||||
// * Convert three bytes representing color to Meta
|
||||
// */
|
||||
// fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = Meta {
|
||||
// RED_KEY put r.toInt()
|
||||
// GREEN_KEY put g.toInt()
|
||||
// BLUE_KEY put b.toInt()
|
||||
// }
|
||||
}
|
@ -8,13 +8,10 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("group")
|
||||
class SimpleVisionGroup : AbstractVisionGroup() {
|
||||
public class SimpleVisionGroup : AbstractVisionGroup() {
|
||||
|
||||
override var styleSheet: StyleSheet? = null
|
||||
|
||||
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
|
||||
override var properties: Config? = null
|
||||
|
||||
@SerialName("children")
|
||||
private val _children = HashMap<NameToken, Vision>()
|
||||
override val children: Map<NameToken, Vision> get() = _children
|
||||
|
@ -16,11 +16,11 @@ import kotlinx.serialization.encoding.Encoder
|
||||
* A container for styles
|
||||
*/
|
||||
@Serializable
|
||||
public class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta> = LinkedHashMap()) {
|
||||
public class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta>) {
|
||||
@Transient
|
||||
internal var owner: Vision? = null
|
||||
|
||||
public constructor(owner: Vision) : this() {
|
||||
public constructor(owner: Vision) : this(LinkedHashMap()) {
|
||||
this.owner = owner
|
||||
}
|
||||
|
||||
@ -91,6 +91,10 @@ public class StyleSheet private constructor(private val styleMap: MutableMap<Str
|
||||
}
|
||||
}
|
||||
|
||||
public fun StyleSheet.update(styleMeta: Meta){
|
||||
TODO()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
|
||||
|
@ -69,7 +69,7 @@ public interface Vision : Configurable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this vision using external meta.
|
||||
* Update this vision using external meta. Children are not updated.
|
||||
*/
|
||||
public fun update(meta: Meta)
|
||||
|
||||
|
@ -0,0 +1,50 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.set
|
||||
import hep.dataforge.names.NameToken
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
||||
|
||||
//public class VisionChange(public val properties: Meta, public val childrenChange: Map<NameToken, VisionChange>)
|
||||
|
||||
public class VisionChangeCollector(
|
||||
public val manager: VisionManager,
|
||||
public val scope: CoroutineScope,
|
||||
public val vision: Vision,
|
||||
public val lock: Mutex = Mutex()
|
||||
) {
|
||||
private val collector: Config = Config()
|
||||
private val childrenCollectors = HashMap<NameToken, VisionChangeCollector>()
|
||||
|
||||
init {
|
||||
vision.onPropertyChange(this) { propertyName ->
|
||||
collector[propertyName] = vision.properties?.get(propertyName)
|
||||
}
|
||||
if (vision is VisionGroup) {
|
||||
vision.children.forEach { (token, child) ->
|
||||
childrenCollectors[token] = VisionChangeCollector(manager, scope, child, lock)
|
||||
}
|
||||
}
|
||||
if (vision is MutableVisionGroup) {
|
||||
TODO("Tread vision structure change")
|
||||
// vision.onChildrenChange(this) { childName, child ->
|
||||
// if(child == null){
|
||||
// childrenCollectors[childName] = null
|
||||
// } else {
|
||||
// childrenCollectors[childName] = manager.encodeToMeta(child)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//fun collectUpdates(manager: VisionManager, scope: CoroutineScope, vision: Vision): Flow<Meta> {
|
||||
//
|
||||
//
|
||||
// vision.
|
||||
//}
|
@ -87,7 +87,7 @@ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision
|
||||
* @param owner the handler to properly remove listeners
|
||||
* @param action First argument of the action is the name of changed child. Second argument is the new value of the object.
|
||||
*/
|
||||
public fun onChildrenChange(owner: Any?, action: (Name, Vision?) -> Unit)
|
||||
public fun onChildrenChange(owner: Any?, action: (NameToken, Vision?) -> Unit)
|
||||
|
||||
/**
|
||||
* Remove children change listener
|
||||
|
@ -3,6 +3,10 @@ package hep.dataforge.vision
|
||||
import hep.dataforge.context.*
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.values.Null
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
@ -10,7 +14,6 @@ import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.subclass
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@DFExperimental
|
||||
public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
@ -50,36 +53,30 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
?: error("Expected node, but value found. Check your serializer!")
|
||||
|
||||
public fun updateVision(vision: Vision, meta: Meta) {
|
||||
|
||||
vision.update(meta)
|
||||
if (vision is MutableVisionGroup) {
|
||||
val children by meta.node()
|
||||
children?.items?.forEach { (token, item) ->
|
||||
when {
|
||||
item.value == Null -> vision[token] = null //Null means removal
|
||||
item.node != null -> {
|
||||
val node = item.node!!
|
||||
val type by node.string()
|
||||
if (type != null) {
|
||||
//If the type is present considering it as new node, not an update
|
||||
vision[token.asName()] = decodeFromMeta(node)
|
||||
} else {
|
||||
val existing = vision.children[token]
|
||||
if (existing != null) {
|
||||
updateVision(existing, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public fun <T : Vision> VisionForm<T>.buildVision(meta: Meta, descriptor: NodeDescriptor? = null): T {
|
||||
// val json = meta.toJson(descriptor)
|
||||
// return jsonFormat.decodeFromJsonElement(serializer, json)
|
||||
// }
|
||||
//
|
||||
// @OptIn(ExperimentalSerializationApi::class)
|
||||
// public fun buildVision(meta: Meta): Vision {
|
||||
// val type = meta["type"].string ?: Vision.serializer().descriptor.serialName
|
||||
// val form = forms.values.find { it.name.toString() == type } ?: error("Could not resolve a form for type $type")
|
||||
// return form.buildVision(meta)
|
||||
// }
|
||||
//
|
||||
// public inline fun <reified T : Vision> buildSpecificVision(meta: Meta): T {
|
||||
// val factory = resolveVisionForm(T::class)
|
||||
// return factory.buildVision(meta)
|
||||
// }
|
||||
//
|
||||
// public fun <T : Vision> writeVisionToMeta(vision: T): Meta {
|
||||
// val form = resolveVisionForm(vision::class) ?: error("Could not resolve a form for $vision")
|
||||
// val json = jsonFormat.encodeToJsonElement(form.serializer, vision)
|
||||
// return json.toMetaItem().node!!
|
||||
// }
|
||||
//
|
||||
// public fun updateVision(vision: Vision, meta: Meta) {
|
||||
// resolveVisionForm(vision::class).patch(vision, meta)
|
||||
// }
|
||||
|
||||
public companion object : PluginFactory<VisionManager> {
|
||||
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
|
||||
override val type: KClass<out VisionManager> = VisionManager::class
|
||||
|
@ -57,7 +57,7 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder {
|
||||
override var scale: Point3D? = null
|
||||
|
||||
@SerialName("children")
|
||||
private val _children = HashMap<NameToken, Vision>()
|
||||
private val _children = LinkedHashMap<NameToken, Vision>()
|
||||
override val children: Map<NameToken, Vision> get() = _children
|
||||
|
||||
override fun attachChildren() {
|
||||
@ -122,7 +122,7 @@ internal class Prototypes(
|
||||
|
||||
override fun removeChild(token: NameToken) {
|
||||
children.remove(token)
|
||||
childrenChanged(token.asName(), null)
|
||||
childrenChanged(token, null)
|
||||
}
|
||||
|
||||
override fun setChild(token: NameToken, child: Vision) {
|
||||
|
@ -16,43 +16,7 @@ import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
//@OptIn(ExperimentalSerializationApi::class)
|
||||
//@DFExperimental
|
||||
//private fun SerializersModule.extractFactories(): List<SolidForm<*>> {
|
||||
// val list = ArrayList<SolidForm<*>>()
|
||||
//
|
||||
// val collector = object : SerializersModuleCollector {
|
||||
// override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>) {
|
||||
// //Do nothing
|
||||
// }
|
||||
//
|
||||
// override fun <Base : Any, Sub : Base> polymorphic(
|
||||
// baseClass: KClass<Base>,
|
||||
// actualClass: KClass<Sub>,
|
||||
// actualSerializer: KSerializer<Sub>,
|
||||
// ) {
|
||||
// if (baseClass == Vision::class) {
|
||||
// @Suppress("UNCHECKED_CAST") val factory: SolidForm<Solid> = SolidForm(
|
||||
// actualClass as KClass<Solid>,
|
||||
// actualSerializer as KSerializer<Solid>
|
||||
// )
|
||||
// list.add(factory)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun <Base : Any> polymorphicDefault(
|
||||
// baseClass: KClass<Base>,
|
||||
// defaultSerializerProvider: (className: String?) -> DeserializationStrategy<out Base>?,
|
||||
// ) {
|
||||
// TODO("Not yet implemented")
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// dumpTo(collector)
|
||||
// return list
|
||||
//}
|
||||
|
||||
@DFExperimental
|
||||
public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
|
||||
public val visionManager: VisionManager by require(VisionManager)
|
||||
@ -96,18 +60,16 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
}
|
||||
}
|
||||
|
||||
public val jsonForSolids = Json{
|
||||
internal val jsonForSolids: Json = Json{
|
||||
prettyPrint = true
|
||||
useArrayPolymorphism = false
|
||||
encodeDefaults = false
|
||||
ignoreUnknownKeys = true
|
||||
serializersModule = SolidManager.serializersModuleForSolids
|
||||
serializersModule = serializersModuleForSolids
|
||||
}
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
public fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(Vision.serializer(), solid)
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
public fun decodeFromString(str: String): Vision = jsonForSolids.decodeFromString(Vision.serializer(), str).also {
|
||||
if(it is VisionGroup){
|
||||
it.attachChildren()
|
||||
|
@ -91,7 +91,7 @@ public fun Solid.color(rgb: Int) {
|
||||
|
||||
public fun Solid.color(r: UByte, g: UByte, b: UByte): Unit = setItem(
|
||||
MATERIAL_COLOR_KEY,
|
||||
Colors.rgbToMeta(r, g, b)
|
||||
Colors.rgbToString(r, g, b).asValue()
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -1,15 +1,20 @@
|
||||
package hep.dataforge.vision.solid.specifications
|
||||
|
||||
import hep.dataforge.meta.Scheme
|
||||
import hep.dataforge.meta.SchemeSpec
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.meta.spec
|
||||
import hep.dataforge.meta.*
|
||||
|
||||
public class Canvas3DOptions : Scheme() {
|
||||
public var axes: Axes by spec(Axes, Axes.empty())
|
||||
public var camera: Camera by spec(Camera, Camera.empty())
|
||||
public var controls: Controls by spec(Controls, Controls.empty())
|
||||
|
||||
public var minSize: Int by int(300)
|
||||
public var minWith: Number by number { minSize }
|
||||
public var minHeight: Number by number { minSize }
|
||||
|
||||
public var maxSize: Int by int(Int.MAX_VALUE)
|
||||
public var maxWith: Number by number { maxSize }
|
||||
public var maxHeight: Number by number { maxSize }
|
||||
|
||||
|
||||
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions)
|
||||
}
|
@ -4,6 +4,6 @@ import hep.dataforge.meta.Scheme
|
||||
import hep.dataforge.meta.SchemeSpec
|
||||
|
||||
|
||||
class Controls : Scheme() {
|
||||
companion object : SchemeSpec<Controls>(::Controls)
|
||||
public class Controls : Scheme() {
|
||||
public companion object : SchemeSpec<Controls>(::Controls)
|
||||
}
|
@ -35,7 +35,7 @@ internal fun mergeChild(parent: VisionGroup, child: Vision): Vision {
|
||||
}
|
||||
|
||||
@DFExperimental
|
||||
object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
|
||||
internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
|
||||
|
||||
override fun SolidGroup.transformInPlace() {
|
||||
fun MutableVisionGroup.replaceChildren() {
|
||||
|
@ -9,7 +9,7 @@ import hep.dataforge.vision.solid.Proxy
|
||||
import hep.dataforge.vision.solid.SolidGroup
|
||||
|
||||
@DFExperimental
|
||||
object UnRef : VisualTreeTransform<SolidGroup>() {
|
||||
internal object UnRef : VisualTreeTransform<SolidGroup>() {
|
||||
private fun VisionGroup.countRefs(): Map<Name, Int> {
|
||||
return children.values.fold(HashMap()) { reducer, obj ->
|
||||
if (obj is VisionGroup) {
|
||||
|
@ -5,11 +5,11 @@ import hep.dataforge.vision.Vision
|
||||
/**
|
||||
* A root class for [Vision] tree optimization
|
||||
*/
|
||||
abstract class VisualTreeTransform<T : Vision> {
|
||||
public abstract class VisualTreeTransform<T : Vision> {
|
||||
protected abstract fun T.transformInPlace()
|
||||
protected abstract fun T.clone(): T
|
||||
|
||||
operator fun invoke(source: T, inPlace: Boolean = true): T {
|
||||
public operator fun invoke(source: T, inPlace: Boolean = true): T {
|
||||
val newSource = if (inPlace) {
|
||||
source
|
||||
} else {
|
||||
@ -21,7 +21,7 @@ abstract class VisualTreeTransform<T : Vision> {
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Vision> T.transform(vararg transform: VisualTreeTransform<T>): T {
|
||||
public fun <T : Vision> T.transform(vararg transform: VisualTreeTransform<T>): T {
|
||||
var res = this
|
||||
transform.forEach {
|
||||
res = it(res)
|
||||
@ -29,6 +29,6 @@ fun <T : Vision> T.transform(vararg transform: VisualTreeTransform<T>): T {
|
||||
return res
|
||||
}
|
||||
|
||||
fun <T : Vision> T.transformInPlace(vararg transform: VisualTreeTransform<in T>) {
|
||||
public fun <T : Vision> T.transformInPlace(vararg transform: VisualTreeTransform<in T>) {
|
||||
transform.forEach { it(this) }
|
||||
}
|
@ -2,6 +2,7 @@ package hep.dataforge.vision.solid.three
|
||||
|
||||
import hep.dataforge.meta.boolean
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.names.startsWith
|
||||
@ -19,13 +20,13 @@ import kotlin.reflect.KClass
|
||||
/**
|
||||
* Basic geometry-based factory
|
||||
*/
|
||||
abstract class MeshThreeFactory<in T : Solid>(
|
||||
public abstract class MeshThreeFactory<in T : Solid>(
|
||||
override val type: KClass<in T>
|
||||
) : ThreeFactory<T> {
|
||||
/**
|
||||
* Build a geometry for an object
|
||||
*/
|
||||
abstract fun buildGeometry(obj: T): BufferGeometry
|
||||
public abstract fun buildGeometry(obj: T): BufferGeometry
|
||||
|
||||
override fun invoke(obj: T): Mesh {
|
||||
val geometry = buildGeometry(obj)
|
||||
@ -60,14 +61,14 @@ abstract class MeshThreeFactory<in T : Solid>(
|
||||
return mesh
|
||||
}
|
||||
|
||||
companion object {
|
||||
val EDGES_KEY = "edges".asName()
|
||||
val WIREFRAME_KEY = "wireframe".asName()
|
||||
val ENABLED_KEY = "enabled".asName()
|
||||
val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY
|
||||
val EDGES_MATERIAL_KEY = EDGES_KEY + SolidMaterial.MATERIAL_KEY
|
||||
val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY
|
||||
val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY
|
||||
public companion object {
|
||||
public val EDGES_KEY: Name = "edges".asName()
|
||||
public val WIREFRAME_KEY: Name = "wireframe".asName()
|
||||
public val ENABLED_KEY: Name = "enabled".asName()
|
||||
public val EDGES_ENABLED_KEY: Name = EDGES_KEY + ENABLED_KEY
|
||||
public val EDGES_MATERIAL_KEY: Name = EDGES_KEY + SolidMaterial.MATERIAL_KEY
|
||||
public val WIREFRAME_ENABLED_KEY: Name = WIREFRAME_KEY + ENABLED_KEY
|
||||
public val WIREFRAME_MATERIAL_KEY: Name = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ import hep.dataforge.vision.solid.Box
|
||||
import hep.dataforge.vision.solid.detail
|
||||
import info.laht.threekt.geometries.BoxBufferGeometry
|
||||
|
||||
object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
|
||||
override fun buildGeometry(obj: Box) =
|
||||
public object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
|
||||
override fun buildGeometry(obj: Box): BoxBufferGeometry =
|
||||
obj.detail?.let { detail ->
|
||||
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail)
|
||||
} ?: BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize)
|
||||
|
@ -35,7 +35,6 @@ import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.events.MouseEvent
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
@ -112,7 +111,10 @@ public class ThreeCanvas(
|
||||
|
||||
element.appendChild(renderer.domElement)
|
||||
|
||||
renderer.setSize(max(options.minSize, element.clientWidth), max(options.minSize, element.clientWidth))
|
||||
renderer.setSize(
|
||||
element.clientWidth.coerceIn(options.minWith.toInt()..options.maxWith.toInt()),
|
||||
element.clientHeight.coerceIn(options.minHeight.toInt()..options.maxHeight.toInt())
|
||||
)
|
||||
|
||||
window.onresize = {
|
||||
renderer.setSize(element.clientWidth, element.clientWidth)
|
||||
@ -238,7 +240,7 @@ public class ThreeCanvas(
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public const val DO_NOT_HIGHLIGHT_TAG = "doNotHighlight"
|
||||
public const val DO_NOT_HIGHLIGHT_TAG: String = "doNotHighlight"
|
||||
private const val HIGHLIGHT_NAME = "@highlight"
|
||||
private const val SELECT_NAME = "@select"
|
||||
}
|
||||
@ -251,7 +253,8 @@ public fun ThreePlugin.output(
|
||||
): ThreeCanvas = ThreeCanvas(element, this, spec, onClick)
|
||||
|
||||
public fun ThreePlugin.render(
|
||||
element: HTMLElement, obj: Solid,
|
||||
spec: Canvas3DOptions = Canvas3DOptions.empty(),
|
||||
onClick: ((Name?) -> Unit)? = null,
|
||||
): Unit = output(element, spec, onClick).render(obj)
|
||||
element: HTMLElement,
|
||||
obj: Solid,
|
||||
onSelect: ((Name?) -> Unit)? = null,
|
||||
options: Canvas3DOptions.() -> Unit = {},
|
||||
): Unit = output(element, Canvas3DOptions(options), onSelect).render(obj)
|
@ -9,17 +9,17 @@ import info.laht.threekt.geometries.PlaneBufferGeometry
|
||||
import info.laht.threekt.materials.MeshBasicMaterial
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import info.laht.threekt.textures.Texture
|
||||
import kotlinx.browser.document
|
||||
import org.w3c.dom.CanvasRenderingContext2D
|
||||
import org.w3c.dom.CanvasTextBaseline
|
||||
import org.w3c.dom.HTMLCanvasElement
|
||||
import org.w3c.dom.MIDDLE
|
||||
import kotlin.browser.document
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Using example from http://stemkoski.github.io/Three.js/Texture-From-Canvas.html
|
||||
*/
|
||||
object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
|
||||
public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
|
||||
override val type: KClass<in SolidLabel> get() = SolidLabel::class
|
||||
|
||||
override fun invoke(obj: SolidLabel): Object3D {
|
||||
|
@ -8,7 +8,7 @@ import info.laht.threekt.objects.Mesh
|
||||
/**
|
||||
* This should be inner, because it uses object builder
|
||||
*/
|
||||
class ThreeCompositeFactory(val three: ThreePlugin) : MeshThreeFactory<Composite>(Composite::class) {
|
||||
public class ThreeCompositeFactory(public val three: ThreePlugin) : MeshThreeFactory<Composite>(Composite::class) {
|
||||
|
||||
override fun buildGeometry(obj: Composite): BufferGeometry {
|
||||
val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh")
|
||||
|
@ -4,7 +4,7 @@ import hep.dataforge.vision.solid.Convex
|
||||
import info.laht.threekt.external.geometries.ConvexBufferGeometry
|
||||
import info.laht.threekt.math.Vector3
|
||||
|
||||
object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
|
||||
public object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
|
||||
override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
|
||||
@Suppress("USELESS_CAST") val vectors = obj.points.toTypedArray() as Array<Vector3>
|
||||
return ConvexBufferGeometry(vectors)
|
||||
|
@ -7,7 +7,7 @@ import info.laht.threekt.geometries.CylinderBufferGeometry
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.pow
|
||||
|
||||
object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) {
|
||||
public object ThreeCylinderFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) {
|
||||
override fun buildGeometry(obj: ConeSegment): BufferGeometry {
|
||||
val cylinder = obj.detail?.let {
|
||||
val segments = it.toDouble().pow(0.5).toInt()
|
||||
|
@ -18,21 +18,21 @@ import kotlin.reflect.KClass
|
||||
* Builder and updater for three.js object
|
||||
*/
|
||||
@Type(TYPE)
|
||||
interface ThreeFactory<in T : Vision> {
|
||||
public interface ThreeFactory<in T : Vision> {
|
||||
|
||||
val type: KClass<in T>
|
||||
public val type: KClass<in T>
|
||||
|
||||
operator fun invoke(obj: T): Object3D
|
||||
public operator fun invoke(obj: T): Object3D
|
||||
|
||||
companion object {
|
||||
const val TYPE = "threeFactory"
|
||||
public companion object {
|
||||
public const val TYPE: String = "threeFactory"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update position, rotation and visibility
|
||||
*/
|
||||
fun Object3D.updatePosition(obj: Vision) {
|
||||
public fun Object3D.updatePosition(obj: Vision) {
|
||||
visible = obj.visible ?: true
|
||||
if(obj is Solid) {
|
||||
position.set(obj.x, obj.y, obj.z)
|
||||
@ -45,7 +45,7 @@ fun Object3D.updatePosition(obj: Vision) {
|
||||
/**
|
||||
* Update non-position non-geometry property
|
||||
*/
|
||||
fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
||||
public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
||||
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
||||
this.material = getMaterial(source, false)
|
||||
} else if (
|
||||
@ -63,7 +63,7 @@ fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
||||
/**
|
||||
* Generic factory for elements which provide inside geometry builder
|
||||
*/
|
||||
object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) {
|
||||
public object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) {
|
||||
override fun buildGeometry(obj: GeometrySolid): BufferGeometry {
|
||||
return obj.run {
|
||||
ThreeGeometryBuilder().apply { toGeometry(this) }.build()
|
||||
|
@ -12,7 +12,7 @@ import kotlin.reflect.KClass
|
||||
/**
|
||||
*
|
||||
*/
|
||||
object ThreeLabelFactory : ThreeFactory<SolidLabel> {
|
||||
public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
|
||||
override val type: KClass<in SolidLabel> get() = SolidLabel::class
|
||||
|
||||
override fun invoke(obj: SolidLabel): Object3D {
|
||||
|
@ -10,7 +10,7 @@ import info.laht.threekt.math.Color
|
||||
import info.laht.threekt.objects.LineSegments
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
object ThreeLineFactory : ThreeFactory<PolyLine> {
|
||||
public object ThreeLineFactory : ThreeFactory<PolyLine> {
|
||||
override val type: KClass<PolyLine> get() = PolyLine::class
|
||||
|
||||
override fun invoke(obj: PolyLine): Object3D {
|
||||
|
@ -11,7 +11,7 @@ import kotlin.collections.set
|
||||
import kotlin.reflect.KClass
|
||||
import info.laht.threekt.objects.Group as ThreeGroup
|
||||
|
||||
class ThreePlugin : AbstractPlugin() {
|
||||
public class ThreePlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
private val objectFactories = HashMap<KClass<out Solid>, ThreeFactory<*>>()
|
||||
@ -35,9 +35,9 @@ class ThreePlugin : AbstractPlugin() {
|
||||
as ThreeFactory<Solid>?
|
||||
}
|
||||
|
||||
fun buildObject3D(obj: Solid): Object3D {
|
||||
public fun buildObject3D(obj: Solid): Object3D {
|
||||
return when (obj) {
|
||||
is ThreeVision -> obj.toObject3D()
|
||||
is ThreeVision -> obj.render()
|
||||
is Proxy -> proxyFactory(obj)
|
||||
is SolidGroup -> {
|
||||
val group = ThreeGroup()
|
||||
@ -69,17 +69,17 @@ class ThreePlugin : AbstractPlugin() {
|
||||
}
|
||||
}
|
||||
|
||||
obj.onChildrenChange(this) { name, child ->
|
||||
if (name.isEmpty()) {
|
||||
logger.error { "Children change with empty name on $group" }
|
||||
return@onChildrenChange
|
||||
}
|
||||
obj.onChildrenChange(this) { nameToken, child ->
|
||||
// if (name.isEmpty()) {
|
||||
// logger.error { "Children change with empty name on $group" }
|
||||
// return@onChildrenChange
|
||||
// }
|
||||
|
||||
// val parentName = name.cutLast()
|
||||
// val childName = name.last()!!
|
||||
|
||||
//removing old object
|
||||
findChild(name)?.let { oldChild ->
|
||||
findChild(nameToken.asName())?.let { oldChild ->
|
||||
oldChild.parent?.remove(oldChild)
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ class ThreePlugin : AbstractPlugin() {
|
||||
if (child != null && child is Solid) {
|
||||
try {
|
||||
val object3D = buildObject3D(child)
|
||||
set(name, object3D)
|
||||
set(nameToken, object3D)
|
||||
} catch (ex: Throwable) {
|
||||
logger.error(ex) { "Failed to render $child" }
|
||||
}
|
||||
@ -108,10 +108,10 @@ class ThreePlugin : AbstractPlugin() {
|
||||
}
|
||||
}
|
||||
|
||||
companion object : PluginFactory<ThreePlugin> {
|
||||
override val tag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP)
|
||||
override val type = ThreePlugin::class
|
||||
override fun invoke(meta: Meta, context: Context) = ThreePlugin()
|
||||
public companion object : PluginFactory<ThreePlugin> {
|
||||
override val tag: PluginTag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP)
|
||||
override val type: KClass<ThreePlugin> = ThreePlugin::class
|
||||
override fun invoke(meta: Meta, context: Context): ThreePlugin = ThreePlugin()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
|
||||
public class ThreeProxyFactory(public val three: ThreePlugin) : ThreeFactory<Proxy> {
|
||||
private val cache = HashMap<Solid, Object3D>()
|
||||
|
||||
override val type: KClass<Proxy> = Proxy::class
|
||||
|
@ -5,7 +5,7 @@ import hep.dataforge.vision.solid.detail
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.geometries.SphereBufferGeometry
|
||||
|
||||
object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
|
||||
public object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) {
|
||||
override fun buildGeometry(obj: Sphere): BufferGeometry {
|
||||
return obj.detail?.let {detail ->
|
||||
SphereBufferGeometry(
|
||||
|
@ -8,11 +8,6 @@ import kotlinx.serialization.Serializable
|
||||
/**
|
||||
* A custom visual object that has its own Three.js renderer
|
||||
*/
|
||||
public interface ThreeVision : Solid {
|
||||
public fun toObject3D(): Object3D
|
||||
public abstract class ThreeVision : BasicSolid() {
|
||||
public abstract fun render(): Object3D
|
||||
}
|
||||
|
||||
@Serializable
|
||||
public class CustomThreeVision(public val threeFactory: ThreeFactory<Solid>) : BasicSolid(), ThreeVision {
|
||||
override fun toObject3D(): Object3D = threeFactory(this)
|
||||
}
|
Loading…
Reference in New Issue
Block a user