Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
212d729afb | |||
3198bad094 | |||
9648533ac8 | |||
7ee40679b9 | |||
f828f86e29 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,3 +7,5 @@ build/
|
||||
data/
|
||||
|
||||
!gradle-wrapper.jar
|
||||
|
||||
/kotlin-js-store/yarn.lock
|
||||
|
@ -2,8 +2,11 @@
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Context receivers flag
|
||||
|
||||
### Changed
|
||||
- Naming of Canvas3D options
|
||||
- Lights are added to the scene instead of 3D options
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
id("org.jetbrains.kotlinx.kover") version "0.5.0-RC"
|
||||
// id("org.jetbrains.kotlinx.kover") version "0.5.0"
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.5.2")
|
||||
@ -8,7 +8,7 @@ val fxVersion by extra("11")
|
||||
|
||||
allprojects{
|
||||
group = "space.kscience"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0-dev-1"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.css.*
|
||||
import react.child
|
||||
import react.dom.render
|
||||
import ringui.SmartTabs
|
||||
import ringui.Tab
|
||||
@ -8,6 +7,7 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.models.Trace
|
||||
import space.kscience.plotly.scatter
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.VisionClient
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||
@ -48,7 +48,7 @@ private class JsPlaygroundApp : Application {
|
||||
}
|
||||
SmartTabs("gravity") {
|
||||
Tab("gravity") {
|
||||
GravityDemo{
|
||||
GravityDemo {
|
||||
attrs {
|
||||
this.context = playgroundContext
|
||||
}
|
||||
@ -73,6 +73,9 @@ private class JsPlaygroundApp : Application {
|
||||
attrs {
|
||||
context = playgroundContext
|
||||
solid {
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
}
|
||||
repeat(100) {
|
||||
sphere(5, name = "sphere[$it]") {
|
||||
x = random.nextDouble(-300.0, 300.0)
|
||||
|
@ -7,6 +7,7 @@ import react.fc
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.layout
|
||||
import space.kscience.plotly.models.Trace
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.markup.VisionOfMarkup
|
||||
import space.kscience.visionforge.react.flexRow
|
||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||
@ -21,10 +22,10 @@ external interface DemoProps : Props {
|
||||
}
|
||||
|
||||
val GravityDemo = fc<DemoProps> { props ->
|
||||
val velocityTrace = Trace{
|
||||
val velocityTrace = Trace {
|
||||
name = "velocity"
|
||||
}
|
||||
val energyTrace = Trace{
|
||||
val energyTrace = Trace {
|
||||
name = "energy"
|
||||
}
|
||||
val markup = VisionOfMarkup()
|
||||
@ -41,6 +42,11 @@ val GravityDemo = fc<DemoProps> { props ->
|
||||
attrs {
|
||||
context = props.context
|
||||
solid {
|
||||
pointLight(200, 200, 200, name = "light"){
|
||||
color(Colors.white)
|
||||
}
|
||||
ambientLight()
|
||||
|
||||
sphere(5.0, "ball") {
|
||||
detail = 16
|
||||
color("red")
|
||||
@ -91,7 +97,7 @@ val GravityDemo = fc<DemoProps> { props ->
|
||||
height = 50.vh - 50.pt
|
||||
}
|
||||
plotly {
|
||||
traces(velocityTrace,energyTrace)
|
||||
traces(velocityTrace, energyTrace)
|
||||
layout {
|
||||
xaxis.title = "time"
|
||||
}
|
||||
|
@ -45,8 +45,9 @@ kotlin {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation("org.apache.commons:commons-math3:3.6.1")
|
||||
implementation(npmlibs.ktor.server.cio)
|
||||
implementation(npmlibs.ktor.serialization)
|
||||
implementation("io.ktor:ktor-server-cio:${ktorVersion}")
|
||||
implementation("io.ktor:ktor-server-content-negotiation:${ktorVersion}")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:${ktorVersion}")
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
|
@ -8,6 +8,12 @@ import space.kscience.visionforge.removeAll
|
||||
import space.kscience.visionforge.setAsRoot
|
||||
import space.kscience.visionforge.setProperty
|
||||
import space.kscience.visionforge.solid.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.set
|
||||
import kotlin.collections.toTypedArray
|
||||
import kotlin.math.PI
|
||||
|
||||
class Model(val manager: VisionManager) {
|
||||
@ -39,7 +45,6 @@ class Model(val manager: VisionManager) {
|
||||
val root: SolidGroup = SolidGroup().apply {
|
||||
setAsRoot(this@Model.manager)
|
||||
material {
|
||||
wireframe
|
||||
color("darkgreen")
|
||||
}
|
||||
rotationX = PI / 2
|
||||
|
@ -17,12 +17,15 @@ import react.fc
|
||||
import react.useMemo
|
||||
import react.useState
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.meta.invoke
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.react.flexColumn
|
||||
import space.kscience.visionforge.react.flexRow
|
||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||
import space.kscience.visionforge.ring.tab
|
||||
import space.kscience.visionforge.solid.specifications.Camera
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import space.kscience.visionforge.solid.three.edges
|
||||
import styled.css
|
||||
@ -42,17 +45,21 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
||||
|
||||
val mmOptions = useMemo {
|
||||
Canvas3DOptions {
|
||||
camera = Camera {
|
||||
camera {
|
||||
distance = 2100.0
|
||||
latitude = PI / 6
|
||||
azimuth = PI + PI / 6
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val root = useMemo(props.model) {
|
||||
props.model.root.apply {
|
||||
edges()
|
||||
ambientLight{
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,22 @@
|
||||
package ru.mipt.npm.muon.monitor.server
|
||||
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
import io.ktor.application.log
|
||||
import io.ktor.features.CallLogging
|
||||
import io.ktor.features.ContentNegotiation
|
||||
import io.ktor.features.DefaultHeaders
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.content.resources
|
||||
import io.ktor.http.content.static
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.routing.get
|
||||
import io.ktor.serialization.json
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.application.log
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.http.content.resources
|
||||
import io.ktor.server.http.content.static
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.get
|
||||
import org.apache.commons.math3.random.JDKRandomGenerator
|
||||
import ru.mipt.npm.muon.monitor.Model
|
||||
import ru.mipt.npm.muon.monitor.sim.Cos2TrackGenerator
|
||||
@ -40,8 +38,6 @@ fun Application.module(context: Context = Global) {
|
||||
environment.log.info("Current directory: $currentDir")
|
||||
val solidManager = context.fetch(Solids)
|
||||
|
||||
install(DefaultHeaders)
|
||||
install(CallLogging)
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ kotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs =
|
||||
freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn" + "-Xlambdas=indy"
|
||||
freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn" + "-Xlambdas=indy" + "-Xcontext-receivers"
|
||||
}
|
||||
}
|
||||
testRuns["test"].executionTask.configure {
|
||||
|
@ -18,7 +18,11 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
|
||||
val meta = Meta {
|
||||
"title" put title
|
||||
}
|
||||
val vision = SolidGroup(block)
|
||||
val vision = SolidGroup(block).apply {
|
||||
ambientLight{
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
render(Name.parse(name), vision, meta)
|
||||
}
|
||||
|
||||
@ -39,6 +43,7 @@ val canvasOptions = Canvas3DOptions {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun VisionLayout<Solid>.showcase() {
|
||||
demo("shapes", "Basic shapes") {
|
||||
ambientLight()
|
||||
box(100.0, 100.0, 100.0) {
|
||||
z = -110.0
|
||||
color("teal")
|
||||
|
@ -59,7 +59,7 @@ box(10, 10, 10, name = "small box"){
|
||||
rotation = Point3D(0, 0, 0)
|
||||
}
|
||||
```
|
||||
![](../docs/images/small-box.png)
|
||||
![](../images/small-box.png)
|
||||
|
||||
The `big box` will have properties with custom values.
|
||||
```kotlin
|
||||
@ -72,7 +72,7 @@ box(40, 40, 40, name = "big box"){
|
||||
rotation = Point3D(60, 80, 0)
|
||||
}
|
||||
```
|
||||
![](../docs/images/big-rotated-box.png)
|
||||
![](../images/big-rotated-box.png)
|
||||
If we compare these boxes, we will see all differences.
|
||||
|
||||
Here is the function `main` with both boxes.
|
||||
@ -111,8 +111,8 @@ fun main(){
|
||||
}
|
||||
}
|
||||
```
|
||||
![](../docs/images/two-boxes-1.png)
|
||||
![](../docs/images/two-boxes-2.png)
|
||||
![](../images/two-boxes-1.png)
|
||||
![](../images/two-boxes-2.png)
|
||||
|
||||
***There is plenty of other properties, especially those, which you can create by yourself. Here we mention just a small part.***
|
||||
|
||||
@ -142,8 +142,8 @@ polyline(Point3D(30, 20, 10), Point3D(30, -100, 30), Point3D(30, -100, 30), Poin
|
||||
}
|
||||
```
|
||||
|
||||
![](../docs/images/polyline-points.png)
|
||||
![](../docs/images/polyline-points-2.png)
|
||||
![](../images/polyline-points.png)
|
||||
![](../images/polyline-points-2.png)
|
||||
|
||||
### 2) Box
|
||||
|
||||
@ -165,7 +165,7 @@ Let's create just usual `box` with equal ribs.
|
||||
color("pink")
|
||||
}
|
||||
```
|
||||
![](../docs/images/box.png)
|
||||
![](../images/box.png)
|
||||
|
||||
Now, let's make `box` with bigger `y` value.
|
||||
```kotlin
|
||||
@ -175,7 +175,7 @@ Now, let's make `box` with bigger `y` value.
|
||||
```
|
||||
As you can see, only the rib of `y-axis` differs from other ribs.
|
||||
|
||||
![](../docs/images/high-box.png)
|
||||
![](../images/high-box.png)
|
||||
|
||||
For a final trial, let's create a `box` with a bigger `x` value.
|
||||
|
||||
@ -189,7 +189,7 @@ For a final trial, let's create a `box` with a bigger `x` value.
|
||||
```
|
||||
Predictably, only the `x-axis` rib is bigger than other ribs.
|
||||
|
||||
![](../docs/images/wide-box.png)
|
||||
![](../images/wide-box.png)
|
||||
|
||||
### 3) Sphere
|
||||
|
||||
@ -206,7 +206,7 @@ As for `radius`, it has `Float` type, and, as you can guess, it sets the radius
|
||||
color("blue")
|
||||
}
|
||||
```
|
||||
![](../docs/images/sphere.png)
|
||||
![](../images/sphere.png)
|
||||
|
||||
### 4) Hexagon
|
||||
|
||||
@ -220,7 +220,7 @@ It is solid which has six edges. It is set by eight values: `node1`,..., `node8`
|
||||
5) Edge with vertices `node1`, `node5`, `node8`, `node4`
|
||||
6) Edge with vertices `node8`, `node5`, `node6`, `node7`
|
||||
|
||||
![](../docs/images/scheme.png)
|
||||
![](../images/scheme.png)
|
||||
|
||||
As the hexagon takes in specific points, we understand that this solid cannot be moved, it is fixed in space, and it can't make pivots.
|
||||
|
||||
@ -239,7 +239,7 @@ Let's make classic parallelepiped.
|
||||
color("green")
|
||||
}
|
||||
```
|
||||
![](../docs/images/classic-hexagon.png)
|
||||
![](../images/classic-hexagon.png)
|
||||
|
||||
Now, let's make a custom hexagon.
|
||||
|
||||
@ -258,7 +258,7 @@ hexagon(
|
||||
color("brown")
|
||||
}
|
||||
```
|
||||
![](../docs/images/custom-hexagon.png)
|
||||
![](../images/custom-hexagon.png)
|
||||
### 3) Cone
|
||||
It takes in six values: `bottomRadius`, `height`, `upperRadius`, `startAngle`, `angle`, and `name`.
|
||||
|
||||
@ -274,8 +274,8 @@ Let's build a classic cone:
|
||||
color("beige")
|
||||
}
|
||||
```
|
||||
![](../docs/images/cone-1.png)
|
||||
![](../docs/images/cone-2.png)
|
||||
![](../images/cone-1.png)
|
||||
![](../images/cone-2.png)
|
||||
|
||||
First of all, we have to try to build a frustum cone:
|
||||
```kotlin
|
||||
@ -283,7 +283,7 @@ cone(60, 80, name = "cone") {
|
||||
color(0u, 40u, 0u)
|
||||
}
|
||||
```
|
||||
![](../docs/images/frustum-cone.png)
|
||||
![](../images/frustum-cone.png)
|
||||
|
||||
Now, we need to make a try to build a cone segment:
|
||||
|
||||
@ -292,8 +292,8 @@ cone(60, 80, angle = PI, name = "cone") {
|
||||
color(0u, 0u, 200u)
|
||||
}
|
||||
```
|
||||
![](../docs/images/cone-segment-1.png)
|
||||
![](../docs/images/cone-segment-2.png)
|
||||
![](../images/cone-segment-1.png)
|
||||
![](../images/cone-segment-2.png)
|
||||
|
||||
Finally, the segment of frustum cone is left for a try:
|
||||
```kotlin
|
||||
@ -301,7 +301,7 @@ cone(60, 100, 20, PI*3/4, angle = PI/3, name = "cone") {
|
||||
color(190u, 0u, 0u)
|
||||
}
|
||||
```
|
||||
![](../docs/images/frustum-cone-segment.png)
|
||||
![](../images/frustum-cone-segment.png)
|
||||
|
||||
### 4) Cone Surface
|
||||
This solid is set by seven values:`bottomOuterRadius`, `bottomInnerRadius`, `height`, `topOuterRadius`, `topInnerRadius`, `startAngle`, and `angle`.
|
||||
@ -318,8 +318,8 @@ Let's build usual cone surface with almost all properties set:
|
||||
rotation = Point3D(2, 50, -9)
|
||||
}
|
||||
```
|
||||
![](../docs/images/cone-surface-1.png)
|
||||
![](../docs/images/cone-surface-2.png)
|
||||
![](../images/cone-surface-1.png)
|
||||
![](../images/cone-surface-2.png)
|
||||
|
||||
Now, let's create a cone surface and set all it's properties:
|
||||
|
||||
@ -329,8 +329,8 @@ coneSurface(30, 25, 10, 10, 8,0f, pi*3/4, name = "cone surface") {
|
||||
rotation = Point3D(2, 50, -9)
|
||||
}
|
||||
```
|
||||
![](../docs/images/cone-surface-fragment.png)
|
||||
![](../docs/images/cone-surface-fragment-2.png)
|
||||
![](../images/cone-surface-fragment.png)
|
||||
![](../images/cone-surface-fragment-2.png)
|
||||
|
||||
### 5) Cylinder
|
||||
|
||||
@ -344,8 +344,8 @@ cylinder(40, 100, "cylinder"){
|
||||
color("indigo")
|
||||
}
|
||||
```
|
||||
![](../docs/images/cylinder-1.png)
|
||||
![](../docs/images/cylinder-2.png)
|
||||
![](../images/cylinder-1.png)
|
||||
![](../images/cylinder-2.png)
|
||||
### 6) Tube
|
||||
|
||||
`tube` takes in `radius`, `height`, `innerRadius`, `startAngle`, `angle`, and `name`. *All values are familiar from `cone`, and `coneSurface` solids.*
|
||||
@ -356,7 +356,7 @@ tube(50, 40, 20, name = "usual tube"){
|
||||
opacity = 0.4
|
||||
}
|
||||
```
|
||||
![](../docs/images/tube.png)
|
||||
![](../images/tube.png)
|
||||
|
||||
This is an example of tube fragment:
|
||||
|
||||
@ -365,7 +365,7 @@ tube(50, 40, 20, 0f, PI, name = "fragmented tube"){
|
||||
color("white")
|
||||
}
|
||||
```
|
||||
![](../docs/images/tube-fragment.png)
|
||||
![](../images/tube-fragment.png)
|
||||
### 7) Extruded
|
||||
|
||||
`extruded` is set by two values: `shape`, and `layer`.
|
@ -1,12 +1,8 @@
|
||||
kotlin.code.style=official
|
||||
kotlin.mpp.stability.nowarn=true
|
||||
|
||||
kotlin.jupyter.add.scanner=false
|
||||
|
||||
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G
|
||||
org.gradle.parallel=true
|
||||
org.gradle.jvmargs=-Xmx4G
|
||||
|
||||
publishing.github=false
|
||||
publishing.sonatype=false
|
||||
|
||||
toolsVersion=0.11.1-kotlin-1.6.10
|
||||
toolsVersion=0.11.4-kotlin-1.6.20
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,7 @@ public external interface TabPaneProps : PropsWithChildren {
|
||||
public val TabPane: FC<TabPaneProps> = fc("TabPane") { props ->
|
||||
var activeTab: String? by useState(props.activeTab)
|
||||
|
||||
val children: Array<out ReactElement?> = Children.map(props.children) {
|
||||
val children: Array<out ReactElement<*>?> = Children.map(props.children) {
|
||||
it.asElementOrNull()
|
||||
} ?: emptyArray()
|
||||
|
||||
|
@ -5,7 +5,7 @@ plugins {
|
||||
val dataforgeVersion: String by rootProject.extra
|
||||
|
||||
kotlin{
|
||||
js{
|
||||
js(IR){
|
||||
useCommonJs()
|
||||
browser {
|
||||
commonWebpackConfig {
|
||||
|
@ -38,6 +38,10 @@ public fun ThreeCanvasWithControlsProps.solid(block: SolidGroup.() -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
public fun ThreeCanvasWithControlsProps.options(block: Canvas3DOptions.() -> Unit){
|
||||
options = Canvas3DOptions(block)
|
||||
}
|
||||
|
||||
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.() -> Unit) {
|
||||
additionalTabs = (additionalTabs ?: emptyMap()) + (title to block)
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
|
||||
|
||||
/**
|
||||
* Render a fragment in the given consumer and return a map of extracted visions
|
||||
* @param manager a VisionManager used for serialization
|
||||
* @param context a context used to create a vision fragment
|
||||
* @param embedData embed Vision initial state in the HTML
|
||||
* @param fetchDataUrl fetch data after first render from given url
|
||||
* @param fetchUpdatesUrl receive push updates from the server at given url
|
||||
|
@ -0,0 +1,96 @@
|
||||
package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.isEmpty
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.setAsRoot
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
/**
|
||||
* Rendering context for visions in HTML
|
||||
*/
|
||||
public interface HtmlVisionContext : ContextAware {
|
||||
|
||||
/**
|
||||
* Generate div id for vision div tag
|
||||
*/
|
||||
public fun generateId(name: Name): String = "vision[$name]"
|
||||
|
||||
/**
|
||||
* Render vision at given [DIV]
|
||||
*/
|
||||
public fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta)
|
||||
}
|
||||
|
||||
|
||||
public typealias HtmlVisionContextFragment = context(HtmlVisionContext) TagConsumer<*>.() -> Unit
|
||||
|
||||
context(HtmlVisionContext)
|
||||
public fun HtmlVisionContextFragment(content: TagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
||||
|
||||
context(HtmlVisionContext)
|
||||
private fun <T> TagConsumer<T>.vision(
|
||||
visionManager: VisionManager,
|
||||
name: Name,
|
||||
vision: Vision,
|
||||
outputMeta: Meta = Meta.EMPTY,
|
||||
): T = div {
|
||||
id = generateId(name)
|
||||
classes = setOf(VisionTagConsumer.OUTPUT_CLASS)
|
||||
vision.setAsRoot(visionManager)
|
||||
attributes[VisionTagConsumer.OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
||||
if (!outputMeta.isEmpty()) {
|
||||
//Hard-code output configuration
|
||||
script {
|
||||
attributes["class"] = VisionTagConsumer.OUTPUT_META_CLASS
|
||||
unsafe {
|
||||
+visionManager.jsonFormat.encodeToString(MetaSerializer, outputMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
renderVision(name, vision, outputMeta)
|
||||
}
|
||||
|
||||
context(HtmlVisionContext)
|
||||
private fun <T> TagConsumer<T>.vision(
|
||||
name: Name,
|
||||
vision: Vision,
|
||||
outputMeta: Meta = Meta.EMPTY,
|
||||
): T = vision(context.visionManager, name, vision, outputMeta)
|
||||
|
||||
/**
|
||||
* Insert a vision in this HTML.
|
||||
*/
|
||||
context(HtmlVisionContext)
|
||||
@DFExperimental
|
||||
@VisionDSL
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
name: Name? = null,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
): T {
|
||||
val output = VisionOutput(context, name)
|
||||
val vision = output.visionProvider()
|
||||
val actualName =
|
||||
name ?: NameToken(VisionTagConsumer.DEFAULT_VISION_NAME, vision.hashCode().toUInt().toString()).asName()
|
||||
return vision(output.buildVisionManager(), actualName, vision, output.meta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a vision in this HTML.
|
||||
*/
|
||||
context(HtmlVisionContext)
|
||||
@DFExperimental
|
||||
@VisionDSL
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
name: String?,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
): T = vision(name?.parseAsName(), visionProvider)
|
@ -17,7 +17,7 @@ import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sin
|
||||
import space.kscience.visionforge.solid.specifications.Camera as CameraSpec
|
||||
import space.kscience.visionforge.solid.specifications.CameraScheme as CameraSpec
|
||||
|
||||
public class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: CameraSpec) {
|
||||
|
||||
|
@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js{
|
||||
js(IR){
|
||||
binaries.library()
|
||||
}
|
||||
sourceSets {
|
||||
|
@ -2,9 +2,12 @@ plugins {
|
||||
id("ru.mipt.npm.gradle.jvm")
|
||||
}
|
||||
|
||||
val ktorVersion = npmlibs.versions.ktor.get()
|
||||
|
||||
dependencies {
|
||||
api(project(":visionforge-core"))
|
||||
api(npmlibs.ktor.server.cio)
|
||||
api(npmlibs.ktor.html.builder)
|
||||
api(npmlibs.ktor.websockets)
|
||||
api("io.ktor:ktor-server-cio:${ktorVersion}")
|
||||
api("io.ktor:ktor-server-html-builder:${ktorVersion}")
|
||||
api("io.ktor:ktor-server-websockets:${ktorVersion}")
|
||||
implementation("io.ktor:ktor-server-cors:${ktorVersion}")
|
||||
}
|
@ -1,24 +1,25 @@
|
||||
package space.kscience.visionforge.server
|
||||
|
||||
import io.ktor.application.*
|
||||
import io.ktor.features.CORS
|
||||
import io.ktor.features.CallLogging
|
||||
import io.ktor.html.respondHtml
|
||||
import io.ktor.http.*
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.content.resources
|
||||
import io.ktor.http.content.static
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.getOrFail
|
||||
import io.ktor.websocket.WebSockets
|
||||
import io.ktor.websocket.webSocket
|
||||
import io.ktor.server.html.respondHtml
|
||||
import io.ktor.server.http.content.resources
|
||||
import io.ktor.server.http.content.static
|
||||
import io.ktor.server.plugins.cors.CORS
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.util.getOrFail
|
||||
import io.ktor.server.websocket.WebSockets
|
||||
import io.ktor.server.websocket.webSocket
|
||||
import io.ktor.websocket.Frame
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.html.*
|
||||
@ -130,13 +131,13 @@ public class VisionServer internal constructor(
|
||||
|
||||
try {
|
||||
withContext(visionManager.context.coroutineContext) {
|
||||
vision.flowChanges(updateInterval.milliseconds).collect { update ->
|
||||
vision.flowChanges(updateInterval.milliseconds).onEach { update ->
|
||||
val json = visionManager.jsonFormat.encodeToString(
|
||||
VisionChange.serializer(),
|
||||
update
|
||||
)
|
||||
outgoing.send(Frame.Text(json))
|
||||
}
|
||||
}.collect()
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
application.log.info("WebSocket update channel for $name is closed with exception: $t")
|
||||
@ -241,21 +242,16 @@ public fun Application.visionServer(
|
||||
webServerUrl: Url,
|
||||
path: String = DEFAULT_PAGE,
|
||||
): VisionServer {
|
||||
if (featureOrNull(WebSockets) == null) {
|
||||
install(WebSockets)
|
||||
}
|
||||
|
||||
if (featureOrNull(CORS) == null) {
|
||||
install(CORS) {
|
||||
anyHost()
|
||||
}
|
||||
}
|
||||
|
||||
if (featureOrNull(CallLogging) == null) {
|
||||
install(CallLogging)
|
||||
}
|
||||
// if (pluginOrNull(CallLogging) == null) {
|
||||
// install(CallLogging)
|
||||
// }
|
||||
|
||||
val serverRoute = (featureOrNull(Routing) ?: install(Routing)).createRouteFromPath(path)
|
||||
val serverRoute = install(Routing).createRouteFromPath(path)
|
||||
|
||||
serverRoute {
|
||||
static {
|
||||
@ -263,7 +259,7 @@ public fun Application.visionServer(
|
||||
}
|
||||
}
|
||||
|
||||
return VisionServer(visionManager, webServerUrl.copy(encodedPath = path), serverRoute)
|
||||
return VisionServer(visionManager, URLBuilder(webServerUrl).apply { encodedPath = path }.build(), serverRoute)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,15 +1,18 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.meta.Configurable
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.values.*
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
@VisionBuilder
|
||||
public class ColorAccessor(
|
||||
private val provider: MutableValueProvider,
|
||||
private val colorKey: Name
|
||||
private val colorKey: Name,
|
||||
) : MutableValueProvider {
|
||||
public var value: Value?
|
||||
get() = provider.getValue(colorKey)
|
||||
@ -24,8 +27,12 @@ public class ColorAccessor(
|
||||
}
|
||||
}
|
||||
|
||||
public fun Configurable.color(): ReadOnlyProperty<Configurable, ColorAccessor> = ReadOnlyProperty { _, property ->
|
||||
ColorAccessor(meta, property.name.asName())
|
||||
}
|
||||
|
||||
public var ColorAccessor?.string: String?
|
||||
get() = this?.value?.let { if(it == Null) null else it.string }
|
||||
get() = this?.value?.let { if (it == Null) null else it.string }
|
||||
set(value) {
|
||||
this?.value = value?.asValue()
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.node
|
||||
import space.kscience.dataforge.meta.descriptors.value
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.visionforge.*
|
||||
|
||||
@Serializable
|
||||
public abstract class LightSource : SolidBase() {
|
||||
override val descriptor: MetaDescriptor get() = LightSource.descriptor
|
||||
|
||||
public val color: ColorAccessor by color()
|
||||
public var intensity: Number by numberProperty(includeStyles = false) { 1.0 }
|
||||
|
||||
public companion object{
|
||||
public val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
value(Vision.VISIBLE_KEY, ValueType.BOOLEAN) {
|
||||
inherited = false
|
||||
default(true)
|
||||
}
|
||||
|
||||
value(LightSource::color.name, ValueType.STRING, ValueType.NUMBER) {
|
||||
inherited = false
|
||||
widgetType = "color"
|
||||
}
|
||||
|
||||
value(LightSource::intensity.name, ValueType.NUMBER) {
|
||||
inherited = false
|
||||
default(1.0)
|
||||
}
|
||||
|
||||
value(SolidMaterial.COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
|
||||
inherited = false
|
||||
widgetType = "color"
|
||||
}
|
||||
|
||||
node(Solid.POSITION_KEY) {
|
||||
hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.light.ambient")
|
||||
public class AmbientLightSource : LightSource()
|
||||
|
||||
@VisionBuilder
|
||||
public fun VisionContainerBuilder<Solid>.ambientLight(
|
||||
name: String? = "@ambientLight",
|
||||
block: AmbientLightSource.() -> Unit = {},
|
||||
): AmbientLightSource = AmbientLightSource().apply(block).also { set(name, it) }
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.light.point")
|
||||
public class PointLightSource : LightSource()
|
||||
|
||||
|
||||
@VisionBuilder
|
||||
public fun VisionContainerBuilder<Solid>.pointLight(
|
||||
x: Number,
|
||||
y: Number,
|
||||
z: Number,
|
||||
name: String? = null,
|
||||
block: PointLightSource.() -> Unit = {},
|
||||
): PointLightSource = PointLightSource().apply(block).also {
|
||||
it.position = Point3D(x, y, z)
|
||||
set(name, it)
|
||||
}
|
@ -72,9 +72,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup {
|
||||
return SolidGroup().apply(block)
|
||||
}
|
||||
public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)
|
||||
|
||||
@VisionBuilder
|
||||
public fun VisionContainerBuilder<Vision>.group(
|
||||
|
@ -27,7 +27,7 @@ public class SolidMaterial : Scheme() {
|
||||
*/
|
||||
public val specularColor: ColorAccessor = ColorAccessor(meta, SPECULAR_COLOR_KEY)
|
||||
|
||||
public val emissiveColor: ColorAccessor = ColorAccessor(meta, "emissiveColor".asName())
|
||||
public val emissiveColor: ColorAccessor = ColorAccessor(meta, EMISSIVE_COLOR_KEY)
|
||||
|
||||
/**
|
||||
* Opacity
|
||||
@ -43,12 +43,15 @@ public class SolidMaterial : Scheme() {
|
||||
|
||||
public val MATERIAL_KEY: Name = "material".asName()
|
||||
public val COLOR_KEY: Name = "color".asName()
|
||||
public val MATERIAL_COLOR_KEY: Name = MATERIAL_KEY + COLOR_KEY
|
||||
public val TYPE_KEY: Name = "type".asName()
|
||||
public val SPECULAR_COLOR_KEY: Name = "specularColor".asName()
|
||||
public val MATERIAL_SPECULAR_COLOR_KEY: Name = MATERIAL_KEY + SPECULAR_COLOR_KEY
|
||||
public val EMISSIVE_COLOR_KEY: Name = "emissiveColor".asName()
|
||||
public val OPACITY_KEY: Name = "opacity".asName()
|
||||
public val MATERIAL_OPACITY_KEY: Name = MATERIAL_KEY + OPACITY_KEY
|
||||
public val WIREFRAME_KEY: Name = "wireframe".asName()
|
||||
public val MATERIAL_COLOR_KEY: Name = MATERIAL_KEY + COLOR_KEY
|
||||
public val MATERIAL_EMISSIVE_COLOR_KEY: Name = MATERIAL_KEY + EMISSIVE_COLOR_KEY
|
||||
public val MATERIAL_SPECULAR_COLOR_KEY: Name = MATERIAL_KEY + SPECULAR_COLOR_KEY
|
||||
public val MATERIAL_WIREFRAME_KEY: Name = MATERIAL_KEY + WIREFRAME_KEY
|
||||
|
||||
public override val descriptor: MetaDescriptor by lazy {
|
||||
@ -56,6 +59,12 @@ public class SolidMaterial : Scheme() {
|
||||
MetaDescriptor {
|
||||
inherited = true
|
||||
|
||||
value(TYPE_KEY, ValueType.STRING){
|
||||
inherited = true
|
||||
allowedValues = listOf("default".asValue(), "simple".asValue())
|
||||
default("default")
|
||||
}
|
||||
|
||||
value(COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
|
||||
inherited = true
|
||||
widgetType = "color"
|
||||
@ -67,6 +76,12 @@ public class SolidMaterial : Scheme() {
|
||||
hide()
|
||||
}
|
||||
|
||||
value(EMISSIVE_COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
|
||||
inherited = true
|
||||
widgetType = "color"
|
||||
hide()
|
||||
}
|
||||
|
||||
value(OPACITY_KEY, ValueType.NUMBER) {
|
||||
inherited = true
|
||||
default(1.0)
|
||||
|
@ -39,6 +39,9 @@ public class Solids(meta: Meta) : VisionPlugin(meta) {
|
||||
subclass(PolyLine.serializer())
|
||||
subclass(SolidLabel.serializer())
|
||||
subclass(Sphere.serializer())
|
||||
|
||||
subclass(AmbientLightSource.serializer())
|
||||
subclass(PointLightSource.serializer())
|
||||
}
|
||||
|
||||
public val serializersModuleForSolids: SerializersModule = SerializersModule {
|
||||
|
@ -7,24 +7,24 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.value
|
||||
import space.kscience.dataforge.meta.double
|
||||
|
||||
public class Axes : Scheme() {
|
||||
public class AxesScheme : Scheme() {
|
||||
public var visible: Boolean by boolean(false)
|
||||
public var size: Double by double(AXIS_SIZE)
|
||||
public var width: Double by double(AXIS_WIDTH)
|
||||
|
||||
public companion object : SchemeSpec<Axes>(::Axes) {
|
||||
public companion object : SchemeSpec<AxesScheme>(::AxesScheme) {
|
||||
public const val AXIS_SIZE: Double = 1000.0
|
||||
public const val AXIS_WIDTH: Double = 3.0
|
||||
|
||||
override val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
value(Axes::visible){
|
||||
value(AxesScheme::visible){
|
||||
default(false)
|
||||
}
|
||||
value(Axes::size){
|
||||
value(AxesScheme::size){
|
||||
default(AXIS_SIZE)
|
||||
}
|
||||
value(Axes::width){
|
||||
value(AxesScheme::width){
|
||||
default(AXIS_WIDTH)
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import space.kscience.dataforge.meta.double
|
||||
import space.kscience.dataforge.meta.int
|
||||
import kotlin.math.PI
|
||||
|
||||
public class Camera : Scheme() {
|
||||
public class CameraScheme : Scheme() {
|
||||
public var fov: Int by int(FIELD_OF_VIEW)
|
||||
|
||||
//var aspect by double(1.0)
|
||||
@ -19,7 +19,7 @@ public class Camera : Scheme() {
|
||||
public var azimuth: Double by double(INITIAL_AZIMUTH)
|
||||
public var latitude: Double by double(INITIAL_LATITUDE)
|
||||
|
||||
public companion object : SchemeSpec<Camera>(::Camera) {
|
||||
public companion object : SchemeSpec<CameraScheme>(::CameraScheme) {
|
||||
public const val INITIAL_DISTANCE: Double = 300.0
|
||||
public const val INITIAL_AZIMUTH: Double = 0.0
|
||||
public const val INITIAL_LATITUDE: Double = PI / 6
|
||||
@ -29,22 +29,22 @@ public class Camera : Scheme() {
|
||||
|
||||
override val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
value(Camera::fov) {
|
||||
value(CameraScheme::fov) {
|
||||
default(FIELD_OF_VIEW)
|
||||
}
|
||||
value(Camera::nearClip) {
|
||||
value(CameraScheme::nearClip) {
|
||||
default(NEAR_CLIP)
|
||||
}
|
||||
value(Camera::farClip) {
|
||||
value(CameraScheme::farClip) {
|
||||
default(FAR_CLIP)
|
||||
}
|
||||
value(Camera::distance) {
|
||||
value(CameraScheme::distance) {
|
||||
default(INITIAL_DISTANCE)
|
||||
}
|
||||
value(Camera::azimuth) {
|
||||
value(CameraScheme::azimuth) {
|
||||
default(INITIAL_AZIMUTH)
|
||||
}
|
||||
value(Camera::latitude) {
|
||||
value(CameraScheme::latitude) {
|
||||
default(INITIAL_LATITUDE)
|
||||
}
|
||||
}
|
||||
@ -52,4 +52,4 @@ public class Camera : Scheme() {
|
||||
}
|
||||
}
|
||||
|
||||
public val Camera.zenith: Double get() = PI / 2 - latitude
|
||||
public val CameraScheme.zenith: Double get() = PI / 2 - latitude
|
@ -8,28 +8,24 @@ import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.hide
|
||||
import space.kscience.visionforge.widgetType
|
||||
|
||||
public class Clipping : Scheme() {
|
||||
public var x: Double? by double()
|
||||
public var y: Double? by double()
|
||||
public var z: Double? by double()
|
||||
|
||||
public companion object : SchemeSpec<Clipping>(::Clipping) {
|
||||
public object Clipping : SchemeSpec<PointScheme>(::PointScheme) {
|
||||
override val descriptor: MetaDescriptor = MetaDescriptor {
|
||||
value(Clipping::x) {
|
||||
value(PointScheme::x) {
|
||||
widgetType = "range"
|
||||
attributes["min"] = 0.0
|
||||
attributes["max"] = 1.0
|
||||
attributes["step"] = 0.01
|
||||
default(1.0)
|
||||
}
|
||||
value(Clipping::y) {
|
||||
value(PointScheme::y) {
|
||||
widgetType = "range"
|
||||
attributes["min"] = 0.0
|
||||
attributes["max"] = 1.0
|
||||
attributes["step"] = 0.01
|
||||
default(1.0)
|
||||
}
|
||||
value(Clipping::z) {
|
||||
value(PointScheme::z) {
|
||||
widgetType = "range"
|
||||
attributes["min"] = 0.0
|
||||
attributes["max"] = 1.0
|
||||
@ -37,9 +33,9 @@ public class Clipping : Scheme() {
|
||||
default(1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CanvasSize : Scheme() {
|
||||
public var minSize: Int by int(400)
|
||||
public var minWith: Number by number { minSize }
|
||||
@ -62,16 +58,15 @@ public class CanvasSize : Scheme() {
|
||||
}
|
||||
|
||||
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 axes: AxesScheme by spec(AxesScheme)
|
||||
public var camera: CameraScheme by spec(CameraScheme)
|
||||
public var controls: ControlsScheme by spec(ControlsScheme)
|
||||
|
||||
public var size: CanvasSize by spec(CanvasSize)
|
||||
|
||||
public var layers: List<Number> by numberList(0)
|
||||
|
||||
public var clipping: Clipping by spec(Clipping)
|
||||
public var clipping: PointScheme by spec(Clipping)
|
||||
|
||||
public var onSelect: ((Name?) -> Unit)? = null
|
||||
|
||||
@ -79,7 +74,7 @@ public class Canvas3DOptions : Scheme() {
|
||||
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
|
||||
override val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
scheme(Canvas3DOptions::axes, Axes)
|
||||
scheme(Canvas3DOptions::axes, AxesScheme)
|
||||
|
||||
value(Canvas3DOptions::layers) {
|
||||
multiple = true
|
||||
@ -90,15 +85,11 @@ public class Canvas3DOptions : Scheme() {
|
||||
|
||||
scheme(Canvas3DOptions::clipping, Clipping)
|
||||
|
||||
scheme(Canvas3DOptions::light, Light){
|
||||
scheme(Canvas3DOptions::camera, CameraScheme) {
|
||||
hide()
|
||||
}
|
||||
|
||||
scheme(Canvas3DOptions::camera, Camera) {
|
||||
hide()
|
||||
}
|
||||
|
||||
scheme(Canvas3DOptions::controls, Controls) {
|
||||
scheme(Canvas3DOptions::controls, ControlsScheme) {
|
||||
hide()
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,6 @@ import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.SchemeSpec
|
||||
|
||||
|
||||
public class Controls : Scheme() {
|
||||
public companion object : SchemeSpec<Controls>(::Controls)
|
||||
public class ControlsScheme : Scheme() {
|
||||
public companion object : SchemeSpec<ControlsScheme>(::ControlsScheme)
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package space.kscience.visionforge.solid.specifications
|
||||
|
||||
import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.SchemeSpec
|
||||
|
||||
public class Light : Scheme() {
|
||||
public companion object : SchemeSpec<Light>(::Light)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package space.kscience.visionforge.solid.specifications
|
||||
|
||||
import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.SchemeSpec
|
||||
import space.kscience.dataforge.meta.double
|
||||
|
||||
public class PointScheme: Scheme(){
|
||||
public var x: Double? by double()
|
||||
public var y: Double? by double()
|
||||
public var z: Double? by double()
|
||||
|
||||
public companion object: SchemeSpec<PointScheme>(::PointScheme)
|
||||
}
|
||||
|
||||
public operator fun PointScheme.invoke(x: Number?, y: Number?, z: Number?){
|
||||
this.x = x?.toDouble()
|
||||
this.y = y?.toDouble()
|
||||
this.z = z?.toDouble()
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.MutableVisionGroup
|
||||
import space.kscience.visionforge.get
|
||||
import kotlin.test.Test
|
||||
@ -55,4 +56,18 @@ class SerializationTest {
|
||||
assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lightSerialization(){
|
||||
val group = SolidGroup {
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
intensity = 100.0
|
||||
}
|
||||
}
|
||||
val serialized = Solids.encodeToString(group)
|
||||
|
||||
val reconstructed = Solids.decodeFromString(serialized) as SolidGroup
|
||||
assertEquals(100.0, (reconstructed["@ambientLight"] as AmbientLightSource).intensity.toDouble())
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package space.kscience.visionforge.tables
|
||||
|
||||
import kotlinext.js.jso
|
||||
import kotlinx.js.jso
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import space.kscience.dataforge.context.AbstractPlugin
|
||||
|
@ -10,6 +10,6 @@ kotlin{
|
||||
|
||||
dependencies {
|
||||
api(project(":visionforge-solid"))
|
||||
implementation(npm("three", "0.130.1"))
|
||||
implementation(npm("three", "0.137.4"))
|
||||
implementation(npm("three-csg-ts", "3.1.9"))
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.names.startsWith
|
||||
import space.kscience.dataforge.values.boolean
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.computePropertyNode
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import space.kscience.visionforge.setProperty
|
||||
@ -75,6 +76,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
|
||||
}
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) {
|
||||
setProperty(EDGES_ENABLED_KEY, enabled)
|
||||
meta.getOrCreate(EDGES_MATERIAL_KEY).updateWith(SolidMaterial, block)
|
||||
|
@ -0,0 +1,19 @@
|
||||
package space.kscience.visionforge.solid.three
|
||||
|
||||
import info.laht.threekt.lights.AmbientLight
|
||||
import info.laht.threekt.math.Color
|
||||
import space.kscience.visionforge.solid.AmbientLightSource
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public object ThreeAmbientLightFactory : ThreeFactory<AmbientLightSource> {
|
||||
override val type: KClass<in AmbientLightSource> get() = AmbientLightSource::class
|
||||
|
||||
override fun invoke(three: ThreePlugin, obj: AmbientLightSource): AmbientLight {
|
||||
val res = AmbientLight().apply {
|
||||
color = obj.color.threeColor() ?: Color(0x404040)
|
||||
intensity = obj.intensity.toDouble()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
}
|
@ -8,12 +8,8 @@ import info.laht.threekt.external.controls.OrbitControls
|
||||
import info.laht.threekt.external.controls.TrackballControls
|
||||
import info.laht.threekt.geometries.EdgesGeometry
|
||||
import info.laht.threekt.helpers.AxesHelper
|
||||
import info.laht.threekt.lights.AmbientLight
|
||||
import info.laht.threekt.materials.LineBasicMaterial
|
||||
import info.laht.threekt.math.Box3
|
||||
import info.laht.threekt.math.Plane
|
||||
import info.laht.threekt.math.Vector2
|
||||
import info.laht.threekt.math.Vector3
|
||||
import info.laht.threekt.math.*
|
||||
import info.laht.threekt.objects.LineSegments
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import info.laht.threekt.scenes.Scene
|
||||
@ -35,7 +31,7 @@ import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
*
|
||||
* A canvas for three-js rendering
|
||||
*/
|
||||
public class ThreeCanvas(
|
||||
public val three: ThreePlugin,
|
||||
@ -60,19 +56,19 @@ public class ThreeCanvas(
|
||||
add(axesObject)
|
||||
}
|
||||
|
||||
//Set up light
|
||||
options.useProperty(Canvas3DOptions::light, this) { lightConfig ->
|
||||
//remove old light if present
|
||||
getObjectByName(LIGHT_NAME)?.let { remove(it) }
|
||||
//add new light
|
||||
val lightObject = buildLight(lightConfig)
|
||||
lightObject.name = LIGHT_NAME
|
||||
add(lightObject)
|
||||
}
|
||||
// //Set up light
|
||||
// options.useProperty(Canvas3DOptions::light, this) { lightConfig ->
|
||||
// //remove old light if present
|
||||
// getObjectByName(LIGHT_NAME)?.let { remove(it) }
|
||||
// //add new light
|
||||
// val lightObject = buildLight(lightConfig)
|
||||
// lightObject.name = LIGHT_NAME
|
||||
// add(lightObject)
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
private fun buildCamera(spec: Camera) = PerspectiveCamera(
|
||||
private fun buildCamera(spec: CameraScheme) = PerspectiveCamera(
|
||||
spec.fov,
|
||||
1.0,
|
||||
spec.nearClip,
|
||||
@ -231,9 +227,24 @@ public class ThreeCanvas(
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040)
|
||||
// private fun buildLight(spec: AmbientLightScheme?): info.laht.threekt.lights.Light = when (spec?.type) {
|
||||
// AmbientLightScheme.Type.POINT -> PointLight().apply {
|
||||
// position.x = spec.position.x ?: 0.0
|
||||
// position.y = spec.position.y ?: 0.0
|
||||
// position.z = spec.position.z ?: 0.0
|
||||
// }
|
||||
// else -> AmbientLight().apply {
|
||||
//
|
||||
// }
|
||||
// }.apply {
|
||||
// this.color = spec?.color?.threeColor() ?: Color(0x404040)
|
||||
//
|
||||
// spec?.intensity?.coerceIn(0.0, 1.0)?.let {
|
||||
// this.intensity = it
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun addControls(element: Node, controls: Controls) {
|
||||
private fun addControls(element: Node, controls: ControlsScheme) {
|
||||
when (controls.meta["type"].string) {
|
||||
"trackball" -> TrackballControls(camera, element)
|
||||
else -> OrbitControls(camera, element)
|
||||
@ -311,7 +322,7 @@ public class ThreeCanvas(
|
||||
private const val SELECT_NAME = "@select"
|
||||
private const val LIGHT_NAME = "@light"
|
||||
private const val AXES_NAME = "@axes"
|
||||
private const val CLIP_HELPER_NAME = "@clipping"
|
||||
//private const val CLIP_HELPER_NAME = "@clipping"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ import space.kscience.dataforge.misc.Type
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.startsWith
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.computeProperty
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
|
||||
import space.kscience.visionforge.solid.three.ThreeFactory.Companion.TYPE
|
||||
@ -58,7 +57,7 @@ public fun Object3D.updatePosition(obj: Vision) {
|
||||
* Update non-position non-geometry property
|
||||
*/
|
||||
public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
|
||||
console.log("$source updated $propertyName with ${source.computeProperty(propertyName)}")
|
||||
// console.log("$source updated $propertyName with ${source.computeProperty(propertyName)}")
|
||||
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
||||
updateMaterialProperty(source, propertyName)
|
||||
} else if (
|
||||
|
@ -4,7 +4,6 @@ package space.kscience.visionforge.solid.three
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.geometries.TextBufferGeometry
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import kotlinext.js.jsObject
|
||||
import kotlinext.js.jso
|
||||
import space.kscience.dataforge.context.logger
|
||||
import space.kscience.dataforge.context.warn
|
||||
|
@ -3,39 +3,52 @@ package space.kscience.visionforge.solid.three
|
||||
import info.laht.threekt.materials.LineBasicMaterial
|
||||
import info.laht.threekt.materials.Material
|
||||
import info.laht.threekt.materials.MeshBasicMaterial
|
||||
import info.laht.threekt.materials.MeshStandardMaterial
|
||||
import info.laht.threekt.math.Color
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.values.*
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.computePropertyNode
|
||||
import space.kscience.visionforge.getStyleNodes
|
||||
import space.kscience.visionforge.solid.ColorAccessor
|
||||
import space.kscience.visionforge.solid.SolidMaterial
|
||||
import space.kscience.visionforge.solid.SolidReference
|
||||
|
||||
|
||||
public object ThreeMaterials {
|
||||
public val DEFAULT_COLOR: Color = Color(Colors.darkgreen)
|
||||
public val DEFAULT: MeshBasicMaterial = MeshBasicMaterial().apply {
|
||||
|
||||
public val DEFAULT: MeshStandardMaterial = MeshStandardMaterial().apply {
|
||||
color.set(DEFAULT_COLOR)
|
||||
cached = true
|
||||
}
|
||||
public val DEFAULT_LINE_COLOR: Color = Color(Colors.black)
|
||||
|
||||
public val BLACK_COLOR: Color = Color(Colors.black)
|
||||
|
||||
public val DEFAULT_EMISSIVE_COLOR: Color = BLACK_COLOR
|
||||
|
||||
public val DEFAULT_LINE_COLOR: Color get() = BLACK_COLOR
|
||||
|
||||
public val DEFAULT_LINE: LineBasicMaterial = LineBasicMaterial().apply {
|
||||
color.set(DEFAULT_LINE_COLOR)
|
||||
cached = true
|
||||
}
|
||||
|
||||
public val SELECTED_MATERIAL: LineBasicMaterial = LineBasicMaterial().apply {
|
||||
color.set(Colors.ivory)
|
||||
linewidth = 8.0
|
||||
cached = true
|
||||
}
|
||||
|
||||
public val HIGHLIGHT_MATERIAL: LineBasicMaterial = LineBasicMaterial().apply {
|
||||
color.set(Colors.blue)
|
||||
linewidth = 8.0
|
||||
cached = true
|
||||
}
|
||||
|
||||
private val lineMaterialCache = HashMap<Meta, LineBasicMaterial>()
|
||||
@ -58,34 +71,21 @@ public object ThreeMaterials {
|
||||
|
||||
private val materialCache = HashMap<Meta, Material>()
|
||||
|
||||
internal fun buildMaterial(meta: Meta): Material = MeshBasicMaterial().apply {
|
||||
internal fun buildMaterial(meta: Meta): Material = when (meta[SolidMaterial.TYPE_KEY]?.string) {
|
||||
"simple" -> MeshBasicMaterial().apply {
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
|
||||
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
|
||||
}
|
||||
else -> MeshStandardMaterial().apply {
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
|
||||
emissive = meta[SolidMaterial.EMISSIVE_COLOR_KEY]?.threeColor() ?: DEFAULT_EMISSIVE_COLOR
|
||||
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
|
||||
}
|
||||
}.apply {
|
||||
opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
|
||||
transparent = opacity < 1.0
|
||||
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
|
||||
needsUpdate = true
|
||||
}
|
||||
// val material = SolidMaterial.read(meta)
|
||||
// return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor ->
|
||||
// MeshPhongMaterial().apply {
|
||||
// color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
|
||||
// specular = specularColor.threeColor()
|
||||
// emissive = material.emissiveColor.threeColor() ?: specular
|
||||
// reflectivity = 0.5
|
||||
// refractionRatio = 1.0
|
||||
// shininess = 100.0
|
||||
// opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
|
||||
// transparent = opacity < 1.0
|
||||
// wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
|
||||
// needsUpdate = true
|
||||
// }
|
||||
// } ?: MeshBasicMaterial().apply {
|
||||
// color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
|
||||
// opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
|
||||
// transparent = opacity < 1.0
|
||||
// wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
|
||||
// needsUpdate = true
|
||||
// }
|
||||
|
||||
internal fun cacheMaterial(meta: Meta): Material = materialCache.getOrPut(meta) {
|
||||
buildMaterial(meta).apply {
|
||||
@ -115,6 +115,16 @@ public fun Meta.threeColor(): Color? {
|
||||
}
|
||||
}
|
||||
|
||||
public fun ColorAccessor.threeColor(): Color? {
|
||||
val value = value
|
||||
return when {
|
||||
value == null -> null
|
||||
value === Null -> null
|
||||
value.type == ValueType.NUMBER -> Color(value.int)
|
||||
else -> Color(value.string)
|
||||
}
|
||||
}
|
||||
|
||||
private var Material.cached: Boolean
|
||||
get() = userData["cached"] == true
|
||||
set(value) {
|
||||
@ -139,7 +149,11 @@ public fun Mesh.updateMaterial(vision: Vision) {
|
||||
}
|
||||
|
||||
public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
|
||||
if (material.cached || propertyName == SolidMaterial.MATERIAL_KEY) {
|
||||
if (
|
||||
material.cached
|
||||
|| propertyName == SolidMaterial.MATERIAL_KEY
|
||||
|| propertyName == SolidMaterial.MATERIAL_KEY + SolidMaterial.TYPE_KEY
|
||||
) {
|
||||
//generate a new material since cached material should not be changed
|
||||
updateMaterial(vision)
|
||||
} else {
|
||||
@ -149,6 +163,16 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
|
||||
?: ThreeMaterials.DEFAULT_COLOR
|
||||
material.needsUpdate = true
|
||||
}
|
||||
SolidMaterial.SPECULAR_COLOR_KEY -> {
|
||||
material.asDynamic().specular = vision.computePropertyNode(SolidMaterial.SPECULAR_COLOR_KEY)?.threeColor()
|
||||
?: ThreeMaterials.DEFAULT_COLOR
|
||||
material.needsUpdate = true
|
||||
}
|
||||
SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY -> {
|
||||
material.asDynamic().emissive = vision.computePropertyNode(SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY)?.threeColor()
|
||||
?: ThreeMaterials.BLACK_COLOR
|
||||
material.needsUpdate = true
|
||||
}
|
||||
SolidMaterial.MATERIAL_OPACITY_KEY -> {
|
||||
val opacity = vision.getPropertyValue(
|
||||
SolidMaterial.MATERIAL_OPACITY_KEY,
|
||||
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.visionforge.solid.three
|
||||
|
||||
import info.laht.threekt.core.Object3D
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import space.kscience.dataforge.context.*
|
||||
@ -27,8 +26,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
private val objectFactories = HashMap<KClass<out Solid>, ThreeFactory<*>>()
|
||||
private val compositeFactory = ThreeCompositeFactory(this)
|
||||
|
||||
//TODO generate a separate supervisor update scope
|
||||
internal val updateScope: CoroutineScope get() = context
|
||||
// internal val updateScope: CoroutineScope get() = context
|
||||
|
||||
init {
|
||||
//Add specialized factories here
|
||||
@ -38,6 +36,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
objectFactories[ConeSegment::class] = ThreeConeFactory
|
||||
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||
objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory
|
||||
objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory
|
||||
objectFactories[PointLightSource::class] = ThreePointLightFactory
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -0,0 +1,32 @@
|
||||
package space.kscience.visionforge.solid.three
|
||||
|
||||
import info.laht.threekt.lights.PointLight
|
||||
import info.laht.threekt.math.Color
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import space.kscience.visionforge.solid.LightSource
|
||||
import space.kscience.visionforge.solid.PointLightSource
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public object ThreePointLightFactory : ThreeFactory<PointLightSource> {
|
||||
override val type: KClass<in PointLightSource> get() = PointLightSource::class
|
||||
|
||||
override fun invoke(three: ThreePlugin, obj: PointLightSource): PointLight {
|
||||
val res = PointLight().apply {
|
||||
matrixAutoUpdate = false
|
||||
color = obj.color.threeColor() ?: Color(0x404040)
|
||||
intensity = obj.intensity.toDouble()
|
||||
updatePosition(obj)
|
||||
}
|
||||
|
||||
obj.onPropertyChange { name ->
|
||||
when (name) {
|
||||
LightSource::color.name.asName() -> res.color = obj.color.threeColor() ?: Color(0x404040)
|
||||
LightSource::intensity.name.asName() -> res.intensity = obj.intensity.toDouble()
|
||||
else -> res.updateProperty(obj, name)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ plugins {
|
||||
val ktorVersion: String by rootProject.extra
|
||||
|
||||
kotlin {
|
||||
js{
|
||||
js(IR){
|
||||
browser {
|
||||
webpackTask {
|
||||
this.outputFileName = "js/visionforge-three.js"
|
||||
|
Loading…
Reference in New Issue
Block a user