mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2024-11-12 23:54:10 +00:00
Internal: threw out a lot of unoptimized code in TraceSector. Now using Tools.FindPotentialSectorAt.
This commit is contained in:
parent
582f457d76
commit
03a1d26302
2 changed files with 21 additions and 425 deletions
|
@ -31,6 +31,7 @@ using CodeImp.DoomBuilder.Types;
|
||||||
using CodeImp.DoomBuilder.Windows;
|
using CodeImp.DoomBuilder.Windows;
|
||||||
using CodeImp.DoomBuilder.VisualModes;
|
using CodeImp.DoomBuilder.VisualModes;
|
||||||
using CodeImp.DoomBuilder.Data;
|
using CodeImp.DoomBuilder.Data;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -2256,7 +2257,14 @@ namespace CodeImp.DoomBuilder.Map
|
||||||
if (ls.Ignore) continue;
|
if (ls.Ignore) continue;
|
||||||
|
|
||||||
// Run sector builder on current edge
|
// Run sector builder on current edge
|
||||||
if(!builder.TraceSector(ls.Line, ls.Front)) continue; // Don't create sector if trace failed
|
Stopwatch watch = new Stopwatch();
|
||||||
|
watch.Start();
|
||||||
|
if (!builder.TraceSector(ls.Line, ls.Front))
|
||||||
|
{
|
||||||
|
General.ErrorLogger.Add(ErrorType.Warning, string.Format("TraceSector: took {0}ms, failed!", watch.ElapsedMilliseconds));
|
||||||
|
continue; // Don't create sector if trace failed
|
||||||
|
}
|
||||||
|
General.ErrorLogger.Add(ErrorType.Warning, string.Format("TraceSector: took {0}ms", watch.ElapsedMilliseconds));
|
||||||
|
|
||||||
// Find any subsequent edges that were part of the sector created
|
// Find any subsequent edges that were part of the sector created
|
||||||
bool has_existing_lines = false;
|
bool has_existing_lines = false;
|
||||||
|
|
|
@ -49,426 +49,14 @@ namespace CodeImp.DoomBuilder.Map
|
||||||
///<summary>Traces all edges to build a closed sector starting from [line]</summary>
|
///<summary>Traces all edges to build a closed sector starting from [line]</summary>
|
||||||
internal bool TraceSector(Linedef line, bool front)
|
internal bool TraceSector(Linedef line, bool front)
|
||||||
{
|
{
|
||||||
if(line == null) return false;
|
// [ZZ] There indeed already was an implementation of this.
|
||||||
|
// To do: make sure this works correctly. I don't understand what the old SectorBuilder was doing exactly, but from the looks of it FindPotentialSectorAt should be ok here.
|
||||||
//DebugConsole.WriteLine(" ");
|
|
||||||
//DebugConsole.WriteLine("TraceSector for line " + line.Index + (front ? " (front)" : " (back)"));
|
|
||||||
|
|
||||||
// Init
|
|
||||||
sector_edges.Clear();
|
sector_edges.Clear();
|
||||||
|
List<LinedefSide> edges = Tools.FindPotentialSectorAt(line, front);
|
||||||
// Create valid vertices list
|
if (edges == null)
|
||||||
vertex_valid = new HashSet<Vertex>(General.Map.Map.Vertices);
|
|
||||||
|
|
||||||
// Find outmost outline
|
|
||||||
for(int a = 0; a < 10000; a++)
|
|
||||||
{
|
|
||||||
// Trace outline
|
|
||||||
if(!TraceOutline(line, front))
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("TraceSector: find outmost outline failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard any vertices outside the traced outline
|
|
||||||
vertex_valid.RemoveWhere(PointOutsideOutline);
|
|
||||||
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after RemoveWhere");
|
|
||||||
|
|
||||||
// If it is clockwise, we've found the outmost outline
|
|
||||||
if(o_clockwise)
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("TraceSector: found outmost outline");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, find the next edge outside the outline
|
|
||||||
LinedefSide next = FindOuterEdge();
|
|
||||||
|
|
||||||
// If none was found, we're outside the map
|
|
||||||
if(next == null)
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("TraceSector aborted: no outer edge");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
sector_edges.AddRange(edges);
|
||||||
|
return sector_edges.Count > 0;
|
||||||
// Repeat with this edge
|
|
||||||
line = next.Line;
|
|
||||||
front = next.Front;
|
|
||||||
}
|
|
||||||
|
|
||||||
//DebugConsole.WriteLine("FindOuterEdge: " + o_edges.Count + " lines");
|
|
||||||
|
|
||||||
// Trace all inner outlines, by tracing from the rightmost vertex
|
|
||||||
// until all vertices have been discarded
|
|
||||||
for(int a = 0; a < 10000; a++)
|
|
||||||
{
|
|
||||||
// Get inner edge
|
|
||||||
LinedefSide edge = FindInnerEdge();
|
|
||||||
|
|
||||||
// Check if we're done
|
|
||||||
if(edge == null)
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("No inner edge found (no edge)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trace outline from edge
|
|
||||||
if(!TraceOutline(edge.Line, edge.Front))
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("No inner edge found (TraceOutline failed)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard any vertices outside the traced outline
|
|
||||||
vertex_valid.RemoveWhere(PointOutsideOutline);
|
|
||||||
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after RemoveWhere");
|
|
||||||
}
|
|
||||||
|
|
||||||
//DebugConsole.WriteLine("FindInnerEdge: " + o_edges.Count + " lines");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
///<summary>Traces the sector outline from lines beginning at [line],
|
|
||||||
/// on either the front or back side ([front])</summary>
|
|
||||||
private bool TraceOutline(Linedef line, bool front)
|
|
||||||
{
|
|
||||||
// Check line was given
|
|
||||||
if(line == null) return false;
|
|
||||||
|
|
||||||
//DebugConsole.WriteLine(" ");
|
|
||||||
//DebugConsole.WriteLine("Tracing line " + line.Index + (front ? " (front)" : " (back)"));
|
|
||||||
|
|
||||||
// Init outline
|
|
||||||
o_edges.Clear();
|
|
||||||
o_bbox = RectangleF.Empty;
|
|
||||||
LinedefSide start = new LinedefSide(line, front);
|
|
||||||
o_edges.Add(start);
|
|
||||||
int edge_sum = 0;
|
|
||||||
Dictionary<Linedef, int> visited_lines = new Dictionary<Linedef, int>();
|
|
||||||
|
|
||||||
// Begin tracing
|
|
||||||
LinedefSide edge = new LinedefSide(line, front);
|
|
||||||
vertex_right = edge.Line.Start;
|
|
||||||
for(int a = 0; a < 10000; a++)
|
|
||||||
{
|
|
||||||
// Update edge sum (for clockwise detection)
|
|
||||||
if(edge.Front)
|
|
||||||
edge_sum += (int)(edge.Line.Start.Position.x * edge.Line.End.Position.y - edge.Line.End.Position.x * edge.Line.Start.Position.y);
|
|
||||||
else
|
|
||||||
edge_sum += (int)(edge.Line.End.Position.x * edge.Line.Start.Position.y - edge.Line.Start.Position.x * edge.Line.End.Position.y);
|
|
||||||
|
|
||||||
// Update rightmost vertex
|
|
||||||
if(edge.Line.Start.Position.x > vertex_right.Position.x)
|
|
||||||
vertex_right = edge.Line.Start;
|
|
||||||
if(edge.Line.End.Position.x > vertex_right.Position.x)
|
|
||||||
vertex_right = edge.Line.End;
|
|
||||||
|
|
||||||
// Get next edge. If no valid next edge was found, go back along the current line
|
|
||||||
LinedefSide edge_next = (NextEdge(edge, visited_lines) ?? new LinedefSide(edge.Line, !edge.Front));
|
|
||||||
|
|
||||||
//DebugConsole.WriteLine("Next line for " + edge.Line.Index + (edge.Front ? " (front)" : " (back)") + ": " + edge_next.Line.Index + (edge_next.Front ? " (front)" : " (back)"));
|
|
||||||
|
|
||||||
// Discard edge vertices
|
|
||||||
vertex_valid.Remove(edge_next.Line.Start);
|
|
||||||
vertex_valid.Remove(edge_next.Line.End);
|
|
||||||
|
|
||||||
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after Remove (in TraceOutline)");
|
|
||||||
|
|
||||||
// Check if we're back to the start
|
|
||||||
if(edge_next.Line == start.Line && edge_next.Front == start.Front)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Add edge to outline
|
|
||||||
o_edges.Add(edge_next);
|
|
||||||
edge.Line = edge_next.Line;
|
|
||||||
edge.Front = edge_next.Front;
|
|
||||||
|
|
||||||
// Update bounding box
|
|
||||||
RectangleF l_bbox = RectangleF.FromLTRB(
|
|
||||||
Math.Min(edge.Line.Start.Position.x, edge.Line.End.Position.x), // left
|
|
||||||
Math.Min(edge.Line.Start.Position.y, edge.Line.End.Position.y), // top
|
|
||||||
Math.Max(edge.Line.Start.Position.x, edge.Line.End.Position.x), // right
|
|
||||||
Math.Max(edge.Line.Start.Position.y, edge.Line.End.Position.y)); // bottom
|
|
||||||
|
|
||||||
//mxd. As it turned out, o_bbox.IsEmpty was not what we needed...
|
|
||||||
o_bbox = (o_bbox == RectangleF.Empty ? l_bbox : RectangleF.Union(o_bbox, l_bbox));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if outline is clockwise
|
|
||||||
o_clockwise = (edge_sum < 0);
|
|
||||||
|
|
||||||
//DebugConsole.WriteLine("TraceOutline for line " + line.Index + " (" + (front ? "front":"back") + ") found " + o_edges.Count + " edges; o_clockwise=" + o_clockwise);
|
|
||||||
|
|
||||||
// Add outline edges to sector edge list
|
|
||||||
sector_edges.AddRange(o_edges);
|
|
||||||
|
|
||||||
// Trace complete
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Finds the next closest edge outside of the current outline (that isn't part of the current outline)</summary>
|
|
||||||
private LinedefSide FindOuterEdge()
|
|
||||||
{
|
|
||||||
// Check we have a rightmost vertex
|
|
||||||
if(vertex_right == null) return null;
|
|
||||||
|
|
||||||
// Init
|
|
||||||
float vr_x = vertex_right.Position.x;
|
|
||||||
float vr_y = vertex_right.Position.y;
|
|
||||||
float min_dist = float.MaxValue;
|
|
||||||
Linedef nearest = null;
|
|
||||||
|
|
||||||
// Fire a ray east from the vertex and find the first line it crosses
|
|
||||||
foreach(Linedef line in General.Map.Map.Linedefs)
|
|
||||||
{
|
|
||||||
// Ignore if the line is completely left of the vertex
|
|
||||||
if(line.Start.Position.x <= vr_x && line.End.Position.x <= vr_x) continue;
|
|
||||||
|
|
||||||
// Ignore horizontal lines
|
|
||||||
if(line.Start.Position.y == line.End.Position.y) continue;
|
|
||||||
|
|
||||||
// Ignore if the line doesn't intersect the y value
|
|
||||||
if((line.Start.Position.y < vr_y && line.End.Position.y < vr_y) ||
|
|
||||||
(line.Start.Position.y > vr_y && line.End.Position.y > vr_y))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get x intercept
|
|
||||||
float int_frac = (vr_y - line.Start.Position.y) / (line.End.Position.y - line.Start.Position.y);
|
|
||||||
float int_x = line.Start.Position.x + ((line.End.Position.x - line.Start.Position.x) * int_frac);
|
|
||||||
float dist = Math.Abs(int_x - vr_x);
|
|
||||||
|
|
||||||
// Check if closest
|
|
||||||
if(nearest == null || dist < min_dist)
|
|
||||||
{
|
|
||||||
min_dist = dist;
|
|
||||||
nearest = line;
|
|
||||||
}
|
|
||||||
else if(Math.Abs(dist - min_dist) < 0.001f)
|
|
||||||
{
|
|
||||||
// In the case of a tie, use the distance to each line as a tiebreaker - this fixes cases where the ray hits a vertex
|
|
||||||
// shared by two lines. Choosing the further line would mean choosing an inner edge, which is clearly wrong.
|
|
||||||
float line_dist = line.SafeDistanceToSq(vertex_right.Position, true);
|
|
||||||
float nearest_dist = nearest.SafeDistanceToSq(vertex_right.Position, true);
|
|
||||||
if(line_dist < nearest_dist)
|
|
||||||
{
|
|
||||||
min_dist = dist;
|
|
||||||
nearest = line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for valid line
|
|
||||||
if(nearest == null) return null;
|
|
||||||
|
|
||||||
// Determine the edge side
|
|
||||||
float side = -nearest.SideOfLine(vertex_right.Position); //mxd. SideOfLine logic is inverted in Slade 3
|
|
||||||
return new LinedefSide(nearest, side > 0); //mxd. The meaning of 0.0 is also inverted!!! (I've spent 2 days figuring this out...)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Find the closest edge within the current outline (that isn't part of the current outline)</summary>
|
|
||||||
private LinedefSide FindInnerEdge()
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("FindInnerEdge: processing " + vertex_valid.Count + " verts");
|
|
||||||
|
|
||||||
// Find rightmost non-discarded vertex
|
|
||||||
vertex_right = null;
|
|
||||||
foreach(Vertex v in vertex_valid)
|
|
||||||
{
|
|
||||||
// Set rightmost if no current rightmost vertex
|
|
||||||
if(vertex_right == null)
|
|
||||||
{
|
|
||||||
vertex_right = v;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the vertex is rightmost
|
|
||||||
if(v.Position.x > vertex_right.Position.x)
|
|
||||||
vertex_right = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no vertex was found, we're done
|
|
||||||
if(vertex_right == null)
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("FindInnerEdge: no vertex_right");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go through vertex's connected lines, to find
|
|
||||||
// the line with the smallest angle parallel with
|
|
||||||
// the right side of the bbox
|
|
||||||
Linedef eline = null;
|
|
||||||
float min_angle = float.MaxValue;
|
|
||||||
foreach(Linedef line in vertex_right.Linedefs)
|
|
||||||
{
|
|
||||||
// Ignore if zero-length
|
|
||||||
if(line.Start == line.End) continue;
|
|
||||||
|
|
||||||
// Get opposite vertex
|
|
||||||
Vertex opposite = (line.Start == vertex_right ? line.End : line.Start);
|
|
||||||
|
|
||||||
// Determine angle
|
|
||||||
float angle = Angle2D.GetAngle(new Vector2D(vertex_right.Position.x + 32, vertex_right.Position.y),
|
|
||||||
new Vector2D(vertex_right.Position.x, vertex_right.Position.y),
|
|
||||||
new Vector2D(opposite.Position.x, opposite.Position.y));
|
|
||||||
|
|
||||||
// Check if minimum
|
|
||||||
if(angle < min_angle)
|
|
||||||
{
|
|
||||||
min_angle = angle;
|
|
||||||
eline = line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no line was found, something is wrong (the vertex may have no attached lines)
|
|
||||||
if(eline == null)
|
|
||||||
{
|
|
||||||
// Discard vertex and try again
|
|
||||||
vertex_valid.Remove(vertex_right);
|
|
||||||
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after Remove (in FindInnerEdge)");
|
|
||||||
|
|
||||||
return FindInnerEdge();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine appropriate side
|
|
||||||
return new LinedefSide(eline, (vertex_right == eline.Start));
|
|
||||||
}
|
|
||||||
|
|
||||||
///<summary>Finds the next adjacent edge to [edge], ie the adjacent edge that creates the smallest angle</summary>
|
|
||||||
private static LinedefSide NextEdge(LinedefSide edge, Dictionary<Linedef, int> visited_lines)
|
|
||||||
{
|
|
||||||
// Get relevant vertices
|
|
||||||
Vertex vertex; // Vertex to be tested
|
|
||||||
Vertex vertex_prev; // 'Previous' vertex
|
|
||||||
if(edge.Front)
|
|
||||||
{
|
|
||||||
vertex = edge.Line.End;
|
|
||||||
vertex_prev = edge.Line.Start;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vertex = edge.Line.Start;
|
|
||||||
vertex_prev = edge.Line.End;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find next connected line with the lowest angle
|
|
||||||
float min_angle = Angle2D.PI2;
|
|
||||||
LinedefSide next = null;
|
|
||||||
foreach(Linedef line in vertex.Linedefs)
|
|
||||||
{
|
|
||||||
// Ignore original line
|
|
||||||
if(line == edge.Line) continue;
|
|
||||||
|
|
||||||
// Ignore if zero-length
|
|
||||||
if(line.Start.Position == line.End.Position) continue;
|
|
||||||
|
|
||||||
// Get next vertex
|
|
||||||
Vertex vertex_next;
|
|
||||||
bool front = true;
|
|
||||||
if(line.Start == vertex)
|
|
||||||
{
|
|
||||||
vertex_next = line.End;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vertex_next = line.Start;
|
|
||||||
front = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore already-traversed lines
|
|
||||||
int side = (front ? 1 : 2);
|
|
||||||
if(visited_lines.ContainsKey(line) && (visited_lines[line] & side) == side) continue;
|
|
||||||
|
|
||||||
|
|
||||||
// Determine angle between lines
|
|
||||||
float angle = Angle2D.GetAngle(new Vector2D(vertex_prev.Position.x, vertex_prev.Position.y),
|
|
||||||
new Vector2D(vertex.Position.x, vertex.Position.y),
|
|
||||||
new Vector2D(vertex_next.Position.x, vertex_next.Position.y));
|
|
||||||
|
|
||||||
// Check if minimum angle
|
|
||||||
if(angle < min_angle)
|
|
||||||
{
|
|
||||||
min_angle = angle;
|
|
||||||
|
|
||||||
if(next == null)
|
|
||||||
{
|
|
||||||
next = new LinedefSide(line, front);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
next.Line = line;
|
|
||||||
next.Front = front;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the next edge found
|
|
||||||
if(next == null) return null;
|
|
||||||
if(!visited_lines.ContainsKey(next.Line)) visited_lines.Add(next.Line, 0);
|
|
||||||
visited_lines[next.Line] |= (next.Front ? 1 : 2);
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Returns true if the vertex is outside the current outline</summary>
|
|
||||||
private bool PointOutsideOutline(Vertex v)
|
|
||||||
{
|
|
||||||
// Check with bounding box
|
|
||||||
Vector2D point = v.Position;
|
|
||||||
bool pointwithin = (point.x >= o_bbox.Left && point.x <= o_bbox.Right && point.y >= o_bbox.Top && point.y <= o_bbox.Bottom);
|
|
||||||
if(!pointwithin)
|
|
||||||
{
|
|
||||||
// If the point is not within the bbox and the outline is clockwise, it can't be within the outline
|
|
||||||
// On the other hand, if the outline is anticlockwise, the point *must* be 'within' the outline
|
|
||||||
return o_clockwise;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find nearest edge
|
|
||||||
int nearest = NearestEdge(point);
|
|
||||||
if(nearest >= 0)
|
|
||||||
{
|
|
||||||
// Check what side of the edge the point is on
|
|
||||||
float side = -o_edges[nearest].Line.SideOfLine(point); //mxd. SideOfLine logic is inverted in Slade 3
|
|
||||||
//mxd. The meaning of 0.0 is also inverted!!!
|
|
||||||
|
|
||||||
// Return false if it is on the correct side
|
|
||||||
if(side > 0 && o_edges[nearest].Front)
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("Point " + point + " is within outline (infront of line) " + nearest);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(side <= 0 && !o_edges[nearest].Front)
|
|
||||||
{
|
|
||||||
//DebugConsole.WriteLine("Point " + point + " is within outline (at the back of line) " + nearest);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not within the outline
|
|
||||||
//DebugConsole.WriteLine("Point " + point + " is outside outline");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int NearestEdge(Vector2D point)
|
|
||||||
{
|
|
||||||
// Init variables
|
|
||||||
float min_dist = float.MaxValue;
|
|
||||||
int nearest = -1;
|
|
||||||
|
|
||||||
// Go through edges
|
|
||||||
for(int i = 0; i < o_edges.Count; i++)
|
|
||||||
{
|
|
||||||
// Get distance to edge
|
|
||||||
float dist = o_edges[i].Line.SafeDistanceToSq(point, true);
|
|
||||||
|
|
||||||
// Check if minimum
|
|
||||||
if(dist < min_dist)
|
|
||||||
{
|
|
||||||
min_dist = dist;
|
|
||||||
nearest = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return nearest edge index
|
|
||||||
return nearest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Checks if the traced sector is valid (ie. all edges are currently referencing the same (existing) sector)</summary>
|
/// <summary>Checks if the traced sector is valid (ie. all edges are currently referencing the same (existing) sector)</summary>
|
||||||
|
|
Loading…
Reference in a new issue