123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace Pathfinding {
- [AddComponentMenu("Pathfinding/Modifiers/Advanced Smooth")]
- [System.Serializable]
-
- [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_advanced_smooth.php")]
- 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 == null || 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);
-
- 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]
-
- 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);
- alfaLeftLeft = Atan2(leftCircleCenter - preLeftCircleCenter);
- alfaRightLeft = Atan2(leftCircleCenter - preRightCircleCenter);
- alfaLeftRight = Atan2(rightCircleCenter - preLeftCircleCenter);
- double magnRightLeft = (leftCircleCenter - preRightCircleCenter).magnitude;
- double magnLeftRight = (rightCircleCenter - preLeftCircleCenter).magnitude;
- bool noRightLeft = false;
- bool noLeftRight = false;
-
- 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);
- deltaLeftRight = noLeftRight ? 0 : (ThreeSixtyRadians * 0.25) - Math.Asin(turningRadius*2 / magnLeftRight);
-
- betaRightRight = ClockwiseAngle(preVaRight, alfaRightRight - ThreeSixtyRadians*0.25);
- betaRightLeft = ClockwiseAngle(preVaRight, alfaRightLeft - deltaRightLeft);
- betaLeftRight = CounterClockwiseAngle(preVaLeft, alfaLeftRight + deltaLeftRight);
- betaLeftLeft = CounterClockwiseAngle(preVaLeft, alfaLeftLeft + ThreeSixtyRadians*0.25);
-
- 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;
-
-
- 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);
-
- 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);
-
- 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);
-
- 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);
-
- 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:
-
- AddCircleSegment(gammaRight, vaRight, true, rightCircleCenter, output, turningRadius);
- break;
- case 1:
-
- AddCircleSegment(gammaLeft, vaLeft, false, leftCircleCenter, output, turningRadius);
- break;
- case 2:
-
- AddCircleSegment(preVaRight, alfaRightRight - ThreeSixtyRadians*0.25, true, preRightCircleCenter, output, turningRadius);
- AddCircleSegment(alfaRightRight - ThreeSixtyRadians*0.25, vaRight, true, rightCircleCenter, output, turningRadius);
- break;
- case 3:
-
- AddCircleSegment(preVaRight, alfaRightLeft - deltaRightLeft, true, preRightCircleCenter, output, turningRadius);
- AddCircleSegment(alfaRightLeft - deltaRightLeft + Math.PI, vaLeft, false, leftCircleCenter, output, turningRadius);
- break;
- case 4:
-
- AddCircleSegment(preVaLeft, alfaLeftRight + deltaLeftRight, false, preLeftCircleCenter, output, turningRadius);
- AddCircleSegment(alfaLeftRight + deltaLeftRight + Math.PI, vaRight, true, rightCircleCenter, output, turningRadius);
- break;
- case 5:
-
- AddCircleSegment(preVaLeft, alfaLeftLeft + ThreeSixtyRadians*0.25, false, preLeftCircleCenter, output, turningRadius);
- AddCircleSegment(alfaLeftLeft + ThreeSixtyRadians*0.25, vaLeft, false, leftCircleCenter, output, turningRadius);
- break;
- case 6:
-
- AddCircleSegment(vaRight, gammaRight, true, rightCircleCenter, output, turningRadius);
- break;
- case 7:
-
- AddCircleSegment(vaLeft, gammaLeft, false, leftCircleCenter, output, turningRadius);
- break;
- }
- }
- }
- [System.Serializable]
-
- 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;
- }
- }
-
- public abstract class TurnConstructor {
-
-
-
-
-
-
- public float constantBias = 0;
-
-
-
-
- public float factorBias = 1;
- public static float turningRadius = 1.0F;
- public const double ThreeSixtyRadians = Math.PI * 2;
- public static Vector3 prev, current, next;
- public static Vector3 t1, t2;
- public static Vector3 normal, prevNormal;
- 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);
-
- 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;
- }
-
- 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;
- }
- }
-
- 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);
- }
- }
-
- 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;
- }
- }
-
- public double GetLengthFromAngle (double angle, double radius) {
- return radius * angle;
- }
-
- public double ClockwiseAngle (double from, double to) {
- return ClampAngle(to - from);
- }
-
- 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);
- }
- }
-
-
- 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;
- }
- }
- }
- }
|