diff --git a/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs b/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs index 333d3168..31cf9544 100644 --- a/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs +++ b/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs @@ -135,6 +135,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing List newverts = new List(); List intersectverts = new List(); List newlines = new List(); + List oldlines = new List(General.Map.Map.Linedefs); List mergeverts = new List(); List nonmergeverts = new List(General.Map.Map.Vertices); @@ -182,7 +183,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing newlines.Add(ld); // Should we split this line to merge with intersecting lines? - if(points[i - 1].stitch || points[i].stitch) + if(points[i - 1].stitch && points[i].stitch) { // Check if any other lines intersect this line List intersections = new List(); @@ -236,12 +237,103 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing // Join merge vertices so that overlapping vertices in the draw become one. MapSet.JoinVertices(mergeverts, mergeverts, false, MapSet.STITCH_DISTANCE); + // We prefer a closed polygon, because then we can determine the interior properly + // Check if the two ends of the polygon are closed + bool drawingclosed = false; + if(newlines.Count > 0) + { + // When not closed, we will try to find a path to close it + Linedef firstline = newlines[0]; + Linedef lastline = newlines[newlines.Count - 1]; + drawingclosed = (firstline.Start == lastline.End); + if(!drawingclosed) + { + // First and last vertex stitch with geometry? + if(points[0].stitch && points[points.Count - 1].stitch) + { + // Find out where they will stitch + Linedef l1 = MapSet.NearestLinedefRange(oldlines, firstline.Start.Position, MapSet.STITCH_DISTANCE); + Linedef l2 = MapSet.NearestLinedefRange(oldlines, lastline.End.Position, MapSet.STITCH_DISTANCE); + if((l1 != null) && (l2 != null)) + { + List shortestpath = null; + + // Same line? + if(l1 == l2) + { + // Then just connect the two + shortestpath = new List(); + shortestpath.Add(new LinedefSide(l1, true)); + } + else + { + // Find the shortest, closest path between these lines + List> paths = new List>(8); + paths.Add(SectorTools.FindClosestPath(l1, true, l2, true, true)); + paths.Add(SectorTools.FindClosestPath(l1, true, l2, false, true)); + paths.Add(SectorTools.FindClosestPath(l1, false, l2, true, true)); + paths.Add(SectorTools.FindClosestPath(l1, false, l2, false, true)); + paths.Add(SectorTools.FindClosestPath(l2, true, l1, true, true)); + paths.Add(SectorTools.FindClosestPath(l2, true, l1, false, true)); + paths.Add(SectorTools.FindClosestPath(l2, false, l1, true, true)); + paths.Add(SectorTools.FindClosestPath(l2, false, l1, false, true)); + + foreach(List p in paths) + if((p != null) && ((shortestpath == null) || (p.Count < shortestpath.Count))) shortestpath = p; + } + + // Found a path? + if(shortestpath != null) + { + // Go for all vertices in the path to make additional lines + v1 = firstline.Start; + for(int i = 1; i < shortestpath.Count; i++) + { + // Get the next position + Vector2D v2pos = shortestpath[i].Front ? shortestpath[i].Line.Start.Position : shortestpath[i].Line.End.Position; + + // Make the new vertex + Vertex v2 = map.CreateVertex(v2pos); + v2.Marked = true; + mergeverts.Add(v2); + + // Make the line + Linedef ld = map.CreateLinedef(v1, v2); + ld.Marked = true; + ld.Selected = true; + ld.ApplySidedFlags(); + ld.UpdateCache(); + newlines.Add(ld); + + // Next + v1 = v2; + } + + // Make the final line + Linedef lld = map.CreateLinedef(v1, lastline.End); + lld.Marked = true; + lld.Selected = true; + lld.ApplySidedFlags(); + lld.UpdateCache(); + newlines.Add(lld); + + // Drawing is now closed + drawingclosed = true; + + // Join merge vertices so that overlapping vertices in the draw become one. + MapSet.JoinVertices(mergeverts, mergeverts, false, MapSet.STITCH_DISTANCE); + } + } + } + } + } + // Merge intersetion vertices with the new lines. This completes the // self intersections for which splits were made above. map.Update(true, false); MapSet.SplitLinesByVertices(newlines, intersectverts, MapSet.STITCH_DISTANCE, null); MapSet.SplitLinesByVertices(newlines, mergeverts, MapSet.STITCH_DISTANCE, null); - + /***************************************************\ STEP 2: Merge the new geometry \***************************************************/ @@ -304,6 +396,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing STEP 3: Join and create new sectors \***************************************************/ + // The code below atempts to create sectors on the front sides of the drawn + // geometry and joins sectors on the back sides of the drawn geometry. + // This code does not change any geometry, it only makes/updates sidedefs. bool[] frontsdone = new bool[newlines.Count]; bool[] backsdone = new bool[newlines.Count]; for(int i = 0; i < newlines.Count; i++) diff --git a/Source/Geometry/SectorTools.cs b/Source/Geometry/SectorTools.cs index 4ad961ce..73f40f41 100644 --- a/Source/Geometry/SectorTools.cs +++ b/Source/Geometry/SectorTools.cs @@ -274,12 +274,22 @@ namespace CodeImp.DoomBuilder.Geometry /// When turnatends is true, the algorithm will continue at the other side of the /// line when a dead end has been reached. Returns null when no path could be found. /// - public static List FindClosestPath(Linedef start, bool front, bool turnatends) + public static List FindClosestPath(Linedef startline, bool startfront, bool turnatends) + { + return FindClosestPath(startline, startfront, startline, startfront, turnatends); + } + + /// + /// This finds the closest path from the beginning of a line to the end of the line. + /// When turnatends is true, the algorithm will continue at the other side of the + /// line when a dead end has been reached. Returns null when no path could be found. + /// + public static List FindClosestPath(Linedef startline, bool startfront, Linedef endline, bool endfront, bool turnatends) { List path = new List(); Dictionary tracecount = new Dictionary(); - Linedef nextline = start; - bool nextfront = front; + Linedef nextline = startline; + bool nextfront = startfront; do { @@ -332,8 +342,12 @@ namespace CodeImp.DoomBuilder.Geometry } // Continue as long as we have not reached the start yet // or we have no next line to trace - while((path != null) && ((nextline != start) || (nextfront != front))); + while((path != null) && ((nextline != endline) || (nextfront != endfront))); + // If start and front are not the same, add the end to the list also + if((path != null) && ((startline != endline) || (startfront != endfront))) + path.Add(new LinedefSide(endline, endfront)); + // Return path (null when trace failed) return path; } diff --git a/Source/Map/Linedef.cs b/Source/Map/Linedef.cs index cfdadaea..23411907 100644 --- a/Source/Map/Linedef.cs +++ b/Source/Map/Linedef.cs @@ -563,6 +563,11 @@ namespace CodeImp.DoomBuilder.Map public void Join(Linedef other) { Sector l1fs, l1bs, l2fs, l2bs; + bool l1was2s, l2was2s; + + // Check which lines were 2 sided + l1was2s = ((other.Front != null) && (other.Back != null)); + l2was2s = ((this.Front != null) && (this.Back != null)); // Get sector references if(other.front != null) l1fs = other.front.Sector; else l1fs = null; @@ -570,118 +575,143 @@ namespace CodeImp.DoomBuilder.Map if(this.front != null) l2fs = this.front.Sector; else l2fs = null; if(this.back != null) l2bs = this.back.Sector; else l2bs = null; - // Compare front sectors - if(l1fs == l2fs) + // This line has no sidedefs? + if((l2fs == null) && (l2bs == null)) { - // Copy textures - if(other.front != null) other.front.AddTexturesTo(this.back); - if(this.front != null) this.front.AddTexturesTo(other.back); - - // Change sidedefs - JoinChangeSidedefs(other, true, back); + // We have no sidedefs, so we have no influence + // Nothing to change on the other line } - // Compare back sectors - else if(l1bs == l2bs) + // Other line has no sidedefs? + else if((l1fs == null) && (l1bs == null)) { - // Copy textures - if(other.back != null) other.back.AddTexturesTo(this.front); - if(this.back != null) this.back.AddTexturesTo(other.front); - - // Change sidedefs - JoinChangeSidedefs(other, false, front); - } - // Compare front and back - else if(l1fs == l2bs) - { - // Copy textures - if(other.front != null) other.front.AddTexturesTo(this.front); - if(this.back != null) this.back.AddTexturesTo(other.back); - - // Change sidedefs - JoinChangeSidedefs(other, true, front); - } - // Compare back and front - else if(l1bs == l2fs) - { - // Copy textures - if(other.back != null) other.back.AddTexturesTo(this.back); - if(this.front != null) this.front.AddTexturesTo(other.front); - - // Change sidedefs - JoinChangeSidedefs(other, false, back); - } - else - { - // Other line single sided? - if(other.back == null) + // The other has no sidedefs, so it has no influence + // Copy my sidedefs to the other + if(this.Start == other.Start) { - // This line with its back to the other? - if(this.start == other.end) - { - // Copy textures - if(other.back != null) other.back.AddTexturesTo(this.front); - if(this.back != null) this.back.AddTexturesTo(other.front); - - // Change sidedefs - JoinChangeSidedefs(other, false, front); - } - else - { - // Copy textures - if(other.back != null) other.back.AddTexturesTo(this.back); - if(this.front != null) this.front.AddTexturesTo(other.front); - - // Change sidedefs - JoinChangeSidedefs(other, false, back); - } - } - // This line single sided? - if(this.back == null) - { - // Other line with its back to this? - if(other.start == this.end) - { - // Copy textures - if(other.back != null) other.back.AddTexturesTo(this.front); - if(this.back != null) this.back.AddTexturesTo(other.front); - - // Change sidedefs - JoinChangeSidedefs(other, false, front); - } - else - { - // Copy textures - if(other.front != null) other.front.AddTexturesTo(this.front); - if(this.back != null) this.back.AddTexturesTo(other.back); - - // Change sidedefs - JoinChangeSidedefs(other, true, front); - } + JoinChangeSidedefs(other, true, front); + JoinChangeSidedefs(other, false, back); } else { - // This line with its back to the other? - if(this.start == other.end) - { - // Copy textures - if(other.back != null) other.back.AddTexturesTo(this.front); - if(this.back != null) this.back.AddTexturesTo(other.front); + JoinChangeSidedefs(other, false, front); + JoinChangeSidedefs(other, true, back); + } + } + else + { + // Compare front sectors + if(l1fs == l2fs) + { + // Copy textures + if(other.front != null) other.front.AddTexturesTo(this.back); + if(this.front != null) this.front.AddTexturesTo(other.back); - // Change sidedefs - JoinChangeSidedefs(other, false, front); + // Change sidedefs + JoinChangeSidedefs(other, true, back); + } + // Compare back sectors + else if(l1bs == l2bs) + { + // Copy textures + if(other.back != null) other.back.AddTexturesTo(this.front); + if(this.back != null) this.back.AddTexturesTo(other.front); + + // Change sidedefs + JoinChangeSidedefs(other, false, front); + } + // Compare front and back + else if(l1fs == l2bs) + { + // Copy textures + if(other.front != null) other.front.AddTexturesTo(this.front); + if(this.back != null) this.back.AddTexturesTo(other.back); + + // Change sidedefs + JoinChangeSidedefs(other, true, front); + } + // Compare back and front + else if(l1bs == l2fs) + { + // Copy textures + if(other.back != null) other.back.AddTexturesTo(this.back); + if(this.front != null) this.front.AddTexturesTo(other.front); + + // Change sidedefs + JoinChangeSidedefs(other, false, back); + } + else + { + // Other line single sided? + if(other.back == null) + { + // This line with its back to the other? + if(this.start == other.end) + { + // Copy textures + if(other.back != null) other.back.AddTexturesTo(this.front); + if(this.back != null) this.back.AddTexturesTo(other.front); + + // Change sidedefs + JoinChangeSidedefs(other, false, front); + } + else + { + // Copy textures + if(other.back != null) other.back.AddTexturesTo(this.back); + if(this.front != null) this.front.AddTexturesTo(other.front); + + // Change sidedefs + JoinChangeSidedefs(other, false, back); + } + } + // This line single sided? + if(this.back == null) + { + // Other line with its back to this? + if(other.start == this.end) + { + // Copy textures + if(other.back != null) other.back.AddTexturesTo(this.front); + if(this.back != null) this.back.AddTexturesTo(other.front); + + // Change sidedefs + JoinChangeSidedefs(other, false, front); + } + else + { + // Copy textures + if(other.front != null) other.front.AddTexturesTo(this.front); + if(this.back != null) this.back.AddTexturesTo(other.back); + + // Change sidedefs + JoinChangeSidedefs(other, true, front); + } } else { - // Copy textures - if(other.back != null) other.back.AddTexturesTo(this.back); - if(this.front != null) this.front.AddTexturesTo(other.front); + // This line with its back to the other? + if(this.start == other.end) + { + // Copy textures + if(other.back != null) other.back.AddTexturesTo(this.front); + if(this.back != null) this.back.AddTexturesTo(other.front); - // Change sidedefs - JoinChangeSidedefs(other, false, back); + // Change sidedefs + JoinChangeSidedefs(other, false, front); + } + else + { + // Copy textures + if(other.back != null) other.back.AddTexturesTo(this.back); + if(this.front != null) this.front.AddTexturesTo(other.front); + + // Change sidedefs + JoinChangeSidedefs(other, false, back); + } } } } - + // If either of the two lines was selected, keep the other selected if(this.selected) other.selected = true; if(this.marked) other.marked = true; @@ -689,6 +719,10 @@ namespace CodeImp.DoomBuilder.Map // Apply single/double sided flags other.ApplySidedFlags(); + // Remove unneeded textures + if(other.front != null) other.front.RemoveUnneededTextures(!(l1was2s && l2was2s)); + if(other.back != null) other.back.RemoveUnneededTextures(!(l1was2s && l2was2s)); + // I got killed by the other. this.Dispose(); } diff --git a/Source/Map/Sidedef.cs b/Source/Map/Sidedef.cs index a07cd562..888a76ba 100644 --- a/Source/Map/Sidedef.cs +++ b/Source/Map/Sidedef.cs @@ -237,6 +237,28 @@ namespace CodeImp.DoomBuilder.Map #region ================== Methods + // This removes textures that are not required + public void RemoveUnneededTextures(bool removemiddle) + { + if(!HighRequired()) + { + this.texnamehigh = "-"; + this.longtexnamehigh = map.EmptyLongName; + } + + if(!MiddleRequired() && removemiddle) + { + this.texnamemid = "-"; + this.longtexnamemid = map.EmptyLongName; + } + + if(!LowRequired()) + { + this.texnamelow = "-"; + this.longtexnamelow = map.EmptyLongName; + } + } + // This checks if a texture is required public bool HighRequired() {