Working on configuration editor

This commit is contained in:
Alexander Nozik 2019-06-02 20:32:31 +03:00
parent 7aaca8c34f
commit eba5096129
27 changed files with 1385 additions and 185 deletions

View File

@ -1,4 +1,4 @@
val dataforgeVersion by extra("0.1.3-dev-1")
val dataforgeVersion by extra("0.1.3-dev-2")
allprojects {
repositories {

View File

@ -9,6 +9,17 @@ kotlin {
val commonMain by getting {
dependencies {
api("hep.dataforge:dataforge-output:$dataforgeVersion")
api("hep.dataforge:dataforge-output-metadata:$dataforgeVersion")
}
}
val jvmMain by getting{
dependencies {
api("hep.dataforge:dataforge-output-jvm:$dataforgeVersion")
}
}
val jsMain by getting{
dependencies {
api("hep.dataforge:dataforge-output-js:$dataforgeVersion")
}
}
}

View File

@ -0,0 +1,67 @@
package hep.dataforge.vis
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.meta.Styled
internal data class InvalidationListener(
val owner: Any?,
val action: () -> Unit
)
/**
* A [DisplayGroup] containing ordered list of elements
*/
class DisplayObjectList(
override val parent: DisplayObject? = null,
// override val type: String = DisplayObject.DEFAULT_TYPE,
meta: Meta = EmptyMeta
) : DisplayGroup {
private val _children = ArrayList<DisplayObject>()
/**
* An ordered list of direct descendants
*/
val children: List<DisplayObject> get() = _children
override fun iterator(): Iterator<DisplayObject> = children.iterator()
override val properties = Styled(meta)
private val listeners = HashSet<InvalidationListener>()
/**
* Add a child object and notify listeners
*/
fun addChild(obj: DisplayObject) {
_children.add(obj)
listeners.forEach { it.action() }
}
/**
* Remove a specific child and notify listeners
*/
fun removeChild(obj: DisplayObject) {
if (_children.remove(obj)) {
listeners.forEach { it.action }
}
}
/**
* Add listener for children change
* TODO add detailed information into change listener
*/
fun onChildrenChange(owner: Any?, action: () -> Unit) {
listeners.add(InvalidationListener(owner, action))
}
/**
* Remove children change listener
*/
fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
}
}

View File

@ -3,7 +3,6 @@ package hep.dataforge.vis
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE
import hep.dataforge.vis.DisplayObject.Companion.META_KEY
import hep.dataforge.vis.DisplayObject.Companion.TAGS_KEY
@ -17,60 +16,34 @@ interface DisplayObject {
*/
val parent: DisplayObject?
/**
* The type of this object. Uses `.` notation. Empty type means untyped group
*/
val type: String
// /**
// * The type of this object. Uses `.` notation. Empty type means untyped group
// */
// val type: String
val properties: Styled
val properties: MutableMeta<*>
companion object {
const val DEFAULT_TYPE = ""
const val TYPE_KEY = "@type"
const val CHILDREN_KEY = "@children"
//const val TYPE_KEY = "@type"
//const val CHILDREN_KEY = "@children"
const val META_KEY = "@meta"
const val TAGS_KEY = "@tags"
}
}
interface DisplayGroup : DisplayObject {
val children: List<DisplayObject>
/**
* Add a child object and notify listeners
*/
fun addChild(obj: DisplayObject)
/**
* Remove a specific child and notify listeners
*/
fun removeChild(obj: DisplayObject)
/**
* Add listener for children change
* TODO add detailed information into change listener
*/
fun onChildrenChange(owner: Any? = null, action: () -> Unit)
/**
* Remove children change listener
*/
fun removeChildrenChangeListener(owner: Any? = null)
}
/**
* Get the property of this display object of parent's if not found
*/
tailrec operator fun DisplayObject.get(name: Name): MetaItem<*>? = properties[name] ?: parent?.get(name)
tailrec fun DisplayObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name)
operator fun DisplayObject.get(name: String) = get(name.toName())
fun DisplayObject.getProperty(name: String): MetaItem<*>? = getProperty(name.toName())
/**
* A change listener for [DisplayObject] configuration.
*/
fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
properties.style.onChange(owner, action)
properties.onChange(owner, action)
parent?.onChange(owner, action)
}
@ -78,7 +51,7 @@ fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, aft
* Remove all meta listeners with matching owners
*/
fun DisplayObject.removeChangeListener(owner: Any?) {
properties.style.removeListener(owner)
properties.removeListener(owner)
parent?.removeChangeListener(owner)
}
@ -90,61 +63,15 @@ val DisplayObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta
val DisplayObject.tags: List<String> get() = properties[TAGS_KEY].stringList
internal data class ObjectListener(
val owner: Any?,
val action: () -> Unit
)
/**
* Basic group of display objects
*/
open class DisplayNode(
override val parent: DisplayObject? = null,
override val type: String = DEFAULT_TYPE,
meta: Meta = EmptyMeta
) : DisplayGroup {
private val _children = ArrayList<DisplayObject>()
override val children: List<DisplayObject> get() = _children
override val properties = Styled(meta)
private val listeners = HashSet<ObjectListener>()
override fun addChild(obj: DisplayObject) {
// val before = _children[name]
// if (obj == null) {
// _children.remove(name)
// } else {
// _children[name] = obj
///**
// * Basic [DisplayObject] leaf element
// */
//open class DisplayLeaf(
// override val parent: DisplayObject?,
//// override val type: String,
// meta: Meta = EmptyMeta
//) : DisplayObject {
// final override val properties = Styled(meta)
//}
// listeners.forEach { it.action(name, before, obj) }
_children.add(obj)
listeners.forEach { it.action() }
}
override fun removeChild(obj: DisplayObject) {
if (_children.remove(obj)) {
listeners.forEach { it.action }
}
}
override fun onChildrenChange(owner: Any?, action: () -> Unit) {
listeners.add(ObjectListener(owner, action))
}
override fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
}
}
/**
* Basic [DisplayObject] leaf element
*/
open class DisplayLeaf(
override val parent: DisplayObject?,
override val type: String,
meta: Meta = EmptyMeta
) : DisplayObject {
final override val properties = Styled(meta)
}
interface DisplayGroup: DisplayObject, Iterable<DisplayObject>

