Refactor geometry

This commit is contained in:
Alexander Nozik 2023-08-12 13:16:18 +03:00
parent 19bebfd1ed
commit efb853c1bc
28 changed files with 454 additions and 154 deletions

View File

@ -3,11 +3,14 @@
## Unreleased ## Unreleased
### Added ### Added
- Integer divistion algebras - Integer division algebras
- Float32 geometries
### Changed ### Changed
- Default naming for algebra and buffers now uses IntXX/FloatXX notation instead of Java types. - Default naming for algebra and buffers now uses IntXX/FloatXX notation instead of Java types.
- Remove unnecessary inlines in basic algebras. - Remove unnecessary inlines in basic algebras.
- QuaternionField -> QuaternionAlgebra and does not implement `Field` anymore since it is non-commutative
- kmath-geometry is split into `euclidean2d` and `euclidean3d`
### Deprecated ### Deprecated

View File

@ -122,16 +122,17 @@ public val Quaternion.reciprocal: Quaternion
/** /**
* Produce a normalized version of this quaternion * Produce a normalized version of this quaternion
*/ */
public fun Quaternion.normalized(): Quaternion = with(QuaternionField){ this@normalized / norm(this@normalized) } public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra){ this@normalized / norm(this@normalized) }
/** /**
* A field of [Quaternion]. * A field of [Quaternion].
*/ */
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Double>, PowerOperations<Quaternion>, public object QuaternionAlgebra : Group<Quaternion>, Norm<Quaternion, Double>, PowerOperations<Quaternion>,
ExponentialOperations<Quaternion>, NumbersAddOps<Quaternion>, ScaleOperations<Quaternion> { ExponentialOperations<Quaternion>, NumbersAddOps<Quaternion>, ScaleOperations<Quaternion> {
override val zero: Quaternion = Quaternion(0.0) override val zero: Quaternion = Quaternion(0.0)
override val one: Quaternion = Quaternion(1.0) public val one: Quaternion = Quaternion(1.0)
/** /**
* The `i` quaternion unit. * The `i` quaternion unit.
@ -154,14 +155,16 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Double>, Pow
override fun scale(a: Quaternion, value: Double): Quaternion = override fun scale(a: Quaternion, value: Double): Quaternion =
Quaternion(a.w * value, a.x * value, a.y * value, a.z * value) Quaternion(a.w * value, a.x * value, a.y * value, a.z * value)
override fun multiply(left: Quaternion, right: Quaternion): Quaternion = Quaternion( public fun multiply(left: Quaternion, right: Quaternion): Quaternion = Quaternion(
left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z, left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z,
left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y, left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y,
left.w * right.y - left.x * right.z + left.y * right.w + left.z * right.x, left.w * right.y - left.x * right.z + left.y * right.w + left.z * right.x,
left.w * right.z + left.x * right.y - left.y * right.x + left.z * right.w, left.w * right.z + left.x * right.y - left.y * right.x + left.z * right.w,
) )
override fun divide(left: Quaternion, right: Quaternion): Quaternion { public operator fun Quaternion.times(other: Quaternion): Quaternion = multiply(this, other)
public fun divide(left: Quaternion, right: Quaternion): Quaternion {
val s = right.w * right.w + right.x * right.x + right.y * right.y + right.z * right.z val s = right.w * right.w + right.x * right.x + right.y * right.y + right.z * right.z
return Quaternion( return Quaternion(
@ -172,14 +175,17 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Double>, Pow
) )
} }
public operator fun Quaternion.div(other: Quaternion): Quaternion = divide(this, other)
override fun power(arg: Quaternion, pow: Number): Quaternion { override fun power(arg: Quaternion, pow: Number): Quaternion {
if (pow is Int) return pwr(arg, pow) if (pow is Int) return power(arg, pow)
if (floor(pow.toDouble()) == pow.toDouble()) return pwr(arg, pow.toInt()) if (floor(pow.toDouble()) == pow.toDouble()) return power(arg, pow.toInt())
return exp(pow * ln(arg)) return exp(pow * ln(arg))
} }
private fun pwr(x: Quaternion, a: Int): Quaternion = when { private fun power(x: Quaternion, a: Int): Quaternion = when {
a < 0 -> -(pwr(x, -a)) a < 0 -> -(power(x, -a))
a == 0 -> one a == 0 -> one
a == 1 -> x a == 1 -> x
a == 2 -> pwr2(x) a == 2 -> pwr2(x)
@ -243,14 +249,15 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Double>, Pow
return Quaternion(ln(n), th * arg.x, th * arg.y, th * arg.z) return Quaternion(ln(n), th * arg.x, th * arg.y, th * arg.z)
} }
override operator fun Number.plus(other: Quaternion): Quaternion = public override operator fun Number.plus(other: Quaternion): Quaternion =
Quaternion(toDouble() + other.w, other.x, other.y, other.z) Quaternion(toDouble() + other.w, other.x, other.y, other.z)
override operator fun Number.minus(other: Quaternion): Quaternion = public override operator fun Number.minus(other: Quaternion): Quaternion =
Quaternion(toDouble() - other.w, -other.x, -other.y, -other.z) Quaternion(toDouble() - other.w, -other.x, -other.y, -other.z)
override operator fun Quaternion.plus(other: Number): Quaternion = Quaternion(w + other.toDouble(), x, y, z) public override operator fun Quaternion.plus(other: Number): Quaternion = Quaternion(w + other.toDouble(), x, y, z)
override operator fun Quaternion.minus(other: Number): Quaternion = Quaternion(w - other.toDouble(), x, y, z)
public override operator fun Quaternion.minus(other: Number): Quaternion = Quaternion(w - other.toDouble(), x, y, z)
override operator fun Number.times(arg: Quaternion): Quaternion = override operator fun Number.times(arg: Quaternion): Quaternion =
Quaternion(toDouble() * arg.w, toDouble() * arg.x, toDouble() * arg.y, toDouble() * arg.z) Quaternion(toDouble() * arg.w, toDouble() * arg.x, toDouble() * arg.y, toDouble() * arg.z)
@ -275,7 +282,7 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Double>, Pow
override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0 override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0
override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0 override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0
override fun tanh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) override fun tanh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg))
override fun asinh(arg: Quaternion): Quaternion = ln(sqrt(arg * arg + one) + arg) override fun asinh(arg: Quaternion): Quaternion = ln(sqrt(pwr2(arg) + one) + arg)
override fun acosh(arg: Quaternion): Quaternion = ln(arg + sqrt((arg - one) * (arg + one))) override fun acosh(arg: Quaternion): Quaternion = ln(arg + sqrt((arg - one) * (arg + one)))
override fun atanh(arg: Quaternion): Quaternion = (ln(arg + one) - ln(one - arg)) / 2.0 override fun atanh(arg: Quaternion): Quaternion = (ln(arg + one) - ln(one - arg)) / 2.0
} }

View File

@ -14,20 +14,20 @@ internal class QuaternionTest {
@Test @Test
fun testNorm() { fun testNorm() {
assertEquals(2.0, QuaternionField.norm(Quaternion(1.0, 1.0, 1.0, 1.0))) assertEquals(2.0, QuaternionAlgebra.norm(Quaternion(1.0, 1.0, 1.0, 1.0)))
} }
@Test @Test
fun testInverse() = QuaternionField { fun testInverse() = QuaternionAlgebra {
val q = Quaternion(1.0, 2.0, -3.0, 4.0) val q = Quaternion(1.0, 2.0, -3.0, 4.0)
assertBufferEquals(one, q * q.reciprocal, 1e-4) assertBufferEquals(one, q * q.reciprocal, 1e-4)
} }
@Test @Test
fun testAddition() { fun testAddition() {
assertEquals(Quaternion(42, 42), QuaternionField { Quaternion(16, 16) + Quaternion(26, 26) }) assertEquals(Quaternion(42, 42), QuaternionAlgebra { Quaternion(16, 16) + Quaternion(26, 26) })
assertEquals(Quaternion(42, 16), QuaternionField { Quaternion(16, 16) + 26 }) assertEquals(Quaternion(42, 16), QuaternionAlgebra { Quaternion(16, 16) + 26 })
assertEquals(Quaternion(42, 16), QuaternionField { 26 + Quaternion(16, 16) }) assertEquals(Quaternion(42, 16), QuaternionAlgebra { 26 + Quaternion(16, 16) })
} }
// @Test // @Test
@ -39,9 +39,9 @@ internal class QuaternionTest {
@Test @Test
fun testMultiplication() { fun testMultiplication() {
assertEquals(Quaternion(42, 42), QuaternionField { Quaternion(4.2, 0) * Quaternion(10, 10) }) assertEquals(Quaternion(42, 42), QuaternionAlgebra { Quaternion(4.2, 0) * Quaternion(10, 10) })
assertEquals(Quaternion(42, 21), QuaternionField { Quaternion(4.2, 2.1) * 10 }) assertEquals(Quaternion(42, 21), QuaternionAlgebra { Quaternion(4.2, 2.1) * 10 })
assertEquals(Quaternion(42, 21), QuaternionField { 10 * Quaternion(4.2, 2.1) }) assertEquals(Quaternion(42, 21), QuaternionAlgebra { 10 * Quaternion(4.2, 2.1) })
} }
// @Test // @Test
@ -53,11 +53,11 @@ internal class QuaternionTest {
@Test @Test
fun testPower() { fun testPower() {
assertEquals(QuaternionField.zero, QuaternionField { zero pow 2 }) assertEquals(QuaternionAlgebra.zero, QuaternionAlgebra { zero pow 2 })
assertEquals(QuaternionField.zero, QuaternionField { zero pow 2 }) assertEquals(QuaternionAlgebra.zero, QuaternionAlgebra { zero pow 2 })
assertEquals( assertEquals(
QuaternionField { i * 8 }.let { it.x.toInt() to it.w.toInt() }, QuaternionAlgebra { i * 8 }.let { it.x.toInt() to it.w.toInt() },
QuaternionField { Quaternion(2, 2) pow 2 }.let { it.x.toInt() to it.w.toInt() }) QuaternionAlgebra { Quaternion(2, 2) pow 2 }.let { it.x.toInt() to it.w.toInt() })
} }
} }

View File

@ -300,7 +300,7 @@ public interface Ring<T> : Group<T>, RingOps<T> {
* commutative operations [add] and [multiply]; binary operation [divide] as multiplication of left operand by * commutative operations [add] and [multiply]; binary operation [divide] as multiplication of left operand by
* reciprocal of right one. * reciprocal of right one.
* *
* @param T the type of element of this semifield. * @param T the type of the semifield element.
*/ */
public interface FieldOps<T> : RingOps<T> { public interface FieldOps<T> : RingOps<T> {
/** /**
@ -336,10 +336,12 @@ public interface FieldOps<T> : RingOps<T> {
/** /**
* Represents field i.e., algebraic structure with three operations: associative, commutative addition and * Represents field i.e., algebraic structure with three operations: associative, commutative addition and
* multiplication, and division. **This interface differs from the eponymous mathematical definition: fields in KMath * multiplication, and division.
*
* **This interface differs from the eponymous mathematical definition: fields in KMath
* also support associative multiplication by scalar.** * also support associative multiplication by scalar.**
* *
* @param T the type of element of this field. * @param T the type of the field element.
*/ */
public interface Field<T> : Ring<T>, FieldOps<T>, ScaleOperations<T>, NumericAlgebra<T> { public interface Field<T> : Ring<T>, FieldOps<T>, ScaleOperations<T>, NumericAlgebra<T> {
override fun number(value: Number): T = scale(one, value.toDouble()) override fun number(value: Number): T = scale(one, value.toDouble())

View File

@ -150,7 +150,7 @@ public interface ScaleOperations<T> : Algebra<T> {
* TODO to be removed and replaced by extensions after multiple receivers are there * TODO to be removed and replaced by extensions after multiple receivers are there
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public interface NumbersAddOps<T> : RingOps<T>, NumericAlgebra<T> { public interface NumbersAddOps<T> : GroupOps<T>, NumericAlgebra<T> {
/** /**
* Addition of element and scalar. * Addition of element and scalar.
* *
@ -177,7 +177,7 @@ public interface NumbersAddOps<T> : RingOps<T>, NumericAlgebra<T> {
public operator fun T.minus(other: Number): T = this - number(other) public operator fun T.minus(other: Number): T = this - number(other)
/** /**
* Subtraction of number from element. * Subtraction of number from the element.
* *
* @receiver the minuend. * @receiver the minuend.
* @param other the subtrahend. * @param other the subtrahend.

View File

@ -82,9 +82,9 @@ public expect fun Number.isInteger(): Boolean
/** /**
* A context extension to include power operations based on exponentiation. * A context extension to include power operations based on exponentiation.
* *
* @param T the type of element of this structure. * @param T the type of this structure element
*/ */
public interface PowerOperations<T> : FieldOps<T> { public interface PowerOperations<T>: Algebra<T> {
/** /**
* Raises [arg] to a power if possible (negative number could not be raised to a fractional power). * Raises [arg] to a power if possible (negative number could not be raised to a fractional power).

View File

@ -16,7 +16,6 @@ import kotlin.math.pow
public typealias DoubleVector = Point<Double> public typealias DoubleVector = Point<Double>
@Suppress("FunctionName")
public fun DoubleVector(vararg doubles: Double): DoubleVector = doubles.asBuffer() public fun DoubleVector(vararg doubles: Double): DoubleVector = doubles.asBuffer()
/** /**

View File

@ -7,6 +7,8 @@ package space.kscience.kmath.geometry
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.kmath.geometry.euclidean2d.DoubleVector2D
import space.kscience.kmath.geometry.euclidean3d.DoubleVector3D
/** /**
* A line formed by [start] vector of start and a [direction] vector. Direction vector is not necessarily normalized, * A line formed by [start] vector of start and a [direction] vector. Direction vector is not necessarily normalized,

View File

@ -0,0 +1,29 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.geometry
import space.kscience.kmath.linear.Point
public interface Vector2D<T> : Point<T>, Vector {
public val x: T
public val y: T
override val size: Int get() = 2
override operator fun get(index: Int): T = when (index) {
0 -> x
1 -> y
else -> error("Accessing outside of point bounds")
}
override operator fun iterator(): Iterator<T> = iterator {
yield(x)
yield(y)
}
}
public operator fun <T> Vector2D<T>.component1(): T = x
public operator fun <T> Vector2D<T>.component2(): T = y

View File

@ -0,0 +1,41 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.geometry
import space.kscience.kmath.linear.Point
import space.kscience.kmath.structures.Buffer
public interface Vector3D<T> : Point<T>, Vector {
public val x: T
public val y: T
public val z: T
override val size: Int get() = 3
override operator fun get(index: Int): T = when (index) {
0 -> x
1 -> y
2 -> z
else -> error("Accessing outside of point bounds")
}
override operator fun iterator(): Iterator<T> = listOf(x, y, z).iterator()
}
public operator fun <T> Vector3D<T>.component1(): T = x
public operator fun <T> Vector3D<T>.component2(): T = y
public operator fun <T> Vector3D<T>.component3(): T = z
public fun <T> Buffer<T>.asVector3D(): Vector3D<T> = object : Vector3D<T> {
init {
require(this@asVector3D.size == 3) { "Buffer of size 3 is required for Vector3D" }
}
override val x: T get() = this@asVector3D[0]
override val y: T get() = this@asVector3D[1]
override val z: T get() = this@asVector3D[2]
override fun toString(): String = this@asVector3D.toString()
}

View File

@ -1,20 +1,19 @@
/* /*
* Copyright 2018-2022 KMath contributors. * Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.geometry package space.kscience.kmath.geometry.euclidean2d
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo import kotlin.math.PI
import kotlin.math.*
/** /**
* A circle in 2D space * A circle in 2D space
*/ */
@Serializable @Serializable
public data class Circle2D( public data class Circle2D(
@Serializable(Euclidean2DSpace.VectorSerializer::class) public val center: DoubleVector2D, @Serializable(Float64Space2D.VectorSerializer::class) public val center: DoubleVector2D,
public val radius: Double public val radius: Double
) )

View File

@ -0,0 +1,79 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.geometry.euclidean2d
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.Float32Field
import space.kscience.kmath.operations.ScaleOperations
import kotlin.math.pow
import kotlin.math.sqrt
@Serializable(Float32Space2D.VectorSerializer::class)
public interface Float32Vector2D: Vector2D<Float>
public object Float32Space2D :
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 = Float32Space2D.vector(x, y)
public val Float32Field.euclidean2D: Float32Space2D get() = Float32Space2D

View File

@ -1,9 +1,9 @@
/* /*
* Copyright 2018-2022 KMath contributors. * Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.geometry package space.kscience.kmath.geometry.euclidean2d
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
@ -11,43 +11,26 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import space.kscience.kmath.linear.Point import space.kscience.kmath.geometry.GeometrySpace
import space.kscience.kmath.geometry.Vector2D
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.Norm import space.kscience.kmath.operations.Norm
import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.ScaleOperations
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
public interface Vector2D<T> : Point<T>, Vector {
public val x: T
public val y: T
override val size: Int get() = 2
override operator fun get(index: Int): T = when (index) {
0 -> x
1 -> y
else -> error("Accessing outside of point bounds")
}
override operator fun iterator(): Iterator<T> = iterator {
yield(x)
yield(y)
}
}
public operator fun <T> Vector2D<T>.component1(): T = x
public operator fun <T> Vector2D<T>.component2(): T = y
public typealias DoubleVector2D = Vector2D<Double> public typealias DoubleVector2D = Vector2D<Double>
public typealias Float64Vector2D = Vector2D<Double> public typealias Float64Vector2D = Vector2D<Double>
public val Vector2D<Double>.r: Double get() = Euclidean2DSpace.norm(this) public val Vector2D<Double>.r: Double get() = Float64Space2D.norm(this)
/** /**
* 2D Euclidean space * 2D Euclidean space
*/ */
public object Euclidean2DSpace : GeometrySpace<DoubleVector2D>, public object Float64Space2D : GeometrySpace<DoubleVector2D>,
ScaleOperations<DoubleVector2D>, ScaleOperations<DoubleVector2D>,
Norm<DoubleVector2D, Double> { Norm<DoubleVector2D, Double> {
@ -88,3 +71,5 @@ public object Euclidean2DSpace : GeometrySpace<DoubleVector2D>,
public val xAxis: DoubleVector2D = vector(1.0, 0.0) public val xAxis: DoubleVector2D = vector(1.0, 0.0)
public val yAxis: DoubleVector2D = vector(0.0, 1.0) public val yAxis: DoubleVector2D = vector(0.0, 1.0)
} }
public val Float64Field.euclidean2D: Float64Space2D get() = Float64Space2D

View File

@ -3,7 +3,9 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.geometry package space.kscience.kmath.geometry.euclidean2d
import space.kscience.kmath.geometry.Vector2D
/** /**

View File

@ -0,0 +1,108 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.geometry.euclidean3d
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.Float32Field
import space.kscience.kmath.operations.ScaleOperations
import kotlin.math.pow
import kotlin.math.sqrt
@Serializable(Float32Space3D.VectorSerializer::class)
public interface Float32Vector3D: Vector3D<Float>
public object Float32Space3D :
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()
/**
* 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 = Float32Space3D.vector(x, y, z)
public val Float32Field.euclidean3D: Float32Space3D get() = Float32Space3D

View File

@ -1,9 +1,9 @@
/* /*
* Copyright 2018-2022 KMath contributors. * Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.geometry package space.kscience.kmath.geometry.euclidean3d
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
@ -11,51 +11,33 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import space.kscience.kmath.linear.Point import space.kscience.kmath.geometry.GeometrySpace
import space.kscience.kmath.geometry.Vector3D
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.Norm import space.kscience.kmath.operations.Norm
import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.structures.Buffer
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
public interface Vector3D<T> : Point<T>, Vector { internal fun leviCivita(i: Int, j: Int, k: Int): Int = when {
public val x: T // even permutation
public val y: T i == 0 && j == 1 && k == 2 -> 1
public val z: T i == 1 && j == 2 && k == 0 -> 1
override val size: Int get() = 3 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
override operator fun get(index: Int): T = when (index) { else -> 0
0 -> x
1 -> y
2 -> z
else -> error("Accessing outside of point bounds")
}
override operator fun iterator(): Iterator<T> = listOf(x, y, z).iterator()
}
public operator fun <T> Vector3D<T>.component1(): T = x
public operator fun <T> Vector3D<T>.component2(): T = y
public operator fun <T> Vector3D<T>.component3(): T = z
public fun <T> Buffer<T>.asVector3D(): Vector3D<T> = object : Vector3D<T> {
init {
require(this@asVector3D.size == 3) { "Buffer of size 3 is required for Vector3D" }
}
override val x: T get() = this@asVector3D[0]
override val y: T get() = this@asVector3D[1]
override val z: T get() = this@asVector3D[2]
override fun toString(): String = this@asVector3D.toString()
} }
public typealias DoubleVector3D = Vector3D<Double> public typealias DoubleVector3D = Vector3D<Double>
public typealias Float64Vector3D = Vector3D<Double> public typealias Float64Vector3D = Vector3D<Double>
public val DoubleVector3D.r: Double get() = Euclidean3DSpace.norm(this) public val DoubleVector3D.r: Double get() = Float64Space3D.norm(this)
public object Euclidean3DSpace : GeometrySpace<DoubleVector3D>, ScaleOperations<DoubleVector3D>, public object Float64Space3D : GeometrySpace<DoubleVector3D>, ScaleOperations<DoubleVector3D>,
Norm<DoubleVector3D, Double> { Norm<DoubleVector3D, Double> {
@Serializable @Serializable
@ -103,21 +85,8 @@ public object Euclidean3DSpace : GeometrySpace<DoubleVector3D>, ScaleOperations<
override fun DoubleVector3D.dot(other: DoubleVector3D): Double = override fun DoubleVector3D.dot(other: DoubleVector3D): Double =
x * other.x + y * other.y + z * other.z x * other.x + y * other.y + z * other.z
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 assumed to be right-handed. * Compute vector product of [first] and [second]. The basis is assumed to be right-handed.
*/ */
public fun vectorProduct( public fun vectorProduct(
first: DoubleVector3D, first: DoubleVector3D,
@ -139,7 +108,7 @@ public object Euclidean3DSpace : GeometrySpace<DoubleVector3D>, ScaleOperations<
} }
/** /**
* Vector product with right basis * Vector product with the right basis
*/ */
public infix fun DoubleVector3D.cross(other: DoubleVector3D): Vector3D<Double> = vectorProduct(this, other) public infix fun DoubleVector3D.cross(other: DoubleVector3D): Vector3D<Double> = vectorProduct(this, other)
@ -147,3 +116,5 @@ public object Euclidean3DSpace : GeometrySpace<DoubleVector3D>, ScaleOperations<
public val yAxis: DoubleVector3D = vector(0.0, 1.0, 0.0) public val yAxis: DoubleVector3D = vector(0.0, 1.0, 0.0)
public val zAxis: DoubleVector3D = vector(0.0, 0.0, 1.0) public val zAxis: DoubleVector3D = vector(0.0, 0.0, 1.0)
} }
public val Float64Field.euclidean3D: Float64Space3D get() = Float64Space3D

View File

@ -1,15 +1,16 @@
/* /*
* Copyright 2018-2022 KMath contributors. * Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.geometry package space.kscience.kmath.geometry.euclidean3d
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.complex.Quaternion import space.kscience.kmath.complex.Quaternion
import space.kscience.kmath.complex.QuaternionField import space.kscience.kmath.complex.QuaternionAlgebra
import space.kscience.kmath.complex.normalized import space.kscience.kmath.complex.normalized
import space.kscience.kmath.complex.reciprocal import space.kscience.kmath.complex.reciprocal
import space.kscience.kmath.geometry.*
import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.Matrix import space.kscience.kmath.linear.Matrix
import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.linear.linearSpace
@ -31,7 +32,7 @@ public val Quaternion.theta: Radians get() = (kotlin.math.acos(normalized().w) *
public fun Quaternion.Companion.fromRotation(theta: Angle, vector: DoubleVector3D): Quaternion { public fun Quaternion.Companion.fromRotation(theta: Angle, vector: DoubleVector3D): Quaternion {
val s = sin(theta / 2) val s = sin(theta / 2)
val c = cos(theta / 2) val c = cos(theta / 2)
val norm = with(Euclidean3DSpace) { vector.norm() } val norm = with(Float64Space3D) { vector.norm() }
return Quaternion(c, vector.x * s / norm, vector.y * s / norm, vector.z * s / norm) return Quaternion(c, vector.x * s / norm, vector.y * s / norm, vector.z * s / norm)
} }
@ -50,9 +51,9 @@ public val Quaternion.vector: DoubleVector3D
} }
/** /**
* Rotate a vector in a [Euclidean3DSpace] * Rotate a vector in a [Float64Space3D]
*/ */
public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, q: Quaternion): DoubleVector3D = with(QuaternionField) { public fun Float64Space3D.rotate(vector: DoubleVector3D, q: Quaternion): DoubleVector3D = with(QuaternionAlgebra) {
val p = vector.toQuaternion() val p = vector.toQuaternion()
(q * p * q.reciprocal).vector (q * p * q.reciprocal).vector
} }
@ -61,10 +62,10 @@ public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, q: Quaternion): Doubl
* Use a composition of quaternions to create a rotation * Use a composition of quaternions to create a rotation
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, composition: QuaternionField.() -> Quaternion): DoubleVector3D = public fun Float64Space3D.rotate(vector: DoubleVector3D, composition: QuaternionAlgebra.() -> Quaternion): DoubleVector3D =
rotate(vector, QuaternionField.composition()) rotate(vector, QuaternionAlgebra.composition())
public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, matrix: Matrix<Double>): DoubleVector3D { public fun Float64Space3D.rotate(vector: DoubleVector3D, matrix: Matrix<Double>): DoubleVector3D {
require(matrix.colNum == 3 && matrix.rowNum == 3) { "Square 3x3 rotation matrix is required" } require(matrix.colNum == 3 && matrix.rowNum == 3) { "Square 3x3 rotation matrix is required" }
return with(Float64Field.linearSpace) { matrix.dot(vector).asVector3D() } return with(Float64Field.linearSpace) { matrix.dot(vector).asVector3D() }
} }
@ -76,7 +77,7 @@ public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, matrix: Matrix<Double
public fun Quaternion.toRotationMatrix( public fun Quaternion.toRotationMatrix(
linearSpace: LinearSpace<Double, *> = Float64Field.linearSpace, linearSpace: LinearSpace<Double, *> = Float64Field.linearSpace,
): Matrix<Double> { ): Matrix<Double> {
val s = QuaternionField.norm(this).pow(-2) val s = QuaternionAlgebra.norm(this).pow(-2)
return linearSpace.matrix(3, 3)( return linearSpace.matrix(3, 3)(
1.0 - 2 * s * (y * y + z * z), 2 * s * (x * y - z * w), 2 * s * (x * z + y * w), 1.0 - 2 * s * (y * y + z * z), 2 * s * (x * y - z * w), 2 * s * (x * z + y * w),
2 * s * (x * y + z * w), 1.0 - 2 * s * (x * x + z * z), 2 * s * (y * z - x * w), 2 * s * (x * y + z * w), 1.0 - 2 * s * (x * x + z * z), 2 * s * (y * z - x * w),

View File

@ -6,6 +6,10 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.GeometrySpace.Companion.DEFAULT_PRECISION import space.kscience.kmath.geometry.GeometrySpace.Companion.DEFAULT_PRECISION
import space.kscience.kmath.geometry.euclidean2d.Float64Space2D
import space.kscience.kmath.geometry.euclidean2d.Float64Vector2D
import space.kscience.kmath.geometry.euclidean3d.Float64Space3D
import space.kscience.kmath.geometry.euclidean3d.Float64Vector3D
/** /**
* Float equality within given [precision] * Float equality within given [precision]
@ -36,7 +40,7 @@ public fun <V : Vector> V.equalsVector(
public fun Float64Vector2D.equalsVector( public fun Float64Vector2D.equalsVector(
other: Float64Vector2D, other: Float64Vector2D,
precision: Double = DEFAULT_PRECISION, precision: Double = DEFAULT_PRECISION,
): Boolean = equalsVector(Euclidean2DSpace, other, precision) ): Boolean = equalsVector(Float64Space2D, other, precision)
/** /**
* Vector equality using Euclidian L2 norm and given [precision] * Vector equality using Euclidian L2 norm and given [precision]
@ -44,7 +48,7 @@ public fun Float64Vector2D.equalsVector(
public fun Float64Vector3D.equalsVector( public fun Float64Vector3D.equalsVector(
other: Float64Vector3D, other: Float64Vector3D,
precision: Double = DEFAULT_PRECISION, precision: Double = DEFAULT_PRECISION,
): Boolean = equalsVector(Euclidean3DSpace, other, precision) ): Boolean = equalsVector(Float64Space3D, other, precision)
/** /**
* Line equality using [GeometrySpace.norm] provided by the [space] and given [precision] * Line equality using [GeometrySpace.norm] provided by the [space] and given [precision]

View File

@ -0,0 +1,55 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.geometry
import space.kscience.kmath.complex.Quaternion
import space.kscience.kmath.complex.QuaternionAlgebra
import space.kscience.kmath.complex.conjugate
import space.kscience.kmath.complex.normalized
import space.kscience.kmath.geometry.euclidean3d.Float32Space3D
import space.kscience.kmath.geometry.euclidean3d.Float32Vector3D
import space.kscience.kmath.geometry.euclidean3d.theta
import kotlin.math.asin
import kotlin.math.atan2
import kotlin.math.pow
public operator fun Quaternion.times(other: Quaternion): Quaternion = QuaternionAlgebra.multiply(this, other)
public operator fun Quaternion.div(other: Quaternion): Quaternion = QuaternionAlgebra.divide(this, other)
public fun Quaternion.power(number: Number): Quaternion = QuaternionAlgebra.power(this, number)
/**
* Linear interpolation between [from] and [to] in spherical space
*/
public fun QuaternionAlgebra.slerp(from: Quaternion, to: Quaternion, fraction: Double): Quaternion =
(to / from).pow(fraction) * from
public fun QuaternionAlgebra.angleBetween(q1: Quaternion, q2: Quaternion): Angle = (q1.conjugate * q2).theta
public val Quaternion.inclination: Radians get() = asin(2 * (w * y - x * z)).radians
public val Quaternion.azimuth: Angle get() = atan2(2 * (w * z + x * y), 1 - 2 * (y.pow(2) + z.pow(2))).radians.normalized()
public infix fun Quaternion.dot(other: Quaternion): Double = w * other.w + x * other.x + y * other.y + z * other.z
private fun Quaternion.normalizedToEuler(): Float32Vector3D {
val roll = atan2(2 * y * w + 2 * x * z, 1 - 2 * y * y - 2 * z * z);
val pitch = atan2(2 * x * w - 2 * y * z, 1 - 2 * x * x - 2 * z * z);
val yaw = asin(2 * x * y + 2 * z * w);
return Float32Vector3D(roll, pitch, yaw)
}
/**
* Quaternion to XYZ Cardan angles
*/
public fun Quaternion.toEuler(): Float32Vector3D = if (QuaternionAlgebra.norm(this) == 0.0) {
Float32Space3D.zero
} else {
normalized().normalizedToEuler()
}

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.euclidean2d.Float64Space2D
import kotlin.math.sqrt import kotlin.math.sqrt
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -12,12 +13,12 @@ import kotlin.test.assertEquals
internal class Euclidean2DSpaceTest { internal class Euclidean2DSpaceTest {
@Test @Test
fun zero() { fun zero() {
assertVectorEquals(Euclidean2DSpace.vector(0.0, 0.0), Euclidean2DSpace.zero) assertVectorEquals(Float64Space2D.vector(0.0, 0.0), Float64Space2D.zero)
} }
@Test @Test
fun norm() { fun norm() {
with(Euclidean2DSpace) { with(Float64Space2D) {
assertEquals(0.0, norm(zero)) assertEquals(0.0, norm(zero))
assertEquals(1.0, norm(vector(1.0, 0.0))) assertEquals(1.0, norm(vector(1.0, 0.0)))
assertEquals(sqrt(2.0), norm(vector(1.0, 1.0))) assertEquals(sqrt(2.0), norm(vector(1.0, 1.0)))
@ -27,7 +28,7 @@ internal class Euclidean2DSpaceTest {
@Test @Test
fun dotProduct() { fun dotProduct() {
with(Euclidean2DSpace) { with(Float64Space2D) {
assertEquals(0.0, zero dot zero) assertEquals(0.0, zero dot zero)
assertEquals(0.0, zero dot vector(1.0, 0.0)) assertEquals(0.0, zero dot vector(1.0, 0.0))
assertEquals(0.0, vector(-2.0, 0.001) dot zero) assertEquals(0.0, vector(-2.0, 0.001) dot zero)
@ -44,7 +45,7 @@ internal class Euclidean2DSpaceTest {
@Test @Test
fun add() { fun add() {
with(Euclidean2DSpace) { with(Float64Space2D) {
assertVectorEquals( assertVectorEquals(
vector(-2.0, 0.001), vector(-2.0, 0.001),
vector(-2.0, 0.001) + zero vector(-2.0, 0.001) + zero
@ -58,7 +59,7 @@ internal class Euclidean2DSpaceTest {
@Test @Test
fun multiply() { fun multiply() {
with(Euclidean2DSpace) { with(Float64Space2D) {
assertVectorEquals(vector(-4.0, 0.0), vector(-2.0, 0.0) * 2) assertVectorEquals(vector(-4.0, 0.0), vector(-2.0, 0.0) * 2)
assertVectorEquals(vector(4.0, 0.0), vector(-2.0, 0.0) * -2) assertVectorEquals(vector(4.0, 0.0), vector(-2.0, 0.0) * -2)
assertVectorEquals(vector(300.0, 0.0003), vector(100.0, 0.0001) * 3) assertVectorEquals(vector(300.0, 0.0003), vector(100.0, 0.0001) * 3)

View File

@ -5,18 +5,19 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.euclidean3d.Float64Space3D
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class Euclidean3DSpaceTest { internal class Euclidean3DSpaceTest {
@Test @Test
fun zero() { fun zero() {
assertVectorEquals(Euclidean3DSpace.vector(0.0, 0.0, 0.0), Euclidean3DSpace.zero) assertVectorEquals(Float64Space3D.vector(0.0, 0.0, 0.0), Float64Space3D.zero)
} }
@Test @Test
fun distance() { fun distance() {
with(Euclidean3DSpace) { with(Float64Space3D) {
assertEquals(0.0, zero.distanceTo(zero)) assertEquals(0.0, zero.distanceTo(zero))
assertEquals(1.0, zero.distanceTo(vector(1.0, 0.0, 0.0))) assertEquals(1.0, zero.distanceTo(vector(1.0, 0.0, 0.0)))
assertEquals(kotlin.math.sqrt(5.000001), vector(1.0, -2.0, 0.001).distanceTo(zero)) assertEquals(kotlin.math.sqrt(5.000001), vector(1.0, -2.0, 0.001).distanceTo(zero))
@ -31,7 +32,7 @@ internal class Euclidean3DSpaceTest {
@Test @Test
fun norm() { fun norm() {
with(Euclidean3DSpace) { with(Float64Space3D) {
assertEquals(0.0, zero.norm()) assertEquals(0.0, zero.norm())
assertEquals(1.0, vector(1.0, 0.0, 0.0).norm()) assertEquals(1.0, vector(1.0, 0.0, 0.0).norm())
assertEquals(kotlin.math.sqrt(3.0), vector(1.0, 1.0, 1.0).norm()) assertEquals(kotlin.math.sqrt(3.0), vector(1.0, 1.0, 1.0).norm())
@ -41,7 +42,7 @@ internal class Euclidean3DSpaceTest {
@Test @Test
fun dotProduct() { fun dotProduct() {
with(Euclidean3DSpace) { with(Float64Space3D) {
assertEquals(0.0, zero dot zero) assertEquals(0.0, zero dot zero)
assertEquals(0.0, zero dot vector(1.0, 0.0, 0.0)) assertEquals(0.0, zero dot vector(1.0, 0.0, 0.0))
assertEquals(0.0, vector(1.0, -2.0, 0.001) dot zero) assertEquals(0.0, vector(1.0, -2.0, 0.001) dot zero)
@ -57,7 +58,7 @@ internal class Euclidean3DSpaceTest {
} }
@Test @Test
fun add() = with(Euclidean3DSpace) { fun add() = with(Float64Space3D) {
assertVectorEquals( assertVectorEquals(
vector(1.0, -2.0, 0.001), vector(1.0, -2.0, 0.001),
vector(1.0, -2.0, 0.001) + zero vector(1.0, -2.0, 0.001) + zero
@ -69,19 +70,19 @@ internal class Euclidean3DSpaceTest {
} }
@Test @Test
fun multiply() = with(Euclidean3DSpace) { fun multiply() = with(Float64Space3D) {
assertVectorEquals(vector(2.0, -4.0, 0.0), vector(1.0, -2.0, 0.0) * 2) assertVectorEquals(vector(2.0, -4.0, 0.0), vector(1.0, -2.0, 0.0) * 2)
} }
@Test @Test
fun vectorProduct() = with(Euclidean3DSpace) { fun vectorProduct() = with(Float64Space3D) {
assertVectorEquals(zAxis, vectorProduct(xAxis, yAxis)) assertVectorEquals(zAxis, vectorProduct(xAxis, yAxis))
assertVectorEquals(zAxis, xAxis cross yAxis) assertVectorEquals(zAxis, xAxis cross yAxis)
assertVectorEquals(-zAxis, vectorProduct(yAxis, xAxis)) assertVectorEquals(-zAxis, vectorProduct(yAxis, xAxis))
} }
@Test @Test
fun doubleVectorProduct() = with(Euclidean3DSpace) { fun doubleVectorProduct() = with(Float64Space3D) {
val a = vector(1, 2, -3) val a = vector(1, 2, -3)
val b = vector(-1, 0, 1) val b = vector(-1, 0, 1)
val c = vector(4, 5, 6) val c = vector(4, 5, 6)

View File

@ -5,13 +5,15 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.euclidean2d.Float64Space2D
import space.kscience.kmath.geometry.euclidean3d.Float64Space3D
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue
internal class ProjectionAlongTest { internal class ProjectionAlongTest {
@Test @Test
fun projectionIntoYEqualsX() { fun projectionIntoYEqualsX() {
with(Euclidean2DSpace) { with(Float64Space2D) {
val normal = vector(-2.0, 2.0) val normal = vector(-2.0, 2.0)
val base = vector(2.3, 2.3) val base = vector(2.3, 2.3)
@ -26,7 +28,7 @@ internal class ProjectionAlongTest {
@Test @Test
fun projectionOntoLine() { fun projectionOntoLine() {
with(Euclidean2DSpace) { with(Float64Space2D) {
val a = 5.0 val a = 5.0
val b = -3.0 val b = -3.0
val c = -15.0 val c = -15.0
@ -42,11 +44,11 @@ internal class ProjectionAlongTest {
} }
@Test @Test
fun projectOntoPlane() = with(Euclidean3DSpace){ fun projectOntoPlane() = with(Float64Space3D){
val normal = vector(1.0, 3.5, 0.07) val normal = vector(1.0, 3.5, 0.07)
val base = vector(2.0, -0.0037, 11.1111) val base = vector(2.0, -0.0037, 11.1111)
with(Euclidean3DSpace) { with(Float64Space3D) {
val testDomain = (-10.0..10.0).generateList(0.43) val testDomain = (-10.0..10.0).generateList(0.43)
for (x in testDomain) { for (x in testDomain) {
for (y in testDomain) { for (y in testDomain) {

View File

@ -5,13 +5,15 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.euclidean2d.Float64Space2D
import space.kscience.kmath.geometry.euclidean3d.Float64Space3D
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue
internal class ProjectionOntoLineTest { internal class ProjectionOntoLineTest {
@Test @Test
fun projectionIntoOx() { fun projectionIntoOx() {
with(Euclidean2DSpace) { with(Float64Space2D) {
val ox = Line(zero, vector(1.0, 0.0)) val ox = Line(zero, vector(1.0, 0.0))
grid(-10.0..10.0, -10.0..10.0, 0.15).forEach { (x, y) -> grid(-10.0..10.0, -10.0..10.0, 0.15).forEach { (x, y) ->
@ -22,7 +24,7 @@ internal class ProjectionOntoLineTest {
@Test @Test
fun projectionIntoOy() { fun projectionIntoOy() {
with(Euclidean2DSpace) { with(Float64Space2D) {
val line = Line(zero, vector(0.0, 1.0)) val line = Line(zero, vector(0.0, 1.0))
grid(-10.0..10.0, -10.0..10.0, 0.15).forEach { (x, y) -> grid(-10.0..10.0, -10.0..10.0, 0.15).forEach { (x, y) ->
@ -33,7 +35,7 @@ internal class ProjectionOntoLineTest {
@Test @Test
fun projectionIntoYEqualsX() { fun projectionIntoYEqualsX() {
with(Euclidean2DSpace) { with(Float64Space2D) {
val line = Line(zero, vector(1.0, 1.0)) val line = Line(zero, vector(1.0, 1.0))
assertVectorEquals(zero, projectToLine(zero, line)) assertVectorEquals(zero, projectToLine(zero, line))
@ -47,7 +49,7 @@ internal class ProjectionOntoLineTest {
@Test @Test
fun projectionOntoLine2d() { fun projectionOntoLine2d() {
with(Euclidean2DSpace) { with(Float64Space2D) {
val a = 5.0 val a = 5.0
val b = -3.0 val b = -3.0
val c = -15.0 val c = -15.0
@ -62,7 +64,7 @@ internal class ProjectionOntoLineTest {
} }
@Test @Test
fun projectionOntoLine3d() = with(Euclidean3DSpace) { fun projectionOntoLine3d() = with(Float64Space3D) {
val line = Line( val line = Line(
base = vector(1.0, 3.5, 0.07), base = vector(1.0, 3.5, 0.07),
direction = vector(2.0, -0.0037, 11.1111) direction = vector(2.0, -0.0037, 11.1111)

View File

@ -7,6 +7,7 @@ package space.kscience.kmath.geometry
import space.kscience.kmath.complex.Quaternion import space.kscience.kmath.complex.Quaternion
import space.kscience.kmath.complex.normalized import space.kscience.kmath.complex.normalized
import space.kscience.kmath.geometry.euclidean3d.*
import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.Float64Buffer
import space.kscience.kmath.testutils.assertBufferEquals import space.kscience.kmath.testutils.assertBufferEquals
import kotlin.test.Test import kotlin.test.Test
@ -14,7 +15,7 @@ import kotlin.test.Test
class RotationTest { class RotationTest {
@Test @Test
fun differentRotations() = with(Euclidean3DSpace) { fun differentRotations() = with(Float64Space3D) {
val vector = vector(1.0, 1.0, 1.0) val vector = vector(1.0, 1.0, 1.0)
val q = Quaternion(1.0, 2.0, -3.0, 4.0).normalized() val q = Quaternion(1.0, 2.0, -3.0, 4.0).normalized()
val rotatedByQ = rotate(vector, q) val rotatedByQ = rotate(vector, q)
@ -36,7 +37,7 @@ class RotationTest {
@Test @Test
fun fromRotation() { fun fromRotation() {
val q = Quaternion.fromRotation(0.3.radians, Euclidean3DSpace.vector(1.0, 1.0, 1.0)) val q = Quaternion.fromRotation(0.3.radians, Float64Space3D.vector(1.0, 1.0, 1.0))
assertBufferEquals(Float64Buffer(0.9887711, 0.0862781, 0.0862781, 0.0862781), q) assertBufferEquals(Float64Buffer(0.9887711, 0.0862781, 0.0862781, 0.0862781), q)
} }

View File

@ -6,12 +6,13 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.euclidean2d.Float64Space2D
import space.kscience.kmath.operations.toList import space.kscience.kmath.operations.toList
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class Vector2DTest { internal class Vector2DTest {
private val vector = Euclidean2DSpace.vector(1.0, -7.999) private val vector = Float64Space2D.vector(1.0, -7.999)
@Test @Test
fun size() { fun size() {

View File

@ -5,12 +5,13 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.euclidean3d.Float64Space3D
import space.kscience.kmath.operations.toList import space.kscience.kmath.operations.toList
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class Vector3DTest { internal class Vector3DTest {
private val vector = Euclidean3DSpace.vector(1.0, -7.999, 0.001) private val vector = Float64Space3D.vector(1.0, -7.999, 0.001)
@Test @Test
fun size() { fun size() {

View File

@ -5,6 +5,8 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.geometry.euclidean2d.DoubleVector2D
import space.kscience.kmath.geometry.euclidean3d.DoubleVector3D
import kotlin.math.abs import kotlin.math.abs
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -16,6 +16,7 @@ import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ColumnStrides
import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.FieldOps
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.PowerOperations import space.kscience.kmath.operations.PowerOperations
@ -32,7 +33,8 @@ internal fun ShapeND.toLongArray(): LongArray = LongArray(size) { get(it).toLong
public class DoubleTensorFlowAlgebra internal constructor( public class DoubleTensorFlowAlgebra internal constructor(
graph: Graph, graph: Graph,
) : TensorFlowAlgebra<Double, TFloat64, Float64Field>(graph), PowerOperations<StructureND<Double>> { ) : TensorFlowAlgebra<Double, TFloat64,
Float64Field>(graph), FieldOps<StructureND<Double>>, PowerOperations<StructureND<Double>> {
override val elementAlgebra: Float64Field get() = Float64Field override val elementAlgebra: Float64Field get() = Float64Field