Make Axes a separate object

This commit is contained in:
Alexander Nozik 2023-06-06 18:43:18 +03:00
parent 2b70afdb86
commit 20851baaf5
13 changed files with 112 additions and 45 deletions

View File

@ -0,0 +1,22 @@
package space.kscience.visionforge.examples
import space.kscience.kmath.geometry.Euclidean3DSpace
import space.kscience.kmath.geometry.radians
import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.solid.*
import kotlin.math.PI
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
vision("canvas") {
requirePlugin(Solids)
solid {
axes(100, "root-axes")
solidGroup("group") {
z = 100
rotate((PI / 4).radians, Euclidean3DSpace.vector(1, 1, 1))
axes(100, "local-axes")
box(50, 50, 50, "box")
}
}
}
}

View File

@ -0,0 +1,26 @@
package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild
public abstract class MiscSolid: SolidBase<MiscSolid>()
@Serializable
@SerialName("solid.axes")
public class AxesSolid(public val size: Double): MiscSolid(){
public companion object{
public const val AXES_NAME: String = "@xes"
}
}
@VisionBuilder
public fun MutableVisionContainer<Solid>.axes(
size: Number,
name: String = "@axes",
block: AxesSolid.() -> Unit = {},
): AxesSolid = AxesSolid(size.toDouble()).apply(block).also {
setChild(name, it)
}

View File

@ -9,9 +9,7 @@ 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.names.plus
import space.kscience.kmath.complex.Quaternion import space.kscience.kmath.complex.Quaternion
import space.kscience.kmath.geometry.RotationOrder import space.kscience.kmath.geometry.*
import space.kscience.kmath.geometry.fromEuler
import space.kscience.kmath.geometry.radians
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY
import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY
@ -242,14 +240,13 @@ public var Solid.quaternion: Quaternion
quaternionValue = value quaternionValue = value
} }
//public var Solid.quaternion: Quaternion?
// get() = meta[Solid::quaternion.name]?.value?.doubleArray?.let { Quaternion(it) }
// set(value) {
// meta[Solid::quaternion.name] = value?.values?.asValue()
// }
public var Solid.scaleX: Number by float(X_SCALE_KEY, 1f) public var Solid.scaleX: Number by float(X_SCALE_KEY, 1f)
public var Solid.scaleY: Number by float(Y_SCALE_KEY, 1f) public var Solid.scaleY: Number by float(Y_SCALE_KEY, 1f)
public var Solid.scaleZ: Number by float(Z_SCALE_KEY, 1f) public var Solid.scaleZ: Number by float(Z_SCALE_KEY, 1f)
/**
* Add rotation with given [angle] relative to given [axis]
*/
public fun Solid.rotate(angle: Angle, axis: DoubleVector3D) {
quaternion = Quaternion.fromRotation(angle, axis)
}

View File

