Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using Pathfinding;
- using UnityEngine;
- using System.Collections.Generic;
- using Pathfinding.Drawing;
- using Pathfinding.Util;
- using UnityEngine.Profiling;
- using Unity.Mathematics;
- using static Pathfinding.Drawing.Palette.Colorbrewer.Set1;
- using Palette = Pathfinding.Drawing.Palette.Colorbrewer.Set1;
- using Unity.Collections;
- public class TurnPalette : VersionedMonoBehaviour {
- public Transform start;
- public Transform end;
- public float curvature = 1;
- public int steps = 3;
- Seeker seeker;
- public int maxCorners = 1000;
- Filter filter = new Filter();
- Follower.PIDFollowerState state;
- public Follower.PIDFollower follower = Follower.PIDFollower.Default();
- public float timeLimit = 10;
- PathInterpolator interpolator = new PathInterpolator();
- protected override void Awake () {
- base.Awake();
- seeker = GetComponent<Seeker>();
- }
- public override void DrawGizmos () {
- if (!Application.isPlaying) {
- AstarPath.FindAstarPath();
- seeker = GetComponent<Seeker>();
- Search();
- // Tangents((Vector2)start.position, start.localScale.x, (Vector2)end.position, end.localScale.x);
- // Tangents((Vector2)start.position, start.localScale.x, (Vector2)end.position, -end.localScale.x);
- // Tangents((Vector2)start.position, -start.localScale.x, (Vector2)end.position, end.localScale.x);
- // Tangents((Vector2)start.position, -start.localScale.x, (Vector2)end.position, -end.localScale.x);
- // var buffer = new NativeList<TurnStraightTurnPath>(Allocator.Temp);
- // Profiler.BeginSample("TurnPalette");
- // var localToWorld = float4x4.TRS(transform.position, quaternion.EulerXZY(new float3(math.radians(90), 0, 0)), 1);
- // var worldToLocal = math.inverse(localToWorld);
- // var startPos = math.transform(worldToLocal, start.position).xy;
- // var endPos = math.transform(worldToLocal, end.position).xy;
- // var startTangent = math.mul(worldToLocal, new float4(start.right, 0)).xy;
- // var endTangent = math.mul(worldToLocal, new float4(end.right, 0)).xy;
- // TurnStraightTurnPath.Calculate(
- // startPos, startTangent, math.min(start.localScale.x, math.length(endPos - startPos)*0.5f),
- // endPos, endTangent, math.min(end.localScale.x, math.length(endPos - startPos)*0.5f),
- // buffer
- // );
- // buffer.Sort();
- // buffer.Add(new TurnStraightTurnPath {
- // length = math.length(endPos - startPos),
- // startCenter = startPos,
- // endCenter = endPos,
- // startRadius = 0,
- // endRadius = 0,
- // startAngle1 = math.atan2(startTangent.y, startTangent.x),
- // startAngle2 = math.atan2(endPos.y - startPos.y, endPos.x - startPos.x),
- // endAngle1 = math.atan2(endPos.y - startPos.y, endPos.x - startPos.x),
- // endAngle2 = math.atan2(endTangent.y, endTangent.x),
- // });
- // bool found = false;
- // for (int i = 0; i < buffer.Length; i++) {
- // if (Validate(AstarPath.active.data.layerGridGraph, buffer[i], localToWorld)) {
- // using(Draw.WithMatrix(localToWorld)) {
- // using(Draw.WithColor(!found ? Palette.Green : Palette.Blue)) {
- // using(Draw.WithLineWidth(found ? 1 : 2)) {
- // buffer[i].Plot();
- // }
- // }
- // }
- // found = true;
- // } else {
- // using(Draw.WithMatrix(localToWorld)) {
- // using(Draw.WithColor(Palette.Red)) {
- // using(Draw.WithLineWidth(1)) {
- // buffer[i].Plot();
- // }
- // }
- // }
- // }
- // }
- // Profiler.EndSample();
- }
- }
- public static bool ValidateInsideFunnel<C>(Funnel.FunnelPortals funnelPortals, C curve, int startSegment, int endSegment) where C : Follower2.ICurve {
- if (funnelPortals.left == null || funnelPortals.right == null) return true;
- var nextExpectedSegment = startSegment + 1;
- for (int k = 0; k < 10; k++) {
- var a = curve.Eval(k/10.0f);
- var b = curve.Eval((k+1)/10.0f);
- for (int j = startSegment + 1; j <= endSegment; j++) {
- var sa = funnelPortals.left[j];
- var sb = funnelPortals.right[j];
- if (VectorMath.LineIntersectionFactorXZ(a, b, sa, sb, out float factor1, out float factor2)) {
- if (factor1 >= 0 && factor1 <= 1) {
- if ((factor2 < 0 || factor2 > 1)) {
- // The curve intersects the portal outside the required range. This is invalid.
- Draw.Line(sa + Vector3.up*0.1f, sb + Vector3.up*0.1f, Color.red);
- return false;
- } else {
- if (j == nextExpectedSegment) {
- nextExpectedSegment++;
- }
- Draw.Line(sa + Vector3.up*0.1f, sb + Vector3.up*0.1f, Color.green);
- }
- }
- }
- }
- }
- return nextExpectedSegment == endSegment || nextExpectedSegment == endSegment + 1;
- }
- static bool Validate (IRaycastableGraph graph, TurnStraightTurnPath turn, float4x4 localToWorld) {
- var a = math.transform(localToWorld, new float3(turn.straightStart, 0));
- var b = math.transform(localToWorld, new float3(turn.straightEnd, 0));
- if (graph.Linecast(a, b)) {
- // Draw.Line(a, b, Palette.Red);
- return false;
- } else {
- // Draw.Line(a, b, Palette.Green);
- }
- return true;
- }
- public struct TurnStraightTurnPath : System.IComparable<TurnStraightTurnPath>, Follower2.ICurve {
- public float length;
- public float additionalCost;
- public float2 startCenter;
- public float2 endCenter;
- public float startRadius;
- public float endRadius;
- public float startAngle1;
- public float startAngle2;
- public float endAngle1;
- public float endAngle2;
- public float2 straightStart {
- get
- {
- return startCenter + startRadius * new float2(math.cos(startAngle2), math.sin(startAngle2));
- }
- }
- public float2 straightEnd {
- get
- {
- return endCenter + endRadius * new float2(math.cos(endAngle1), math.sin(endAngle1));
- }
- }
- public float arcLength => length;
- public void Plot () {
- Draw.xy.Circle(new float3(startCenter, 0), startRadius, startAngle1, startAngle2);
- Draw.Line(new float3(straightStart, 0), new float3(straightEnd, 0));
- Draw.xy.Circle(new float3(endCenter, 0), endRadius, endAngle1, endAngle2);
- }
- public static void CalculateSingleArc (float2 startPos, float2 startTangent, float2 endPos, NativeList<TurnStraightTurnPath> buffer) {
- var arc = CalculateSingleArc(startPos, startTangent, endPos);
- if (arc != null) {
- buffer.Add(arc.Value);
- }
- }
- public static TurnStraightTurnPath? CalculateBiarc (float2 startPos, float2 startTangent, float2 endPos, float2 endTangent) {
- startTangent = math.normalizesafe(startTangent);
- endTangent = math.normalizesafe(endTangent);
- var dir = (endPos - startPos)*0.5f;
- var dirAngle = math.atan2(dir.y, dir.x);
- var startAngle = math.atan2(startTangent.y, startTangent.x);
- var endAngle = math.atan2(endTangent.y, endTangent.x);
- var halfDist = math.length(dir);
- var p = 1;
- var omega = ((startAngle - dirAngle) + (endAngle - dirAngle)) * 0.5f;
- var sinOmega = math.sin(omega);
- var startCurvature = math.rcp(halfDist) * (math.sin(startAngle - dirAngle) + math.rcp(p)*sinOmega);
- var endCurvature = math.rcp(halfDist) * (math.sin(endAngle - dirAngle) + p*sinOmega);
- var startSignedRadius = math.rcp(startCurvature);
- var endSignedRadius = math.rcp(endCurvature);
- var startNormal = new float2(startTangent.y, -startTangent.x);
- var endNormal = new float2(endTangent.y, -endTangent.x);
- var startCenter = startPos + startNormal * startSignedRadius;
- var endCenter = endPos + endNormal * startSignedRadius;
- var startAngle1 = startAngle + math.sign(startSignedRadius)*math.PI*0.5f;
- var startDeltaAngle = startCurvature;
- return new TurnStraightTurnPath {
- startCenter = startCenter,
- endCenter = endCenter,
- startAngle1 = startAngle1,
- startAngle2 = startAngle + math.sign(startSignedRadius)*math.PI*0.5f,
- // endAngle1 = startAngle + halfDeltaAngle,
- // endAngle2 = startAngle + 2*halfDeltaAngle,
- // startRadius = math.abs(startSignedRadius),
- // endRadius = math.abs(endSignedRadius),
- // length = radius * 2*math.abs(halfDeltaAngle),
- };
- }
- public static TurnStraightTurnPath? CalculateSingleArc (float2 startPos, float2 startTangent, float2 endPos) {
- var d = (endPos - startPos) * 0.5f;
- var cosAlpha = math.dot(startTangent, d) / math.sqrt(math.lengthsq(startTangent) * math.lengthsq(d));
- if (cosAlpha < 0) {
- return null;
- }
- var side = math.sign(d.x*startTangent.y - d.y*startTangent.x);
- var sinAlpha = math.sqrt(1 - cosAlpha*cosAlpha) * side;
- var tanInvAlpha = cosAlpha/sinAlpha;
- var centerRelativeToStart = d + new float2(d.y, -d.x) * tanInvAlpha;
- var radius = math.length(centerRelativeToStart);
- var startAngle = math.atan2(startTangent.y, startTangent.x) + side*0.5f*math.PI;
- var halfDeltaAngle = -math.asin(sinAlpha);
- var center = startPos + centerRelativeToStart;
- using (Draw.WithMatrix(float4x4.TRS(float3.zero, quaternion.EulerXZY(new float3(math.radians(90), 0, 0)), 1))) {
- // Draw.xy.Circle(new float3(center, 0), radius, Color.red);
- // Draw.Line(new float3(startPos, 0), new float3(endPos, 0), Color.red);
- // Draw.Line(new float3((startPos + d), 0), new float3(center, 0), Color.magenta);
- }
- return new TurnStraightTurnPath {
- startCenter = center,
- endCenter = center,
- // Note: it's not strictly necessary to spread out the progress over both the start circle and end circle
- // but it makes for a slightly more well behaved derivative function with respect to t.
- startAngle1 = startAngle,
- startAngle2 = startAngle + halfDeltaAngle,
- endAngle1 = startAngle + halfDeltaAngle,
- endAngle2 = startAngle + 2*halfDeltaAngle,
- startRadius = radius,
- endRadius = radius,
- length = radius * 2*math.abs(halfDeltaAngle),
- };
- }
- public static TurnStraightTurnPath? Calculate (float2 startCircleCenter, float startRadius, float2 endCircleCenter, float endRadius, float startAngle, float endAngle, int startDir, int endDir) {
- // Draw.xy.Circle(new float3(c1, 0), startRadius, Color.black);
- // Draw.xy.Circle(new float3(c2, 0), endRadius, Red);
- var normal = Tangents(startCircleCenter, startRadius * startDir, endCircleCenter, endRadius * endDir);
- if (normal == null) return null;
- var startNormal = normal.Value * startRadius * startDir;
- var endNormal = normal.Value * endRadius * endDir;
- // Draw.Line((Vector2)(c1 + startNormal), (Vector2)(c2 + endNormal), colors[(startDir+1) + (endDir+1)/2]);
- var sAngle1 = startAngle;
- var sAngle2 = math.atan2(startNormal.y, startNormal.x);
- var eAngle1 = math.atan2(endNormal.y, endNormal.x);
- var eAngle2 = endAngle;
- OrderAngles(sAngle1, ref sAngle2, startDir == -1);
- OrderAngles(eAngle1, ref eAngle2, endDir == -1);
- var startArc = math.abs(sAngle2 - sAngle1);
- var endArc = math.abs(eAngle2 - eAngle1);
- var straightDistance = math.length((startCircleCenter + startNormal) - (endCircleCenter + endNormal));
- var totalLength = startArc * startRadius + endArc * endRadius + straightDistance;
- return new TurnStraightTurnPath {
- length = totalLength,
- startCenter = startCircleCenter,
- endCenter = endCircleCenter,
- startAngle1 = sAngle1,
- startAngle2 = sAngle2,
- endAngle1 = eAngle1,
- endAngle2 = eAngle2,
- startRadius = startRadius,
- endRadius = endRadius,
- };
- }
- public static void Calculate (float2 startPos, float2 startTangent, float startRadius, float2 endPos, float2 endTangent, float endRadius, NativeList<TurnStraightTurnPath> buffer) {
- var startRight = new float2(startTangent.y, -startTangent.x);
- var endRight = new float2(endTangent.y, -endTangent.x);
- // Draw.Line((Vector2)startPos, (Vector2)(startPos + normal*r1), Purple);
- // Draw.Line((Vector2)c2, (Vector2)(c2 + normal*r2), Purple);
- // Draw.Line((Vector2)(startPos + normal*r1), (Vector2)(c2 + normal*r2), Purple);
- // RR, RL, LR, LL
- var startAngle = math.atan2(startTangent.y, startTangent.x);
- var endAngle = math.atan2(endTangent.y, endTangent.x);
- var colors = new[] { Red, Orange, Blue, Purple };
- for (var startDir = -1; startDir <= 1; startDir += 2) {
- for (var endDir = -1; endDir <= 1; endDir += 2) {
- var c1 = startPos - startRight * startDir * startRadius;
- var c2 = endPos - endRight * endDir * endRadius;
- var turn = Calculate(
- c1, startRadius,
- c2, endRadius,
- startAngle - math.PI * 0.5f * startDir,
- endAngle - math.PI * 0.5f * endDir,
- startDir,
- endDir
- );
- if (turn != null) {
- buffer.Add(turn.Value);
- }
- // Debug.Log(startDir + " " + endDir + " " + math.degrees(l1) + " " + math.degrees(l2) + ": " + math.degrees(startAngle - math.PI*0.5f*startDir) + " " + math.degrees(math.atan2(startNormal.y, startNormal.x)));
- // Draw.xy.Circle(new float3(c1, -0.1f), startRadius, startAngle - math.PI*0.5f*startDir, math.atan2(startNormal.y, startNormal.x), colors[(startDir+1) + (endDir+1)/2]);
- }
- }
- }
- public float Cost {
- get {
- if (math.all(this.startCenter == this.endCenter)) {
- // Single arc
- return this.length * 0.9f + additionalCost;
- } else {
- return this.length + additionalCost;
- }
- }
- }
- public int CompareTo (TurnStraightTurnPath other) {
- return this.Cost.CompareTo(other.Cost);
- }
- public Vector3 Eval (float t) {
- t = math.clamp(t, 0, 1);
- if (t < 0.25f) {
- t = t * 4;
- float angle = math.lerp(startAngle1, startAngle2, t);
- return new Vector3(this.startCenter.x, 0, this.startCenter.y) + new Vector3(math.cos(angle) * startRadius, 0, math.sin(angle) * startRadius);
- } else if (t < 0.75f) {
- t = (t - 0.25f)*2;
- var p = math.lerp(this.straightStart, this.straightEnd, t);
- return new Vector3(p.x, 0, p.y);
- } else {
- t = (t - 0.75f)*4;
- float angle = math.lerp(endAngle1, endAngle2, t);
- return new Vector3(this.endCenter.x, 0, this.endCenter.y) + new Vector3(math.cos(angle) * endRadius, 0, math.sin(angle) * endRadius);
- }
- }
- public void Plot (Color color) {
- using (Draw.WithColor(color)) {
- this.Plot();
- }
- }
- public Vector3 Derivative (float t) {
- t = math.clamp(t, 0, 1);
- if (t < 0.25f && startAngle2 != startAngle1) {
- var duDt = 4;
- var u = t * duDt;
- float angle = math.lerp(startAngle1, startAngle2, u);
- var dangleDu = startAngle2 - startAngle1;
- var dpDangle = new Vector3(-math.sin(angle) * startRadius, 0, math.cos(angle) * startRadius);
- return dpDangle * dangleDu * duDt; // = dp/dt
- } else if (t < 0.75f) {
- var duDt = 2;
- var d = (this.straightEnd - this.straightStart)*duDt;
- return new Vector3(d.x, 0, d.y);
- } else {
- var duDt = 4;
- var u = (t - 0.75f) * duDt;
- float angle = math.lerp(endAngle1, endAngle2, u);
- var dangleDu = endAngle2 - endAngle1;
- var dpDangle = new Vector3(-math.sin(angle) * endRadius, 0, math.cos(angle) * endRadius);
- return dpDangle * dangleDu * duDt; // = dp/dt
- }
- }
- public Vector3 SecondDerivative (float t) {
- t = math.clamp(t, 0, 1);
- if (t < 0.25f && startAngle2 != startAngle1) {
- var duDt = 4;
- var u = t * duDt;
- float angle = math.lerp(startAngle1, startAngle2, u);
- var dangleDu = startAngle2 - startAngle1;
- var dpDangle2 = new Vector3(-math.cos(angle) * startRadius, 0, -math.sin(angle) * startRadius);
- Debug.Log(t + " " + startAngle1 + " " + startAngle2 + " " + startRadius + " " + dpDangle2 + " " + dangleDu + " " + duDt);
- return dpDangle2 * dangleDu * dangleDu * duDt * duDt; // = dp/dt^2
- } else if (t < 0.75f) {
- return Vector3.zero;
- } else {
- var duDt = 4;
- var u = (t-0.75f) * duDt;
- float angle = math.lerp(endAngle1, endAngle2, u);
- var dangleDu = endAngle2 - endAngle1;
- var dpDangle2 = new Vector3(-math.cos(angle) * endRadius, 0, -math.sin(angle) * endRadius);
- return dpDangle2 * dangleDu * dangleDu * duDt * duDt; // = dp/dt^2
- }
- }
- public Vector3 IntegrateCurvature (float arcLength) {
- var lstart = Mathf.Abs(startAngle2 - startAngle1) * startRadius;
- var lend = Mathf.Abs(endAngle2 - endAngle1) * endRadius;
- var lstraight = this.length - lstart - lend;
- var cstart = Curvature(0);
- var cend = Curvature(1);
- Debug.Log("Integrate " + arcLength + " of " + length + " (" + lstart + ", " + lstraight + " " + lend + ") " + Mathf.Clamp01(arcLength / lstart) + " " + Mathf.Clamp01((arcLength - lstart - lstraight) / lend));
- return
- cstart * (Mathf.Clamp01(arcLength / lstart) * lstart)
- + cend * (Mathf.Clamp01((arcLength - lstart - lstraight) / lend) * lend);
- }
- public Vector3 Curvature (float t) {
- // TODO: Can be much simpler based on the start/end radii
- var xp = Derivative(t);
- var xpp = SecondDerivative(t);
- var dx = xp.magnitude;
- if (dx < 0.000001f) return Vector3.zero;
- return Vector3.Cross(xp, xpp) / (dx*dx*dx);
- }
- public Follower2.ICurve FitWithStartPoint (Vector3 startPoint, Vector3 normalizedTangent) {
- if (math.all(this.startCenter == this.endCenter)) {
- // Single arc
- var v = CalculateSingleArc(new float3(startPoint).xz, new float3(normalizedTangent).xz, new float3(this.Eval(1)).xz);
- if (v != null) return v.Value;
- }
- var tangentAngle = math.atan2(normalizedTangent.z, normalizedTangent.x);
- var normal = new float2(normalizedTangent.z, -normalizedTangent.x);
- var startDir = this.startAngle2 > this.startAngle1 ? 1 : -1;
- var endDir = this.endAngle2 > this.endAngle1 ? 1 : -1;
- var startAngle = tangentAngle - startDir * math.PI * 0.5f;
- if (startDir == 1) normal = -normal;
- var startCenter = new float3(startPoint).xz + normal * startRadius;
- var curve = Calculate(startCenter, startRadius, endCenter, endRadius, startAngle, endAngle2, startDir, endDir);
- if (curve != null) {
- return curve;
- } else {
- var maxRad = math.length(endCenter - startCenter) * 0.25f;
- return Calculate(startCenter, maxRad, endCenter, maxRad, startAngle, endAngle2, startDir, endDir);
- }
- }
- }
- static void OrderAngles (float a1, ref float a2, bool cw) {
- var delta = AngleDifference(a1, a2, cw);
- a2 = a1 + delta * (cw ? -1 : 1);
- }
- static float AngleDifference (float a1, float a2, bool cw) {
- if (cw) {
- var tmp = a1;
- a1 = a2;
- a2 = tmp;
- }
- var delta = a2 - a1;
- delta = math.fmod(delta, math.PI * 2);
- if (delta < 0.0f) delta += math.PI * 2;
- return delta;
- }
- void Update () {
- Search();
- }
- void Search () {
- var p = seeker.StartPath(this.start.position, this.end.position, null);
- p.BlockUntilCalculated();
- seeker.startEndModifier.Apply(p);
- if (p.error) return;
- filter.path = p;
- bool filterIsRequired = filter.path.traversalProvider != null || filter.path.enabledTags != ~0;
- var filterDelegate = filterIsRequired ? filter.cachedDelegate : null;
- var result = GridStringPulling.Calculate(p.path, 0, p.path.Count - 1, p.vectorPath[0], p.vectorPath[p.vectorPath.Count - 1], (GraphNode node) => 0, filterDelegate, maxCorners);
- p.Claim(this);
- p.Release(this);
- var localToWorld = float4x4.TRS(transform.position, quaternion.EulerXZY(new float3(math.radians(90), 0, 0)), 1);
- var worldToLocal = math.inverse(localToWorld);
- var shortestPath = new List<float2>(result.Count);
- for (int i = 0; i < result.Count; i++) {
- shortestPath.Add(math.transform(worldToLocal, result[i]).xy);
- }
- var startTangent = math.mul(worldToLocal, new float4(this.start.forward, 0)).xy;
- var endTangent = math.mul(worldToLocal, new float4(this.end.forward, 0)).xy;
- var funnelPortals = new Funnel.FunnelPortals {
- left = null,
- right = null,
- };
- FindBestCurves(shortestPath, funnelPortals, this.start.localScale.x, startTangent, endTangent, 0, 4, AstarPath.active.data.layerGridGraph, localToWorld);
- }
- public static List<TurnStraightTurnPath> FindBestCurves (List<float2> shortestPath, Funnel.FunnelPortals funnelPortals, float turningRadius, float2 startTangent, float2 endTangent, int startIndex, int maxCorners, IRaycastableGraph graph, float4x4 localToWorld) {
- var n = Mathf.Min(maxCorners, shortestPath.Count - startIndex);
- var lowestCosts = ArrayPool<float>.Claim(n);
- var positions = ArrayPool<float2>.Claim(n);
- var tangents = ArrayPool<float2>.Claim(n);
- var tangentPriors = ArrayPool<float2>.Claim(n);
- var best = ArrayPool<TurnStraightTurnPath>.Claim(n);
- var parents = ArrayPool<int>.Claim(n);
- for (int i = 1; i < lowestCosts.Length; i++) lowestCosts[i] = float.PositiveInfinity;
- lowestCosts[0] = 0;
- for (int i = startIndex; i < startIndex + n; i++) {
- float2 tangent;
- if (i == 0) tangent = shortestPath[startIndex + i + 1] - shortestPath[i];
- else if (i == shortestPath.Count - 1) tangent = shortestPath[i] - shortestPath[i - 1];
- else tangent = math.normalizesafe(shortestPath[i + 1] - shortestPath[i]) - math.normalizesafe(shortestPath[i - 1] - shortestPath[i]);
- tangent = math.normalizesafe(tangent);
- tangentPriors[i - startIndex] = tangent; // math.mul(worldToLocal, new float4(tangent, 0)).xy;
- positions[i - startIndex] = shortestPath[i]; // math.transform(worldToLocal, shortestPath[i]).xy;
- }
- tangentPriors[0] = startTangent;
- if (n == shortestPath.Count - startIndex) {
- tangentPriors[n - 1] = endTangent;
- }
- var buffer = new NativeList<TurnStraightTurnPath>(Allocator.Temp);
- for (int i = 0; i < n; i++) {
- const int MaxForwardStep = 3;
- for (int j = i + 1; j < Mathf.Min(i + MaxForwardStep + 1, n); j++) {
- buffer.Clear();
- var startPos = positions[i];
- var endPos = positions[j];
- var radius1 = math.min(turningRadius, math.length(endPos - startPos) * 0.25f);
- var radius2 = math.min(turningRadius, math.length(endPos - startPos) * 0.25f);
- var tangent = i > 0 ? math.normalizesafe(new float3(best[i].Derivative(1)).xz) : tangentPriors[i];
- TurnStraightTurnPath.Calculate(
- startPos, tangent, radius1,
- endPos, tangentPriors[j], radius2,
- buffer
- );
- var singleArc = TurnPalette.TurnStraightTurnPath.CalculateSingleArc(
- startPos,
- tangent,
- endPos
- );
- if (singleArc.HasValue) {
- var arc = singleArc.Value;
- // +0% if in the right direction, +100% if 90 degree wrong, +200% if completely wrong direction.
- arc.additionalCost = arc.Cost * (1 - math.dot(math.normalizesafe(new float3(arc.Derivative(1.0f)).xz), tangentPriors[j]));
- buffer.Add(arc);
- }
- if (j == i + 1) {
- buffer.Add(new TurnStraightTurnPath {
- length = math.length(endPos - startPos) * 2 + 1,
- startCenter = startPos,
- endCenter = endPos,
- startRadius = 0,
- endRadius = 0,
- startAngle1 = math.atan2(tangent.y, tangent.x),
- startAngle2 = math.atan2(endPos.y - startPos.y, endPos.x - startPos.x),
- endAngle1 = math.atan2(endPos.y - startPos.y, endPos.x - startPos.x),
- endAngle2 = math.atan2(tangentPriors[j].y, tangentPriors[j].x),
- });
- }
- buffer.Sort();
- for (int k = 0; k < buffer.Length; k++) {
- var costAtEnd = lowestCosts[i] + buffer[k].Cost;
- if (costAtEnd < lowestCosts[j]) {
- if (ValidateInsideFunnel(funnelPortals, buffer[k], startIndex + i, startIndex + j) && (graph == null || Validate(graph, buffer[k], localToWorld))) {
- using (Draw.WithMatrix(localToWorld))
- {
- // using (Draw.WithColor(!found ? Palette.Green : Palette.Blue))
- // {
- // using (Draw.WithLineWidth(found ? 1 : 2))
- // {
- // buffer[i].Plot();
- // }
- // }
- using (Draw.WithColor(Palette.Blue))
- {
- // using (Draw.WithLineWidth(found ? 1 : 2))
- // {
- buffer[k].Plot();
- // }
- }
- }
- lowestCosts[j] = costAtEnd;
- best[j] = buffer[k];
- parents[j] = i;
- } else {
- using (Draw.WithMatrix(localToWorld))
- {
- using (Draw.WithColor(Palette.Red))
- {
- buffer[k].Plot();
- }
- }
- }
- } else {
- using (Draw.WithMatrix(localToWorld))
- {
- using (Draw.WithColor(Palette.Orange * new Color(1, 1, 1, 0.2f)))
- {
- buffer[k].Plot();
- }
- }
- // Buffer was sorted, so we can never succeed again
- // break;
- }
- }
- }
- }
- var result = new List<TurnStraightTurnPath>();
- Debug.Log("Best cost " + lowestCosts[n - 1]);
- if (!float.IsPositiveInfinity(lowestCosts[n - 1])) {
- var c = n - 1;
- while (c != 0) {
- result.Add(best[c]);
- using (Draw.WithMatrix(localToWorld)) {
- using (Draw.WithColor(Palette.Green))
- {
- using (Draw.WithLineWidth(2))
- {
- best[c].Plot();
- }
- }
- }
- c = parents[c];
- }
- }
- result.Reverse();
- return result;
- // var tangent1 = i > 0 ? shortestPath[i + 1] - shortestPath[i - 1] : shortestPath[1] - shortestPath[0];
- // var tangent2 = i + 2 < shortestPath.Count ? shortestPath[i + 2] - shortestPath[i] : shortestPath[i + 1] - shortestPath[i];
- // tangent1 = tangent1.normalized;
- // tangent2 = tangent2.normalized;
- // var start = shortestPath[i];
- // var end = shortestPath[i+1];
- // var radius1 = i == 0 ? this.start.localScale.x : this.end.localScale.x;
- // var radius2 = this.end.localScale.x;
- // var startTangent = math.mul(worldToLocal, new float4(tangent1, 0)).xy;
- // var endTangent = math.mul(worldToLocal, new float4(tangent2, 0)).xy;
- // }
- // for (int j = 0; j < shortestPath.Count - 1; j++)
- // {
- // var buffer = new NativeList<TurnStraightTurnPath>(Allocator.Temp);
- // Profiler.BeginSample("TurnPalette");
- // var startPos = math.transform(worldToLocal, start).xy;
- // var endPos = math.transform(worldToLocal, end).xy;
- // radius1 = math.min(radius1, math.length(endPos - startPos) * 0.5f);
- // radius2 = math.min(radius2, math.length(endPos - startPos) * 0.5f);
- // TurnStraightTurnPath.Calculate(
- // startPos, startTangent, radius1,
- // endPos, endTangent, radius2,
- // buffer
- // );
- // buffer.Sort();
- // buffer.Add(new TurnStraightTurnPath
- // {
- // length = math.length(endPos - startPos),
- // startCenter = startPos,
- // endCenter = endPos,
- // startRadius = 0,
- // endRadius = 0,
- // startAngle1 = math.atan2(startTangent.y, startTangent.x),
- // startAngle2 = math.atan2(endPos.y - startPos.y, endPos.x - startPos.x),
- // endAngle1 = math.atan2(endPos.y - startPos.y, endPos.x - startPos.x),
- // endAngle2 = math.atan2(endTangent.y, endTangent.x),
- // });
- // bool found = false;
- // for (int i = 0; i < buffer.Length; i++)
- // {
- // if (Validate(AstarPath.active.data.layerGridGraph, buffer[i], localToWorld))
- // {
- // using (Draw.WithMatrix(localToWorld))
- // {
- // using (Draw.WithColor(!found ? Palette.Green : Palette.Blue))
- // {
- // using (Draw.WithLineWidth(found ? 1 : 2))
- // {
- // buffer[i].Plot();
- // }
- // }
- // }
- // found = true;
- // }
- // else
- // {
- // // using (Draw.WithMatrix(localToWorld))
- // // {
- // // using (Draw.WithColor(Palette.Red))
- // // {
- // // using (Draw.WithLineWidth(1))
- // // {
- // // buffer[i].Plot();
- // // }
- // // }
- // // }
- // }
- // }
- // Profiler.EndSample();
- // }
- }
- class Filter {
- public Path path;
- public readonly System.Func<GraphNode, bool> cachedDelegate;
- public Filter() {
- cachedDelegate = this.CanTraverse;
- }
- bool CanTraverse (GraphNode node) {
- return path.CanTraverse(node);
- }
- }
- float lastT = 0;
- void OnPathComplete (Path _p) {
- seeker.startEndModifier.Apply(_p);
- var p = _p as ABPath;
- if (p.error) return;
- filter.path = p;
- bool filterIsRequired = filter.path.traversalProvider != null || filter.path.enabledTags != ~0;
- var filterDelegate = filterIsRequired ? filter.cachedDelegate : null;
- var result = GridStringPulling.Calculate(p.path, 0, p.path.Count - 1, p.vectorPath[0], p.vectorPath[p.vectorPath.Count - 1], (GraphNode node) => 0, filterDelegate, maxCorners);
- p.Claim(this);
- p.Release(this);
- Draw.Polyline(result, new Color(1, 1, 1, 0.2f));
- var output = Calculate(result, Follower.PIDFollower.Direction(state.direction), end.forward, curvature, steps, out float firstCurvature);
- Profiler.BeginSample("Validate original");
- Validate(p.path[0].Graph as GridGraph, result);
- Profiler.EndSample();
- bool valid = Validate(p.path[0].Graph as GridGraph, output);
- Draw.Polyline(output, valid ? Color.black : Color.red);
- ListPool<Vector3>.Release(ref result);
- interpolator.SetPath(output);
- if ((state.t > timeLimit || !state.cursor.valid)) {
- state = new Follower.PIDFollowerState {
- p = transform.position,
- direction = 0,
- t = 0,
- cursor = interpolator.start,
- };
- Debug.Log("Error " + follower.totalError);
- follower.totalError = 0;
- }
- // state.interpolator.distance = d;
- var prev = state;
- var dt = Application.isPlaying ? Time.deltaTime : Time.realtimeSinceStartup - lastT;
- lastT = Time.realtimeSinceStartup;
- follower.fixedCurvature = firstCurvature;
- follower.Eval(ref state, dt);
- if (float.IsNaN(state.p.x)) {
- Debug.LogError("NaN");
- state = prev;
- }
- Debug.Log(state.p);
- Draw.Line(prev.p, state.p, Color.blue);
- using (Draw.WithDuration(5))
- {
- Draw.xz.Circle(state.p, 0.01f, Color.blue);
- }
- Draw.xz.Circle(state.p, 0.008f, Color.magenta);
- }
- public static bool Validate (GridGraph graph, List<Vector3> points) {
- Profiler.BeginSample("Validate");
- var node = graph.GetNearest(points[0], null).node as GridNodeBase;
- var normalized = node.NormalizePoint(points[0]);
- for (int i = 1; i < points.Count; i++) {
- var nextNode = graph.GetNearest(points[i], null).node as GridNodeBase;
- var nextNormalized = nextNode.NormalizePoint(points[i]);
- GridHitInfo hit;
- if (graph.Linecast(node, normalized, nextNode, nextNormalized, out hit)) {
- Draw.Ray((Vector3)hit.node.position, Vector3.up, Color.red);
- return false;
- }
- node = nextNode;
- normalized = nextNormalized;
- }
- Profiler.EndSample();
- return true;
- }
- public static List<Vector3> Calculate (List<Vector3> points, Vector3 tangentStart, Vector3 tangentEnd, float desiredCurvature, int steps, out float firstCurvature) {
- Draw.Polyline(points);
- var output = new List<Vector3>();
- firstCurvature = 0;
- for (int i = 0; i < points.Count - 1; i++) {
- var t0 = i == 0 ? tangentStart : points[i + 1] - points[i - 1];
- t0 = t0.normalized;
- var t1 = i >= points.Count - 2 ? tangentEnd : points[i + 2] - points[i];
- t1 = t1.normalized;
- var normal0 = i == 0 ? Vector3.zero : 2 * points[i] - (points[i + 1] + points[i - 1]);
- var normal1 = i >= points.Count - 2 ? Vector3.zero : 2 * points[i + 1] - (points[i + 2] + points[i]);
- var p0 = points[i] + normal0.normalized * 0.001f;
- var p3 = points[i + 1] + normal1.normalized * 0.001f;
- // var p2 = p3 - t1;
- // tau = |6*t2*(p2 - 2*p1 + p0) + 6*t*(p3 - 2*p2 + p1)|
- // tau(0) = |6*(p2 - 2*p1 + p0)| = |6*(p2 - 2*(p0 + t0*x) + p0)| = |6*p2 - 12*p0 - 12*t0*x + p0|
- // = |6*p2 - 11*p0 - 12*t0*x| = (6*p2 - 11*p0 - 12*t0*x)*(6*p2 - 11*p0 - 12*t0*x)
- // 121*p0^2 - 132*p0*p2 + 264*p0*t0*x + 36*p2^2 - 144*p2*t0*x + 144*t0^2*x^2
- // (121*p0^2 - 132*p0*p2 + 36*p2^2) + (264*p0 - 144*p2)*t0*x + 144*t0^2*x^2
- // dx/dt = 3*t2*t2*(p1-p0) + 6*t2*t*(p2 - p1) + 3*t*t*(p3 - p2)
- // dx(0)/dt = 3*t0*x
- // (121*p0^2 - 132*p0*p2 + 36*p2^2) + (264*p0 - 144*p2)*t0*x + 144*t0^2*x^2 = desiredCurvature * (9*t0^2*x*x)
- // var c = (121*p0.sqrMagnitude - 132*Vector3.Dot(p0, p2) + 36*p2.sqrMagnitude);
- // var b = Vector3.Dot(264*p0 - 144*p2, t0);
- // var a = 144*t0.sqrMagnitude - desiredCurvature*9*t0.sqrMagnitude;
- // var disc = Mathf.Sqrt(b*b - 4*a*c);
- // Debug.Log(b*b - 4*a*c);
- // var x1 = (-b + disc)/(2*a);
- // var x2 = (-b - disc)/(2*a);
- // var x = Mathf.Max(x1, x2);
- // 6*p2 - 6*p0 - 12*t0*x
- // cross(6*p2 - 6*p0 - 12*t0*x, 3*t0*x) / (27*x^3)
- // d/dx cross(6*p2 - 6*p0 - 12*t0*x, t0/(x^2*9))
- // = (cross(d(6*p2 - 6*p0 - 12*t0*x)/dx, t0/(x^2*9)) + cross(6*p2 - 6*p0 - 12*t0*x, -2*t0/(x^3*9)))
- // = (cross(-12*t0, t0/(x^2*9)) + cross(6*p2 - 6*p0 - 12*t0*x, -2*t0/(x^3*9)))
- // = cross(6*p2 - 6*p0 - 12*t0*x6, -2*t0/(x^3*9)))
- // = cross(6*p2 - 6*p0, -2*t0/(x^3*9)))
- float x0 = 1;
- float x1 = 1;
- var p1 = p0 + t0 * x0;
- var p2 = p3 - t1 * x1;
- for (int k = 0; k < 4; k++) {
- float t = (k % 2) == 0 ? 0 : 1;
- for (int j = 0; j < steps; j++) {
- var derivative = AstarSplines.CubicBezierDerivative(p0, p1, p2, p3, t);
- var secondDerivative = AstarSplines.CubicBezierSecondDerivative(p0, p1, p2, p3, t);
- var curvature = Vector3.Cross(derivative, secondDerivative) / Mathf.Pow(derivative.magnitude, 3);
- Vector3 curvatureDerivative;
- if ((k % 2) == 0) {
- curvatureDerivative = Vector3.Cross(6 * p2 - 6 * p0, -2 * t0) / (x0 * x0 * x0 * 9);
- x0 += (curvature.magnitude - desiredCurvature) / curvatureDerivative.magnitude * 0.2f;
- x0 = Mathf.Clamp(x0, 0.01f, 100);
- } else {
- curvatureDerivative = Vector3.Cross(-6 * p3 + 6 * p1, -2 * t1) / (x1 * x1 * x1 * 9);
- x1 += (curvature.magnitude - desiredCurvature) / curvatureDerivative.magnitude * 0.2f;
- x1 = Mathf.Clamp(x1, 0.01f, 100);
- }
- x1 = 1;
- x0 = 1;
- // 6*t2*(p2 - 2*p1 + p0) + 6*t*(p3 - 2*p2 + p1
- p1 = p0 + t0 * x0;
- p2 = p3 - t1 * x1;
- }
- }
- {
- Draw.Line(p0, p1, Color.blue);
- Draw.Line(p2, p3, Color.blue);
- // Draw.Bezier(p0, p1, p2, p3, Color.black);
- // Draw.Bezier(p0, p0 + t0, p3 - t1, p3, Color.magenta);
- }
- if (i == 0) {
- var derivative = AstarSplines.CubicBezierDerivative(p0, p1, p2, p3, 0);
- var secondDerivative = AstarSplines.CubicBezierSecondDerivative(p0, p1, p2, p3, 0);
- var curvature = Vector3.Cross(derivative, secondDerivative) / Mathf.Pow(derivative.magnitude, 3);
- Debug.Log("Curvature: " + curvature);
- firstCurvature = curvature.y;
- }
- for (int j = 0; j <= 50; j++) {
- output.Add(AstarSplines.CubicBezier(p0, p1, p2, p3, j / 50f));
- }
- }
- return output;
- }
- struct Arc {
- }
- struct TangentsInfo {
- public float2 normal;
- }
- static float2? Tangents (float2 c1, float r1, float2 c2, float r2) {
- // L, R
- // L, L
- // R, L
- // R, R
- float2 c = c2 - c1;
- float r = r2 - r1;
- float z = math.lengthsq(c);
- float d = z - r * r;
- if (d < -0.00001f) return null;
- d = math.sqrt(math.abs(d));
- var normal = (-c * r + new float2(c.y, -c.x) * d) / z;
- return normal;
- // Draw.Line((Vector2)c1, (Vector2)(c1 + normal*r1), Purple);
- // Draw.Line((Vector2)c2, (Vector2)(c2 + normal*r2), Purple);
- // Draw.Line((Vector2)(c1 + normal*r1), (Vector2)(c2 + normal*r2), Purple);
- // Draw.xy.Circle(new float3(c1, 0), r1, Pure.Black);
- // Draw.xy.Circle(new float3(c2, 0), r2, Red);
- // line l;
- // l.a = (c.x * r + c.y * d) / z;
- // l.b = (c.y * r - c.x * d) / z;
- // l.c = r1;
- // ans.push_back (l);
- }
- // public static List<Vector3> Calculate(List<Vector3> points, Vector3 tangentStart, Vector3 tangentEnd, float desiredCurvature, int steps, out float firstCurvature) {
- // }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement