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.plus
import space.kscience.kmath.complex.Quaternion
import space.kscience.kmath.geometry.RotationOrder
import space.kscience.kmath.geometry.fromEuler
import space.kscience.kmath.geometry.radians
import space.kscience.kmath.geometry.*
import space.kscience.visionforge.*
import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY
import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY
@ -242,14 +240,13 @@ public var Solid.quaternion: Quaternion
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.scaleY: Number by float(Y_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(PointLightSource.serializer())
subclass(AxesSolid.serializer())
}
public val serializersModuleForSolids: SerializersModule = SerializersModule {

View File

@ -7,19 +7,19 @@ import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild
public sealed class StlVision: SolidBase<StlVision>()
public sealed class StlSolid: SolidBase<StlSolid>()
@Serializable
@SerialName("solid.stl.url")
public class StlUrlVision(public val url: String) : StlVision()
public class StlUrlSolid(public val url: String) : StlSolid()
@Serializable
@SerialName("solid.stl.binary")
public class StlBinaryVision(public val data: ByteArray) : StlVision()
public class StlBinarySolid(public val data: ByteArray) : StlSolid()
@VisionBuilder
public inline fun MutableVisionContainer<Solid>.stl(
url: String,
name: String? = null,
action: StlVision.() -> Unit = {},
): StlVision = StlUrlVision(url).apply(action).also { setChild(name, it) }
action: StlSolid.() -> Unit = {},
): 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.double
@Deprecated("Use separate axes object instead")
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)
@Suppress("DEPRECATION")
public companion object : SchemeSpec<AxesScheme>(::AxesScheme) {
public const val AXIS_SIZE: Double = 1000.0
public const val AXIS_WIDTH: Double = 3.0

View File

@ -59,6 +59,7 @@ public class CanvasSize : Scheme() {
}
public class Canvas3DOptions : Scheme() {
@Suppress("DEPRECATION")
public var axes: AxesScheme by spec(AxesScheme)
public var camera: CameraScheme by spec(CameraScheme)
public var controls: ControlsScheme by spec(ControlsScheme)
@ -75,6 +76,7 @@ public class Canvas3DOptions : Scheme() {
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
override val descriptor: MetaDescriptor by lazy {
MetaDescriptor {
@Suppress("DEPRECATION")
scheme(Canvas3DOptions::axes, AxesScheme)
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
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() {
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 {
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 fun pick(): Object3D? {
@ -267,6 +257,7 @@ public class ThreeCanvas(
}
three.context.launch {
val object3D = three.buildObject3D(vision)
object3D.name = "@root"
scene.add(object3D)
root = object3D

View File

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

View File

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

View File

@ -7,11 +7,13 @@ import kotlin.reflect.KClass
public object ThreeSmartLineFactory : ThreeFactory<PolyLine> {
override val type: KClass<in PolyLine> get() = PolyLine::class
override suspend fun build(three: ThreePlugin, vision: PolyLine, observe: Boolean): Object3D {
return if (vision.thickness == 1.0) {
ThreeLineFactory.build(three, vision, observe)
} else {
ThreeMeshLineFactory.build(three, vision, observe)
}
override suspend fun build(
three: ThreePlugin,
vision: PolyLine,
observe: Boolean,
): Object3D = if (vision.thickness == 1.0) {
ThreeLineFactory.build(three, vision, observe)
} else {
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.Int8Array
import space.kscience.visionforge.solid.StlBinaryVision
import space.kscience.visionforge.solid.StlUrlVision
import space.kscience.visionforge.solid.StlVision
import space.kscience.visionforge.solid.StlBinarySolid
import space.kscience.visionforge.solid.StlSolid
import space.kscience.visionforge.solid.StlUrlSolid
import three.core.BufferGeometry
import three.external.loaders.STLLoader
import kotlin.coroutines.resume
@ -13,15 +13,15 @@ import kotlin.coroutines.suspendCoroutine
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 {
requestHeader = listOf("Access-Control-Allow-Origin: *")
}
override suspend fun buildGeometry(obj: StlVision): BufferGeometry = when (obj) {
is StlBinaryVision -> loader.parse(obj.data)
is StlUrlVision -> suspendCoroutine { continuation ->
override suspend fun buildGeometry(obj: StlSolid): BufferGeometry = when (obj) {
is StlBinarySolid -> loader.parse(obj.data)
is StlUrlSolid -> suspendCoroutine { continuation ->
loader.load(
url = obj.url,
onLoad = {