View File

@ -20,7 +20,7 @@ class DisplayObjectDelegate(
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): MetaItem<*>? {
val name = key ?: property.name.toName()
return if (inherited) {
thisRef[name]
thisRef.getProperty(name)
} else {
thisRef.properties[name]
} ?: default
@ -28,7 +28,7 @@ class DisplayObjectDelegate(
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: MetaItem<*>?) {
val name = key ?: property.name.toName()
thisRef.properties.style[name] = value
thisRef.properties[name] = value
}
}
@ -42,7 +42,7 @@ class DisplayObjectDelegateWrapper<T>(
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T {
val name = key ?: property.name.toName()
return if (inherited) {
read(thisRef[name])
read(thisRef.getProperty(name))
} else {
read(thisRef.properties[name])
} ?: default
@ -50,7 +50,7 @@ class DisplayObjectDelegateWrapper<T>(
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: T) {
val name = key ?: property.name.toName()
thisRef.properties.style.write(name, value)
thisRef.properties[name] = value
}
}

View File

@ -0,0 +1,43 @@
package hep.dataforge.vis
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
/**
* A navigable hierarchical display node
*/
interface DisplayTree : DisplayGroup {
operator fun get(nameToken: NameToken): DisplayObject?
}
interface MutableDisplayTree : DisplayTree {
operator fun set(nameToken: NameToken, group: DisplayObject)
}
/**
* Recursively get a child
*/
tailrec operator fun DisplayTree.get(name: Name): DisplayObject? = when (name.length) {
0 -> this
1 -> this[name[0]]
else -> name.first()?.let { this[it] as? DisplayTree }?.get(name.cutFirst())
}
/**
* Set given object creating intermediate empty groups if needed
* @param name - the full name of a child
* @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter)
*/
fun MutableDisplayTree.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayObject): Unit =
when (name.length) {
0 -> error("Can't set object with empty name")
1 -> set(name[0], objFactory(this))
else -> (this[name.first()!!] ?: DisplayObjectList(this)).run {
if (this is MutableDisplayTree) {
this.set(name.cutFirst(), objFactory)
} else {
error("Can't assign child to a leaf element $this")
}
}
}

View File

@ -1,40 +0,0 @@
package hep.dataforge.vis
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
interface NamedObject : DisplayObject {
val name: String
operator fun get(nameToken: NameToken): DisplayGroup?
operator fun set(nameToken: NameToken, group: DisplayGroup)
}
/**
* Recursively get a child
*/
tailrec operator fun NamedObject.get(name: Name): DisplayObject? = when (name.length) {
0 -> this
1 -> this[name[0]]
else -> name.first()?.let { this[it] as? NamedObject }?.get(name.cutFirst())
}
/**
* Set given object creating intermediate empty groups if needed
* @param name - the full name of a child
* @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter)
*/
fun NamedObject.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayGroup): Unit = when (name.length) {
0 -> error("Can't set object with empty name")
1 -> set(name[0], objFactory(this))
else -> (this[name.first()!!] ?: DisplayNode(this))
.run {
if (this is NamedObject) {
this.set(name.cutFirst(), objFactory)
} else {
error("Can't assign child to a leaf element $this")
}
}
}

View File

@ -0,0 +1,23 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.openjfx.gradle.JavaFXOptions
plugins {
kotlin("jvm")
id("org.openjfx.javafxplugin")
}
dependencies {
api(project(":dataforge-vis-common"))
api("no.tornado:tornadofx:1.7.18")
api("no.tornado:tornadofx-controlsfx:0.1")
}
configure<JavaFXOptions> {
modules("javafx.controls")
}
tasks.withType<KotlinCompile> {
kotlinOptions{
jvmTarget = "1.8"
}
}

View File

@ -0,0 +1,121 @@
package hep.dataforge.vis.fx
import hep.dataforge.context.*
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.meta.boolean
import javafx.application.Application
import javafx.application.Platform
import javafx.collections.FXCollections
import javafx.collections.ObservableSet
import javafx.collections.SetChangeListener
import javafx.scene.Scene
import javafx.scene.image.Image
import javafx.scene.image.ImageView
import javafx.stage.Stage
import tornadofx.*
import kotlin.reflect.KClass
/**
* Plugin holding JavaFX application instance and its root stage
*/
class FXPlugin(meta: Meta = EmptyMeta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag
private val stages: ObservableSet<Stage> = FXCollections.observableSet()
val consoleMode: Boolean by meta.boolean(true)
init {
if (consoleMode) {
stages.addListener(SetChangeListener { change ->
if (change.set.isEmpty()) {
Platform.setImplicitExit(true)
} else {
Platform.setImplicitExit(false)
}
})
}
}
/**
* Wait for application and toolkit to start if needed
*/
override fun attach(context: Context) {
super.attach(context)
if (FX.getApplication(FX.defaultScope) == null) {
if (consoleMode) {
Thread {
context.logger.debug("Starting FX application surrogate")
launch<ApplicationSurrogate>()
}.apply {
name = "${context.name} FX application thread"
start()
}
while (!FX.initialized.get()) {
if (Thread.interrupted()) {
throw RuntimeException("Interrupted application start")
}
}
Platform.setImplicitExit(false)
} else {
throw RuntimeException("FX Application not defined")
}
}
}
/**
* Define an application to use in this context
*/
fun setApp(app: Application, stage: Stage) {
FX.registerApplication(FX.defaultScope, app, stage)
}
/**
* Show something in a pre-constructed stage. Blocks thread until stage is created
*
* @param cons
*/
fun display(action: Stage.() -> Unit) {
runLater {
val stage = Stage()
stage.initOwner(FX.primaryStage)
stage.action()
stage.show()
stages.add(stage)
stage.setOnCloseRequest { stages.remove(stage) }
}
}
fun display(component: UIComponent, width: Double = 800.0, height: Double = 600.0) {
display {
scene = Scene(component.root, width, height)
title = component.title
}
}
companion object : PluginFactory<FXPlugin> {
override val type: KClass<out FXPlugin> = FXPlugin::class
override val tag: PluginTag = PluginTag("vis.fx", group = PluginTag.DATAFORGE_GROUP)
override fun invoke(meta: Meta): FXPlugin = FXPlugin(meta)
}
}
val dfIcon: Image = Image(Global::class.java.getResourceAsStream("/img/df.png"))
val dfIconView = ImageView(dfIcon)
/**
* An application surrogate without any visible primary stage
*/
class ApplicationSurrogate : App() {
override fun start(stage: Stage) {
FX.registerApplication(this, stage)
FX.initialized.value = true
}
}
fun Context.display(width: Double = 800.0, height: Double = 600.0, component: () -> UIComponent) {
plugins.getOrLoad<FXPlugin>().display(component(), width, height)
}

