v0.2.0-dev-22 #47

Merged
altavir merged 158 commits from dev into master 2021-07-17 11:04:22 +03:00
11 changed files with 131 additions and 172 deletions
Showing only changes of commit 91e7e8573e - Show all commits

View File

@ -20,7 +20,7 @@ allprojects {
} }
group = "space.kscience" group = "space.kscience"
version = "0.2.0-dev-18" version = "0.2.0-dev-19"
} }
subprojects { subprojects {

View File

@ -1,29 +1,20 @@
package space.kscience.visionforge.gdml.demo package space.kscience.visionforge.gdml.demo
import kotlinx.browser.window import kotlinx.browser.window
import kotlinx.css.height
import kotlinx.css.vh
import org.w3c.files.FileReader import org.w3c.files.FileReader
import org.w3c.files.get import org.w3c.files.get
import react.* import react.*
import react.dom.h1
import ringui.grid.ringCol
import ringui.grid.ringGrid
import ringui.grid.ringRow
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.gdml.Gdml import space.kscience.gdml.Gdml
import space.kscience.gdml.decodeFromString import space.kscience.gdml.decodeFromString
import space.kscience.visionforge.bootstrap.nameCrumbs
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.ring.tab
import space.kscience.visionforge.ring.ringThreeControls
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import styled.styledDiv
import styled.css
external interface GDMLAppProps : RProps { external interface GDMLAppProps : RProps {
var context: Context var context: Context
@ -33,19 +24,7 @@ external interface GDMLAppProps : RProps {
@JsExport @JsExport
val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props -> val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
var selected by useState { props.selected }
var vision: Solid? by useState { props.vision } var vision: Solid? by useState { props.vision }
val onSelect: (Name?) -> Unit = {
selected = it
}
val options = useMemo {
Canvas3DOptions.invoke {
this.onSelect = onSelect
}
}
val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager } val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager }
fun loadData(name: String, data: String) { fun loadData(name: String, data: String) {
@ -64,43 +43,16 @@ val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
vision = parsedVision as? Solid ?: error("Parsed vision is not a solid") vision = parsedVision as? Solid ?: error("Parsed vision is not a solid")
} }
styledDiv {
ringGrid { child(ThreeCanvasWithControls) {
ringRow { attrs {
ringCol { this.context = props.context
attrs { this.solid = vision
lg = 9 this.selected = props.selected
} tab("Load") {
flexColumn {
css {
height = 100.vh
}
h1 { +"GDML/JSON loader demo" }
//canvas
child(ThreeCanvasComponent) {
attrs {
this.context = props.context
this.solid = vision
this.selected = selected
this.options = options
}
}
}
}
ringCol {
attrs {
lg = 3
}
flexColumn {
css {
height = 100.vh
}
fileDrop("(drag file here)") { files -> fileDrop("(drag file here)") { files ->
val file = files?.get(0) val file = files?.get(0)
if (file != null) { if (file != null) {
FileReader().apply { FileReader().apply {
onload = { onload = {
val string = result as String val string = result as String
@ -110,8 +62,6 @@ val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
} }
} }
} }
nameCrumbs(selected, "World", onSelect)
ringThreeControls(options, vision, selected, onSelect)
} }
} }
} }

View File

@ -11,7 +11,7 @@ import space.kscience.visionforge.Application
import space.kscience.visionforge.VisionClient import space.kscience.visionforge.VisionClient
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.ThreeWithControls import space.kscience.visionforge.ring.ThreeWithControlsPlugin
import space.kscience.visionforge.startApplication import space.kscience.visionforge.startApplication
import styled.css import styled.css
import styled.styledDiv import styled.styledDiv
@ -21,7 +21,7 @@ private class JsPlaygroundApp : Application {
override fun start(state: Map<String, Any>) { override fun start(state: Map<String, Any>) {
val playgroundContext = Context { val playgroundContext = Context {
plugin(ThreeWithControls) plugin(ThreeWithControlsPlugin)
plugin(VisionClient) plugin(VisionClient)
} }

View File

@ -1,10 +1,10 @@
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.ring.ThreeWithControls import space.kscience.visionforge.ring.ThreeWithControlsPlugin
import space.kscience.visionforge.runVisionClient import space.kscience.visionforge.runVisionClient
@DFExperimental @DFExperimental
fun main() = runVisionClient { fun main() = runVisionClient {
plugin(PlotlyPlugin) plugin(PlotlyPlugin)
plugin(ThreeWithControls) plugin(ThreeWithControlsPlugin)
} }

View File

@ -1,12 +1,12 @@
package space.kscience.visionforge.gdml.jupyter package space.kscience.visionforge.gdml.jupyter
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.ring.ThreeWithControls import space.kscience.visionforge.ring.ThreeWithControlsPlugin
import space.kscience.visionforge.runVisionClient import space.kscience.visionforge.runVisionClient
@DFExperimental @DFExperimental
@JsExport @JsExport
fun main(): Unit = runVisionClient { fun main(): Unit = runVisionClient {
plugin(ThreeWithControls) plugin(ThreeWithControlsPlugin)
} }

View File

@ -1,8 +1,5 @@
package space.kscience.visionforge.react package space.kscience.visionforge.react
import kotlinx.css.margin
import kotlinx.css.padding
import kotlinx.css.px
import kotlinx.html.InputType import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onChangeFunction
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
@ -15,7 +12,6 @@ import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import styled.css
import styled.styledInput import styled.styledInput
@JsExport @JsExport
@ -42,10 +38,6 @@ public val RangeValueChooser: FunctionalComponent<ValueChooserProps> =
flexRow { flexRow {
styledInput(type = InputType.checkBox) { styledInput(type = InputType.checkBox) {
css{
padding(0.px)
margin(0.px)
}
attrs { attrs {
defaultChecked = rangeDisabled.not() defaultChecked = rangeDisabled.not()
onChangeFunction = handleDisable onChangeFunction = handleDisable

View File

@ -1,8 +1,6 @@
package space.kscience.visionforge.react package space.kscience.visionforge.react
import kotlinx.css.FlexBasis import kotlinx.css.*
import kotlinx.css.flexBasis
import kotlinx.css.flexGrow
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import react.* import react.*
@ -39,7 +37,7 @@ public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functio
useEffect(listOf(props.solid, props.options, elementRef)) { useEffect(listOf(props.solid, props.options, elementRef)) {
if (canvas == null) { if (canvas == null) {
val element = elementRef.current as? HTMLElement ?: error("Canvas element not found") val element = elementRef.current as? HTMLElement ?: error("Canvas element not found")
canvas = three.getOrCreateCanvas(element, props.options ?: Canvas3DOptions()) canvas = ThreeCanvas(three, element, props.options ?: Canvas3DOptions())
} }
} }
@ -55,8 +53,10 @@ public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functio
styledDiv { styledDiv {
css { css {
flexGrow = 1.0 maxWidth = 100.vw
flexBasis = FlexBasis.fill maxHeight = 100.vh
display = Display.block
bottom = 0.px
} }
ref = elementRef ref = elementRef
} }

View File

@ -2,9 +2,11 @@ package space.kscience.visionforge.ring
import kotlinx.css.* import kotlinx.css.*
import react.* import react.*
import ringui.grid.RowPosition
import ringui.grid.ringCol import ringui.grid.ringCol
import ringui.grid.ringGrid import ringui.grid.ringGrid
import ringui.grid.ringRow import ringui.grid.ringRow
import ringui.tabs.ringTab
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.ThreeCanvasComponent
@ -17,6 +19,11 @@ public external interface ThreeCanvasWithControlsProps : RProps {
public var context: Context public var context: Context
public var solid: Solid? public var solid: Solid?
public var selected: Name? public var selected: Name?
public var additionalTabs: Map<String, RBuilder.() -> Unit>?
}
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.()->Unit){
additionalTabs = (additionalTabs?: emptyMap()) + (title to block)
} }
@JsExport @JsExport
@ -35,11 +42,15 @@ public val ThreeCanvasWithControls: (props: ThreeCanvasWithControlsProps) -> dyn
styledDiv { styledDiv {
css { css {
height = 100.pct height = 100.pct
width = 100.pct
maxHeight = 100.vh maxHeight = 100.vh
maxWidth = 100.vw maxWidth = 100.vw
} }
ringGrid { ringGrid {
ringRow { ringRow {
attrs {
start = RowPosition.sm
}
ringCol { ringCol {
attrs { attrs {
xs = 12 xs = 12
@ -50,12 +61,11 @@ public val ThreeCanvasWithControls: (props: ThreeCanvasWithControlsProps) -> dyn
child(ThreeCanvasComponent) { child(ThreeCanvasComponent) {
attrs { attrs {
this.context = props.context this.context = props.context
this.solid = props.solid as? Solid this.solid = props.solid
this.selected = selected this.selected = selected
this.options = options this.options = options
} }
} }
} }
ringCol { ringCol {
attrs { attrs {
@ -67,11 +77,14 @@ public val ThreeCanvasWithControls: (props: ThreeCanvasWithControlsProps) -> dyn
styledDiv { styledDiv {
css { css {
padding(top = 4.px) padding(top = 4.px)
width = 100.pct
//border(1.px, BorderStyle.solid, Color.lightGray) //border(1.px, BorderStyle.solid, Color.lightGray)
height = 100.pct
overflowY = Overflow.auto
} }
ringThreeControls(options, props.solid, selected, onSelect) ringThreeControls(options, props.solid, selected, onSelect) {
props.additionalTabs?.forEach { (title, builder) ->
ringTab(title, title, builder)
}
}
} }
} }
} }

View File

@ -15,7 +15,7 @@ import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer { public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
public val three: ThreePlugin by require(ThreePlugin) public val three: ThreePlugin by require(ThreePlugin)
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
@ -27,7 +27,7 @@ public class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer {
react.dom.render(element) { react.dom.render(element) {
child(ThreeCanvasWithControls) { child(ThreeCanvasWithControls) {
attrs { attrs {
this.context = this@ThreeWithControls.context this.context = this@ThreeWithControlsPlugin.context
this.solid = vision as? Solid this.solid = vision as? Solid
} }
} }
@ -41,9 +41,9 @@ public class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer {
} }
} }
public companion object : PluginFactory<ThreeWithControls> { public companion object : PluginFactory<ThreeWithControlsPlugin> {
override val tag: PluginTag = PluginTag("vision.threejs.withControls", PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("vision.threejs.withControls", PluginTag.DATAFORGE_GROUP)
override val type: KClass<ThreeWithControls> = ThreeWithControls::class override val type: KClass<ThreeWithControlsPlugin> = ThreeWithControlsPlugin::class
override fun invoke(meta: Meta, context: Context): ThreeWithControls = ThreeWithControls() override fun invoke(meta: Meta, context: Context): ThreeWithControlsPlugin = ThreeWithControlsPlugin()
} }
} }

View File

@ -45,12 +45,7 @@ public class Clipping : Scheme() {
} }
} }
public class Canvas3DOptions : Scheme() { public class CanvasSize : Scheme() {
public var axes: Axes by spec(Axes)
public var light: Light by spec(Light)
public var camera: Camera by spec(Camera)
public var controls: Controls by spec(Controls)
public var minSize: Int by int(400) public var minSize: Int by int(400)
public var minWith: Number by number { minSize } public var minWith: Number by number { minSize }
public var minHeight: Number by number { minSize } public var minHeight: Number by number { minSize }
@ -59,6 +54,26 @@ public class Canvas3DOptions : Scheme() {
public var maxWith: Number by number { maxSize } public var maxWith: Number by number { maxSize }
public var maxHeight: Number by number { maxSize } public var maxHeight: Number by number { maxSize }
public companion object : SchemeSpec<CanvasSize>(::CanvasSize) {
override val descriptor: NodeDescriptor = NodeDescriptor {
value(CanvasSize::minSize)
value(CanvasSize::minWith)
value(CanvasSize::minHeight)
value(CanvasSize::maxSize)
value(CanvasSize::maxWith)
value(CanvasSize::maxHeight)
}
}
}
public class Canvas3DOptions : Scheme() {
public var axes: Axes by spec(Axes)
public var light: Light by spec(Light)
public var camera: Camera by spec(Camera)
public var controls: Controls by spec(Controls)
public var size: CanvasSize by spec(CanvasSize)
public var layers: List<Number> by numberList(0) public var layers: List<Number> by numberList(0)
public var clipping: Clipping by spec(Clipping) public var clipping: Clipping by spec(Clipping)
@ -71,30 +86,19 @@ public class Canvas3DOptions : Scheme() {
NodeDescriptor { NodeDescriptor {
scheme(Canvas3DOptions::axes, Axes) scheme(Canvas3DOptions::axes, Axes)
scheme(Canvas3DOptions::light, Light) scheme(Canvas3DOptions::light, Light)
scheme(Canvas3DOptions::camera, Camera) { scheme(Canvas3DOptions::camera, Camera) {
hide() hide()
} }
scheme(Canvas3DOptions::controls, Controls) { scheme(Canvas3DOptions::controls, Controls) {
hide() hide()
} }
value(Canvas3DOptions::minSize) {
hide() scheme(Canvas3DOptions::size, CanvasSize) {
}
value(Canvas3DOptions::minWith) {
hide()
}
value(Canvas3DOptions::minHeight) {
hide()
}
value(Canvas3DOptions::maxSize) {
hide()
}
value(Canvas3DOptions::maxWith) {
hide()
}
value(Canvas3DOptions::maxHeight) {
hide() hide()
} }
value(Canvas3DOptions::layers) { value(Canvas3DOptions::layers) {
type(ValueType.NUMBER) type(ValueType.NUMBER)
multiple = true multiple = true
@ -108,8 +112,8 @@ public class Canvas3DOptions : Scheme() {
} }
} }
public fun Canvas3DOptions.computeWidth(external: Number): Int = public fun CanvasSize.computeWidth(external: Number): Int =
(external.toInt()).coerceIn(minWith.toInt()..maxWith.toInt()) (external.toInt()).coerceIn(minWith.toInt()..maxWith.toInt())
public fun Canvas3DOptions.computeHeight(external: Number): Int = public fun CanvasSize.computeHeight(external: Number): Int =
(external.toInt()).coerceIn(minHeight.toInt()..maxHeight.toInt()) (external.toInt()).coerceIn(minHeight.toInt()..maxHeight.toInt())

View File

@ -20,6 +20,7 @@ import info.laht.threekt.objects.Mesh
import info.laht.threekt.scenes.Scene import info.laht.threekt.scenes.Scene
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node import org.w3c.dom.Node
import org.w3c.dom.events.MouseEvent import org.w3c.dom.events.MouseEvent
import space.kscience.dataforge.context.info import space.kscience.dataforge.context.info
@ -40,10 +41,8 @@ import kotlin.math.sin
public class ThreeCanvas( public class ThreeCanvas(
public val three: ThreePlugin, public val three: ThreePlugin,
public val element: Element, public val element: Element,
public val options: Canvas3DOptions = Canvas3DOptions() public val options: Canvas3DOptions = Canvas3DOptions(),
) { ) {
private var boundingBox: Box3? = null private var boundingBox: Box3? = null
private var root: Object3D? = null private var root: Object3D? = null
set(value) { set(value) {
@ -102,40 +101,10 @@ public class ThreeCanvas(
antialias = true antialias = true
}.apply { }.apply {
setClearColor(Colors.skyblue, 1) setClearColor(Colors.skyblue, 1)
//Clipping planes
options.onChange(this@ThreeCanvas) { name, _, _ ->
if (name.startsWith(Canvas3DOptions::clipping.name.asName())) {
localClippingEnabled = true
val clipping = options.clipping
if(!clipping.isEmpty()) {
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)
}
clippingPlanes = listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray()
}
}
} else {
localClippingEnabled = false
}
}
} }
private val canvas = (renderer.domElement as HTMLCanvasElement).apply { private val canvas = (renderer.domElement as HTMLCanvasElement).apply {
className += "three-canvas" className += "three-canvas"
width = 600
height = 600
style.apply { style.apply {
width = "100%" width = "100%"
height = "100%" height = "100%"
@ -143,35 +112,29 @@ public class ThreeCanvas(
} }
} }
/**
* Force camera aspect ration and renderer size recalculation
*/
private fun updateSize() {
val width = element.clientWidth
val height = element.clientHeight
canvas.width = width
canvas.height = height
renderer.setSize(width, height, false)
camera.aspect = width.toDouble() / height.toDouble()
camera.updateProjectionMatrix()
}
/**
* Attach canvas to given [HTMLElement]
*/
init { init {
check(element.getElementsByClassName("three-canvas").length == 0) { check(element.getElementsByClassName("three-canvas").length == 0) {
"Three canvas already created in this element" "Three canvas already created in this element"
} }
element.appendChild(canvas) element.appendChild(canvas)
updateSize() updateSize()
}
/**
* Force camera aspect ration and renderer size recalculation
*/
public fun updateSize() {
val width = canvas.clientWidth
val height = canvas.clientHeight
canvas.style.apply {
minWidth = "${options.minWith.toInt()}px"
maxWidth = "${options.maxWith.toInt()}px"
minHeight = "${options.minHeight.toInt()}px"
maxHeight = "${options.maxHeight.toInt()}px"
}
renderer.setSize(width, height, false)
camera.aspect = width.toDouble() / height.toDouble()
camera.updateProjectionMatrix()
}
/**
* Attach canvas to given [HTMLElement]
*/
init {
canvas.addEventListener("pointerdown", { canvas.addEventListener("pointerdown", {
val picked = pick() val picked = pick()
options.onSelect?.invoke(picked?.fullName()) options.onSelect?.invoke(picked?.fullName())
@ -186,7 +149,7 @@ public class ThreeCanvas(
} }
}, false) }, false)
canvas.onresize = { (element as? HTMLElement)?.onresize = {
updateSize() updateSize()
} }
@ -203,6 +166,43 @@ public class ThreeCanvas(
renderer.render(scene, camera) renderer.render(scene, camera)
} }
//Clipping planes
options.onChange(this@ThreeCanvas) { name, _, _ ->
if (name.startsWith(Canvas3DOptions::clipping.name.asName())) {
val clipping = options.clipping
if (!clipping.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()
}
} 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"
}
}
}
} }
/** /**