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
- Hexagon interface and GenericHexagon implementation (Box inherits Hexagon)
- Increased the default detail level for spheres and cones to 32
- Simple clipping for Solids in ThreeJs
### Changed
- 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)
descriptor?.widgetType == "color" -> child(ColorValueChooser, props)
descriptor?.widgetType == "multiSelect" -> child(MultiSelectChooser, props)
descriptor?.widgetType == "range" -> child(RangeValueChooser, props)
type == ValueType.BOOLEAN -> child(BooleanValueChooser, props)
type == ValueType.NUMBER -> child(NumberValueChooser, 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)
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) {
type(ValueType.BOOLEAN)
block()

View File

@ -17,16 +17,7 @@ public class Composite(
public val compositeType: CompositeType,
public val first: Solid,
public val second: 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)
}
) : SolidBase(), Solid
@VisionBuilder
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.descriptors.NodeDescriptor
import space.kscience.dataforge.meta.descriptors.attributes
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.hide
@ -9,15 +10,41 @@ import space.kscience.visionforge.scheme
import space.kscience.visionforge.value
import space.kscience.visionforge.widgetType
public class ClippingPlane: Scheme(){
public var x: Double by double(0.0)
public var y: Double by double(0.0)
public var z: Double by double(0.0)
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<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 var axes: Axes by spec(Axes)
public var light: Light by spec(Light)
@ -34,7 +61,7 @@ public class Canvas3DOptions : Scheme() {
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
@ -75,6 +102,7 @@ public class Canvas3DOptions : Scheme() {
widgetType = "multiSelect"
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
external class Plane {
constructor()
external class Plane() {
constructor(normal: Vector3, constant: Double)
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.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.objects.LineSegments
import info.laht.threekt.objects.Mesh
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.string
import space.kscience.dataforge.meta.useProperty
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
import space.kscience.dataforge.names.*
import space.kscience.visionforge.Colors
import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.specifications.*
@ -59,7 +58,7 @@ public class ThreeCanvas(
private set
private val scene: Scene = Scene().apply {
options.useProperty(Canvas3DOptions::axes) { axesConfig ->
options.useProperty(Canvas3DOptions::axes, this) { axesConfig ->
getObjectByName(AXES_NAME)?.let { remove(it) }
val axesObject = AxesHelper(axes.size.toInt()).apply { visible = axes.visible }
axesObject.name = AXES_NAME
@ -67,7 +66,7 @@ public class ThreeCanvas(
}
//Set up light
options.useProperty(Canvas3DOptions::light) { lightConfig ->
options.useProperty(Canvas3DOptions::light, this) { lightConfig ->
//remove old light if present
getObjectByName(LIGHT_NAME)?.let { remove(it) }
//add new light
@ -87,7 +86,7 @@ public class ThreeCanvas(
translateX(spec.distance * sin(spec.zenith) * sin(spec.azimuth))
translateY(spec.distance * cos(spec.zenith))
translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth))
options.useProperty(Canvas3DOptions::layers) { selectedLayers ->
options.useProperty(Canvas3DOptions::layers, this) { selectedLayers ->
(0..31).forEach {
if (it in selectedLayers) {
this@apply.layers.enable(it)
@ -98,7 +97,6 @@ public class ThreeCanvas(
}
}
public val camera: PerspectiveCamera = buildCamera(options.camera)
private var picked: Object3D? = null
@ -107,6 +105,30 @@ public class ThreeCanvas(
antialias = true
}.apply {
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 {
@ -291,5 +313,6 @@ 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"
}
}

View File

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