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