mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2024-11-15 09:01:40 +00:00
291 lines
9.1 KiB
C#
291 lines
9.1 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using CodeImp.DoomBuilder.Map;
|
||
|
using System.Collections.ObjectModel;
|
||
|
using CodeImp.DoomBuilder.IO;
|
||
|
|
||
|
//JBR Tracks tracer
|
||
|
namespace CodeImp.DoomBuilder.Geometry
|
||
|
{
|
||
|
public class TracksTracer
|
||
|
{
|
||
|
private List<List<Vector2D>> openpaths;
|
||
|
private List<List<Vector2D>> closepaths;
|
||
|
private bool isvalid;
|
||
|
|
||
|
public List<List<Vector2D>> OpenPaths { get { return openpaths; } }
|
||
|
public List<List<Vector2D>> ClosePaths { get { return closepaths; } }
|
||
|
public bool IsValid { get { return isvalid; } }
|
||
|
|
||
|
#region ==== Public methods ====
|
||
|
|
||
|
/// <summary>
|
||
|
/// Create tracks tracer class
|
||
|
/// </summary>
|
||
|
/// <param name="lines">Linedefs to trace</param>
|
||
|
public TracksTracer(ICollection<Linedef> lines)
|
||
|
{
|
||
|
isvalid = true;
|
||
|
|
||
|
// Get all vertices connected to the linedefs
|
||
|
List<Vertex> allvertices = new List<Vertex>();
|
||
|
foreach (Linedef ld in lines)
|
||
|
{
|
||
|
if (!allvertices.Contains(ld.Start)) allvertices.Add(ld.Start);
|
||
|
if (!allvertices.Contains(ld.End)) allvertices.Add(ld.End);
|
||
|
}
|
||
|
|
||
|
// Create a list of valid vertices
|
||
|
List<Vertex> vertices = new List<Vertex>();
|
||
|
foreach (Vertex v in allvertices)
|
||
|
{
|
||
|
List<Linedef> list = GetSelectedLinedefFromVertex(lines, v);
|
||
|
if (list.Count == 1 || list.Count == 2) vertices.Add(v);
|
||
|
}
|
||
|
|
||
|
// Scan for open paths
|
||
|
openpaths = new List<List<Vector2D>>();
|
||
|
List<Vector2D> processed = new List<Vector2D>(); // for holding processed locations
|
||
|
foreach (Vertex v in vertices)
|
||
|
{
|
||
|
if (!processed.Contains(v.Position))
|
||
|
{
|
||
|
List<Linedef> list1 = GetSelectedLinedefFromVertex(lines, v);
|
||
|
if (list1.Count == 1)
|
||
|
{
|
||
|
List<Vector2D> path = new List<Vector2D>();
|
||
|
processed.Add(v.Position);
|
||
|
path.Add(v.Position);
|
||
|
Vertex v2 = (list1[0].Start == v) ? list1[0].End : list1[0].Start;
|
||
|
bool reverse = (list1[0].Start == v);
|
||
|
do
|
||
|
{
|
||
|
processed.Add(v2.Position);
|
||
|
path.Add(v2.Position);
|
||
|
List<Linedef> list2 = GetSelectedLinedefFromVertex(lines, v2);
|
||
|
if (list2.Count == 1)
|
||
|
{
|
||
|
// Linedef continue on the opposide side
|
||
|
v2 = (list2[0].Start == v2) ? list2[0].End : list2[0].Start;
|
||
|
}
|
||
|
else if (list2.Count == 2)
|
||
|
{
|
||
|
// Linedef continue in one of the opposide sides
|
||
|
Vertex v3 = (list2[0].Start == v2) ? list2[0].End : list2[0].Start;
|
||
|
if (processed.Contains(v3.Position))
|
||
|
{
|
||
|
v2 = (list2[1].Start == v2) ? list2[1].End : list2[1].Start;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
v2 = v3;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Collided with a 3+ way vertex, no good...
|
||
|
isvalid = false;
|
||
|
break;
|
||
|
}
|
||
|
} while (!processed.Contains(v2.Position));
|
||
|
if (reverse) path.Reverse();
|
||
|
openpaths.Add(path);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Scan for close paths
|
||
|
closepaths = new List<List<Vector2D>>();
|
||
|
foreach (Vertex v in vertices)
|
||
|
{
|
||
|
if (!processed.Contains(v.Position))
|
||
|
{
|
||
|
List<Linedef> list1 = GetSelectedLinedefFromVertex(lines, v);
|
||
|
List<Vector2D> path = new List<Vector2D>();
|
||
|
processed.Add(v.Position);
|
||
|
path.Add(v.Position);
|
||
|
Vertex v2 = (list1[0].Start == v) ? list1[0].End : list1[0].Start;
|
||
|
do
|
||
|
{
|
||
|
processed.Add(v2.Position);
|
||
|
path.Add(v2.Position);
|
||
|
List<Linedef> list2 = GetSelectedLinedefFromVertex(lines, v2);
|
||
|
if (list2.Count == 2)
|
||
|
{
|
||
|
// Linedef continue in one of the opposide sides
|
||
|
Vertex v3 = (list2[0].Start == v2) ? list2[0].End : list2[0].Start;
|
||
|
if (processed.Contains(v3.Position))
|
||
|
{
|
||
|
v2 = (list2[1].Start == v2) ? list2[1].End : list2[1].Start;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
v2 = v3;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Collided with a 3+ way vertex, no good...
|
||
|
isvalid = false;
|
||
|
break;
|
||
|
}
|
||
|
} while (!processed.Contains(v2.Position));
|
||
|
path.Add(path[0]); // Close it by looping back to 0
|
||
|
if (!InsideCheckFlip(path)) path.Reverse(); // Reverse if parallel was created outside
|
||
|
closepaths.Add(path);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Parallelize open-path, used to create roads
|
||
|
/// </summary>
|
||
|
/// <param name="path">Path in OpenPaths</param>
|
||
|
/// <param name="distance">Distance between parallels / Width of the track</param>
|
||
|
/// <param name="flip">Flip to the other side?</param>
|
||
|
/// <returns>Parallel path</returns>
|
||
|
public static List<Vector2D> ParallelizeOpenPath(List<Vector2D> path, float distance, bool flip)
|
||
|
{
|
||
|
List<Vector2D> parallel = new List<Vector2D>();
|
||
|
if (path.Count < 2) return parallel;
|
||
|
parallel.Add(GetPerpendicular3(path[0], path[0], path[1], distance, flip));
|
||
|
int i = 2;
|
||
|
for (; i < path.Count; i++)
|
||
|
{
|
||
|
parallel.Add(GetPerpendicular3(path[i - 2], path[i - 1], path[i], distance, flip));
|
||
|
}
|
||
|
parallel.Add(GetPerpendicular3(path[i - 2], path[i - 1], path[i - 1], distance, flip));
|
||
|
return parallel;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Parallelize close-path, used to create roads
|
||
|
/// </summary>
|
||
|
/// <param name="path">Path in ClosePaths</param>
|
||
|
/// <param name="distance">Distance between parallels / Width of the track</param>
|
||
|
/// <param name="flip">Flip to the other side?</param>
|
||
|
/// <returns>Parallel path</returns>
|
||
|
public static List<Vector2D> ParallelizeClosePath(List<Vector2D> path, float distance, bool flip)
|
||
|
{
|
||
|
List<Vector2D> parallel = new List<Vector2D>();
|
||
|
if (path.Count < 2) return parallel;
|
||
|
int i = path.Count;
|
||
|
parallel.Add(GetPerpendicular3(path[i - 2], path[0], path[1], distance, flip));
|
||
|
for (i = 2; i < path.Count; i++)
|
||
|
{
|
||
|
parallel.Add(GetPerpendicular3(path[i - 2], path[i - 1], path[i], distance, flip));
|
||
|
}
|
||
|
parallel.Add(GetPerpendicular3(path[i - 2], path[i - 1], path[1], distance, flip));
|
||
|
return parallel;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Add path into DrawnVertex list
|
||
|
/// </summary>
|
||
|
/// <param name="dvl">DrawnVertex list</param>
|
||
|
/// <param name="path">Path to add</param>
|
||
|
/// <param name="stitch">Stitch?</param>
|
||
|
/// <param name="stitchline">Stitchline?</param>
|
||
|
/// <returns>Number of elements added</returns>
|
||
|
public static int AddPathToDrawnVertex(List<DrawnVertex> dvl, List<Vector2D> path, bool stitch, bool stitchline)
|
||
|
{
|
||
|
int elements = 0;
|
||
|
DrawnVertex newpoint = new DrawnVertex(); // struct
|
||
|
newpoint.stitch = stitch;
|
||
|
newpoint.stitchline = stitchline;
|
||
|
foreach (Vector2D pos in path)
|
||
|
{
|
||
|
if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary ||
|
||
|
pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary)
|
||
|
continue;
|
||
|
|
||
|
newpoint.pos = pos;
|
||
|
dvl.Add(newpoint);
|
||
|
elements++;
|
||
|
}
|
||
|
return elements;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Add point into DrawnVertex list
|
||
|
/// </summary>
|
||
|
/// <param name="dvl">DrawnVertex list</param>
|
||
|
/// <param name="pos">Point to add</param>
|
||
|
/// <param name="stitch">Stitch?</param>
|
||
|
/// <param name="stitchline">Stitchline?</param>
|
||
|
/// <returns>Number of elements added, 1 if success</returns>
|
||
|
public static int AddPointToDrawnVertex(List<DrawnVertex> dvl, Vector2D pos, bool stitch, bool stitchline)
|
||
|
{
|
||
|
if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary ||
|
||
|
pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary)
|
||
|
return 0;
|
||
|
|
||
|
DrawnVertex newpoint = new DrawnVertex();
|
||
|
newpoint.pos = pos;
|
||
|
newpoint.stitch = stitch;
|
||
|
newpoint.stitchline = stitchline;
|
||
|
dvl.Add(newpoint);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region ==== Private methods ====
|
||
|
|
||
|
private bool InsideCheckFlip(List<Vector2D> track)
|
||
|
{
|
||
|
if (track.Count < 2) return false;
|
||
|
|
||
|
// Calculate median point
|
||
|
double x = 0, y = 0;
|
||
|
foreach (Vector2D v in track)
|
||
|
{
|
||
|
x += v.x;
|
||
|
y += v.y;
|
||
|
}
|
||
|
x /= track.Count;
|
||
|
y /= track.Count;
|
||
|
Vector2D median = new Vector2D((float)x, (float)y);
|
||
|
|
||
|
// See which side is the first track line
|
||
|
Line2D line = new Line2D(track[0], track[1]);
|
||
|
return line.GetSideOfLine(median) >= 0;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get selected linedefs from a vertex
|
||
|
/// </summary>
|
||
|
/// <param name="lines">Collection of lines</param>
|
||
|
/// <param name="v">Vertex to get linedefs from</param>
|
||
|
/// <returns>List of all selected linedefs</returns>
|
||
|
private List<Linedef> GetSelectedLinedefFromVertex(ICollection<Linedef> lines, Vertex v)
|
||
|
{
|
||
|
List<Linedef> list = new List<Linedef>();
|
||
|
foreach (Linedef ld in v.Linedefs)
|
||
|
{
|
||
|
if (lines.Contains(ld)) list.Add(ld);
|
||
|
}
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Create line on the center vertex that is perpendicular to left and right vertices
|
||
|
/// </summary>
|
||
|
/// <param name="left">Left vertex</param>
|
||
|
/// <param name="center">Center vertex</param>
|
||
|
/// <param name="right">Right vertex</param>
|
||
|
/// <param name="scale">Scale of the perpendicular line</param>
|
||
|
/// <param name="backwards">Should perpendicular go backwards?</param>
|
||
|
/// <returns>Position of the perpendicular</returns>
|
||
|
private static Vector2D GetPerpendicular3(Vector2D left, Vector2D center, Vector2D right, float scale, bool backwards)
|
||
|
{
|
||
|
Vector2D deltapos = (right - center).GetNormal() - (left - center).GetNormal();
|
||
|
if (backwards) deltapos = -deltapos;
|
||
|
Vector2D normalunit = deltapos.GetPerpendicular().GetNormal();
|
||
|
return center + normalunit * scale;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|