Skip to content

Commit

Permalink
Merge pull request #126 from OliBomby/dev
Browse files Browse the repository at this point in the history
Dev update 1.7.1.1
  • Loading branch information
OliBomby authored Oct 20, 2020
2 parents 888a2ad + 8d667be commit 4c331fb
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 6 deletions.
37 changes: 37 additions & 0 deletions Mapping Tools/Classes/SliderPathStuff/SliderPathUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,42 @@ public static IEnumerable<BezierSubdivision> ChopAnchorsLinear(List<Vector2> anc
yield return subdivision;
}
}

public static double CalculateLoss(IReadOnlyCollection<Vector2> points, IReadOnlyList<Vector2> labels) {
int n = points.Count;
double totalLoss = 0;

foreach (var point in points) {
var minLoss = Double.PositiveInfinity;

for (int i = 0; i < labels.Count - 1; i++) {
var p1 = labels[i];
var p2 = labels[i + 1];

var loss = MinimumDistance(p1, p2, point);

if (loss < minLoss) {
minLoss = loss;
}
}

totalLoss += minLoss;
}

return totalLoss / n;
}

private static double MinimumDistance(Vector2 v, Vector2 w, Vector2 p) {
// Return minimum distance between line segment vw and point p
double l2 = Vector2.DistanceSquared(v, w); // i.e. |w-v|^2 - avoid a sqrt
if (l2 == 0.0) return Vector2.Distance(p, v); // v == w case
// Consider the line extending the segment, parameterized as v + t (w - v).
// We find projection of point p onto the line.
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
// We clamp t from [0,1] to handle points outside the segment vw.
double t = Math.Max(0, Math.Min(1, Vector2.Dot(p - v, w - v) / l2));
Vector2 projection = v + t * (w - v); // Projection falls on the segment
return Vector2.Distance(p, projection);
}
}
}
84 changes: 80 additions & 4 deletions Mapping Tools/Classes/Tools/PathGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public IEnumerable<Vector2> GeneratePath(double maxAngle = Math.PI * 1 / 4) {
/// <param name="approximationMode"></param>
/// <returns></returns>
public IEnumerable<Vector2> GeneratePath(double startIndex, double endIndex,
double maxAngle = Math.PI * 1 / 4, ApproximationMode approximationMode = ApproximationMode.DoubleMiddle) {
double maxAngle = Math.PI * 1 / 4, ApproximationMode approximationMode = ApproximationMode.Best) {
var segments = GetNonInflectionSegments(startIndex, endIndex, maxAngle);

foreach (var segment in segments) {
Expand All @@ -89,6 +89,9 @@ public IEnumerable<Vector2> GeneratePath(double startIndex, double endIndex,
case ApproximationMode.DoubleMiddle:
middle = DoubleMiddleApproximation(segment.Item1, segment.Item2);
break;
case ApproximationMode.Best:
middle = BestApproximation(segment.Item1, segment.Item2);
break;
default:
middle = null;
break;
Expand All @@ -102,12 +105,55 @@ public IEnumerable<Vector2> GeneratePath(double startIndex, double endIndex,
}
}

private Vector2? BestApproximation(double startIndex, double endIndex) {
// Make sure start index is before end index
// The results will be the same for flipped indices
if (startIndex > endIndex) {
var tempEndIndex = endIndex;
endIndex = startIndex;
startIndex = tempEndIndex;
}

var p1 = GetContinuousPosition(startIndex);
var p2 = GetContinuousPosition(endIndex);

const int numTestPoints = 100;
var labels = _path.GetRange((int) startIndex, (int) Math.Ceiling(endIndex) - (int) startIndex + 1);

Vector2?[] middles = {
TangentIntersectionApproximation(startIndex, endIndex),
DoubleMiddleApproximation(startIndex, endIndex)
};

Vector2? bestMiddle = null;
double bestLoss = double.PositiveInfinity;

foreach (var middle in middles) {
var bezier = new BezierCurveQuadric(p1, p2, middle ?? (p2 - p1) / 2);

var interpolatedPoints = new Vector2[numTestPoints];
for (int i = 0; i < numTestPoints; i++) {
double t = (double) i / (numTestPoints - 1);
interpolatedPoints[i] = bezier.CalculatePoint(t);
}

var loss = SliderPathUtil.CalculateLoss(interpolatedPoints, labels);

if (loss < bestLoss) {
bestLoss = loss;
bestMiddle = middle;
}
}

return bestMiddle;
}

private Vector2? TangentIntersectionApproximation(double startIndex, double endIndex) {
var p1 = GetContinuousPosition(startIndex);
var p2 = GetContinuousPosition(endIndex);

var a1 = GetContinuousAngle(startIndex);
var a2 = GetContinuousAngle(endIndex);
var a2 = GetContinuousAngle(endIndex - 2 * Precision.DOUBLE_EPSILON);

if (Math.Abs(GetSmallestAngle(a1, a2)) > 0.1) {
var t1 = new Line2(p1, a1);
Expand All @@ -128,10 +174,14 @@ public IEnumerable<Vector2> GeneratePath(double startIndex, double endIndex,
var p1 = GetContinuousPosition(startIndex);
var p2 = GetContinuousPosition(endIndex);

var d1 = GetContinuousDistance(startIndex);
var d2 = GetContinuousDistance(endIndex);
var middleIndex = GetIndexAtDistance((d1 + d2) / 2);

var averagePoint = (p1 + p2) / 2;
var middlePoint = GetContinuousPosition((startIndex + endIndex) / 2);
var middlePoint = GetContinuousPosition(middleIndex);

if (Vector2.DistanceSquared(averagePoint, middlePoint) < 1) {
if (Vector2.DistanceSquared(averagePoint, middlePoint) < 0.1) {
return null;
}

Expand Down Expand Up @@ -294,6 +344,31 @@ public double GetContinuousAngle(double index) {
return _angle[segmentIndex];
}

public double GetContinuousDistance(double index) {
int segmentIndex = (int)Math.Floor(index);
double segmentProgression = index - segmentIndex;

return Math.Abs(segmentProgression) < Precision.DOUBLE_EPSILON ?
_pathL[segmentIndex] :
Math.Abs(segmentProgression - 1) < Precision.DOUBLE_EPSILON ?
_pathL[segmentIndex + 1] :
(1 - segmentProgression) * _pathL[segmentIndex] + segmentProgression * _pathL[segmentIndex + 1];
}

public double GetIndexAtDistance(double distance) {
var index = _pathL.BinarySearch(distance);
if (index >= 0) {
return index;
}

var i2 = ~index;
var i1 = i2 - 1;
var d1 = _pathL[i1];
var d2 = _pathL[i2];

return (distance - d1) / (d2 - d1) + i1;
}

private static double Modulo(double a, double n) {
return a - Math.Floor(a / n) * n;
}
Expand Down Expand Up @@ -326,6 +401,7 @@ public static double CalculatePathLength(List<Vector2> anchors) {
public enum ApproximationMode {
TangentIntersection,
DoubleMiddle,
Best
}
}
}
4 changes: 2 additions & 2 deletions Mapping Tools/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.1.0")]
[assembly: AssemblyFileVersion("1.7.1.0")]
[assembly: AssemblyVersion("1.7.1.1")]
[assembly: AssemblyFileVersion("1.7.1.1")]
[assembly: NeutralResourcesLanguage("en")]

0 comments on commit 4c331fb

Please sign in to comment.