View File

@ -0,0 +1,173 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package hep.dataforge.vis.fx.meta
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.Config
import hep.dataforge.names.NameToken
import hep.dataforge.vis.fx.dfIconView
import hep.dataforge.vis.fx.values.ValueChooser
import javafx.scene.control.*
import javafx.scene.control.cell.TextFieldTreeTableCell
import javafx.scene.layout.Priority
import javafx.scene.paint.Color
import javafx.scene.text.Text
import org.controlsfx.glyphfont.Glyph
import tornadofx.*
/**
* FXML Controller class
*
* @author Alexander Nozik
*/
class ConfigEditor(
val configuration: Config,
title: String = "Configuration editor",
val descriptor: NodeDescriptor? = null
) : Fragment(title = title, icon = dfIconView) {
val filter: (FXMeta) -> Boolean = { cfg ->
when (cfg) {
is FXMetaNode<*> -> !(cfg.descriptor?.tags?.contains(NO_CONFIGURATOR_TAG) ?: false)
is FXMetaValue -> !(cfg.descriptor?.tags?.contains(NO_CONFIGURATOR_TAG) ?: false)
}
}
override val root = borderpane {
center = treetableview<FXMeta> {
root = TreeItem(FXMeta.root(configuration, descriptor))
root.isExpanded = true
sortMode = TreeSortMode.ALL_DESCENDANTS
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
column("Name", FXMeta::name) {
setCellFactory {
object : TextFieldTreeTableCell<FXMeta, NameToken>() {
override fun updateItem(item: NameToken?, empty: Boolean) {
super.updateItem(item, empty)
contextMenu?.items?.removeIf { it.text == "Remove" }
if (!empty) {
if (treeTableRow.item != null) {
textFillProperty().bind(treeTableRow.item.hasValue.objectBinding {
if (it == true) {
Color.BLACK
} else {
Color.GRAY
}
})
if (treeTableRow.treeItem.value.hasValue.get()) {
contextmenu {
item("Remove") {
action {
treeTableRow.item.remove()
}
}
}
}
}
}
}
}
}
}
column("Value") { param: TreeTableColumn.CellDataFeatures<FXMeta, FXMeta> ->
param.value.valueProperty()
}.setCellFactory {
ValueCell()
}
column("Description") { param: TreeTableColumn.CellDataFeatures<FXMeta, String> -> param.value.value.descriptionProperty }
.setCellFactory { param: TreeTableColumn<FXMeta, String> ->
val cell = TreeTableCell<FXMeta, String>()
val text = Text()
cell.graphic = text
cell.prefHeight = Control.USE_COMPUTED_SIZE
text.wrappingWidthProperty().bind(param.widthProperty())
text.textProperty().bind(cell.itemProperty())
cell
}
}
}
private fun showNodeDialog(): String? {
val dialog = TextInputDialog()
dialog.title = "Node name selection"
dialog.contentText = "Enter a name for new node: "
dialog.headerText = null
val result = dialog.showAndWait()
return result.orElse(null)
}
private fun showValueDialog(): String? {
val dialog = TextInputDialog()
dialog.title = "Value name selection"
dialog.contentText = "Enter a name for new value: "
dialog.headerText = null
val result = dialog.showAndWait()
return result.orElse(null)
}
private inner class ValueCell : TreeTableCell<FXMeta, FXMeta?>() {
public override fun updateItem(item: FXMeta?, empty: Boolean) {
if (!empty) {
if (item != null) {
when (item) {
is FXMetaValue -> {
text = null
val chooser = ValueChooser.build(item.valueProperty, item.descriptor) {
item.set(it)
}
graphic = chooser.node
}
is FXMetaNode<*> -> {
item as FXMetaNode<Config>
text = null
graphic = hbox {
button("node", Glyph("FontAwesome", "PLUS_CIRCLE")) {
hgrow = Priority.ALWAYS
maxWidth = Double.POSITIVE_INFINITY
action {
showNodeDialog()?.let {
item.addNode(it)
}
}
}
button("value", Glyph("FontAwesome", "PLUS_SQUARE")) {
hgrow = Priority.ALWAYS
maxWidth = Double.POSITIVE_INFINITY
action {
showValueDialog()?.let {
item.addValue(it)
}
}
}
}
}
}
} else {
text = null
graphic = null
}
} else {
text = null
graphic = null
}
}
}
companion object {
/**
* The tag not to display node or value in configurator
*/
const val NO_CONFIGURATOR_TAG = "nocfg"
}
}

View File

