Use KMath-geometry for solids

This commit is contained in:
Alexander Nozik 2023-06-03 17:55:27 +03:00
parent a872f10523
commit 38302eac4c
32 changed files with 365 additions and 221 deletions

View File

@ -32,12 +32,12 @@ private fun Solid.rotate(rot: DoubleArray) {
val xAngle = atan2(-rot[5], rot[8])
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
val zAngle = atan2(-rot[1], rot[0])
rotation = Point3D(xAngle, yAngle, zAngle)
rotation = Float32Vector3D(xAngle, yAngle, zAngle)
}
private fun Solid.translate(trans: DoubleArray) {
val (x, y, z) = trans
position = Point3D(x, y, z)
position = Float32Vector3D(x, y, z)
}
private fun Solid.useMatrix(matrix: DGeoMatrix?) {
@ -72,7 +72,7 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) {
val fScale by matrix.meta.doubleArray()
translate(fTranslation)
rotate(fRotationMatrix)
scale = Point3D(fScale[0], fScale[1], fScale[2])
scale = Float32Vector3D(fScale[0], fScale[1], fScale[2])
}
}
}
@ -248,14 +248,14 @@ private fun SolidGroup.addShape(
val fDz by shape.meta.double(0.0)
//TODO check proper node order
val node1 = Point3D(-fBl1, -fH1, -fDz)
val node2 = Point3D(fBl1, -fH1, -fDz)
val node3 = Point3D(fTl1, fH1, -fDz)
val node4 = Point3D(-fTl1, fH1, -fDz)
val node5 = Point3D(-fBl2, -fH2, fDz)
val node6 = Point3D(fBl2, -fH2, fDz)
val node7 = Point3D(fTl2, fH2, fDz)
val node8 = Point3D(-fTl2, fH2, fDz)
val node1 = Float32Vector3D(-fBl1, -fH1, -fDz)
val node2 = Float32Vector3D(fBl1, -fH1, -fDz)
val node3 = Float32Vector3D(fTl1, fH1, -fDz)
val node4 = Float32Vector3D(-fTl1, fH1, -fDz)
val node5 = Float32Vector3D(-fBl2, -fH2, fDz)
val node6 = Float32Vector3D(fBl2, -fH2, fDz)
val node7 = Float32Vector3D(fTl2, fH2, fDz)
val node8 = Float32Vector3D(-fTl2, fH2, fDz)
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
}
@ -264,7 +264,7 @@ private fun SolidGroup.addShape(
val fScale by shape.dObject(::DGeoScale)
fShape?.let { scaledShape ->
solidGroup(name?.let { Name.parse(it) }) {
scale = Point3D(fScale?.x ?: 1.0, fScale?.y ?: 1.0, fScale?.z ?: 1.0)
scale = Float32Vector3D(fScale?.x ?: 1.0, fScale?.y ?: 1.0, fScale?.z ?: 1.0)
addShape(scaledShape, context)
apply(block)
}

View File

@ -25,12 +25,12 @@ private fun Solid.rotate(rot: DoubleArray) {
val xAngle = atan2(-rot[5], rot[8])
val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
val zAngle = atan2(-rot[1], rot[0])
rotation = Point3D(xAngle, yAngle, zAngle)
rotation = Float32Vector3D(xAngle, yAngle, zAngle)
}
private fun Solid.translate(trans: DoubleArray) {
val (x, y, z) = trans
position = Point3D(x, y, z)
position = Float32Vector3D(x, y, z)
}
private fun Solid.useMatrix(matrix: TGeoMatrix?) {
@ -52,7 +52,7 @@ private fun Solid.useMatrix(matrix: TGeoMatrix?) {
translate(matrix.fTranslation)
rotate(matrix.fRotationMatrix)
val (xScale, yScale, zScale) = matrix.fScale
scale = Point3D(xScale, yScale, zScale)
scale = Float32Vector3D(xScale, yScale, zScale)
}
}
}

View File

@ -7,6 +7,7 @@ kscience{
}
kotlin{
explicitApi = null
js(IR){
useCommonJs()
browser {

View File

@ -40,6 +40,8 @@ kscience {
}
}
kotlin.explicitApi = null
application {
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
}

View File

@ -1,9 +1,9 @@
package ru.mipt.npm.muon.monitor
import kotlinx.serialization.Serializable
import space.kscience.visionforge.solid.Point3D
import space.kscience.visionforge.solid.Float32Vector3D
typealias Track = List<Point3D>
typealias Track = List<Float32Vector3D>
/**
*

View File

@ -16,7 +16,7 @@ class Model(val manager: VisionManager) {
private fun MutableVisionContainer<Solid>.pixel(pixel: SC1) {
val group = solidGroup(pixel.name) {
position = Point3D(pixel.center.x, pixel.center.y, pixel.center.z)
position = Float32Vector3D(pixel.center.x, pixel.center.y, pixel.center.z)
box(pixel.xSize, pixel.ySize, pixel.zSize)
label(pixel.name) {
z = -Monitor.PIXEL_Z_SIZE / 2 - 5

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_Z_SIZE
import space.kscience.visionforge.solid.Point3D
import space.kscience.visionforge.solid.plus
import space.kscience.visionforge.solid.Float32Euclidean3DSpace
import space.kscience.visionforge.solid.Float32Vector3D
/**
* A single pixel
*/
class SC1(
val name: String,
val center: Point3D,
val center: Float32Vector3D,
val xSize: Float = PIXEL_XY_SIZE, val ySize: Float = PIXEL_XY_SIZE, val zSize: Float = PIXEL_Z_SIZE,
)
class SC16(
val name: String,
val center: Point3D,
val center: Float32Vector3D,
) {
/**
@ -109,9 +109,9 @@ class SC16(
else -> throw Error()
}
val offset = Point3D(-y, x, 0)//rotateDetector(Point3D(x, y, 0.0));
val offset = Float32Vector3D(-y, x, 0)//rotateDetector(Point3D(x, y, 0.0));
val pixelName = "${name}_${index}"
SC1(pixelName, offset + center)
SC1(pixelName, with(Float32Euclidean3DSpace) { offset + center })
}
}
}
@ -154,7 +154,7 @@ object Monitor {
val x = split[4].toDouble() - 500
val y = split[5].toDouble() - 500
val z = 180 - split[6].toDouble()
SC16(detectorName, Point3D(x, y, z))
SC16(detectorName, Float32Vector3D(x, y, z))
} else {
null
}

View File

@ -10,8 +10,7 @@ import io.ktor.server.application.install
import io.ktor.server.application.log
import io.ktor.server.cio.CIO
import io.ktor.server.engine.embeddedServer
import io.ktor.server.http.content.resources
import io.ktor.server.http.content.static
import io.ktor.server.http.content.staticResources
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.response.respond
import io.ktor.server.response.respondText
@ -53,9 +52,7 @@ fun Application.module(context: Context = Global) {
status = HttpStatusCode.OK
)
}
static("/") {
resources()
}
staticResources("/", null)
}
try {
Desktop.getDesktop().browse(URI("http://localhost:8080/index.html"))

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 ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
import ru.mipt.npm.muon.monitor.Monitor.GEOMETRY_TOLERANCE
import space.kscience.visionforge.solid.Point3D
import space.kscience.visionforge.solid.Float32Vector3D
/**
* Created by darksnake on 11-May-16.
@ -50,12 +50,12 @@ fun makeTrack(x: Double, y: Double, theta: Double, phi: Double): Line {
)
}
fun Vector3D.toPoint() = Point3D(x, y, z)
fun Vector3D.toKMathVector() = Float32Vector3D(x, y, z)
fun Line.toPoints(): List<Point3D> {
fun Line.toKMathVectors(): List<Float32Vector3D> {
val basePoint = basePlane.intersection(this)
val bottom = basePoint.subtract(2000.0, direction)
val top = basePoint.add(2000.0, direction)
return listOf(bottom.toPoint(), top.toPoint())
return listOf(bottom.toKMathVector(), top.toKMathVector())
}

View File

@ -43,7 +43,7 @@ fun readEffs(): Map<String, Double> {
fun buildEventByTrack(index: Int, track: Line, hitResolver: (Line) -> Collection<SC1> = defaultHitResolver): Event {
return Event(index, track.toPoints(), hitResolver(track).map { it.name })
return Event(index, track.toKMathVectors(), hitResolver(track).map { it.name })
}
val defaultHitResolver: (Line) -> Collection<SC1> = { track: Line ->

View File

@ -19,6 +19,8 @@ kscience {
}
}
kotlin.explicitApi = null
application {
mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt")
}

View File

@ -130,7 +130,10 @@ fun VisionLayout<Solid>.showcase() {
color.set(Colors.blue)
}
repeat(20) {
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
polyline(
Float32Vector3D(100, 100, 100),
Float32Vector3D(-100, -100, -100)
) {
thickness = 3.0
rotationX = it * PI2 / 20
color.set(Colors.green)

View File

@ -248,14 +248,14 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
val dyBottom = solid.y1.toDouble() / 2
val dyTop = solid.y2.toDouble() / 2
val dz = solid.z.toDouble() / 2
val node1 = Point3D(-dxBottom, -dyBottom, -dz)
val node2 = Point3D(dxBottom, -dyBottom, -dz)
val node3 = Point3D(dxBottom, dyBottom, -dz)
val node4 = Point3D(-dxBottom, dyBottom, -dz)
val node5 = Point3D(-dxTop, -dyTop, dz)
val node6 = Point3D(dxTop, -dyTop, dz)
val node7 = Point3D(dxTop, dyTop, dz)
val node8 = Point3D(-dxTop, dyTop, dz)
val node1 = Float32Vector3D(-dxBottom, -dyBottom, -dz)
val node2 = Float32Vector3D(dxBottom, -dyBottom, -dz)
val node3 = Float32Vector3D(dxBottom, dyBottom, -dz)
val node4 = Float32Vector3D(-dxBottom, dyBottom, -dz)
val node5 = Float32Vector3D(-dxTop, -dyTop, dz)
val node6 = Float32Vector3D(dxTop, -dyTop, dz)
val node7 = Float32Vector3D(dxTop, dyTop, dz)
val node8 = Float32Vector3D(-dxTop, dyTop, dz)
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
}

View File

@ -2,6 +2,8 @@ plugins {
id("space.kscience.gradle.mpp")
}
val kmathVersion = "0.3.1"
kscience {
jvm()
js()
@ -11,6 +13,7 @@ kscience {
}
useCoroutines()
dependencies {
api("space.kscience:kmath-geometry:0.3.1")
api(projects.visionforgeCore)
}
dependencies(jvmTest) {

View File

@ -36,8 +36,8 @@ public class ConeSegment(
require(segments >= 4) { "The number of segments in cone is too small" }
val angleStep = phi / (segments - 1)
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
fun shape(r: Float, z: Float): List<Float32Vector3D> = (0 until segments).map { i ->
Float32Vector3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
}
geometryBuilder.apply {
@ -53,8 +53,8 @@ public class ConeSegment(
if (phi == PI2) {
face4(bottomPoints.last(), bottomPoints[0], topPoints[0], topPoints.last())
}
val zeroBottom = Point3D(0f, 0f, -height / 2)
val zeroTop = Point3D(0f, 0f, +height / 2)
val zeroBottom = Float32Vector3D(0f, 0f, -height / 2)
val zeroTop = Float32Vector3D(0f, 0f, +height / 2)
for (it in 1 until segments) {
face(bottomPoints[it - 1], zeroBottom, bottomPoints[it])
face(topPoints[it - 1], topPoints[it], zeroTop)

View File

@ -38,8 +38,8 @@ public class ConeSurface(
require(segments >= 4) { "The number of segments in tube is too small" }
val angleStep = phi / (segments - 1)
fun shape(r: Float, z: Float): List<Point3D> = (0 until segments).map { i ->
Point3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
fun shape(r: Float, z: Float): List<Float32Vector3D> = (0 until segments).map { i ->
Float32Vector3D(r * cos(phiStart + angleStep * i), r * sin(phiStart + angleStep * i), z)
}
geometryBuilder.apply {
@ -56,8 +56,8 @@ public class ConeSurface(
face4(bottomOuterPoints.last(), bottomOuterPoints[0], topOuterPoints[0], topOuterPoints.last())
}
if (bottomInnerRadius == 0f) {
val zeroBottom = Point3D(0f, 0f, -height / 2)
val zeroTop = Point3D(0f, 0f, height / 2)
val zeroBottom = Float32Vector3D(0f, 0f, -height / 2)
val zeroTop = Float32Vector3D(0f, 0f, height / 2)
(1 until segments).forEach {
face(bottomOuterPoints[it - 1], zeroBottom, bottomOuterPoints[it])
face(topOuterPoints[it - 1], topOuterPoints[it], zeroTop)

View File

@ -7,7 +7,7 @@ import space.kscience.visionforge.setChild
@Serializable
@SerialName("solid.convex")
public class Convex(public val points: List<Point3D>) : SolidBase<Convex>()
public class Convex(public val points: List<Float32Vector3D>) : SolidBase<Convex>()
public inline fun MutableVisionContainer<Solid>.convex(
name: String? = null,
@ -15,10 +15,10 @@ public inline fun MutableVisionContainer<Solid>.convex(
): Convex = ConvexBuilder().apply(action).build().also { setChild(name, it) }
public class ConvexBuilder {
private val points = ArrayList<Point3D>()
private val points = ArrayList<Float32Vector3D>()
public fun point(x: Number, y: Number, z: Number) {
points.add(Point3D(x, y, z))
points.add(Float32Vector3D(x, y, z))
}
public fun build(): Convex {

View File

@ -4,19 +4,23 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.*
import space.kscience.kmath.geometry.component1
import space.kscience.kmath.geometry.component2
import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
public typealias Shape2D = List<Point2D>
public typealias Shape2D = List<Float32Vector2D>
@Serializable
public class Shape2DBuilder(private val points: ArrayList<Point2D> = ArrayList()) {
public class Shape2DBuilder(private val points: ArrayList<Float32Vector2D> = ArrayList()) {
public fun point(x: Number, y: Number) {
points.add(Point2D(x, y))
points.add(Float32Vector2D(x, y))
}
public infix fun Number.to(y: Number): Unit = point(this, y)
@ -38,7 +42,7 @@ public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Flo
@Serializable
@SerialName("solid.extrude")
public class Extruded(
public val shape: List<Point2D>,
public val shape: List<Float32Vector2D>,
public val layers: List<Layer>,
) : SolidBase<Extruded>(), GeometrySolid {
@ -50,18 +54,18 @@ public class Extruded(
/**
* Expand the shape for specific layers
*/
val layers: List<List<Point3D>> = layers.map { layer ->
val layers: List<List<Float32Vector3D>> = layers.map { layer ->
shape.map { (x, y) ->
val newX = layer.x + x * layer.scale
val newY = layer.y + y * layer.scale
Point3D(newX, newY, layer.z)
Float32Vector3D(newX, newY, layer.z)
}
}
if (layers.size < 2) error("Extruded shape requires more than one layer")
var lowerLayer = layers.first()
var upperLayer: List<Point3D>
var upperLayer: List<Float32Vector3D>
for (i in (1 until layers.size)) {
upperLayer = layers[i]
@ -94,7 +98,7 @@ public class Extruded(
}
public class ExtrudeBuilder(
public var shape: List<Point2D> = emptyList(),
public var shape: List<Float32Vector2D> = emptyList(),
public var layers: MutableList<Layer> = ArrayList(),
public val properties: MutableMeta = MutableMeta(),
) {

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 meta optional additional platform-specific parameters like color or texture index
*/
public fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = Meta.EMPTY)
public fun face(vertex1: Float32Vector3D, vertex2: Float32Vector3D, vertex3: Float32Vector3D, normal: Float32Vector3D? = null, meta: Meta = Meta.EMPTY)
public fun build(): T
}
public fun GeometryBuilder<*>.face4(
vertex1: Point3D,
vertex2: Point3D,
vertex3: Point3D,
vertex4: Point3D,
normal: Point3D? = null,
vertex1: Float32Vector3D,
vertex2: Float32Vector3D,
vertex3: Float32Vector3D,
vertex4: Float32Vector3D,
normal: Float32Vector3D? = null,
meta: Meta = Meta.EMPTY
) {
face(vertex1, vertex2, vertex3, normal, meta)
@ -37,9 +37,9 @@ public interface GeometrySolid : Solid {
public fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>)
}
public fun <T : Any> GeometryBuilder<T>.cap(shape: List<Point3D>, normal: Point3D? = null) {
public fun <T : Any> GeometryBuilder<T>.cap(shape: List<Float32Vector3D>, normal: Float32Vector3D? = null) {
//FIXME won't work for non-convex shapes
val center = Point3D(
val center = Float32Vector3D(
shape.map { it.x }.average(),
shape.map { it.y }.average(),
shape.map { it.z }.average()

View File

@ -7,14 +7,14 @@ import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild
public interface Hexagon : GeometrySolid {
public val node1: Point3D
public val node2: Point3D
public val node3: Point3D
public val node4: Point3D
public val node5: Point3D
public val node6: Point3D
public val node7: Point3D
public val node8: Point3D
public val node1: Float32Vector3D
public val node2: Float32Vector3D
public val node3: Float32Vector3D
public val node4: Float32Vector3D
public val node5: Float32Vector3D
public val node6: Float32Vector3D
public val node7: Float32Vector3D
public val node8: Float32Vector3D
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
geometryBuilder.face4(node1, node4, node3, node2)
@ -41,14 +41,14 @@ public class Box(
private inline val dy get() = ySize / 2
private inline val dz get() = zSize / 2
override val node1: Point3D get() = Point3D(-dx, -dy, -dz)
override val node2: Point3D get() = Point3D(dx, -dy, -dz)
override val node3: Point3D get() = Point3D(dx, dy, -dz)
override val node4: Point3D get() = Point3D(-dx, dy, -dz)
override val node5: Point3D get() = Point3D(-dx, -dy, dz)
override val node6: Point3D get() = Point3D(dx, -dy, dz)
override val node7: Point3D get() = Point3D(dx, dy, dz)
override val node8: Point3D get() = Point3D(-dx, dy, dz)
override val node1: Float32Vector3D get() = Float32Vector3D(-dx, -dy, -dz)
override val node2: Float32Vector3D get() = Float32Vector3D(dx, -dy, -dz)
override val node3: Float32Vector3D get() = Float32Vector3D(dx, dy, -dz)
override val node4: Float32Vector3D get() = Float32Vector3D(-dx, dy, -dz)
override val node5: Float32Vector3D get() = Float32Vector3D(-dx, -dy, dz)
override val node6: Float32Vector3D get() = Float32Vector3D(dx, -dy, dz)
override val node7: Float32Vector3D get() = Float32Vector3D(dx, dy, dz)
override val node8: Float32Vector3D get() = Float32Vector3D(-dx, dy, dz)
}
@VisionBuilder
@ -63,26 +63,26 @@ public inline fun MutableVisionContainer<Solid>.box(
@Serializable
@SerialName("solid.hexagon")
public class GenericHexagon(
override val node1: Point3D,
override val node2: Point3D,
override val node3: Point3D,
override val node4: Point3D,
override val node5: Point3D,
override val node6: Point3D,
override val node7: Point3D,
override val node8: Point3D,
override val node1: Float32Vector3D,
override val node2: Float32Vector3D,
override val node3: Float32Vector3D,
override val node4: Float32Vector3D,
override val node5: Float32Vector3D,
override val node6: Float32Vector3D,
override val node7: Float32Vector3D,
override val node8: Float32Vector3D,
) : SolidBase<GenericHexagon>(), Hexagon
@VisionBuilder
public inline fun MutableVisionContainer<Solid>.hexagon(
node1: Point3D,
node2: Point3D,
node3: Point3D,
node4: Point3D,
node5: Point3D,
node6: Point3D,
node7: Point3D,
node8: Point3D,
node1: Float32Vector3D,
node2: Float32Vector3D,
node3: Float32Vector3D,
node4: Float32Vector3D,
node5: Float32Vector3D,
node6: Float32Vector3D,
node7: Float32Vector3D,
node8: Float32Vector3D,
name: String? = null,
action: Hexagon.() -> Unit = {},
): Hexagon = GenericHexagon(node1, node2, node3, node4, node5, node6, node7, node8).apply(action).also { setChild(name, it) }

View File

@ -70,6 +70,6 @@ public fun MutableVisionContainer<Solid>.pointLight(
name: String? = null,
block: PointLightSource.() -> Unit = {},
): PointLightSource = PointLightSource().apply(block).also {
it.position = Point3D(x, y, z)
it.position = Float32Vector3D(x, y, z)
setChild(name, it)
}

View File

@ -3,11 +3,14 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.number
import space.kscience.visionforge.*
import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.root
import space.kscience.visionforge.setChild
@Serializable
@SerialName("solid.line")
public class PolyLine(public val points: List<Point3D>) : SolidBase<PolyLine>() {
public class PolyLine(public val points: List<Float32Vector3D>) : SolidBase<PolyLine>() {
//var lineType by string()
public var thickness: Number by properties.root(inherit = false, includeStyles = true).number { DEFAULT_THICKNESS }
@ -19,7 +22,7 @@ public class PolyLine(public val points: List<Point3D>) : SolidBase<PolyLine>()
@VisionBuilder
public fun MutableVisionContainer<Solid>.polyline(
vararg points: Point3D,
vararg points: Float32Vector3D,
name: String? = null,
action: PolyLine.() -> Unit = {},
): PolyLine = PolyLine(points.toList()).apply(action).also { setChild(name, it) }

View File

@ -174,18 +174,21 @@ internal fun point(
defaultX: Float,
defaultY: Float = defaultX,
defaultZ: Float = defaultX,
): ReadWriteProperty<Solid, Point3D?> =
object : ReadWriteProperty<Solid, Point3D?> {
override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? {
): ReadWriteProperty<Solid, Float32Vector3D?> =
object : ReadWriteProperty<Solid, Float32Vector3D?> {
override fun getValue(thisRef: Solid, property: KProperty<*>): Float32Vector3D? {
val item = thisRef.properties.own?.get(name) ?: return null
return object : Point3D {
//using dynamic property accessor because values could change
return object : Float32Vector3D {
override val x: Float get() = item[X_KEY]?.float ?: defaultX
override val y: Float get() = item[Y_KEY]?.float ?: defaultY
override val z: Float get() = item[Z_KEY]?.float ?: defaultZ
override fun toString(): String = item.toString()
}
}
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) {
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Float32Vector3D?) {
if (value == null) {
thisRef.properties.setProperty(name, null)
} else {
@ -196,9 +199,9 @@ internal fun point(
}
}
public var Solid.position: Point3D? by point(POSITION_KEY, 0f)
public var Solid.rotation: Point3D? by point(ROTATION_KEY, 0f)
public var Solid.scale: Point3D? by point(SCALE_KEY, 1f)
public var Solid.position: Float32Vector3D? by point(POSITION_KEY, 0f)
public var Solid.rotation: Float32Vector3D? by point(ROTATION_KEY, 0f)
public var Solid.scale: Float32Vector3D? by point(SCALE_KEY, 1f)
public var Solid.x: Number by float(X_POSITION_KEY, 0f)
public var Solid.y: Number by float(Y_POSITION_KEY, 0f)
@ -208,10 +211,10 @@ public var Solid.rotationX: Number by float(X_ROTATION_KEY, 0f)
public var Solid.rotationY: Number by float(Y_ROTATION_KEY, 0f)
public var Solid.rotationZ: Number by float(Z_ROTATION_KEY, 0f)
public var Solid.quaternion: Pair<Float, Point3D>?
public var Solid.quaternion: Pair<Float, Float32Vector3D>?
get() = properties.getValue(Solid.QUATERNION_KEY)?.list?.let {
require(it.size == 4) { "Quaternion must be a number array of 4 elements" }
it[0].float to Point3D(it[1].float, it[2].float, it[3].float)
it[0].float to Float32Vector3D(it[1].float, it[2].float, it[3].float)
}
set(value) {
properties.setValue(

View File

@ -51,6 +51,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Sol
}
public val serializersModuleForSolids: SerializersModule = SerializersModule {
polymorphic(Vision::class) {
subclass(SimpleVisionGroup.serializer())
solids()

View File

@ -20,12 +20,12 @@ public class Sphere(
) : SolidBase<Sphere>(), GeometrySolid {
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Point3D {
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Float32Vector3D {
// This transformation matches three.js sphere implementation
val y = r * cos(theta)
val z = r * sin(theta) * sin(phi)
val x = -r * sin(theta) * cos(phi)
return Point3D(x, y, z)
return Float32Vector3D(x, y, z)
}
val segments = this.detail ?: 32

View File

@ -27,12 +27,12 @@ public class SphereLayer(
require(outerRadius > 0) { "Outer radius must be positive" }
require(innerRadius >= 0) { "inner radius must be non-negative" }
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Point3D {
fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Float32Vector3D {
// This transformation matches three.js sphere implementation
val y = r * cos(theta)
val z = r * sin(theta) * sin(phi)
val x = -r * sin(theta) * cos(phi)
return Point3D(x, y, z)
return Float32Vector3D(x, y, z)
}
val segments = detail ?: 32

View File

@ -1,10 +1,5 @@
package space.kscience.visionforge.solid
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaProvider
import space.kscience.dataforge.meta.float
@ -13,105 +8,48 @@ import space.kscience.visionforge.solid.Solid.Companion.X_KEY
import space.kscience.visionforge.solid.Solid.Companion.Y_KEY
import space.kscience.visionforge.solid.Solid.Companion.Z_KEY
import kotlin.math.PI
import kotlin.math.pow
import kotlin.math.sqrt
public const val PI2: Float = 2 * PI.toFloat()
@Serializable
public data class Point2D(public var x: Float, public var y: Float)
public fun Point2D(x: Number, y: Number): Point2D = Point2D(x.toFloat(), y.toFloat())
public fun Point2D.toMeta(): Meta = Meta {
public fun Float32Vector2D.toMeta(): Meta = Meta {
X_KEY put x
Y_KEY put y
}
internal fun Meta.point2D(): Point2D = Point2D(this["x"].float ?: 0f, this["y"].float ?: 0f)
internal fun Meta.toVector2D(): Float32Vector2D =
Float32Vector2D(this["x"].float ?: 0f, this["y"].float ?: 0f)
@Serializable(Point3DSerializer::class)
public interface Point3D {
public val x: Float
public val y: Float
public val z: Float
//@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
//@Serializable(Point3DSerializer::class)
//public interface MutablePoint3D : Float32Vector3D {
// override var x: Float
// override var y: Float
// override var z: Float
//}
//
//
//public fun MutablePoint3D.normalizeInPlace() {
// val norm = sqrt(x.pow(2) + y.pow(2) + z.pow(2))
// x /= norm
// y /= norm
// z /= norm
//}
public companion object {
public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0)
public val ONE: Point3D = Point3D(1.0, 1.0, 1.0)
}
}
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(Point3DSerializer::class)
public interface MutablePoint3D : Point3D {
override var x: Float
override var y: Float
override var z: Float
}
@Serializable
private class Point3DImpl(override var x: Float, override var y: Float, override var z: Float) : MutablePoint3D
internal object Point3DSerializer : KSerializer<Point3D> {
override val descriptor: SerialDescriptor = Point3DImpl.serializer().descriptor
override fun deserialize(decoder: Decoder): MutablePoint3D = decoder.decodeSerializableValue(Point3DImpl.serializer())
override fun serialize(encoder: Encoder, value: Point3D) {
val impl: Point3DImpl = (value as? Point3DImpl) ?: Point3DImpl(value.x, value.y, value.z)
encoder.encodeSerializableValue(Point3DImpl.serializer(), impl)
}
}
public fun Point3D(x: Number, y: Number, z: Number): Point3D = Point3DImpl(x.toFloat(), y.toFloat(), z.toFloat())
public operator fun Point3D.plus(other: Point3D): Point3D = Point3D(
this.x + other.x,
this.y + other.y,
this.z + other.z
internal fun MetaProvider.point3D(default: Float = 0f) = Float32Euclidean3DSpace.vector(
getMeta(X_KEY).float ?: default,
getMeta(Y_KEY).float ?: default,
getMeta(Z_KEY).float ?: default
)
public operator fun Point3D.minus(other: Point3D): Point3D = Point3D(
this.x - other.x,
this.y - other.y,
this.z - other.z
)
public operator fun Point3D.unaryMinus(): Point3D = Point3D(
-x,
-y,
-z
)
public infix fun Point3D.cross(other: Point3D): Point3D = Point3D(
y * other.z - z * other.y,
z * other.x - x * other.z,
x * other.y - y * other.x
)
public fun MutablePoint3D.normalizeInPlace() {
val norm = sqrt(x.pow(2) + y.pow(2) + z.pow(2))
x /= norm
y /= norm
z /= norm
}
internal fun MetaProvider.point3D(default: Float = 0f) = object : Point3D {
override val x: Float by float(default)
override val y: Float by float(default)
override val z: Float by float(default)
}
public fun Point3D.toMeta(): Meta = Meta {
public fun Float32Vector3D.toMeta(): Meta = Meta {
X_KEY put x
Y_KEY put y
Z_KEY put z
}
internal fun Meta.toVector(default: Float = 0f) = Point3D(
internal fun Meta.toVector3D(default: Float = 0f) = Float32Vector3D(
this[X_KEY].float ?: default,
this[Y_KEY].float ?: default,
this[Z_KEY].float ?: default

View File

@ -2,13 +2,10 @@ package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.getIndexed
import space.kscience.dataforge.meta.toMeta
import space.kscience.dataforge.misc.DFExperimental
import kotlin.test.Test
import kotlin.test.assertEquals
class ConvexTest {
@OptIn(DFExperimental::class)
@Suppress("UNUSED_VARIABLE")
@Test
fun testConvexBuilder() {
val group = testSolids.solidGroup {

View File

@ -1,15 +1,14 @@
package space.kscience.visionforge.solid.three
import space.kscience.dataforge.meta.Meta
import space.kscience.visionforge.solid.Float32Euclidean3DSpace
import space.kscience.visionforge.solid.Float32Vector3D
import space.kscience.visionforge.solid.GeometryBuilder
import three.core.BufferGeometry
import three.core.Float32BufferAttribute
import three.math.Vector3
import space.kscience.dataforge.meta.Meta
import space.kscience.visionforge.solid.GeometryBuilder
import space.kscience.visionforge.solid.Point3D
import space.kscience.visionforge.solid.cross
import space.kscience.visionforge.solid.minus
internal fun Point3D.toVector() = Vector3(x, y, z)
internal fun Float32Vector3D.toVector() = Vector3(x, y, z)
internal fun <T> MutableList<T>.add(vararg values: T) {
values.forEach {
@ -27,10 +26,10 @@ public class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
private val normals = ArrayList<Float>()
// private val colors = ArrayList<Float>()
private val vertexCache = HashMap<Point3D, Short>()
private val vertexCache = HashMap<Float32Vector3D, Short>()
private var counter: Short = -1
private fun vertex(vertex: Point3D, normal: Point3D): Short = vertexCache.getOrPut(vertex) {
private fun vertex(vertex: Float32Vector3D, normal: Float32Vector3D): Short = vertexCache.getOrPut(vertex) {
//add vertex and update cache if needed
positions.add(vertex.x, vertex.y, vertex.z)
normals.add(normal.x, vertex.y, vertex.z)
@ -39,8 +38,14 @@ public class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
counter
}
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) {
val actualNormal: Point3D = normal ?: ((vertex3 - vertex2) cross (vertex1 - vertex2))
override fun face(
vertex1: Float32Vector3D,
vertex2: Float32Vector3D,
vertex3: Float32Vector3D,
normal: Float32Vector3D?,
meta: Meta,
) = with(Float32Euclidean3DSpace) {
val actualNormal: Float32Vector3D = normal ?: ((vertex3 - vertex2) cross (vertex1 - vertex2))
indices.add(
vertex(vertex1, actualNormal),
vertex(vertex2, actualNormal),

View File

@ -8,6 +8,7 @@ import kotlin.test.Test
class TestServerExtensions {
@Suppress("UNUSED_VARIABLE")
@Test
fun testServerHeader(){
val string = createHTML().apply {