/// <summary> /// Calculates the length of the generated path in world units. /// </summary> /// <param name="from">The start of the segment to calculate the length of</param> /// <param name="to">The end of the segment</param> /// <returns></returns> public virtual float CalculateLength(double from = 0.0, double to = 1.0, EvaluateMode mode = EvaluateMode.Cached) { if (mode == EvaluateMode.Accurate) { return(spline.CalculateLength(from, to)); } float length = 0f; Vector3 pos = EvaluatePosition(from); int sampleIndex = DMath.CeilInt(from * (samples.Length - 1)); int endSampleIndex = DMath.FloorInt(to * (samples.Length - 1)); for (int i = sampleIndex; i < endSampleIndex; i++) { length += Vector3.Distance(samples[i].position, pos); pos = samples[i].position; } length += Vector3.Distance(EvaluatePosition(to), pos); return(length); }
/// <summary> /// Project a world space point onto the spline and write to a SplineSample. /// </summary> /// <param name="point">3D Point in world space</param> /// <param name="result">The SplineSample object to write the result into</param> /// <param name="from">Sample from [0-1] default: 0.0</param> /// <param name="to">Sample to [0-1] default: 1.0</param> /// <returns></returns> public virtual void Project(Vector3 point, SplineSample result, double from = 0.0, double to = 1.0, EvaluateMode mode = EvaluateMode.Cached) { if (mode == EvaluateMode.Accurate) { spline.Evaluate(result, spline.Project(point, 4, from, to)); return; } if (samples.Length == 0) { return; } if (samples.Length == 1) { if (result == null) { result = new SplineSample(samples[0]); } else { result.CopyFrom(samples[0]); } return; } //First make a very rough sample of the from-to region int steps = 2; if (spline != null) { steps = (spline.points.Length - 1) * 6; //Sampling six points per segment is enough to find the closest point range } int step = samples.Length / steps; if (step < 1) { step = 1; } float minDist = (point - samples[0].position).sqrMagnitude; int fromIndex = 0; int toIndex = samples.Length - 1; if (from != 0.0) { fromIndex = DMath.FloorInt(from * (samples.Length - 1)); } if (to != 1.0) { toIndex = Mathf.CeilToInt((float)to * (samples.Length - 1)); } int checkFrom = fromIndex; int checkTo = toIndex; //Find the closest point range which will be checked in detail later for (int i = fromIndex; i <= toIndex; i += step) { if (i > toIndex) { i = toIndex; } float dist = (point - samples[i].position).sqrMagnitude; if (dist < minDist) { minDist = dist; checkFrom = Mathf.Max(i - step, 0); checkTo = Mathf.Min(i + step, samples.Length - 1); } if (i == toIndex) { break; } } minDist = (point - samples[checkFrom].position).sqrMagnitude; int index = checkFrom; //Find the closest result within the range for (int i = checkFrom + 1; i <= checkTo; i++) { float dist = (point - samples[i].position).sqrMagnitude; if (dist < minDist) { minDist = dist; index = i; } } //Project the point on the line between the two closest samples int backIndex = index - 1; if (backIndex < 0) { backIndex = 0; } int frontIndex = index + 1; if (frontIndex > samples.Length - 1) { frontIndex = samples.Length - 1; } Vector3 back = LinearAlgebraUtility.ProjectOnLine(samples[backIndex].position, samples[index].position, point); Vector3 front = LinearAlgebraUtility.ProjectOnLine(samples[index].position, samples[frontIndex].position, point); float backLength = (samples[index].position - samples[backIndex].position).magnitude; float frontLength = (samples[index].position - samples[frontIndex].position).magnitude; float backProjectDist = (back - samples[backIndex].position).magnitude; float frontProjectDist = (front - samples[frontIndex].position).magnitude; if (backIndex < index && index < frontIndex) { if ((point - back).sqrMagnitude < (point - front).sqrMagnitude) { SplineSample.Lerp(samples[backIndex], samples[index], backProjectDist / backLength, result); } else { SplineSample.Lerp(samples[frontIndex], samples[index], frontProjectDist / frontLength, result); } } else if (backIndex < index) { SplineSample.Lerp(samples[backIndex], samples[index], backProjectDist / backLength, result); } else { SplineSample.Lerp(samples[frontIndex], samples[index], frontProjectDist / frontLength, result); } }
/// <summary> /// Returns the percent from the spline at a given distance from the start point /// </summary> /// <param name="start">The start point</param> /// <param name="distance">The distance to travel</param> /// <param name="direction">The direction towards which to move</param> /// <param name="traveled">Returns the distance actually traveled. Usually equal to distance but could be less if the travel distance exceeds the remaining spline length.</param> /// <param name="mode">If set to EvaluateMode.Accurate, the actual spline will be evaluated instead of the cached samples.</param> /// <returns></returns> public virtual double Travel(double start, float distance, Spline.Direction direction, out float traveled, EvaluateMode mode = EvaluateMode.Cached) { traveled = 0f; if (mode == EvaluateMode.Accurate) { return(spline.Travel(start, distance, direction)); } if (samples.Length <= 1) { return(0.0); } if (direction == Spline.Direction.Forward && start >= 1.0) { return(1.0); } else if (direction == Spline.Direction.Backward && start <= 0.0) { return(0.0); } if (distance == 0f) { return(DMath.Clamp01(start)); } Vector3 lastPosition = EvaluatePosition(start); double lastPercent = start; int nextSampleIndex = direction == Spline.Direction.Forward ? DMath.CeilInt(start * (samples.Length - 1)) : DMath.FloorInt(start * (samples.Length - 1)); float lastDistance = 0f; while (true) { lastDistance = Vector3.Distance(samples[nextSampleIndex].position, lastPosition); lastPosition = samples[nextSampleIndex].position; traveled += lastDistance; if (traveled >= distance) { break; } lastPercent = samples[nextSampleIndex].percent; if (direction == Spline.Direction.Forward) { if (nextSampleIndex == samples.Length - 1) { break; } nextSampleIndex++; } else { if (nextSampleIndex == 0) { break; } nextSampleIndex--; } } return(DMath.Lerp(lastPercent, samples[nextSampleIndex].percent, 1f - (traveled - distance) / lastDistance)); }
/// <summary> /// 将板块的相对坐标转换成 /// </summary> /// <returns></returns> public Point PointFToPoint(PointF coord, SizeF unit, PointF parent = default(PointF), EvaluateMode mode = EvaluateMode.Round) { PointF p = new PointF(parent.X, parent.Y); p.X += coord.X * unit.Width; p.Y += coord.Y * unit.Height; switch (mode) { case EvaluateMode.Truncate: return(Point.Truncate(p)); case EvaluateMode.Celling: return(Point.Ceiling(p)); case EvaluateMode.Round: return(Point.Round(p)); case EvaluateMode.Floor: return(new Point((int)Math.Floor(p.X), (int)Math.Floor(p.Y))); default: break; } return(Point.Truncate(p)); }