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:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 40
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3.5.3
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2.5.0
|
uses: actions/setup-java@v3.12.0
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: liberica
|
distribution: liberica
|
||||||
- name: execute build
|
- name: execute build
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2.7.1
|
||||||
with:
|
with:
|
||||||
arguments: build
|
arguments: build
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
- MeshLine for thick lines
|
- MeshLine for thick lines
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- Color accessor property is now `colorProperty`. Color uses `invoke` instead of `set`
|
||||||
- API update for server and pages
|
- API update for server and pages
|
||||||
- Edges moved to solids module for easier construction
|
- Edges moved to solids module for easier construction
|
||||||
- Visions **must** be rooted in order to subscribe to updates.
|
- Visions **must** be rooted in order to subscribe to updates.
|
||||||
@ -20,6 +21,7 @@
|
|||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Jupyter integration for IDEA and Jupyter lab.
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ val fxVersion by extra("11")
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.3.0-dev-9"
|
version = "0.3.0-dev-13"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -4,12 +4,18 @@ import space.kscience.dataforge.meta.*
|
|||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.parseAsName
|
import space.kscience.dataforge.names.parseAsName
|
||||||
import space.kscience.dataforge.names.plus
|
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.MutableVisionContainer
|
||||||
import space.kscience.visionforge.isEmpty
|
import space.kscience.visionforge.isEmpty
|
||||||
import space.kscience.visionforge.set
|
import space.kscience.visionforge.set
|
||||||
import space.kscience.visionforge.solid.*
|
import space.kscience.visionforge.solid.*
|
||||||
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
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()
|
private val volumesName = Name.EMPTY //"volumes".asName()
|
||||||
|
|
||||||
@ -27,17 +33,15 @@ private data class RootToSolidContext(
|
|||||||
val colorCache: MutableMap<Meta, String> = mutableMapOf(),
|
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) {
|
private fun Solid.rotate(rot: DoubleArray) {
|
||||||
val xAngle = atan2(-rot[5], rot[8])
|
val matrix = VirtualMatrix(3, 3) { i, j -> rot[i * 3 + j] }
|
||||||
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
|
quaternion = Quaternion.fromRotationMatrix(matrix)
|
||||||
val zAngle = atan2(-rot[1], rot[0])
|
|
||||||
rotation = Point3D(xAngle, yAngle, zAngle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.translate(trans: DoubleArray) {
|
private fun Solid.translate(trans: DoubleArray) {
|
||||||
val (x, y, z) = trans
|
val (x, y, z) = trans
|
||||||
position = Point3D(x, y, z)
|
position = Float32Vector3D(x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.useMatrix(matrix: DGeoMatrix?) {
|
private fun Solid.useMatrix(matrix: DGeoMatrix?) {
|
||||||
@ -72,7 +76,7 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) {
|
|||||||
val fScale by matrix.meta.doubleArray()
|
val fScale by matrix.meta.doubleArray()
|
||||||
translate(fTranslation)
|
translate(fTranslation)
|
||||||
rotate(fRotationMatrix)
|
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" }
|
require(fNz > 1) { "The polyhedron geometry requires at least two planes" }
|
||||||
val baseRadius = fRmax[0]
|
val baseRadius = fRmax[0]
|
||||||
shape {
|
shape {
|
||||||
(0..fNedges).forEach {
|
(0..<fNedges).forEach {
|
||||||
val phi = deltaphi * fNedges * it + startphi
|
val phi = deltaphi / fNedges * it + startphi
|
||||||
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
point(baseRadius * cos(phi), baseRadius * sin(phi))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(0 until fNz).forEach { index ->
|
(0 until fNz).forEach { index ->
|
||||||
@ -223,7 +227,7 @@ private fun SolidGroup.addShape(
|
|||||||
"TGeoShapeAssembly" -> {
|
"TGeoShapeAssembly" -> {
|
||||||
val fVolume by shape.dObject(::DGeoVolume)
|
val fVolume by shape.dObject(::DGeoVolume)
|
||||||
fVolume?.let { volume ->
|
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)
|
val fDz by shape.meta.double(0.0)
|
||||||
//TODO check proper node order
|
//TODO check proper node order
|
||||||
val node1 = Point3D(-fBl1, -fH1, -fDz)
|
val node1 = Float32Vector3D(-fBl1, -fH1, -fDz)
|
||||||
val node2 = Point3D(fBl1, -fH1, -fDz)
|
val node2 = Float32Vector3D(fBl1, -fH1, -fDz)
|
||||||
val node3 = Point3D(fTl1, fH1, -fDz)
|
val node3 = Float32Vector3D(fTl1, fH1, -fDz)
|
||||||
val node4 = Point3D(-fTl1, fH1, -fDz)
|
val node4 = Float32Vector3D(-fTl1, fH1, -fDz)
|
||||||
val node5 = Point3D(-fBl2, -fH2, fDz)
|
val node5 = Float32Vector3D(-fBl2, -fH2, fDz)
|
||||||
val node6 = Point3D(fBl2, -fH2, fDz)
|
val node6 = Float32Vector3D(fBl2, -fH2, fDz)
|
||||||
val node7 = Point3D(fTl2, fH2, fDz)
|
val node7 = Float32Vector3D(fTl2, fH2, fDz)
|
||||||
val node8 = Point3D(-fTl2, fH2, fDz)
|
val node8 = Float32Vector3D(-fTl2, fH2, fDz)
|
||||||
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
|
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +268,7 @@ private fun SolidGroup.addShape(
|
|||||||
val fScale by shape.dObject(::DGeoScale)
|
val fScale by shape.dObject(::DGeoScale)
|
||||||
fShape?.let { scaledShape ->
|
fShape?.let { scaledShape ->
|
||||||
solidGroup(name?.let { Name.parse(it) }) {
|
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)
|
addShape(scaledShape, context)
|
||||||
apply(block)
|
apply(block)
|
||||||
}
|
}
|
||||||
@ -328,7 +332,7 @@ private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid?
|
|||||||
group
|
group
|
||||||
}.apply {
|
}.apply {
|
||||||
volume.fMedium?.let { medium ->
|
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) {
|
if (!context.ignoreRootColors) {
|
||||||
@ -348,29 +352,29 @@ private fun SolidGroup.addRootVolume(
|
|||||||
cache: Boolean = true,
|
cache: Boolean = true,
|
||||||
block: Solid.() -> Unit = {},
|
block: Solid.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
val combinedName = name?.parseAsName()?.let {
|
||||||
val combinedName = if (volume.fName.isEmpty()) {
|
// this fix is required to work around malformed root files with duplicated node names
|
||||||
name
|
if (get(it) != null) {
|
||||||
} else if (name == null) {
|
it.withIndex(volume.hashCode().toString(16))
|
||||||
volume.fName
|
|
||||||
} else {
|
} else {
|
||||||
"${name}_${volume.fName}"
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cache) {
|
if (!cache) {
|
||||||
val group = buildVolume(volume, context)?.apply(block)
|
val group = buildVolume(volume, context)?.apply(block) ?: return
|
||||||
setChild(combinedName?.let { Name.parse(it) }, group)
|
setChild(combinedName, group)
|
||||||
} else {
|
} else {
|
||||||
val templateName = volumesName + volume.name
|
val templateName = volumesName + volume.name
|
||||||
val existing = getPrototype(templateName)
|
val existing = context.prototypeHolder.getPrototype(templateName)
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
context.prototypeHolder.prototypes {
|
context.prototypeHolder.prototypes {
|
||||||
val group = buildVolume(volume, context)
|
val group = buildVolume(volume, context) ?: return@prototypes
|
||||||
setChild(templateName, group)
|
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 xAngle = atan2(-rot[5], rot[8])
|
||||||
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
|
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
|
||||||
val zAngle = atan2(-rot[1], rot[0])
|
val zAngle = atan2(-rot[1], rot[0])
|
||||||
rotation = Point3D(xAngle, yAngle, zAngle)
|
rotation = Float32Vector3D(xAngle, yAngle, zAngle)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.translate(trans: DoubleArray) {
|
private fun Solid.translate(trans: DoubleArray) {
|
||||||
val (x, y, z) = trans
|
val (x, y, z) = trans
|
||||||
position = Point3D(x, y, z)
|
position = Float32Vector3D(x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.useMatrix(matrix: TGeoMatrix?) {
|
private fun Solid.useMatrix(matrix: TGeoMatrix?) {
|
||||||
@ -52,7 +52,7 @@ private fun Solid.useMatrix(matrix: TGeoMatrix?) {
|
|||||||
translate(matrix.fTranslation)
|
translate(matrix.fTranslation)
|
||||||
rotate(matrix.fRotationMatrix)
|
rotate(matrix.fRotationMatrix)
|
||||||
val (xScale, yScale, zScale) = matrix.fScale
|
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.Solid
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.solid.ambientLight
|
import space.kscience.visionforge.solid.ambientLight
|
||||||
import space.kscience.visionforge.solid.set
|
import space.kscience.visionforge.solid.invoke
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
|||||||
console.info("Marking layers for file $name")
|
console.info("Marking layers for file $name")
|
||||||
markLayers()
|
markLayers()
|
||||||
ambientLight {
|
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.react.render
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.solid.ambientLight
|
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.solid.three.ThreePlugin
|
||||||
import space.kscience.visionforge.startApplication
|
import space.kscience.visionforge.startApplication
|
||||||
import styled.injectGlobal
|
import styled.injectGlobal
|
||||||
@ -49,7 +49,7 @@ private class GDMLDemoApp : Application {
|
|||||||
child(GDMLApp) {
|
child(GDMLApp) {
|
||||||
val vision = GdmlShowCase.cubes().toVision().apply {
|
val vision = GdmlShowCase.cubes().toVision().apply {
|
||||||
ambientLight {
|
ambientLight {
|
||||||
color.set(Colors.white)
|
color(Colors.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
|
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
|
||||||
|
@ -7,7 +7,8 @@ kscience{
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin{
|
kotlin{
|
||||||
js(IR){
|
explicitApi = null
|
||||||
|
js{
|
||||||
useCommonJs()
|
useCommonJs()
|
||||||
browser {
|
browser {
|
||||||
binaries.executable()
|
binaries.executable()
|
||||||
|
@ -76,7 +76,7 @@ private class JsPlaygroundApp : Application {
|
|||||||
solids = playgroundContext.request(Solids)
|
solids = playgroundContext.request(Solids)
|
||||||
solid {
|
solid {
|
||||||
ambientLight {
|
ambientLight {
|
||||||
color.set(Colors.white)
|
color(Colors.white)
|
||||||
}
|
}
|
||||||
repeat(100) {
|
repeat(100) {
|
||||||
sphere(5, name = "sphere[$it]") {
|
sphere(5, name = "sphere[$it]") {
|
||||||
@ -84,7 +84,7 @@ private class JsPlaygroundApp : Application {
|
|||||||
y = random.nextDouble(-300.0, 300.0)
|
y = random.nextDouble(-300.0, 300.0)
|
||||||
z = random.nextDouble(-300.0, 300.0)
|
z = random.nextDouble(-300.0, 300.0)
|
||||||
material {
|
material {
|
||||||
color.set(random.nextInt())
|
color(random.nextInt())
|
||||||
}
|
}
|
||||||
detail = 16
|
detail = 16
|
||||||
}
|
}
|
||||||
|
@ -42,13 +42,13 @@ val GravityDemo = fc<DemoProps> { props ->
|
|||||||
solids = props.solids
|
solids = props.solids
|
||||||
solid {
|
solid {
|
||||||
pointLight(200, 200, 200, name = "light"){
|
pointLight(200, 200, 200, name = "light"){
|
||||||
color.set(Colors.white)
|
color(Colors.white)
|
||||||
}
|
}
|
||||||
ambientLight()
|
ambientLight()
|
||||||
|
|
||||||
sphere(5.0, "ball") {
|
sphere(5.0, "ball") {
|
||||||
detail = 16
|
detail = 16
|
||||||
color.set("red")
|
color("red")
|
||||||
val h = 100.0
|
val h = 100.0
|
||||||
y = h
|
y = h
|
||||||
solids.context.launch {
|
solids.context.launch {
|
||||||
|
@ -40,6 +40,8 @@ kscience {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin.explicitApi = null
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package ru.mipt.npm.muon.monitor
|
package ru.mipt.npm.muon.monitor
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
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) {
|
private fun MutableVisionContainer<Solid>.pixel(pixel: SC1) {
|
||||||
val group = solidGroup(pixel.name) {
|
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)
|
box(pixel.xSize, pixel.ySize, pixel.zSize)
|
||||||
label(pixel.name) {
|
label(pixel.name) {
|
||||||
z = -Monitor.PIXEL_Z_SIZE / 2 - 5
|
z = -Monitor.PIXEL_Z_SIZE / 2 - 5
|
||||||
@ -39,7 +39,7 @@ class Model(val manager: VisionManager) {
|
|||||||
val root: SolidGroup = SolidGroup().apply {
|
val root: SolidGroup = SolidGroup().apply {
|
||||||
setAsRoot(this@Model.manager)
|
setAsRoot(this@Model.manager)
|
||||||
material {
|
material {
|
||||||
color.set("darkgreen")
|
color("darkgreen")
|
||||||
}
|
}
|
||||||
rotationX = PI / 2
|
rotationX = PI / 2
|
||||||
solidGroup("bottom") {
|
solidGroup("bottom") {
|
||||||
@ -64,7 +64,7 @@ class Model(val manager: VisionManager) {
|
|||||||
|
|
||||||
private fun highlight(pixel: String) {
|
private fun highlight(pixel: String) {
|
||||||
println("highlight $pixel")
|
println("highlight $pixel")
|
||||||
map[pixel]?.color.set("blue")
|
map[pixel]?.color("blue")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
@ -82,7 +82,7 @@ class Model(val manager: VisionManager) {
|
|||||||
}
|
}
|
||||||
event.track?.let {
|
event.track?.let {
|
||||||
tracks.polyline(*it.toTypedArray(), name = "track[${event.id}]") {
|
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_XY_SIZE
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE
|
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE
|
||||||
import space.kscience.visionforge.solid.Point3D
|
import space.kscience.visionforge.solid.Float32Euclidean3DSpace
|
||||||
import space.kscience.visionforge.solid.plus
|
import space.kscience.visionforge.solid.Float32Vector3D
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single pixel
|
* A single pixel
|
||||||
*/
|
*/
|
||||||
class SC1(
|
class SC1(
|
||||||
val name: String,
|
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,
|
val xSize: Float = PIXEL_XY_SIZE, val ySize: Float = PIXEL_XY_SIZE, val zSize: Float = PIXEL_Z_SIZE,
|
||||||
)
|
)
|
||||||
|
|
||||||
class SC16(
|
class SC16(
|
||||||
val name: String,
|
val name: String,
|
||||||
val center: Point3D,
|
val center: Float32Vector3D,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,9 +109,9 @@ class SC16(
|
|||||||
|
|
||||||
else -> throw Error()
|
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}"
|
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 x = split[4].toDouble() - 500
|
||||||
val y = split[5].toDouble() - 500
|
val y = split[5].toDouble() - 500
|
||||||
val z = 180 - split[6].toDouble()
|
val z = 180 - split[6].toDouble()
|
||||||
SC16(detectorName, Point3D(x, y, z))
|
SC16(detectorName, Float32Vector3D(x, y, z))
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import space.kscience.visionforge.ring.tab
|
|||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.solid.ambientLight
|
import space.kscience.visionforge.solid.ambientLight
|
||||||
import space.kscience.visionforge.solid.edges
|
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 space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
@ -58,7 +58,7 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
|||||||
props.model.root.apply {
|
props.model.root.apply {
|
||||||
edges()
|
edges()
|
||||||
ambientLight{
|
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.application.log
|
||||||
import io.ktor.server.cio.CIO
|
import io.ktor.server.cio.CIO
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
import io.ktor.server.http.content.resources
|
import io.ktor.server.http.content.staticResources
|
||||||
import io.ktor.server.http.content.static
|
|
||||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.respond
|
||||||
import io.ktor.server.response.respondText
|
import io.ktor.server.response.respondText
|
||||||
@ -53,9 +52,7 @@ fun Application.module(context: Context = Global) {
|
|||||||
status = HttpStatusCode.OK
|
status = HttpStatusCode.OK
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
static("/") {
|
staticResources("/", null)
|
||||||
resources()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Desktop.getDesktop().browse(URI("http://localhost:8080/index.html"))
|
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 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.CENTRAL_LAYER_Z
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.GEOMETRY_TOLERANCE
|
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.
|
* 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 basePoint = basePlane.intersection(this)
|
||||||
val bottom = basePoint.subtract(2000.0, direction)
|
val bottom = basePoint.subtract(2000.0, direction)
|
||||||
val top = basePoint.add(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 {
|
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 ->
|
val defaultHitResolver: (Line) -> Collection<SC1> = { track: Line ->
|
||||||
|
@ -47,12 +47,11 @@ kotlin {
|
|||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.visionforgeSolid)
|
implementation(projects.visionforgeSolid)
|
||||||
implementation(projects.visionforgeGdml)
|
|
||||||
implementation(projects.visionforgePlotly)
|
implementation(projects.visionforgePlotly)
|
||||||
implementation(projects.visionforgeMarkdown)
|
implementation(projects.visionforgeMarkdown)
|
||||||
implementation(projects.visionforgeTables)
|
implementation(projects.visionforgeTables)
|
||||||
implementation(projects.cernRootLoader)
|
implementation(projects.cernRootLoader)
|
||||||
implementation(projects.jupyter)
|
api(projects.visionforgeJupyter.visionforgeJupyterCommon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +65,8 @@ kotlin {
|
|||||||
|
|
||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation("io.ktor:ktor-server-cio:${spclibs.versions.ktor.get()}")
|
||||||
|
implementation(projects.visionforgeGdml)
|
||||||
implementation(projects.visionforgeServer)
|
implementation(projects.visionforgeServer)
|
||||||
implementation(spclibs.logback.classic)
|
implementation(spclibs.logback.classic)
|
||||||
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
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": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"pycharm": {
|
"pycharm": {
|
||||||
"is_executing": true
|
"is_executing": true
|
||||||
},
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2023-05-29T15:22:37.933397300Z",
|
|
||||||
"start_time": "2023-05-29T15:22:37.913872100Z"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@ -18,57 +14,23 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
"outputs": [],
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
"source": [
|
||||||
"vf.startServer()"
|
"vf.startServer()"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false,
|
"collapsed": false,
|
||||||
"jupyter": {
|
"jupyter": {
|
||||||
"outputs_hidden": false
|
"outputs_hidden": false
|
||||||
},
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2023-05-29T15:22:51.410680600Z",
|
|
||||||
"start_time": "2023-05-29T15:22:51.250779400Z"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
"source": [
|
||||||
"import kotlinx.coroutines.*\n",
|
"import kotlinx.coroutines.*\n",
|
||||||
"import kotlin.random.Random\n",
|
"import kotlin.random.Random\n",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import space.kscience.dataforge.misc.DFExperimental
|
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.markup.MarkupPlugin
|
||||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||||
@ -12,5 +12,5 @@ fun main() = runVisionClient {
|
|||||||
plugin(PlotlyPlugin)
|
plugin(PlotlyPlugin)
|
||||||
plugin(MarkupPlugin)
|
plugin(MarkupPlugin)
|
||||||
plugin(TableVisionJsPlugin)
|
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.html.ResourceLocation
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.solid.color
|
import space.kscience.visionforge.solid.color
|
||||||
import space.kscience.visionforge.solid.set
|
import space.kscience.visionforge.solid.invoke
|
||||||
import space.kscience.visionforge.visible
|
import space.kscience.visionforge.visible
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ fun main() = makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceL
|
|||||||
visible = false
|
visible = false
|
||||||
}
|
}
|
||||||
if(solid.name.startsWith("gas")){
|
if(solid.name.startsWith("gas")){
|
||||||
color.set("green")
|
color("green")
|
||||||
} else {
|
} else {
|
||||||
//make all solids semi-transparent
|
//make all solids semi-transparent
|
||||||
transparent()
|
transparent()
|
||||||
|
@ -19,7 +19,7 @@ fun main() = makeVisionFile(
|
|||||||
vision {
|
vision {
|
||||||
solid {
|
solid {
|
||||||
ambientLight {
|
ambientLight {
|
||||||
color.set(Colors.white)
|
color(Colors.white)
|
||||||
}
|
}
|
||||||
repeat(100) {
|
repeat(100) {
|
||||||
sphere(5, name = "sphere[$it]") {
|
sphere(5, name = "sphere[$it]") {
|
||||||
@ -27,7 +27,7 @@ fun main() = makeVisionFile(
|
|||||||
y = random.nextDouble(-300.0, 300.0)
|
y = random.nextDouble(-300.0, 300.0)
|
||||||
z = random.nextDouble(-300.0, 300.0)
|
z = random.nextDouble(-300.0, 300.0)
|
||||||
material {
|
material {
|
||||||
color.set(random.nextInt())
|
color(random.nextInt())
|
||||||
}
|
}
|
||||||
detail = 16
|
detail = 16
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import space.kscience.visionforge.Colors
|
|||||||
import space.kscience.visionforge.html.ResourceLocation
|
import space.kscience.visionforge.html.ResourceLocation
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.solid.ambientLight
|
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 space.kscience.visionforge.solid.solid
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
@ -26,7 +26,7 @@ private fun Meta.countTypes(): Sequence<String> = sequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
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.nextEntry
|
||||||
it.readAllBytes().decodeToString()
|
it.readAllBytes().decodeToString()
|
||||||
}
|
}
|
||||||
@ -44,9 +44,9 @@ fun main() {
|
|||||||
requirePlugin(Solids)
|
requirePlugin(Solids)
|
||||||
solid {
|
solid {
|
||||||
ambientLight {
|
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))
|
Path("data/BM@N.vf.json").writeText(Solids.encodeToString(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
package space.kscience.visionforge.examples
|
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.dataforge.context.Global
|
||||||
import space.kscience.visionforge.html.*
|
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 space.kscience.visionforge.visionManager
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
public fun makeVisionFile(
|
public fun makeVisionFile(
|
||||||
path: Path? = null,
|
path: Path? = null,
|
||||||
title: String = "VisionForge page",
|
title: String = "VisionForge page",
|
||||||
@ -26,6 +39,45 @@ public fun makeVisionFile(
|
|||||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
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
|
//@DFExperimental
|
||||||
//public fun Context.makeVisionFile(
|
//public fun Context.makeVisionFile(
|
||||||
// vision: Vision,
|
// vision: Vision,
|
||||||
|
@ -10,23 +10,23 @@ fun main() = makeVisionFile {
|
|||||||
ambientLight()
|
ambientLight()
|
||||||
box(100.0, 100.0, 100.0) {
|
box(100.0, 100.0, 100.0) {
|
||||||
z = -110.0
|
z = -110.0
|
||||||
color.set("teal")
|
color("teal")
|
||||||
}
|
}
|
||||||
sphere(50.0) {
|
sphere(50.0) {
|
||||||
x = 110
|
x = 110
|
||||||
detail = 16
|
detail = 16
|
||||||
color.set("red")
|
color("red")
|
||||||
}
|
}
|
||||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||||
y = 110
|
y = 110
|
||||||
detail = 16
|
detail = 16
|
||||||
rotationX = PI / 4
|
rotationX = PI / 4
|
||||||
color.set("blue")
|
color("blue")
|
||||||
}
|
}
|
||||||
sphereLayer(50, 40, theta = PI / 2) {
|
sphereLayer(50, 40, theta = PI / 2) {
|
||||||
rotationX = -PI * 3 / 4
|
rotationX = -PI * 3 / 4
|
||||||
z = 110
|
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.html.ResourceLocation
|
||||||
import space.kscience.visionforge.solid.box
|
import space.kscience.visionforge.solid.box
|
||||||
|
import space.kscience.visionforge.solid.invoke
|
||||||
import space.kscience.visionforge.solid.material
|
import space.kscience.visionforge.solid.material
|
||||||
import space.kscience.visionforge.solid.set
|
|
||||||
import space.kscience.visionforge.solid.solid
|
import space.kscience.visionforge.solid.solid
|
||||||
|
|
||||||
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||||
@ -11,7 +11,7 @@ fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
|||||||
solid {
|
solid {
|
||||||
box(100, 100, 100)
|
box(100, 100, 100)
|
||||||
material {
|
material {
|
||||||
emissiveColor.set("red")
|
emissiveColor("red")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -8,13 +8,15 @@ kscience {
|
|||||||
// useSerialization {
|
// useSerialization {
|
||||||
// json()
|
// json()
|
||||||
// }
|
// }
|
||||||
|
useKtor()
|
||||||
dependencies{
|
dependencies{
|
||||||
|
implementation("io.ktor:ktor-server-cio")
|
||||||
implementation(projects.visionforgeThreejs.visionforgeThreejsServer)
|
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 {
|
application {
|
||||||
mainClass.set("ru.mipt.npm.sat.SatServerKt")
|
mainClass.set("ru.mipt.npm.sat.SatServerKt")
|
||||||
|
@ -15,7 +15,7 @@ internal fun Solids.visionOfSatellite(
|
|||||||
ySegmentSize: Number = xSegmentSize,
|
ySegmentSize: Number = xSegmentSize,
|
||||||
fiberDiameter: Number = 1.0,
|
fiberDiameter: Number = 1.0,
|
||||||
): SolidGroup = solidGroup {
|
): SolidGroup = solidGroup {
|
||||||
color.set("darkgreen")
|
color("darkgreen")
|
||||||
val transparent by style {
|
val transparent by style {
|
||||||
this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3
|
this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ fun main() {
|
|||||||
//Create a geometry
|
//Create a geometry
|
||||||
val sat = solids.visionOfSatellite(ySegments = 3).apply {
|
val sat = solids.visionOfSatellite(ySegments = 3).apply {
|
||||||
ambientLight {
|
ambientLight {
|
||||||
color.set(Colors.white)
|
color(Colors.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val server = embeddedServer(CIO, port = 7777) {
|
val server = embeddedServer(CIO, port = 7777) {
|
||||||
@ -63,7 +63,7 @@ fun main() {
|
|||||||
val randomJ = Random.nextInt(1, 4)
|
val randomJ = Random.nextInt(1, 4)
|
||||||
val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]")
|
val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]")
|
||||||
val targetVision = sat[target] as Solid
|
val targetVision = sat[target] as Solid
|
||||||
targetVision.color.set("red")
|
targetVision.color("red")
|
||||||
delay(1000)
|
delay(1000)
|
||||||
//use to ensure that color is cleared
|
//use to ensure that color is cleared
|
||||||
targetVision.color.value = Null
|
targetVision.color.value = Null
|
||||||
|
@ -3,8 +3,8 @@ package ru.mipt.npm.sat
|
|||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.visionforge.html.ResourceLocation
|
import space.kscience.visionforge.html.ResourceLocation
|
||||||
import space.kscience.visionforge.solid.box
|
import space.kscience.visionforge.solid.box
|
||||||
|
import space.kscience.visionforge.solid.invoke
|
||||||
import space.kscience.visionforge.solid.material
|
import space.kscience.visionforge.solid.material
|
||||||
import space.kscience.visionforge.solid.set
|
|
||||||
import space.kscience.visionforge.solid.solid
|
import space.kscience.visionforge.solid.solid
|
||||||
import space.kscience.visionforge.three.makeThreeJsFile
|
import space.kscience.visionforge.three.makeThreeJsFile
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ fun main() = makeThreeJsFile(resourceLocation = ResourceLocation.SYSTEM) {
|
|||||||
solid {
|
solid {
|
||||||
box(100, 100, 100)
|
box(100, 100, 100)
|
||||||
material {
|
material {
|
||||||
emissiveColor.set("red")
|
emissiveColor("red")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("space.kscience.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
application
|
// application
|
||||||
}
|
}
|
||||||
|
|
||||||
kscience {
|
kscience {
|
||||||
useCoroutines()
|
useCoroutines()
|
||||||
jvm {
|
jvm()
|
||||||
withJava()
|
js{
|
||||||
|
binaries.executable()
|
||||||
}
|
}
|
||||||
js()
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.visionforgeSolid)
|
implementation(projects.visionforgeSolid)
|
||||||
implementation(projects.visionforgeGdml)
|
implementation(projects.visionforgeGdml)
|
||||||
@ -19,6 +19,8 @@ kscience {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
kotlin.explicitApi = null
|
||||||
mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt")
|
|
||||||
}
|
//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.Meta
|
||||||
import space.kscience.dataforge.meta.invoke
|
import space.kscience.dataforge.meta.invoke
|
||||||
import space.kscience.dataforge.names.Name
|
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.Colors
|
||||||
import space.kscience.visionforge.solid.*
|
import space.kscience.visionforge.solid.*
|
||||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
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 {
|
val vision = solids.solidGroup {
|
||||||
block()
|
block()
|
||||||
ambientLight {
|
ambientLight {
|
||||||
color.set(Colors.white)
|
color(Colors.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render(Name.parse(name), vision, meta)
|
render(Name.parse(name), vision, meta)
|
||||||
@ -47,23 +49,23 @@ fun VisionLayout<Solid>.showcase() {
|
|||||||
ambientLight()
|
ambientLight()
|
||||||
box(100.0, 100.0, 100.0) {
|
box(100.0, 100.0, 100.0) {
|
||||||
z = -110.0
|
z = -110.0
|
||||||
color.set("teal")
|
color("teal")
|
||||||
}
|
}
|
||||||
sphere(50.0) {
|
sphere(50.0) {
|
||||||
x = 110
|
x = 110
|
||||||
detail = 16
|
detail = 16
|
||||||
color.set("red")
|
color("red")
|
||||||
}
|
}
|
||||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||||
y = 110
|
y = 110
|
||||||
detail = 16
|
detail = 16
|
||||||
rotationX = PI / 4
|
rotationX = PI / 4
|
||||||
color.set("blue")
|
color("blue")
|
||||||
}
|
}
|
||||||
sphereLayer(50, 40, theta = PI / 2) {
|
sphereLayer(50, 40, theta = PI / 2) {
|
||||||
rotationX = -PI * 3 / 4
|
rotationX = -PI * 3 / 4
|
||||||
z = 110
|
z = 110
|
||||||
color.set(Colors.pink)
|
color(Colors.pink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ fun VisionLayout<Solid>.showcase() {
|
|||||||
visible = false
|
visible = false
|
||||||
x = 110.0
|
x = 110.0
|
||||||
//override color for this cube
|
//override color for this cube
|
||||||
color.set(1530)
|
color(1530)
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
@ -93,7 +95,7 @@ fun VisionLayout<Solid>.showcase() {
|
|||||||
val random = Random(111)
|
val random = Random(111)
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
delay(1000)
|
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 {
|
solidGroup {
|
||||||
x = 200
|
x = 200
|
||||||
rotationY = PI / 4
|
rotationY = PI / 4
|
||||||
|
axes(200)
|
||||||
box(100, 100, 100) {
|
box(100, 100, 100) {
|
||||||
rotationZ = PI / 4
|
rotate((PI / 4).radians, Euclidean3DSpace.zAxis)
|
||||||
color.set(Colors.red)
|
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) {
|
for (i in 0..100) {
|
||||||
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
|
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
|
rotationX = -PI / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,13 +136,16 @@ fun VisionLayout<Solid>.showcase() {
|
|||||||
sphere(100) {
|
sphere(100) {
|
||||||
detail = 32
|
detail = 32
|
||||||
opacity = 0.4
|
opacity = 0.4
|
||||||
color.set(Colors.blue)
|
color(Colors.blue)
|
||||||
}
|
}
|
||||||
repeat(20) {
|
repeat(20) {
|
||||||
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
|
polyline(
|
||||||
|
Float32Vector3D(100, 100, 100),
|
||||||
|
Float32Vector3D(-100, -100, -100)
|
||||||
|
) {
|
||||||
thickness = 3.0
|
thickness = 3.0
|
||||||
rotationX = it * PI2 / 20
|
rotationX = it * PI2 / 20
|
||||||
color.set(Colors.green)
|
color(Colors.green)
|
||||||
//rotationY = it * PI2 / 20
|
//rotationY = it * PI2 / 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,6 +159,10 @@ fun VisionLayout<Solid>.showcase() {
|
|||||||
z = 26
|
z = 26
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
demo("STL", "STL loaded from URL") {
|
||||||
|
stl("https://ozeki.hu/attachments/116/Menger_sponge_sample.stl")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VisionLayout<Solid>.showcaseCSG() {
|
fun VisionLayout<Solid>.showcaseCSG() {
|
||||||
@ -160,7 +176,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
|||||||
detail = 32
|
detail = 32
|
||||||
}
|
}
|
||||||
material {
|
material {
|
||||||
color.set(Colors.pink)
|
color(Colors.pink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composite(CompositeType.UNION) {
|
composite(CompositeType.UNION) {
|
||||||
@ -170,7 +186,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
|||||||
sphere(50) {
|
sphere(50) {
|
||||||
detail = 32
|
detail = 32
|
||||||
}
|
}
|
||||||
color.set("lightgreen")
|
color("lightgreen")
|
||||||
opacity = 0.7
|
opacity = 0.7
|
||||||
}
|
}
|
||||||
composite(CompositeType.SUBTRACT) {
|
composite(CompositeType.SUBTRACT) {
|
||||||
@ -181,7 +197,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
|||||||
sphere(50) {
|
sphere(50) {
|
||||||
detail = 32
|
detail = 32
|
||||||
}
|
}
|
||||||
color.set("teal")
|
color("teal")
|
||||||
opacity = 0.7
|
opacity = 0.7
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,7 +208,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
|||||||
detail = 32
|
detail = 32
|
||||||
}
|
}
|
||||||
box(100, 100, 100)
|
box(100, 100, 100)
|
||||||
color.set("red")
|
color("red")
|
||||||
opacity = 0.5
|
opacity = 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ internal fun SolidGroup.varBox(
|
|||||||
|
|
||||||
internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() {
|
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 geometry = BoxGeometry(xSize, ySize, 1)
|
||||||
|
|
||||||
val material = ThreeMaterials.DEFAULT.clone()
|
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:playground",
|
||||||
// ":demo:plotly-fx",
|
// ":demo:plotly-fx",
|
||||||
":demo:js-playground",
|
":demo:js-playground",
|
||||||
":jupyter",
|
":visionforge-jupyter",
|
||||||
":jupyter:visionforge-jupyter-gdml"
|
":visionforge-jupyter:visionforge-jupyter-common"
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,6 @@ package space.kscience.visionforge.react
|
|||||||
|
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.w3c.dom.HTMLElement
|
|
||||||
import react.*
|
import react.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.request
|
import space.kscience.dataforge.context.request
|
||||||
@ -29,7 +28,7 @@ public val ThreeCanvasComponent: FC<ThreeCanvasProps> = fc("ThreeCanvasComponent
|
|||||||
|
|
||||||
useEffect(props.solid, props.options, elementRef) {
|
useEffect(props.solid, props.options, elementRef) {
|
||||||
if (canvas == null) {
|
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())
|
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.PluginFactory
|
||||||
import space.kscience.dataforge.context.PluginTag
|
import space.kscience.dataforge.context.PluginTag
|
||||||
import space.kscience.dataforge.meta.Meta
|
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.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.visionforge.ElementVisionRenderer
|
import space.kscience.visionforge.ElementVisionRenderer
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.react.render
|
import space.kscience.visionforge.react.render
|
||||||
import space.kscience.visionforge.solid.Solid
|
import space.kscience.visionforge.solid.Solid
|
||||||
|
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||||
|
|
||||||
public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
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
|
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||||
|
if(meta["controls.enabled"].boolean == false){
|
||||||
|
three.render(element, name, vision, meta)
|
||||||
|
} else {
|
||||||
space.kscience.visionforge.react.createRoot(element).render {
|
space.kscience.visionforge.react.createRoot(element).render {
|
||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
attrs {
|
attrs {
|
||||||
this.solids = three.solids
|
this.solids = three.solids
|
||||||
this.builderOfSolid = context.async { vision as Solid}
|
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(
|
public fun Vision.onPropertyChange(
|
||||||
scope: CoroutineScope? = manager?.context,
|
scope: CoroutineScope? = manager?.context,
|
||||||
callback: (Name) -> Unit
|
callback: suspend (Name) -> Unit
|
||||||
): Job = properties.changes.onEach {
|
): Job = properties.changes.onEach {
|
||||||
callback(it)
|
callback(it)
|
||||||
}.launchIn(scope ?: error("Orphan Vision can't observe properties"))
|
}.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
|
public val jsonFormat: Json
|
||||||
get() = Json(defaultJson) {
|
get() = Json(defaultJson) {
|
||||||
encodeDefaults = false
|
|
||||||
serializersModule = this@VisionManager.serializersModule
|
serializersModule = this@VisionManager.serializersModule
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +84,6 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
|
|||||||
serializersModule = defaultSerialModule
|
serializersModule = defaultSerialModule
|
||||||
prettyPrint = true
|
prettyPrint = true
|
||||||
useArrayPolymorphism = false
|
useArrayPolymorphism = false
|
||||||
encodeDefaults = false
|
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
explicitNulls = false
|
explicitNulls = false
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,28 @@ import kotlinx.html.FlowContent
|
|||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
|
|
||||||
public typealias HtmlFragment = TagConsumer<*>.() -> Unit
|
/**
|
||||||
|
* A standalone HTML fragment
|
||||||
public fun HtmlFragment.renderToString(): String = createHTML().apply(this).finalize()
|
*/
|
||||||
|
public fun interface HtmlFragment {
|
||||||
public fun TagConsumer<*>.fragment(fragment: HtmlFragment) {
|
public fun TagConsumer<*>.append()
|
||||||
fragment()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
* Create a string from this [HtmlFragment]
|
||||||
other()
|
*/
|
||||||
|
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 kotlinx.html.*
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
|
|
||||||
public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
|
public fun interface HtmlVisionFragment{
|
||||||
|
public fun VisionTagConsumer<*>.append()
|
||||||
@DFExperimental
|
}
|
||||||
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
|
||||||
|
|
||||||
|
public fun HtmlVisionFragment.appendTo(consumer: VisionTagConsumer<*>): Unit = consumer.append()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a fragment in the given consumer and return a map of extracted visions
|
* 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(
|
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.
|
* 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 {
|
script {
|
||||||
type = "text/javascript"
|
type = "text/javascript"
|
||||||
this.src = src
|
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 {
|
link {
|
||||||
rel = "stylesheet"
|
rel = "stylesheet"
|
||||||
this.href = href
|
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)
|
title(title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ fun FlowContent.renderVisionFragment(
|
|||||||
renderer(name, vision, outputMeta)
|
renderer(name, vision, outputMeta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fragment(consumer)
|
fragment.appendTo(consumer)
|
||||||
return visionMap
|
return visionMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ private fun VisionOutput.base(block: VisionGroup.() -> Unit) = context.visionMan
|
|||||||
@DFExperimental
|
@DFExperimental
|
||||||
class HtmlTagTest {
|
class HtmlTagTest {
|
||||||
|
|
||||||
val fragment: HtmlVisionFragment = {
|
val fragment = HtmlVisionFragment{
|
||||||
div {
|
div {
|
||||||
h1 { +"Head" }
|
h1 { +"Head" }
|
||||||
vision("ddd") {
|
vision("ddd") {
|
||||||
|
@ -286,8 +286,8 @@ public fun VisionClient.renderAllVisionsIn(element: Element) {
|
|||||||
/**
|
/**
|
||||||
* Render all visions in an element with a given [id]
|
* Render all visions in an element with a given [id]
|
||||||
*/
|
*/
|
||||||
public fun VisionClient.renderAllVisionsById(id: String): Unit = whenDocumentLoaded {
|
public fun VisionClient.renderAllVisionsById(document: Document, id: String): Unit {
|
||||||
val element = getElementById(id)
|
val element = document.getElementById(id)
|
||||||
if (element != null) {
|
if (element != null) {
|
||||||
renderAllVisionsIn(element)
|
renderAllVisionsIn(element)
|
||||||
} else {
|
} else {
|
||||||
|
@ -34,10 +34,10 @@ public interface HtmlVisionContext : ContextAware {
|
|||||||
|
|
||||||
public typealias HtmlVisionContextFragment = context(HtmlVisionContext) TagConsumer<*>.() -> Unit
|
public typealias HtmlVisionContextFragment = context(HtmlVisionContext) TagConsumer<*>.() -> Unit
|
||||||
|
|
||||||
context(HtmlVisionContext)
|
//context(HtmlVisionContext)
|
||||||
public fun HtmlVisionFragment(
|
//public fun HtmlVisionFragment(
|
||||||
content: TagConsumer<*>.() -> Unit,
|
// content: TagConsumer<*>.() -> Unit,
|
||||||
): HtmlVisionFragment = content
|
//): HtmlVisionFragment = HtmlVisionFragment { }
|
||||||
|
|
||||||
context(HtmlVisionContext)
|
context(HtmlVisionContext)
|
||||||
private fun <T> TagConsumer<T>.vision(
|
private fun <T> TagConsumer<T>.vision(
|
||||||
|
@ -91,14 +91,14 @@ internal fun checkOrStoreFile(htmlPath: Path, filePath: Path, resource: String,
|
|||||||
*/
|
*/
|
||||||
internal fun fileScriptHeader(
|
internal fun fileScriptHeader(
|
||||||
path: Path,
|
path: Path,
|
||||||
): HtmlFragment = {
|
): HtmlFragment = HtmlFragment{
|
||||||
script {
|
script {
|
||||||
type = "text/javascript"
|
type = "text/javascript"
|
||||||
src = path.toString()
|
src = path.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun embedScriptHeader(resource: String, classLoader: ClassLoader): HtmlFragment = {
|
internal fun embedScriptHeader(resource: String, classLoader: ClassLoader): HtmlFragment = HtmlFragment{
|
||||||
script {
|
script {
|
||||||
type = "text/javascript"
|
type = "text/javascript"
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -113,7 +113,7 @@ internal fun fileCssHeader(
|
|||||||
cssPath: Path,
|
cssPath: Path,
|
||||||
resource: String,
|
resource: String,
|
||||||
classLoader: ClassLoader,
|
classLoader: ClassLoader,
|
||||||
): HtmlFragment = {
|
): HtmlFragment = HtmlFragment{
|
||||||
val relativePath = checkOrStoreFile(basePath, cssPath, resource, classLoader)
|
val relativePath = checkOrStoreFile(basePath, cssPath, resource, classLoader)
|
||||||
link {
|
link {
|
||||||
rel = "stylesheet"
|
rel = "stylesheet"
|
||||||
|
@ -78,7 +78,7 @@ public fun VisionPage.makeFile(
|
|||||||
charset = "utf-8"
|
charset = "utf-8"
|
||||||
}
|
}
|
||||||
actualHeaders.values.forEach {
|
actualHeaders.values.forEach {
|
||||||
fragment(it)
|
appendFragment(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
|
@ -42,7 +42,7 @@ public class GdmlLoaderOptions {
|
|||||||
* Configure paint for given solid with given [GdmlMaterial]
|
* Configure paint for given solid with given [GdmlMaterial]
|
||||||
*/
|
*/
|
||||||
public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit =
|
public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit =
|
||||||
{ material, _ -> color.set(randomColor(material)) }
|
{ material, _ -> color(randomColor(material)) }
|
||||||
private set
|
private set
|
||||||
|
|
||||||
public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) {
|
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.dataforge.names.plus
|
||||||
|
|
||||||
import space.kscience.gdml.*
|
import space.kscience.gdml.*
|
||||||
|
import space.kscience.kmath.geometry.RotationOrder
|
||||||
import space.kscience.visionforge.*
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.html.VisionOutput
|
import space.kscience.visionforge.html.VisionOutput
|
||||||
import space.kscience.visionforge.solid.*
|
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" }
|
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
|
||||||
val baseRadius = solid.planes.first().rmax * lScale
|
val baseRadius = solid.planes.first().rmax * lScale
|
||||||
shape {
|
shape {
|
||||||
(0..solid.numsides).forEach {
|
(0..<solid.numsides).forEach {
|
||||||
val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
|
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 ->
|
solid.planes.forEach { plane ->
|
||||||
@ -248,14 +249,14 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
|
|||||||
val dyBottom = solid.y1.toDouble() / 2
|
val dyBottom = solid.y1.toDouble() / 2
|
||||||
val dyTop = solid.y2.toDouble() / 2
|
val dyTop = solid.y2.toDouble() / 2
|
||||||
val dz = solid.z.toDouble() / 2
|
val dz = solid.z.toDouble() / 2
|
||||||
val node1 = Point3D(-dxBottom, -dyBottom, -dz)
|
val node1 = Float32Vector3D(-dxBottom, -dyBottom, -dz)
|
||||||
val node2 = Point3D(dxBottom, -dyBottom, -dz)
|
val node2 = Float32Vector3D(dxBottom, -dyBottom, -dz)
|
||||||
val node3 = Point3D(dxBottom, dyBottom, -dz)
|
val node3 = Float32Vector3D(dxBottom, dyBottom, -dz)
|
||||||
val node4 = Point3D(-dxBottom, dyBottom, -dz)
|
val node4 = Float32Vector3D(-dxBottom, dyBottom, -dz)
|
||||||
val node5 = Point3D(-dxTop, -dyTop, dz)
|
val node5 = Float32Vector3D(-dxTop, -dyTop, dz)
|
||||||
val node6 = Point3D(dxTop, -dyTop, dz)
|
val node6 = Float32Vector3D(dxTop, -dyTop, dz)
|
||||||
val node7 = Point3D(dxTop, dyTop, dz)
|
val node7 = Float32Vector3D(dxTop, dyTop, dz)
|
||||||
val node8 = Point3D(-dxTop, dyTop, dz)
|
val node8 = Float32Vector3D(-dxTop, dyTop, dz)
|
||||||
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
|
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,13 +5,17 @@ plugins {
|
|||||||
description = "Common visionforge jupyter module"
|
description = "Common visionforge jupyter module"
|
||||||
|
|
||||||
kscience {
|
kscience {
|
||||||
|
useKtor()
|
||||||
jvm()
|
jvm()
|
||||||
js()
|
js()
|
||||||
jupyterLibrary()
|
jupyterLibrary()
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.visionforgeCore)
|
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)
|
api(projects.visionforgeServer)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package space.kscience.visionforge.jupyter
|
package space.kscience.visionforge.jupyter
|
||||||
|
|
||||||
import kotlinx.browser.window
|
import kotlinx.browser.window
|
||||||
|
import org.w3c.dom.Document
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import space.kscience.dataforge.context.AbstractPlugin
|
import space.kscience.dataforge.context.AbstractPlugin
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
@ -13,15 +14,15 @@ import space.kscience.visionforge.renderAllVisionsById
|
|||||||
import space.kscience.visionforge.renderAllVisionsIn
|
import space.kscience.visionforge.renderAllVisionsIn
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public class VFNotebookPlugin : AbstractPlugin() {
|
public class VFNotebookClient : AbstractPlugin() {
|
||||||
private val client by require(VisionClient)
|
private val client by require(VisionClient)
|
||||||
|
|
||||||
public fun renderAllVisionsIn(element: Element) {
|
public fun renderAllVisionsIn(element: Element) {
|
||||||
client.renderAllVisionsIn(element)
|
client.renderAllVisionsIn(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun renderAllVisionsById(id: String) {
|
public fun renderAllVisionsById(document: Document, id: String) {
|
||||||
client.renderAllVisionsById(id)
|
client.renderAllVisionsById(document, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun renderAllVisions() {
|
public fun renderAllVisions() {
|
||||||
@ -30,17 +31,18 @@ public class VFNotebookPlugin : AbstractPlugin() {
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
console.info("Loading VisionForge global hooks")
|
||||||
//register VisionForge in the browser window
|
//register VisionForge in the browser window
|
||||||
window.asDynamic().vf = this
|
window.parent.asDynamic().vf = this
|
||||||
window.asDynamic().VisionForge = this
|
window.parent.asDynamic().VisionForge = this
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("NON_EXPORTABLE_TYPE")
|
@Suppress("NON_EXPORTABLE_TYPE")
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
@Suppress("NON_EXPORTABLE_TYPE")
|
@Suppress("NON_EXPORTABLE_TYPE")
|
||||||
public companion object : PluginFactory<VFNotebookPlugin> {
|
public companion object : PluginFactory<VFNotebookClient> {
|
||||||
override fun build(context: Context, meta: Meta): VFNotebookPlugin = VFNotebookPlugin()
|
override fun build(context: Context, meta: Meta): VFNotebookClient = VFNotebookClient()
|
||||||
|
|
||||||
override val tag: PluginTag = PluginTag(name = "vision.notebook", group = PluginTag.DATAFORGE_GROUP)
|
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 class HtmlFormFragment internal constructor(
|
||||||
public val vision: VisionOfHtmlForm,
|
public val vision: VisionOfHtmlForm,
|
||||||
public val formBody: HtmlFragment,
|
public val formBody: HtmlFragment,
|
||||||
){
|
) {
|
||||||
public val values: Meta? get() = vision.values
|
public val values: Meta? get() = vision.values
|
||||||
public operator fun get(valueName: String): Meta? = values?.get(valueName)
|
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 {
|
public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment {
|
||||||
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
|
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
|
||||||
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
|
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
|
||||||
@ -25,3 +28,7 @@ public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
useKtor()
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.visionforgeCore)
|
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-html-builder")
|
||||||
api("io.ktor:ktor-server-websockets")
|
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.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.cio.*
|
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.html.*
|
import io.ktor.server.html.*
|
||||||
import io.ktor.server.http.content.*
|
import io.ktor.server.http.content.*
|
||||||
@ -58,7 +57,7 @@ public class VisionRoute(
|
|||||||
override val context: Context get() = visionManager.context
|
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)
|
public var updateInterval: Long by meta.long(300, key = UPDATE_INTERVAL_KEY)
|
||||||
|
|
||||||
@ -171,8 +170,8 @@ public fun Application.visionPage(
|
|||||||
meta {
|
meta {
|
||||||
charset = "utf-8"
|
charset = "utf-8"
|
||||||
}
|
}
|
||||||
headers.forEach { header ->
|
headers.forEach { headerContent ->
|
||||||
consumer.header()
|
headerContent.appendTo(consumer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
|
@ -2,6 +2,8 @@ plugins {
|
|||||||
id("space.kscience.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val kmathVersion = "0.3.1"
|
||||||
|
|
||||||
kscience {
|
kscience {
|
||||||
jvm()
|
jvm()
|
||||||
js()
|
js()
|
||||||
@ -11,6 +13,7 @@ kscience {
|
|||||||
}
|
}
|
||||||
useCoroutines()
|
useCoroutines()
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api("space.kscience:kmath-geometry:0.3.1")
|
||||||
api(projects.visionforgeCore)
|
api(projects.visionforgeCore)
|
||||||
}
|
}
|
||||||
dependencies(jvmTest) {
|
dependencies(jvmTest) {
|
||||||
|
@ -28,7 +28,7 @@ public class ColorAccessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun Vision.color(
|
public fun Vision.colorProperty(
|
||||||
propertyName: Name? = null,
|
propertyName: Name? = null,
|
||||||
): ReadOnlyProperty<Vision, ColorAccessor> = ReadOnlyProperty { _, property ->
|
): ReadOnlyProperty<Vision, ColorAccessor> = ReadOnlyProperty { _, property ->
|
||||||
ColorAccessor(properties.root(true), propertyName ?: property.name.asName())
|
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
|
* 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()
|
this?.value = webColor.asValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set color as RGB integer
|
* Set color as RGB integer
|
||||||
*/
|
*/
|
||||||
public fun ColorAccessor?.set(rgb: Int) {
|
public operator fun ColorAccessor?.invoke(rgb: Int) {
|
||||||
this?.value = Colors.rgbToString(rgb).asValue()
|
this?.value = Colors.rgbToString(rgb).asValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set color as RGB
|
* 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()
|
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" }
|
require(segments >= 4) { "The number of segments in cone is too small" }
|
||||||
val angleStep = phi / (segments - 1)
|
val angleStep = phi / (segments - 1)
|
||||||
|
|
||||||
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
|
fun shape(r: Float, z: Float): List<Float32Vector3D> = (0 until segments).map { i ->
|
||||||
Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
Float32Vector3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
||||||
}
|
}
|
||||||
|
|
||||||
geometryBuilder.apply {
|
geometryBuilder.apply {
|
||||||
@ -53,8 +53,8 @@ public class ConeSegment(
|
|||||||
if (phi == PI2) {
|
if (phi == PI2) {
|
||||||
face4(bottomPoints.last(), bottomPoints[0], topPoints[0], topPoints.last())
|
face4(bottomPoints.last(), bottomPoints[0], topPoints[0], topPoints.last())
|
||||||
}
|
}
|
||||||
val zeroBottom = Point3D(0f, 0f, -height / 2)
|
val zeroBottom = Float32Vector3D(0f, 0f, -height / 2)
|
||||||
val zeroTop = Point3D(0f, 0f, +height / 2)
|
val zeroTop = Float32Vector3D(0f, 0f, +height / 2)
|
||||||
for (it in 1 until segments) {
|
for (it in 1 until segments) {
|
||||||
face(bottomPoints[it - 1], zeroBottom, bottomPoints[it])
|
face(bottomPoints[it - 1], zeroBottom, bottomPoints[it])
|
||||||
face(topPoints[it - 1], topPoints[it], zeroTop)
|
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" }
|
require(segments >= 4) { "The number of segments in tube is too small" }
|
||||||
val angleStep = phi / (segments - 1)
|
val angleStep = phi / (segments - 1)
|
||||||
|
|
||||||
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
|
fun shape(r: Float, z: Float): List<Float32Vector3D> = (0 until segments).map { i ->
|
||||||
Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
Float32Vector3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
|
||||||
}
|
}
|
||||||
|
|
||||||
geometryBuilder.apply {
|
geometryBuilder.apply {
|
||||||
@ -56,8 +56,8 @@ public class ConeSurface(
|
|||||||
face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last())
|
face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last())
|
||||||
}
|
}
|
||||||
if (bottomInnerRadius == 0f) {
|
if (bottomInnerRadius == 0f) {
|
||||||
val zeroBottom = Point3D(0f, 0f, -height / 2)
|
val zeroBottom = Float32Vector3D(0f, 0f, -height / 2)
|
||||||
val zeroTop = Point3D(0f, 0f, height / 2)
|
val zeroTop = Float32Vector3D(0f, 0f, height / 2)
|
||||||
(1 until segments).forEach {
|
(1 until segments).forEach {
|
||||||
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it])
|
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it])
|
||||||
face(topOuterPoints[it - 1], topOuterPoints[it], zeroTop)
|
face(topOuterPoints[it - 1], topOuterPoints[it], zeroTop)
|
||||||
|
@ -7,7 +7,7 @@ import space.kscience.visionforge.setChild
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.convex")
|
@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(
|
public inline fun MutableVisionContainer<Solid>.convex(
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
@ -15,10 +15,10 @@ public inline fun MutableVisionContainer<Solid>.convex(
|
|||||||
): Convex = ConvexBuilder().apply(action).build().also { setChild(name, it) }
|
): Convex = ConvexBuilder().apply(action).build().also { setChild(name, it) }
|
||||||
|
|
||||||
public class ConvexBuilder {
|
public class ConvexBuilder {
|
||||||
private val points = ArrayList<Point3D>()
|
private val points = ArrayList<Float32Vector3D>()
|
||||||
|
|
||||||
public fun point(x: Number, y: Number, z: Number) {
|
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 {
|
public fun build(): Convex {
|
||||||
|
@ -4,23 +4,25 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
import space.kscience.dataforge.meta.MutableMeta
|
||||||
import space.kscience.dataforge.names.Name
|
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.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
|
|
||||||
public typealias Shape2D = List<Point2D>
|
public typealias Shape2D = List<Float32Vector2D>
|
||||||
|
|
||||||
@Serializable
|
@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) {
|
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
|
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
|
@Serializable
|
||||||
@SerialName("solid.extrude")
|
@SerialName("solid.extrude")
|
||||||
public class Extruded(
|
public class Extruded(
|
||||||
public val shape: List<Point2D>,
|
public val shape: List<Float32Vector2D>,
|
||||||
public val layers: List<Layer>,
|
public val layers: List<Layer>,
|
||||||
) : SolidBase<Extruded>(), GeometrySolid {
|
) : SolidBase<Extruded>(), GeometrySolid {
|
||||||
|
|
||||||
@ -50,18 +52,18 @@ public class Extruded(
|
|||||||
/**
|
/**
|
||||||
* Expand the shape for specific layers
|
* 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) ->
|
shape.map { (x, y) ->
|
||||||
val newX = layer.x + x * layer.scale
|
val newX = layer.x + x * layer.scale
|
||||||
val newY = layer.y + y * 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")
|
if (layers.size < 2) error("Extruded shape requires more than one layer")
|
||||||
|
|
||||||
var lowerLayer = layers.first()
|
var lowerLayer = layers.first()
|
||||||
var upperLayer: List<Point3D>
|
var upperLayer: List<Float32Vector3D>
|
||||||
|
|
||||||
for (i in (1 until layers.size)) {
|
for (i in (1 until layers.size)) {
|
||||||
upperLayer = layers[i]
|
upperLayer = layers[i]
|
||||||
@ -94,7 +96,7 @@ public class Extruded(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class ExtrudeBuilder(
|
public class ExtrudeBuilder(
|
||||||
public var shape: List<Point2D> = emptyList(),
|
public var shape: List<Float32Vector2D> = emptyList(),
|
||||||
public var layers: MutableList<Layer> = ArrayList(),
|
public var layers: MutableList<Layer> = ArrayList(),
|
||||||
public val properties: MutableMeta = MutableMeta(),
|
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 normal optional external normal to the face
|
||||||
* @param meta optional additional platform-specific parameters like color or texture index
|
* @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 build(): T
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun GeometryBuilder<*>.face4(
|
public fun GeometryBuilder<*>.face4(
|
||||||
vertex1: Point3D,
|
vertex1: Float32Vector3D,
|
||||||
vertex2: Point3D,
|
vertex2: Float32Vector3D,
|
||||||
vertex3: Point3D,
|
vertex3: Float32Vector3D,
|
||||||
vertex4: Point3D,
|
vertex4: Float32Vector3D,
|
||||||
normal: Point3D? = null,
|
normal: Float32Vector3D? = null,
|
||||||
meta: Meta = Meta.EMPTY
|
meta: Meta = Meta.EMPTY
|
||||||
) {
|
) {
|
||||||
face(vertex1, vertex2, vertex3, normal, meta)
|
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> 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
|
//FIXME won't work for non-convex shapes
|
||||||
val center = Point3D(
|
val center = Float32Vector3D(
|
||||||
shape.map { it.x }.average(),
|
shape.map { it.x }.average(),
|
||||||
shape.map { it.y }.average(),
|
shape.map { it.y }.average(),
|
||||||
shape.map { it.z }.average()
|
shape.map { it.z }.average()
|
||||||
|
@ -7,14 +7,14 @@ import space.kscience.visionforge.VisionBuilder
|
|||||||
import space.kscience.visionforge.setChild
|
import space.kscience.visionforge.setChild
|
||||||
|
|
||||||
public interface Hexagon : GeometrySolid {
|
public interface Hexagon : GeometrySolid {
|
||||||
public val node1: Point3D
|
public val node1: Float32Vector3D
|
||||||
public val node2: Point3D
|
public val node2: Float32Vector3D
|
||||||
public val node3: Point3D
|
public val node3: Float32Vector3D
|
||||||
public val node4: Point3D
|
public val node4: Float32Vector3D
|
||||||
public val node5: Point3D
|
public val node5: Float32Vector3D
|
||||||
public val node6: Point3D
|
public val node6: Float32Vector3D
|
||||||
public val node7: Point3D
|
public val node7: Float32Vector3D
|
||||||
public val node8: Point3D
|
public val node8: Float32Vector3D
|
||||||
|
|
||||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||||
geometryBuilder.face4(node1, node4, node3, node2)
|
geometryBuilder.face4(node1, node4, node3, node2)
|
||||||
@ -41,14 +41,14 @@ public class Box(
|
|||||||
private inline val dy get() = ySize / 2
|
private inline val dy get() = ySize / 2
|
||||||
private inline val dz get() = zSize / 2
|
private inline val dz get() = zSize / 2
|
||||||
|
|
||||||
override val node1: Point3D get() = Point3D(-dx, -dy, -dz)
|
override val node1: Float32Vector3D get() = Float32Vector3D(-dx, -dy, -dz)
|
||||||
override val node2: Point3D get() = Point3D(dx, -dy, -dz)
|
override val node2: Float32Vector3D get() = Float32Vector3D(dx, -dy, -dz)
|
||||||
override val node3: Point3D get() = Point3D(dx, dy, -dz)
|
override val node3: Float32Vector3D get() = Float32Vector3D(dx, dy, -dz)
|
||||||
override val node4: Point3D get() = Point3D(-dx, dy, -dz)
|
override val node4: Float32Vector3D get() = Float32Vector3D(-dx, dy, -dz)
|
||||||
override val node5: Point3D get() = Point3D(-dx, -dy, dz)
|
override val node5: Float32Vector3D get() = Float32Vector3D(-dx, -dy, dz)
|
||||||
override val node6: Point3D get() = Point3D(dx, -dy, dz)
|
override val node6: Float32Vector3D get() = Float32Vector3D(dx, -dy, dz)
|
||||||
override val node7: Point3D get() = Point3D(dx, dy, dz)
|
override val node7: Float32Vector3D get() = Float32Vector3D(dx, dy, dz)
|
||||||
override val node8: Point3D get() = Point3D(-dx, dy, dz)
|
override val node8: Float32Vector3D get() = Float32Vector3D(-dx, dy, dz)
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
@ -63,26 +63,26 @@ public inline fun MutableVisionContainer<Solid>.box(
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.hexagon")
|
@SerialName("solid.hexagon")
|
||||||
public class GenericHexagon(
|
public class GenericHexagon(
|
||||||
override val node1: Point3D,
|
override val node1: Float32Vector3D,
|
||||||
override val node2: Point3D,
|
override val node2: Float32Vector3D,
|
||||||
override val node3: Point3D,
|
override val node3: Float32Vector3D,
|
||||||
override val node4: Point3D,
|
override val node4: Float32Vector3D,
|
||||||
override val node5: Point3D,
|
override val node5: Float32Vector3D,
|
||||||
override val node6: Point3D,
|
override val node6: Float32Vector3D,
|
||||||
override val node7: Point3D,
|
override val node7: Float32Vector3D,
|
||||||
override val node8: Point3D,
|
override val node8: Float32Vector3D,
|
||||||
) : SolidBase<GenericHexagon>(), Hexagon
|
) : SolidBase<GenericHexagon>(), Hexagon
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public inline fun MutableVisionContainer<Solid>.hexagon(
|
public inline fun MutableVisionContainer<Solid>.hexagon(
|
||||||
node1: Point3D,
|
node1: Float32Vector3D,
|
||||||
node2: Point3D,
|
node2: Float32Vector3D,
|
||||||
node3: Point3D,
|
node3: Float32Vector3D,
|
||||||
node4: Point3D,
|
node4: Float32Vector3D,
|
||||||
node5: Point3D,
|
node5: Float32Vector3D,
|
||||||
node6: Point3D,
|
node6: Float32Vector3D,
|
||||||
node7: Point3D,
|
node7: Float32Vector3D,
|
||||||
node8: Point3D,
|
node8: Float32Vector3D,
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
action: Hexagon.() -> Unit = {},
|
action: Hexagon.() -> Unit = {},
|
||||||
): Hexagon = GenericHexagon(node1, node2, node3, node4, node5, node6, node7, node8).apply(action).also { setChild(name, it) }
|
): 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>() {
|
public abstract class LightSource : SolidBase<LightSource>() {
|
||||||
override val descriptor: MetaDescriptor get() = LightSource.descriptor
|
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 var intensity: Number by properties.root(includeStyles = false).number(INTENSITY_KEY) { 1.0 }
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
@ -70,6 +70,6 @@ public fun MutableVisionContainer<Solid>.pointLight(
|
|||||||
name: String? = null,
|
name: String? = null,
|
||||||
block: PointLightSource.() -> Unit = {},
|
block: PointLightSource.() -> Unit = {},
|
||||||
): PointLightSource = PointLightSource().apply(block).also {
|
): PointLightSource = PointLightSource().apply(block).also {
|
||||||
it.position = Point3D(x, y, z)
|
it.position = Float32Vector3D(x, y, z)
|
||||||
setChild(name, it)
|
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.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.number
|
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
|
@Serializable
|
||||||
@SerialName("solid.line")
|
@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()
|
//var lineType by string()
|
||||||
public var thickness: Number by properties.root(inherit = false, includeStyles = true).number { DEFAULT_THICKNESS }
|
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
|
@VisionBuilder
|
||||||
public fun MutableVisionContainer<Solid>.polyline(
|
public fun MutableVisionContainer<Solid>.polyline(
|
||||||
vararg points: Point3D,
|
vararg points: Float32Vector3D,
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
action: PolyLine.() -> Unit = {},
|
action: PolyLine.() -> Unit = {},
|
||||||
): PolyLine = PolyLine(points.toList()).apply(action).also { setChild(name, it) }
|
): 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.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.names.plus
|
import space.kscience.dataforge.names.plus
|
||||||
|
import space.kscience.kmath.complex.Quaternion
|
||||||
|
import space.kscience.kmath.complex.QuaternionField
|
||||||
|
import space.kscience.kmath.geometry.*
|
||||||
import space.kscience.visionforge.*
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY
|
import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY
|
||||||
import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY
|
import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY
|
||||||
@ -58,8 +61,6 @@ public interface Solid : Vision {
|
|||||||
|
|
||||||
public val ROTATION_KEY: Name = "rotation".asName()
|
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 X_ROTATION_KEY: Name = ROTATION_KEY + X_KEY
|
||||||
public val Y_ROTATION_KEY: Name = ROTATION_KEY + Y_KEY
|
public val Y_ROTATION_KEY: Name = ROTATION_KEY + Y_KEY
|
||||||
public val Z_ROTATION_KEY: Name = ROTATION_KEY + Z_KEY
|
public val Z_ROTATION_KEY: Name = ROTATION_KEY + Z_KEY
|
||||||
@ -122,15 +123,6 @@ public var Solid.layer: Int
|
|||||||
|
|
||||||
// Common properties
|
// Common properties
|
||||||
|
|
||||||
public enum class RotationOrder {
|
|
||||||
XYZ,
|
|
||||||
YZX,
|
|
||||||
ZXY,
|
|
||||||
XZY,
|
|
||||||
YXZ,
|
|
||||||
ZYX
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotation order
|
* Rotation order
|
||||||
*/
|
*/
|
||||||
@ -174,18 +166,21 @@ internal fun point(
|
|||||||
defaultX: Float,
|
defaultX: Float,
|
||||||
defaultY: Float = defaultX,
|
defaultY: Float = defaultX,
|
||||||
defaultZ: Float = defaultX,
|
defaultZ: Float = defaultX,
|
||||||
): ReadWriteProperty<Solid, Point3D?> =
|
): ReadWriteProperty<Solid, Float32Vector3D?> =
|
||||||
object : ReadWriteProperty<Solid, Point3D?> {
|
object : ReadWriteProperty<Solid, Float32Vector3D?> {
|
||||||
override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? {
|
override fun getValue(thisRef: Solid, property: KProperty<*>): Float32Vector3D? {
|
||||||
val item = thisRef.properties.own?.get(name) ?: return null
|
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 x: Float get() = item[X_KEY]?.float ?: defaultX
|
||||||
override val y: Float get() = item[Y_KEY]?.float ?: defaultY
|
override val y: Float get() = item[Y_KEY]?.float ?: defaultY
|
||||||
override val z: Float get() = item[Z_KEY]?.float ?: defaultZ
|
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) {
|
if (value == null) {
|
||||||
thisRef.properties.setProperty(name, null)
|
thisRef.properties.setProperty(name, null)
|
||||||
} else {
|
} else {
|
||||||
@ -196,9 +191,9 @@ internal fun point(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.position: Point3D? by point(POSITION_KEY, 0f)
|
public var Solid.position: Float32Vector3D? by point(POSITION_KEY, 0f)
|
||||||
public var Solid.rotation: Point3D? by point(ROTATION_KEY, 0f)
|
public var Solid.rotation: Float32Vector3D? by point(ROTATION_KEY, 0f)
|
||||||
public var Solid.scale: Point3D? by point(SCALE_KEY, 1f)
|
public var Solid.scale: Float32Vector3D? by point(SCALE_KEY, 1f)
|
||||||
|
|
||||||
public var Solid.x: Number by float(X_POSITION_KEY, 0f)
|
public var Solid.x: Number by float(X_POSITION_KEY, 0f)
|
||||||
public var Solid.y: Number by float(Y_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.rotationY: Number by float(Y_ROTATION_KEY, 0f)
|
||||||
public var Solid.rotationZ: Number by float(Z_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" }
|
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) {
|
set(value) {
|
||||||
properties.setValue(
|
properties.setValue(
|
||||||
Solid.QUATERNION_KEY,
|
ROTATION_KEY,
|
||||||
value?.let {
|
value?.let {
|
||||||
ListValue(
|
ListValue(
|
||||||
value.first,
|
value.w,
|
||||||
value.second.x,
|
value.x,
|
||||||
value.second.y,
|
value.y,
|
||||||
value.second.z
|
value.z
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
//public var Solid.quaternion: Quaternion?
|
* Quaternion value including information from euler angles
|
||||||
// get() = meta[Solid::quaternion.name]?.value?.doubleArray?.let { Quaternion(it) }
|
*/
|
||||||
// set(value) {
|
public var Solid.quaternion: Quaternion
|
||||||
// meta[Solid::quaternion.name] = value?.values?.asValue()
|
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.scaleX: Number by float(X_SCALE_KEY, 1f)
|
||||||
public var Solid.scaleY: Number by float(Y_SCALE_KEY, 1f)
|
public var Solid.scaleY: Number by float(Y_SCALE_KEY, 1f)
|
||||||
public var Solid.scaleZ: Number by float(Z_SCALE_KEY, 1f)
|
public var Solid.scaleZ: Number by float(Z_SCALE_KEY, 1f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add rotation with given [angle] relative to given [axis]
|
||||||
|
*/
|
||||||
|
public fun Solid.rotate(angle: Angle, axis: DoubleVector3D): Unit = with(QuaternionField) {
|
||||||
|
quaternion = Quaternion.fromRotation(angle, axis)*quaternion
|
||||||
|
}
|
@ -17,7 +17,7 @@ import space.kscience.visionforge.VisionBuilder
|
|||||||
*/
|
*/
|
||||||
public interface PrototypeHolder {
|
public interface PrototypeHolder {
|
||||||
/**
|
/**
|
||||||
* Build or update prototype tree
|
* Build or update the prototype tree
|
||||||
*/
|
*/
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun prototypes(builder: MutableVisionContainer<Solid>.() -> Unit)
|
public fun prototypes(builder: MutableVisionContainer<Solid>.() -> Unit)
|
||||||
@ -43,6 +43,9 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, Mutable
|
|||||||
it to value
|
it to value
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a child solid with given relative [name] if it exists
|
||||||
|
*/
|
||||||
public operator fun get(name: Name): Solid? = children.getChild(name) as? Solid
|
public operator fun get(name: Name): Solid? = children.getChild(name) as? Solid
|
||||||
|
|
||||||
private var prototypes: SolidGroup?
|
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
|
@VisionBuilder
|
||||||
public inline fun MutableVisionContainer<Solid>.solidGroup(
|
public inline fun MutableVisionContainer<Solid>.solidGroup(
|
||||||
name: Name? = null,
|
name: Name? = null,
|
||||||
@ -99,3 +104,8 @@ public inline fun MutableVisionContainer<Solid>.solidGroup(
|
|||||||
name: String,
|
name: String,
|
||||||
action: SolidGroup.() -> Unit = {},
|
action: SolidGroup.() -> Unit = {},
|
||||||
): SolidGroup = solidGroup(name.parseAsName(), action)
|
): 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.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
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.coroutines.launch
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
@ -61,7 +63,7 @@ public class SolidReference(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getValue(name: Name, inherit: Boolean?, includeStyles: Boolean?): Value? {
|
override fun getValue(name: Name, inherit: Boolean?, includeStyles: Boolean?): Value? {
|
||||||
if(name == Vision.STYLE_KEY){
|
if (name == Vision.STYLE_KEY) {
|
||||||
return buildList {
|
return buildList {
|
||||||
properties?.getValue(Vision.STYLE_KEY)?.list?.forEach {
|
properties?.getValue(Vision.STYLE_KEY)?.list?.forEach {
|
||||||
add(it)
|
add(it)
|
||||||
@ -90,7 +92,7 @@ public class SolidReference(
|
|||||||
prototype.getStyleProperty(name)?.value?.let { return it }
|
prototype.getStyleProperty(name)?.value?.let { return it }
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inheritFlag){
|
if (inheritFlag) {
|
||||||
//5. own inheritance
|
//5. own inheritance
|
||||||
parent?.properties?.getValue(name, inheritFlag, includeStyles)?.let { return it }
|
parent?.properties?.getValue(name, inheritFlag, includeStyles)?.let { return it }
|
||||||
//6. prototype inheritance
|
//6. prototype inheritance
|
||||||
@ -226,13 +228,13 @@ internal class SolidReferenceChild(
|
|||||||
*/
|
*/
|
||||||
public fun MutableVisionContainer<Solid>.ref(
|
public fun MutableVisionContainer<Solid>.ref(
|
||||||
templateName: Name,
|
templateName: Name,
|
||||||
name: String? = null,
|
name: Name? = null,
|
||||||
): SolidReference = SolidReference(templateName).also { setChild(name, it) }
|
): SolidReference = SolidReference(templateName).also { setChild(name, it) }
|
||||||
|
|
||||||
public fun MutableVisionContainer<Solid>.ref(
|
public fun MutableVisionContainer<Solid>.ref(
|
||||||
templateName: String,
|
templateName: Name,
|
||||||
name: String? = null,
|
name: String,
|
||||||
): SolidReference = ref(Name.parse(templateName), name)
|
): SolidReference = ref(templateName, name.parseAsName())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add new [SolidReference] wrapping given object and automatically adding it to the prototypes.
|
* 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) {
|
} else if (existing != obj) {
|
||||||
error("Can't add different prototype on top of existing one")
|
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(AmbientLightSource.serializer())
|
||||||
subclass(PointLightSource.serializer())
|
subclass(PointLightSource.serializer())
|
||||||
|
|
||||||
|
subclass(AxesSolid.serializer())
|
||||||
}
|
}
|
||||||
|
|
||||||
public val serializersModuleForSolids: SerializersModule = SerializersModule {
|
public val serializersModuleForSolids: SerializersModule = SerializersModule {
|
||||||
|
|
||||||
polymorphic(Vision::class) {
|
polymorphic(Vision::class) {
|
||||||
subclass(SimpleVisionGroup.serializer())
|
subclass(SimpleVisionGroup.serializer())
|
||||||
solids()
|
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 {
|
) : SolidBase<Sphere>(), GeometrySolid {
|
||||||
|
|
||||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
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
|
// This transformation matches three.js sphere implementation
|
||||||
val y = r * cos(theta)
|
val y = r * cos(theta)
|
||||||
val z = r * sin(theta) * sin(phi)
|
val z = r * sin(theta) * sin(phi)
|
||||||
val x = -r * sin(theta) * cos(phi)
|
val x = -r * sin(theta) * cos(phi)
|
||||||
return Point3D(x, y, z)
|
return Float32Vector3D(x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
val segments = this.detail ?: 32
|
val segments = this.detail ?: 32
|
||||||
|
@ -27,12 +27,12 @@ public class SphereLayer(
|
|||||||
require(outerRadius > 0) { "Outer radius must be positive" }
|
require(outerRadius > 0) { "Outer radius must be positive" }
|
||||||
require(innerRadius >= 0) { "inner radius must be non-negative" }
|
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
|
// This transformation matches three.js sphere implementation
|
||||||
val y = r * cos(theta)
|
val y = r * cos(theta)
|
||||||
val z = r * sin(theta) * sin(phi)
|
val z = r * sin(theta) * sin(phi)
|
||||||
val x = -r * sin(theta) * cos(phi)
|
val x = -r * sin(theta) * cos(phi)
|
||||||
return Point3D(x, y, z)
|
return Float32Vector3D(x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
val segments = detail ?: 32
|
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
|
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.Meta
|
||||||
import space.kscience.dataforge.meta.MetaProvider
|
import space.kscience.dataforge.meta.MetaProvider
|
||||||
import space.kscience.dataforge.meta.float
|
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.Y_KEY
|
||||||
import space.kscience.visionforge.solid.Solid.Companion.Z_KEY
|
import space.kscience.visionforge.solid.Solid.Companion.Z_KEY
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.sqrt
|
|
||||||
|
|
||||||
public const val PI2: Float = 2 * PI.toFloat()
|
public const val PI2: Float = 2 * PI.toFloat()
|
||||||
|
|
||||||
@Serializable
|
public fun Float32Vector2D.toMeta(): Meta = Meta {
|
||||||
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 {
|
|
||||||
X_KEY put x
|
X_KEY put x
|
||||||
Y_KEY put y
|
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)
|
//@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
|
||||||
public interface Point3D {
|
//@Serializable(Point3DSerializer::class)
|
||||||
public val x: Float
|
//public interface MutablePoint3D : Float32Vector3D {
|
||||||
public val y: Float
|
// override var x: Float
|
||||||
public val z: 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 {
|
internal fun MetaProvider.point3D(default: Float = 0f) = Float32Euclidean3DSpace.vector(
|
||||||
public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0)
|
getMeta(X_KEY).float ?: default,
|
||||||
public val ONE: Point3D = Point3D(1.0, 1.0, 1.0)
|
getMeta(Y_KEY).float ?: default,
|
||||||
}
|
getMeta(Z_KEY).float ?: default
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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(
|
public fun Float32Vector3D.toMeta(): Meta = Meta {
|
||||||
-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 {
|
|
||||||
X_KEY put x
|
X_KEY put x
|
||||||
Y_KEY put y
|
Y_KEY put y
|
||||||
Z_KEY put z
|
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[X_KEY].float ?: default,
|
||||||
this[Y_KEY].float ?: default,
|
this[Y_KEY].float ?: default,
|
||||||
this[Z_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.descriptors.value
|
||||||
import space.kscience.dataforge.meta.double
|
import space.kscience.dataforge.meta.double
|
||||||
|
|
||||||
|
@Deprecated("Use separate axes object instead")
|
||||||
public class AxesScheme : Scheme() {
|
public class AxesScheme : Scheme() {
|
||||||
public var visible: Boolean by boolean(false)
|
public var visible: Boolean by boolean(false)
|
||||||
public var size: Double by double(AXIS_SIZE)
|
public var size: Double by double(AXIS_SIZE)
|
||||||
public var width: Double by double(AXIS_WIDTH)
|
public var width: Double by double(AXIS_WIDTH)
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
public companion object : SchemeSpec<AxesScheme>(::AxesScheme) {
|
public companion object : SchemeSpec<AxesScheme>(::AxesScheme) {
|
||||||
public const val AXIS_SIZE: Double = 1000.0
|
public const val AXIS_SIZE: Double = 1000.0
|
||||||
public const val AXIS_WIDTH: Double = 3.0
|
public const val AXIS_WIDTH: Double = 3.0
|
||||||
|
@ -59,6 +59,7 @@ public class CanvasSize : Scheme() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class Canvas3DOptions : Scheme() {
|
public class Canvas3DOptions : Scheme() {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
public var axes: AxesScheme by spec(AxesScheme)
|
public var axes: AxesScheme by spec(AxesScheme)
|
||||||
public var camera: CameraScheme by spec(CameraScheme)
|
public var camera: CameraScheme by spec(CameraScheme)
|
||||||
public var controls: ControlsScheme by spec(ControlsScheme)
|
public var controls: ControlsScheme by spec(ControlsScheme)
|
||||||
@ -75,6 +76,7 @@ public class Canvas3DOptions : Scheme() {
|
|||||||
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
|
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
|
||||||
override val descriptor: MetaDescriptor by lazy {
|
override val descriptor: MetaDescriptor by lazy {
|
||||||
MetaDescriptor {
|
MetaDescriptor {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
scheme(Canvas3DOptions::axes, AxesScheme)
|
scheme(Canvas3DOptions::axes, AxesScheme)
|
||||||
|
|
||||||
value(Canvas3DOptions::layers) {
|
value(Canvas3DOptions::layers) {
|
||||||
|
@ -3,6 +3,7 @@ package space.kscience.visionforge.solid.transform
|
|||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
|
import space.kscience.kmath.complex.QuaternionField
|
||||||
import space.kscience.visionforge.root
|
import space.kscience.visionforge.root
|
||||||
import space.kscience.visionforge.solid.*
|
import space.kscience.visionforge.solid.*
|
||||||
|
|
||||||
@ -14,10 +15,7 @@ internal fun Solid.updateFrom(other: Solid): Solid {
|
|||||||
x += other.x
|
x += other.x
|
||||||
y += other.y
|
y += other.y
|
||||||
z += other.y
|
z += other.y
|
||||||
if(quaternion != null || other.quaternion != null) TODO("Quaternion support not implemented")
|
quaternion = with(QuaternionField) { other.quaternion * quaternion }
|
||||||
rotationX += other.rotationX
|
|
||||||
rotationY += other.rotationY
|
|
||||||
rotationZ += other.rotationZ
|
|
||||||
scaleX *= other.scaleX
|
scaleX *= other.scaleX
|
||||||
scaleY *= other.scaleY
|
scaleY *= other.scaleY
|
||||||
scaleZ *= other.scaleZ
|
scaleZ *= other.scaleZ
|
||||||
|
@ -18,7 +18,7 @@ class CompositeTest {
|
|||||||
detail = 32
|
detail = 32
|
||||||
}
|
}
|
||||||
material {
|
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.getIndexed
|
||||||
import space.kscience.dataforge.meta.toMeta
|
import space.kscience.dataforge.meta.toMeta
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class ConvexTest {
|
class ConvexTest {
|
||||||
@OptIn(DFExperimental::class)
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
@Test
|
@Test
|
||||||
fun testConvexBuilder() {
|
fun testConvexBuilder() {
|
||||||
val group = testSolids.solidGroup {
|
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