All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
commonMain.ru.casperix.math.curve.float64.Arc2d.kt Maven / Gradle / Ivy
package ru.casperix.math.curve.float64
import ru.casperix.math.geometry.float64.Geometry2Double
import ru.casperix.math.polar.float64.PolarCoordinateDouble
import ru.casperix.math.angle.float64.RadianDouble
import ru.casperix.math.geometry.*
import ru.casperix.math.interpolation.float64.linearInterpolate
import ru.casperix.math.intersection.float64.Intersection2Double
import ru.casperix.math.vector.float64.Vector2d
import ru.casperix.math.vector.rotateCCW
import ru.casperix.math.vector.rotateCW
import kotlinx.serialization.Serializable
import kotlin.math.PI
import kotlin.math.absoluteValue
import kotlin.math.sign
/**
* @param center -- center of circle on which arc is placed
* @param startAngle -- angle in radian for start point
* @param deltaAngle -- delta by circle to end point (positive -- counterclockwise, or negative -- clockwise)
*/
@Serializable
data class Arc2d(val center: Vector2d, val startAngle: RadianDouble, val deltaAngle: RadianDouble, val range: Double) : Curve2d {
val finishAngle = startAngle + deltaAngle
override val start: Vector2d = center + PolarCoordinateDouble(range, startAngle).toDecart()
override val finish: Vector2d = center + PolarCoordinateDouble(range, finishAngle).toDecart()
override fun invert(): Arc2d {
return Arc2d(center, finishAngle, -deltaAngle, range)
}
fun isAngleInside(angle: RadianDouble): Boolean {
var control = (angle - startAngle).normalize().value
val deltaAngle = deltaAngle.value
return if (deltaAngle >= 0f) {
if (control < 0f) control += PI2
control in 0.0..deltaAngle
} else {
if (control > 0f) control -= PI2
control in deltaAngle..0.0
}
}
override fun grow(startOffset: Double, finishOffset: Double): Curve2d {
val changeStartAngle = RadianDouble(startOffset / range * deltaAngle.value.sign)
val changeFinishAngle = RadianDouble(finishOffset / range * deltaAngle.value.sign)
return Arc2d(center, startAngle - changeStartAngle, deltaAngle + (changeStartAngle + changeFinishAngle), range)
}
override fun getProjection(position: Vector2d): Double {
val angle = RadianDouble.byDirection(position - center)
var control = (angle - startAngle).normalize().value
val deltaAngle = deltaAngle.value
return if (deltaAngle >= 0) {
if (control < 0f) control += fPI2
control /deltaAngle
} else {
if (control > 0f) control -= fPI2
control / deltaAngle
}
}
companion object {
/**
* @param start - first arc point.
* @param startTangent - tangent for first arc point
* @param finish - last arc point
*/
fun byTangent(start: Vector2d, startTangent: Vector2d, finish: Vector2d): Arc2d {
val startNormal = startTangent.rotateCCW()
val basis = (finish - start)
val middleNormal = basis.rotateCW()
val middlePoint = (finish - start) / 2.0
val centerOffset = Intersection2Double.getLineWithLine(
Line2d(Vector2d.ZERO, startNormal),
Line2d(middlePoint, middlePoint+middleNormal)
) ?: throw Exception("Invalid center")
val center = start + centerOffset
val startAngle = RadianDouble.byDirection(start - center).value
var finishAngle = RadianDouble.byDirection(finish - center).value
val range = center.distTo(start)
val pad = Geometry2Double.getPointAroundRay(Vector2d.ZERO, startTangent, finish - start, 0.0)
if (pad == PointAroundRay.LEFT) {
if (finishAngle < startAngle) {
finishAngle += fPI2
}
} else {
if (finishAngle > startAngle) {
finishAngle -= fPI2
}
}
val deltaAngle = finishAngle - startAngle
return Arc2d(center, RadianDouble(startAngle), RadianDouble(deltaAngle), range)
}
}
override fun divide(t: Double): Pair {
val deltaAngleFirst = linearInterpolate(0.0, deltaAngle.value, t)
val deltaAngleLast = deltaAngle.value - deltaAngleFirst
return Pair(
Arc2d(center, startAngle, RadianDouble(deltaAngleFirst), range),
Arc2d(center, startAngle + RadianDouble(deltaAngleFirst), RadianDouble(deltaAngleLast), range),
)
}
fun getAngle(t: Double): RadianDouble {
return RadianDouble(linearInterpolate(startAngle.value, finishAngle.value, t))
}
override fun getPosition(t: Double): Vector2d {
val angle = getAngle(t)
return center + PolarCoordinateDouble(range, angle).toDecart()
}
override fun getTangent(t: Double): Vector2d {
val delta = if (deltaAngle.value > 0.0) PI / 2 else -PI / 2
return PolarCoordinateDouble(1.0, getAngle(t) + RadianDouble(delta)).toDecart()
}
override fun getNormal(t: Double): Vector2d {
val delta = if (deltaAngle.value > 0.0) -PI else 0.0
return PolarCoordinateDouble(1.0, getAngle(t) + RadianDouble(delta)).toDecart()
}
override fun length(): Double {
return range * deltaAngle.value.absoluteValue
}
}