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> openpaths; private List> closepaths; private bool isvalid; public List> OpenPaths { get { return openpaths; } } public List> ClosePaths { get { return closepaths; } } public bool IsValid { get { return isvalid; } } #region ==== Public methods ==== /// /// Create tracks tracer class /// /// Linedefs to trace public TracksTracer(ICollection lines) { isvalid = true; // Get all vertices connected to the linedefs List allvertices = new List(); 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 vertices = new List(); foreach (Vertex v in allvertices) { List list = GetSelectedLinedefFromVertex(lines, v); if (list.Count == 1 || list.Count == 2) vertices.Add(v); } // Scan for open paths openpaths = new List>(); List processed = new List(); // for holding processed locations foreach (Vertex v in vertices) { if (!processed.Contains(v.Position)) { List list1 = GetSelectedLinedefFromVertex(lines, v); if (list1.Count == 1) { List path = new List(); 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 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>(); foreach (Vertex v in vertices) { if (!processed.Contains(v.Position)) { List list1 = GetSelectedLinedefFromVertex(lines, v); List path = new List(); 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 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); } } } /// /// Parallelize open-path, used to create roads /// /// Path in OpenPaths /// Distance between parallels / Width of the track /// Flip to the other side? /// Parallel path public static List ParallelizeOpenPath(List path, float distance, bool flip) { List parallel = new List(); 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; } /// /// Parallelize close-path, used to create roads /// /// Path in ClosePaths /// Distance between parallels / Width of the track /// Flip to the other side? /// Parallel path public static List ParallelizeClosePath(List path, float distance, bool flip) { List parallel = new List(); 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; } /// /// Add path into DrawnVertex list /// /// DrawnVertex list /// Path to add /// Stitch? /// Stitchline? /// Number of elements added public static int AddPathToDrawnVertex(List dvl, List 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; } /// /// Add point into DrawnVertex list /// /// DrawnVertex list /// Point to add /// Stitch? /// Stitchline? /// Number of elements added, 1 if success public static int AddPointToDrawnVertex(List 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 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; } /// /// Get selected linedefs from a vertex /// /// Collection of lines /// Vertex to get linedefs from /// List of all selected linedefs private List GetSelectedLinedefFromVertex(ICollection lines, Vertex v) { List list = new List(); foreach (Linedef ld in v.Linedefs) { if (lines.Contains(ld)) list.Add(ld); } return list; } /// /// Create line on the center vertex that is perpendicular to left and right vertices /// /// Left vertex /// Center vertex /// Right vertex /// Scale of the perpendicular line /// Should perpendicular go backwards? /// Position of the perpendicular 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 } }