@ -0,0 +1,286 @@
//package hep.dataforge.vis.fx.meta
//
//import hep.dataforge.descriptors.NodeDescriptor
//import hep.dataforge.descriptors.ValueDescriptor
//import hep.dataforge.meta.Config
//import hep.dataforge.meta.Meta
//import hep.dataforge.names.Name
//import hep.dataforge.values.Null
//import hep.dataforge.values.Value
//import javafx.beans.binding.StringBinding
//import javafx.beans.property.SimpleObjectProperty
//import javafx.beans.property.SimpleStringProperty
//import javafx.beans.value.ObservableBooleanValue
//import javafx.beans.value.ObservableStringValue
//import javafx.collections.FXCollections
//import javafx.collections.ObservableList
//import javafx.scene.control.TreeItem
//import tornadofx.*
//
//class ConfigTreeItem(configFX: ConfigFX) : TreeItem<ConfigFX>(configFX) {
// init {
// this.children.bind(value.children) { ConfigTreeItem(it) }
// }
//
// override fun isLeaf(): Boolean = value is ConfigFXValue
//}
//
//
///**
// * A node, containing relative representation of configuration node and description
// * Created by darksnake on 01-May-17.
// */
//sealed class ConfigFX(name: String) {
//
// val nameProperty = SimpleStringProperty(name)
// val name by nameProperty
//
// val parentProperty = SimpleObjectProperty<ConfigFXNode>()
// val parent by parentProperty
//
// abstract val hasValueProperty: ObservableBooleanValue
// //abstract val hasDefaultProperty: ObservableBooleanValue
//
// abstract val descriptionProperty: ObservableStringValue
//
// abstract val children: ObservableList<ConfigFX>
//
// /**
// * remove itself from parent
// */
// abstract fun remove()
//
// abstract fun invalidate()
//}
//
//
///**
// * Tree item for node
// * Created by darksnake on 30-Apr-17.
// */
//open class ConfigFXNode(name: String, parent: ConfigFXNode? = null) : ConfigFX(name) {
//
// final override val hasValueProperty = parentProperty.booleanBinding(nameProperty) {
// it?.config?.hasMeta(this.name) ?: false
// }
//
// /**
// * A descriptor that could be manually set to the node
// */
// val descriptorProperty = SimpleObjectProperty<NodeDescriptor?>()
//
// /**
// * Actual descriptor which holds value inferred from parrent
// */
// private val actualDescriptor = objectBinding(descriptorProperty, parentProperty, nameProperty) {
// value ?: parent?.descriptor?.getNodeDescriptor(name)
// }
//
// val descriptor: NodeDescriptor? by actualDescriptor
//
// val configProperty = SimpleObjectProperty<Config?>()
//
// private val actualConfig = objectBinding(configProperty, parentProperty, nameProperty) {
// value ?: parent?.config?.getMetaList(name)?.firstOrNull()
// }
//
// val config: Config? by actualConfig
//
// final override val descriptionProperty: ObservableStringValue = stringBinding(actualDescriptor) {
// value?.info ?: ""
// }
//
// override val children: ObservableList<ConfigFX> = FXCollections.observableArrayList<ConfigFX>()
//
// init {
// parentProperty.set(parent)
// hasValueProperty.onChange {
// parent?.hasValueProperty?.invalidate()
// }
// invalidate()
// }
//
// /**
// * Get existing configuration node or create and attach new one
// *
// * @return
// */
// private fun getOrBuildNode(): Config {
// return config ?: if (parent == null) {
// throw RuntimeException("The configuration for root node is note defined")
// } else {
// parent.getOrBuildNode().requestNode(name)
// }
// }
//
// fun addValue(name: String) {
// getOrBuildNode().setValue(name, Null)
// }
//
// fun setValue(name: String, value: Value) {
// getOrBuildNode().setValue(name, value)
// }
//
// fun removeValue(valueName: String) {
// config?.removeValue(valueName)
// children.removeIf { it.name == name }
// }
//
// fun addNode(name: String) {
// getOrBuildNode().requestNode(name)
// }
//
// fun removeNode(name: String) {
// config?.removeNode(name)
// }
//
// override fun remove() {
// //FIXME does not work on multinodes
// parent?.removeNode(name)
// invalidate()
// }
//
// final override fun invalidate() {
// actualDescriptor.invalidate()
// actualConfig.invalidate()
// hasValueProperty.invalidate()
//
// val nodeNames = ArrayList<String>()
// val valueNames = ArrayList<String>()
//
// config?.apply {
// nodeNames.addAll(this.nodeNames.toList())
// valueNames.addAll(this.valueNames.toList())
// }
//
// descriptor?.apply {
// nodeNames.addAll(childrenDescriptors().keys)
// valueNames.addAll(valueDescriptors().keys)
// }
//
// //removing old values
// children.removeIf { !(valueNames.contains(it.name) || nodeNames.contains(it.name)) }
//
// valueNames.forEach { name ->
// children.find { it.name == name }?.invalidate().orElse {
// children.add(ConfigFXValue(name, this))
// }
// }
//
// nodeNames.forEach { name ->
// children.find { it.name == name }?.invalidate().orElse {
// children.add(ConfigFXNode(name, this))
// }
// }
// children.sortBy { it.name }
// }
//
// fun updateValue(path: Name, value: Value?) {
// when {
// path.length == 0 -> kotlin.error("Path never could be empty when updating value")
// path.length == 1 -> {
// val hasDescriptor = descriptor?.getValueDescriptor(path) != null
// if (value == null && !hasDescriptor) {
// //removing the value if it is present
// children.removeIf { it.name == path.unescaped }
// } else {
// //invalidating value if it is present
// children.find { it is ConfigFXValue && it.name == path.unescaped }?.invalidate().orElse {
// //adding new node otherwise
// children.add(ConfigFXValue(path.unescaped, this))
// }
// }
// }
// path.length > 1 -> children.filterIsInstance<ConfigFXNode>().find { it.name == path.first.unescaped }?.updateValue(
// path.cutFirst(),
// value
// )
// }
// }
//
// fun updateNode(path: Name, list: List<Meta>) {
// when {
// path.isEmpty() -> invalidate()
// path.length == 1 -> {
// val hasDescriptor = descriptor?.getNodeDescriptor(path.unescaped) != null
// if (list.isEmpty() && !hasDescriptor) {
// children.removeIf { it.name == path.unescaped }
// } else {
// children.find { it is ConfigFXNode && it.name == path.unescaped }?.invalidate().orElse {
// children.add(ConfigFXNode(path.unescaped, this))
// }
// }
// }
// else -> children.filterIsInstance<ConfigFXNode>().find { it.name == path.first.toString() }?.updateNode(
// path.cutFirst(),
// list
// )
// }
// }
//}
//
//class ConfigFXRoot(rootConfig: Config, rootDescriptor: NodeDescriptor? = null) : ConfigFXNode(rootConfig.name),
// ConfigChangeListener {
//
// init {
// configProperty.set(rootConfig)
// descriptorProperty.set(rootDescriptor)
// rootConfig.addListener(this)
// invalidate()
// }
//
// override fun notifyValueChanged(name: Name, oldItem: Value?, newItem: Value?) {
// updateValue(name, newItem)
// }
//
// override fun notifyNodeChanged(nodeName: Name, oldItem: List<Meta>, newItem: List<Meta>) {
// updateNode(nodeName, newItem)
// }
//}
//
//
///**
// * Created by darksnake on 01-May-17.
// */
//class ConfigFXValue(name: String, parent: ConfigFXNode) : ConfigFX(name) {
//
// init {
// parentProperty.set(parent)
// }
//
// override val hasValueProperty = parentProperty.booleanBinding(nameProperty) {
// it?.config?.hasValue(this.name) ?: false
// }
//
//
// override val children: ObservableList<ConfigFX> = FXCollections.emptyObservableList()
//
// val descriptor: ValueDescriptor? = parent.descriptor?.values[name]
//
// override val descriptionProperty: ObservableStringValue = object : StringBinding() {
// override fun computeValue(): String {
// return descriptor?.info ?: ""
// }
// }
//
// val valueProperty = parentProperty.objectBinding(nameProperty) {
// parent.config?.optValue(name).nullable ?: descriptor?.default
// }
//
// var value: Value
// set(value) {
// parent?.setValue(name, value)
// }
// get() = valueProperty.value ?: Value.NULL
//
//
// override fun remove() {
// parent?.removeValue(name)
// invalidate()
// }
//
// override fun invalidate() {
// valueProperty.invalidate()
// hasValueProperty.invalidate()
// }
//}

