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

View File

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

View File

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

@ -111,3 +111,5 @@ public fun Vision.getStyleItems(name: Name): Sequence<MetaItem<*>> {
* Collect all styles for this object in a single laminate * 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 @Serializable
public class VisionChange( public data class VisionChange(
public val reset: Boolean = false, public val reset: 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,

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) 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) visionMap[element]?.update(change)
?: console.info("Target vision for element $element with name $name not found") ?: console.info("Target vision for element $element with name $name not found")
} else { } 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. * 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 = 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) }

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) element.appendChild(canvas)
updateSize() updateSize()
} }
@ -189,8 +190,10 @@ public class ThreeCanvas(
} }
public override fun render(vision: Solid) { public override fun render(vision: Solid) {
//clear old root scene.children.find { it.name == "@root" }?.let {
clear() //Throw error is something is already rendered here
error("Root object already is present in the canvas")
}
val object3D = three.buildObject3D(vision) val object3D = three.buildObject3D(vision)
object3D.name = "@root" 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 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) { override fun render(element: Element, vision: Vision, meta: Meta) {
createCanvas(element, Canvas3DOptions.read(meta)).render( renderSolid(
vision as? Solid ?: error("Solid expected but ${vision::class} is found") 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, element: HTMLElement,
obj: Solid, obj: Solid,
options: Canvas3DOptions.() -> Unit = {}, 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) { internal operator fun Object3D.set(token: NameToken, object3D: Object3D) {
object3D.name = token.toString() object3D.name = token.toString()