forked from kscience/visionforge
Fix canvas update
This commit is contained in:
parent
fb3ff4d6fc
commit
359eb05f83
@ -20,7 +20,6 @@ kotlin {
|
||||
useCommonJs()
|
||||
browser {
|
||||
commonWebpackConfig {
|
||||
sourceMaps = false
|
||||
cssSupport.enabled = false
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.Page
|
||||
import space.kscience.visionforge.html.embedVisionFragment
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.toVision
|
||||
import space.kscience.visionforge.plotly.asVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
@ -80,7 +80,7 @@ internal class VisionForgePlayGroundForJupyter : JupyterIntegration() {
|
||||
|
||||
render<Plot> { plot ->
|
||||
val fragment = HtmlVisionFragment {
|
||||
vision(plot.toVision())
|
||||
vision(plot.asVision())
|
||||
}
|
||||
|
||||
HTML(produceHtmlVisionString(fragment))
|
||||
|
@ -3,11 +3,13 @@ package ru.mipt.npm.muon.monitor
|
||||
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
||||
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
|
||||
import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.removeAll
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.solid.*
|
||||
import kotlin.math.PI
|
||||
|
||||
class Model {
|
||||
class Model(val manager: VisionManager) {
|
||||
private val map = HashMap<String, SolidGroup>()
|
||||
private val events = HashSet<Event>()
|
||||
|
||||
@ -34,6 +36,7 @@ class Model {
|
||||
var tracks: SolidGroup
|
||||
|
||||
val root: SolidGroup = SolidGroup().apply {
|
||||
root(this@Model.manager)
|
||||
rotationX = PI / 2
|
||||
group("bottom") {
|
||||
Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach {
|
||||
@ -78,7 +81,5 @@ class Model {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun buildGeometry() = Model().root
|
||||
}
|
||||
fun encodeToString(): String = manager.encodeToString(this.root)
|
||||
}
|
@ -7,13 +7,16 @@ import kotlinx.browser.document
|
||||
import react.child
|
||||
import react.dom.render
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.bootstrap.useBootstrap
|
||||
import space.kscience.visionforge.startApplication
|
||||
|
||||
private class MMDemoApp : Application {
|
||||
|
||||
private val model = Model()
|
||||
private val visionManager = Global.fetch(VisionManager)
|
||||
private val model = Model(visionManager)
|
||||
|
||||
private val connection = HttpClient {
|
||||
install(JsonFeature) {
|
||||
|
@ -52,7 +52,7 @@ fun Application.module(context: Context = Global) {
|
||||
}
|
||||
get("/geometry") {
|
||||
call.respondText(
|
||||
solidManager.visionManager.encodeToString(Model.buildGeometry()),
|
||||
Model(solidManager.visionManager).encodeToString(),
|
||||
contentType = ContentType.Application.Json,
|
||||
status = HttpStatusCode.OK
|
||||
)
|
||||
|
@ -15,8 +15,9 @@ group = "ru.mipt.npm"
|
||||
|
||||
dependencies{
|
||||
implementation(project(":visionforge-threejs:visionforge-threejs-server"))
|
||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("ru.mipt.npm.gradle.sat.SatServerKt")
|
||||
mainClass.set("ru.mipt.npm.sat.SatServerKt")
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.invoke
|
||||
import space.kscience.dataforge.names.toName
|
||||
@ -73,7 +76,7 @@ fun VisionLayout<Solid>.showcase() {
|
||||
//override color for this cube
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
launch(Dispatchers.Main) {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !(visible ?: false)
|
||||
@ -82,7 +85,7 @@ fun VisionLayout<Solid>.showcase() {
|
||||
}
|
||||
}
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
launch(Dispatchers.Main) {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
|
@ -19,6 +19,7 @@ import space.kscience.visionforge.VisionLayout
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.three.ThreeCanvas
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
import space.kscience.visionforge.solid.three.configure
|
||||
|
||||
class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
|
||||
private lateinit var navigationElement: HTMLElement
|
||||
@ -70,7 +71,7 @@ class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
|
||||
}
|
||||
}
|
||||
val element = document.getElementById("output-$name") ?: error("Element not found")
|
||||
three.createCanvas(element, canvasOptions)
|
||||
three.getOrCreateCanvas(element).also { it.configure(canvasOptions) }
|
||||
}.render(vision)
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.js")
|
||||
}
|
||||
|
||||
val dataforgeVersion: String by rootProject.extra
|
||||
|
||||
dependencies{
|
||||
api(project(":ui:react"))
|
||||
|
||||
api("subroh0508.net.kotlinmaterialui:core:0.4.5")
|
||||
api("subroh0508.net.kotlinmaterialui:lab:0.4.5")
|
||||
api(npm("@material-ui/core","4.9.14"))
|
||||
api(npm("@material-ui/lab","4.0.0-alpha.51"))
|
||||
//api(npm("@material-ui/icons","4.9.1"))
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package space.kscience.visionforge.material
|
||||
|
||||
//@JsModule("@material-ui/icons/ExpandMore")
|
||||
//external class ExpandMoreIcon : Component<RProps, RState>{
|
||||
// override fun render(): dynamic
|
||||
//}
|
||||
//
|
||||
//@JsModule("@material-ui/icons/ChevronRight")
|
||||
//external class ChevronRightIcon : Component<RProps, RState>{
|
||||
// override fun render(): dynamic
|
||||
//}
|
@ -1,97 +0,0 @@
|
||||
package space.kscience.visionforge.material
|
||||
|
||||
import space.kscience.visionforge.react.component
|
||||
import space.kscience.visionforge.react.state
|
||||
import kotlinx.html.DIV
|
||||
import materialui.components.card.card
|
||||
import materialui.components.cardcontent.cardContent
|
||||
import materialui.components.cardheader.cardHeader
|
||||
import materialui.components.container.container
|
||||
import materialui.components.container.enums.ContainerMaxWidth
|
||||
import materialui.components.expansionpanel.expansionPanel
|
||||
import materialui.components.expansionpaneldetails.expansionPanelDetails
|
||||
import materialui.components.expansionpanelsummary.expansionPanelSummary
|
||||
import materialui.components.grid.GridElementBuilder
|
||||
import materialui.components.grid.enums.GridDirection
|
||||
import materialui.components.grid.enums.GridStyle
|
||||
import materialui.components.grid.grid
|
||||
import materialui.components.paper.paper
|
||||
import materialui.components.typography.typographyH5
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.child
|
||||
import react.dom.RDOMBuilder
|
||||
|
||||
|
||||
fun accordionComponent(elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>) =
|
||||
component<RProps> {
|
||||
val expandedIndex: Int? by state { null }
|
||||
|
||||
container {
|
||||
attrs {
|
||||
maxWidth = ContainerMaxWidth.`false`
|
||||
}
|
||||
elements.forEachIndexed { index, (header, body) ->
|
||||
expansionPanel {
|
||||
attrs {
|
||||
expanded = index == expandedIndex
|
||||
}
|
||||
expansionPanelSummary {
|
||||
typographyH5 {
|
||||
+header
|
||||
}
|
||||
}
|
||||
expansionPanelDetails {
|
||||
this.body()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias RAccordionBuilder = MutableList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>
|
||||
|
||||
fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) {
|
||||
add(title to builder)
|
||||
}
|
||||
|
||||
fun RBuilder.accordion(builder: RAccordionBuilder.() -> Unit) {
|
||||
val list: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>> =
|
||||
ArrayList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>().apply(builder)
|
||||
child(accordionComponent(list))
|
||||
}
|
||||
|
||||
fun RBuilder.materialCard(title: String, block: RBuilder.() -> Unit) {
|
||||
card {
|
||||
cardHeader {
|
||||
attrs {
|
||||
this.title = typographyH5 {
|
||||
+title
|
||||
}
|
||||
}
|
||||
}
|
||||
cardContent {
|
||||
paper {
|
||||
this.block()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RBuilder.column(vararg classMap: Pair<GridStyle, String>, block: GridElementBuilder<DIV>.() -> Unit) =
|
||||
grid(*classMap) {
|
||||
attrs {
|
||||
container = true
|
||||
direction = GridDirection.column
|
||||
}
|
||||
block()
|
||||
}
|
||||
|
||||
fun RBuilder.row(vararg classMap: Pair<GridStyle, String>, block: GridElementBuilder<DIV>.() -> Unit) =
|
||||
grid(*classMap) {
|
||||
attrs {
|
||||
container = true
|
||||
direction = GridDirection.row
|
||||
}
|
||||
block()
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
package space.kscience.visionforge.material
|
||||
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionGroup
|
||||
import space.kscience.visionforge.isEmpty
|
||||
import space.kscience.visionforge.react.component
|
||||
import space.kscience.visionforge.react.state
|
||||
import kotlinx.html.UL
|
||||
import materialui.lab.components.treeItem.treeItem
|
||||
import materialui.lab.components.treeView.SingleSelectTreeViewElementBuilder
|
||||
import materialui.lab.components.treeView.treeView
|
||||
import react.FunctionalComponent
|
||||
import react.RBuilder
|
||||
import react.RProps
|
||||
import react.child
|
||||
import react.dom.span
|
||||
|
||||
interface ObjectTreeProps : RProps {
|
||||
var name: Name
|
||||
var selected: Name?
|
||||
var obj: Vision
|
||||
var clickCallback: (Name?) -> Unit
|
||||
}
|
||||
|
||||
private fun RBuilder.treeBranch(name: Name, obj: Vision): Unit {
|
||||
treeItem {
|
||||
val token = name.last()?.toString() ?: "World"
|
||||
attrs {
|
||||
nodeId = name.toString()
|
||||
label {
|
||||
span {
|
||||
+token
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obj is VisionGroup) {
|
||||
obj.children.entries
|
||||
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
||||
.sortedBy { (it.value as? VisionGroup)?.isEmpty ?: true }
|
||||
.forEach { (childToken, child) ->
|
||||
treeBranch(name + childToken, child)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props ->
|
||||
var selected: String? by state { props.selected.toString() }
|
||||
treeView {
|
||||
this as SingleSelectTreeViewElementBuilder<UL>
|
||||
attrs {
|
||||
this.selected = selected
|
||||
this.onNodeSelect{ _, selectedItem ->
|
||||
selected = selectedItem
|
||||
val itemName = selected?.toName()
|
||||
props.clickCallback(itemName)
|
||||
Unit
|
||||
}
|
||||
defaultCollapseIcon {
|
||||
span{
|
||||
+"-"
|
||||
}
|
||||
//child(ExpandMoreIcon::class) {}
|
||||
}//{<ExpandMoreIcon />}
|
||||
defaultExpandIcon {
|
||||
span{
|
||||
+"+"
|
||||
}
|
||||
//child(ChevronRightIcon::class) {}
|
||||
}//{<ChevronRightIcon />}
|
||||
}
|
||||
treeBranch(props.name, props.obj)
|
||||
}
|
||||
}
|
||||
|
||||
fun RBuilder.objectTree(
|
||||
vision: Vision,
|
||||
selected: Name? = null,
|
||||
clickCallback: (Name?) -> Unit = {}
|
||||
) {
|
||||
child(ObjectTree) {
|
||||
attrs {
|
||||
this.name = Name.EMPTY
|
||||
this.obj = vision
|
||||
this.selected = selected
|
||||
this.clickCallback = clickCallback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
package materialui.components.slider
|
||||
|
||||
import kotlinx.html.DIV
|
||||
import kotlinx.html.Tag
|
||||
import materialui.components.MaterialElementBuilder
|
||||
import materialui.components.getValue
|
||||
import materialui.components.inputbase.enums.InputBaseStyle
|
||||
import materialui.components.setValue
|
||||
import react.RClass
|
||||
import react.ReactElement
|
||||
|
||||
class SliderElementBuilder<Props: SliderProps> internal constructor(
|
||||
type: RClass<Props>,
|
||||
classMap: List<Pair<Enum<*>, String>>
|
||||
) : MaterialElementBuilder<DIV, Props>(type, classMap, { DIV(mapOf(), it) }) {
|
||||
fun Tag.classes(vararg classMap: Pair<InputBaseStyle, String>) {
|
||||
classes(classMap.toList())
|
||||
}
|
||||
var Tag.defaultValue: Number? by materialProps
|
||||
var Tag.disabled: Boolean? by materialProps// = false
|
||||
var Tag.getAriaLabel: String? by materialProps
|
||||
var Tag.getAriaValueText: String? by materialProps
|
||||
var Tag.marks: Array<String>? by materialProps
|
||||
var Tag.max: Number? by materialProps// = 100
|
||||
var Tag.min: Number? by materialProps// = 0,
|
||||
var Tag.name: String? by materialProps
|
||||
var Tag.onChange: ((dynamic, Number) -> Unit)? by materialProps
|
||||
var Tag.onChangeCommitted: ((dynamic, Number) -> Unit)? by materialProps
|
||||
var Tag.orientation: SliderOrientation? by materialProps
|
||||
var Tag.scale: ((Number) -> Number)? by materialProps// {it}
|
||||
var Tag.step: Number? by materialProps// = 1,
|
||||
//ThumbComponent = 'span',
|
||||
var Tag.track: SliderTrack by materialProps
|
||||
var Tag.value: Number? by materialProps
|
||||
var Tag.ValueLabelComponent: ReactElement? by materialProps
|
||||
var Tag.valueLabelDisplay: SliderValueLabelDisplay by materialProps
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package materialui.components.slider
|
||||
|
||||
enum class SliderOrientation {
|
||||
horizontal,
|
||||
vertical
|
||||
}
|
||||
|
||||
enum class SliderTrack{
|
||||
normal,
|
||||
`false`,
|
||||
inverted,
|
||||
}
|
||||
|
||||
enum class SliderValueLabelDisplay{
|
||||
on,
|
||||
auto,
|
||||
off
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package materialui.components.slider
|
||||
|
||||
import materialui.components.StandardProps
|
||||
import materialui.components.input.enums.InputStyle
|
||||
import react.RBuilder
|
||||
import react.RClass
|
||||
import react.ReactElement
|
||||
|
||||
@JsModule("@material-ui/core/Slider")
|
||||
private external val sliderModule: dynamic
|
||||
|
||||
external interface SliderProps : StandardProps {
|
||||
var defaultValue: Number?
|
||||
var disabled: Boolean?// = false
|
||||
var getAriaLabel: String?
|
||||
var getAriaValueText: String?
|
||||
var marks: Array<String>?
|
||||
var max: Number?// = 100
|
||||
var min: Number?// = 0,
|
||||
var name: String?
|
||||
var onChange: ((dynamic, Number) -> Unit)?
|
||||
var onChangeCommitted: ((dynamic, Number) -> Unit)?
|
||||
var orientation: SliderOrientation?
|
||||
var scale: ((Number) -> Number)?// {it}
|
||||
var step: Number? // = 1,
|
||||
//ThumbComponent = 'span',
|
||||
var track: SliderTrack
|
||||
var value: Number?
|
||||
var ValueLabelComponent: ReactElement?
|
||||
var valueLabelDisplay: SliderValueLabelDisplay
|
||||
//valueLabelFormat = Identity,
|
||||
}
|
||||
|
||||
@Suppress("UnsafeCastFromDynamic")
|
||||
private val sliderComponent: RClass<SliderProps> = sliderModule.default
|
||||
|
||||
fun RBuilder.slider(vararg classMap: Pair<InputStyle, String>, block: SliderElementBuilder<SliderProps>.() -> Unit) =
|
||||
child(SliderElementBuilder(sliderComponent, classMap.toList()).apply(block).create())
|
@ -14,6 +14,7 @@ import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import space.kscience.visionforge.solid.three.ThreeCanvas
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
import space.kscience.visionforge.solid.three.configure
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
|
||||
@ -36,12 +37,15 @@ public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functio
|
||||
val elementRef = useRef<Element>(null)
|
||||
var canvas by useState<ThreeCanvas?>(null)
|
||||
|
||||
val three: ThreePlugin = useMemo({props.context.fetch(ThreePlugin)}, arrayOf(props.context))
|
||||
val three: ThreePlugin = useMemo({ props.context.fetch(ThreePlugin) }, arrayOf(props.context))
|
||||
|
||||
useEffect(listOf(props.obj, props.options, elementRef)) {
|
||||
if (canvas == null) {
|
||||
val element = elementRef.current as? HTMLElement ?: error("Canvas element not found")
|
||||
val newCanvas: ThreeCanvas = three.createCanvas(element, props.options ?: Canvas3DOptions.empty())
|
||||
val newCanvas: ThreeCanvas = three.getOrCreateCanvas(element)
|
||||
props.options?.let {
|
||||
newCanvas.configure(it)
|
||||
}
|
||||
props.canvasCallback?.invoke(newCanvas)
|
||||
canvas = newCanvas
|
||||
}
|
||||
|
@ -13,18 +13,25 @@ import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.visionforge.Vision.Companion.TYPE
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
/**
|
||||
* A root type for display hierarchy
|
||||
*/
|
||||
@Type(TYPE)
|
||||
public interface Vision : Described {
|
||||
public interface Vision : Described, CoroutineScope {
|
||||
|
||||
/**
|
||||
* The parent object of this one. If null, this one is a root.
|
||||
*/
|
||||
public var parent: VisionGroup?
|
||||
|
||||
public val manager: VisionManager? get() = parent?.manager
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = manager?.context?.coroutineContext ?: EmptyCoroutineContext
|
||||
|
||||
/**
|
||||
* Get property.
|
||||
* @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false.
|
||||
|
@ -1,7 +1,5 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
@ -30,14 +28,14 @@ internal data class PropertyListener(
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("vision")
|
||||
public open class VisionBase(
|
||||
internal var properties: Config? = null,
|
||||
@Transient override var parent: VisionGroup? = null,
|
||||
@Transient public val coroutineScope: CoroutineScope = GlobalScope,
|
||||
) : Vision {
|
||||
public open class VisionBase : Vision {
|
||||
protected var properties: Config? = null
|
||||
|
||||
@Transient
|
||||
override var parent: VisionGroup? = null
|
||||
|
||||
@Synchronized
|
||||
protected fun getOrCreateConfig(): Config {
|
||||
protected fun getOrCreateProperties(): Config {
|
||||
if (properties == null) {
|
||||
val newProperties = Config()
|
||||
properties = newProperties
|
||||
@ -77,7 +75,7 @@ public open class VisionBase(
|
||||
}
|
||||
|
||||
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
||||
getOrCreateConfig().setItem(name, item)
|
||||
getOrCreateProperties().setItem(name, item)
|
||||
if (notify) {
|
||||
invalidateProperty(name)
|
||||
}
|
||||
@ -104,7 +102,7 @@ public open class VisionBase(
|
||||
get() = propertyInvalidationFlow
|
||||
|
||||
override fun invalidateProperty(propertyName: Name) {
|
||||
coroutineScope.launch {
|
||||
launch {
|
||||
if (propertyName == STYLE_KEY) {
|
||||
updateStyles(styles)
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(s
|
||||
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit =
|
||||
set(token.asName(), child)
|
||||
|
||||
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit = set(key?.toName(), child)
|
||||
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit =
|
||||
set(key?.toName(), child)
|
||||
|
||||
public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null }
|
@ -48,7 +48,7 @@ public open class VisionGroupBase(
|
||||
* Propagate children change event upwards
|
||||
*/
|
||||
private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
|
||||
coroutineScope.launch {
|
||||
launch {
|
||||
_structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after))
|
||||
}
|
||||
}
|
||||
@ -141,4 +141,16 @@ public open class VisionGroupBase(
|
||||
}
|
||||
super.update(change)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-serializable root group used to propagate manager to its children
|
||||
*/
|
||||
internal class RootVisionGroup(override val manager: VisionManager) : VisionGroupBase()
|
||||
|
||||
/**
|
||||
* Designate this [VisionGroup] as a root group and assign a [VisionManager] as its parent
|
||||
*/
|
||||
public fun Vision.root(manager: VisionManager){
|
||||
parent = RootVisionGroup(manager)
|
||||
}
|
@ -14,7 +14,7 @@ public fun TagConsumer<*>.embedVisionFragment(
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> {
|
||||
val visionMap = HashMap<Name, Vision>()
|
||||
val consumer = object : VisionTagConsumer<Any?>(this@embedVisionFragment, idPrefix) {
|
||||
val consumer = object : VisionTagConsumer<Any?>(this@embedVisionFragment, manager, idPrefix) {
|
||||
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
||||
visionMap[name] = vision
|
||||
script {
|
||||
@ -34,13 +34,13 @@ public fun FlowContent.embedVisionFragment(
|
||||
manager: VisionManager,
|
||||
idPrefix: String? = null,
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> = consumer.embedVisionFragment(manager, idPrefix, fragment)
|
||||
): Map<Name, Vision> = consumer.embedVisionFragment(manager, idPrefix, fragment)
|
||||
|
||||
|
||||
internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
|
||||
|
||||
@DFExperimental
|
||||
public fun TagConsumer<*>.embedAndRenderVisionFragment(manager: VisionManager, id: Any, fragment: HtmlVisionFragment){
|
||||
public fun TagConsumer<*>.embedAndRenderVisionFragment(manager: VisionManager, id: Any, fragment: HtmlVisionFragment) {
|
||||
div {
|
||||
div {
|
||||
this.id = id.toString()
|
||||
|
@ -17,7 +17,7 @@ import kotlin.collections.set
|
||||
* A placeholder object to attach inline vision builders.
|
||||
*/
|
||||
@DFExperimental
|
||||
public class VisionOutput @PublishedApi internal constructor() {
|
||||
public class VisionOutput @PublishedApi internal constructor(public val manager: VisionManager) {
|
||||
public var meta: Meta = Meta.EMPTY
|
||||
|
||||
//TODO expose a way to define required plugins.
|
||||
@ -32,6 +32,7 @@ public class VisionOutput @PublishedApi internal constructor() {
|
||||
*/
|
||||
public abstract class VisionTagConsumer<R>(
|
||||
private val root: TagConsumer<R>,
|
||||
public val manager:VisionManager,
|
||||
private val idPrefix: String? = null,
|
||||
) : TagConsumer<R> by root {
|
||||
|
||||
@ -62,7 +63,7 @@ public abstract class VisionTagConsumer<R>(
|
||||
script {
|
||||
attributes["class"] = OUTPUT_META_CLASS
|
||||
unsafe {
|
||||
+VisionManager.defaultJson.encodeToString(MetaSerializer, outputMeta)
|
||||
+manager.jsonFormat.encodeToString(MetaSerializer, outputMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,7 +77,7 @@ public abstract class VisionTagConsumer<R>(
|
||||
name: Name,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
): T {
|
||||
val output = VisionOutput()
|
||||
val output = VisionOutput(manager)
|
||||
val vision = output.visionProvider()
|
||||
return vision(name, vision, output.meta)
|
||||
}
|
||||
|
@ -2,14 +2,13 @@ package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.set
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionBase
|
||||
import space.kscience.visionforge.configure
|
||||
import space.kscience.visionforge.meta
|
||||
import space.kscience.visionforge.*
|
||||
import kotlin.test.Test
|
||||
|
||||
typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit
|
||||
@ -20,7 +19,7 @@ fun FlowContent.renderVisionFragment(
|
||||
fragment: HtmlVisionFragment,
|
||||
): Map<Name, Vision> {
|
||||
val visionMap = HashMap<Name, Vision>()
|
||||
val consumer = object : VisionTagConsumer<Any?>(consumer, idPrefix) {
|
||||
val consumer = object : VisionTagConsumer<Any?>(consumer, Global.fetch(VisionManager), idPrefix) {
|
||||
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
||||
visionMap[name] = vision
|
||||
renderer(name, vision, outputMeta)
|
||||
@ -37,7 +36,7 @@ class HtmlTagTest {
|
||||
fun VisionOutput.base(block: VisionBase.() -> Unit) =
|
||||
VisionBase().apply(block)
|
||||
|
||||
val fragment: HtmlVisionFragment = {
|
||||
val fragment: HtmlVisionFragment = {
|
||||
div {
|
||||
h1 { +"Head" }
|
||||
vision("ddd") {
|
||||
@ -72,7 +71,7 @@ class HtmlTagTest {
|
||||
@Test
|
||||
fun testStringRender() {
|
||||
println(
|
||||
createHTML().div{
|
||||
createHTML().div {
|
||||
renderVisionFragment(simpleVisionRenderer, fragment = fragment)
|
||||
}
|
||||
)
|
||||
|
@ -128,7 +128,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
|
||||
logger.info { "Updating vision data from $wsUrl" }
|
||||
|
||||
val ws = WebSocket(wsUrl.toString()).apply {
|
||||
WebSocket(wsUrl.toString()).apply {
|
||||
onmessage = { messageEvent ->
|
||||
val stringData: String? = messageEvent.data as? String
|
||||
if (stringData != null) {
|
||||
|
@ -17,7 +17,7 @@ public interface ElementVisionRenderer {
|
||||
/**
|
||||
* Display the [vision] inside a given [element] replacing its current content
|
||||
*/
|
||||
public fun render(element: Element, vision: Vision, meta: Meta = Meta.EMPTY): Unit
|
||||
public fun render(element: Element, vision: Vision, meta: Meta = Meta.EMPTY)
|
||||
|
||||
public companion object {
|
||||
public const val TYPE: String = "elementVisionRenderer"
|
||||
|
@ -2,7 +2,7 @@ plugins {
|
||||
id("ru.mipt.npm.gradle.mpp")
|
||||
}
|
||||
|
||||
val plotlyVersion = "0.4.2"
|
||||
val plotlyVersion = "0.4.3"
|
||||
|
||||
kscience {
|
||||
useSerialization()
|
||||
|
@ -1,5 +1,6 @@
|
||||
package space.kscience.visionforge.plotly
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.Config
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
@ -7,13 +8,23 @@ import space.kscience.plotly.Plot
|
||||
import space.kscience.plotly.Plotly
|
||||
import space.kscience.visionforge.VisionBase
|
||||
import space.kscience.visionforge.html.VisionOutput
|
||||
import space.kscience.visionforge.root
|
||||
|
||||
@Serializable
|
||||
public class VisionOfPlotly(private val plotConfig: Config) : VisionBase(plotConfig){
|
||||
public val plot: Plot get() = Plot(plotConfig)
|
||||
@SerialName("vision.plotly")
|
||||
public class VisionOfPlotly private constructor() : VisionBase() {
|
||||
public constructor(plot: Plot) : this() {
|
||||
properties = plot.config
|
||||
}
|
||||
|
||||
public val plot: Plot get() = Plot(properties ?: Config())
|
||||
}
|
||||
|
||||
public fun Plot.toVision(): VisionOfPlotly = VisionOfPlotly(config)
|
||||
public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this)
|
||||
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.plotly(block: Plot.() -> Unit): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block).config)
|
||||
public inline fun VisionOutput.plotly(
|
||||
block: Plot.() -> Unit,
|
||||
): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block)).apply {
|
||||
root(this@plotly.manager)
|
||||
}
|
@ -31,6 +31,8 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
|
||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
|
||||
val config = PlotlyConfig.read(meta)
|
||||
println(plot.config)
|
||||
println(plot.data[0].toMeta())
|
||||
element.plot(plot, config)
|
||||
}
|
||||
|
||||
|
@ -43,12 +43,6 @@ import java.net.URI
|
||||
import kotlin.collections.set
|
||||
import kotlin.time.Duration
|
||||
|
||||
public enum class VisionServerDataMode {
|
||||
EMBED,
|
||||
FETCH,
|
||||
CONNECT
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A ktor plugin container with given [routing]
|
||||
@ -61,7 +55,9 @@ public class VisionServer internal constructor(
|
||||
override val config: Config = Config()
|
||||
public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY)
|
||||
public var cacheFragments: Boolean by config.boolean(true)
|
||||
public var dataMode: VisionServerDataMode = VisionServerDataMode.CONNECT
|
||||
public var dataEmbed: Boolean by config.boolean(true, "data.embed".toName())
|
||||
public var dataFetch: Boolean by config.boolean(false, "data.fetch".toName())
|
||||
public var dataConnect: Boolean by config.boolean(true, "data.connect".toName())
|
||||
|
||||
/**
|
||||
* a list of headers that should be applied to all pages
|
||||
@ -79,25 +75,26 @@ public class VisionServer internal constructor(
|
||||
): Map<Name, Vision> {
|
||||
val visionMap = HashMap<Name, Vision>()
|
||||
|
||||
val consumer = object : VisionTagConsumer<Any?>(consumer) {
|
||||
val consumer = object : VisionTagConsumer<Any?>(consumer, visionManager) {
|
||||
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
||||
visionMap[name] = vision
|
||||
// Toggle update mode
|
||||
when (dataMode) {
|
||||
VisionServerDataMode.EMBED -> {
|
||||
script {
|
||||
attributes["class"] = OUTPUT_DATA_CLASS
|
||||
unsafe {
|
||||
+"\n${visionManager.encodeToString(vision)}\n"
|
||||
}
|
||||
if (dataConnect) {
|
||||
attributes[OUTPUT_CONNECT_ATTRIBUTE] = "auto"
|
||||
}
|
||||
|
||||
if (dataFetch) {
|
||||
attributes[OUTPUT_FETCH_ATTRIBUTE] = "auto"
|
||||
}
|
||||
|
||||
if (dataEmbed) {
|
||||
script {
|
||||
type = "text/json"
|
||||
attributes["class"] = OUTPUT_DATA_CLASS
|
||||
unsafe {
|
||||
+"\n${visionManager.encodeToString(vision)}\n"
|
||||
}
|
||||
}
|
||||
VisionServerDataMode.FETCH -> {
|
||||
attributes[OUTPUT_FETCH_ATTRIBUTE] = "auto"
|
||||
}
|
||||
VisionServerDataMode.CONNECT -> {
|
||||
attributes[OUTPUT_CONNECT_ATTRIBUTE] = "auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,4 +68,5 @@ public class Solids(meta: Meta) : VisionPlugin(meta) {
|
||||
|
||||
@VisionBuilder
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)
|
||||
public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup =
|
||||
SolidGroup().apply(block).apply { root(this@solid.manager) }
|
||||
|
@ -24,9 +24,7 @@ import org.w3c.dom.Node
|
||||
import org.w3c.dom.events.MouseEvent
|
||||
import space.kscience.dataforge.context.info
|
||||
import space.kscience.dataforge.context.logger
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.meta.useProperty
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
@ -41,8 +39,8 @@ import kotlin.math.sin
|
||||
*/
|
||||
public class ThreeCanvas(
|
||||
public val three: ThreePlugin,
|
||||
public val options: Canvas3DOptions,
|
||||
) {
|
||||
public val options = Canvas3DOptions()
|
||||
|
||||
private var boundingBox: Box3? = null
|
||||
private var root: Object3D? = null
|
||||
@ -315,4 +313,11 @@ public class ThreeCanvas(
|
||||
private const val AXES_NAME = "@axes"
|
||||
private const val CLIP_HELPER_NAME = "@clipping"
|
||||
}
|
||||
}
|
||||
|
||||
public fun ThreeCanvas.configure(options: Canvas3DOptions) {
|
||||
this.options.update(options.toMeta())
|
||||
options.onChange(this) { name, _, newItem ->
|
||||
options[name] = newItem
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import space.kscience.dataforge.context.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.update
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.visionforge.ElementVisionRenderer
|
||||
import space.kscience.visionforge.Vision
|
||||
@ -111,12 +112,13 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
public fun createCanvas(
|
||||
private val canvasCache = HashMap<Element, ThreeCanvas>()
|
||||
|
||||
public fun getOrCreateCanvas(
|
||||
element: Element,
|
||||
options: Canvas3DOptions = Canvas3DOptions.empty(),
|
||||
): ThreeCanvas = ThreeCanvas(this, options).apply {
|
||||
): ThreeCanvas = canvasCache.getOrPut(element){ThreeCanvas(this).apply {
|
||||
attach(element)
|
||||
}
|
||||
}}
|
||||
|
||||
override fun content(target: String): Map<Name, Any> {
|
||||
return when (target) {
|
||||
@ -128,11 +130,10 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
override fun rateVision(vision: Vision): Int =
|
||||
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING
|
||||
|
||||
public fun renderSolid(
|
||||
fun renderSolid(
|
||||
element: Element,
|
||||
vision: Solid,
|
||||
options: Canvas3DOptions,
|
||||
): ThreeCanvas = createCanvas(element, options).apply {
|
||||
): ThreeCanvas = getOrCreateCanvas(element).apply {
|
||||
render(vision)
|
||||
}
|
||||
|
||||
@ -140,8 +141,9 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
renderSolid(
|
||||
element,
|
||||
vision as? Solid ?: error("Solid expected but ${vision::class} found"),
|
||||
Canvas3DOptions.read(meta)
|
||||
)
|
||||
).apply {
|
||||
options.update(meta)
|
||||
}
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<ThreePlugin> {
|
||||
@ -154,8 +156,10 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
public fun ThreePlugin.render(
|
||||
element: HTMLElement,
|
||||
obj: Solid,
|
||||
options: Canvas3DOptions.() -> Unit = {},
|
||||
): ThreeCanvas = renderSolid(element, obj, Canvas3DOptions(options))
|
||||
optionsBuilder: Canvas3DOptions.() -> Unit = {},
|
||||
): ThreeCanvas = renderSolid(element, obj).apply {
|
||||
options.apply(optionsBuilder)
|
||||
}
|
||||
|
||||
internal operator fun Object3D.set(token: NameToken, object3D: Object3D) {
|
||||
object3D.name = token.toString()
|
||||
|
Loading…
Reference in New Issue
Block a user