View File

@ -0,0 +1,119 @@
package hep.dataforge.vis.fx.meta
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.*
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.values.Value
import javafx.beans.binding.ListBinding
import javafx.beans.binding.ObjectBinding
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableBooleanValue
import javafx.beans.value.ObservableStringValue
import javafx.collections.ObservableList
import tornadofx.*
sealed class FXMeta {
abstract val name: NameToken
abstract val parent: FXMetaNode<*>?
abstract val descriptionProperty: ObservableStringValue
abstract val hasValue: ObservableBooleanValue
companion object {
fun <M : MetaNode<M>> root(node: M, descriptor: NodeDescriptor? = null): FXMetaNode<M> =
FXMetaNode(NameToken("root"), null, node, descriptor)
fun root(node: Meta, descriptor: NodeDescriptor? = null): FXMetaNode<SealedMeta> =
root(node.seal(), descriptor)
}
}
class FXMetaNode<M : MetaNode<M>>(
override val name: NameToken,
override val parent: FXMetaNode<M>?,
node: M? = null,
descriptor: NodeDescriptor? = null
) : FXMeta() {
/**
* A descriptor that could be manually set to the node
*/
val descriptorProperty = SimpleObjectProperty(descriptor)
/**
* Actual descriptor which holds value inferred from parrent
*/
private val actualDescriptorProperty = objectBinding(descriptorProperty) {
value ?: parent?.descriptor?.nodes?.get(this@FXMetaNode.name.body)
}
val descriptor: NodeDescriptor? by actualDescriptorProperty
private val innerNodeProperty = SimpleObjectProperty(node)
val nodeProperty: ObjectBinding<M?> = objectBinding(innerNodeProperty) {
value ?: parent?.node?.get(this@FXMetaNode.name.asName()).node
}
val node: M? by nodeProperty
override val descriptionProperty = descriptorProperty.stringBinding { it?.info ?: "" }
override val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null }
val children = object : ListBinding<FXMeta>() {
override fun computeValue(): ObservableList<FXMeta> {
TODO()
}
}
}
class FXMetaValue(
override val name: NameToken,
override val parent: FXMetaNode<*>,
value: Value? = null
) : FXMeta() {
val descriptorProperty = parent.descriptorProperty.objectBinding {
it?.values?.get(name.body)
}
/**
* A descriptor that could be manually set to the node
*/
val descriptor by descriptorProperty
private val innerValueProperty = SimpleObjectProperty(value)
val valueProperty = descriptorProperty.objectBinding { descriptor ->
parent.node[name].value ?: descriptor?.default
}
override val hasValue: ObservableBooleanValue = valueProperty.booleanBinding { it != null }
val value by valueProperty
override val descriptionProperty = descriptorProperty.stringBinding { it?.info ?: "" }
}
fun <M : MutableMeta<M>> FXMetaNode<M>.remove(name: NameToken) {
node?.remove(name.asName())
children.invalidate()
}
fun FXMeta.remove() {
(parent?.node as? MutableMeta<*>)?.remove(name.asName())
}
fun <M : MutableMeta<M>> FXMetaNode<M>.addValue(key: String){
TODO()
}
fun <M : MutableMeta<M>> FXMetaNode<M>.addNode(key: String){
TODO()
}
fun FXMetaValue.set(value: Value?){
TODO()
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2018 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package hep.dataforge.vis.fx.meta
import hep.dataforge.meta.Meta
import hep.dataforge.meta.seal
import hep.dataforge.vis.fx.dfIconView
import javafx.beans.property.SimpleStringProperty
import javafx.scene.control.TreeItem
import javafx.scene.control.TreeSortMode
import javafx.scene.control.TreeTableView
import tornadofx.*
open class MetaViewer(val meta: Meta, title: String = "Meta viewer") : Fragment(title, dfIconView) {
override val root = borderpane {
center {
treetableview<FXMeta> {
isShowRoot = false
root = TreeItem(FXMeta.root(meta.seal()))
populate {
when (val fxMeta = it.value) {
is FXMetaNode<*> -> {
fxMeta.children
}
is FXMetaValue -> null
}
}
root.isExpanded = true
sortMode = TreeSortMode.ALL_DESCENDANTS
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
column("Name", FXMeta::name)
column<FXMeta, String>("Value"){
when(val item = it.value.value){
is FXMetaValue -> item.valueProperty.stringBinding{it?.string?: ""}
is FXMetaNode<*> -> SimpleStringProperty("[node]")
}
}
}
}
}
}

View File

@ -0,0 +1,47 @@
package hep.dataforge.vis.fx.values
import hep.dataforge.meta.Meta
import hep.dataforge.values.Null
import hep.dataforge.values.Value
import hep.dataforge.values.asValue
import javafx.scene.control.ColorPicker
import javafx.scene.paint.Color
import org.slf4j.LoggerFactory
import tornadofx.*
/**
* Created by darksnake on 01-May-17.
*/
class ColorValueChooser : ValueChooserBase<ColorPicker>() {
private fun ColorPicker.setColor(value: Value?) {
if (value != null && value != Null) {
try {
runLater {
this.value = Color.valueOf(value.string)
}
} catch (ex: Exception) {
LoggerFactory.getLogger(javaClass).warn("Invalid color field value: " + value.string)
}
}
}
override fun setDisplayValue(value: Value) {
node.setColor(value)
}
override fun buildNode(): ColorPicker {
val node = ColorPicker()
node.styleClass.add("split-button")
node.maxWidth = java.lang.Double.MAX_VALUE
node.setColor(value)
node.setOnAction { _ -> value = node.value.toString().asValue() }
return node
}
companion object: ValueChooser.Factory{
override val name: String = "color"
override fun invoke(meta: Meta): ValueChooser = ColorValueChooser()
}
}

View File

@ -0,0 +1,58 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package hep.dataforge.vis.fx.values
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.value
import hep.dataforge.values.Value
import hep.dataforge.values.parseValue
import javafx.collections.FXCollections
import javafx.scene.control.ComboBox
import javafx.util.StringConverter
import java.util.*
class ComboBoxValueChooser(val values: Collection<Value>? = null) : ValueChooserBase<ComboBox<Value>>() {
// @Override
// protected void displayError(String error) {
// //TODO ControlsFX decorator here
// }
private fun allowedValues(): Collection<Value> {
return values ?: descriptor?.allowedValues ?: Collections.emptyList();
}
override fun buildNode(): ComboBox<Value> {
val node = ComboBox(FXCollections.observableArrayList(allowedValues()))
node.maxWidth = java.lang.Double.MAX_VALUE
node.isEditable = false
node.selectionModel.select(currentValue())
node.converter = object : StringConverter<Value>() {
override fun toString(value: Value?): String {
return value?.string ?: ""
}
override fun fromString(string: String?): Value {
return (string ?: "").parseValue()
}
}
this.valueProperty.bind(node.valueProperty())
return node
}
override fun setDisplayValue(value: Value) {
node.selectionModel.select(value)
}
companion object : ValueChooser.Factory {
override val name: String = "combo"
override fun invoke(meta: Meta): ValueChooser = ComboBoxValueChooser(meta["values"].value?.list)
}
}

View File

@ -0,0 +1,106 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package hep.dataforge.vis.fx.values
import hep.dataforge.meta.Meta
import hep.dataforge.values.*
import javafx.beans.value.ObservableValue
import javafx.scene.control.TextField
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyEvent
import tornadofx.*
class TextValueChooser : ValueChooserBase<TextField>() {
private val displayText: String
get() = currentValue().let {
if (it.isNull()) {
""
} else {
it.string
}
}
override fun buildNode(): TextField {
val node = TextField()
val defaultValue = currentValue()
node.text = displayText
node.style = String.format("-fx-text-fill: %s;", textColor(defaultValue))
// commit on enter
node.setOnKeyPressed { event: KeyEvent ->
if (event.code == KeyCode.ENTER) {
commit()
}
}
// restoring value on click outside
node.focusedProperty().addListener { _: ObservableValue<out Boolean>, oldValue: Boolean, newValue: Boolean ->
if (oldValue && !newValue) {
node.text = displayText
}
}
// changing text color while editing
node.textProperty().onChange { newValue ->
if (newValue != null) {
val value = newValue.parseValue()
if (!validate(value)) {
node.style = String.format("-fx-text-fill: %s;", "red")
} else {
node.style = String.format("-fx-text-fill: %s;", textColor(value))
}
}
}
return node
}
private fun commit() {
val newValue = node.text.parseValue()
if (validate(newValue)) {
value = newValue
} else {
resetValue()
displayError("Value not allowed")
}
}
private fun textColor(item: Value): String {
return when (item.type) {
ValueType.BOOLEAN -> if (item.boolean) {
"blue"
} else {
"salmon"
}
ValueType.STRING -> "magenta"
else -> "black"
}
}
private fun validate(value: Value): Boolean {
return descriptor?.isAllowedValue(value) ?: true
}
// @Override
// protected void displayError(String error) {
// //TODO ControlsFX decorator here
// }
override fun setDisplayValue(value: Value) {
node.text = if (value.isNull()) {
""
} else {
value.string
}
}
companion object : ValueChooser.Factory {
override val name: String = "text"
override fun invoke(meta: Meta): ValueChooser = TextValueChooser()
}
}

View File

@ -0,0 +1,23 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package hep.dataforge.vis.fx.values
import hep.dataforge.values.Value
/**
* @param success
* @param value Value after change
* @param message Message on unsuccessful change
*/
class ValueCallbackResponse(val success: Boolean, val value: Value, val message: String)
/**
* A callback for some visual object trying to change some value
* @author [Alexander Nozik](mailto:altavir@gmail.com)
*/
typealias ValueCallback = (Value) -> ValueCallbackResponse

View File

@ -0,0 +1,116 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package hep.dataforge.vis.fx.values
import hep.dataforge.context.Context
import hep.dataforge.context.Named
import hep.dataforge.descriptors.ValueDescriptor
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.provider.Type
import hep.dataforge.provider.provideByType
import hep.dataforge.values.Null
import hep.dataforge.values.Value
import javafx.beans.property.ObjectProperty
import javafx.beans.value.ObservableValue
import javafx.scene.Node
import tornadofx.*
/**
* A value chooser object. Must have an empty constructor to be invoked by
* reflections.
*
* @author [Alexander Nozik](mailto:altavir@gmail.com)
*/
interface ValueChooser {
/**
* Get or create a Node that could be later inserted into some parent
* object.
*
* @return
*/
val node: Node
/**
* The descriptor property for this value. Could be null
*
* @return
*/
val descriptorProperty: ObjectProperty<ValueDescriptor?>
var descriptor: ValueDescriptor?
val valueProperty: ObjectProperty<Value?>
var value: Value?
/**
* Set display value but do not notify listeners
*
* @param value
*/
fun setDisplayValue(value: Value)
fun setDisabled(disabled: Boolean) {
//TODO replace by property
}
fun setCallback(callback: ValueCallback)
@Type("hep.dataforge.vis.fx.valueChooserFactory")
interface Factory: Named {
operator fun invoke(meta: Meta = EmptyMeta): ValueChooser
}
companion object {
private fun findWidgetByType(context: Context, type: String): Factory? {
return when(type){
TextValueChooser.name -> TextValueChooser
ColorValueChooser.name -> ColorValueChooser
ComboBoxValueChooser.name -> ComboBoxValueChooser
else-> context.provideByType(type)//Search for additional factories in the plugin
}
}
private fun build(descriptor: ValueDescriptor?): ValueChooser {
return if (descriptor == null) {
TextValueChooser();
} else {
//val types = descriptor.type
val chooser: ValueChooser = when {
descriptor.allowedValues.isNotEmpty() -> ComboBoxValueChooser()
descriptor.tags.contains("widget:color") -> ColorValueChooser()
else -> TextValueChooser()
}
chooser.descriptor = descriptor
chooser
}
}
fun build(
value: ObservableValue<Value?>,
descriptor: ValueDescriptor? = null,
setter: (Value) -> Unit
): ValueChooser {
val chooser = build(descriptor)
chooser.setDisplayValue(value.value ?: Null)
value.onChange {
chooser.setDisplayValue(it ?: Null)
}
chooser.setCallback { result ->
if (descriptor?.isAllowedValue(result) != false) {
setter(result)
ValueCallbackResponse(true, result, "OK")
} else {
ValueCallbackResponse(false, value.value ?: Null, "Not allowed")
}
}
return chooser
}
}
}

View File

@ -0,0 +1,70 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package hep.dataforge.vis.fx.values
import hep.dataforge.descriptors.ValueDescriptor
import hep.dataforge.values.Null
import hep.dataforge.values.Value
import javafx.beans.property.SimpleObjectProperty
import javafx.scene.Node
import org.slf4j.LoggerFactory
import tornadofx.*
/**
* ValueChooser boilerplate
*
* @author Alexander Nozik
*/
abstract class ValueChooserBase<out T : Node> : ValueChooser {
override val node by lazy { buildNode() }
final override val valueProperty = SimpleObjectProperty<Value>(Null)
final override val descriptorProperty = SimpleObjectProperty<ValueDescriptor>()
override var descriptor: ValueDescriptor? by descriptorProperty
override var value: Value? by valueProperty
fun resetValue() {
setDisplayValue(currentValue())
}
/**
* Current value or default value
* @return
*/
protected fun currentValue(): Value {
return value ?: descriptor?.default ?: Null
}
/**
* True if builder node is successful
*
* @return
*/
protected abstract fun buildNode(): T
/**
* Display validation error
*
* @param error
*/
protected fun displayError(error: String) {
LoggerFactory.getLogger(javaClass).error(error)
}
override fun setCallback(callback: ValueCallback) {
valueProperty.onChange { newValue: Value? ->
val response = callback(newValue ?: Null)
if (response.value != valueProperty.get()) {
setDisplayValue(response.value)
}
if (!response.success) {
displayError(response.message)
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -7,6 +7,7 @@ import hep.dataforge.context.PluginTag
import hep.dataforge.meta.Meta
import hep.dataforge.names.toName
import hep.dataforge.vis.spatial.ThreePlugin
import kotlin.reflect.KClass
class GDMLPlugin : AbstractPlugin() {
override val tag: PluginTag get() = GDMLPlugin.tag
@ -30,7 +31,7 @@ class GDMLPlugin : AbstractPlugin() {
companion object : PluginFactory<GDMLPlugin> {
override val tag = PluginTag("vis.gdml", "hep.dataforge")
override val type = GDMLPlugin::class
override val type: KClass<GDMLPlugin> = GDMLPlugin::class
override fun invoke(meta: Meta) = GDMLPlugin()
}
}

View File

@ -2,10 +2,8 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.vis.DisplayGroup
import hep.dataforge.vis.DisplayLeaf
import hep.dataforge.vis.DisplayObject
import hep.dataforge.vis.double
import hep.dataforge.vis.DisplayObjectList
class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
var xSize by double(1.0)
@ -19,5 +17,5 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta)
}
}
fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
fun DisplayObjectList.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
Box(this, meta).apply(action).also { addChild(it) }

View File

@ -2,9 +2,8 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.*
import hep.dataforge.names.toName
import hep.dataforge.vis.DisplayGroup
import hep.dataforge.vis.DisplayLeaf
import hep.dataforge.vis.DisplayObject
import hep.dataforge.vis.DisplayObjectList
class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
@ -21,7 +20,7 @@ class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, met
}
}
fun DisplayGroup.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) =
fun DisplayObjectList.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build(this, meta).also { addChild(it) }
class ConvexBuilder {

View File

@ -2,9 +2,8 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.*
import hep.dataforge.names.toName
import hep.dataforge.vis.DisplayGroup
import hep.dataforge.vis.DisplayLeaf
import hep.dataforge.vis.DisplayObject
import hep.dataforge.vis.DisplayObjectList
class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
@ -22,5 +21,5 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, m
}
fun DisplayGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
fun DisplayObjectList.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
Extruded(this, meta).apply(action).also { addChild(it) }

