diff --git a/Source/BuilderModes/ErrorChecks/CheckOverlappingLines.cs b/Source/BuilderModes/ErrorChecks/CheckOverlappingLines.cs index ffe59b90..3c6700ea 100644 --- a/Source/BuilderModes/ErrorChecks/CheckOverlappingLines.cs +++ b/Source/BuilderModes/ErrorChecks/CheckOverlappingLines.cs @@ -63,6 +63,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public override void Run() { Dictionary donelines = new Dictionary(); + BlockMap blockmap = BuilderPlug.Me.ErrorCheckForm.BlockMap; // Go for all the liendefs foreach(Linedef l in General.Map.Map.Linedefs) @@ -71,36 +72,44 @@ namespace CodeImp.DoomBuilder.BuilderModes if(!donelines.ContainsKey(l)) { // And go for all the linedefs that could overlap - foreach(Linedef d in General.Map.Map.Linedefs) + List blocks = blockmap.GetLineBlocks(l.Start.Position, l.End.Position); + Dictionary doneblocklines = new Dictionary(blocks.Count * 3); + foreach(BlockEntry b in blocks) { - // Not the same line? - if(!object.ReferenceEquals(l, d)) + foreach(Linedef d in b.Lines) { - float lu, du; - - // Check if the lines touch. Note that I don't include 0.0 and 1.0 here because - // the lines may be touching at the ends when sharing the same vertex. - if(l.Line.GetIntersection(d.Line, out du, out lu)) + // Not the same line and not already checked + if(!object.ReferenceEquals(l, d) && !doneblocklines.ContainsKey(d)) { - if((lu > 0.0f) && (lu < 1.0f) && (du > 0.0f) && (du < 1.0f)) + float lu, du; + + // Check if the lines touch. Note that I don't include 0.0 and 1.0 here because + // the lines may be touching at the ends when sharing the same vertex. + if(l.Line.GetIntersection(d.Line, out du, out lu)) { - // Check if not the same sector on all sides - Sector samesector = null; - if(l.Front != null) samesector = l.Front.Sector; - else if(l.Back != null) samesector = l.Back.Sector; - else if(d.Front != null) samesector = d.Front.Sector; - else if(d.Back != null) samesector = d.Back.Sector; - if((l.Front == null) || (l.Front.Sector != samesector)) samesector = null; - else if((l.Back == null) || (l.Back.Sector != samesector)) samesector = null; - else if((d.Front == null) || (d.Front.Sector != samesector)) samesector = null; - else if((d.Back == null) || (d.Back.Sector != samesector)) samesector = null; - - if(samesector == null) + if((lu > 0.0f) && (lu < 1.0f) && (du > 0.0f) && (du < 1.0f)) { - SubmitResult(new ResultLineOverlapping(l, d)); - donelines[d] = d; + // Check if not the same sector on all sides + Sector samesector = null; + if(l.Front != null) samesector = l.Front.Sector; + else if(l.Back != null) samesector = l.Back.Sector; + else if(d.Front != null) samesector = d.Front.Sector; + else if(d.Back != null) samesector = d.Back.Sector; + if((l.Front == null) || (l.Front.Sector != samesector)) samesector = null; + else if((l.Back == null) || (l.Back.Sector != samesector)) samesector = null; + else if((d.Front == null) || (d.Front.Sector != samesector)) samesector = null; + else if((d.Back == null) || (d.Back.Sector != samesector)) samesector = null; + + if(samesector == null) + { + SubmitResult(new ResultLineOverlapping(l, d)); + donelines[d] = d; + } } } + + // Checked + doneblocklines.Add(d, d); } } } diff --git a/Source/BuilderModes/ErrorChecks/CheckStuckedThings.cs b/Source/BuilderModes/ErrorChecks/CheckStuckedThings.cs index f4fc5b8c..74b9d66c 100644 --- a/Source/BuilderModes/ErrorChecks/CheckStuckedThings.cs +++ b/Source/BuilderModes/ErrorChecks/CheckStuckedThings.cs @@ -34,6 +34,7 @@ using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.Types; using CodeImp.DoomBuilder.Config; using System.Threading; +using System.Drawing; #endregion @@ -64,6 +65,8 @@ namespace CodeImp.DoomBuilder.BuilderModes // This runs the check public override void Run() { + BlockMap blockmap = BuilderPlug.Me.ErrorCheckForm.BlockMap; + // Go for all the things foreach(Thing t in General.Map.Map.Things) { @@ -80,26 +83,34 @@ namespace CodeImp.DoomBuilder.BuilderModes Vector2D rb = new Vector2D(t.Position.x + blockingsize, t.Position.y + blockingsize); // Go for all the lines to see if this thing is stucked - foreach(Linedef l in General.Map.Map.Linedefs) + List blocks = blockmap.GetSquareRange(new RectangleF(lt.x, lt.y, (rb.x - lt.x), (rb.y - lt.y))); + Dictionary doneblocklines = new Dictionary(blocks.Count * 3); + foreach(BlockEntry b in blocks) { - // Test only single-sided lines - if(l.Back == null) + foreach(Linedef l in b.Lines) { - // Test if line ends are inside the thing - if(PointInRect(lt, rb, l.Start.Position) || - PointInRect(lt, rb, l.End.Position)) + // Only test when sinlge-sided and not already checked + if((l.Back == null) && !doneblocklines.ContainsKey(l)) { - // Thing stucked in line! - stucked = true; - } - // Test if the line intersects the square - else if(Line2D.GetIntersection(l.Start.Position, l.End.Position, lt.x, lt.y, rb.x, lt.y) || - Line2D.GetIntersection(l.Start.Position, l.End.Position, rb.x, lt.y, rb.x, rb.y) || - Line2D.GetIntersection(l.Start.Position, l.End.Position, rb.x, rb.y, lt.x, rb.y) || - Line2D.GetIntersection(l.Start.Position, l.End.Position, lt.x, rb.y, lt.x, lt.y)) - { - // Thing stucked in line! - stucked = true; + // Test if line ends are inside the thing + if(PointInRect(lt, rb, l.Start.Position) || + PointInRect(lt, rb, l.End.Position)) + { + // Thing stucked in line! + stucked = true; + } + // Test if the line intersects the square + else if(Line2D.GetIntersection(l.Start.Position, l.End.Position, lt.x, lt.y, rb.x, lt.y) || + Line2D.GetIntersection(l.Start.Position, l.End.Position, rb.x, lt.y, rb.x, rb.y) || + Line2D.GetIntersection(l.Start.Position, l.End.Position, rb.x, rb.y, lt.x, rb.y) || + Line2D.GetIntersection(l.Start.Position, l.End.Position, lt.x, rb.y, lt.x, lt.y)) + { + // Thing stucked in line! + stucked = true; + } + + // Checked + doneblocklines.Add(l, l); } } } diff --git a/Source/BuilderModes/Interface/ErrorCheckForm.cs b/Source/BuilderModes/Interface/ErrorCheckForm.cs index 0be8dfb1..ae4ad3a1 100644 --- a/Source/BuilderModes/Interface/ErrorCheckForm.cs +++ b/Source/BuilderModes/Interface/ErrorCheckForm.cs @@ -58,15 +58,17 @@ namespace CodeImp.DoomBuilder.BuilderModes private volatile bool running = false; private Thread checksthread; + private BlockMap blockmap; #endregion #region ================== Properties public ErrorResult SelectedResult { get { return results.SelectedItem as ErrorResult; } } + public BlockMap BlockMap { get { return blockmap; } } #endregion - + #region ================== Constructor / Show // Constructor @@ -110,7 +112,7 @@ namespace CodeImp.DoomBuilder.BuilderModes } #endregion - + #region ================== Thread Calls public void SubmitResult(ErrorResult result) @@ -170,6 +172,8 @@ namespace CodeImp.DoomBuilder.BuilderModes buttoncheck.Text = "Start Analysis"; Cursor.Current = Cursors.Default; running = false; + blockmap.Dispose(); + blockmap = null; } } @@ -180,6 +184,14 @@ namespace CodeImp.DoomBuilder.BuilderModes Cursor.Current = Cursors.WaitCursor; + // Make blockmap + RectangleF area = MapSet.CreateArea(General.Map.Map.Vertices); + area = MapSet.IncreaseArea(area, General.Map.Map.Things); + blockmap = new BlockMap(area); + blockmap.AddLinedefsSet(General.Map.Map.Linedefs); + blockmap.AddSectorsSet(General.Map.Map.Sectors); + blockmap.AddThingsSet(General.Map.Map.Things); + // Open the results panel this.Size = new Size(this.Width, this.Height - this.ClientSize.Height + resultspanel.Top + resultspanel.Height); progress.Value = 0; diff --git a/Source/Map/BlockMap.cs b/Source/Map/BlockMap.cs index 43bc3d8d..9ad690d3 100644 --- a/Source/Map/BlockMap.cs +++ b/Source/Map/BlockMap.cs @@ -52,8 +52,6 @@ namespace CodeImp.DoomBuilder.Map // Blocks protected BlockEntry[,] blockmap; - protected Point lefttop; - protected Point rightbottom; protected Size size; protected RectangleF range; @@ -65,27 +63,28 @@ namespace CodeImp.DoomBuilder.Map #region ================== Properties public bool IsDisposed { get { return isdisposed; } } + public Size Size { get { return size; } } + public RectangleF Range { get { return range; } } + public int BlockSize { get { return BLOCK_SIZE; } } #endregion #region ================== Constructor / Disposer // Constructor - internal BlockMap(RectangleF range) + public BlockMap(RectangleF range) { // Initialize this.range = range; - lefttop = new Point((int)range.Left >> BLOCK_SIZE_SHIFT, (int)range.Top >> BLOCK_SIZE_SHIFT); - rightbottom = new Point((int)range.Right >> BLOCK_SIZE_SHIFT, (int)range.Bottom >> BLOCK_SIZE_SHIFT); - int width = (rightbottom.X - lefttop.X) + 1; - int height = (rightbottom.Y - lefttop.Y) + 1; - size = new Size(width, height); - blockmap = new BlockEntry[width, height]; + Point lefttop = new Point((int)range.Left >> BLOCK_SIZE_SHIFT, (int)range.Top >> BLOCK_SIZE_SHIFT); + Point rightbottom = new Point((int)range.Right >> BLOCK_SIZE_SHIFT, (int)range.Bottom >> BLOCK_SIZE_SHIFT); + size = new Size((rightbottom.X - lefttop.X) + 1, (rightbottom.Y - lefttop.Y) + 1); + blockmap = new BlockEntry[size.Width, size.Height]; Clear(); } // Disposer - internal void Dispose() + public void Dispose() { // Not already disposed? if(!isdisposed) @@ -105,15 +104,15 @@ namespace CodeImp.DoomBuilder.Map // This returns the block coordinates protected Point GetBlockCoordinates(Vector2D v) { - return new Point(((int)v.x >> BLOCK_SIZE_SHIFT) - lefttop.X, - ((int)v.y >> BLOCK_SIZE_SHIFT) - lefttop.Y); + return new Point((int)(v.x - range.Left) >> BLOCK_SIZE_SHIFT, + (int)(v.y - range.Top) >> BLOCK_SIZE_SHIFT); } // This returns the block center in world coordinates protected Vector2D GetBlockCenter(Point p) { - return new Vector2D((float)(((p.X + lefttop.X) << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1)), - (float)(((p.Y + lefttop.Y) << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1))); + return new Vector2D((float)((p.X << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1)) + range.Left, + (float)((p.Y << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1)) + range.Top); } // This returns true when the given block is inside range @@ -148,7 +147,7 @@ namespace CodeImp.DoomBuilder.Map } // This clears the blockmap - public void Clear() + public virtual void Clear() { for(int x = 0; x < size.Width; x++) { @@ -160,7 +159,7 @@ namespace CodeImp.DoomBuilder.Map } // This returns a range of blocks in a square - public List GetSquareRange(RectangleF rect) + public virtual List GetSquareRange(RectangleF rect) { // Calculate block coordinates Point lt = GetBlockCoordinates(new Vector2D(rect.Left, rect.Top)); @@ -186,7 +185,7 @@ namespace CodeImp.DoomBuilder.Map } // This returns all blocks along the given line - public List GetLineBlocks(Vector2D v1, Vector2D v2) + public virtual List GetLineBlocks(Vector2D v1, Vector2D v2) { float deltax, deltay; float posx, posy; @@ -200,95 +199,136 @@ namespace CodeImp.DoomBuilder.Map // Find start and end block pos = GetBlockCoordinates(v1); end = GetBlockCoordinates(v2); - - // Add this block - if(IsInRange(pos)) entries.Add(blockmap[pos.X, pos.Y]); - - // Moving outside the block? - if(pos != end) + + // Horizontal straight line? + if(pos.Y == end.Y) { - // Calculate current block edges - float cl = pos.X * BLOCK_SIZE; - float cr = (pos.X + 1) * BLOCK_SIZE; - float ct = pos.Y * BLOCK_SIZE; - float cb = (pos.Y + 1) * BLOCK_SIZE; - - // Line directions - dirx = Math.Sign(v2.x - v1.x); - diry = Math.Sign(v2.y - v1.y); - - // Calculate offset and delta movement over x - if(dirx >= 0) + // Simple loop + pos.X = CropToRangeX(pos.X); + end.X = CropToRangeX(end.X); + if(IsInRange(new Point(pos.X, pos.Y))) { - posx = (cr - v1.x) / (v2.x - v1.x); - deltax = BLOCK_SIZE / (v2.x - v1.x); - } - else - { - // Calculate offset and delta movement over x - posx = (v1.x - cl) / (v1.x - v2.x); - deltax = BLOCK_SIZE / (v1.x - v2.x); - } - - // Calculate offset and delta movement over y - if(diry >= 0) - { - posy = (cb - v1.y) / (v2.y - v1.y); - deltay = BLOCK_SIZE / (v2.y - v1.y); - } - else - { - posy = (v1.y - ct) / (v1.y - v2.y); - deltay = BLOCK_SIZE / (v1.y - v2.y); - } - - // Continue while not reached the end - while(pos != end) - { - // Check in which direction to move - if(posx < posy) + dirx = Math.Sign(v2.x - v1.x); + if(dirx != 0) { - // Move horizontally - posx += deltax; - if(pos.X != end.X) pos.X += dirx; + for(int x = pos.X; x != end.X; x += dirx) + { + entries.Add(blockmap[x, pos.Y]); + } + } + entries.Add(blockmap[end.X, end.Y]); + } + } + // Vertical straight line? + else if(pos.X == end.X) + { + // Simple loop + pos.Y = CropToRangeY(pos.Y); + end.Y = CropToRangeY(end.Y); + if(IsInRange(new Point(pos.X, pos.Y))) + { + diry = Math.Sign(v2.y - v1.y); + if(diry != 0) + { + for(int y = pos.Y; y != end.Y; y += diry) + { + entries.Add(blockmap[pos.X, y]); + } + } + entries.Add(blockmap[end.X, end.Y]); + } + } + else + { + // Add this block + if(IsInRange(pos)) entries.Add(blockmap[pos.X, pos.Y]); + + // Moving outside the block? + if(pos != end) + { + // Calculate current block edges + float cl = pos.X * BLOCK_SIZE; + float cr = (pos.X + 1) * BLOCK_SIZE; + float ct = pos.Y * BLOCK_SIZE; + float cb = (pos.Y + 1) * BLOCK_SIZE; + + // Line directions + dirx = Math.Sign(v2.x - v1.x); + diry = Math.Sign(v2.y - v1.y); + + // Calculate offset and delta movement over x + if(dirx >= 0) + { + posx = (cr - v1.x) / (v2.x - v1.x); + deltax = BLOCK_SIZE / (v2.x - v1.x); } else { - // Move vertically - posy += deltay; - if(pos.Y != end.Y) pos.Y += diry; + // Calculate offset and delta movement over x + posx = (v1.x - cl) / (v1.x - v2.x); + deltax = BLOCK_SIZE / (v1.x - v2.x); + } + + // Calculate offset and delta movement over y + if(diry >= 0) + { + posy = (cb - v1.y) / (v2.y - v1.y); + deltay = BLOCK_SIZE / (v2.y - v1.y); + } + else + { + posy = (v1.y - ct) / (v1.y - v2.y); + deltay = BLOCK_SIZE / (v1.y - v2.y); + } + + // Continue while not reached the end + while(pos != end) + { + // Check in which direction to move + if(posx < posy) + { + // Move horizontally + posx += deltax; + if(pos.X != end.X) pos.X += dirx; + } + else + { + // Move vertically + posy += deltay; + if(pos.Y != end.Y) pos.Y += diry; + } + + // Add lines to this block + if(IsInRange(pos)) entries.Add(blockmap[pos.X, pos.Y]); } - - // Add lines to this block - if(IsInRange(pos)) entries.Add(blockmap[pos.X, pos.Y]); } } - + // Return list return entries; } // This puts a thing in the blockmap - public void AddThingsSet(ICollection things) + public virtual void AddThingsSet(ICollection things) { foreach(Thing t in things) AddThing(t); } // This puts a thing in the blockmap - public void AddThing(Thing t) + public virtual void AddThing(Thing t) { Point p = GetBlockCoordinates(t.Position); if(IsInRange(p)) blockmap[p.X, p.Y].Things.Add(t); } // This puts a secotr in the blockmap - public void AddSectorsSet(ICollection sectors) + public virtual void AddSectorsSet(ICollection sectors) { foreach(Sector s in sectors) AddSector(s); } // This puts a sector in the blockmap - public void AddSector(Sector s) + public virtual void AddSector(Sector s) { Point p1 = GetBlockCoordinates(new Vector2D(s.BBox.Left, s.BBox.Top)); Point p2 = GetBlockCoordinates(new Vector2D(s.BBox.Right, s.BBox.Bottom)); @@ -304,13 +344,13 @@ namespace CodeImp.DoomBuilder.Map } // This puts a whole set of linedefs in the blocks they cross - public void AddLinedefsSet(ICollection lines) + public virtual void AddLinedefsSet(ICollection lines) { foreach(Linedef l in lines) AddLinedef(l); } // This puts a single linedef in all blocks it crosses - public void AddLinedef(Linedef line) + public virtual void AddLinedef(Linedef line) { Vector2D v1, v2; float deltax, deltay; @@ -335,9 +375,12 @@ namespace CodeImp.DoomBuilder.Map if(IsInRange(new Point(pos.X, pos.Y))) { dirx = Math.Sign(v2.x - v1.x); - for(int x = pos.X; x != end.X; x += dirx) + if(dirx != 0) { - blockmap[x, pos.Y].Lines.Add(line); + for(int x = pos.X; x != end.X; x += dirx) + { + blockmap[x, pos.Y].Lines.Add(line); + } } blockmap[end.X, end.Y].Lines.Add(line); } @@ -351,9 +394,12 @@ namespace CodeImp.DoomBuilder.Map if(IsInRange(new Point(pos.X, pos.Y))) { diry = Math.Sign(v2.y - v1.y); - for(int y = pos.Y; y != end.Y; y += diry) + if(diry != 0) { - blockmap[pos.X, y].Lines.Add(line); + for(int y = pos.Y; y != end.Y; y += diry) + { + blockmap[pos.X, y].Lines.Add(line); + } } blockmap[end.X, end.Y].Lines.Add(line); } diff --git a/Source/Map/MapSet.cs b/Source/Map/MapSet.cs index 721f811b..008c6aa2 100644 --- a/Source/Map/MapSet.cs +++ b/Source/Map/MapSet.cs @@ -1299,6 +1299,28 @@ namespace CodeImp.DoomBuilder.Map return new RectangleF(l, t, r - l, b - t); } + // This increases and existing area with the given vertices + public static RectangleF IncreaseArea(RectangleF area, ICollection things) + { + float l = area.Left; + float t = area.Top; + float r = area.Right; + float b = area.Bottom; + + // Go for all vertices + foreach(Thing th in things) + { + // Adjust boundaries by vertices + if(th.Position.x < l) l = th.Position.x; + if(th.Position.x > r) r = th.Position.x; + if(th.Position.y < t) t = th.Position.y; + if(th.Position.y > b) b = th.Position.y; + } + + // Return a rect + return new RectangleF(l, t, r - l, b - t); + } + // This increases and existing area with the given vertices public static RectangleF IncreaseArea(RectangleF area, ICollection verts) { diff --git a/Tests/ErrorChecking/overlappinglines_a.wad b/Tests/ErrorChecking/overlappinglines_a.wad new file mode 100644 index 00000000..2b181ac8 Binary files /dev/null and b/Tests/ErrorChecking/overlappinglines_a.wad differ