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:
Alexander Nozik 2023-09-16 12:30:52 +03:00
commit 20fc81305c
131 changed files with 1615 additions and 960 deletions

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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 TaitBryan 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 { it
"${name}_${volume.fName}" }
} }
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)
} }
} }

View File

@ -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)
} }
} }
} }

View File

@ -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)
} }
} }
} }

View File

@ -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))

View File

@ -7,7 +7,8 @@ kscience{
} }
kotlin{ kotlin{
js(IR){ explicitApi = null
js{
useCommonJs() useCommonJs()
browser { browser {
binaries.executable() binaries.executable()

View File

@ -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
} }

View File

@ -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 {

View File

@ -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")
} }

View File

@ -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>
/** /**
* *

View File

@ -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")
} }
} }
} }

View File

@ -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
} }

View File

@ -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)
} }
} }
} }

View File

@ -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"))

View File

@ -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())
} }

View File

@ -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 ->

View File

@ -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")

View 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
}

View File

@ -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",

View File

@ -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)
} }

View File

@ -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() }
}
}
}
}

View 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
}
}
}
}
}
}

View File

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

View File

@ -0,0 +1,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)
}
}
}
}
}

View File

@ -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()

View File

@ -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
} }

View File

@ -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))
} }
} }

View File

@ -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,

View File

@ -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)
} }

View File

@ -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")
} }
} }
} }

View File

@ -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")

View File

@ -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
} }

View File

@ -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

View File

@ -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")
} }
} }
} }

View File

@ -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")
//}

View File

@ -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
} }
} }

View File

@ -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()

View File

@ -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)
}

View File

@ -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()
}
}

View File

@ -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")
}
```

View File

@ -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
}

View File

@ -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)
}

View File

@ -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() }
}
}
}
}

View File

@ -1,3 +0,0 @@
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
config.module.rules.push(...ringConfig.module.rules)

View File

@ -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"
) )

View File

@ -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())
} }
} }

View File

@ -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) {
space.kscience.visionforge.react.createRoot(element).render { if(meta["controls.enabled"].boolean == false){
child(ThreeCanvasWithControls) { three.render(element, name, vision, meta)
attrs { } else {
this.solids = three.solids space.kscience.visionforge.react.createRoot(element).render {
this.builderOfSolid = context.async { vision as Solid} child(ThreeCanvasWithControls) {
attrs {
this.solids = three.solids
this.options = Canvas3DOptions.read(meta)
this.builderOfSolid = context.async { vision as Solid }
}
} }
} }
} }

View File

@ -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"))

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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(

View File

@ -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)
} }
} }

View File

@ -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") {

View File

@ -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 {

View File

@ -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(

View File

@ -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"

View File

@ -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 {

View File

@ -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) {

View File

@ -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)
} }

View File

@ -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)
} }
} }

View File

@ -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)
} }

View 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)
}

View 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)

View File

@ -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)) {
@ -24,4 +27,8 @@ public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlF
builder() builder()
} }
} }
} }
public fun VisionForge.form(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment =
HtmlFormFragment(id, builder)

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View 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: {}
}
]
}
)

View File

@ -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")
} }
} }

View File

@ -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 {

View File

@ -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) {

View File

@ -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()
} }

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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(),
) { ) {

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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) }

View File

@ -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)
} }

View File

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

View File

@ -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) }

View File

@ -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
}

View File

@ -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)

View File

@ -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())
} }

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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) }

View File

@ -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

View File

@ -7,11 +7,13 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.double
@Deprecated("Use separate axes object instead")
public class AxesScheme : Scheme() { public class AxesScheme : Scheme() {
public var visible: Boolean by boolean(false) public var visible: Boolean by boolean(false)
public var size: Double by double(AXIS_SIZE) public var size: Double by double(AXIS_SIZE)
public var width: Double by double(AXIS_WIDTH) public var width: Double by double(AXIS_WIDTH)
@Suppress("DEPRECATION")
public companion object : SchemeSpec<AxesScheme>(::AxesScheme) { public companion object : SchemeSpec<AxesScheme>(::AxesScheme) {
public const val AXIS_SIZE: Double = 1000.0 public const val AXIS_SIZE: Double = 1000.0
public const val AXIS_WIDTH: Double = 3.0 public const val AXIS_WIDTH: Double = 3.0

View File

@ -59,6 +59,7 @@ public class CanvasSize : Scheme() {
} }
public class Canvas3DOptions : Scheme() { public class Canvas3DOptions : Scheme() {
@Suppress("DEPRECATION")
public var axes: AxesScheme by spec(AxesScheme) public var axes: AxesScheme by spec(AxesScheme)
public var camera: CameraScheme by spec(CameraScheme) public var camera: CameraScheme by spec(CameraScheme)
public var controls: ControlsScheme by spec(ControlsScheme) public var controls: ControlsScheme by spec(ControlsScheme)
@ -75,6 +76,7 @@ public class Canvas3DOptions : Scheme() {
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) { public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
override val descriptor: MetaDescriptor by lazy { override val descriptor: MetaDescriptor by lazy {
MetaDescriptor { MetaDescriptor {
@Suppress("DEPRECATION")
scheme(Canvas3DOptions::axes, AxesScheme) scheme(Canvas3DOptions::axes, AxesScheme)
value(Canvas3DOptions::layers) { value(Canvas3DOptions::layers) {

View File

@ -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

View File

@ -18,7 +18,7 @@ class CompositeTest {
detail = 32 detail = 32
} }
material { material {
color.set("pink") color("pink")
} }
} }
} }

View File

@ -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