View File

@ -3,17 +3,17 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.*
import hep.dataforge.output.Output
import hep.dataforge.vis.DisplayGroup
import hep.dataforge.vis.DisplayNode
import hep.dataforge.vis.DisplayObject
import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE
import hep.dataforge.vis.get
import hep.dataforge.vis.DisplayObjectList
import hep.dataforge.vis.getProperty
fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}) =
DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) }
fun DisplayObjectList.group(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit = {}): DisplayGroup =
DisplayObjectList(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) }
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) =
render(DisplayNode(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta)
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit) =
render(DisplayObjectList(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta)
//TODO replace properties by containers?
@ -23,9 +23,9 @@ fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.()
* Visibility property. Inherited from parent
*/
var DisplayObject.visible
get() = this["visible"].boolean ?: true
get() = getProperty("visible").boolean ?: true
set(value) {
properties.style["visible"] = value
properties["visible"] = value
}
// 3D Object position
@ -36,7 +36,7 @@ var DisplayObject.visible
var DisplayObject.x
get() = properties["pos.x"].number ?: 0.0
set(value) {
properties.style["pos.x"] = value
properties["pos.x"] = value
}
/**
@ -45,7 +45,7 @@ var DisplayObject.x
var DisplayObject.y
get() = properties["pos.y"].number ?: 0.0
set(value) {
properties.style["pos.y"] = value
properties["pos.y"] = value
}
/**
@ -54,7 +54,7 @@ var DisplayObject.y
var DisplayObject.z
get() = properties["pos.z"].number ?: 0.0
set(value) {
properties.style["pos.z"] = value
properties["pos.z"] = value
}
// 3D Object rotation
@ -65,7 +65,7 @@ var DisplayObject.z
var DisplayObject.rotationX
get() = properties["rotation.x"].number ?: 0.0
set(value) {
properties.style["rotation.x"] = value
properties["rotation.x"] = value
}
/**
@ -74,7 +74,7 @@ var DisplayObject.rotationX
var DisplayObject.rotationY
get() = properties["rotation.y"].number ?: 0.0
set(value) {
properties.style["rotation.y"] = value
properties["rotation.y"] = value
}
/**
@ -83,7 +83,7 @@ var DisplayObject.rotationY
var DisplayObject.rotationZ
get() = properties["rotation.z"].number ?: 0.0
set(value) {
properties.style["rotation.z"] = value
properties["rotation.z"] = value
}
enum class RotationOrder {
@ -101,7 +101,7 @@ enum class RotationOrder {
var DisplayObject.rotationOrder: RotationOrder
get() = properties["rotation.order"].enum<RotationOrder>() ?: RotationOrder.XYZ
set(value) {
properties.style["rotation.order"] = value
properties["rotation.order"] = value
}
// 3D object scale
@ -112,7 +112,7 @@ var DisplayObject.rotationOrder: RotationOrder
var DisplayObject.scaleX
get() = properties["scale.x"].number ?: 1.0
set(value) {
properties.style["scale.x"] = value
properties["scale.x"] = value
}
/**
@ -121,7 +121,7 @@ var DisplayObject.scaleX
var DisplayObject.scaleY
get() = properties["scale.y"].number ?: 1.0
set(value) {
properties.style["scale.y"] = value
properties["scale.y"] = value
}
/**
@ -130,15 +130,15 @@ var DisplayObject.scaleY
var DisplayObject.scaleZ
get() = properties["scale.z"].number ?: 1.0
set(value) {
properties.style["scale.z"] = value
properties["scale.z"] = value
}
fun DisplayObject.color(rgb: Int) {
this.properties.style["color"] = rgb
this.properties["color"] = rgb
}
fun DisplayObject.color(meta: Meta) {
this.properties.style["color"] = meta
this.properties["color"] = meta
}
fun DisplayObject.color(r: Int, g: Int, b: Int) = color(buildMeta {

View File

@ -1,17 +1,14 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.get
import hep.dataforge.meta.getAll
import hep.dataforge.meta.node
import hep.dataforge.names.toName
import hep.dataforge.vis.DisplayNode
import hep.dataforge.vis.DisplayObjectList
import kotlin.test.Test
import kotlin.test.assertEquals
class ConvexTest {
@Test
fun testConvexBuilder() {
val group = DisplayNode().apply {
val group = DisplayObjectList().apply {
convex {
point(50, 50, -50)
point(50, -50, -50)

View File

@ -22,15 +22,16 @@ rootProject.name = "dataforge-vis"
include(
":dataforge-vis-common",
":dataforge-vis-fx",
":dataforge-vis-spatial",
":dataforge-vis-spatial-fx",
":dataforge-vis-spatial-js"
)
if(file("../dataforge-core").exists()) {
includeBuild("../dataforge-core"){
dependencySubstitution {
substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
}
}
}
//if(file("../dataforge-core").exists()) {
// includeBuild("../dataforge-core"){
// dependencySubstitution {
// substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
// }
// }
//}