Clipping implemented

This commit is contained in:
Alexander Nozik 2021-05-11 15:28:05 +03:00
parent 8652b03fa5
commit bdbff04d78
10 changed files with 134 additions and 29 deletions

View File

@ -8,6 +8,7 @@
- SphereLayer solid - SphereLayer solid
- Hexagon interface and GenericHexagon implementation (Box inherits Hexagon) - Hexagon interface and GenericHexagon implementation (Box inherits Hexagon)
- Increased the default detail level for spheres and cones to 32 - Increased the default detail level for spheres and cones to 32
- Simple clipping for Solids in ThreeJs
### Changed ### Changed
- Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate. - Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate.

View File

@ -0,0 +1,43 @@
package space.kscience.visionforge.react
import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event
import react.FunctionalComponent
import react.functionalComponent
import react.useState
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.values.asValue
import styled.styledInput
@JsExport
public val RangeValueChooser: FunctionalComponent<ValueChooserProps> =
functionalComponent("RangeValueChooser") { props ->
var innerValue by useState(props.item.string)
val handleChange: (Event) -> Unit = {
val newValue = (it.target as HTMLInputElement).value
props.valueChanged?.invoke(newValue.toDoubleOrNull()?.asValue())
innerValue = newValue
}
styledInput(type = InputType.range) {
attrs {
value = innerValue ?: ""
onChangeFunction = handleChange
val minValue = props.descriptor?.attributes?.get("min").string
minValue?.let {
min = it
}
val maxValue = props.descriptor?.attributes?.get("max").string
maxValue?.let {
max = it
}
props.descriptor?.attributes?.get("step").string?.let {
step = it
}
}
}
}

View File

@ -153,6 +153,7 @@ public val ValueChooser: FunctionalComponent<ValueChooserProps> = functionalComp
rawInput -> child(StringValueChooser, props) rawInput -> child(StringValueChooser, props)
descriptor?.widgetType == "color" -> child(ColorValueChooser, props) descriptor?.widgetType == "color" -> child(ColorValueChooser, props)
descriptor?.widgetType == "multiSelect" -> child(MultiSelectChooser, props) descriptor?.widgetType == "multiSelect" -> child(MultiSelectChooser, props)
descriptor?.widgetType == "range" -> child(RangeValueChooser, props)
type == ValueType.BOOLEAN -> child(BooleanValueChooser, props) type == ValueType.BOOLEAN -> child(BooleanValueChooser, props)
type == ValueType.NUMBER -> child(NumberValueChooser, props) type == ValueType.NUMBER -> child(NumberValueChooser, props)
descriptor?.allowedValues?.isNotEmpty() ?: false -> child(ComboValueChooser, props) descriptor?.allowedValues?.isNotEmpty() ?: false -> child(ComboValueChooser, props)

View File

@ -23,6 +23,11 @@ public inline fun <S : Scheme, reified T> NodeDescriptorBuilder.value(
type(ValueType.NUMBER) type(ValueType.NUMBER)
block() block()
} }
typeOf<Number?>(), typeOf<Int?>(), typeOf<Double?>(), typeOf<Short?>(), typeOf<Long?>(), typeOf<Float?>() ->
value(property.name) {
type(ValueType.NUMBER)
block()
}
typeOf<Boolean>() -> value(property.name) { typeOf<Boolean>() -> value(property.name) {
type(ValueType.BOOLEAN) type(ValueType.BOOLEAN)
block() block()

View File

@ -17,16 +17,7 @@ public class Composite(
public val compositeType: CompositeType, public val compositeType: CompositeType,
public val first: Solid, public val first: Solid,
public val second: Solid, public val second: Solid,
) : SolidBase(), Solid { ) : SolidBase(), Solid
//
// init {
// first.parent = this
// second.parent = this
// }
//
// override val children: Map<NameToken, Vision>
// get() = mapOf(NameToken("first") to first, NameToken("second") to second)
}
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.composite( public inline fun VisionContainerBuilder<Solid>.composite(

View File

@ -2,6 +2,7 @@ package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.meta.descriptors.attributes
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.hide import space.kscience.visionforge.hide
@ -9,14 +10,40 @@ import space.kscience.visionforge.scheme
import space.kscience.visionforge.value import space.kscience.visionforge.value
import space.kscience.visionforge.widgetType import space.kscience.visionforge.widgetType
public class ClippingPlane: Scheme(){ public class Clipping : Scheme() {
public var x: Double by double(0.0) public var x: Double? by double()
public var y: Double by double(0.0) public var y: Double? by double()
public var z: Double by double(0.0) public var z: Double? by double()
public companion object: SchemeSpec<ClippingPlane>(::ClippingPlane) public companion object : SchemeSpec<Clipping>(::Clipping) {
override val descriptor: NodeDescriptor = NodeDescriptor {
value(Clipping::x) {
widgetType = "range"
attributes {
set("min", 0.0)
set("max", 1.0)
set("step", 0.01)
}
}
value(Clipping::y) {
widgetType = "range"
attributes {
set("min", 0.0)
set("max", 1.0)
set("step", 0.01)
}
}
value(Clipping::z) {
widgetType = "range"
attributes {
set("min", 0.0)
set("max", 1.0)
set("step", 0.01)
}
}
}
}
} }
public class Canvas3DOptions : Scheme() { public class Canvas3DOptions : Scheme() {
public var axes: Axes by spec(Axes) public var axes: Axes by spec(Axes)
@ -34,7 +61,7 @@ public class Canvas3DOptions : Scheme() {
public var layers: List<Number> by numberList(0) public var layers: List<Number> by numberList(0)
//public var clippingPlanes: List<ClippingPlane> by list public var clipping: Clipping by spec(Clipping)
public var onSelect: ((Name?) -> Unit)? = null public var onSelect: ((Name?) -> Unit)? = null
@ -75,6 +102,7 @@ public class Canvas3DOptions : Scheme() {
widgetType = "multiSelect" widgetType = "multiSelect"
allow(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) allow(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
} }
scheme(Canvas3DOptions::clipping, Clipping)
} }
} }
} }

