Fixes and bouncing ball demo.

This commit is contained in:
Alexander Nozik 2021-08-15 18:15:17 +03:00
parent f99a359e24
commit af327c17e3
16 changed files with 214 additions and 215 deletions

View File

@ -1,4 +1,8 @@
import kotlinx.browser.document
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.css.*
import react.child
import react.dom.render
@ -17,6 +21,7 @@ import space.kscience.visionforge.solid.*
import space.kscience.visionforge.startApplication
import styled.css
import styled.styledDiv
import kotlin.math.sqrt
import kotlin.random.Random
private class JsPlaygroundApp : Application {
@ -31,9 +36,38 @@ private class JsPlaygroundApp : Application {
val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page")
val bouncingSphere = SolidGroup {
sphere(5.0, "ball") {
detail = 16
color("red")
val h = 100.0
y = h
GlobalScope.launch {
val g = 10.0
val dt = 0.1
var time = 0.0
var velocity = 0.0
while (isActive) {
delay(20)
time += dt
velocity -= g * dt
y = y.toDouble() + velocity * dt
if (y.toDouble() <= 2.5){
velocity = sqrt(2*g*h)
}
}
}
}
box(200, 5, 200, name = "floor"){
y = -2.5
}
}
val visionOfD0 = GdmlShowCase.babyIaxo().toVision()
val random = Random(112233)
val visionOfSpheres = SolidGroup {
repeat(100) {
sphere(5, name = "sphere[$it]") {
@ -56,7 +90,16 @@ private class JsPlaygroundApp : Application {
height = 100.vh
width = 100.vw
}
SmartTabs("D0") {
SmartTabs("gravity") {
Tab("gravity") {
child(ThreeCanvasWithControls) {
attrs {
context = playgroundContext
solid = bouncingSphere
}
}
}
Tab("D0") {
child(ThreeCanvasWithControls) {
attrs {
@ -73,8 +116,8 @@ private class JsPlaygroundApp : Application {
}
}
}
Tab("plotly"){
Plotly{
Tab("plotly") {
Plotly {
attrs {
context = playgroundContext
plot = space.kscience.plotly.Plotly.plot {

View File

@ -66,7 +66,7 @@ public val CanvasControls: FunctionComponent<CanvasControlsProps> = functionalCo
}
}
propertyEditor(
ownProperties = props.canvasOptions,
ownProperties = props.canvasOptions.meta,
allProperties = props.canvasOptions.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
descriptor = Canvas3DOptions.descriptor,
expanded = false

View File

@ -4,10 +4,13 @@ import org.w3c.dom.Element
import react.RBuilder
import react.dom.render
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.*
import space.kscience.visionforge.Vision
import space.kscience.visionforge.computeProperties
import space.kscience.visionforge.getStyle
import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles
public fun RBuilder.visionPropertyEditor(
vision: Vision,
@ -19,7 +22,6 @@ public fun RBuilder.visionPropertyEditor(
propertyEditor(
ownProperties = vision.meta,
allProperties = vision.computeProperties(),
updateFlow = vision.propertyChanges,
descriptor = descriptor,
key = key
)

View File

@ -10,7 +10,6 @@ import react.dom.attrs
import react.dom.option
import react.dom.select
import react.functionalComponent
import react.useState
import space.kscience.dataforge.meta.descriptors.allowedValues
import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.string
@ -18,19 +17,16 @@ import space.kscience.dataforge.values.string
@JsExport
public val MultiSelectChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("MultiSelectChooser") { props ->
var selectedItems by useState { props.item?.value?.list ?: emptyList() }
val onChange: (Event) -> Unit = { event: Event ->
val newSelected = (event.target as HTMLSelectElement).selectedOptions.asList()
.map { (it as HTMLOptionElement).value.asValue() }
props.valueChanged?.invoke(newSelected.asValue())
selectedItems = newSelected
props.meta.value = newSelected.asValue()
}
select {
attrs {
multiple = true
values = selectedItems.mapTo(HashSet()) { it.string }
values = (props.actual.value?.list ?: emptyList()).mapTo(HashSet()) { it.string }
onChangeFunction = onChange
}
props.descriptor?.allowedValues?.forEach { optionValue ->

View File

@ -1,14 +1,5 @@
package space.kscience.visionforge.react
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.css.*
import kotlinx.css.properties.TextDecoration
import kotlinx.html.js.onClickFunction
@ -22,7 +13,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.ValueRequirement
import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import space.kscience.visionforge.hidden
import styled.css
import styled.styledButton
@ -32,17 +22,17 @@ import styled.styledSpan
public external interface PropertyEditorProps : RProps {
/**
* Root config object - always non null
* Root config object - always non-null
*/
public var ownProperties: MutableMetaProvider
public var meta: ObservableMutableMeta
/**
* Provide default item (greyed out if used)
*/
public var allProperties: MetaProvider?
public var withDefault: MetaProvider
/**
* Full path to the displayed node in [ownProperties]. Could be empty
* Full path to the displayed node in [meta]. Could be empty
*/
public var name: Name
@ -51,16 +41,6 @@ public external interface PropertyEditorProps : RProps {
*/
public var descriptor: MetaDescriptor?
/**
* A coroutine scope for updates
*/
public var scope: CoroutineScope?
/**
* Flow names of updated properties
*/
public var updateFlow: Flow<Name>?
/**
* Initial expanded state
*/
@ -68,67 +48,59 @@ public external interface PropertyEditorProps : RProps {
}
private val PropertyEditorItem: FunctionComponent<PropertyEditorProps> =
functionalComponent("ConfigEditorItem") { props ->
functionalComponent("PropertyEditorItem") { props ->
propertyEditorItem(props)
}
private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
var expanded: Boolean by useState { props.expanded ?: true }
val descriptor: MetaDescriptor? = props.descriptor?.get(props.name)
var ownProperty: Meta? by useState { props.ownProperties.getMeta(props.name) }
val actualMeta = props.allProperties?.getMeta(props.name)
val descriptor: MetaDescriptor? = useMemo(props.descriptor, props.name) { props.descriptor?.get(props.name) }
var ownProperty: ObservableMutableMeta by useState { props.meta.getOrCreate(props.name) }
val keys = useMemo(descriptor) {
buildSet {
descriptor?.children?.filterNot {
it.key.startsWith("@") || it.value.hidden
}?.forEach {
add(NameToken(it.key))
}
//ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) }
}
}
val token = props.name.lastOrNull()?.toString() ?: "Properties"
fun update() {
ownProperty = props.ownProperties.getMeta(props.name)
ownProperty = props.meta.getOrCreate(props.name)
}
if (props.updateFlow != null) {
useEffect(props.ownProperties, props.updateFlow) {
val updateJob = props.updateFlow!!.onEach { updatedName ->
if (updatedName == props.name) {
update()
}
}.launchIn(props.scope ?: GlobalScope)
cleanup {
updateJob.cancel()
useEffect(props.meta) {
props.meta.onChange(props) { updatedName ->
if (updatedName == props.name) {
update()
}
}
cleanup {
props.meta.removeListener(props)
}
}
val expanderClick: (Event) -> Unit = {
expanded = !expanded
}
val valueChanged: (Value?) -> Unit = {
if (it == null) {
props.ownProperties.remove(props.name)
} else {
props.ownProperties.setValue(props.name, it)
}
update()
}
val removeClick: (Event) -> Unit = {
props.ownProperties.remove(props.name)
props.meta.remove(props.name)
update()
}
val keys = buildSet {
descriptor?.children?.filterNot {
it.key.startsWith("@") || it.value.hidden
}?.forEach {
add(NameToken(it.key))
}
//ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) }
}
flexRow {
css {
alignItems = Align.center
}
if(keys.isNotEmpty()) {
if (keys.isNotEmpty()) {
styledSpan {
css {
+TreeStyles.treeCaret
@ -144,25 +116,26 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
styledSpan {
css {
+TreeStyles.treeLabel
if (ownProperty == null) {
if (ownProperty.isEmpty()) {
+TreeStyles.treeLabelInactive
}
}
+token
}
if(!props.name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) {
if (!props.name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) {
styledDiv {
css {
//+TreeStyles.resizeableInput
width = 160.px
margin(1.px, 5.px)
}
valueChooser(
props.name,
actualMeta,
descriptor,
valueChanged
)
ValueChooser{
attrs {
this.descriptor = descriptor
this.meta = ownProperty
this.actual = props.withDefault.getMeta(props.name) ?: ownProperty
}
}
}
styledButton {
@ -184,7 +157,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
}
+"\u00D7"
attrs {
if (ownProperty == null) {
if (ownProperty.isEmpty()) {
disabled = true
} else {
onClickFunction = removeClick
@ -206,8 +179,8 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
child(PropertyEditorItem) {
attrs {
this.key = props.name.toString()
this.ownProperties = props.ownProperties
this.allProperties = props.allProperties
this.meta = props.meta
this.withDefault = props.withDefault
this.name = props.name + token
this.descriptor = props.descriptor
}
@ -217,74 +190,52 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
}
}
}
}
@JsExport
public val PropertyEditor: FunctionComponent<PropertyEditorProps> = functionalComponent("PropertyEditor") { props ->
child(PropertyEditorItem) {
attrs {
this.key = ""
this.ownProperties = props.ownProperties
this.allProperties = props.allProperties
this.meta = props.meta
this.withDefault = props.withDefault
this.name = Name.EMPTY
this.descriptor = props.descriptor
this.scope = props.scope
this.expanded = props.expanded
}
}
}
public fun RBuilder.propertyEditor(
ownProperties: MutableMetaProvider,
allProperties: MetaProvider? = ownProperties,
updateFlow: Flow<Name>? = null,
ownProperties: ObservableMutableMeta,
allProperties: MetaProvider = ownProperties,
descriptor: MetaDescriptor? = null,
scope: CoroutineScope? = null,
key: Any? = null,
expanded: Boolean? = null
) {
child(PropertyEditor) {
attrs {
this.ownProperties = ownProperties
this.allProperties = allProperties
this.updateFlow = updateFlow
this.meta = ownProperties
this.withDefault = allProperties
this.descriptor = descriptor
this.key = key?.toString() ?: ""
this.scope = scope
this.expanded = expanded
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun ObservableMutableMeta.flowUpdates(): Flow<Name> = callbackFlow {
onChange(this) { name ->
launch {
send(name)
}
}
awaitClose {
removeListener(this)
}
}
public fun RBuilder.configEditor(
config: ObservableMutableMeta,
default: MetaProvider? = null,
default: MetaProvider = config,
descriptor: MetaDescriptor? = null,
key: Any? = null,
scope: CoroutineScope? = null,
): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key)
): Unit = propertyEditor(config, default, descriptor, key = key)
public fun Element.configEditor(
config: ObservableMutableMeta,
default: Meta = config,
descriptor: MetaDescriptor? = null,
default: Meta? = null,
key: Any? = null,
scope: CoroutineScope? = null,
): Unit = render(this) {
configEditor(config, default, descriptor, key, scope)
configEditor(config, default, descriptor, key = key)
}

View File

@ -10,6 +10,7 @@ import react.FunctionComponent
import react.dom.attrs
import react.functionalComponent
import react.useState
import space.kscience.dataforge.meta.descriptors.ValueRequirement
import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
@ -20,30 +21,32 @@ import styled.styledInput
@JsExport
public val RangeValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("RangeValueChooser") { props ->
var innerValue by useState(props.item.double)
var rangeDisabled: Boolean by useState(props.item == null)
var innerValue by useState(props.actual.double)
var rangeDisabled: Boolean by useState(props.meta.value == null)
val handleDisable: (Event) -> Unit = {
val checkBoxValue = (it.target as HTMLInputElement).checked
rangeDisabled = !checkBoxValue
if(!checkBoxValue) {
props.valueChanged?.invoke(null)
props.meta.value = if(!checkBoxValue) {
null
} else {
props.valueChanged?.invoke(innerValue?.asValue())
innerValue?.asValue()
}
}
val handleChange: (Event) -> Unit = {
val newValue = (it.target as HTMLInputElement).value
props.valueChanged?.invoke(newValue.toDoubleOrNull()?.asValue())
props.meta.value = newValue.toDoubleOrNull()?.asValue()
innerValue = newValue.toDoubleOrNull()
}
flexRow {
styledInput(type = InputType.checkBox) {
attrs {
defaultChecked = rangeDisabled.not()
onChangeFunction = handleDisable
if(props.descriptor?.valueRequirement != ValueRequirement.REQUIRED) {
styledInput(type = InputType.checkBox) {
attrs {
defaultChecked = rangeDisabled.not()
onChangeFunction = handleDisable
}
}
}
@ -55,6 +58,7 @@ public val RangeValueChooser: FunctionComponent<ValueChooserProps> =
disabled = rangeDisabled
value = innerValue?.toString() ?: ""
onChangeFunction = handleChange
consumer.onTagEvent(this, "input", handleChange)
val minValue = props.descriptor?.attributes?.get("min").string
minValue?.let {
min = it

View File

@ -13,14 +13,13 @@ import org.w3c.dom.events.Event
import react.*
import react.dom.attrs
import react.dom.option
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.allowedValues
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.*
import space.kscience.dataforge.values.ValueType
import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.int
import space.kscience.dataforge.values.string
import space.kscience.visionforge.Colors
import space.kscience.visionforge.widgetType
import styled.css
@ -28,23 +27,19 @@ import styled.styledInput
import styled.styledSelect
public external interface ValueChooserProps : RProps {
public var item: Meta?
public var descriptor: MetaDescriptor?
//public var nullable: Boolean?
public var valueChanged: ((Value?) -> Unit)?
public var meta: ObservableMutableMeta
public var actual: Meta
}
@JsExport
public val StringValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("StringValueChooser") { props ->
var value by useState(props.item.string ?: "")
var value by useState(props.actual.string ?: "")
val keyDown: (Event) -> Unit = { event ->
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
value = (event.target as HTMLInputElement).value
if (value != props.item.string) {
props.valueChanged?.invoke(value.asValue())
}
props.meta.value = value.asValue()
}
}
val handleChange: (Event) -> Unit = {
@ -67,7 +62,7 @@ public val BooleanValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("BooleanValueChooser") { props ->
val handleChange: (Event) -> Unit = {
val newValue = (it.target as HTMLInputElement).checked
props.valueChanged?.invoke(newValue.asValue())
props.meta.value = newValue.asValue()
}
styledInput(type = InputType.checkBox) {
css {
@ -75,7 +70,7 @@ public val BooleanValueChooser: FunctionComponent<ValueChooserProps> =
}
attrs {
//this.attributes["indeterminate"] = (props.item == null).toString()
defaultChecked = props.item.boolean ?: false
defaultChecked = props.actual.boolean ?: false
onChangeFunction = handleChange
}
}
@ -84,7 +79,7 @@ public val BooleanValueChooser: FunctionComponent<ValueChooserProps> =
@JsExport
public val NumberValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("NumberValueChooser") { props ->
var innerValue by useState(props.item.string ?: "")
var innerValue by useState(props.actual.string ?: "")
val keyDown: (Event) -> Unit = { event ->
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
innerValue = (event.target as HTMLInputElement).value
@ -92,7 +87,7 @@ public val NumberValueChooser: FunctionComponent<ValueChooserProps> =
if (number == null) {
console.error("The input value $innerValue is not a number")
} else {
props.valueChanged?.invoke(number.asValue())
props.meta.value = number.asValue()
}
}
}
@ -123,10 +118,10 @@ public val NumberValueChooser: FunctionComponent<ValueChooserProps> =
@JsExport
public val ComboValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("ComboValueChooser") { props ->
var selected by useState(props.item.string ?: "")
var selected by useState(props.actual.string ?: "")
val handleChange: (Event) -> Unit = {
selected = (it.target as HTMLSelectElement).value
props.valueChanged?.invoke(selected.asValue())
props.meta.value = selected.asValue()
}
styledSelect {
css {
@ -138,7 +133,7 @@ public val ComboValueChooser: FunctionComponent<ValueChooserProps> =
}
}
attrs {
this.value = props.item?.string ?: ""
this.value = props.actual.string ?: ""
multiple = false
onChangeFunction = handleChange
}
@ -149,14 +144,14 @@ public val ComboValueChooser: FunctionComponent<ValueChooserProps> =
public val ColorValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("ColorValueChooser") { props ->
var value by useState(
props.item?.value?.let { value ->
props.actual.value?.let { value ->
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
else value.string
} ?: "#000000"
)
val handleChange: (Event) -> Unit = {
value = (it.target as HTMLInputElement).value
props.valueChanged?.invoke(value.asValue())
props.meta.value = value.asValue()
}
styledInput(type = InputType.color) {
css {
@ -189,19 +184,3 @@ public val ValueChooser: FunctionComponent<ValueChooserProps> = functionalCompon
else -> child(StringValueChooser, props)
}
}
internal fun RBuilder.valueChooser(
name: Name,
item: Meta?,
descriptor: MetaDescriptor? = null,
callback: (Value?) -> Unit,
) {
child(ValueChooser) {
attrs {
key = name.toString()
this.item = item
this.descriptor = descriptor
this.valueChanged = callback
}
}
}

View File

@ -15,7 +15,6 @@ import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.length
import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.computeProperties
import space.kscience.visionforge.propertyChanges
import space.kscience.visionforge.react.ThreeCanvasComponent
import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.flexRow
@ -137,7 +136,6 @@ public val ThreeCanvasWithControls: FunctionComponent<ThreeCanvasWithControlsPro
propertyEditor(
ownProperties = vision.meta,
allProperties = vision.computeProperties(),
updateFlow = vision.propertyChanges,
descriptor = vision.descriptor,
key = selected
)

View File

@ -8,11 +8,14 @@ import ringui.Island
import ringui.SmartTabs
import ringui.Tab
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.*
import space.kscience.visionforge.Vision
import space.kscience.visionforge.computeProperties
import space.kscience.visionforge.getStyle
import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles
public fun RBuilder.ringPropertyEditor(
vision: Vision,
@ -30,7 +33,6 @@ public fun RBuilder.ringPropertyEditor(
propertyEditor(
ownProperties = vision.meta,
allProperties = vision.computeProperties(),
updateFlow = vision.propertyChanges,
descriptor = descriptor,
key = key
)

View File

@ -72,7 +72,7 @@ internal val CanvasControls: FunctionComponent<CanvasControlsProps> = functional
}
}
propertyEditor(
ownProperties = props.options,
ownProperties = props.options.meta,
allProperties = props.options.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
descriptor = Canvas3DOptions.descriptor,
expanded = false

View File

@ -12,10 +12,12 @@ import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.boolean
import space.kscience.visionforge.Vision.Companion.TYPE
import kotlin.reflect.KProperty1
/**
* A root type for display hierarchy
@ -129,3 +131,17 @@ public var Vision.visible: Boolean?
get() = getPropertyValue(Vision.VISIBLE_KEY)?.boolean
set(value) = meta.setValue(Vision.VISIBLE_KEY, value?.asValue())
public fun <V : Vision, T> V.useProperty(
property: KProperty1<V, T>,
owner: Any? = null,
callBack: V.(T) -> Unit,
) {
//Pass initial value.
callBack(property.get(this))
meta.onChange(owner) { name ->
if (name.startsWith(property.name.asName())) {
callBack(property.get(this@useProperty))
}
}
}

View File

@ -44,7 +44,7 @@ public open class VisionBase(
}
@Transient
private val listeners = HashSet<MetaListener>()
private val listeners: MutableList<MetaListener> = mutableListOf()
private inner class VisionProperties(val pathName: Name) : ObservableMutableMeta {

View File

@ -11,7 +11,6 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBase
import space.kscience.visionforge.setProperty
@Serializable
@SerialName("vision.markup")
@ -20,15 +19,11 @@ public class VisionOfMarkup(
) : VisionBase() {
//FIXME to be removed after https://github.com/Kotlin/kotlinx.serialization/issues/1602 fix
override var properties: MutableMeta? = null
protected override var properties: MutableMeta? = null
//TODO add templates
public var content: String?
get() = meta.getMeta(CONTENT_PROPERTY_KEY).string
set(value) {
setProperty(CONTENT_PROPERTY_KEY, value)
}
public var content: String? by meta.string(CONTENT_PROPERTY_KEY)
public companion object {
public val CONTENT_PROPERTY_KEY: Name = "content".asName()

View File

@ -1,16 +1,19 @@
package space.kscience.visionforge.markup
import kotlinx.browser.document
import kotlinx.dom.clear
import kotlinx.html.dom.append
import kotlinx.serialization.modules.SerializersModule
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
import org.w3c.dom.Element
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.meta.Meta
import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
import space.kscience.visionforge.VisionPlugin
import space.kscience.visionforge.*
import space.kscience.visionforge.markup.VisionOfMarkup.Companion.COMMONMARK_FORMAT
import space.kscience.visionforge.markup.VisionOfMarkup.Companion.GFM_FORMAT
import kotlin.reflect.KClass
public class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
@ -26,8 +29,20 @@ public class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
override fun render(element: Element, vision: Vision, meta: Meta) {
require(vision is VisionOfMarkup) { "The vision is not a markup vision" }
val div = document.createElement("div")
val flavour = when (vision.format) {
COMMONMARK_FORMAT -> CommonMarkFlavourDescriptor()
GFM_FORMAT -> GFMFlavourDescriptor()
//TODO add new formats via plugins
else-> error("Format ${vision.format} not recognized")
}
vision.useProperty(VisionOfMarkup::content) {
div.clear()
div.append {
markdown(flavour) { vision.content ?: "" }
}
}
element.append(div)
TODO()
}
public companion object : PluginFactory<MarkupPlugin> {

View File

@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.scheme
import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.hide
import space.kscience.visionforge.widgetType
@ -21,20 +20,21 @@ public class Clipping : Scheme() {
attributes["min"] = 0.0
attributes["max"] = 1.0
attributes["step"] = 0.01
default(1.0)
}
value(Clipping::y) {
widgetType = "range"
attributes["min"] = 0.0
attributes["max"] = 1.0
attributes["step"] = 0.01
default(1.0)
}
value(Clipping::z) {
widgetType = "range"
attributes["min"] = 0.0
attributes["max"] = 1.0
attributes["step"] = 0.01
default(1.0)
}
}
}

View File

@ -168,42 +168,40 @@ public class ThreeCanvas(
}
//Clipping planes
options.meta.onChange(this@ThreeCanvas) { name->
if (name.startsWith(Canvas3DOptions::clipping.name.asName())) {
val clipping = options.clipping
if (!clipping.meta.isEmpty()) {
renderer.localClippingEnabled = true
boundingBox?.let { boundingBox ->
val xClippingPlane = clipping.x?.let {
val absoluteValue = boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) * it
Plane(Vector3(-1.0, 0.0, 0.0), absoluteValue)
}
val yClippingPlane = clipping.y?.let {
val absoluteValue = boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) * it
Plane(Vector3(0.0, -1.0, 0.0), absoluteValue)
}
val zClippingPlane = clipping.z?.let {
val absoluteValue = boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) * it
Plane(Vector3(0.0, 0.0, -1.0), absoluteValue)
}
renderer.clippingPlanes =
listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray()
options.useProperty(Canvas3DOptions::clipping){clipping ->
if (!clipping.meta.isEmpty()) {
renderer.localClippingEnabled = true
boundingBox?.let { boundingBox ->
val xClippingPlane = clipping.x?.let {
val absoluteValue = boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) * it
Plane(Vector3(-1.0, 0.0, 0.0), absoluteValue)
}
val yClippingPlane = clipping.y?.let {
val absoluteValue = boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) * it
Plane(Vector3(0.0, -1.0, 0.0), absoluteValue)
}
} else {
renderer.localClippingEnabled = false
}
} else if (name.startsWith(Canvas3DOptions::size.name.asName())) {
canvas.style.apply {
minWidth = "${options.size.minWith.toInt()}px"
maxWidth = "${options.size.maxWith.toInt()}px"
minHeight = "${options.size.minHeight.toInt()}px"
maxHeight = "${options.size.maxHeight.toInt()}px"
}
}
val zClippingPlane = clipping.z?.let {
val absoluteValue = boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) * it
Plane(Vector3(0.0, 0.0, -1.0), absoluteValue)
}
renderer.clippingPlanes =
listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray()
}
} else {
renderer.localClippingEnabled = false
}
}
options.useProperty(Canvas3DOptions::size){
canvas.style.apply {
minWidth = "${options.size.minWith.toInt()}px"
maxWidth = "${options.size.maxWith.toInt()}px"
minHeight = "${options.size.minHeight.toInt()}px"
maxHeight = "${options.size.maxHeight.toInt()}px"
}
}
}
/**