From 2b70afdb869a7f812762d133877fea9f65ea1a81 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 4 Jun 2023 20:58:03 +0300 Subject: [PATCH] Suspended renderers and property change listeners --- demo/js-playground/build.gradle.kts | 2 +- demo/solid-showcase/build.gradle.kts | 14 +++---- .../kscience/visionforge/solid/demo/demo.kt | 4 ++ .../space/kscience/visionforge/Vision.kt | 2 +- .../kscience/visionforge/solid/StlVision.kt | 25 ++++++++++++ .../solid/three/ThreeBoxFactory.kt | 2 +- .../solid/three/ThreeConeFactory.kt | 2 +- .../solid/three/ThreeConvexFactory.kt | 2 +- .../visionforge/solid/three/ThreeFactory.kt | 2 +- .../visionforge/solid/three/ThreeJsVision.kt | 13 ------- .../solid/three/ThreeMeshFactory.kt | 3 +- .../visionforge/solid/three/ThreePlugin.kt | 1 + .../solid/three/ThreeSphereFactory.kt | 2 +- .../solid/three/ThreeStlFactory.kt | 38 +++++++++++++++++++ .../three/external/loaders/STLLoader.kt | 13 +++++-- 15 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/StlVision.kt create mode 100644 visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeStlFactory.kt diff --git a/demo/js-playground/build.gradle.kts b/demo/js-playground/build.gradle.kts index 603b921a..94835588 100644 --- a/demo/js-playground/build.gradle.kts +++ b/demo/js-playground/build.gradle.kts @@ -8,7 +8,7 @@ kscience{ kotlin{ explicitApi = null - js(IR){ + js{ useCommonJs() browser { binaries.executable() diff --git a/demo/solid-showcase/build.gradle.kts b/demo/solid-showcase/build.gradle.kts index 8f1c370b..05a02260 100644 --- a/demo/solid-showcase/build.gradle.kts +++ b/demo/solid-showcase/build.gradle.kts @@ -1,14 +1,14 @@ plugins { id("space.kscience.gradle.mpp") - application +// application } kscience { useCoroutines() - jvm { - withJava() + jvm() + js{ + binaries.executable() } - js() dependencies { implementation(projects.visionforgeSolid) implementation(projects.visionforgeGdml) @@ -21,6 +21,6 @@ kscience { kotlin.explicitApi = null -application { - mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt") -} \ No newline at end of file +//application { +// mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt") +//} \ No newline at end of file diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index 5fdec43b..6f2cb61f 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -150,6 +150,10 @@ fun VisionLayout.showcase() { z = 26 } } + + demo("STL", "STL loaded from URL"){ + stl("https://ozeki.hu/attachments/116/Menger_sponge_sample.stl") + } } fun VisionLayout.showcaseCSG() { diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt index aa698409..ce5f588f 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt @@ -67,7 +67,7 @@ public var Vision.visible: Boolean? */ public fun Vision.onPropertyChange( scope: CoroutineScope? = manager?.context, - callback: (Name) -> Unit + callback: suspend (Name) -> Unit ): Job = properties.changes.onEach { callback(it) }.launchIn(scope ?: error("Orphan Vision can't observe properties")) \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/StlVision.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/StlVision.kt new file mode 100644 index 00000000..8c975c47 --- /dev/null +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/StlVision.kt @@ -0,0 +1,25 @@ +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 sealed class StlVision: SolidBase() + +@Serializable +@SerialName("solid.stl.url") +public class StlUrlVision(public val url: String) : StlVision() + +@Serializable +@SerialName("solid.stl.binary") +public class StlBinaryVision(public val data: ByteArray) : StlVision() + +@VisionBuilder +public inline fun MutableVisionContainer.stl( + url: String, + name: String? = null, + action: StlVision.() -> Unit = {}, +): StlVision = StlUrlVision(url).apply(action).also { setChild(name, it) } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt index 21c52198..d6840f75 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeBoxFactory.kt @@ -5,7 +5,7 @@ import space.kscience.visionforge.solid.detail import three.geometries.BoxGeometry public object ThreeBoxFactory : ThreeMeshFactory(Box::class) { - override fun buildGeometry(obj: Box): BoxGeometry = + override suspend fun buildGeometry(obj: Box): BoxGeometry = obj.detail?.let { detail -> BoxGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail) } ?: BoxGeometry(obj.xSize, obj.ySize, obj.zSize) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt index 6b2db845..bcc147c7 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConeFactory.kt @@ -8,7 +8,7 @@ import kotlin.math.PI import kotlin.math.pow public object ThreeConeFactory : ThreeMeshFactory(ConeSegment::class) { - override fun buildGeometry(obj: ConeSegment): BufferGeometry { + override suspend fun buildGeometry(obj: ConeSegment): BufferGeometry { val cylinder = obj.detail?.let { val segments = it.toDouble().pow(0.5).toInt() CylinderGeometry( diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConvexFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConvexFactory.kt index 83115b13..b772c1bf 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConvexFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeConvexFactory.kt @@ -4,7 +4,7 @@ import space.kscience.visionforge.solid.Convex import three.external.geometries.ConvexBufferGeometry public object ThreeConvexFactory : ThreeMeshFactory(Convex::class) { - override fun buildGeometry(obj: Convex): ConvexBufferGeometry { + override suspend fun buildGeometry(obj: Convex): ConvexBufferGeometry { val vectors = obj.points.map { it.toVector() }.toTypedArray() return ConvexBufferGeometry(vectors) } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt index 7c87a0f3..dcf94717 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt @@ -84,7 +84,7 @@ public fun Object3D.updateProperty(source: Vision, propertyName: Name) { * Generic factory for elements which provide inside geometry builder */ public object ThreeShapeFactory : ThreeMeshFactory(GeometrySolid::class) { - override fun buildGeometry(obj: GeometrySolid): BufferGeometry = ThreeGeometryBuilder().apply { + override suspend fun buildGeometry(obj: GeometrySolid): BufferGeometry = ThreeGeometryBuilder().apply { obj.toGeometry(this) }.build() } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeJsVision.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeJsVision.kt index dcb328a1..6f6e166a 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeJsVision.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeJsVision.kt @@ -1,6 +1,5 @@ package space.kscience.visionforge.solid.three -import org.w3c.dom.url.URL import space.kscience.visionforge.solid.SolidBase import three.core.Object3D @@ -10,15 +9,3 @@ import three.core.Object3D public abstract class ThreeJsVision : SolidBase() { public abstract suspend fun render(three: ThreePlugin): Object3D } - -public class ThreeStlVision(val url: URL): ThreeJsVision(){ - override suspend fun render(three: ThreePlugin): Object3D { -// suspendCoroutine { -// -// } -// STLLoader() - - TODO() - } - -} diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt index ab067326..06224bbb 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt @@ -21,10 +21,11 @@ import kotlin.reflect.KClass public abstract class ThreeMeshFactory( override val type: KClass, ) : ThreeFactory { + /** * Build a geometry for an object */ - public abstract fun buildGeometry(obj: T): BufferGeometry + public abstract suspend fun buildGeometry(obj: T): BufferGeometry override suspend fun build(three: ThreePlugin, vision: T, observe: Boolean): Mesh { val geometry = buildGeometry(vision) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index 17249648..e2af8c30 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -37,6 +37,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory objectFactories[PointLightSource::class] = ThreePointLightFactory + objectFactories[StlVision::class] = ThreeStlFactory } @Suppress("UNCHECKED_CAST") diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt index d738c073..9753f9ad 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeSphereFactory.kt @@ -6,7 +6,7 @@ import three.core.BufferGeometry import three.geometries.SphereGeometry public object ThreeSphereFactory : ThreeMeshFactory(Sphere::class) { - override fun buildGeometry(obj: Sphere): BufferGeometry { + override suspend fun buildGeometry(obj: Sphere): BufferGeometry { return obj.detail?.let { detail -> SphereGeometry( radius = obj.radius, diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeStlFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeStlFactory.kt new file mode 100644 index 00000000..d21389aa --- /dev/null +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeStlFactory.kt @@ -0,0 +1,38 @@ +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 three.core.BufferGeometry +import three.external.loaders.STLLoader +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +fun ArrayBuffer.toByteArray(): ByteArray = Int8Array(this).unsafeCast() + +public object ThreeStlFactory : ThreeMeshFactory(StlVision::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 -> + loader.load( + url = obj.url, + onLoad = { + continuation.resume(it) + }, + onError = { + continuation.resumeWithException(RuntimeException("Failed to load STL object from ${obj.url}")) + } + ) + } + } + + +} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/three/external/loaders/STLLoader.kt b/visionforge-threejs/src/main/kotlin/three/external/loaders/STLLoader.kt index 5fa253ab..e3e6133f 100644 --- a/visionforge-threejs/src/main/kotlin/three/external/loaders/STLLoader.kt +++ b/visionforge-threejs/src/main/kotlin/three/external/loaders/STLLoader.kt @@ -22,17 +22,19 @@ * THE SOFTWARE. */ -@file:JsModule("three") +@file:JsModule("three/examples/jsm/loaders/STLLoader.js") @file:JsNonModule package three.external.loaders +import org.khronos.webgl.ArrayBuffer import org.w3c.xhr.XMLHttpRequest import three.core.BufferGeometry -import three.core.Object3D external class STLLoader { + var requestHeader: List + fun load( url: String, onLoad: (BufferGeometry) -> Unit, @@ -40,7 +42,10 @@ external class STLLoader { onError: () -> Unit = definedExternally ) - fun parse(data: String): Object3D - fun parse(data: ByteArray): Object3D + fun parse(data: String): BufferGeometry + + fun parse(data: ByteArray): BufferGeometry + + fun parse(data: ArrayBuffer): BufferGeometry } \ No newline at end of file