org.gtlp.util.path.Path.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gtlp-processing-utils Show documentation
Show all versions of gtlp-processing-utils Show documentation
Utility Library for Processing
The newest version!
package org.gtlp.util.path
import org.gtlp.util.math.Vector
import java.util.*
/**
* Path defined by [Vector]s at a specified progress.
* Variable [length] for ease of use (speed of objects following this path, for instance).
*
* Make sure to add at least two points.
*
* Linear interpolation between two [Vector]s.
*/
data class Path(val length: Float) {
/**
* A map to store [Vector]s at a specified progress.
*/
private var pointsMap = mutableMapOf()
/**
* Returns a [Vector] of position for the given progress.
* Requires at least two points to be present in [pointsMap]
*
* @param progress between 0 and [length]
*
* @return A [Vector] defining the position at the given progress
*/
@Throws(InconsistentDataException::class)
operator fun get(progress: Float): Vector {
val left = getNearestBefore(progress)
val right = getNearestAfter(progress)
if (left == null || right == null) {
throw InconsistentDataException("No suitable points found")
}
if (left.value == right.value) {
return left.value
}
val interval = right.key - left.key
val offset = progress - left.key
return left.value.lerp(right.value, offset / interval)
}
/**
* Get all points that create this path
*
* @return a collection of [Vector]s
*/
fun get(): Collection {
return pointsMap.values.toSet()
}
/**
* Sets the position at a certain progress and sorts the map.
*
* @param progress the progress as a [Float] between 0 and [length] (not checked).
* @param position the position at [progress]
*/
operator fun set(progress: Float, position: Vector) {
pointsMap[progress] = position
pointsMap = pointsMap.toSortedMap()
}
/**
* Looks for the nearest [Vector] in [pointsMap] with a progress less
* than or equal to [progress].
*
* This implementation returns early when the progress of a vector is
* greater than the limiting parameter.
*
* @param progress the limiting progress
*
* @return the nearest [Vector] in [pointsMap] that has a progress
* smaller than or equal to [progress]
*
* @see getNearestAfter
*/
private fun getNearestBefore(progress: Float): MutableMap.MutableEntry? {
var candidate: MutableMap.MutableEntry? = null
var candidateDistance: Float = Float.MAX_VALUE
pointsMap.entries.forEach {
if (it.key > progress) return@forEach
val distance = progress - it.key
if (distance in 0f..candidateDistance) {
candidate = it
candidateDistance = distance
}
}
return candidate
}
/**
* Looks for the nearest [Vector] in [pointsMap] with a progress greater
* than or equal to [progress].
*
* This implementation returns early when the progress of a vector is
* less than the limiting parameter.
*
* @param progress the limiting progress
*
* @return the nearest [Vector] in [pointsMap] that has a progress
* greater than or equal to [progress]
*
* @see getNearestBefore
*/
private fun getNearestAfter(progress: Float): MutableMap.MutableEntry? {
var candidate: MutableMap.MutableEntry? = null
var candidateDistance: Float = Float.MAX_VALUE
pointsMap.entries.reversed().forEach {
if (it.key < progress) return@forEach
val distance = it.key - progress
if (distance in 0f..candidateDistance) {
candidate = it
candidateDistance = distance
}
}
return candidate
}
/**
* Convenience method to set any [Number] as progress.
*
* @param progress a number defining the progress, will be converted to [Float]
* @param position the position at [progress]
*
* @see [set]
*/
operator fun set(progress: Number, position: Vector) {
set(progress.toFloat(), position)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as Path
if (length != other.length) return false
if (pointsMap != other.pointsMap) return false
return true
}
override fun hashCode(): Int {
return Objects.hash(length, pointsMap)
}
}