mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-20 16:51:21 +00:00
242 lines
8 KiB
C#
242 lines
8 KiB
C#
|
using Microsoft.Xna.Framework;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
|
|||
|
namespace FSO.Common.WorldGeometry.Paths
|
|||
|
{
|
|||
|
public class LinePath
|
|||
|
{
|
|||
|
public List<LinePathSegment> Segments = new List<LinePathSegment>();
|
|||
|
public bool SharpStart;
|
|||
|
public bool SharpEnd;
|
|||
|
public int TemplateNum;
|
|||
|
|
|||
|
public float StartOffset;
|
|||
|
public float Length;
|
|||
|
|
|||
|
public LinePath()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public LinePath(List<Vector2> line)
|
|||
|
{
|
|||
|
for (int i=0; i<line.Count-1; i++)
|
|||
|
{
|
|||
|
var seg = new LinePathSegment(line[i], line[i + 1]);
|
|||
|
Length += seg.Length;
|
|||
|
Segments.Add(seg);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Tuple<Vector2, Vector2> GetPositionNormalAt(float offset)
|
|||
|
{
|
|||
|
foreach (var seg in Segments)
|
|||
|
{
|
|||
|
//is the given offset in this segment?
|
|||
|
if (offset < seg.Length)
|
|||
|
{
|
|||
|
var i = offset / seg.Length;
|
|||
|
return new Tuple<Vector2, Vector2>(Vector2.Lerp(seg.Start, seg.End, i), Vector2.Lerp(seg.StartNormal, seg.EndNormal, i));
|
|||
|
}
|
|||
|
offset -= seg.Length;
|
|||
|
}
|
|||
|
var last = Segments.Last();
|
|||
|
return new Tuple<Vector2, Vector2>(last.End, last.EndNormal);
|
|||
|
}
|
|||
|
|
|||
|
public List<LinePath> Split(float dist, float gap)
|
|||
|
{
|
|||
|
var result = new List<LinePath>();
|
|||
|
var startGap = dist - gap / 2;
|
|||
|
var endGap = dist + gap / 2;
|
|||
|
|
|||
|
bool before = 0 < startGap;
|
|||
|
LinePath current = new LinePath();
|
|||
|
if (before)
|
|||
|
{
|
|||
|
current.SharpStart = SharpStart;
|
|||
|
current.SharpEnd = true;
|
|||
|
current.StartOffset = StartOffset;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
current.SharpStart = true;
|
|||
|
current.SharpEnd = SharpEnd;
|
|||
|
current.StartOffset = StartOffset + endGap;
|
|||
|
}
|
|||
|
current.TemplateNum = TemplateNum;
|
|||
|
|
|||
|
float soFar = 0;
|
|||
|
foreach (var segment in Segments)
|
|||
|
{
|
|||
|
if (before)
|
|||
|
{
|
|||
|
if (soFar + segment.Length <= startGap)
|
|||
|
{
|
|||
|
//add this segment
|
|||
|
current.Segments.Add(segment);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//this segment extends over the gap.
|
|||
|
//an additional segment must be added to reach the start gap
|
|||
|
if (soFar != startGap && segment.Length != 0)
|
|||
|
{
|
|||
|
var bridge = new LinePathSegment(segment.Start, Vector2.Lerp(segment.Start, segment.End, (startGap - soFar) / segment.Length));
|
|||
|
bridge.StartNormal = segment.StartNormal;
|
|||
|
current.Segments.Add(bridge);
|
|||
|
}
|
|||
|
|
|||
|
current.Length = current.Segments.Sum(x => x.Length);
|
|||
|
result.Add(current);
|
|||
|
current = new LinePath();
|
|||
|
current.SharpStart = true;
|
|||
|
current.SharpEnd = SharpEnd;
|
|||
|
current.StartOffset = StartOffset + endGap;
|
|||
|
current.TemplateNum = TemplateNum;
|
|||
|
before = false;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!before)
|
|||
|
{
|
|||
|
if (current.Segments.Count == 0)
|
|||
|
{
|
|||
|
//waiting to get to a segment that ends after the gap.
|
|||
|
if (soFar + segment.Length > endGap)
|
|||
|
{
|
|||
|
var bridge = new LinePathSegment(Vector2.Lerp(segment.Start, segment.End, (endGap - soFar) / segment.Length), segment.End);
|
|||
|
bridge.EndNormal = segment.EndNormal;
|
|||
|
current.Segments.Add(bridge);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//add this segment
|
|||
|
current.Segments.Add(segment);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
soFar += segment.Length;
|
|||
|
}
|
|||
|
current.Length = current.Segments.Sum(x => x.Length);
|
|||
|
result.Add(current);
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public List<Vector3> Intersections(LinePath other)
|
|||
|
{
|
|||
|
var epsilon = (0.9f * 0.9f) / 0.5f;
|
|||
|
|
|||
|
//finds intersections between this linepath and another.
|
|||
|
var result = new List<Vector3>();
|
|||
|
float soFar = 0;
|
|||
|
for (int i=0; i<Segments.Count; i++)
|
|||
|
{
|
|||
|
var seg1 = Segments[i];
|
|||
|
for (int j=0; j<other.Segments.Count; j++)
|
|||
|
{
|
|||
|
var seg2 = other.Segments[j];
|
|||
|
var inter = seg1.Intersect(seg2);
|
|||
|
|
|||
|
if (inter != null)
|
|||
|
{
|
|||
|
var interc = inter.Value;
|
|||
|
interc.Z += soFar;
|
|||
|
result.Add(interc);
|
|||
|
}
|
|||
|
}
|
|||
|
soFar += seg1.Length;
|
|||
|
}
|
|||
|
|
|||
|
//remove dupes
|
|||
|
result = result.OrderBy(x => x.Z).ToList();
|
|||
|
for (int i = 0; i < result.Count - 1; i++)
|
|||
|
{
|
|||
|
var first = result[i];
|
|||
|
while (i < result.Count - 1)
|
|||
|
{
|
|||
|
var second = result[i + 1];
|
|||
|
var distance = second - first;
|
|||
|
distance.Z = 0;
|
|||
|
if (distance.LengthSquared() < epsilon)
|
|||
|
{
|
|||
|
result.RemoveAt(i);
|
|||
|
}
|
|||
|
else;
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public void PrepareJoins()
|
|||
|
{
|
|||
|
LinePathSegment last = null;
|
|||
|
foreach (var line in Segments)
|
|||
|
{
|
|||
|
if (last != null)
|
|||
|
{
|
|||
|
last.EndNormal = line.StartNormal = Vector2.Normalize(last.EndNormal + line.StartNormal);
|
|||
|
}
|
|||
|
last = line;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class LinePathSegment
|
|||
|
{
|
|||
|
public Vector2 Start;
|
|||
|
public Vector2 End;
|
|||
|
|
|||
|
public Vector2 Direction;
|
|||
|
|
|||
|
//normals are used when constucting geometry from a line. they face to the right from the line.
|
|||
|
//to create a seamless line, we average the end normal of this line and the start normal of the last, setting both to the result.
|
|||
|
public Vector2 StartNormal;
|
|||
|
public Vector2 EndNormal;
|
|||
|
|
|||
|
public float Length;
|
|||
|
|
|||
|
public LinePathSegment(Vector2 start, Vector2 end)
|
|||
|
{
|
|||
|
Start = start;
|
|||
|
End = end;
|
|||
|
Direction = end - start;
|
|||
|
Length = Direction.Length();
|
|||
|
|
|||
|
var dirn = Direction;
|
|||
|
dirn.Normalize();
|
|||
|
StartNormal = EndNormal = new Vector2(-dirn.Y, dirn.X);
|
|||
|
}
|
|||
|
|
|||
|
public Vector3? Intersect(LinePathSegment other) //xy: point, z: distance along line
|
|||
|
{
|
|||
|
if (this.Length == 0 || other.Length == 0) return null;
|
|||
|
var epsilon = 0.0001f;
|
|||
|
|
|||
|
Vector2 a = Direction;
|
|||
|
Vector2 b = other.Direction;
|
|||
|
Vector2 c = Start - other.Start;
|
|||
|
|
|||
|
//percent of line 1 where we intersect with line 2
|
|||
|
float ip = 1 / (-b.X * a.Y + a.X * b.Y); //projection
|
|||
|
float t = (b.X * c.Y - b.Y * c.X) * ip;
|
|||
|
|
|||
|
//percent of line 2 where we intersect line 1
|
|||
|
float ip2 = 1 / (-a.X * b.Y + b.X * a.Y);
|
|||
|
float s = (a.X * (-c.Y) - a.Y * (-c.X)) * ip2;
|
|||
|
|
|||
|
if (float.IsNaN(t) || t < -epsilon || t > 1 + epsilon || float.IsNaN(s) || s < -epsilon || s > 1 + epsilon)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
return new Vector3(Direction * t + Start, t * Length);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|