@ -48,6 +48,8 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Sol
subclass(AmbientLightSource.serializer()) subclass(AmbientLightSource.serializer())
subclass(PointLightSource.serializer()) subclass(PointLightSource.serializer())
subclass(AxesSolid.serializer())
} }
public val serializersModuleForSolids: SerializersModule = SerializersModule { public val serializersModuleForSolids: SerializersModule = SerializersModule {

View File

@ -7,19 +7,19 @@ import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild import space.kscience.visionforge.setChild
public sealed class StlVision: SolidBase<StlVision>() public sealed class StlSolid: SolidBase<StlSolid>()
@Serializable @Serializable
@SerialName("solid.stl.url") @SerialName("solid.stl.url")
public class StlUrlVision(public val url: String) : StlVision() public class StlUrlSolid(public val url: String) : StlSolid()
@Serializable @Serializable
@SerialName("solid.stl.binary") @SerialName("solid.stl.binary")
public class StlBinaryVision(public val data: ByteArray) : StlVision() public class StlBinarySolid(public val data: ByteArray) : StlSolid()
@VisionBuilder @VisionBuilder
public inline fun MutableVisionContainer<Solid>.stl( public inline fun MutableVisionContainer<Solid>.stl(
url: String, url: String,
name: String? = null, name: String? = null,
action: StlVision.() -> Unit = {}, action: StlSolid.() -> Unit = {},
): StlVision = StlUrlVision(url).apply(action).also { setChild(name, it) } ): StlSolid = StlUrlSolid(url).apply(action).also { setChild(name, it) }

View File

@ -7,11 +7,13 @@ 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
@Deprecated("Use separate axes object instead")
public class AxesScheme : 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)
@Suppress("DEPRECATION")
public companion object : SchemeSpec<AxesScheme>(::AxesScheme) { 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

View File

@ -59,6 +59,7 @@ public class CanvasSize : Scheme() {
} }
public class Canvas3DOptions : Scheme() { public class Canvas3DOptions : Scheme() {
@Suppress("DEPRECATION")
public var axes: AxesScheme by spec(AxesScheme) public var axes: AxesScheme by spec(AxesScheme)
public var camera: CameraScheme by spec(CameraScheme) public var camera: CameraScheme by spec(CameraScheme)
public var controls: ControlsScheme by spec(ControlsScheme) public var controls: ControlsScheme by spec(ControlsScheme)
@ -75,6 +76,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 {
@Suppress("DEPRECATION")
scheme(Canvas3DOptions::axes, AxesScheme) scheme(Canvas3DOptions::axes, AxesScheme)
value(Canvas3DOptions::layers) { value(Canvas3DOptions::layers) {

View File

@ -0,0 +1,22 @@
package space.kscience.visionforge.solid.three
import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.AxesSolid
import three.helpers.AxesHelper
import kotlin.reflect.KClass
public object ThreeAxesFactory : ThreeFactory<AxesSolid> {
override val type: KClass<in AxesSolid> get() = AxesSolid::class
override suspend fun build(three: ThreePlugin, vision: AxesSolid, observe: Boolean): AxesHelper {
val res = AxesHelper(vision.size.toInt())
if (observe) {
vision.onPropertyChange(three.context) { propertyName ->
res.updateProperty(vision, propertyName)
}
}
return res
}
}

View File

@ -57,16 +57,6 @@ public class ThreeCanvas(
axesObject.name = AXES_NAME axesObject.name = AXES_NAME
add(axesObject) 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)
// }
} }
@ -110,7 +100,7 @@ public class ThreeCanvas(
} }
/** /**
* Force camera aspect ration and renderer size recalculation * Force camera aspect ratio and renderer size recalculation
*/ */
private fun updateSize() { private fun updateSize() {
val width = element.clientWidth val width = element.clientWidth
@ -202,7 +192,7 @@ public class ThreeCanvas(
} }
/** /**
* Resolve full name of the object relative to the global root * Resolve the full name of the object relative to the global root
*/ */
private fun Object3D.fullName(): Name { private fun Object3D.fullName(): Name {
if (root == null) error("Can't resolve element name without the root") if (root == null) error("Can't resolve element name without the root")
@ -213,7 +203,7 @@ public class ThreeCanvas(
} }
} }
//find first non-static parent in this object ancestry //find the first non-static parent in this object ancestry
private tailrec fun Object3D.upTrace(): Object3D? = if (!name.startsWith("@")) this else parent?.upTrace() private tailrec fun Object3D.upTrace(): Object3D? = if (!name.startsWith("@")) this else parent?.upTrace()
private fun pick(): Object3D? { private fun pick(): Object3D? {
@ -267,6 +257,7 @@ public class ThreeCanvas(
} }
three.context.launch { three.context.launch {
val object3D = three.buildObject3D(vision) val object3D = three.buildObject3D(vision)
object3D.name = "@root" object3D.name = "@root"
scene.add(object3D) scene.add(object3D)
root = object3D root = object3D

View File

@ -37,7 +37,7 @@ public interface ThreeFactory<in T : Vision> {
* Update position, rotation and visibility * Update position, rotation and visibility
*/ */
public fun Object3D.updatePosition(vision: Vision) { public fun Object3D.updatePosition(vision: Vision) {
visible = vision.visible ?: true // visible = vision.visible ?: true
if (vision is Solid) { if (vision is Solid) {
position.set(vision.x, vision.y, vision.z) position.set(vision.x, vision.y, vision.z)

View File

@ -37,7 +37,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory
objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory
objectFactories[PointLightSource::class] = ThreePointLightFactory objectFactories[PointLightSource::class] = ThreePointLightFactory
objectFactories[StlVision::class] = ThreeStlFactory objectFactories[StlSolid::class] = ThreeStlFactory
objectFactories[AxesSolid::class] = ThreeAxesFactory
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")

View File

@ -7,11 +7,13 @@ import kotlin.reflect.KClass
public object ThreeSmartLineFactory : ThreeFactory<PolyLine> { public object ThreeSmartLineFactory : ThreeFactory<PolyLine> {
override val type: KClass<in PolyLine> get() = PolyLine::class override val type: KClass<in PolyLine> get() = PolyLine::class
override suspend fun build(three: ThreePlugin, vision: PolyLine, observe: Boolean): Object3D { override suspend fun build(
return if (vision.thickness == 1.0) { three: ThreePlugin,
vision: PolyLine,
observe: Boolean,
): Object3D = if (vision.thickness == 1.0) {
ThreeLineFactory.build(three, vision, observe) ThreeLineFactory.build(three, vision, observe)
} else { } else {
ThreeMeshLineFactory.build(three, vision, observe) ThreeMeshLineFactory.build(three, vision, observe)
} }
}
} }

View File

@ -2,9 +2,9 @@ package space.kscience.visionforge.solid.three
import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.ArrayBuffer
import org.khronos.webgl.Int8Array import org.khronos.webgl.Int8Array
import space.kscience.visionforge.solid.StlBinaryVision import space.kscience.visionforge.solid.StlBinarySolid
import space.kscience.visionforge.solid.StlUrlVision import space.kscience.visionforge.solid.StlSolid
import space.kscience.visionforge.solid.StlVision import space.kscience.visionforge.solid.StlUrlSolid
import three.core.BufferGeometry import three.core.BufferGeometry
import three.external.loaders.STLLoader import three.external.loaders.STLLoader
import kotlin.coroutines.resume import kotlin.coroutines.resume
@ -13,15 +13,15 @@ import kotlin.coroutines.suspendCoroutine
fun ArrayBuffer.toByteArray(): ByteArray = Int8Array(this).unsafeCast<ByteArray>() fun ArrayBuffer.toByteArray(): ByteArray = Int8Array(this).unsafeCast<ByteArray>()
public object ThreeStlFactory : ThreeMeshFactory<StlVision>(StlVision::class) { public object ThreeStlFactory : ThreeMeshFactory<StlSolid>(StlSolid::class) {
private val loader = STLLoader().apply { private val loader = STLLoader().apply {
requestHeader = listOf("Access-Control-Allow-Origin: *") requestHeader = listOf("Access-Control-Allow-Origin: *")
} }
override suspend fun buildGeometry(obj: StlVision): BufferGeometry = when (obj) { override suspend fun buildGeometry(obj: StlSolid): BufferGeometry = when (obj) {
is StlBinaryVision -> loader.parse(obj.data) is StlBinarySolid -> loader.parse(obj.data)
is StlUrlVision -> suspendCoroutine { continuation -> is StlUrlSolid -> suspendCoroutine { continuation ->
loader.load( loader.load(
url = obj.url, url = obj.url,
onLoad = { onLoad = {