diff options
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/AdvancedSmooth.cs')
-rw-r--r-- | Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/AdvancedSmooth.cs | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/AdvancedSmooth.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/AdvancedSmooth.cs new file mode 100644 index 0000000..e807462 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/AdvancedSmooth.cs @@ -0,0 +1,561 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Pathfinding { + /// <summary> + /// Smoothing by dividing path into turns and straight segments. + /// + /// Deprecated: This modifier is deprecated + /// </summary> + [System.Serializable] + [System.Obsolete("This modifier is deprecated")] + [HelpURL("https://arongranberg.com/astar/documentation/stable/advancedsmooth.html")] + public class AdvancedSmooth : MonoModifier { + public override int Order { get { return 40; } } + + public float turningRadius = 1.0F; + + public MaxTurn turnConstruct1 = new MaxTurn(); + public ConstantTurn turnConstruct2 = new ConstantTurn(); + + public override void Apply (Path p) { + Vector3[] vectorPath = p.vectorPath.ToArray(); + + if (vectorPath.Length <= 2) { + return; + } + + List<Vector3> newPath = new List<Vector3>(); + newPath.Add(vectorPath[0]); + + TurnConstructor.turningRadius = turningRadius; + + for (int i = 1; i < vectorPath.Length-1; i++) { + List<Turn> turnList = new List<Turn>(); + + TurnConstructor.Setup(i, vectorPath); + turnConstruct1.Prepare(i, vectorPath); + turnConstruct2.Prepare(i, vectorPath); + + TurnConstructor.PostPrepare(); + + if (i == 1) { + turnConstruct1.PointToTangent(turnList); + turnConstruct2.PointToTangent(turnList); + } else { + turnConstruct1.TangentToTangent(turnList); + turnConstruct2.TangentToTangent(turnList); + } + + EvaluatePaths(turnList, newPath); + + //Last point + if (i == vectorPath.Length-2) { + turnConstruct1.TangentToPoint(turnList); + turnConstruct2.TangentToPoint(turnList); + } + + EvaluatePaths(turnList, newPath); + } + + newPath.Add(vectorPath[vectorPath.Length-1]); + p.vectorPath = newPath; + } + + void EvaluatePaths (List<Turn> turnList, List<Vector3> output) { + turnList.Sort(); + + for (int j = 0; j < turnList.Count; j++) { + if (j == 0) { + turnList[j].GetPath(output); + } + } + + turnList.Clear(); + + if (TurnConstructor.changedPreviousTangent) { + turnConstruct1.OnTangentUpdate(); + turnConstruct2.OnTangentUpdate(); + } + } + + [System.Serializable] + /// <summary>Type of turn.</summary> + public class MaxTurn : TurnConstructor { + Vector3 preRightCircleCenter = Vector3.zero; + Vector3 preLeftCircleCenter = Vector3.zero; + + Vector3 rightCircleCenter; + Vector3 leftCircleCenter; + + double vaRight, vaLeft, preVaLeft, preVaRight; + + double gammaLeft, gammaRight; + + double betaRightRight, betaRightLeft, betaLeftRight, betaLeftLeft; + + double deltaRightLeft, deltaLeftRight; + + double alfaRightRight, alfaLeftLeft, alfaRightLeft, alfaLeftRight; + + public override void OnTangentUpdate () { + rightCircleCenter = current + normal * turningRadius; + leftCircleCenter = current - normal * turningRadius; + + vaRight = Atan2(current-rightCircleCenter); + vaLeft = vaRight + Math.PI; + } + + public override void Prepare (int i, Vector3[] vectorPath) { + preRightCircleCenter = rightCircleCenter; + preLeftCircleCenter = leftCircleCenter; + + rightCircleCenter = current + normal * turningRadius; + leftCircleCenter = current - normal * turningRadius; + + preVaRight = vaRight; + preVaLeft = vaLeft; + + vaRight = Atan2(current-rightCircleCenter); + vaLeft = vaRight + Math.PI; + } + + public override void TangentToTangent (List<Turn> turnList) { + alfaRightRight = Atan2(rightCircleCenter - preRightCircleCenter); // + Math.PI*0.5; //Angle tangent to the angle the previous circle (from the current circle) + alfaLeftLeft = Atan2(leftCircleCenter - preLeftCircleCenter); // + Math.PI*0.5; + alfaRightLeft = Atan2(leftCircleCenter - preRightCircleCenter); // + Math.PI*0.5; //RightLeft means: from the previous right circle to the current left circle + alfaLeftRight = Atan2(rightCircleCenter - preLeftCircleCenter); // + Math.PI*0.5; //LeftRight means: from the previous left circle to the current right circle + + double magnRightLeft = (leftCircleCenter - preRightCircleCenter).magnitude; + double magnLeftRight = (rightCircleCenter - preLeftCircleCenter).magnitude; + + bool noRightLeft = false; + bool noLeftRight = false; + + //Discard RightLeft and LeftRight paths if the circles lie to close to each other + if (magnRightLeft < turningRadius*2) { + magnRightLeft = turningRadius*2; + noRightLeft = true; + } + + if (magnLeftRight < turningRadius*2) { + magnLeftRight = turningRadius*2; + noLeftRight = true; + } + + deltaRightLeft = noRightLeft ? 0 : (ThreeSixtyRadians * 0.25) - Math.Asin(turningRadius*2 / magnRightLeft); //turn*2 should be r1 + r2 for circles with different radiis + deltaLeftRight = noLeftRight ? 0 : (ThreeSixtyRadians * 0.25) - Math.Asin(turningRadius*2 / magnLeftRight); //turn*2 should be r1 + r2 for circles with different radiis + + + //Length for the first turn + betaRightRight = ClockwiseAngle(preVaRight, alfaRightRight - ThreeSixtyRadians*0.25); // ThreeSixtyRadians * 0.25 = 90 degrees + betaRightLeft = ClockwiseAngle(preVaRight, alfaRightLeft - deltaRightLeft); + betaLeftRight = CounterClockwiseAngle(preVaLeft, alfaLeftRight + deltaLeftRight); + betaLeftLeft = CounterClockwiseAngle(preVaLeft, alfaLeftLeft + ThreeSixtyRadians*0.25); + + + //Add length for the second turn + betaRightRight += ClockwiseAngle(alfaRightRight - ThreeSixtyRadians*0.25, vaRight); + betaRightLeft += CounterClockwiseAngle(alfaRightLeft + deltaRightLeft, vaLeft); + betaLeftRight += ClockwiseAngle(alfaLeftRight - deltaLeftRight, vaRight); + betaLeftLeft += CounterClockwiseAngle(alfaLeftLeft + ThreeSixtyRadians*0.25, vaLeft); + + betaRightRight = GetLengthFromAngle(betaRightRight, turningRadius); + betaRightLeft = GetLengthFromAngle(betaRightLeft, turningRadius); + betaLeftRight = GetLengthFromAngle(betaLeftRight, turningRadius); + betaLeftLeft = GetLengthFromAngle(betaLeftLeft, turningRadius); + + Vector3 + pRightRight1, pRightRight2, + pRightLeft1, pRightLeft2, + pLeftRight1, pLeftRight2, + pLeftLeft1, pLeftLeft2; + + //Debug.Log ("=== DELTA VALUES===\nRightLeft "+ToDegrees (deltaRightLeft)+" - LeftRight "+ToDegrees (deltaLeftRight)); + //Set up points where the straigh segments starts and ends (between the turns) + pRightRight1 = AngleToVector(alfaRightRight - ThreeSixtyRadians*0.25)*turningRadius + preRightCircleCenter; + pRightLeft1 = AngleToVector(alfaRightLeft - deltaRightLeft)*turningRadius + preRightCircleCenter; + pLeftRight1 = AngleToVector(alfaLeftRight + deltaLeftRight)*turningRadius + preLeftCircleCenter; + pLeftLeft1 = AngleToVector(alfaLeftLeft + ThreeSixtyRadians*0.25)*turningRadius + preLeftCircleCenter; + + pRightRight2 = AngleToVector(alfaRightRight - ThreeSixtyRadians*0.25)*turningRadius + rightCircleCenter; + pRightLeft2 = AngleToVector(alfaRightLeft - deltaRightLeft + Math.PI)*turningRadius + leftCircleCenter; + pLeftRight2 = AngleToVector(alfaLeftRight + deltaLeftRight + Math.PI)*turningRadius + rightCircleCenter; + pLeftLeft2 = AngleToVector(alfaLeftLeft + ThreeSixtyRadians*0.25)*turningRadius + leftCircleCenter; + betaRightRight += (pRightRight1 - pRightRight2).magnitude; + betaRightLeft += (pRightLeft1 - pRightLeft2).magnitude; + betaLeftRight += (pLeftRight1 - pLeftRight2).magnitude; + betaLeftLeft += (pLeftLeft1 - pLeftLeft2).magnitude; + + if (noRightLeft) { + betaRightLeft += 10000000; + } + if (noLeftRight) { + betaLeftRight += 10000000; + } + + turnList.Add(new Turn((float)betaRightRight, this, 2)); + turnList.Add(new Turn((float)betaRightLeft, this, 3)); + turnList.Add(new Turn((float)betaLeftRight, this, 4)); + turnList.Add(new Turn((float)betaLeftLeft, this, 5)); + } + + public override void PointToTangent (List<Turn> turnList) { + bool noRight = false, noLeft = false; + + float rightMagn = (prev-rightCircleCenter).magnitude; + float leftMagn = (prev-leftCircleCenter).magnitude; + + if (rightMagn < turningRadius) + noRight = true; + if (leftMagn < turningRadius) + noLeft = true; + + double alfa = noRight ? 0 : Atan2(prev-rightCircleCenter); + double delta = noRight ? 0 : (ThreeSixtyRadians * 0.25) - Math.Asin(turningRadius / (prev-rightCircleCenter).magnitude); + + //Angle to the point where turning ends on the right circle + gammaRight = alfa + delta; + + double betaRight = noRight ? 0 : ClockwiseAngle(gammaRight, vaRight); + + double alfaLeft = noLeft ? 0 : Atan2(prev-leftCircleCenter); + double deltaLeft = noLeft ? 0 : (ThreeSixtyRadians * 0.25) - Math.Asin(turningRadius / (prev-leftCircleCenter).magnitude); + + //Angle to the point where turning ends + gammaLeft = alfaLeft - deltaLeft; + + double betaLeft = noLeft ? 0 : CounterClockwiseAngle(gammaLeft, vaLeft); + + if (!noRight) + turnList.Add(new Turn((float)betaRight, this, 0)); + if (!noLeft) + turnList.Add(new Turn((float)betaLeft, this, 1)); + } + + public override void TangentToPoint (List<Turn> turnList) { + bool noRight = false, noLeft = false; + + float rightMagn = (next-rightCircleCenter).magnitude; + float leftMagn = (next-leftCircleCenter).magnitude; + + if (rightMagn < turningRadius) + noRight = true; + if (leftMagn < turningRadius) + noLeft = true; + + if (!noRight) { + double alfa = Atan2(next-rightCircleCenter); + double delta = (ThreeSixtyRadians * 0.25) - Math.Asin(turningRadius / rightMagn); + + //Angle to the point where turning ends on the right circle + gammaRight = alfa - delta; + double betaRight = ClockwiseAngle(vaRight, gammaRight); + + turnList.Add(new Turn((float)betaRight, this, 6)); + } + + if (!noLeft) { + double alfaLeft = Atan2(next-leftCircleCenter); + double deltaLeft = (ThreeSixtyRadians * 0.25) - Math.Asin(turningRadius / leftMagn); + + //Angle to the point where turning ends + gammaLeft = alfaLeft + deltaLeft; + + double betaLeft = CounterClockwiseAngle(vaLeft, gammaLeft); + + + turnList.Add(new Turn((float)betaLeft, this, 7)); + } + } + + public override void GetPath (Turn turn, List<Vector3> output) { + switch (turn.id) { + case 0: + //Right - Point to tangent + AddCircleSegment(gammaRight, vaRight, true, rightCircleCenter, output, turningRadius); + break; + case 1: + //Left - Point to tangent + AddCircleSegment(gammaLeft, vaLeft, false, leftCircleCenter, output, turningRadius); + break; + case 2: + //Right Right - Tangent to tangent + AddCircleSegment(preVaRight, alfaRightRight - ThreeSixtyRadians*0.25, true, preRightCircleCenter, output, turningRadius); + AddCircleSegment(alfaRightRight - ThreeSixtyRadians*0.25, vaRight, true, rightCircleCenter, output, turningRadius); + break; + case 3: + //Right Left - Tangent to tangent + AddCircleSegment(preVaRight, alfaRightLeft - deltaRightLeft, true, preRightCircleCenter, output, turningRadius); + AddCircleSegment(alfaRightLeft - deltaRightLeft + Math.PI, vaLeft, false, leftCircleCenter, output, turningRadius); + break; + case 4: + //Left Right - Tangent to tangent + AddCircleSegment(preVaLeft, alfaLeftRight + deltaLeftRight, false, preLeftCircleCenter, output, turningRadius); + AddCircleSegment(alfaLeftRight + deltaLeftRight + Math.PI, vaRight, true, rightCircleCenter, output, turningRadius); + break; + case 5: + //Left Left - Tangent to tangent + AddCircleSegment(preVaLeft, alfaLeftLeft + ThreeSixtyRadians*0.25, false, preLeftCircleCenter, output, turningRadius); + AddCircleSegment(alfaLeftLeft + ThreeSixtyRadians*0.25, vaLeft, false, leftCircleCenter, output, turningRadius); + break; + case 6: + //Right - Tangent to point + AddCircleSegment(vaRight, gammaRight, true, rightCircleCenter, output, turningRadius); + break; + case 7: + //Left - Tangent to point + AddCircleSegment(vaLeft, gammaLeft, false, leftCircleCenter, output, turningRadius); + break; + } + } + } + + [System.Serializable] + /// <summary>Constant turning speed.</summary> + public class ConstantTurn : TurnConstructor { + Vector3 circleCenter; + double gamma1; + double gamma2; + + bool clockwise; + + public override void Prepare (int i, Vector3[] vectorPath) {} + + public override void TangentToTangent (List<Turn> turnList) { + Vector3 preNormal = Vector3.Cross(t1, Vector3.up); + + Vector3 dir = (current-prev); + Vector3 pos = dir*0.5F + prev; + + dir = Vector3.Cross(dir, Vector3.up); + + bool didIntersect; + circleCenter = VectorMath.LineDirIntersectionPointXZ(prev, preNormal, pos, dir, out didIntersect); + + if (!didIntersect) { + return; + } + + gamma1 = Atan2(prev-circleCenter); + gamma2 = Atan2(current-circleCenter); + + clockwise = !VectorMath.RightOrColinearXZ(circleCenter, prev, prev+t1); + + double beta = clockwise ? ClockwiseAngle(gamma1, gamma2) : CounterClockwiseAngle(gamma1, gamma2); + + beta = GetLengthFromAngle(beta, (circleCenter - current).magnitude); + + turnList.Add(new Turn((float)beta, this, 0)); + } + + public override void GetPath (Turn turn, List<Vector3> output) { + AddCircleSegment(gamma1, gamma2, clockwise, circleCenter, output, (circleCenter - current).magnitude); + + normal = (current - circleCenter).normalized; + t2 = Vector3.Cross(normal, Vector3.up).normalized; + normal = -normal; + + if (!clockwise) { + t2 = -t2; + normal = -normal; + } + + changedPreviousTangent = true; + } + } + + /// <summary>Abstract turn constructor.</summary> + public abstract class TurnConstructor { + /// <summary> + /// Constant bias to add to the path lengths. + /// This can be used to favor certain turn types before others. + /// By for example setting this to -5, paths from this path constructor will be chosen + /// if there are no other paths more than 5 world units shorter than this one (as opposed to just any shorter path) + /// </summary> + public float constantBias = 0; + + /// <summary> + /// Bias to multiply the path lengths with. This can be used to favor certain turn types before others. + /// See: <see cref="constantBias"/> + /// </summary> + public float factorBias = 1; + + public static float turningRadius = 1.0F; + + public const double ThreeSixtyRadians = Math.PI * 2; + + public static Vector3 prev, current, next; //The current points + public static Vector3 t1, t2; //The current tangents - t2 is at 'current', t1 is at 'prev' + public static Vector3 normal, prevNormal; //Normal at 'current' + + public static bool changedPreviousTangent = false; + + public abstract void Prepare(int i, Vector3[] vectorPath); + public virtual void OnTangentUpdate () {} + public virtual void PointToTangent (List<Turn> turnList) {} + public virtual void TangentToPoint (List<Turn> turnList) {} + public virtual void TangentToTangent (List<Turn> turnList) {} + public abstract void GetPath(Turn turn, List<Vector3> output); + //abstract void Evaluate (Turn turn); + + public static void Setup (int i, Vector3[] vectorPath) { + current = vectorPath[i]; + prev = vectorPath[i-1]; + next = vectorPath[i+1]; + + prev.y = current.y; + next.y = current.y; + + t1 = t2; + + t2 = (next-current).normalized - (prev-current).normalized; + t2 = t2.normalized; + + prevNormal = normal; + + normal = Vector3.Cross(t2, Vector3.up); + normal = normal.normalized; + } + + public static void PostPrepare () { + changedPreviousTangent = false; + } + + //Utilities + + public void AddCircleSegment (double startAngle, double endAngle, bool clockwise, Vector3 center, List<Vector3> output, float radius) { + double step = ThreeSixtyRadians / 100; + + if (clockwise) { + while (endAngle > startAngle+ThreeSixtyRadians) { + endAngle -= ThreeSixtyRadians; + } + + while (endAngle < startAngle) { + endAngle += ThreeSixtyRadians; + } + } else { + while (endAngle < startAngle-ThreeSixtyRadians) { + endAngle += ThreeSixtyRadians; + } + + while (endAngle > startAngle) { + endAngle -= ThreeSixtyRadians; + } + } + + //Add curve + if (clockwise) { + for (double i = startAngle; i < endAngle; i += step) { + output.Add(AngleToVector(i)*radius+center); + } + } else { + for (double i = startAngle; i > endAngle; i -= step) { + output.Add(AngleToVector(i)*radius+center); + } + } + + //Add last point + output.Add(AngleToVector(endAngle)*radius+center); + } + + public void DebugCircleSegment (Vector3 center, double startAngle, double endAngle, double radius, Color color) { + double step = ThreeSixtyRadians / 100; + + while (endAngle < startAngle) { + endAngle += ThreeSixtyRadians; + } + + Vector3 prev = AngleToVector(startAngle)*(float)radius+center; + for (double i = startAngle+step; i < endAngle; i += step) { + Debug.DrawLine(prev, AngleToVector(i)*(float)radius+center); + } + + Debug.DrawLine(prev, AngleToVector(endAngle)*(float)radius+center); + } + + public void DebugCircle (Vector3 center, double radius, Color color) { + double step = ThreeSixtyRadians / 100; + Vector3 prePos = AngleToVector(-step)*(float)radius+center; + + for (double i = 0; i < ThreeSixtyRadians; i += step) { + Vector3 pos = AngleToVector(i)*(float)radius+center; + Debug.DrawLine(prePos, pos, color); + prePos = pos; + } + } + + /// <summary>Returns the length of an circular arc with a radius and angle. Angle is specified in radians</summary> + public double GetLengthFromAngle (double angle, double radius) { + return radius * angle; + } + + /// <summary>Returns the angle between from and to in a clockwise direction</summary> + public double ClockwiseAngle (double from, double to) { + return ClampAngle(to - from); + } + + /// <summary>Returns the angle between from and to in a counter-clockwise direction</summary> + public double CounterClockwiseAngle (double from, double to) { + return ClampAngle(from - to); + } + + public Vector3 AngleToVector (double a) { + return new Vector3((float)Math.Cos(a), 0, (float)Math.Sin(a)); + } + + public double ToDegrees (double rad) { + return rad * Mathf.Rad2Deg; + } + + public double ClampAngle (double a) { + while (a < 0) { a += ThreeSixtyRadians; } + while (a > ThreeSixtyRadians) { a -= ThreeSixtyRadians; } + return a; + } + + public double Atan2 (Vector3 v) { + return Math.Atan2(v.z, v.x); + } + } + + //Turn class + /// <summary>Represents a turn in a path.</summary> + public struct Turn : IComparable<Turn> { + public float length; + public int id; + + public TurnConstructor constructor; + + public float score { + get { + return length*constructor.factorBias+constructor.constantBias; + } + } + + public Turn (float length, TurnConstructor constructor, int id = 0) { + this.length = length; + this.id = id; + this.constructor = constructor; + } + + public void GetPath (List<Vector3> output) { + constructor.GetPath(this, output); + } + + public int CompareTo (Turn t) { + return t.score > score ? -1 : (t.score < score ? 1 : 0); + } + + public static bool operator < (Turn lhs, Turn rhs) { + return lhs.score < rhs.score; + } + + public static bool operator > (Turn lhs, Turn rhs) { + return lhs.score > rhs.score; + } + } + } +} |