Removed NioTSO client and server

- NioTSO client isn't needed because we're using RayLib
- Added FreeSO's API server to handle most backend operations
This commit is contained in:
Tony Bark 2024-05-01 02:55:43 -04:00
parent f12ba1502b
commit 22191ce648
591 changed files with 53264 additions and 3362 deletions

View file

@ -0,0 +1,241 @@
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);
}
}
}

View file

@ -0,0 +1,161 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml;
namespace FSO.Common.WorldGeometry.Paths
{
public enum SVGPathSegmentType
{
MoveTo,
LineTo,
CurveTo,
Close
}
public class SVGPathSegment
{
public SVGPathSegmentType Type;
public Vector2 Position;
public Vector2 ControlPoint1;
public Vector2 ControlPoint2;
}
public class SVGPath
{
public string ID;
public List<SVGPathSegment> Segments;
public SVGPath(string id, List<SVGPathSegment> segs)
{
ID = id;
Segments = segs;
}
}
public class SVGParser
{
public List<SVGPath> Paths;
public SVGParser(string svgText)
{
var xml = new XmlDocument();
xml.XmlResolver = null;
xml.LoadXml(svgText);
Paths = new List<SVGPath>();
var paths = xml.GetElementsByTagName("path");
foreach (XmlNode path in paths)
{
var str = path.Attributes["d"].InnerText.Replace(',', ' ');
int template = 0;
var id = path.Attributes["id"]?.InnerText;
var elems = str.Split(' ');
var pos = new Vector2(0, 0);
var newPath = new List<SVGPathSegment>();
for (int i = 0; i < elems.Length; i += 0)
{
var type = elems[i++];
if (type.Length == 0) continue;
var relative = char.IsLower(type[0]);
if (!relative) pos = new Vector2();
switch (type.ToLower())
{
case "m":
case "l":
//lineto
pos += new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture));
newPath.Add(new SVGPathSegment()
{
Position = pos,
Type = (type.ToLower() == "l") ? SVGPathSegmentType.LineTo : SVGPathSegmentType.MoveTo
});
break;
case "c":
var cp1 = new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture)) + pos;
var cp2 = new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture)) + pos;
pos += new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture));
newPath.Add(new SVGPathSegment()
{
Position = pos,
ControlPoint1 = cp1,
ControlPoint2 = cp2,
Type = SVGPathSegmentType.CurveTo
});
break;
case "z":
//close
newPath.Add(new SVGPathSegment()
{
Type = SVGPathSegmentType.Close
});
break;
}
}
Paths.Add(new SVGPath(id, newPath));
}
}
public LinePath ToLinePath(SVGPath inpath)
{
var segs = inpath.Segments;
var line = new List<Vector2>();
var closed = false;
var pos = new Vector2(0, 0);
foreach (var seg in segs)
{
switch (seg.Type)
{
case SVGPathSegmentType.MoveTo:
case SVGPathSegmentType.LineTo:
line.Add(seg.Position);
break;
case SVGPathSegmentType.CurveTo:
//subdivided curve. currently 20 subdivisions.
var subdiv = 20;
var lastPos = line.Last();
for (int i=1; i<subdiv; i++)
{
var t = i / (float)subdiv;
var s = 1 - t;
line.Add(new Vector2(
(s * s * s) * lastPos.X + 3 * (s * s * t) * seg.ControlPoint1.X + 3 * (s * t * t) * seg.ControlPoint2.X + (t * t * t) * seg.Position.X,
(s * s * s) * lastPos.Y + 3 * (s * s * t) * seg.ControlPoint1.Y + 3 * (s * t * t) * seg.ControlPoint2.Y + (t * t * t) * seg.Position.Y
));
}
break;
case SVGPathSegmentType.Close:
//finish at the start.
if (line.First() != line.Last()) line.Add(line.First());
closed = true;
break;
}
}
var path = new LinePath(line);
if (inpath.ID != null && inpath.ID.StartsWith("template"))
{
path.TemplateNum = int.Parse(inpath.ID.Substring(8));
}
if (closed && path.Segments.Count > 0) {
var first = path.Segments.First();
var last = path.Segments.Last();
first.StartNormal = last.EndNormal = (first.StartNormal + last.EndNormal) / 2;
path.SharpEnd = true;
path.SharpStart = true;
}
return path;
}
public List<LinePath> ToLinePaths()
{
return Paths.Select(x => ToLinePath(x)).ToList();
}
}
}