Merge branch 'dev' into beta/1.9.0
# Conflicts: # jupyter/visionforge-jupyter-gdml/build.gradle.kts # visionforge-jupyter/build.gradle.kts # visionforge-threejs/src/jsMain/kotlin/space/kscience/visionforge/solid/three/ThreeAxesFactory.kt # visionforge-threejs/src/jsMain/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt # visionforge-threejs/src/jsMain/kotlin/space/kscience/visionforge/solid/three/ThreeStlFactory.kt
This commit is contained in:
commit
20fc81305c
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -8,15 +8,15 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.5.3
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2.5.0
|
||||
uses: actions/setup-java@v3.12.0
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: liberica
|
||||
- name: execute build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/gradle-build-action@v2.7.1
|
||||
with:
|
||||
arguments: build
|
||||
|
@ -6,6 +6,7 @@
|
||||
- MeshLine for thick lines
|
||||
|
||||
### Changed
|
||||
- Color accessor property is now `colorProperty`. Color uses `invoke` instead of `set`
|
||||
- API update for server and pages
|
||||
- Edges moved to solids module for easier construction
|
||||
- Visions **must** be rooted in order to subscribe to updates.
|
||||
@ -20,6 +21,7 @@
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
- Jupyter integration for IDEA and Jupyter lab.
|
||||
|
||||
### Security
|
||||
|
||||
|
@ -13,7 +13,7 @@ val fxVersion by extra("11")
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.3.0-dev-9"
|
||||
version = "0.3.0-dev-13"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
@ -4,12 +4,18 @@ import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.names.withIndex
|
||||
import space.kscience.kmath.complex.Quaternion
|
||||
import space.kscience.kmath.geometry.fromRotationMatrix
|
||||
import space.kscience.kmath.linear.VirtualMatrix
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.isEmpty
|
||||
import space.kscience.visionforge.set
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
||||
import kotlin.math.*
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
private val volumesName = Name.EMPTY //"volumes".asName()
|
||||
|
||||
@ -27,17 +33,15 @@ private data class RootToSolidContext(
|
||||
val colorCache: MutableMap<Meta, String> = mutableMapOf(),
|
||||
)
|
||||
|
||||
// converting to XYZ to Tait–Bryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
|
||||
// apply rotation from a matrix
|
||||
private fun Solid.rotate(rot: DoubleArray) {
|
||||
val xAngle = atan2(-rot[5], rot[8])
|
||||
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
|
||||
val zAngle = atan2(-rot[1], rot[0])
|
||||
rotation = Point3D(xAngle, yAngle, zAngle)
|
||||
val matrix = VirtualMatrix(3, 3) { i, j -> rot[i * 3 + j] }
|
||||
quaternion = Quaternion.fromRotationMatrix(matrix)
|
||||
}
|
||||
|
||||
private fun Solid.translate(trans: DoubleArray) {
|
||||
val (x, y, z) = trans
|
||||
position = Point3D(x, y, z)
|
||||
position = Float32Vector3D(x, y, z)
|
||||
}
|
||||
|
||||
private fun Solid.useMatrix(matrix: DGeoMatrix?) {
|
||||
@ -72,7 +76,7 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) {
|
||||
val fScale by matrix.meta.doubleArray()
|
||||
translate(fTranslation)
|
||||
rotate(fRotationMatrix)
|
||||
scale = Point3D(fScale[0], fScale[1], fScale[2])
|
||||
scale = Float32Vector3D(fScale[0], fScale[1], fScale[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,9 +212,9 @@ private fun SolidGroup.addShape(
|
||||
require(fNz > 1) { "The polyhedron geometry requires at least two planes" }
|
||||
val baseRadius = fRmax[0]
|
||||
shape {
|
||||
(0..fNedges).forEach {
|
||||
val phi = deltaphi * fNedges * it + startphi
|
||||
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
||||
(0..<fNedges).forEach {
|
||||
val phi = deltaphi / fNedges * it + startphi
|
||||
point(baseRadius * cos(phi), baseRadius * sin(phi))
|
||||
}
|
||||
}
|
||||
(0 until fNz).forEach { index ->
|
||||
@ -223,7 +227,7 @@ private fun SolidGroup.addShape(
|
||||
"TGeoShapeAssembly" -> {
|
||||
val fVolume by shape.dObject(::DGeoVolume)
|
||||
fVolume?.let { volume ->
|
||||
addRootVolume(volume, context, block = block)
|
||||
addRootVolume(volume, context, name = volume.fName.ifEmpty { null }, block = block)
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,14 +252,14 @@ private fun SolidGroup.addShape(
|
||||
|
||||
val fDz by shape.meta.double(0.0)
|
||||
//TODO check proper node order
|
||||
val node1 = Point3D(-fBl1, -fH1, -fDz)
|
||||
val node2 = Point3D(fBl1, -fH1, -fDz)
|
||||
val node3 = Point3D(fTl1, fH1, -fDz)
|
||||
val node4 = Point3D(-fTl1, fH1, -fDz)
|
||||
val node5 = Point3D(-fBl2, -fH2, fDz)
|
||||
val node6 = Point3D(fBl2, -fH2, fDz)
|
||||
val node7 = Point3D(fTl2, fH2, fDz)
|
||||
val node8 = Point3D(-fTl2, fH2, fDz)
|
||||
val node1 = Float32Vector3D(-fBl1, -fH1, -fDz)
|
||||
val node2 = Float32Vector3D(fBl1, -fH1, -fDz)
|
||||
val node3 = Float32Vector3D(fTl1, fH1, -fDz)
|
||||
val node4 = Float32Vector3D(-fTl1, fH1, -fDz)
|
||||
val node5 = Float32Vector3D(-fBl2, -fH2, fDz)
|
||||
val node6 = Float32Vector3D(fBl2, -fH2, fDz)
|
||||
val node7 = Float32Vector3D(fTl2, fH2, fDz)
|
||||
val node8 = Float32Vector3D(-fTl2, fH2, fDz)
|
||||
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
|
||||
}
|
||||
|
||||
@ -264,7 +268,7 @@ private fun SolidGroup.addShape(
|
||||
val fScale by shape.dObject(::DGeoScale)
|
||||
fShape?.let { scaledShape ->
|
||||
solidGroup(name?.let { Name.parse(it) }) {
|
||||
scale = Point3D(fScale?.x ?: 1.0, fScale?.y ?: 1.0, fScale?.z ?: 1.0)
|
||||
scale = Float32Vector3D(fScale?.x ?: 1.0, fScale?.y ?: 1.0, fScale?.z ?: 1.0)
|
||||
addShape(scaledShape, context)
|
||||
apply(block)
|
||||
}
|
||||
@ -328,7 +332,7 @@ private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid?
|
||||
group
|
||||
}.apply {
|
||||
volume.fMedium?.let { medium ->
|
||||
color.set(context.colorCache.getOrPut(medium.meta) { RootColors[11 + context.colorCache.size] })
|
||||
color(context.colorCache.getOrPut(medium.meta) { RootColors[11 + context.colorCache.size] })
|
||||
}
|
||||
|
||||
if (!context.ignoreRootColors) {
|
||||
@ -348,29 +352,29 @@ private fun SolidGroup.addRootVolume(
|
||||
cache: Boolean = true,
|
||||
block: Solid.() -> Unit = {},
|
||||
) {
|
||||
|
||||
val combinedName = if (volume.fName.isEmpty()) {
|
||||
name
|
||||
} else if (name == null) {
|
||||
volume.fName
|
||||
} else {
|
||||
"${name}_${volume.fName}"
|
||||
val combinedName = name?.parseAsName()?.let {
|
||||
// this fix is required to work around malformed root files with duplicated node names
|
||||
if (get(it) != null) {
|
||||
it.withIndex(volume.hashCode().toString(16))
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
if (!cache) {
|
||||
val group = buildVolume(volume, context)?.apply(block)
|
||||
setChild(combinedName?.let { Name.parse(it) }, group)
|
||||
val group = buildVolume(volume, context)?.apply(block) ?: return
|
||||
setChild(combinedName, group)
|
||||
} else {
|
||||
val templateName = volumesName + volume.name
|
||||
val existing = getPrototype(templateName)
|
||||
val existing = context.prototypeHolder.getPrototype(templateName)
|
||||
if (existing == null) {
|
||||
context.prototypeHolder.prototypes {
|
||||
val group = buildVolume(volume, context)
|
||||
val group = buildVolume(volume, context) ?: return@prototypes
|
||||
setChild(templateName, group)
|
||||
}
|
||||
}
|
||||
|
||||
ref(templateName, name).apply(block)
|
||||
ref(templateName, combinedName).apply(block)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,12 @@ private fun Solid.rotate(rot: DoubleArray) {
|
||||
val xAngle = atan2(-rot[5], rot[8])
|
||||
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
|
||||
val zAngle = atan2(-rot[1], rot[0])
|
||||
rotation = Point3D(xAngle, yAngle, zAngle)
|
||||
rotation = Float32Vector3D(xAngle, yAngle, zAngle)
|
||||
}
|
||||
|
||||
private fun Solid.translate(trans: DoubleArray) {
|
||||
val (x, y, z) = trans
|
||||
position = Point3D(x, y, z)
|
||||
position = Float32Vector3D(x, y, z)
|
||||
}
|
||||
|
||||
private fun Solid.useMatrix(matrix: TGeoMatrix?) {
|
||||
@ -52,7 +52,7 @@ private fun Solid.useMatrix(matrix: TGeoMatrix?) {
|
||||
translate(matrix.fTranslation)
|
||||
rotate(matrix.fRotationMatrix)
|
||||
val (xScale, yScale, zScale) = matrix.fScale
|
||||
scale = Point3D(xScale, yScale, zScale)
|
||||
scale = Float32Vector3D(xScale, yScale, zScale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import space.kscience.visionforge.setAsRoot
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.set
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
|
||||
@ -53,7 +53,7 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
||||
console.info("Marking layers for file $name")
|
||||
markLayers()
|
||||
ambientLight {
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import space.kscience.visionforge.react.createRoot
|
||||
import space.kscience.visionforge.react.render
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.set
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
import space.kscience.visionforge.startApplication
|
||||
import styled.injectGlobal
|
||||
@ -49,7 +49,7 @@ private class GDMLDemoApp : Application {
|
||||
child(GDMLApp) {
|
||||
val vision = GdmlShowCase.cubes().toVision().apply {
|
||||
ambientLight {
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
|
||||
|
@ -7,7 +7,8 @@ kscience{
|
||||
}
|
||||
|
||||
kotlin{
|
||||
js(IR){
|
||||
explicitApi = null
|
||||
js{
|
||||
useCommonJs()
|
||||
browser {
|
||||
binaries.executable()
|
||||
|
@ -76,7 +76,7 @@ private class JsPlaygroundApp : Application {
|
||||
solids = playgroundContext.request(Solids)
|
||||
solid {
|
||||
ambientLight {
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
repeat(100) {
|
||||
sphere(5, name = "sphere[$it]") {
|
||||
@ -84,7 +84,7 @@ private class JsPlaygroundApp : Application {
|
||||
y = random.nextDouble(-300.0, 300.0)
|
||||
z = random.nextDouble(-300.0, 300.0)
|
||||
material {
|
||||
color.set(random.nextInt())
|
||||
color(random.nextInt())
|
||||
}
|
||||
detail = 16
|
||||
}
|
||||
|
@ -42,13 +42,13 @@ val GravityDemo = fc<DemoProps> { props ->
|
||||
solids = props.solids
|
||||
solid {
|
||||
pointLight(200, 200, 200, name = "light"){
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
ambientLight()
|
||||
|
||||
sphere(5.0, "ball") {
|
||||
detail = 16
|
||||
color.set("red")
|
||||
color("red")
|
||||
val h = 100.0
|
||||
y = h
|
||||
solids.context.launch {
|
||||
|
@ -40,6 +40,8 @@ kscience {
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.explicitApi = null
|
||||
|
||||
application {
|
||||
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.solid.Point3D
|
||||
import space.kscience.visionforge.solid.Float32Vector3D
|
||||
|
||||
typealias Track = List<Point3D>
|
||||
typealias Track = List<Float32Vector3D>
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -16,7 +16,7 @@ class Model(val manager: VisionManager) {
|
||||
|
||||
private fun MutableVisionContainer<Solid>.pixel(pixel: SC1) {
|
||||
val group = solidGroup(pixel.name) {
|
||||
position = Point3D(pixel.center.x, pixel.center.y, pixel.center.z)
|
||||
position = Float32Vector3D(pixel.center.x, pixel.center.y, pixel.center.z)
|
||||
box(pixel.xSize, pixel.ySize, pixel.zSize)
|
||||
label(pixel.name) {
|
||||
z = -Monitor.PIXEL_Z_SIZE / 2 - 5
|
||||
@ -39,7 +39,7 @@ class Model(val manager: VisionManager) {
|
||||
val root: SolidGroup = SolidGroup().apply {
|
||||
setAsRoot(this@Model.manager)
|
||||
material {
|
||||
color.set("darkgreen")
|
||||
color("darkgreen")
|
||||
}
|
||||
rotationX = PI / 2
|
||||
solidGroup("bottom") {
|
||||
@ -64,7 +64,7 @@ class Model(val manager: VisionManager) {
|
||||
|
||||
private fun highlight(pixel: String) {
|
||||
println("highlight $pixel")
|
||||
map[pixel]?.color.set("blue")
|
||||
map[pixel]?.color("blue")
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
@ -82,7 +82,7 @@ class Model(val manager: VisionManager) {
|
||||
}
|
||||
event.track?.let {
|
||||
tracks.polyline(*it.toTypedArray(), name = "track[${event.id}]") {
|
||||
color.set("red")
|
||||
color("red")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,21 +2,21 @@ package ru.mipt.npm.muon.monitor
|
||||
|
||||
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_XY_SIZE
|
||||
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE
|
||||
import space.kscience.visionforge.solid.Point3D
|
||||
import space.kscience.visionforge.solid.plus
|
||||
import space.kscience.visionforge.solid.Float32Euclidean3DSpace
|
||||
import space.kscience.visionforge.solid.Float32Vector3D
|
||||
|
||||
/**
|
||||
* A single pixel
|
||||
*/
|
||||
class SC1(
|
||||
val name: String,
|
||||
val center: Point3D,
|
||||
val center: Float32Vector3D,
|
||||
val xSize: Float = PIXEL_XY_SIZE, val ySize: Float = PIXEL_XY_SIZE, val zSize: Float = PIXEL_Z_SIZE,
|
||||
)
|
||||
|
||||
class SC16(
|
||||
val name: String,
|
||||
val center: Point3D,
|
||||
val center: Float32Vector3D,
|
||||
) {
|
||||
|
||||
/**
|
||||
@ -109,9 +109,9 @@ class SC16(
|
||||
|
||||
else -> throw Error()
|
||||
}
|
||||
val offset = Point3D(-y, x, 0)//rotateDetector(Point3D(x, y, 0.0));
|
||||
val offset = Float32Vector3D(-y, x, 0)//rotateDetector(Point3D(x, y, 0.0));
|
||||
val pixelName = "${name}_${index}"
|
||||
SC1(pixelName, offset + center)
|
||||
SC1(pixelName, with(Float32Euclidean3DSpace) { offset + center })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,7 +154,7 @@ object Monitor {
|
||||
val x = split[4].toDouble() - 500
|
||||
val y = split[5].toDouble() - 500
|
||||
val z = 180 - split[6].toDouble()
|
||||
SC16(detectorName, Point3D(x, y, z))
|
||||
SC16(detectorName, Float32Vector3D(x, y, z))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import space.kscience.visionforge.ring.tab
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.edges
|
||||
import space.kscience.visionforge.solid.set
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
@ -58,7 +58,7 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
||||
props.model.root.apply {
|
||||
edges()
|
||||
ambientLight{
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,7 @@ import io.ktor.server.application.install
|
||||
import io.ktor.server.application.log
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.http.content.resources
|
||||
import io.ktor.server.http.content.static
|
||||
import io.ktor.server.http.content.staticResources
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondText
|
||||
@ -53,9 +52,7 @@ fun Application.module(context: Context = Global) {
|
||||
status = HttpStatusCode.OK
|
||||
)
|
||||
}
|
||||
static("/") {
|
||||
resources()
|
||||
}
|
||||
staticResources("/", null)
|
||||
}
|
||||
try {
|
||||
Desktop.getDesktop().browse(URI("http://localhost:8080/index.html"))
|
||||
|
@ -5,7 +5,7 @@ import org.apache.commons.math3.geometry.euclidean.threed.Plane
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D
|
||||
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
||||
import ru.mipt.npm.muon.monitor.Monitor.GEOMETRY_TOLERANCE
|
||||
import space.kscience.visionforge.solid.Point3D
|
||||
import space.kscience.visionforge.solid.Float32Vector3D
|
||||
|
||||
/**
|
||||
* Created by darksnake on 11-May-16.
|
||||
@ -50,12 +50,12 @@ fun makeTrack(x: Double, y: Double, theta: Double, phi: Double): Line {
|
||||
)
|
||||
}
|
||||
|
||||
fun Vector3D.toPoint() = Point3D(x, y, z)
|
||||
fun Vector3D.toKMathVector() = Float32Vector3D(x, y, z)
|
||||
|
||||
fun Line.toPoints(): List<Point3D> {
|
||||
fun Line.toKMathVectors(): List<Float32Vector3D> {
|
||||
val basePoint = basePlane.intersection(this)
|
||||
val bottom = basePoint.subtract(2000.0, direction)
|
||||
val top = basePoint.add(2000.0, direction)
|
||||
return listOf(bottom.toPoint(), top.toPoint())
|
||||
return listOf(bottom.toKMathVector(), top.toKMathVector())
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ fun readEffs(): Map<String, Double> {
|
||||
|
||||
|
||||
fun buildEventByTrack(index: Int, track: Line, hitResolver: (Line) -> Collection<SC1> = defaultHitResolver): Event {
|
||||
return Event(index, track.toPoints(), hitResolver(track).map { it.name })
|
||||
return Event(index, track.toKMathVectors(), hitResolver(track).map { it.name })
|
||||
}
|
||||
|
||||
val defaultHitResolver: (Line) -> Collection<SC1> = { track: Line ->
|
||||
|
@ -47,12 +47,11 @@ kotlin {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(projects.visionforgeSolid)
|
||||
implementation(projects.visionforgeGdml)
|
||||
implementation(projects.visionforgePlotly)
|
||||
implementation(projects.visionforgeMarkdown)
|
||||
implementation(projects.visionforgeTables)
|
||||
implementation(projects.cernRootLoader)
|
||||
implementation(projects.jupyter)
|
||||
api(projects.visionforgeJupyter.visionforgeJupyterCommon)
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +65,8 @@ kotlin {
|
||||
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-server-cio:${spclibs.versions.ktor.get()}")
|
||||
implementation(projects.visionforgeGdml)
|
||||
implementation(projects.visionforgeServer)
|
||||
implementation(spclibs.logback.classic)
|
||||
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
||||
|
101
demo/playground/notebooks/common-demo.ipynb
Normal file
101
demo/playground/notebooks/common-demo.ipynb
Normal file
@ -0,0 +1,101 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"@file:Repository(\"*mavenLocal\")\n",
|
||||
"@file:Repository(\"https://repo.kotlin.link\")\n",
|
||||
"@file:Repository(\"https://maven.pkg.jetbrains.space/spc/p/sci/dev\")\n",
|
||||
"@file:DependsOn(\"space.kscience:visionforge-jupyter-common-jvm:0.3.0-dev-12\")\n",
|
||||
"//import space.kscience.visionforge.jupyter.JupyterCommonIntegration\n",
|
||||
"//\n",
|
||||
"//val integration = JupyterCommonIntegration()\n",
|
||||
"//USE(integration.getDefinitions(notebook).first())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vf.fragment {\n",
|
||||
" h1 { +\"AAA\" }\n",
|
||||
" vision {\n",
|
||||
" solid {\n",
|
||||
" ambientLight()\n",
|
||||
" box(100, 100, 200)\n",
|
||||
"\n",
|
||||
" sphere(100) {\n",
|
||||
" x = 300\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" vision {\n",
|
||||
" plotly {\n",
|
||||
" scatter {\n",
|
||||
" x(1, 2, 3, 1)\n",
|
||||
" y(1, 2, 3, 4)\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
},
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"Plotly.plot { \n",
|
||||
" scatter{\n",
|
||||
" x(1,2,3)\n",
|
||||
" y(1,2,3)\n",
|
||||
" }\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Kotlin",
|
||||
"language": "kotlin",
|
||||
"name": "kotlin"
|
||||
},
|
||||
"ktnbPluginMetadata": {
|
||||
"isAddProjectLibrariesToClasspath": false
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": "text/x-kotlin",
|
||||
"file_extension": ".kt",
|
||||
"mimetype": "text/x-kotlin",
|
||||
"name": "kotlin",
|
||||
"nbconvert_exporter": "",
|
||||
"pygments_lexer": "kotlin",
|
||||
"version": "1.8.20"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
@ -2,15 +2,11 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"tags": [],
|
||||
"pycharm": {
|
||||
"is_executing": true
|
||||
},
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-05-29T15:22:37.933397300Z",
|
||||
"start_time": "2023-05-29T15:22:37.913872100Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
@ -18,57 +14,23 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-05-29T15:22:50.486483300Z",
|
||||
"start_time": "2023-05-29T15:22:50.457485500Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Line_2.jupyter.kts (1:1 - 3) Unresolved reference: vf"
|
||||
]
|
||||
}
|
||||
],
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vf.startServer()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
},
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-05-29T15:22:51.410680600Z",
|
||||
"start_time": "2023-05-29T15:22:51.250779400Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Line_3.jupyter.kts (1:16 - 26) Unresolved reference: coroutines\n",
|
||||
"Line_3.jupyter.kts (4:1 - 7) Unresolved reference: Plotly\n",
|
||||
"Line_3.jupyter.kts (5:5 - 12) Unresolved reference: scatter\n",
|
||||
"Line_3.jupyter.kts (6:9 - 10) Unresolved reference: x\n",
|
||||
"Line_3.jupyter.kts (7:9 - 10) Unresolved reference: y\n",
|
||||
"Line_3.jupyter.kts (8:12 - 14) Unresolved reference: vf\n",
|
||||
"Line_3.jupyter.kts (9:13 - 15) Unresolved reference: vf\n",
|
||||
"Line_3.jupyter.kts (10:23 - 31) Unresolved reference: isActive\n",
|
||||
"Line_3.jupyter.kts (11:21 - 26) Unresolved reference: delay\n",
|
||||
"Line_3.jupyter.kts (12:21 - 22) Unresolved reference: y"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import kotlinx.coroutines.*\n",
|
||||
"import kotlin.random.Random\n",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.jupyter.VFNotebookPlugin
|
||||
import space.kscience.visionforge.jupyter.VFNotebookClient
|
||||
import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
@ -12,5 +12,5 @@ fun main() = runVisionClient {
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
plugin(TableVisionJsPlugin)
|
||||
plugin(VFNotebookPlugin)
|
||||
plugin(VFNotebookClient)
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.plotly.Plot
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.jupyter.VFIntegrationBase
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.asVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
@DFExperimental
|
||||
internal class VisionForgePlayGroundForJupyter : VFIntegrationBase(
|
||||
Context("VisionForge") {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
}.visionManager
|
||||
) {
|
||||
|
||||
override fun Builder.afterLoaded() {
|
||||
resources {
|
||||
js("VisionForge") {
|
||||
classPath("js/visionforge-playground.js")
|
||||
}
|
||||
}
|
||||
|
||||
import(
|
||||
"space.kscience.gdml.*",
|
||||
"space.kscience.plotly.*",
|
||||
"space.kscience.plotly.models.*",
|
||||
"space.kscience.visionforge.solid.*",
|
||||
)
|
||||
|
||||
|
||||
render<Gdml> { gdmlModel ->
|
||||
handler.produceHtml {
|
||||
vision { gdmlModel.toVision() }
|
||||
}
|
||||
}
|
||||
|
||||
render<Plot> { plot ->
|
||||
handler.produceHtml {
|
||||
vision { plot.asVision() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
102
demo/playground/src/jvmMain/kotlin/antenna.kt
Normal file
102
demo/playground/src/jvmMain/kotlin/antenna.kt
Normal file
@ -0,0 +1,102 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.dataforge.meta.configure
|
||||
import space.kscience.kmath.complex.Quaternion
|
||||
import space.kscience.kmath.complex.QuaternionField
|
||||
import space.kscience.kmath.complex.conjugate
|
||||
import space.kscience.kmath.geometry.*
|
||||
import space.kscience.visionforge.solid.*
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
fun main() = serve {
|
||||
|
||||
// val azimuth = 60.degrees
|
||||
// val inclination = 15.degrees
|
||||
|
||||
// val direction = with(QuaternionField) {
|
||||
// Quaternion.fromRotation(-azimuth, Euclidean3DSpace.zAxis) *
|
||||
// Quaternion.fromRotation(Angle.piDiv2 - inclination, Euclidean3DSpace.yAxis)
|
||||
// }
|
||||
|
||||
//val direction2 = Quaternion.fromEuler(Angle.zero, Angle.piDiv2 - inclination, -azimuth, RotationOrder.ZYX)
|
||||
|
||||
val target = Quaternion.fromEuler((-45).degrees, 45.degrees, Angle.zero, RotationOrder.XYZ)
|
||||
|
||||
|
||||
vision("canvas") {
|
||||
requirePlugin(Solids)
|
||||
|
||||
solid(options = {
|
||||
configure { "controls.enabled" put false }
|
||||
}) {
|
||||
rotationX = -PI / 2
|
||||
rotationZ = PI
|
||||
//axes(200)
|
||||
ambientLight()
|
||||
val platform = solidGroup("platform") {
|
||||
cylinder(50, 5, name = "base")
|
||||
solidGroup("frame") {
|
||||
z = 60
|
||||
|
||||
solidGroup("antenna") {
|
||||
axes(200)
|
||||
tube(40, 10, 30)
|
||||
sphereLayer(100, 95, theta = PI / 6) {
|
||||
z = 100
|
||||
rotationX = -PI / 2
|
||||
}
|
||||
cylinder(5, 30) {
|
||||
z = 15
|
||||
}
|
||||
|
||||
sphereLayer(101, 94, phi = PI / 32, theta = PI / 6) {
|
||||
z = 100
|
||||
rotationX = -PI / 2
|
||||
color("red")
|
||||
}
|
||||
|
||||
quaternion = target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val frame = platform["frame"] as SolidGroup
|
||||
|
||||
val antenna = frame["antenna"] as SolidGroup
|
||||
|
||||
val xPeriod = 5000 //ms
|
||||
val yPeriod = 7000 //ms
|
||||
|
||||
val incRot = Quaternion.fromRotation(30.degrees, Euclidean3DSpace.zAxis)
|
||||
|
||||
|
||||
val rotationJob = context.launch {
|
||||
var time: Long = 0L
|
||||
while (isActive) {
|
||||
with(QuaternionField) {
|
||||
delay(200)
|
||||
platform.quaternion = Quaternion.fromRotation(
|
||||
15.degrees * sin(time.toDouble() * 2 * PI / xPeriod),
|
||||
Euclidean3DSpace.xAxis
|
||||
) * Quaternion.fromRotation(
|
||||
15.degrees * cos(time * 2 * PI / yPeriod),
|
||||
Euclidean3DSpace.yAxis
|
||||
)
|
||||
|
||||
val qi = platform.quaternion * incRot
|
||||
|
||||
antenna.quaternion = qi.conjugate * incRot.conjugate * target
|
||||
|
||||
time += 200
|
||||
//antenna.quaternion = Quaternion.fromRotation(5.degrees, Euclidean3DSpace.zAxis) * antenna.quaternion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
demo/playground/src/jvmMain/kotlin/axes.kt
Normal file
22
demo/playground/src/jvmMain/kotlin/axes.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
demo/playground/src/jvmMain/kotlin/extruded.kt
Normal file
21
demo/playground/src/jvmMain/kotlin/extruded.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.extruded
|
||||
import space.kscience.visionforge.solid.polygon
|
||||
import space.kscience.visionforge.solid.solid
|
||||
|
||||
fun main() = makeVisionFile {
|
||||
vision("canvas") {
|
||||
solid {
|
||||
ambientLight()
|
||||
extruded("extruded") {
|
||||
shape{
|
||||
polygon(8, 100)
|
||||
layer(-30)
|
||||
layer(30)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.color
|
||||
import space.kscience.visionforge.solid.set
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.visible
|
||||
import java.nio.file.Path
|
||||
|
||||
@ -229,7 +229,7 @@ fun main() = makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceL
|
||||
visible = false
|
||||
}
|
||||
if(solid.name.startsWith("gas")){
|
||||
color.set("green")
|
||||
color("green")
|
||||
} else {
|
||||
//make all solids semi-transparent
|
||||
transparent()
|
||||
|
@ -19,7 +19,7 @@ fun main() = makeVisionFile(
|
||||
vision {
|
||||
solid {
|
||||
ambientLight {
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
repeat(100) {
|
||||
sphere(5, name = "sphere[$it]") {
|
||||
@ -27,7 +27,7 @@ fun main() = makeVisionFile(
|
||||
y = random.nextDouble(-300.0, 300.0)
|
||||
z = random.nextDouble(-300.0, 300.0)
|
||||
material {
|
||||
color.set(random.nextInt())
|
||||
color(random.nextInt())
|
||||
}
|
||||
detail = 16
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.set
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.solid
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlin.io.path.Path
|
||||
@ -26,7 +26,7 @@ private fun Meta.countTypes(): Sequence<String> = sequence {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val string = ZipInputStream(TGeoManager::class.java.getResourceAsStream("/root/BM@N_geometry.zip")!!).use {
|
||||
val string = ZipInputStream(TGeoManager::class.java.getResourceAsStream("/root/geometry_run_7-2076.zip")!!).use {
|
||||
it.nextEntry
|
||||
it.readAllBytes().decodeToString()
|
||||
}
|
||||
@ -44,9 +44,9 @@ fun main() {
|
||||
requirePlugin(Solids)
|
||||
solid {
|
||||
ambientLight {
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
rootGeo(geo,"BM@N", maxLayer = 3, ignoreRootColors = true).also {
|
||||
rootGeo(geo,"BM@N", ignoreRootColors = true).also {
|
||||
Path("data/BM@N.vf.json").writeText(Solids.encodeToString(it))
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,24 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.http.content.staticResources
|
||||
import io.ktor.server.routing.routing
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.visionforge.html.*
|
||||
import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.server.close
|
||||
import space.kscience.visionforge.server.openInBrowser
|
||||
import space.kscience.visionforge.server.visionPage
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.tables.TableVisionPlugin
|
||||
import space.kscience.visionforge.visionManager
|
||||
import java.awt.Desktop
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
public fun makeVisionFile(
|
||||
path: Path? = null,
|
||||
title: String = "VisionForge page",
|
||||
@ -26,6 +39,45 @@ public fun makeVisionFile(
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
}
|
||||
|
||||
public fun serve(
|
||||
title: String = "VisionForge page",
|
||||
show: Boolean = true,
|
||||
content: HtmlVisionFragment,
|
||||
) {
|
||||
val context = Context("playground") {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
plugin(TableVisionPlugin)
|
||||
}
|
||||
|
||||
val server = embeddedServer(CIO, port = 7779) {
|
||||
routing {
|
||||
staticResources("", null, null)
|
||||
}
|
||||
|
||||
visionPage(
|
||||
context.visionManager,
|
||||
VisionPage.scriptHeader("js/visionforge-playground.js") {
|
||||
defer = true
|
||||
},
|
||||
VisionPage.title(title),
|
||||
visionFragment = content
|
||||
)
|
||||
}.start(false)
|
||||
|
||||
if (show) {
|
||||
server.openInBrowser()
|
||||
}
|
||||
|
||||
println("Enter 'exit' to close server")
|
||||
while (readlnOrNull() != "exit") {
|
||||
//
|
||||
}
|
||||
|
||||
server.close()
|
||||
}
|
||||
|
||||
//@DFExperimental
|
||||
//public fun Context.makeVisionFile(
|
||||
// vision: Vision,
|
||||
|
@ -10,23 +10,23 @@ fun main() = makeVisionFile {
|
||||
ambientLight()
|
||||
box(100.0, 100.0, 100.0) {
|
||||
z = -110.0
|
||||
color.set("teal")
|
||||
color("teal")
|
||||
}
|
||||
sphere(50.0) {
|
||||
x = 110
|
||||
detail = 16
|
||||
color.set("red")
|
||||
color("red")
|
||||
}
|
||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||
y = 110
|
||||
detail = 16
|
||||
rotationX = PI / 4
|
||||
color.set("blue")
|
||||
color("blue")
|
||||
}
|
||||
sphereLayer(50, 40, theta = PI / 2) {
|
||||
rotationX = -PI * 3 / 4
|
||||
z = 110
|
||||
color.set(Colors.pink)
|
||||
color(Colors.pink)
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,8 +2,8 @@ package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.box
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.material
|
||||
import space.kscience.visionforge.solid.set
|
||||
import space.kscience.visionforge.solid.solid
|
||||
|
||||
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
@ -11,7 +11,7 @@ fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
solid {
|
||||
box(100, 100, 100)
|
||||
material {
|
||||
emissiveColor.set("red")
|
||||
emissiveColor("red")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -8,13 +8,15 @@ kscience {
|
||||
// useSerialization {
|
||||
// json()
|
||||
// }
|
||||
useKtor()
|
||||
dependencies{
|
||||
implementation("io.ktor:ktor-server-cio")
|
||||
implementation(projects.visionforgeThreejs.visionforgeThreejsServer)
|
||||
implementation("ch.qos.logback:logback-classic:1.4.5")
|
||||
implementation(spclibs.logback.classic)
|
||||
}
|
||||
}
|
||||
|
||||
group = "ru.mipt.npm"
|
||||
group = "center.sciprog"
|
||||
|
||||
application {
|
||||
mainClass.set("ru.mipt.npm.sat.SatServerKt")
|
||||
|
@ -15,7 +15,7 @@ internal fun Solids.visionOfSatellite(
|
||||
ySegmentSize: Number = xSegmentSize,
|
||||
fiberDiameter: Number = 1.0,
|
||||
): SolidGroup = solidGroup {
|
||||
color.set("darkgreen")
|
||||
color("darkgreen")
|
||||
val transparent by style {
|
||||
this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ fun main() {
|
||||
//Create a geometry
|
||||
val sat = solids.visionOfSatellite(ySegments = 3).apply {
|
||||
ambientLight {
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
val server = embeddedServer(CIO, port = 7777) {
|
||||
@ -63,7 +63,7 @@ fun main() {
|
||||
val randomJ = Random.nextInt(1, 4)
|
||||
val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]")
|
||||
val targetVision = sat[target] as Solid
|
||||
targetVision.color.set("red")
|
||||
targetVision.color("red")
|
||||
delay(1000)
|
||||
//use to ensure that color is cleared
|
||||
targetVision.color.value = Null
|
||||
|
@ -3,8 +3,8 @@ package ru.mipt.npm.sat
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.box
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.material
|
||||
import space.kscience.visionforge.solid.set
|
||||
import space.kscience.visionforge.solid.solid
|
||||
import space.kscience.visionforge.three.makeThreeJsFile
|
||||
|
||||
@ -14,7 +14,7 @@ fun main() = makeThreeJsFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
solid {
|
||||
box(100, 100, 100)
|
||||
material {
|
||||
emissiveColor.set("red")
|
||||
emissiveColor("red")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
@ -19,6 +19,8 @@ kscience {
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt")
|
||||
}
|
||||
kotlin.explicitApi = null
|
||||
|
||||
//application {
|
||||
// mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt")
|
||||
//}
|
@ -4,6 +4,8 @@ import kotlinx.coroutines.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.invoke
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.kmath.geometry.Euclidean3DSpace
|
||||
import space.kscience.kmath.geometry.radians
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
@ -21,7 +23,7 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
|
||||
val vision = solids.solidGroup {
|
||||
block()
|
||||
ambientLight {
|
||||
color.set(Colors.white)
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
render(Name.parse(name), vision, meta)
|
||||
@ -47,23 +49,23 @@ fun VisionLayout<Solid>.showcase() {
|
||||
ambientLight()
|
||||
box(100.0, 100.0, 100.0) {
|
||||
z = -110.0
|
||||
color.set("teal")
|
||||
color("teal")
|
||||
}
|
||||
sphere(50.0) {
|
||||
x = 110
|
||||
detail = 16
|
||||
color.set("red")
|
||||
color("red")
|
||||
}
|
||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||
y = 110
|
||||
detail = 16
|
||||
rotationX = PI / 4
|
||||
color.set("blue")
|
||||
color("blue")
|
||||
}
|
||||
sphereLayer(50, 40, theta = PI / 2) {
|
||||
rotationX = -PI * 3 / 4
|
||||
z = 110
|
||||
color.set(Colors.pink)
|
||||
color(Colors.pink)
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +80,7 @@ fun VisionLayout<Solid>.showcase() {
|
||||
visible = false
|
||||
x = 110.0
|
||||
//override color for this cube
|
||||
color.set(1530)
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
while (isActive) {
|
||||
@ -93,7 +95,7 @@ fun VisionLayout<Solid>.showcase() {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
group.color.set(random.nextInt(0, Int.MAX_VALUE))
|
||||
group.color(random.nextInt(0, Int.MAX_VALUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,9 +105,16 @@ fun VisionLayout<Solid>.showcase() {
|
||||
solidGroup {
|
||||
x = 200
|
||||
rotationY = PI / 4
|
||||
axes(200)
|
||||
box(100, 100, 100) {
|
||||
rotationZ = PI / 4
|
||||
color.set(Colors.red)
|
||||
rotate((PI / 4).radians, Euclidean3DSpace.zAxis)
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
while (isActive) {
|
||||
delay(100)
|
||||
rotate((PI/20).radians,Euclidean3DSpace.yAxis)
|
||||
}
|
||||
}
|
||||
color(Colors.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,7 +127,7 @@ fun VisionLayout<Solid>.showcase() {
|
||||
for (i in 0..100) {
|
||||
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
|
||||
}
|
||||
color.set(Colors.teal)
|
||||
color(Colors.teal)
|
||||
rotationX = -PI / 2
|
||||
}
|
||||
}
|
||||
@ -127,13 +136,16 @@ fun VisionLayout<Solid>.showcase() {
|
||||
sphere(100) {
|
||||
detail = 32
|
||||
opacity = 0.4
|
||||
color.set(Colors.blue)
|
||||
color(Colors.blue)
|
||||
}
|
||||
repeat(20) {
|
||||
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
|
||||
polyline(
|
||||
Float32Vector3D(100, 100, 100),
|
||||
Float32Vector3D(-100, -100, -100)
|
||||
) {
|
||||
thickness = 3.0
|
||||
rotationX = it * PI2 / 20
|
||||
color.set(Colors.green)
|
||||
color(Colors.green)
|
||||
//rotationY = it * PI2 / 20
|
||||
}
|
||||
}
|
||||
@ -147,6 +159,10 @@ fun VisionLayout<Solid>.showcase() {
|
||||
z = 26
|
||||
}
|
||||
}
|
||||
|
||||
demo("STL", "STL loaded from URL") {
|
||||
stl("https://ozeki.hu/attachments/116/Menger_sponge_sample.stl")
|
||||
}
|
||||
}
|
||||
|
||||
fun VisionLayout<Solid>.showcaseCSG() {
|
||||
@ -160,7 +176,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
||||
detail = 32
|
||||
}
|
||||
material {
|
||||
color.set(Colors.pink)
|
||||
color(Colors.pink)
|
||||
}
|
||||
}
|
||||
composite(CompositeType.UNION) {
|
||||
@ -170,7 +186,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
||||
sphere(50) {
|
||||
detail = 32
|
||||
}
|
||||
color.set("lightgreen")
|
||||
color("lightgreen")
|
||||
opacity = 0.7
|
||||
}
|
||||
composite(CompositeType.SUBTRACT) {
|
||||
@ -181,7 +197,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
||||
sphere(50) {
|
||||
detail = 32
|
||||
}
|
||||
color.set("teal")
|
||||
color("teal")
|
||||
opacity = 0.7
|
||||
}
|
||||
}
|
||||
@ -192,7 +208,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
||||
detail = 32
|
||||
}
|
||||
box(100, 100, 100)
|
||||
color.set("red")
|
||||
color("red")
|
||||
opacity = 0.5
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ internal fun SolidGroup.varBox(
|
||||
|
||||
internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() {
|
||||
|
||||
override fun render(three: ThreePlugin): Object3D {
|
||||
override suspend fun render(three: ThreePlugin): Object3D {
|
||||
val geometry = BoxGeometry(xSize, ySize, 1)
|
||||
|
||||
val material = ThreeMaterials.DEFAULT.clone()
|
||||
|
@ -1,144 +0,0 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.util.url
|
||||
import io.ktor.server.websocket.WebSockets
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.context.info
|
||||
import space.kscience.dataforge.context.logger
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.visionFragment
|
||||
import space.kscience.visionforge.server.VisionRoute
|
||||
import space.kscience.visionforge.server.serveVisionData
|
||||
import space.kscience.visionforge.visionManager
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextUInt
|
||||
|
||||
internal fun TagConsumer<*>.renderScriptForId(id: String) {
|
||||
script {
|
||||
type = "text/javascript"
|
||||
unsafe { +"VisionForge.renderAllVisionsById(\"$id\");" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler class that includes a server and common utilities
|
||||
*/
|
||||
public class VFForNotebook(override val context: Context) : ContextAware, CoroutineScope {
|
||||
|
||||
public val visionManager: VisionManager = context.visionManager
|
||||
|
||||
private var counter = 0
|
||||
|
||||
private var engine: ApplicationEngine? = null
|
||||
|
||||
public var isolateFragments: Boolean = false
|
||||
|
||||
override val coroutineContext: CoroutineContext get() = context.coroutineContext
|
||||
|
||||
public fun legacyMode() {
|
||||
isolateFragments = true
|
||||
}
|
||||
|
||||
public fun isServerRunning(): Boolean = engine != null
|
||||
|
||||
public fun html(block: TagConsumer<*>.() -> Unit): MimeTypedResult = HTML(createHTML().apply(block).finalize())
|
||||
|
||||
public fun startServer(
|
||||
host: String = context.properties["visionforge.host"].string ?: "localhost",
|
||||
port: Int = context.properties["visionforge.port"].int ?: VisionRoute.DEFAULT_PORT,
|
||||
): MimeTypedResult = html {
|
||||
if (engine != null) {
|
||||
p {
|
||||
style = "color: red;"
|
||||
+"Stopping current VisionForge server"
|
||||
}
|
||||
}
|
||||
|
||||
//val connector: EngineConnectorConfig = EngineConnectorConfig(host, port)
|
||||
|
||||
engine?.stop(1000, 2000)
|
||||
engine = context.embeddedServer(CIO, port, host) {
|
||||
install(WebSockets)
|
||||
}.start(false)
|
||||
|
||||
p {
|
||||
style = "color: blue;"
|
||||
+"Starting VisionForge server on http://$host:$port"
|
||||
}
|
||||
}
|
||||
|
||||
public fun stopServer() {
|
||||
engine?.apply {
|
||||
logger.info { "Stopping VisionForge server" }
|
||||
stop(1000, 2000)
|
||||
engine = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun produceHtmlString(
|
||||
fragment: HtmlVisionFragment,
|
||||
): String = createHTML().apply {
|
||||
val id = "fragment[${fragment.hashCode()}/${Random.nextUInt()}]"
|
||||
div {
|
||||
this.id = id
|
||||
val engine = engine
|
||||
if (engine != null) {
|
||||
//if server exist, serve dynamically
|
||||
//server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment)
|
||||
val cellRoute = "content-${counter++}"
|
||||
|
||||
val collector: MutableMap<Name, Vision> = mutableMapOf()
|
||||
|
||||
val url = engine.environment.connectors.first().let {
|
||||
url {
|
||||
protocol = URLProtocol.WS
|
||||
host = it.host
|
||||
port = it.port
|
||||
pathSegments = listOf(cellRoute, "ws")
|
||||
}
|
||||
}
|
||||
|
||||
engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), collector)
|
||||
|
||||
visionFragment(
|
||||
visionManager,
|
||||
embedData = true,
|
||||
updatesUrl = url,
|
||||
onVisionRendered = { name, vision -> collector[name] = vision },
|
||||
fragment = fragment
|
||||
)
|
||||
} else {
|
||||
//if not, use static rendering
|
||||
visionFragment(visionManager, fragment = fragment)
|
||||
}
|
||||
}
|
||||
renderScriptForId(id)
|
||||
}.finalize()
|
||||
|
||||
public fun produceHtml(isolated: Boolean? = null, fragment: HtmlVisionFragment): MimeTypedResult =
|
||||
HTML(produceHtmlString(fragment), isolated ?: isolateFragments)
|
||||
|
||||
public fun fragment(body: HtmlVisionFragment): MimeTypedResult = produceHtml(fragment = body)
|
||||
public fun page(body: HtmlVisionFragment): MimeTypedResult = produceHtml(true, body)
|
||||
|
||||
public fun form(builder: FORM.() -> Unit): HtmlFormFragment =
|
||||
HtmlFormFragment("form[${counter++}]", builder = builder)
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.declare
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextUInt
|
||||
|
||||
/**
|
||||
* A base class for different Jupyter VF integrations
|
||||
*/
|
||||
@DFExperimental
|
||||
public abstract class VFIntegrationBase(
|
||||
public val visionManager: VisionManager,
|
||||
) : JupyterIntegration(), ContextAware {
|
||||
|
||||
override val context: Context get() = visionManager.context
|
||||
protected val handler: VFForNotebook = VFForNotebook(visionManager.context)
|
||||
|
||||
protected abstract fun Builder.afterLoaded()
|
||||
|
||||
final override fun Builder.onLoaded() {
|
||||
|
||||
onLoaded {
|
||||
declare("VisionForge" to handler, "vf" to handler)
|
||||
}
|
||||
|
||||
onShutdown {
|
||||
handler.stopServer()
|
||||
}
|
||||
|
||||
import(
|
||||
"kotlinx.html.*",
|
||||
"space.kscience.visionforge.html.*"
|
||||
)
|
||||
|
||||
render<HtmlFragment> { fragment ->
|
||||
handler.produceHtml(fragment = fragment)
|
||||
}
|
||||
|
||||
render<HtmlVisionFragment> { fragment ->
|
||||
handler.produceHtml(fragment = fragment)
|
||||
}
|
||||
|
||||
render<Vision> { vision ->
|
||||
handler.produceHtml {
|
||||
vision(vision)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render<VisionPage> { page ->
|
||||
HTML(createHTML().apply {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
page.pageHeaders.values.forEach {
|
||||
fragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
val id = "fragment[${page.hashCode()}/${Random.nextUInt()}]"
|
||||
div {
|
||||
this.id = id
|
||||
visionFragment(visionManager, fragment = page.content)
|
||||
}
|
||||
renderScriptForId(id)
|
||||
}
|
||||
}.finalize(), true)
|
||||
}
|
||||
|
||||
render<HtmlFormFragment> { fragment ->
|
||||
handler.produceHtml {
|
||||
if (!handler.isServerRunning()) {
|
||||
p {
|
||||
style = "color: red;"
|
||||
+"The server is not running. Forms are not interactive. Start server with `VisionForge.startServer()."
|
||||
}
|
||||
}
|
||||
fragment(fragment.formBody)
|
||||
vision(fragment.vision)
|
||||
}
|
||||
}
|
||||
|
||||
afterLoaded()
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
# Module visionforge-jupyter-gdml
|
||||
|
||||
Jupyter api artifact for GDML rendering
|
||||
|
||||
## Usage
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-jupyter-gdml:0.2.0`.
|
||||
|
||||
**Gradle Groovy:**
|
||||
```groovy
|
||||
repositories {
|
||||
maven { url 'https://repo.kotlin.link' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'space.kscience:visionforge-jupyter-gdml:0.2.0'
|
||||
}
|
||||
```
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-jupyter-gdml:0.2.0")
|
||||
}
|
||||
```
|
@ -1,38 +0,0 @@
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
}
|
||||
|
||||
description = "Jupyter api artifact for GDML rendering"
|
||||
|
||||
kscience {
|
||||
fullStack("js/gdml-jupyter.js",
|
||||
jsConfig = { useCommonJs() }
|
||||
) {
|
||||
commonWebpackConfig {
|
||||
sourceMaps = false
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commonMain{
|
||||
implementation(projects.visionforgeSolid)
|
||||
implementation(projects.jupyter)
|
||||
}
|
||||
|
||||
jvmMain{
|
||||
implementation(projects.visionforgeGdml)
|
||||
}
|
||||
|
||||
jsMain{
|
||||
implementation(projects.visionforgeThreejs)
|
||||
implementation(projects.ui.ring)
|
||||
}
|
||||
|
||||
jupyterLibrary("space.kscience.visionforge.gdml.jupyter.GdmlForJupyter")
|
||||
}
|
||||
|
||||
readme {
|
||||
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package space.kscience.visionforge.gdml.jupyter
|
||||
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.runVisionClient
|
||||
|
||||
@DFExperimental
|
||||
@JsExport
|
||||
public fun main(): Unit = runVisionClient {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
package space.kscience.visionforge.gdml.jupyter
|
||||
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.jupyter.VFIntegrationBase
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
@DFExperimental
|
||||
internal class GdmlForJupyter : VFIntegrationBase(
|
||||
Context("GDML") {
|
||||
plugin(Solids)
|
||||
}.visionManager
|
||||
) {
|
||||
|
||||
override fun Builder.afterLoaded() {
|
||||
|
||||
resources {
|
||||
js("three") {
|
||||
classPath("js/gdml-jupyter.js")
|
||||
}
|
||||
}
|
||||
|
||||
import(
|
||||
"space.kscience.gdml.*",
|
||||
"space.kscience.visionforge.gdml.jupyter.*"
|
||||
)
|
||||
|
||||
render<Gdml> { gdmlModel ->
|
||||
handler.produceHtml {
|
||||
vision { gdmlModel.toVision() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
|
||||
|
||||
config.module.rules.push(...ringConfig.module.rules)
|
@ -64,6 +64,6 @@ include(
|
||||
":demo:playground",
|
||||
// ":demo:plotly-fx",
|
||||
":demo:js-playground",
|
||||
":jupyter",
|
||||
":jupyter:visionforge-jupyter-gdml"
|
||||
":visionforge-jupyter",
|
||||
":visionforge-jupyter:visionforge-jupyter-common"
|
||||
)
|
||||
|
@ -2,7 +2,6 @@ package space.kscience.visionforge.react
|
||||
|
||||
import kotlinx.css.*
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import react.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.request
|
||||
@ -29,7 +28,7 @@ public val ThreeCanvasComponent: FC<ThreeCanvasProps> = fc("ThreeCanvasComponent
|
||||
|
||||
useEffect(props.solid, props.options, elementRef) {
|
||||
if (canvas == null) {
|
||||
val element = elementRef.current as? HTMLElement ?: error("Canvas element not found")
|
||||
val element = elementRef.current ?: error("Canvas element not found")
|
||||
canvas = ThreeCanvas(three, element, props.options ?: Canvas3DOptions())
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,15 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.PluginFactory
|
||||
import space.kscience.dataforge.context.PluginTag
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.boolean
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.ElementVisionRenderer
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.react.render
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
|
||||
public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
@ -24,11 +27,16 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
||||
|
||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||
space.kscience.visionforge.react.createRoot(element).render {
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
this.solids = three.solids
|
||||
this.builderOfSolid = context.async { vision as Solid}
|
||||
if(meta["controls.enabled"].boolean == false){
|
||||
three.render(element, name, vision, meta)
|
||||
} else {
|
||||
space.kscience.visionforge.react.createRoot(element).render {
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
this.solids = three.solids
|
||||
this.options = Canvas3DOptions.read(meta)
|
||||
this.builderOfSolid = context.async { vision as Solid }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"))
|
@ -35,7 +35,6 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
|
||||
|
||||
public val jsonFormat: Json
|
||||
get() = Json(defaultJson) {
|
||||
encodeDefaults = false
|
||||
serializersModule = this@VisionManager.serializersModule
|
||||
}
|
||||
|
||||
@ -85,7 +84,6 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
|
||||
serializersModule = defaultSerialModule
|
||||
prettyPrint = true
|
||||
useArrayPolymorphism = false
|
||||
encodeDefaults = false
|
||||
ignoreUnknownKeys = true
|
||||
explicitNulls = false
|
||||
}
|
||||
|
@ -4,19 +4,28 @@ import kotlinx.html.FlowContent
|
||||
import kotlinx.html.TagConsumer
|
||||
import kotlinx.html.stream.createHTML
|
||||
|
||||
public typealias HtmlFragment = TagConsumer<*>.() -> Unit
|
||||
|
||||
public fun HtmlFragment.renderToString(): String = createHTML().apply(this).finalize()
|
||||
|
||||
public fun TagConsumer<*>.fragment(fragment: HtmlFragment) {
|
||||
fragment()
|
||||
/**
|
||||
* A standalone HTML fragment
|
||||
*/
|
||||
public fun interface HtmlFragment {
|
||||
public fun TagConsumer<*>.append()
|
||||
}
|
||||
|
||||
public fun FlowContent.fragment(fragment: HtmlFragment) {
|
||||
fragment(consumer)
|
||||
}
|
||||
/**
|
||||
* Convenience method to append fragment to the given [consumer]
|
||||
*/
|
||||
public fun HtmlFragment.appendTo(consumer: TagConsumer<*>): Unit = consumer.append()
|
||||
|
||||
public operator fun HtmlFragment.plus(other: HtmlFragment): HtmlFragment = {
|
||||
this@plus()
|
||||
other()
|
||||
/**
|
||||
* Create a string from this [HtmlFragment]
|
||||
*/
|
||||
public fun HtmlFragment.renderToString(): String = createHTML().apply { append() }.finalize()
|
||||
|
||||
public fun TagConsumer<*>.appendFragment(fragment: HtmlFragment): Unit = fragment.appendTo(this)
|
||||
|
||||
public fun FlowContent.appendFragment(fragment: HtmlFragment): Unit = fragment.appendTo(consumer)
|
||||
|
||||
public operator fun HtmlFragment.plus(other: HtmlFragment): HtmlFragment = HtmlFragment {
|
||||
this@plus.appendTo(this)
|
||||
other.appendTo(this)
|
||||
}
|
@ -2,18 +2,17 @@ package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
|
||||
public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
|
||||
|
||||
@DFExperimental
|
||||
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
||||
public fun interface HtmlVisionFragment{
|
||||
public fun VisionTagConsumer<*>.append()
|
||||
}
|
||||
|
||||
public fun HtmlVisionFragment.appendTo(consumer: VisionTagConsumer<*>): Unit = consumer.append()
|
||||
|
||||
/**
|
||||
* Render a fragment in the given consumer and return a map of extracted visions
|
||||
@ -84,7 +83,7 @@ public fun TagConsumer<*>.visionFragment(
|
||||
}
|
||||
}
|
||||
|
||||
fragment(consumer)
|
||||
fragment.appendTo(consumer)
|
||||
}
|
||||
|
||||
public fun FlowContent.visionFragment(
|
||||
|
@ -17,7 +17,7 @@ public data class VisionPage(
|
||||
/**
|
||||
* Use a script with given [src] as a global header for all pages.
|
||||
*/
|
||||
public fun scriptHeader(src: String, block: SCRIPT.() -> Unit = {}): HtmlFragment = {
|
||||
public fun scriptHeader(src: String, block: SCRIPT.() -> Unit = {}): HtmlFragment = HtmlFragment{
|
||||
script {
|
||||
type = "text/javascript"
|
||||
this.src = src
|
||||
@ -26,9 +26,9 @@ public data class VisionPage(
|
||||
}
|
||||
|
||||
/**
|
||||
* Use css with given stylesheet link as a global header for all pages.
|
||||
* Use css with the given stylesheet link as a global header for all pages.
|
||||
*/
|
||||
public fun styleSheetHeader(href: String, block: LINK.() -> Unit = {}): HtmlFragment = {
|
||||
public fun styleSheetHeader(href: String, block: LINK.() -> Unit = {}): HtmlFragment = HtmlFragment{
|
||||
link {
|
||||
rel = "stylesheet"
|
||||
this.href = href
|
||||
@ -36,7 +36,7 @@ public data class VisionPage(
|
||||
}
|
||||
}
|
||||
|
||||
public fun title(title:String): HtmlFragment = {
|
||||
public fun title(title:String): HtmlFragment = HtmlFragment{
|
||||
title(title)
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ fun FlowContent.renderVisionFragment(
|
||||
renderer(name, vision, outputMeta)
|
||||
}
|
||||
}
|
||||
fragment(consumer)
|
||||
fragment.appendTo(consumer)
|
||||
return visionMap
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ private fun VisionOutput.base(block: VisionGroup.() -> Unit) = context.visionMan
|
||||
@DFExperimental
|
||||
class HtmlTagTest {
|
||||
|
||||
val fragment: HtmlVisionFragment = {
|
||||
val fragment = HtmlVisionFragment{
|
||||
div {
|
||||
h1 { +"Head" }
|
||||
vision("ddd") {
|
||||
|
@ -286,8 +286,8 @@ public fun VisionClient.renderAllVisionsIn(element: Element) {
|
||||
/**
|
||||
* Render all visions in an element with a given [id]
|
||||
*/
|
||||
public fun VisionClient.renderAllVisionsById(id: String): Unit = whenDocumentLoaded {
|
||||
val element = getElementById(id)
|
||||
public fun VisionClient.renderAllVisionsById(document: Document, id: String): Unit {
|
||||
val element = document.getElementById(id)
|
||||
if (element != null) {
|
||||
renderAllVisionsIn(element)
|
||||
} else {
|
||||
|
@ -34,10 +34,10 @@ public interface HtmlVisionContext : ContextAware {
|
||||
|
||||
public typealias HtmlVisionContextFragment = context(HtmlVisionContext) TagConsumer<*>.() -> Unit
|
||||
|
||||
context(HtmlVisionContext)
|
||||
public fun HtmlVisionFragment(
|
||||
content: TagConsumer<*>.() -> Unit,
|
||||
): HtmlVisionFragment = content
|
||||
//context(HtmlVisionContext)
|
||||
//public fun HtmlVisionFragment(
|
||||
// content: TagConsumer<*>.() -> Unit,
|
||||
//): HtmlVisionFragment = HtmlVisionFragment { }
|
||||
|
||||
context(HtmlVisionContext)
|
||||
private fun <T> TagConsumer<T>.vision(
|
||||
|
@ -91,14 +91,14 @@ internal fun checkOrStoreFile(htmlPath: Path, filePath: Path, resource: String,
|
||||
*/
|
||||
internal fun fileScriptHeader(
|
||||
path: Path,
|
||||
): HtmlFragment = {
|
||||
): HtmlFragment = HtmlFragment{
|
||||
script {
|
||||
type = "text/javascript"
|
||||
src = path.toString()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun embedScriptHeader(resource: String, classLoader: ClassLoader): HtmlFragment = {
|
||||
internal fun embedScriptHeader(resource: String, classLoader: ClassLoader): HtmlFragment = HtmlFragment{
|
||||
script {
|
||||
type = "text/javascript"
|
||||
unsafe {
|
||||
@ -113,7 +113,7 @@ internal fun fileCssHeader(
|
||||
cssPath: Path,
|
||||
resource: String,
|
||||
classLoader: ClassLoader,
|
||||
): HtmlFragment = {
|
||||
): HtmlFragment = HtmlFragment{
|
||||
val relativePath = checkOrStoreFile(basePath, cssPath, resource, classLoader)
|
||||
link {
|
||||
rel = "stylesheet"
|
||||
|
@ -78,7 +78,7 @@ public fun VisionPage.makeFile(
|
||||
charset = "utf-8"
|
||||
}
|
||||
actualHeaders.values.forEach {
|
||||
fragment(it)
|
||||
appendFragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
|
@ -42,7 +42,7 @@ public class GdmlLoaderOptions {
|
||||
* Configure paint for given solid with given [GdmlMaterial]
|
||||
*/
|
||||
public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit =
|
||||
{ material, _ -> color.set(randomColor(material)) }
|
||||
{ material, _ -> color(randomColor(material)) }
|
||||
private set
|
||||
|
||||
public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) {
|
||||
|
@ -6,6 +6,7 @@ import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
|
||||
import space.kscience.gdml.*
|
||||
import space.kscience.kmath.geometry.RotationOrder
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.html.VisionOutput
|
||||
import space.kscience.visionforge.solid.*
|
||||
@ -206,9 +207,9 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
|
||||
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
|
||||
val baseRadius = solid.planes.first().rmax * lScale
|
||||
shape {
|
||||
(0..solid.numsides).forEach {
|
||||
(0..<solid.numsides).forEach {
|
||||
val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
|
||||
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
||||
point(baseRadius * cos(phi), baseRadius * sin(phi))
|
||||
}
|
||||
}
|
||||
solid.planes.forEach { plane ->
|
||||
@ -248,14 +249,14 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
|
||||
val dyBottom = solid.y1.toDouble() / 2
|
||||
val dyTop = solid.y2.toDouble() / 2
|
||||
val dz = solid.z.toDouble() / 2
|
||||
val node1 = Point3D(-dxBottom, -dyBottom, -dz)
|
||||
val node2 = Point3D(dxBottom, -dyBottom, -dz)
|
||||
val node3 = Point3D(dxBottom, dyBottom, -dz)
|
||||
val node4 = Point3D(-dxBottom, dyBottom, -dz)
|
||||
val node5 = Point3D(-dxTop, -dyTop, dz)
|
||||
val node6 = Point3D(dxTop, -dyTop, dz)
|
||||
val node7 = Point3D(dxTop, dyTop, dz)
|
||||
val node8 = Point3D(-dxTop, dyTop, dz)
|
||||
val node1 = Float32Vector3D(-dxBottom, -dyBottom, -dz)
|
||||
val node2 = Float32Vector3D(dxBottom, -dyBottom, -dz)
|
||||
val node3 = Float32Vector3D(dxBottom, dyBottom, -dz)
|
||||
val node4 = Float32Vector3D(-dxBottom, dyBottom, -dz)
|
||||
val node5 = Float32Vector3D(-dxTop, -dyTop, dz)
|
||||
val node6 = Float32Vector3D(dxTop, -dyTop, dz)
|
||||
val node7 = Float32Vector3D(dxTop, dyTop, dz)
|
||||
val node8 = Float32Vector3D(-dxTop, dyTop, dz)
|
||||
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,17 @@ plugins {
|
||||
description = "Common visionforge jupyter module"
|
||||
|
||||
kscience {
|
||||
useKtor()
|
||||
jvm()
|
||||
js()
|
||||
jupyterLibrary()
|
||||
dependencies {
|
||||
api(projects.visionforgeCore)
|
||||
}
|
||||
jvmMain {
|
||||
dependencies(jvmMain){
|
||||
api("io.ktor:ktor-server-cio-jvm")
|
||||
api("io.ktor:ktor-server-websockets-jvm")
|
||||
api("io.ktor:ktor-server-cors-jvm")
|
||||
api(projects.visionforgeServer)
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import kotlinx.browser.window
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import space.kscience.dataforge.context.AbstractPlugin
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -13,15 +14,15 @@ import space.kscience.visionforge.renderAllVisionsById
|
||||
import space.kscience.visionforge.renderAllVisionsIn
|
||||
|
||||
@JsExport
|
||||
public class VFNotebookPlugin : AbstractPlugin() {
|
||||
public class VFNotebookClient : AbstractPlugin() {
|
||||
private val client by require(VisionClient)
|
||||
|
||||
public fun renderAllVisionsIn(element: Element) {
|
||||
client.renderAllVisionsIn(element)
|
||||
}
|
||||
|
||||
public fun renderAllVisionsById(id: String) {
|
||||
client.renderAllVisionsById(id)
|
||||
public fun renderAllVisionsById(document: Document, id: String) {
|
||||
client.renderAllVisionsById(document, id)
|
||||
}
|
||||
|
||||
public fun renderAllVisions() {
|
||||
@ -30,17 +31,18 @@ public class VFNotebookPlugin : AbstractPlugin() {
|
||||
|
||||
|
||||
init {
|
||||
console.info("Loading VisionForge global hooks")
|
||||
//register VisionForge in the browser window
|
||||
window.asDynamic().vf = this
|
||||
window.asDynamic().VisionForge = this
|
||||
window.parent.asDynamic().vf = this
|
||||
window.parent.asDynamic().VisionForge = this
|
||||
}
|
||||
|
||||
@Suppress("NON_EXPORTABLE_TYPE")
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
@Suppress("NON_EXPORTABLE_TYPE")
|
||||
public companion object : PluginFactory<VFNotebookPlugin> {
|
||||
override fun build(context: Context, meta: Meta): VFNotebookPlugin = VFNotebookPlugin()
|
||||
public companion object : PluginFactory<VFNotebookClient> {
|
||||
override fun build(context: Context, meta: Meta): VFNotebookClient = VFNotebookClient()
|
||||
|
||||
override val tag: PluginTag = PluginTag(name = "vision.notebook", group = PluginTag.DATAFORGE_GROUP)
|
||||
}
|
177
visionforge-jupyter/src/jvmMain/kotlin/VisionForge.kt
Normal file
177
visionforge-jupyter/src/jvmMain/kotlin/VisionForge.kt
Normal file
@ -0,0 +1,177 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.util.url
|
||||
import io.ktor.server.websocket.WebSockets
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.context.info
|
||||
import space.kscience.dataforge.context.logger
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.visionFragment
|
||||
import space.kscience.visionforge.server.VisionRoute
|
||||
import space.kscience.visionforge.server.serveVisionData
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextUInt
|
||||
|
||||
|
||||
@Suppress("FunctionName")
|
||||
internal inline fun HTML(isolated: Boolean = false, block: TagConsumer<*>.() -> Unit): MimeTypedResult =
|
||||
HTML(createHTML().apply(block).finalize(), isolated)
|
||||
|
||||
internal fun KotlinKernelHost.displayHtml(block: TagConsumer<*>.() -> Unit) {
|
||||
display(HTML(false, block), null)
|
||||
}
|
||||
|
||||
public enum class VisionForgeCompatibility {
|
||||
JUPYTER,
|
||||
JUPYTER_LAB,
|
||||
DATALORE,
|
||||
IDEA
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler class that includes a server and common utilities
|
||||
*/
|
||||
@Suppress("ExtractKtorModule")
|
||||
public class VisionForge(
|
||||
public val visionManager: VisionManager,
|
||||
public val notebook: Notebook,
|
||||
meta: Meta = Meta.EMPTY,
|
||||
) : ContextAware, CoroutineScope {
|
||||
|
||||
override val context: Context get() = visionManager.context
|
||||
|
||||
public val configuration: ObservableMutableMeta = meta.toMutableMeta()
|
||||
|
||||
private var counter = 0
|
||||
|
||||
private var engine: ApplicationEngine? = null
|
||||
|
||||
override val coroutineContext: CoroutineContext get() = context.coroutineContext
|
||||
|
||||
|
||||
public fun isServerRunning(): Boolean = engine != null
|
||||
|
||||
public fun getProperty(name: String): TypedMeta<*>? = configuration[name] ?: context.properties[name]
|
||||
|
||||
internal fun startServer(
|
||||
kernel: KotlinKernelHost,
|
||||
host: String = getProperty("visionforge.host").string ?: "localhost",
|
||||
port: Int = getProperty("visionforge.port").int ?: VisionRoute.DEFAULT_PORT,
|
||||
) {
|
||||
engine?.let {
|
||||
kernel.displayHtml {
|
||||
p {
|
||||
style = "color: red;"
|
||||
+"Stopping current VisionForge server"
|
||||
}
|
||||
}
|
||||
it.stop(1000, 2000)
|
||||
}
|
||||
|
||||
//val connector: EngineConnectorConfig = EngineConnectorConfig(host, port)
|
||||
|
||||
|
||||
engine = context.embeddedServer(CIO, port, host) {
|
||||
install(WebSockets)
|
||||
}.start(false)
|
||||
|
||||
kernel.displayHtml {
|
||||
p {
|
||||
style = "color: blue;"
|
||||
+"Starting VisionForge server on port $port"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun stopServer(kernel: KotlinKernelHost) {
|
||||
engine?.apply {
|
||||
logger.info { "Stopping VisionForge server" }
|
||||
stop(1000, 2000)
|
||||
engine = null
|
||||
}
|
||||
|
||||
kernel.displayHtml {
|
||||
p {
|
||||
style = "color: red;"
|
||||
+"VisionForge server stopped"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun TagConsumer<*>.renderScriptForId(id: String) {
|
||||
script {
|
||||
type = "text/javascript"
|
||||
//language=JavaScript
|
||||
unsafe { +"parent.VisionForge.renderAllVisionsById(document, \"$id\");" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public fun produceHtml(
|
||||
isolated: Boolean? = null,
|
||||
fragment: HtmlVisionFragment,
|
||||
): MimeTypedResult {
|
||||
val iframeIsolation = isolated ?: when (notebook.jupyterClientType) {
|
||||
JupyterClientType.DATALORE, JupyterClientType.JUPYTER_NOTEBOOK -> true
|
||||
else -> false
|
||||
}
|
||||
return HTML(
|
||||
iframeIsolation
|
||||
) {
|
||||
val id = "fragment[${fragment.hashCode()}/${Random.nextUInt()}]"
|
||||
div {
|
||||
this.id = id
|
||||
val engine = engine
|
||||
if (engine != null) {
|
||||
//if server exist, serve dynamically
|
||||
//server.serveVisionsFromFragment(consumer, "content-${counter++}", fragment)
|
||||
val cellRoute = "content-${counter++}"
|
||||
|
||||
val collector: MutableMap<Name, Vision> = mutableMapOf()
|
||||
|
||||
val url = engine.environment.connectors.first().let {
|
||||
url {
|
||||
protocol = URLProtocol.WS
|
||||
host = it.host
|
||||
port = it.port
|
||||
pathSegments = listOf(cellRoute, "ws")
|
||||
}
|
||||
}
|
||||
|
||||
engine.application.serveVisionData(VisionRoute(cellRoute, visionManager), collector)
|
||||
|
||||
visionFragment(
|
||||
visionManager,
|
||||
embedData = true,
|
||||
updatesUrl = url,
|
||||
onVisionRendered = { name, vision -> collector[name] = vision },
|
||||
fragment = fragment
|
||||
)
|
||||
} else {
|
||||
//if not, use static rendering
|
||||
visionFragment(visionManager, fragment = fragment)
|
||||
}
|
||||
}
|
||||
renderScriptForId(id)
|
||||
}
|
||||
}
|
||||
|
||||
public fun form(builder: FORM.() -> Unit): HtmlFormFragment =
|
||||
HtmlFormFragment("form[${counter++}]", builder = builder)
|
||||
}
|
||||
|
133
visionforge-jupyter/src/jvmMain/kotlin/VisionForgeIntegration.kt
Normal file
133
visionforge-jupyter/src/jvmMain/kotlin/VisionForgeIntegration.kt
Normal file
@ -0,0 +1,133 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import kotlinx.html.*
|
||||
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost
|
||||
import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
|
||||
import org.jetbrains.kotlinx.jupyter.api.declare
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextUInt
|
||||
|
||||
/**
|
||||
* A base class for different Jupyter VF integrations
|
||||
*/
|
||||
@DFExperimental
|
||||
public abstract class VisionForgeIntegration(
|
||||
public val visionManager: VisionManager,
|
||||
) : JupyterIntegration(), ContextAware {
|
||||
|
||||
override val context: Context get() = visionManager.context
|
||||
|
||||
protected abstract fun Builder.afterLoaded(vf: VisionForge)
|
||||
|
||||
final override fun Builder.onLoaded() {
|
||||
|
||||
val vf: VisionForge = VisionForge(visionManager, notebook)
|
||||
|
||||
onLoaded {
|
||||
val kernel: KotlinKernelHost = this
|
||||
declare("VisionForge" to vf, "vf" to vf)
|
||||
vf.startServer(kernel)
|
||||
vf.configuration.onChange(this) { name ->
|
||||
if (name.toString() == "visionforge.port") {
|
||||
kernel.displayHtml {
|
||||
p { +"Property 'visionforge.port' changed. Restarting server" }
|
||||
}
|
||||
vf.startServer(kernel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onShutdown {
|
||||
vf.stopServer(this)
|
||||
}
|
||||
|
||||
import(
|
||||
"kotlinx.html.*",
|
||||
"space.kscience.visionforge.html.*",
|
||||
"space.kscience.visionforge.jupyter.*"
|
||||
)
|
||||
//
|
||||
// render<HtmlFragment> { fragment ->
|
||||
// HTML(fragment.renderToString())
|
||||
// }
|
||||
//
|
||||
// render<HtmlVisionFragment> { fragment ->
|
||||
// handler.produceHtml(fragment = fragment)
|
||||
// }
|
||||
|
||||
render<Vision> { vision ->
|
||||
vf.produceHtml {
|
||||
vision(vision)
|
||||
}
|
||||
}
|
||||
|
||||
render<VisionPage> { page ->
|
||||
HTML(true) {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
page.pageHeaders.values.forEach {
|
||||
appendFragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
val id = "fragment[${page.hashCode()}/${Random.nextUInt()}]"
|
||||
div {
|
||||
this.id = id
|
||||
visionFragment(visionManager, fragment = page.content)
|
||||
}
|
||||
with(vf) {
|
||||
renderScriptForId(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render<HtmlFormFragment> { fragment ->
|
||||
vf.produceHtml {
|
||||
if (!vf.isServerRunning()) {
|
||||
p {
|
||||
style = "color: red;"
|
||||
+"The server is not running. Forms are not interactive. Start server with `VisionForge.startServer()."
|
||||
}
|
||||
}
|
||||
appendFragment(fragment.formBody)
|
||||
vision(fragment.vision)
|
||||
}
|
||||
}
|
||||
|
||||
afterLoaded(vf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a fragment without a head to be embedded in the page
|
||||
*/
|
||||
@Suppress("UnusedReceiverParameter")
|
||||
public fun VisionForge.html(body: TagConsumer<*>.() -> Unit): MimeTypedResult = HTML(false, body)
|
||||
|
||||
|
||||
/**
|
||||
* Create a fragment without a head to be embedded in the page
|
||||
*/
|
||||
public fun VisionForge.fragment(body: VisionTagConsumer<*>.() -> Unit): MimeTypedResult = produceHtml(false, body)
|
||||
|
||||
|
||||
/**
|
||||
* Create a standalone page in the notebook
|
||||
*/
|
||||
public fun VisionForge.page(
|
||||
pageHeaders: Map<String, HtmlFragment> = emptyMap(),
|
||||
body: VisionTagConsumer<*>.() -> Unit,
|
||||
): VisionPage = VisionPage(visionManager, pageHeaders, body)
|
||||
|
@ -11,11 +11,14 @@ import space.kscience.visionforge.html.VisionOfHtmlForm
|
||||
public class HtmlFormFragment internal constructor(
|
||||
public val vision: VisionOfHtmlForm,
|
||||
public val formBody: HtmlFragment,
|
||||
){
|
||||
) {
|
||||
public val values: Meta? get() = vision.values
|
||||
public operator fun get(valueName: String): Meta? = values?.get(valueName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Top level function to create a form
|
||||
*/
|
||||
public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment {
|
||||
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
|
||||
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
|
||||
@ -24,4 +27,8 @@ public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlF
|
||||
builder()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public fun VisionForge.form(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment =
|
||||
HtmlFormFragment(id, builder)
|
@ -0,0 +1,42 @@
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
}
|
||||
|
||||
description = "Jupyter api artifact including all common modules"
|
||||
|
||||
kscience {
|
||||
fullStack(
|
||||
"js/visionforge-jupyter-common.js",
|
||||
jsConfig = { useCommonJs() }
|
||||
) {
|
||||
commonWebpackConfig {
|
||||
sourceMaps = false
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.visionforgeSolid)
|
||||
api(projects.visionforgePlotly)
|
||||
api(projects.visionforgeTables)
|
||||
api(projects.visionforgeMarkdown)
|
||||
api(projects.visionforgeJupyter)
|
||||
}
|
||||
|
||||
jvmMain {
|
||||
api(projects.visionforgeGdml)
|
||||
}
|
||||
|
||||
jsMain {
|
||||
implementation(projects.ui.ring)
|
||||
implementation(projects.visionforgeThreejs)
|
||||
}
|
||||
|
||||
jupyterLibrary("space.kscience.visionforge.jupyter.JupyterCommonIntegration")
|
||||
}
|
||||
|
||||
readme {
|
||||
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package space.kscience.visionforge.gdml.jupyter
|
||||
|
||||
import space.kscience.visionforge.jupyter.VFNotebookClient
|
||||
import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.runVisionClient
|
||||
import space.kscience.visionforge.tables.TableVisionJsPlugin
|
||||
|
||||
public fun main(): Unit = runVisionClient {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
plugin(TableVisionJsPlugin)
|
||||
plugin(VFNotebookClient)
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
package space.kscience.visionforge.jupyter
|
||||
|
||||
import kotlinx.html.*
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.plotly.Plot
|
||||
import space.kscience.plotly.PlotlyPage
|
||||
import space.kscience.plotly.StaticPlotlyRenderer
|
||||
import space.kscience.tables.*
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.HtmlFragment
|
||||
import space.kscience.visionforge.html.VisionPage
|
||||
import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.asVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.tables.TableVisionPlugin
|
||||
import space.kscience.visionforge.tables.toVision
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
|
||||
@DFExperimental
|
||||
public class JupyterCommonIntegration : VisionForgeIntegration(CONTEXT.visionManager) {
|
||||
|
||||
override fun Builder.afterLoaded(vf: VisionForge) {
|
||||
|
||||
resources {
|
||||
js("visionforge-common") {
|
||||
classPath("js/visionforge-jupyter-common.js")
|
||||
}
|
||||
}
|
||||
|
||||
import(
|
||||
"space.kscience.gdml.*",
|
||||
"space.kscience.visionforge.solid.*",
|
||||
"space.kscience.tables.*",
|
||||
"space.kscience.dataforge.meta.*",
|
||||
"space.kscience.plotly.*",
|
||||
"space.kscience.plotly.models.*",
|
||||
"space.kscience.visionforge.plotly.plotly"
|
||||
)
|
||||
|
||||
render<Gdml> { gdmlModel ->
|
||||
vf.produceHtml {
|
||||
vision { gdmlModel.toVision() }
|
||||
}
|
||||
}
|
||||
|
||||
render<Table<*>> { table ->
|
||||
vf.produceHtml {
|
||||
vision { table.toVision() }
|
||||
}
|
||||
}
|
||||
|
||||
render<Plot> { plot ->
|
||||
vf.produceHtml {
|
||||
vision { plot.asVision() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render<PlotlyPage> { plotlyPage ->
|
||||
val headers = plotlyPage.headers.associate { plotlyFragment ->
|
||||
plotlyFragment.hashCode().toString(16) to HtmlFragment {
|
||||
plotlyFragment.visit(this)
|
||||
}
|
||||
|
||||
}
|
||||
VisionPage(visionManager, headers) {
|
||||
div{
|
||||
p { +"Plotly page renderer is not recommended in VisionForge, use `vf.page{}`" }
|
||||
}
|
||||
div {
|
||||
plotlyPage.fragment.render.invoke(this, StaticPlotlyRenderer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
private val CONTEXT: Context = Context("Jupyter-common") {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(TableVisionPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
}
|
||||
}
|
||||
}
|
24
visionforge-jupyter/visionforge-jupyter-common/webpack.config.d/01.ring.js
vendored
Normal file
24
visionforge-jupyter/visionforge-jupyter-common/webpack.config.d/01.ring.js
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
|
||||
|
||||
const path = require('path');
|
||||
|
||||
config.module.rules.push(...ringConfig.module.rules)
|
||||
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: [
|
||||
path.resolve(__dirname, "../../node_modules/@jetbrains/ring-ui")
|
||||
],
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
options: {}
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
@ -6,10 +6,10 @@ kscience{
|
||||
useKtor()
|
||||
dependencies {
|
||||
api(projects.visionforgeCore)
|
||||
api("io.ktor:ktor-server-cio")
|
||||
api("io.ktor:ktor-server-host-common")
|
||||
api("io.ktor:ktor-server-html-builder")
|
||||
api("io.ktor:ktor-server-websockets")
|
||||
implementation("io.ktor:ktor-server-cors")
|
||||
api("io.ktor:ktor-server-cors")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package space.kscience.visionforge.server
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.cio.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.html.*
|
||||
import io.ktor.server.http.content.*
|
||||
@ -58,7 +57,7 @@ public class VisionRoute(
|
||||
override val context: Context get() = visionManager.context
|
||||
|
||||
/**
|
||||
* Update minimal interval between updates in milliseconds (if there are no updates, push will not happen
|
||||
* Update the minimal interval between updates in milliseconds (if there are no updates, push will not happen
|
||||
*/
|
||||
public var updateInterval: Long by meta.long(300, key = UPDATE_INTERVAL_KEY)
|
||||
|
||||
@ -171,8 +170,8 @@ public fun Application.visionPage(
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
headers.forEach { header ->
|
||||
consumer.header()
|
||||
headers.forEach { headerContent ->
|
||||
headerContent.appendTo(consumer)
|
||||
}
|
||||
}
|
||||
body {
|
||||
|
@ -2,6 +2,8 @@ plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
}
|
||||
|
||||
val kmathVersion = "0.3.1"
|
||||
|
||||
kscience {
|
||||
jvm()
|
||||
js()
|
||||
@ -11,6 +13,7 @@ kscience {
|
||||
}
|
||||
useCoroutines()
|
||||
dependencies {
|
||||
api("space.kscience:kmath-geometry:0.3.1")
|
||||
api(projects.visionforgeCore)
|
||||
}
|
||||
dependencies(jvmTest) {
|
||||
|
@ -28,7 +28,7 @@ public class ColorAccessor(
|
||||
}
|
||||
}
|
||||
|
||||
public fun Vision.color(
|
||||
public fun Vision.colorProperty(
|
||||
propertyName: Name? = null,
|
||||
): ReadOnlyProperty<Vision, ColorAccessor> = ReadOnlyProperty { _, property ->
|
||||
ColorAccessor(properties.root(true), propertyName ?: property.name.asName())
|
||||
@ -43,21 +43,21 @@ public var ColorAccessor?.string: String?
|
||||
/**
|
||||
* Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string
|
||||
*/
|
||||
public fun ColorAccessor?.set(webColor: String) {
|
||||
public operator fun ColorAccessor?.invoke(webColor: String) {
|
||||
this?.value = webColor.asValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color as RGB integer
|
||||
*/
|
||||
public fun ColorAccessor?.set(rgb: Int) {
|
||||
public operator fun ColorAccessor?.invoke(rgb: Int) {
|
||||
this?.value = Colors.rgbToString(rgb).asValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color as RGB
|
||||
*/
|
||||
public fun ColorAccessor?.set(r: UByte, g: UByte, b: UByte) {
|
||||
public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) {
|
||||
this?.value = Colors.rgbToString(r, g, b).asValue()
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,8 @@ public class ConeSegment(
|
||||
require(segments >= 4) { "The number of segments in cone is too small" }
|
||||
val angleStep = phi / (segments - 1)
|
||||
|
||||
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
|
||||
Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
||||
fun shape(r: Float, z: Float): List<Float32Vector3D> = (0 until segments).map { i ->
|
||||
Float32Vector3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
||||
}
|
||||
|
||||
geometryBuilder.apply {
|
||||
@ -53,8 +53,8 @@ public class ConeSegment(
|
||||
if (phi == PI2) {
|
||||
face4(bottomPoints.last(), bottomPoints[0], topPoints[0], topPoints.last())
|
||||
}
|
||||
val zeroBottom = Point3D(0f, 0f, -height / 2)
|
||||
val zeroTop = Point3D(0f, 0f, +height / 2)
|
||||
val zeroBottom = Float32Vector3D(0f, 0f, -height / 2)
|
||||
val zeroTop = Float32Vector3D(0f, 0f, +height / 2)
|
||||
for (it in 1 until segments) {
|
||||
face(bottomPoints[it - 1], zeroBottom, bottomPoints[it])
|
||||
face(topPoints[it - 1], topPoints[it], zeroTop)
|
||||
|
@ -38,8 +38,8 @@ public class ConeSurface(
|
||||
require(segments >= 4) { "The number of segments in tube is too small" }
|
||||
val angleStep = phi / (segments - 1)
|
||||
|
||||
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
|
||||
Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
||||
fun shape(r: Float, z: Float): List<Float32Vector3D> = (0 until segments).map { i ->
|
||||
Float32Vector3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
||||
}
|
||||
|
||||
geometryBuilder.apply {
|
||||
@ -56,8 +56,8 @@ public class ConeSurface(
|
||||
face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last())
|
||||
}
|
||||
if (bottomInnerRadius == 0f) {
|
||||
val zeroBottom = Point3D(0f, 0f, -height / 2)
|
||||
val zeroTop = Point3D(0f, 0f, height / 2)
|
||||
val zeroBottom = Float32Vector3D(0f, 0f, -height / 2)
|
||||
val zeroTop = Float32Vector3D(0f, 0f, height / 2)
|
||||
(1 until segments).forEach {
|
||||
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it])
|
||||
face(topOuterPoints[it - 1], topOuterPoints[it], zeroTop)
|
||||
|
@ -7,7 +7,7 @@ import space.kscience.visionforge.setChild
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.convex")
|
||||
public class Convex(public val points: List<Point3D>) : SolidBase<Convex>()
|
||||
public class Convex(public val points: List<Float32Vector3D>) : SolidBase<Convex>()
|
||||
|
||||
public inline fun MutableVisionContainer<Solid>.convex(
|
||||
name: String? = null,
|
||||
@ -15,10 +15,10 @@ public inline fun MutableVisionContainer<Solid>.convex(
|
||||
): Convex = ConvexBuilder().apply(action).build().also { setChild(name, it) }
|
||||
|
||||
public class ConvexBuilder {
|
||||
private val points = ArrayList<Point3D>()
|
||||
private val points = ArrayList<Float32Vector3D>()
|
||||
|
||||
public fun point(x: Number, y: Number, z: Number) {
|
||||
points.add(Point3D(x, y, z))
|
||||
points.add(Float32Vector3D(x, y, z))
|
||||
}
|
||||
|
||||
public fun build(): Convex {
|
||||
|
@ -4,23 +4,25 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.kmath.geometry.component1
|
||||
import space.kscience.kmath.geometry.component2
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
|
||||
public typealias Shape2D = List<Point2D>
|
||||
public typealias Shape2D = List<Float32Vector2D>
|
||||
|
||||
@Serializable
|
||||
public class Shape2DBuilder(private val points: ArrayList<Point2D> = ArrayList()) {
|
||||
public class Shape2DBuilder(private val points: ArrayList<Float32Vector2D> = ArrayList()) {
|
||||
|
||||
public fun point(x: Number, y: Number) {
|
||||
points.add(Point2D(x, y))
|
||||
points.add(Float32Vector2D(x, y))
|
||||
}
|
||||
|
||||
public infix fun Number.to(y: Number): Unit = point(this, y)
|
||||
|
||||
public fun build(): Shape2D = points
|
||||
}
|
||||
|
||||
@ -38,7 +40,7 @@ public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Flo
|
||||
@Serializable
|
||||
@SerialName("solid.extrude")
|
||||
public class Extruded(
|
||||
public val shape: List<Point2D>,
|
||||
public val shape: List<Float32Vector2D>,
|
||||
public val layers: List<Layer>,
|
||||
) : SolidBase<Extruded>(), GeometrySolid {
|
||||
|
||||
@ -50,18 +52,18 @@ public class Extruded(
|
||||
/**
|
||||
* Expand the shape for specific layers
|
||||
*/
|
||||
val layers: List<List<Point3D>> = layers.map { layer ->
|
||||
val layers: List<List<Float32Vector3D>> = layers.map { layer ->
|
||||
shape.map { (x, y) ->
|
||||
val newX = layer.x + x * layer.scale
|
||||
val newY = layer.y + y * layer.scale
|
||||
Point3D(newX, newY, layer.z)
|
||||
Float32Vector3D(newX, newY, layer.z)
|
||||
}
|
||||
}
|
||||
|
||||
if (layers.size < 2) error("Extruded shape requires more than one layer")
|
||||
|
||||
var lowerLayer = layers.first()
|
||||
var upperLayer: List<Point3D>
|
||||
var upperLayer: List<Float32Vector3D>
|
||||
|
||||
for (i in (1 until layers.size)) {
|
||||
upperLayer = layers[i]
|
||||
@ -94,7 +96,7 @@ public class Extruded(
|
||||
}
|
||||
|
||||
public class ExtrudeBuilder(
|
||||
public var shape: List<Point2D> = emptyList(),
|
||||
public var shape: List<Float32Vector2D> = emptyList(),
|
||||
public var layers: MutableList<Layer> = ArrayList(),
|
||||
public val properties: MutableMeta = MutableMeta(),
|
||||
) {
|
||||
|
@ -0,0 +1,71 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import space.kscience.kmath.geometry.GeometrySpace
|
||||
import space.kscience.kmath.geometry.Vector2D
|
||||
import space.kscience.kmath.operations.ScaleOperations
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@Serializable(Float32Euclidean2DSpace.VectorSerializer::class)
|
||||
public interface Float32Vector2D: Vector2D<Float>
|
||||
|
||||
|
||||
public object Float32Euclidean2DSpace :
|
||||
GeometrySpace<Float32Vector2D>,
|
||||
ScaleOperations<Float32Vector2D> {
|
||||
|
||||
@Serializable
|
||||
@SerialName("Float32Vector2D")
|
||||
private data class Vector2DImpl(
|
||||
override val x: Float,
|
||||
override val y: Float,
|
||||
) : Float32Vector2D
|
||||
|
||||
public object VectorSerializer : KSerializer<Float32Vector2D> {
|
||||
private val proxySerializer = Vector2DImpl.serializer()
|
||||
override val descriptor: SerialDescriptor get() = proxySerializer.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Float32Vector2D = decoder.decodeSerializableValue(proxySerializer)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Float32Vector2D) {
|
||||
val vector = value as? Vector2DImpl ?: Vector2DImpl(value.x, value.y)
|
||||
encoder.encodeSerializableValue(proxySerializer, vector)
|
||||
}
|
||||
}
|
||||
|
||||
public fun vector(x: Float, y: Float): Float32Vector2D =
|
||||
Vector2DImpl(x, y)
|
||||
|
||||
public fun vector(x: Number, y: Number): Float32Vector2D =
|
||||
vector(x.toFloat(), y.toFloat())
|
||||
|
||||
override val zero: Float32Vector2D by lazy { vector(0f, 0f) }
|
||||
|
||||
override fun norm(arg: Float32Vector2D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2)).toDouble()
|
||||
|
||||
public fun Float32Vector2D.norm(): Double = norm(this)
|
||||
|
||||
override fun Float32Vector2D.unaryMinus(): Float32Vector2D = vector(-x, -y)
|
||||
|
||||
override fun Float32Vector2D.distanceTo(other: Float32Vector2D): Double = (this - other).norm()
|
||||
|
||||
override fun add(left: Float32Vector2D, right: Float32Vector2D): Float32Vector2D =
|
||||
vector(left.x + right.x, left.y + right.y)
|
||||
|
||||
override fun scale(a: Float32Vector2D, value: Double): Float32Vector2D =
|
||||
vector(a.x * value, a.y * value)
|
||||
|
||||
override fun Float32Vector2D.dot(other: Float32Vector2D): Double =
|
||||
(x * other.x + y * other.y).toDouble()
|
||||
|
||||
public val xAxis: Float32Vector2D = vector(1.0, 0.0)
|
||||
public val yAxis: Float32Vector2D = vector(0.0, 1.0)
|
||||
}
|
||||
|
||||
public fun Float32Vector2D(x: Number, y: Number): Float32Vector2D = Float32Euclidean2DSpace.vector(x, y)
|
@ -0,0 +1,113 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import space.kscience.kmath.geometry.GeometrySpace
|
||||
import space.kscience.kmath.geometry.Vector3D
|
||||
import space.kscience.kmath.operations.ScaleOperations
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@Serializable(Float32Euclidean3DSpace.VectorSerializer::class)
|
||||
public interface Float32Vector3D: Vector3D<Float>
|
||||
|
||||
|
||||
public object Float32Euclidean3DSpace :
|
||||
GeometrySpace<Float32Vector3D>,
|
||||
ScaleOperations<Float32Vector3D>{
|
||||
|
||||
@Serializable
|
||||
@SerialName("Float32Vector3D")
|
||||
private data class Vector3DImpl(
|
||||
override val x: Float,
|
||||
override val y: Float,
|
||||
override val z: Float,
|
||||
) : Float32Vector3D
|
||||
|
||||
public object VectorSerializer : KSerializer<Float32Vector3D> {
|
||||
private val proxySerializer = Vector3DImpl.serializer()
|
||||
override val descriptor: SerialDescriptor get() = proxySerializer.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Float32Vector3D = decoder.decodeSerializableValue(proxySerializer)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Float32Vector3D) {
|
||||
val vector = value as? Vector3DImpl ?: Vector3DImpl(value.x, value.y, value.z)
|
||||
encoder.encodeSerializableValue(proxySerializer, vector)
|
||||
}
|
||||
}
|
||||
|
||||
public fun vector(x: Float, y: Float, z: Float): Float32Vector3D =
|
||||
Vector3DImpl(x, y, z)
|
||||
|
||||
public fun vector(x: Number, y: Number, z: Number): Float32Vector3D =
|
||||
vector(x.toFloat(), y.toFloat(), z.toFloat())
|
||||
|
||||
override val zero: Float32Vector3D by lazy { vector(0.0, 0.0, 0.0) }
|
||||
|
||||
override fun norm(arg: Float32Vector3D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2) + arg.z.pow(2)).toDouble()
|
||||
|
||||
public fun Float32Vector3D.norm(): Double = norm(this)
|
||||
|
||||
override fun Float32Vector3D.unaryMinus(): Float32Vector3D = vector(-x, -y, -z)
|
||||
|
||||
override fun Float32Vector3D.distanceTo(other: Float32Vector3D): Double = (this - other).norm()
|
||||
|
||||
override fun add(left: Float32Vector3D, right: Float32Vector3D): Float32Vector3D =
|
||||
vector(left.x + right.x, left.y + right.y, left.z + right.z)
|
||||
|
||||
override fun scale(a: Float32Vector3D, value: Double): Float32Vector3D =
|
||||
vector(a.x * value, a.y * value, a.z * value)
|
||||
|
||||
override fun Float32Vector3D.dot(other: Float32Vector3D): Double =
|
||||
(x * other.x + y * other.y + z * other.z).toDouble()
|
||||
|
||||
private fun leviCivita(i: Int, j: Int, k: Int): Int = when {
|
||||
// even permutation
|
||||
i == 0 && j == 1 && k == 2 -> 1
|
||||
i == 1 && j == 2 && k == 0 -> 1
|
||||
i == 2 && j == 0 && k == 1 -> 1
|
||||
// odd permutations
|
||||
i == 2 && j == 1 && k == 0 -> -1
|
||||
i == 0 && j == 2 && k == 1 -> -1
|
||||
i == 1 && j == 0 && k == 2 -> -1
|
||||
|
||||
else -> 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute vector product of [first] and [second]. The basis is assumed to be right-handed.
|
||||
*/
|
||||
public fun vectorProduct(
|
||||
first: Float32Vector3D,
|
||||
second: Float32Vector3D,
|
||||
): Float32Vector3D {
|
||||
var x = 0.0
|
||||
var y = 0.0
|
||||
var z = 0.0
|
||||
|
||||
for (j in (0..2)) {
|
||||
for (k in (0..2)) {
|
||||
x += leviCivita(0, j, k) * first[j] * second[k]
|
||||
y += leviCivita(1, j, k) * first[j] * second[k]
|
||||
z += leviCivita(2, j, k) * first[j] * second[k]
|
||||
}
|
||||
}
|
||||
|
||||
return vector(x, y, z)
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector product in a right-handed basis
|
||||
*/
|
||||
public infix fun Float32Vector3D.cross(other: Float32Vector3D): Float32Vector3D = vectorProduct(this, other)
|
||||
|
||||
public val xAxis: Float32Vector3D = vector(1.0, 0.0, 0.0)
|
||||
public val yAxis: Float32Vector3D = vector(0.0, 1.0, 0.0)
|
||||
public val zAxis: Float32Vector3D = vector(0.0, 0.0, 1.0)
|
||||
}
|
||||
|
||||
public fun Float32Vector3D(x: Number, y: Number, z: Number): Float32Vector3D = Float32Euclidean3DSpace.vector(x,y,z)
|
@ -13,17 +13,17 @@ public interface GeometryBuilder<T : Any> {
|
||||
* @param normal optional external normal to the face
|
||||
* @param meta optional additional platform-specific parameters like color or texture index
|
||||
*/
|
||||
public fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = Meta.EMPTY)
|
||||
public fun face(vertex1: Float32Vector3D, vertex2: Float32Vector3D, vertex3: Float32Vector3D, normal: Float32Vector3D? = null, meta: Meta = Meta.EMPTY)
|
||||
|
||||
public fun build(): T
|
||||
}
|
||||
|
||||
public fun GeometryBuilder<*>.face4(
|
||||
vertex1: Point3D,
|
||||
vertex2: Point3D,
|
||||
vertex3: Point3D,
|
||||
vertex4: Point3D,
|
||||
normal: Point3D? = null,
|
||||
vertex1: Float32Vector3D,
|
||||
vertex2: Float32Vector3D,
|
||||
vertex3: Float32Vector3D,
|
||||
vertex4: Float32Vector3D,
|
||||
normal: Float32Vector3D? = null,
|
||||
meta: Meta = Meta.EMPTY
|
||||
) {
|
||||
face(vertex1, vertex2, vertex3, normal, meta)
|
||||
@ -37,9 +37,9 @@ public interface GeometrySolid : Solid {
|
||||
public fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>)
|
||||
}
|
||||
|
||||
public fun <T : Any> GeometryBuilder<T>.cap(shape: List<Point3D>, normal: Point3D? = null) {
|
||||
public fun <T : Any> GeometryBuilder<T>.cap(shape: List<Float32Vector3D>, normal: Float32Vector3D? = null) {
|
||||
//FIXME won't work for non-convex shapes
|
||||
val center = Point3D(
|
||||
val center = Float32Vector3D(
|
||||
shape.map { it.x }.average(),
|
||||
shape.map { it.y }.average(),
|
||||
shape.map { it.z }.average()
|
||||
|
@ -7,14 +7,14 @@ import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
public interface Hexagon : GeometrySolid {
|
||||
public val node1: Point3D
|
||||
public val node2: Point3D
|
||||
public val node3: Point3D
|
||||
public val node4: Point3D
|
||||
public val node5: Point3D
|
||||
public val node6: Point3D
|
||||
public val node7: Point3D
|
||||
public val node8: Point3D
|
||||
public val node1: Float32Vector3D
|
||||
public val node2: Float32Vector3D
|
||||
public val node3: Float32Vector3D
|
||||
public val node4: Float32Vector3D
|
||||
public val node5: Float32Vector3D
|
||||
public val node6: Float32Vector3D
|
||||
public val node7: Float32Vector3D
|
||||
public val node8: Float32Vector3D
|
||||
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
geometryBuilder.face4(node1, node4, node3, node2)
|
||||
@ -41,14 +41,14 @@ public class Box(
|
||||
private inline val dy get() = ySize / 2
|
||||
private inline val dz get() = zSize / 2
|
||||
|
||||
override val node1: Point3D get() = Point3D(-dx, -dy, -dz)
|
||||
override val node2: Point3D get() = Point3D(dx, -dy, -dz)
|
||||
override val node3: Point3D get() = Point3D(dx, dy, -dz)
|
||||
override val node4: Point3D get() = Point3D(-dx, dy, -dz)
|
||||
override val node5: Point3D get() = Point3D(-dx, -dy, dz)
|
||||
override val node6: Point3D get() = Point3D(dx, -dy, dz)
|
||||
override val node7: Point3D get() = Point3D(dx, dy, dz)
|
||||
override val node8: Point3D get() = Point3D(-dx, dy, dz)
|
||||
override val node1: Float32Vector3D get() = Float32Vector3D(-dx, -dy, -dz)
|
||||
override val node2: Float32Vector3D get() = Float32Vector3D(dx, -dy, -dz)
|
||||
override val node3: Float32Vector3D get() = Float32Vector3D(dx, dy, -dz)
|
||||
override val node4: Float32Vector3D get() = Float32Vector3D(-dx, dy, -dz)
|
||||
override val node5: Float32Vector3D get() = Float32Vector3D(-dx, -dy, dz)
|
||||
override val node6: Float32Vector3D get() = Float32Vector3D(dx, -dy, dz)
|
||||
override val node7: Float32Vector3D get() = Float32Vector3D(dx, dy, dz)
|
||||
override val node8: Float32Vector3D get() = Float32Vector3D(-dx, dy, dz)
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
@ -63,26 +63,26 @@ public inline fun MutableVisionContainer<Solid>.box(
|
||||
@Serializable
|
||||
@SerialName("solid.hexagon")
|
||||
public class GenericHexagon(
|
||||
override val node1: Point3D,
|
||||
override val node2: Point3D,
|
||||
override val node3: Point3D,
|
||||
override val node4: Point3D,
|
||||
override val node5: Point3D,
|
||||
override val node6: Point3D,
|
||||
override val node7: Point3D,
|
||||
override val node8: Point3D,
|
||||
override val node1: Float32Vector3D,
|
||||
override val node2: Float32Vector3D,
|
||||
override val node3: Float32Vector3D,
|
||||
override val node4: Float32Vector3D,
|
||||
override val node5: Float32Vector3D,
|
||||
override val node6: Float32Vector3D,
|
||||
override val node7: Float32Vector3D,
|
||||
override val node8: Float32Vector3D,
|
||||
) : SolidBase<GenericHexagon>(), Hexagon
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Solid>.hexagon(
|
||||
node1: Point3D,
|
||||
node2: Point3D,
|
||||
node3: Point3D,
|
||||
node4: Point3D,
|
||||
node5: Point3D,
|
||||
node6: Point3D,
|
||||
node7: Point3D,
|
||||
node8: Point3D,
|
||||
node1: Float32Vector3D,
|
||||
node2: Float32Vector3D,
|
||||
node3: Float32Vector3D,
|
||||
node4: Float32Vector3D,
|
||||
node5: Float32Vector3D,
|
||||
node6: Float32Vector3D,
|
||||
node7: Float32Vector3D,
|
||||
node8: Float32Vector3D,
|
||||
name: String? = null,
|
||||
action: Hexagon.() -> Unit = {},
|
||||
): Hexagon = GenericHexagon(node1, node2, node3, node4, node5, node6, node7, node8).apply(action).also { setChild(name, it) }
|
@ -15,7 +15,7 @@ import space.kscience.visionforge.*
|
||||
public abstract class LightSource : SolidBase<LightSource>() {
|
||||
override val descriptor: MetaDescriptor get() = LightSource.descriptor
|
||||
|
||||
public val color: ColorAccessor by color(SolidMaterial.COLOR_KEY)
|
||||
public val color: ColorAccessor by colorProperty(SolidMaterial.COLOR_KEY)
|
||||
public var intensity: Number by properties.root(includeStyles = false).number(INTENSITY_KEY) { 1.0 }
|
||||
|
||||
public companion object {
|
||||
@ -70,6 +70,6 @@ public fun MutableVisionContainer<Solid>.pointLight(
|
||||
name: String? = null,
|
||||
block: PointLightSource.() -> Unit = {},
|
||||
): PointLightSource = PointLightSource().apply(block).also {
|
||||
it.position = Point3D(x, y, z)
|
||||
it.position = Float32Vector3D(x, y, z)
|
||||
setChild(name, it)
|
||||
}
|
@ -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)
|
||||
}
|
@ -3,11 +3,14 @@ package space.kscience.visionforge.solid
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.number
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.line")
|
||||
public class PolyLine(public val points: List<Point3D>) : SolidBase<PolyLine>() {
|
||||
public class PolyLine(public val points: List<Float32Vector3D>) : SolidBase<PolyLine>() {
|
||||
|
||||
//var lineType by string()
|
||||
public var thickness: Number by properties.root(inherit = false, includeStyles = true).number { DEFAULT_THICKNESS }
|
||||
@ -19,7 +22,7 @@ public class PolyLine(public val points: List<Point3D>) : SolidBase<PolyLine>()
|
||||
|
||||
@VisionBuilder
|
||||
public fun MutableVisionContainer<Solid>.polyline(
|
||||
vararg points: Point3D,
|
||||
vararg points: Float32Vector3D,
|
||||
name: String? = null,
|
||||
action: PolyLine.() -> Unit = {},
|
||||
): PolyLine = PolyLine(points.toList()).apply(action).also { setChild(name, it) }
|
@ -8,6 +8,9 @@ import space.kscience.dataforge.meta.descriptors.value
|
||||
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.complex.QuaternionField
|
||||
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
|
||||
@ -58,8 +61,6 @@ public interface Solid : Vision {
|
||||
|
||||
public val ROTATION_KEY: Name = "rotation".asName()
|
||||
|
||||
public val QUATERNION_KEY: Name = "quaternion".asName()
|
||||
|
||||
public val X_ROTATION_KEY: Name = ROTATION_KEY + X_KEY
|
||||
public val Y_ROTATION_KEY: Name = ROTATION_KEY + Y_KEY
|
||||
public val Z_ROTATION_KEY: Name = ROTATION_KEY + Z_KEY
|
||||
@ -122,15 +123,6 @@ public var Solid.layer: Int
|
||||
|
||||
// Common properties
|
||||
|
||||
public enum class RotationOrder {
|
||||
XYZ,
|
||||
YZX,
|
||||
ZXY,
|
||||
XZY,
|
||||
YXZ,
|
||||
ZYX
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotation order
|
||||
*/
|
||||
@ -174,18 +166,21 @@ internal fun point(
|
||||
defaultX: Float,
|
||||
defaultY: Float = defaultX,
|
||||
defaultZ: Float = defaultX,
|
||||
): ReadWriteProperty<Solid, Point3D?> =
|
||||
object : ReadWriteProperty<Solid, Point3D?> {
|
||||
override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? {
|
||||
): ReadWriteProperty<Solid, Float32Vector3D?> =
|
||||
object : ReadWriteProperty<Solid, Float32Vector3D?> {
|
||||
override fun getValue(thisRef: Solid, property: KProperty<*>): Float32Vector3D? {
|
||||
val item = thisRef.properties.own?.get(name) ?: return null
|
||||
return object : Point3D {
|
||||
//using dynamic property accessor because values could change
|
||||
return object : Float32Vector3D {
|
||||
override val x: Float get() = item[X_KEY]?.float ?: defaultX
|
||||
override val y: Float get() = item[Y_KEY]?.float ?: defaultY
|
||||
override val z: Float get() = item[Z_KEY]?.float ?: defaultZ
|
||||
|
||||
override fun toString(): String = item.toString()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) {
|
||||
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Float32Vector3D?) {
|
||||
if (value == null) {
|
||||
thisRef.properties.setProperty(name, null)
|
||||
} else {
|
||||
@ -196,9 +191,9 @@ internal fun point(
|
||||
}
|
||||
}
|
||||
|
||||
public var Solid.position: Point3D? by point(POSITION_KEY, 0f)
|
||||
public var Solid.rotation: Point3D? by point(ROTATION_KEY, 0f)
|
||||
public var Solid.scale: Point3D? by point(SCALE_KEY, 1f)
|
||||
public var Solid.position: Float32Vector3D? by point(POSITION_KEY, 0f)
|
||||
public var Solid.rotation: Float32Vector3D? by point(ROTATION_KEY, 0f)
|
||||
public var Solid.scale: Float32Vector3D? by point(SCALE_KEY, 1f)
|
||||
|
||||
public var Solid.x: Number by float(X_POSITION_KEY, 0f)
|
||||
public var Solid.y: Number by float(Y_POSITION_KEY, 0f)
|
||||
@ -208,33 +203,49 @@ public var Solid.rotationX: Number by float(X_ROTATION_KEY, 0f)
|
||||
public var Solid.rotationY: Number by float(Y_ROTATION_KEY, 0f)
|
||||
public var Solid.rotationZ: Number by float(Z_ROTATION_KEY, 0f)
|
||||
|
||||
public var Solid.quaternion: Pair<Float, Point3D>?
|
||||
get() = properties.getValue(Solid.QUATERNION_KEY)?.list?.let {
|
||||
/**
|
||||
* Raw quaternion value defined in properties
|
||||
*/
|
||||
public var Solid.quaternionOrNull: Quaternion?
|
||||
get() = properties.getValue(ROTATION_KEY)?.list?.let {
|
||||
require(it.size == 4) { "Quaternion must be a number array of 4 elements" }
|
||||
it[0].float to Point3D(it[1].float, it[2].float, it[3].float)
|
||||
Quaternion(it[0].float, it[1].float, it[2].float, it[3].float)
|
||||
}
|
||||
set(value) {
|
||||
properties.setValue(
|
||||
Solid.QUATERNION_KEY,
|
||||
ROTATION_KEY,
|
||||
value?.let {
|
||||
ListValue(
|
||||
value.first,
|
||||
value.second.x,
|
||||
value.second.y,
|
||||
value.second.z
|
||||
value.w,
|
||||
value.x,
|
||||
value.y,
|
||||
value.z
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
//public var Solid.quaternion: Quaternion?
|
||||
// get() = meta[Solid::quaternion.name]?.value?.doubleArray?.let { Quaternion(it) }
|
||||
// set(value) {
|
||||
// meta[Solid::quaternion.name] = value?.values?.asValue()
|
||||
// }
|
||||
|
||||
/**
|
||||
* Quaternion value including information from euler angles
|
||||
*/
|
||||
public var Solid.quaternion: Quaternion
|
||||
get() = quaternionOrNull ?: Quaternion.fromEuler(
|
||||
rotationX.radians,
|
||||
rotationY.radians,
|
||||
rotationZ.radians,
|
||||
rotationOrder
|
||||
)
|
||||
set(value) {
|
||||
quaternionOrNull = value
|
||||
}
|
||||
|
||||
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)
|
||||
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): Unit = with(QuaternionField) {
|
||||
quaternion = Quaternion.fromRotation(angle, axis)*quaternion
|
||||
}
|
@ -17,7 +17,7 @@ import space.kscience.visionforge.VisionBuilder
|
||||
*/
|
||||
public interface PrototypeHolder {
|
||||
/**
|
||||
* Build or update prototype tree
|
||||
* Build or update the prototype tree
|
||||
*/
|
||||
@VisionBuilder
|
||||
public fun prototypes(builder: MutableVisionContainer<Solid>.() -> Unit)
|
||||
@ -43,6 +43,9 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, Mutable
|
||||
it to value
|
||||
}.toMap()
|
||||
|
||||
/**
|
||||
* Get a child solid with given relative [name] if it exists
|
||||
*/
|
||||
public operator fun get(name: Name): Solid? = children.getChild(name) as? Solid
|
||||
|
||||
private var prototypes: SolidGroup?
|
||||
@ -84,6 +87,8 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, Mutable
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun SolidGroup.get(name:String): Solid? = get(name.parseAsName())
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Solid>.solidGroup(
|
||||
name: Name? = null,
|
||||
@ -99,3 +104,8 @@ public inline fun MutableVisionContainer<Solid>.solidGroup(
|
||||
name: String,
|
||||
action: SolidGroup.() -> Unit = {},
|
||||
): SolidGroup = solidGroup(name.parseAsName(), action)
|
||||
|
||||
/**
|
||||
* Create a [SolidGroup] using given configuration [block]
|
||||
*/
|
||||
public inline fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)
|
@ -2,7 +2,9 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
@ -61,7 +63,7 @@ public class SolidReference(
|
||||
}
|
||||
|
||||
override fun getValue(name: Name, inherit: Boolean?, includeStyles: Boolean?): Value? {
|
||||
if(name == Vision.STYLE_KEY){
|
||||
if (name == Vision.STYLE_KEY) {
|
||||
return buildList {
|
||||
properties?.getValue(Vision.STYLE_KEY)?.list?.forEach {
|
||||
add(it)
|
||||
@ -90,7 +92,7 @@ public class SolidReference(
|
||||
prototype.getStyleProperty(name)?.value?.let { return it }
|
||||
}
|
||||
|
||||
if(inheritFlag){
|
||||
if (inheritFlag) {
|
||||
//5. own inheritance
|
||||
parent?.properties?.getValue(name, inheritFlag, includeStyles)?.let { return it }
|
||||
//6. prototype inheritance
|
||||
@ -226,13 +228,13 @@ internal class SolidReferenceChild(
|
||||
*/
|
||||
public fun MutableVisionContainer<Solid>.ref(
|
||||
templateName: Name,
|
||||
name: String? = null,
|
||||
name: Name? = null,
|
||||
): SolidReference = SolidReference(templateName).also { setChild(name, it) }
|
||||
|
||||
public fun MutableVisionContainer<Solid>.ref(
|
||||
templateName: String,
|
||||
name: String? = null,
|
||||
): SolidReference = ref(Name.parse(templateName), name)
|
||||
templateName: Name,
|
||||
name: String,
|
||||
): SolidReference = ref(templateName, name.parseAsName())
|
||||
|
||||
/**
|
||||
* Add new [SolidReference] wrapping given object and automatically adding it to the prototypes.
|
||||
@ -251,7 +253,7 @@ public fun SolidGroup.newRef(
|
||||
} else if (existing != obj) {
|
||||
error("Can't add different prototype on top of existing one")
|
||||
}
|
||||
return children.ref(prototypeName, name)
|
||||
return children.ref(prototypeName, name?.parseAsName())
|
||||
}
|
||||
|
||||
|
||||
|
@ -48,9 +48,12 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Sol
|
||||
|
||||
subclass(AmbientLightSource.serializer())
|
||||
subclass(PointLightSource.serializer())
|
||||
|
||||
subclass(AxesSolid.serializer())
|
||||
}
|
||||
|
||||
public val serializersModuleForSolids: SerializersModule = SerializersModule {
|
||||
|
||||
polymorphic(Vision::class) {
|
||||
subclass(SimpleVisionGroup.serializer())
|
||||
solids()
|
||||
@ -91,3 +94,7 @@ public inline fun VisionOutput.solid(options: Canvas3DOptions? = null, block: So
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun VisionOutput.solid(options: Canvas3DOptions.() -> Unit, block: SolidGroup.() -> Unit): SolidGroup =
|
||||
solid(Canvas3DOptions(options), block)
|
||||
|
@ -20,12 +20,12 @@ public class Sphere(
|
||||
) : SolidBase<Sphere>(), GeometrySolid {
|
||||
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Point3D {
|
||||
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Float32Vector3D {
|
||||
// This transformation matches three.js sphere implementation
|
||||
val y = r * cos(theta)
|
||||
val z = r * sin(theta) * sin(phi)
|
||||
val x = -r * sin(theta) * cos(phi)
|
||||
return Point3D(x, y, z)
|
||||
return Float32Vector3D(x, y, z)
|
||||
}
|
||||
|
||||
val segments = this.detail ?: 32
|
||||
|
@ -27,12 +27,12 @@ public class SphereLayer(
|
||||
require(outerRadius > 0) { "Outer radius must be positive" }
|
||||
require(innerRadius >= 0) { "inner radius must be non-negative" }
|
||||
|
||||
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Point3D {
|
||||
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Float32Vector3D {
|
||||
// This transformation matches three.js sphere implementation
|
||||
val y = r * cos(theta)
|
||||
val z = r * sin(theta) * sin(phi)
|
||||
val x = -r * sin(theta) * cos(phi)
|
||||
return Point3D(x, y, z)
|
||||
return Float32Vector3D(x, y, z)
|
||||
}
|
||||
|
||||
val segments = detail ?: 32
|
||||
|
@ -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 StlSolid: SolidBase<StlSolid>()
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.stl.url")
|
||||
public class StlUrlSolid(public val url: String) : StlSolid()
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.stl.binary")
|
||||
public class StlBinarySolid(public val data: ByteArray) : StlSolid()
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Solid>.stl(
|
||||
url: String,
|
||||
name: String? = null,
|
||||
action: StlSolid.() -> Unit = {},
|
||||
): StlSolid = StlUrlSolid(url).apply(action).also { setChild(name, it) }
|
@ -1,10 +1,5 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaProvider
|
||||
import space.kscience.dataforge.meta.float
|
||||
@ -13,105 +8,48 @@ import space.kscience.visionforge.solid.Solid.Companion.X_KEY
|
||||
import space.kscience.visionforge.solid.Solid.Companion.Y_KEY
|
||||
import space.kscience.visionforge.solid.Solid.Companion.Z_KEY
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
public const val PI2: Float = 2 * PI.toFloat()
|
||||
|
||||
@Serializable
|
||||
public data class Point2D(public var x: Float, public var y: Float)
|
||||
|
||||
public fun Point2D(x: Number, y: Number): Point2D = Point2D(x.toFloat(), y.toFloat())
|
||||
|
||||
public fun Point2D.toMeta(): Meta = Meta {
|
||||
public fun Float32Vector2D.toMeta(): Meta = Meta {
|
||||
X_KEY put x
|
||||
Y_KEY put y
|
||||
}
|
||||
|
||||
internal fun Meta.point2D(): Point2D = Point2D(this["x"].float ?: 0f, this["y"].float ?: 0f)
|
||||
internal fun Meta.toVector2D(): Float32Vector2D =
|
||||
Float32Vector2D(this["x"].float ?: 0f, this["y"].float ?: 0f)
|
||||
|
||||
@Serializable(Point3DSerializer::class)
|
||||
public interface Point3D {
|
||||
public val x: Float
|
||||
public val y: Float
|
||||
public val z: Float
|
||||
//@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
|
||||
//@Serializable(Point3DSerializer::class)
|
||||
//public interface MutablePoint3D : Float32Vector3D {
|
||||
// override var x: Float
|
||||
// override var y: Float
|
||||
// override var z: Float
|
||||
//}
|
||||
//
|
||||
//
|
||||
//public fun MutablePoint3D.normalizeInPlace() {
|
||||
// val norm = sqrt(x.pow(2) + y.pow(2) + z.pow(2))
|
||||
// x /= norm
|
||||
// y /= norm
|
||||
// z /= norm
|
||||
//}
|
||||
|
||||
public companion object {
|
||||
public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0)
|
||||
public val ONE: Point3D = Point3D(1.0, 1.0, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
|
||||
@Serializable(Point3DSerializer::class)
|
||||
public interface MutablePoint3D : Point3D {
|
||||
override var x: Float
|
||||
override var y: Float
|
||||
override var z: Float
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private class Point3DImpl(override var x: Float, override var y: Float, override var z: Float) : MutablePoint3D
|
||||
|
||||
internal object Point3DSerializer : KSerializer<Point3D> {
|
||||
|
||||
override val descriptor: SerialDescriptor = Point3DImpl.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutablePoint3D = decoder.decodeSerializableValue(Point3DImpl.serializer())
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Point3D) {
|
||||
val impl: Point3DImpl = (value as? Point3DImpl) ?: Point3DImpl(value.x, value.y, value.z)
|
||||
encoder.encodeSerializableValue(Point3DImpl.serializer(), impl)
|
||||
}
|
||||
}
|
||||
|
||||
public fun Point3D(x: Number, y: Number, z: Number): Point3D = Point3DImpl(x.toFloat(), y.toFloat(), z.toFloat())
|
||||
|
||||
public operator fun Point3D.plus(other: Point3D): Point3D = Point3D(
|
||||
this.x + other.x,
|
||||
this.y + other.y,
|
||||
this.z + other.z
|
||||
internal fun MetaProvider.point3D(default: Float = 0f) = Float32Euclidean3DSpace.vector(
|
||||
getMeta(X_KEY).float ?: default,
|
||||
getMeta(Y_KEY).float ?: default,
|
||||
getMeta(Z_KEY).float ?: default
|
||||
)
|
||||
|
||||
public operator fun Point3D.minus(other: Point3D): Point3D = Point3D(
|
||||
this.x - other.x,
|
||||
this.y - other.y,
|
||||
this.z - other.z
|
||||
)
|
||||
|
||||
public operator fun Point3D.unaryMinus(): Point3D = Point3D(
|
||||
-x,
|
||||
-y,
|
||||
-z
|
||||
)
|
||||
|
||||
public infix fun Point3D.cross(other: Point3D): Point3D = Point3D(
|
||||
y * other.z - z * other.y,
|
||||
z * other.x - x * other.z,
|
||||
x * other.y - y * other.x
|
||||
)
|
||||
|
||||
public fun MutablePoint3D.normalizeInPlace() {
|
||||
val norm = sqrt(x.pow(2) + y.pow(2) + z.pow(2))
|
||||
x /= norm
|
||||
y /= norm
|
||||
z /= norm
|
||||
}
|
||||
|
||||
internal fun MetaProvider.point3D(default: Float = 0f) = object : Point3D {
|
||||
override val x: Float by float(default)
|
||||
override val y: Float by float(default)
|
||||
override val z: Float by float(default)
|
||||
}
|
||||
|
||||
public fun Point3D.toMeta(): Meta = Meta {
|
||||
public fun Float32Vector3D.toMeta(): Meta = Meta {
|
||||
X_KEY put x
|
||||
Y_KEY put y
|
||||
Z_KEY put z
|
||||
}
|
||||
|
||||
|
||||
internal fun Meta.toVector(default: Float = 0f) = Point3D(
|
||||
internal fun Meta.toVector3D(default: Float = 0f) = Float32Vector3D(
|
||||
this[X_KEY].float ?: default,
|
||||
this[Y_KEY].float ?: default,
|
||||
this[Z_KEY].float ?: default
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -3,6 +3,7 @@ package space.kscience.visionforge.solid.transform
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.kmath.complex.QuaternionField
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.solid.*
|
||||
|
||||
@ -14,10 +15,7 @@ internal fun Solid.updateFrom(other: Solid): Solid {
|
||||
x += other.x
|
||||
y += other.y
|
||||
z += other.y
|
||||
if(quaternion != null || other.quaternion != null) TODO("Quaternion support not implemented")
|
||||
rotationX += other.rotationX
|
||||
rotationY += other.rotationY
|
||||
rotationZ += other.rotationZ
|
||||
quaternion = with(QuaternionField) { other.quaternion * quaternion }
|
||||
scaleX *= other.scaleX
|
||||
scaleY *= other.scaleY
|
||||
scaleZ *= other.scaleZ
|
||||
|
@ -18,7 +18,7 @@ class CompositeTest {
|
||||
detail = 32
|
||||
}
|
||||
material {
|
||||
color.set("pink")
|
||||
color("pink")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,10 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.meta.getIndexed
|
||||
import space.kscience.dataforge.meta.toMeta
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ConvexTest {
|
||||
@OptIn(DFExperimental::class)
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
@Test
|
||||
fun testConvexBuilder() {
|
||||
val group = testSolids.solidGroup {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user