View File

@ -0,0 +1,15 @@
@file:JsModule("three")
@file:JsNonModule
package info.laht.threekt.helpers
import info.laht.threekt.math.Color
import info.laht.threekt.math.Plane
import info.laht.threekt.objects.LineSegments
/**
* Helper object to visualize a [Plane].
*/
external class PlaneHelper(plane : Plane, size : Float, hex : Color): LineSegments{
var plane: Plane
var size: Float
}

View File

@ -27,9 +27,7 @@
package info.laht.threekt.math package info.laht.threekt.math
external class Plane { external class Plane() {
constructor()
constructor(normal: Vector3, constant: Double) constructor(normal: Vector3, constant: Double)
var normal: Vector3 var normal: Vector3

View File

@ -12,7 +12,9 @@ import info.laht.threekt.helpers.AxesHelper
import info.laht.threekt.lights.AmbientLight 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.Box3
import info.laht.threekt.math.Plane
import info.laht.threekt.math.Vector2 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
@ -25,10 +27,7 @@ import space.kscience.dataforge.context.logger
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.meta.useProperty import space.kscience.dataforge.meta.useProperty
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.*
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.specifications.* import space.kscience.visionforge.solid.specifications.*
@ -59,7 +58,7 @@ public class ThreeCanvas(
private set private set
private val scene: Scene = Scene().apply { private val scene: Scene = Scene().apply {
options.useProperty(Canvas3DOptions::axes) { axesConfig -> options.useProperty(Canvas3DOptions::axes, this) { axesConfig ->
getObjectByName(AXES_NAME)?.let { remove(it) } getObjectByName(AXES_NAME)?.let { remove(it) }
val axesObject = AxesHelper(axes.size.toInt()).apply { visible = axes.visible } val axesObject = AxesHelper(axes.size.toInt()).apply { visible = axes.visible }
axesObject.name = AXES_NAME axesObject.name = AXES_NAME
@ -67,7 +66,7 @@ public class ThreeCanvas(
} }
//Set up light //Set up light
options.useProperty(Canvas3DOptions::light) { 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
@ -87,7 +86,7 @@ public class ThreeCanvas(
translateX(spec.distance * sin(spec.zenith) * sin(spec.azimuth)) translateX(spec.distance * sin(spec.zenith) * sin(spec.azimuth))
translateY(spec.distance * cos(spec.zenith)) translateY(spec.distance * cos(spec.zenith))
translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth)) translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth))
options.useProperty(Canvas3DOptions::layers) { selectedLayers -> options.useProperty(Canvas3DOptions::layers, this) { selectedLayers ->
(0..31).forEach { (0..31).forEach {
if (it in selectedLayers) { if (it in selectedLayers) {
this@apply.layers.enable(it) this@apply.layers.enable(it)
@ -98,7 +97,6 @@ public class ThreeCanvas(
} }
} }
public val camera: PerspectiveCamera = buildCamera(options.camera) public val camera: PerspectiveCamera = buildCamera(options.camera)
private var picked: Object3D? = null private var picked: Object3D? = null
@ -107,6 +105,30 @@ public class ThreeCanvas(
antialias = true antialias = true
}.apply { }.apply {
setClearColor(Colors.skyblue, 1) setClearColor(Colors.skyblue, 1)
//Clipping planes
localClippingEnabled = true
options.onChange(this@ThreeCanvas) { name, _, _ ->
if (name.startsWith(Canvas3DOptions::clipping.name.asName())) {
val clipping = options.clipping
boundingBox?.let { boundingBox ->
val xClippingPlane = clipping.x?.let {
val absoluteValue = boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) * it
Plane(Vector3(-1.0, 0.0, 0.0), absoluteValue)
}
val yClippingPlane = clipping.y?.let {
val absoluteValue = boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) * it
Plane(Vector3(0.0, -1.0, 0.0), absoluteValue)
}
val zClippingPlane = clipping.z?.let {
val absoluteValue = boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) * it
Plane(Vector3(0.0, 0.0, -1.0), absoluteValue)
}
clippingPlanes = listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray()
}
}
}
} }
private val canvas = (renderer.domElement as HTMLCanvasElement).apply { private val canvas = (renderer.domElement as HTMLCanvasElement).apply {
@ -291,5 +313,6 @@ 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"
} }
} }

View File

@ -26,7 +26,7 @@ public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
val canvas = document.createElement("canvas") as HTMLCanvasElement val canvas = document.createElement("canvas") as HTMLCanvasElement
val context = canvas.getContext("2d") as CanvasRenderingContext2D val context = canvas.getContext("2d") as CanvasRenderingContext2D
context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}" context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}"
context.fillStyle = obj.color ?: "black" context.fillStyle = obj.color.value ?: "black"
context.textBaseline = CanvasTextBaseline.MIDDLE context.textBaseline = CanvasTextBaseline.MIDDLE
val metrics = context.measureText(obj.text) val metrics = context.measureText(obj.text)
//canvas.width = metrics.width.toInt() //canvas.width = metrics.width.toInt()