mirror of
https://github.com/Kizuren/SynthMaze.git
synced 2025-12-25 08:43:59 +01:00
206 lines
6.7 KiB
C#
206 lines
6.7 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Pathfinding.Util {
|
|
/// <summary>Interpolates along a sequence of points</summary>
|
|
public class PathInterpolator {
|
|
List<Vector3> path;
|
|
|
|
float distanceToSegmentStart;
|
|
float currentDistance;
|
|
float currentSegmentLength = float.PositiveInfinity;
|
|
float totalDistance = float.PositiveInfinity;
|
|
|
|
/// <summary>Current position</summary>
|
|
public virtual Vector3 position {
|
|
get {
|
|
float t = currentSegmentLength > 0.0001f ? (currentDistance - distanceToSegmentStart) / currentSegmentLength : 0f;
|
|
return Vector3.Lerp(path[segmentIndex], path[segmentIndex+1], t);
|
|
}
|
|
}
|
|
|
|
/// <summary>Last point in the path</summary>
|
|
public Vector3 endPoint {
|
|
get {
|
|
return path[path.Count-1];
|
|
}
|
|
}
|
|
|
|
/// <summary>Tangent of the curve at the current position</summary>
|
|
public Vector3 tangent {
|
|
get {
|
|
return path[segmentIndex+1] - path[segmentIndex];
|
|
}
|
|
}
|
|
|
|
/// <summary>Remaining distance until the end of the path</summary>
|
|
public float remainingDistance {
|
|
get {
|
|
return totalDistance - distance;
|
|
}
|
|
set {
|
|
distance = totalDistance - value;
|
|
}
|
|
}
|
|
|
|
/// <summary>Traversed distance from the start of the path</summary>
|
|
public float distance {
|
|
get {
|
|
return currentDistance;
|
|
}
|
|
set {
|
|
currentDistance = value;
|
|
|
|
while (currentDistance < distanceToSegmentStart && segmentIndex > 0) PrevSegment();
|
|
while (currentDistance > distanceToSegmentStart + currentSegmentLength && segmentIndex < path.Count - 2) NextSegment();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Current segment.
|
|
/// The start and end points of the segment are path[value] and path[value+1].
|
|
/// </summary>
|
|
public int segmentIndex { get; private set; }
|
|
|
|
/// <summary>
|
|
/// True if this instance has a path set.
|
|
/// See: SetPath
|
|
/// </summary>
|
|
public bool valid {
|
|
get {
|
|
return path != null;
|
|
}
|
|
}
|
|
|
|
/// <summary>Appends the remaining path between <see cref="position"/> and <see cref="endPoint"/> to buffer</summary>
|
|
public void GetRemainingPath (List<Vector3> buffer) {
|
|
if (!valid) throw new System.Exception("PathInterpolator is not valid");
|
|
buffer.Add(position);
|
|
for (int i = segmentIndex+1; i < path.Count; i++) {
|
|
buffer.Add(path[i]);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the path to interpolate along.
|
|
/// This will reset all interpolation variables.
|
|
/// </summary>
|
|
public void SetPath (List<Vector3> path) {
|
|
this.path = path;
|
|
currentDistance = 0;
|
|
segmentIndex = 0;
|
|
distanceToSegmentStart = 0;
|
|
|
|
if (path == null) {
|
|
totalDistance = float.PositiveInfinity;
|
|
currentSegmentLength = float.PositiveInfinity;
|
|
return;
|
|
}
|
|
|
|
if (path.Count < 2) throw new System.ArgumentException("Path must have a length of at least 2");
|
|
|
|
currentSegmentLength = (path[1] - path[0]).magnitude;
|
|
totalDistance = 0f;
|
|
|
|
var prev = path[0];
|
|
for (int i = 1; i < path.Count; i++) {
|
|
var current = path[i];
|
|
totalDistance += (current - prev).magnitude;
|
|
prev = current;
|
|
}
|
|
}
|
|
|
|
/// <summary>Move to the specified segment and move a fraction of the way to the next segment</summary>
|
|
public void MoveToSegment (int index, float fractionAlongSegment) {
|
|
if (path == null) return;
|
|
if (index < 0 || index >= path.Count - 1) throw new System.ArgumentOutOfRangeException("index");
|
|
while (segmentIndex > index) PrevSegment();
|
|
while (segmentIndex < index) NextSegment();
|
|
distance = distanceToSegmentStart + Mathf.Clamp01(fractionAlongSegment) * currentSegmentLength;
|
|
}
|
|
|
|
/// <summary>Move as close as possible to the specified point</summary>
|
|
public void MoveToClosestPoint (Vector3 point) {
|
|
if (path == null) return;
|
|
|
|
float bestDist = float.PositiveInfinity;
|
|
float bestFactor = 0f;
|
|
int bestIndex = 0;
|
|
|
|
for (int i = 0; i < path.Count-1; i++) {
|
|
float factor = VectorMath.ClosestPointOnLineFactor(path[i], path[i+1], point);
|
|
Vector3 closest = Vector3.Lerp(path[i], path[i+1], factor);
|
|
float dist = (point - closest).sqrMagnitude;
|
|
|
|
if (dist < bestDist) {
|
|
bestDist = dist;
|
|
bestFactor = factor;
|
|
bestIndex = i;
|
|
}
|
|
}
|
|
|
|
MoveToSegment(bestIndex, bestFactor);
|
|
}
|
|
|
|
public void MoveToLocallyClosestPoint (Vector3 point, bool allowForwards = true, bool allowBackwards = true) {
|
|
if (path == null) return;
|
|
|
|
while (allowForwards && segmentIndex < path.Count - 2 && (path[segmentIndex+1] - point).sqrMagnitude <= (path[segmentIndex] - point).sqrMagnitude) {
|
|
NextSegment();
|
|
}
|
|
|
|
while (allowBackwards && segmentIndex > 0 && (path[segmentIndex-1] - point).sqrMagnitude <= (path[segmentIndex] - point).sqrMagnitude) {
|
|
PrevSegment();
|
|
}
|
|
|
|
// Check the distances to the two segments extending from the vertex path[segmentIndex]
|
|
// and pick the position on those segments that is closest to the #point parameter.
|
|
float factor1 = 0, factor2 = 0, d1 = float.PositiveInfinity, d2 = float.PositiveInfinity;
|
|
if (segmentIndex > 0) {
|
|
factor1 = VectorMath.ClosestPointOnLineFactor(path[segmentIndex-1], path[segmentIndex], point);
|
|
d1 = (Vector3.Lerp(path[segmentIndex-1], path[segmentIndex], factor1) - point).sqrMagnitude;
|
|
}
|
|
|
|
if (segmentIndex < path.Count - 1) {
|
|
factor2 = VectorMath.ClosestPointOnLineFactor(path[segmentIndex], path[segmentIndex+1], point);
|
|
d2 = (Vector3.Lerp(path[segmentIndex], path[segmentIndex+1], factor2) - point).sqrMagnitude;
|
|
}
|
|
|
|
if (d1 < d2) MoveToSegment(segmentIndex - 1, factor1);
|
|
else MoveToSegment(segmentIndex, factor2);
|
|
}
|
|
|
|
public void MoveToCircleIntersection2D (Vector3 circleCenter3D, float radius, IMovementPlane transform) {
|
|
if (path == null) return;
|
|
|
|
// Move forwards as long as we are getting closer to circleCenter3D
|
|
while (segmentIndex < path.Count - 2 && VectorMath.ClosestPointOnLineFactor(path[segmentIndex], path[segmentIndex+1], circleCenter3D) > 1) {
|
|
NextSegment();
|
|
}
|
|
|
|
var circleCenter = transform.ToPlane(circleCenter3D);
|
|
|
|
// Move forwards as long as the current segment endpoint is within the circle
|
|
while (segmentIndex < path.Count - 2 && (transform.ToPlane(path[segmentIndex+1]) - circleCenter).sqrMagnitude <= radius*radius) {
|
|
NextSegment();
|
|
}
|
|
|
|
// Calculate the intersection with the circle. This involves some math.
|
|
var factor = VectorMath.LineCircleIntersectionFactor(circleCenter, transform.ToPlane(path[segmentIndex]), transform.ToPlane(path[segmentIndex+1]), radius);
|
|
// Move to the intersection point
|
|
MoveToSegment(segmentIndex, factor);
|
|
}
|
|
|
|
protected virtual void PrevSegment () {
|
|
segmentIndex--;
|
|
currentSegmentLength = (path[segmentIndex+1] - path[segmentIndex]).magnitude;
|
|
distanceToSegmentStart -= currentSegmentLength;
|
|
}
|
|
|
|
protected virtual void NextSegment () {
|
|
segmentIndex++;
|
|
distanceToSegmentStart += currentSegmentLength;
|
|
currentSegmentLength = (path[segmentIndex+1] - path[segmentIndex]).magnitude;
|
|
}
|
|
}
|
|
}
|