Strict mode for Three canvas. Styles builders

This commit is contained in:
Alexander Nozik 2020-12-22 17:48:14 +03:00
parent 216be4a6a1
commit 3c0df98f50
11 changed files with 99 additions and 20 deletions

View File

@ -1,6 +1,9 @@
package ru.mipt.npm.sat
import hep.dataforge.meta.set
import hep.dataforge.vision.solid.*
import hep.dataforge.vision.style
import hep.dataforge.vision.useStyle
import kotlin.math.PI
internal fun visionOfSatellite(
@ -12,7 +15,18 @@ internal fun visionOfSatellite(
ySegmentSize: Number = xSegmentSize,
fiberDiameter: Number = 1.0,
): SolidGroup = SolidGroup {
opacity = 0.3
val transparent by style {
this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3
}
val red by style {
this[SolidMaterial.MATERIAL_COLOR_KEY] = "red"
}
val blue by style {
this[SolidMaterial.MATERIAL_COLOR_KEY] = "blue"
}
val totalXSize = xSegments * xSegmentSize.toDouble()
val totalYSize = ySegments * ySegmentSize.toDouble()
for (layer in 1..layers) {
@ -20,6 +34,7 @@ internal fun visionOfSatellite(
for (i in 1..xSegments) {
for (j in 1..ySegments) {
box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") {
useStyle(transparent)
z = (layer - 0.5) * layerHeight.toDouble()
x = (i - 0.5) * xSegmentSize.toDouble()
y = (j - 0.5) * ySegmentSize.toDouble()
@ -29,23 +44,21 @@ internal fun visionOfSatellite(
group("fibers") {
for (i in 1..xSegments) {
cylinder(fiberDiameter, totalYSize) {
useStyle(red)
rotationX = PI / 2
z = (layer - 1.0) * layerHeight.toDouble() + fiberDiameter.toDouble()
x = (i - 0.5) * xSegmentSize.toDouble()
y = totalYSize/2
color("red")
y = totalYSize / 2
}
}
for (j in 1..ySegments) {
cylinder(fiberDiameter, totalXSize) {
useStyle(blue)
rotationY = PI / 2
z = (layer) * layerHeight.toDouble() - fiberDiameter.toDouble()
y = (j - 0.5) * xSegmentSize.toDouble()
x = totalXSize/2
color("blue")
x = totalXSize / 2
}
}
}

View File

@ -42,9 +42,9 @@ fun main() {
val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName()
val targetVision = sat[target] as Solid
targetVision.color("red")
delay(300)
delay(1000)
targetVision.color.clear()
delay(10)
delay(500)
}
}

View File

@ -37,8 +37,7 @@ public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functio
if (canvas == null) {
val element = elementRef.current as? HTMLElement ?: error("Canvas element not found")
val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
val newCanvas: ThreeCanvas =
three.createCanvas(element, props.options ?: Canvas3DOptions.empty())
val newCanvas: ThreeCanvas = three.createCanvas(element, props.options ?: Canvas3DOptions.empty())
props.canvasCallback?.invoke(newCanvas)
canvas = newCanvas
}

View File

@ -0,0 +1,33 @@
package hep.dataforge.vision
import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import kotlin.properties.ReadOnlyProperty
/**
* A reference to a style defined in a specific container
*/
public class StyleReference(public val owner: VisionGroup, public val name: String)
private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): Boolean = when {
reference.owner === vision -> true
vision.parent == null -> false
else -> styleIsDefined(vision.parent!!, reference)
}
@VisionBuilder
public fun Vision.useStyle(reference: StyleReference) {
//check that style is defined in a parent
//check(styleIsDefined(this, reference)) { "Style reference does not belong to a Vision parent" }
useStyle(reference.name)
}
@DFExperimental
@VisionBuilder
public fun VisionGroup.style(builder: MetaBuilder.() -> Unit): ReadOnlyProperty<Any?, StyleReference> =
ReadOnlyProperty { _, property ->
val styleName = property.name
styleSheet.define(styleName, Meta(builder))
StyleReference(this, styleName)
}

View File

@ -110,4 +110,6 @@ public fun Vision.getStyleItems(name: Name): Sequence<MetaItem<*>> {
/**
* Collect all styles for this object in a single laminate
*/
public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::getStyle))
public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::getStyle))

View File

@ -63,7 +63,7 @@ private fun Vision.isolate(manager: VisionManager): Vision {
}
@Serializable
public class VisionChange(
public data class VisionChange(
public val reset: Boolean = false,
public val vision: Vision? = null,
@Serializable(MetaSerializer::class) public val properties: Meta? = null,

View File

@ -0,0 +1,18 @@
package hep.dataforge.vision
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name
/**
* Property containers are used to create a symmetric behaviors for vision properties and style builders
*/
public interface VisionPropertyContainer<out T> {
public fun getProperty(
name: Name,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): MetaItem<*>?
public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true)
}

View File

@ -138,7 +138,7 @@ public class VisionClient : AbstractPlugin() {
renderVision(element, change.vision, outputMeta)
}
logger.debug { "Got update $change for output with name $name" }
logger.info { "Got update ${change.toString()} for output with name $name" }
visionMap[element]?.update(change)
?: console.info("Target vision for element $element with name $name not found")
} else {

View File

@ -88,6 +88,7 @@ public fun VisionContainerBuilder<Vision>.group(
/**
* Define a group with given [name], attach it to this parent and return it.
*/
@VisionBuilder
public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup =
SolidGroup().apply(action).also { set(name, it) }

View File

@ -130,7 +130,8 @@ public class ThreeCanvas(
}
}
public fun attach(element: Element) {
internal fun attach(element: Element) {
check(element.children.length == 0){"The element for Three canvas is not empty"}
element.appendChild(canvas)
updateSize()
}
@ -189,8 +190,10 @@ public class ThreeCanvas(
}
public override fun render(vision: Solid) {
//clear old root
clear()
scene.children.find { it.name == "@root" }?.let {
//Throw error is something is already rendered here
error("Root object already is present in the canvas")
}
val object3D = three.buildObject3D(vision)
object3D.name = "@root"

View File

@ -136,9 +136,19 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
return if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING
}
public fun renderSolid(
element: Element,
vision: Solid,
options: Canvas3DOptions,
): ThreeCanvas = createCanvas(element, options).apply {
render(vision)
}
override fun render(element: Element, vision: Vision, meta: Meta) {
createCanvas(element, Canvas3DOptions.read(meta)).render(
vision as? Solid ?: error("Solid expected but ${vision::class} is found")
renderSolid(
element,
vision as? Solid ?: error("Solid expected but ${vision::class} is found"),
Canvas3DOptions.read(meta)
)
}
@ -153,7 +163,7 @@ public fun ThreePlugin.render(
element: HTMLElement,
obj: Solid,
options: Canvas3DOptions.() -> Unit = {},
): ThreeCanvas = createCanvas(element, Canvas3DOptions(options)).apply { render(obj) }
): ThreeCanvas = renderSolid(element, obj, Canvas3DOptions(options))
internal operator fun Object3D.set(token: NameToken, object3D: Object3D) {
object3D.name = token.toString()