forked from kscience/visionforge
Replace light model for 3ds
This commit is contained in:
parent
db5064f6ed
commit
f828f86e29
@ -4,6 +4,8 @@
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
- Naming of Canvas3D options
|
||||
- Lights are added to the scene instead of 3D options
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
@ -8,7 +8,7 @@ val fxVersion by extra("11")
|
||||
|
||||
allprojects{
|
||||
group = "space.kscience"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1-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
|
||||
@ -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
|
||||
@ -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")
|
||||
|
@ -17,12 +17,13 @@ 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.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.specifications.Canvas3DOptions
|
||||
import space.kscience.visionforge.solid.three.edges
|
||||
import styled.css
|
||||
@ -42,17 +43,19 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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`.
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -0,0 +1,44 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.numberProperty
|
||||
import space.kscience.visionforge.set
|
||||
|
||||
@Serializable
|
||||
public abstract class LightSource : SolidBase() {
|
||||
@Transient
|
||||
public val color: ColorAccessor = ColorAccessor(meta, "color".asName())
|
||||
public var intensity: Number by numberProperty(includeStyles = false) { 1.0 }
|
||||
}
|
||||
|
||||
@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
|
||||
@ -38,7 +34,7 @@ public class Clipping : Scheme() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CanvasSize : Scheme() {
|
||||
public var minSize: Int by int(400)
|
||||
@ -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())
|
||||
}
|
||||
|
||||
}
|
@ -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 (
|
||||
|
@ -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,28 @@
|
||||
package space.kscience.visionforge.solid.three
|
||||
|
||||
import info.laht.threekt.lights.PointLight
|
||||
import info.laht.threekt.math.Color
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
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 {
|
||||
else -> res.updateProperty(obj, name)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user