diff --git a/Build/Scripting/ZDoom_DECORATE.cfg b/Build/Scripting/ZDoom_DECORATE.cfg
index f91c586..253b1f9 100644
--- a/Build/Scripting/ZDoom_DECORATE.cfg
+++ b/Build/Scripting/ZDoom_DECORATE.cfg
@@ -282,7 +282,7 @@ keywords
A_DropInventory = "A_DropInventory(str type)";
A_DropItem = "A_DropItem(str item[, int dropamount = -1[, int chance = 256]])\nThe calling actor drops the specified item.\nThis works in a similar way to the DropItem actor property.";
A_SelectWeapon = "bool A_SelectWeapon(str type)";
- A_RadiusGive = "int A_RadiusGive(str item, float distance, int flags[, int amount = 0[, str filter = \"None\"[, str species = \"None\"[, float mindist = 0]]]])\nflags: RGF flags.";
+ A_RadiusGive = "int A_RadiusGive(str item, float distance, int flags[, int amount = 0[, str filter = \"None\"[, str species = \"None\"[, int mindist = 0[, int limit = 0]]]]])\nflags: RGF flags.";
//Weapon functions
A_WeaponReady = "A_WeaponReady[(int flags = 0)]\nflags: WRF flags.";
A_Lower = "A_Lower";
diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index f8b39d7..0460f17 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -917,6 +917,7 @@
+
diff --git a/Source/Core/Controls/DebugConsole.cs b/Source/Core/Controls/DebugConsole.cs
index 20ddb05..9713117 100644
--- a/Source/Core/Controls/DebugConsole.cs
+++ b/Source/Core/Controls/DebugConsole.cs
@@ -156,7 +156,11 @@ namespace CodeImp.DoomBuilder
else
message = message.TrimEnd() + " " + duration + " ms.";
+#if DEBUG
WriteLine(DebugMessageType.SPECIAL, message);
+#else
+ General.ShowErrorMessage(message, MessageBoxButtons.OK, false);
+#endif
starttime = -1;
}
diff --git a/Source/Core/Geometry/Angle2D.cs b/Source/Core/Geometry/Angle2D.cs
index 36a8bb1..bbf84ee 100644
--- a/Source/Core/Geometry/Angle2D.cs
+++ b/Source/Core/Geometry/Angle2D.cs
@@ -82,6 +82,53 @@ namespace CodeImp.DoomBuilder.Geometry
// Return result
return d;
}
+
+ //mxd. Slade 3 MathStuff::angle2DRad ripoff...
+ //Returns the angle between the 2d points [p1], [p2] and [p3]
+ public static float GetAngle(Vector2D p1, Vector2D p2, Vector2D p3)
+ {
+ // From: http://stackoverflow.com/questions/3486172/angle-between-3-points
+ // modified not to bother converting to degrees
+ Vector2D ab = new Vector2D(p2.x - p1.x, p2.y - p1.y);
+ Vector2D cb = new Vector2D(p2.x - p3.x, p2.y - p3.y);
+
+ // dot product
+ float dot = (ab.x * cb.x + ab.y * cb.y);
+
+ // length square of both vectors
+ float abSqr = ab.x * ab.x + ab.y * ab.y;
+ float cbSqr = cb.x * cb.x + cb.y * cb.y;
+
+ // square of cosine of the needed angle
+ float cosSqr = dot * dot / abSqr / cbSqr;
+
+ // this is a known trigonometric equality:
+ // cos(alpha * 2) = [ cos(alpha) ]^2 * 2 - 1
+ float cos2 = 2.0f * cosSqr - 1.0f;
+
+ // Here's the only invocation of the heavy function.
+ // It's a good idea to check explicitly if cos2 is within [-1 .. 1] range
+ float alpha2 =
+ (cos2 <= -1) ? PI :
+ (cos2 >= 1) ? 0.0f :
+ (float)Math.Acos(cos2);
+
+ float rs = alpha2 * 0.5f;
+
+ // Now revolve the ambiguities.
+ // 1. If dot product of two vectors is negative - the angle is definitely
+ // above 90 degrees. Still we have no information regarding the sign of the angle.
+
+ // NOTE: This ambiguity is the consequence of our method: calculating the cosine
+ // of the double angle. This allows us to get rid of calling sqrt.
+ if(dot < 0) rs = PI - rs;
+
+ // 2. Determine the sign. For this we'll use the Determinant of two vectors.
+ float det = (ab.x * cb.y - ab.y * cb.x);
+ if(det < 0) rs = (2.0f * PI) - rs;
+
+ return rs;
+ }
#endregion
}
diff --git a/Source/Core/Geometry/EarClipPolygon.cs b/Source/Core/Geometry/EarClipPolygon.cs
index c237948..838d7d3 100644
--- a/Source/Core/Geometry/EarClipPolygon.cs
+++ b/Source/Core/Geometry/EarClipPolygon.cs
@@ -109,44 +109,35 @@ namespace CodeImp.DoomBuilder.Geometry
}
// Point inside the polygon?
- // See: http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
+ // See: http://paulbourke.net/geometry/polygonmesh/index.html#insidepoly
public bool Intersect(Vector2D p)
{
Vector2D v1 = base.Last.Value.Position;
LinkedListNode n = base.First;
uint c = 0;
+ Vector2D v2;
// Go for all vertices
while(n != null)
{
// Get next vertex
- Vector2D v2 = n.Value.Position;
-
- // Determine min/max values
- float miny = Math.Min(v1.y, v2.y);
- float maxy = Math.Max(v1.y, v2.y);
- float maxx = Math.Max(v1.x, v2.x);
+ v2 = n.Value.Position;
// Check for intersection
- if((p.y > miny) && (p.y <= maxy))
- {
- if(p.x <= maxx)
- {
- if(v1.y != v2.y)
- {
- float xint = (p.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x;
- if((v1.x == v2.x) || (p.x <= xint)) c++;
- }
- }
- }
-
+ if(v1.y != v2.y //mxd. If line is not horizontal...
+ && p.y > (v1.y < v2.y ? v1.y : v2.y) //mxd. ...And test point y intersects with the line y bounds...
+ && p.y <= (v1.y > v2.y ? v1.y : v2.y) //mxd
+ && (p.x < (v1.x < v2.x ? v1.x : v2.x) || (p.x <= (v1.x > v2.x ? v1.x : v2.x) //mxd. ...And test point x is to the left of the line, or is inside line x bounds and intersects it
+ && (v1.x == v2.x || p.x <= ((p.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x)))))
+ c++; //mxd. ...Count the line as crossed
+
// Move to next
v1 = v2;
n = n.Next;
}
- // Inside this polygon?
- if((c & 0x00000001UL) != 0)
+ // Inside this polygon when we crossed odd number of polygon lines
+ if(c % 2 != 0)
{
// Check if not inside the children
foreach(EarClipPolygon child in children)
@@ -158,11 +149,9 @@ namespace CodeImp.DoomBuilder.Geometry
// Inside polygon!
return true;
}
- else
- {
- // Not inside the polygon
- return false;
- }
+
+ // Not inside the polygon
+ return false;
}
// This inserts a polygon if it is a child of this one
diff --git a/Source/Core/Geometry/Line2D.cs b/Source/Core/Geometry/Line2D.cs
index c3b2a9c..61e4db9 100644
--- a/Source/Core/Geometry/Line2D.cs
+++ b/Source/Core/Geometry/Line2D.cs
@@ -17,6 +17,7 @@
#region ================== Namespaces
using System;
+using CodeImp.DoomBuilder.Map;
#endregion
@@ -65,6 +66,13 @@ namespace CodeImp.DoomBuilder.Geometry
this.v1 = new Vector2D(x1, y1);
this.v2 = new Vector2D(x2, y2);
}
+
+ //mxd. Constructor
+ public Line2D(Linedef line)
+ {
+ this.v1 = line.Start.Position;
+ this.v2 = line.End.Position;
+ }
#endregion
diff --git a/Source/Core/Geometry/LinedefSide.cs b/Source/Core/Geometry/LinedefSide.cs
index 2a49641..3704865 100644
--- a/Source/Core/Geometry/LinedefSide.cs
+++ b/Source/Core/Geometry/LinedefSide.cs
@@ -35,6 +35,7 @@ namespace CodeImp.DoomBuilder.Geometry
private Linedef line;
private bool front;
+ private bool ignore; //mxd
#endregion
@@ -42,6 +43,7 @@ namespace CodeImp.DoomBuilder.Geometry
public Linedef Line { get { return line; } set { line = value; } }
public bool Front { get { return front; } set { front = value; } }
+ public bool Ignore { get { return ignore; } set { ignore = value; } } //mxd
#endregion
@@ -109,7 +111,7 @@ namespace CodeImp.DoomBuilder.Geometry
{
Sidedef side = (front ? line.Front : line.Back);
Sector sector = (side != null ? side.Sector : null);
- return line + " (" + (front ? "front" : "back") + ")" + (sector != null ? ", Sector " + sector.Index : "");
+ return line + " (" + (front ? "front" : "back") + ")" + (sector != null ? ", Sector " + sector.Index : ", no sector");
}
#endif
diff --git a/Source/Core/Geometry/Tools.cs b/Source/Core/Geometry/Tools.cs
index 140833b..f8c8f86 100644
--- a/Source/Core/Geometry/Tools.cs
+++ b/Source/Core/Geometry/Tools.cs
@@ -1262,8 +1262,8 @@ namespace CodeImp.DoomBuilder.Geometry
// self intersections for which splits were made above.
map.Update(true, false);
map.BeginAddRemove();
- MapSet.SplitLinesByVertices(newlines, intersectverts, MapSet.STITCH_DISTANCE, null);
- MapSet.SplitLinesByVertices(newlines, mergeverts, MapSet.STITCH_DISTANCE, null);
+ MapSet.SplitLinesByVertices(newlines, intersectverts, MapSet.STITCH_DISTANCE, null, false);
+ MapSet.SplitLinesByVertices(newlines, mergeverts, MapSet.STITCH_DISTANCE, null, false);
map.EndAddRemove();
/***************************************************\
@@ -1330,7 +1330,7 @@ namespace CodeImp.DoomBuilder.Geometry
// Before this point, the new geometry is not linked with the existing geometry.
// Now perform standard geometry stitching to merge the new geometry with the rest
// of the map. The marked vertices indicate the new geometry.
- map.StitchGeometry();
+ map.StitchGeometry(false);
map.Update(true, false);
// Find our new lines again, because they have been merged with the other geometry
@@ -2230,7 +2230,6 @@ namespace CodeImp.DoomBuilder.Geometry
}
}
- //mxd
private static bool SectorWasInvalid(Sector s)
{
if(s.Sidedefs.Count < 3 || s.FlatVertices.Length < 3)
@@ -2359,263 +2358,18 @@ namespace CodeImp.DoomBuilder.Geometry
return 0;
}
- //mxd. Try to create/remove/reassign outer sidedefs. Selected linedefs and verts are marked
- public static HashSet AdjustOuterSidedefs(HashSet selectedsectors, HashSet selectedlines)
- {
- HashSet adjustedsides = new HashSet();
- HashSet outersides = new HashSet();
- HashSet innersides = new HashSet();
- HashSet singlesidedlines = new HashSet();
- HashSet lineswithoutsides = new HashSet();
-
- // Collect lines without sidedefs and inner and outer sides
- foreach(Linedef line in selectedlines)
- {
- if(line.Front == null && line.Back == null)
- {
- lineswithoutsides.Add(line);
- }
- else
- {
- if(line.Back != null && line.Back.Sector != null)
- {
- if(!selectedsectors.Contains(line.Back.Sector)) outersides.Add(line.Back);
- else innersides.Add(line.Back);
- }
-
- if(line.Front != null && line.Front.Sector != null)
- {
- if(!selectedsectors.Contains(line.Front.Sector)) outersides.Add(line.Front);
- else innersides.Add(line.Front);
- }
- }
- }
-
- // Collect inner and outer sides and single-sided lines
- foreach(Sector sector in selectedsectors)
- {
- foreach(Sidedef side in sector.Sidedefs)
- {
- if(side.Other == null) singlesidedlines.Add(side.Line);
- else if(!selectedsectors.Contains(side.Other.Sector)) outersides.Add(side.Other);
- innersides.Add(side);
- }
- }
-
- // Check lines without sidedefs. Add new sidedefs if necessary
- foreach(Linedef line in lineswithoutsides)
- {
- bool sideschanged = false;
-
- // Add front side?
- Vector2D testpoint = line.GetSidePoint(true);
- Linedef nl = General.Map.Map.NearestLinedef(testpoint, selectedlines);
- if(nl != null)
- {
- Sidedef ns = (nl.SideOfLine(testpoint) <= 0 ? nl.Front : nl.Back);
- if(ns != null)
- {
- // Create new sidedef
- Sidedef newside = General.Map.Map.CreateSidedef(line, true, ns.Sector);
-
- // Copy props from the other side
- ns.CopyPropertiesTo(newside);
-
- // Store
- adjustedsides.Add(newside);
-
- sideschanged = true;
- }
- }
-
- // Add back side?
- testpoint = line.GetSidePoint(false);
- nl = General.Map.Map.NearestLinedef(testpoint, selectedlines);
- if(nl != null)
- {
- Sidedef ns = (nl.SideOfLine(testpoint) <= 0 ? nl.Front : nl.Back);
- if(ns != null)
- {
- // Create new sidedef
- Sidedef newside = General.Map.Map.CreateSidedef(line, false, ns.Sector);
-
- // Copy props from the other side
- ns.CopyPropertiesTo(newside);
-
- // Store
- adjustedsides.Add(newside);
-
- sideschanged = true;
- }
- }
-
- // Correct the sided flags
- if(sideschanged)
- {
- // Correct the linedef
- if((line.Front == null) && (line.Back != null))
- {
- line.FlipVertices();
- line.FlipSidedefs();
- }
-
- // Correct the sided flags
- line.ApplySidedFlags();
- }
- }
-
- // These steps must be done in 2 phases, otherwise we'll end up getting sidedefs modified in a previous adjustment loop step
-
- // Find sidedefs to join singlesided lines
- Dictionary linesectorref = new Dictionary();
- foreach(Linedef line in singlesidedlines)
- {
- // Line is now inside a sector? (check from the missing side!)
- Sector nearest = FindPotentialSector(line, (line.Front == null), outersides);
-
- // We can reattach our line!
- if(nearest != null) linesectorref[line] = nearest;
- }
-
- // Find sidedefs to join outer sides
- Dictionary sidesectorref = new Dictionary();
- foreach(Sidedef side in outersides)
- {
- // Side is inside a sector?
- Sector nearest = FindPotentialSector(side.Line, side.IsFront, outersides);
-
- // We can reattach our side!
- if(nearest != null)
- {
- if(side.Sector != nearest) sidesectorref[side] = nearest; // This side will be reattached in phase 2
- else sidesectorref.Remove(side); // This side is already attached where it needs to be
-
- // Store
- adjustedsides.Add(side);
- }
- else
- {
- sidesectorref[side] = null; // This side will be removed in phase 2
- }
- }
-
- // Check single-sided lines. Add new sidedefs if necessary
- // Key is dragged single-sided line, value is a sector dragged line ended up in.
- foreach(KeyValuePair group in linesectorref)
- {
- Linedef line = group.Key;
-
- // Create new sidedef
- Sidedef newside = General.Map.Map.CreateSidedef(line, line.Front == null, group.Value);
-
- // Copy props from the other side
- Sidedef propssource = (line.Front ?? line.Back);
- propssource.CopyPropertiesTo(newside);
-
- // Store
- adjustedsides.Add(newside);
-
- // Correct the linedef
- if((line.Front == null) && (line.Back != null))
- {
- line.FlipVertices();
- line.FlipSidedefs();
- }
-
- // Correct the sided flags
- line.ApplySidedFlags();
- }
-
- // Check outer sidedefs. Remove/change sector if necessary
- // Key is outer sidedef of dragged geometry, value is a sector dragged side ended up in.
- foreach(KeyValuePair group in sidesectorref)
- {
- if(group.Value == null)
- {
- // Other side textures may require updating...
- if(group.Key.Other != null) adjustedsides.Add(group.Key.Other);
-
- // Side points nowhere. Remove it
- Linedef l = group.Key.Line;
- group.Key.Dispose();
-
- // Correct the linedef
- if((l.Front == null) && (l.Back != null))
- {
- l.FlipVertices();
- l.FlipSidedefs();
- }
-
- // Correct the sided flags
- l.ApplySidedFlags();
- }
- else
- {
- // Reattach side
- group.Key.SetSector(group.Value);
-
- // Store
- adjustedsides.Add(group.Key);
- }
- }
-
- // Inner side textures may need updating
- foreach(Sidedef s in innersides)
- {
- if(!s.IsDisposed) s.RemoveUnneededTextures(s.Other != null, false, true);
- }
-
- // Update map geometry
- General.Map.Map.Update();
-
- // Done
- return adjustedsides;
- }
-
//mxd
- private static Sector FindPotentialSector(Linedef line, bool front, HashSet sidestoexclude)
+ public static Sector FindPotentialSector(Linedef line, bool front)
{
List sectorsides = FindPotentialSectorAt(line, front);
if(sectorsides == null) return null;
Sector result = null;
- // Special case: if sectorsides match sidestoexclude and all sidestoexclude reference the same sector, return that sector
- if(sidestoexclude.Count > 2 && sectorsides.Count == sidestoexclude.Count)
- {
- bool allsidesmatch = true;
-
- // Check if all sidestoexclude reference the same sector...
- foreach(Sidedef s in sidestoexclude)
- {
- if(result == null) result = s.Sector;
- else if(result != s.Sector)
- {
- allsidesmatch = false;
- break;
- }
- }
-
- // Check if sidestoexclude match sectorsides...
- if(allsidesmatch)
- {
- HashSet sectorsidesset = new HashSet();
- foreach(LinedefSide ls in sectorsides)
- {
- sectorsidesset.Add(ls.Front ? ls.Line.Front : ls.Line.Back);
- }
-
- allsidesmatch = sectorsidesset.SetEquals(sidestoexclude);
- }
-
- // Sides are already where they need to be
- if(allsidesmatch) return result;
- }
-
- // Filter outersides from the list, proceed only if all sectorsides reference the same sector
+ // Proceed only if all sectorsides reference the same sector
foreach(LinedefSide sectorside in sectorsides)
{
Sidedef target = (sectorside.Front ? sectorside.Line.Front : sectorside.Line.Back);
- if(target != null && !sidestoexclude.Contains(target))
+ if(target != null)
{
if(result == null) result = target.Sector;
else if(result != target.Sector) return null; // Fial...
diff --git a/Source/Core/Map/Linedef.cs b/Source/Core/Map/Linedef.cs
index 691e385..22a4e86 100644
--- a/Source/Core/Map/Linedef.cs
+++ b/Source/Core/Map/Linedef.cs
@@ -1255,7 +1255,7 @@ namespace CodeImp.DoomBuilder.Map
{
// Check which lines were 2 sided
bool otherwas2s = ((other.Front != null) && (other.Back != null));
- //bool thiswas2s = ((this.Front != null) && (this.Back != null));
+ bool thiswas2s = ((this.Front != null) && (this.Back != null));
// Get sector references
Sector otherfs = (other.front != null ? other.front.Sector : null);
@@ -1359,8 +1359,7 @@ namespace CodeImp.DoomBuilder.Map
// Other line with its back to this?
if(other.start == this.end)
{
- //mxd. Marked sector means other side belongs to a sector being moved...
- if(otherbs == null || !otherbs.Marked)
+ if(otherbs == null)
{
// Copy textures
if(other.back != null) other.back.AddTexturesTo(this.front);
@@ -1371,8 +1370,7 @@ namespace CodeImp.DoomBuilder.Map
}
else
{
- //mxd. Marked sector means other side belongs to a sector being moved...
- if(otherfs == null || !otherfs.Marked)
+ if(otherfs == null)
{
// Copy textures
if(other.front != null) other.front.AddTexturesTo(this.front);
@@ -1387,9 +1385,8 @@ namespace CodeImp.DoomBuilder.Map
// This line with its back to the other?
if(this.start == other.end)
{
- //mxd. Marked sector means other side belongs to a sector being moved...
//mxd. Attach our front to other back?
- if(otherbs == null || !otherbs.Marked)
+ if(otherbs == null)
{
// Copy textures
if(other.back != null) other.back.AddTexturesTo(this.front);
@@ -1399,7 +1396,7 @@ namespace CodeImp.DoomBuilder.Map
if(front != null && !JoinChangeSidedefs(other, false, front)) return false;
}
//mxd. Attach our back to other front?
- else if(otherfs == null || !otherfs.Marked)
+ else if(otherfs == null)
{
// Copy textures
if(other.front != null) other.front.AddTexturesTo(this.back);
@@ -1412,9 +1409,8 @@ namespace CodeImp.DoomBuilder.Map
// Both lines face the same way
else
{
- //mxd. Marked sector means other side belongs to a sector being moved...
//mxd. Attach our back to other back?
- if(otherbs == null || !otherbs.Marked)
+ if(otherbs == null)
{
// Copy textures
if(other.back != null) other.back.AddTexturesTo(this.back);
@@ -1424,7 +1420,7 @@ namespace CodeImp.DoomBuilder.Map
if(back != null && !JoinChangeSidedefs(other, false, back)) return false;
}
//mxd. Attach our front to other front?
- else if(otherfs == null || !otherfs.Marked)
+ else if(otherfs == null)
{
// Copy textures
if(other.front != null) other.front.AddTexturesTo(this.front);
@@ -1443,8 +1439,8 @@ namespace CodeImp.DoomBuilder.Map
other.ApplySidedFlags();
// Remove unneeded textures
- //if(other.front != null) other.front.RemoveUnneededTextures(!(otherwas2s && thiswas2s));
- //if(other.back != null) other.back.RemoveUnneededTextures(!(otherwas2s && thiswas2s));
+ if(other.front != null) other.front.RemoveUnneededTextures(!(otherwas2s && thiswas2s));
+ if(other.back != null) other.back.RemoveUnneededTextures(!(otherwas2s && thiswas2s));
}
// If either of the two lines was selected, keep the other selected
diff --git a/Source/Core/Map/MapSet.cs b/Source/Core/Map/MapSet.cs
index 8c0e0d6..406e9b9 100644
--- a/Source/Core/Map/MapSet.cs
+++ b/Source/Core/Map/MapSet.cs
@@ -1989,9 +1989,9 @@ namespace CodeImp.DoomBuilder.Map
}
/// This filters lines by a rectangular area.
- public static ICollection FilterByArea(ICollection lines, ref RectangleF area)
+ public static List FilterByArea(ICollection lines, ref RectangleF area)
{
- ICollection newlines = new List(lines.Count);
+ List newlines = new List(lines.Count);
// Go for all lines
foreach(Linedef l in lines)
@@ -2046,17 +2046,18 @@ namespace CodeImp.DoomBuilder.Map
///
/// Stitches marked geometry with non-marked geometry. Returns false when the operation failed.
///
- public bool StitchGeometry()
+ public bool StitchGeometry() { return StitchGeometry(false); } //mxd. Compatibility
+ public bool StitchGeometry(bool correctsectorrefs)
{
// Find vertices
ICollection movingverts = General.Map.Map.GetMarkedVertices(true);
ICollection fixedverts = General.Map.Map.GetMarkedVertices(false);
// Find lines that moved during the drag
- ICollection movinglines = LinedefsFromMarkedVertices(false, true, true);
+ List movinglines = LinedefsFromMarkedVertices(false, true, true);
// Find all non-moving lines
- ICollection fixedlines = LinedefsFromMarkedVertices(true, false, false);
+ List fixedlines = LinedefsFromMarkedVertices(true, false, false);
// Determine area in which we are editing
RectangleF editarea = CreateArea(movinglines);
@@ -2075,25 +2076,383 @@ namespace CodeImp.DoomBuilder.Map
// Split moving lines with unselected vertices
ICollection nearbyfixedverts = FilterByArea(fixedverts, ref editarea);
- if(!SplitLinesByVertices(movinglines, nearbyfixedverts, STITCH_DISTANCE, movinglines))
+ if(!SplitLinesByVertices(movinglines, nearbyfixedverts, STITCH_DISTANCE, movinglines, correctsectorrefs))
return false;
// Split non-moving lines with selected vertices
fixedlines = FilterByArea(fixedlines, ref editarea);
- if(!SplitLinesByVertices(fixedlines, movingverts, STITCH_DISTANCE, movinglines))
+ if(!SplitLinesByVertices(fixedlines, movingverts, STITCH_DISTANCE, movinglines, correctsectorrefs))
return false;
+
+ //mxd. Split moving lines with fixed lines
+ if(!SplitLinesByLines(fixedlines, movinglines, correctsectorrefs)) return false;
// Remove looped linedefs
RemoveLoopedLinedefs(movinglines);
// Join overlapping lines
- if(!JoinOverlappingLines(movinglines))
- return false;
-
+ if(!JoinOverlappingLines(movinglines)) return false;
+
EndAddRemove();
+
+ //mxd. Correct sector references
+ if(correctsectorrefs)
+ {
+ // Linedefs cache needs to be up to date...
+ Update(true, false);
+
+ // Fix stuff...
+ List changedlines = LinedefsFromMarkedVertices(false, true, true);
+ CorrectSectorReferences(changedlines, true);
+ CorrectOuterSides(new HashSet(changedlines));
+ }
return true;
}
+
+ //mxd. Shameless SLADEMap::correctSectors ripoff... Corrects/builds sectors for all lines in [lines]
+ private static void CorrectSectorReferences(List lines, bool existing_only)
+ {
+ // Create a list of sidedefs to perform sector creation with
+ List edges = new List();
+ if(existing_only)
+ {
+ foreach(Linedef l in lines)
+ {
+ // Add only existing sides as edges (or front side if line has none)
+ if(l.Front != null || l.Back == null)
+ edges.Add(new LinedefSide(l, true));
+ if(l.Back != null)
+ edges.Add(new LinedefSide(l, false));
+ }
+ }
+ else
+ {
+ foreach(Linedef l in lines)
+ {
+ // Add front side
+ edges.Add(new LinedefSide(l, true));
+
+ // Add back side if there's a sector
+ if(General.Map.Map.GetSectorByCoordinates(l.GetSidePoint(false)) != null)
+ edges.Add(new LinedefSide(l, false));
+ }
+ }
+
+ HashSet sides_correct = new HashSet();
+ foreach(LinedefSide ls in edges)
+ {
+ if(ls.Front && ls.Line.Front != null)
+ sides_correct.Add(ls.Line.Front);
+ else if(!ls.Front && ls.Line.Back != null)
+ sides_correct.Add(ls.Line.Back);
+ }
+
+ //mxd. Get affected sectors
+ HashSet affectedsectors = new HashSet(General.Map.Map.GetSelectedSectors(true));
+ affectedsectors.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(lines));
+
+ //mxd. Collect their lines
+ HashSet sectorlines = new HashSet();
+ foreach(Sector s in affectedsectors)
+ {
+ foreach(Sidedef side in s.Sidedefs)
+ {
+ if(side.Line != null) sectorlines.Add(side.Line);
+ }
+ }
+
+ // Build sectors
+ SectorBuilder builder = new SectorBuilder();
+ List sectors_reused = new List();
+
+ foreach(LinedefSide ls in edges)
+ {
+ // Skip if edge is ignored
+ if(ls.Ignore) continue;
+
+ // Run sector builder on current edge
+ if(!builder.TraceSector(ls.Line, ls.Front)) continue; // Don't create sector if trace failed
+
+ // Find any subsequent edges that were part of the sector created
+ bool has_existing_lines = false;
+ bool has_existing_sides = false;
+ bool has_zero_sided_lines = false;
+ bool has_sides_belonging_to_dragged_sectors = false; //mxd
+ List edges_in_sector = new List();
+ foreach(LinedefSide edge in builder.SectorEdges)
+ {
+ bool line_is_ours = false;
+ if(sectorlines.Contains(edge.Line)) has_sides_belonging_to_dragged_sectors = true; //mxd
+
+ foreach(LinedefSide ls2 in edges)
+ {
+ if(ls2.Line == edge.Line)
+ {
+ line_is_ours = true;
+ if(ls2.Front == edge.Front)
+ {
+ edges_in_sector.Add(ls2);
+ break;
+ }
+ }
+ }
+
+ if(line_is_ours)
+ {
+ if(edge.Line.Front == null && edge.Line.Back == null)
+ has_zero_sided_lines = true;
+ }
+ else
+ {
+ has_existing_lines = true;
+ if(edge.Front ? edge.Line.Front != null : edge.Line.Back != null)
+ has_existing_sides = true;
+ }
+ }
+
+ // Pasting or moving a two-sided line into an enclosed void should NOT
+ // create a new sector out of the entire void.
+ // Heuristic: if the traced sector includes any edges that are NOT
+ // "ours", and NONE of those edges already exist, that sector must be
+ // in an enclosed void, and should not be drawn.
+ // However, if existing_only is false, the caller expects us to create
+ // new sides anyway; skip this check.
+ if(existing_only && has_existing_lines && !has_existing_sides && !has_sides_belonging_to_dragged_sectors) continue;
+
+ // Ignore traced edges when trying to create any further sectors
+ foreach(LinedefSide ls3 in edges_in_sector) ls3.Ignore = true;
+
+ // Check if sector traced is already valid
+ if(builder.IsValidSector()) continue;
+
+ // Check if we traced over an existing sector (or part of one)
+ Sector sector = builder.FindExistingSector(sides_correct);
+ if(sector != null)
+ {
+ // Check if it's already been (re)used
+ bool reused = false;
+ foreach(Sector s in sectors_reused)
+ {
+ if(s == sector)
+ {
+ reused = true;
+ break;
+ }
+ }
+
+ // If we can reuse the sector, do so
+ if(!reused)
+ sectors_reused.Add(sector);
+ else
+ sector = null;
+ }
+
+ // Create sector
+ builder.CreateSector(sector, null);
+ }
+
+ // Remove any sides that weren't part of a sector
+ foreach(LinedefSide ls in edges)
+ {
+ if(ls.Ignore || ls.Line == null) continue;
+
+ if(ls.Front)
+ {
+ if(ls.Line.Front != null)
+ {
+ ls.Line.Front.Dispose();
+
+ // Update doublesided flag
+ ls.Line.ApplySidedFlags();
+ }
+ }
+ else
+ {
+ if(ls.Line.Back != null)
+ {
+ ls.Line.Back.Dispose();
+
+ // Update doublesided flag
+ ls.Line.ApplySidedFlags();
+ }
+ }
+ }
+
+ // Check if any lines need to be flipped
+ FlipBackwardLinedefs(lines);
+
+ // Find an adjacent sector to copy properties from
+ Sector sector_copy = null;
+ foreach(Linedef l in lines)
+ {
+ // Check front sector
+ Sector sector = (l.Front != null ? l.Front.Sector : null);
+ if(sector != null && !sector.Marked)
+ {
+ // Copy this sector if it isn't newly created
+ sector_copy = sector;
+ break;
+ }
+
+ // Check back sector
+ sector = (l.Back != null ? l.Back.Sector : null);
+ if(sector != null && !sector.Marked)
+ {
+ // Copy this sector if it isn't newly created
+ sector_copy = sector;
+ break;
+ }
+ }
+
+ // Go through newly created sectors
+ List newsectors = General.Map.Map.GetMarkedSectors(true); //mxd
+ foreach(Sector s in newsectors)
+ {
+ // Skip if sector already has properties
+ if(s.CeilTexture != "-") continue;
+
+ // Copy from adjacent sector if any
+ if(sector_copy != null)
+ {
+ sector_copy.CopyPropertiesTo(s);
+ continue;
+ }
+
+ // Otherwise, use defaults from game configuration
+ s.SetFloorTexture(General.Map.Options.DefaultFloorTexture);
+ s.SetCeilTexture(General.Map.Options.DefaultCeilingTexture);
+ s.FloorHeight = General.Settings.DefaultFloorHeight;
+ s.CeilHeight = General.Settings.DefaultCeilingHeight;
+ s.Brightness = General.Settings.DefaultBrightness;
+ }
+
+ // Update line textures
+ List newsides = General.Map.Map.GetMarkedSidedefs(true);
+ foreach(Sidedef side in newsides)
+ {
+ // Clear any unneeded textures
+ side.RemoveUnneededTextures(side.Other != null);
+
+ // Set middle texture if needed
+ if(side.MiddleRequired() && side.MiddleTexture == "-")
+ {
+ // Find adjacent texture (any)
+ string tex = GetAdjacentMiddleTexture(side.Line.Start);
+ if(tex == "-") tex = GetAdjacentMiddleTexture(side.Line.End);
+
+ // If no adjacent texture, get default from game configuration
+ if(tex == "-") tex = General.Settings.DefaultTexture;
+
+ // Set texture
+ side.SetTextureMid(tex);
+ }
+
+ // Update sided flags
+ side.Line.ApplySidedFlags();
+ }
+
+ // Remove any extra sectors
+ General.Map.Map.RemoveUnusedSectors(false);
+ }
+
+ //mxd. Try to create outer sidedefs if needed
+ private static void CorrectOuterSides(HashSet changedlines)
+ {
+ HashSet linesmissingfront = new HashSet();
+ HashSet linesmissingback = new HashSet();
+
+ // Collect lines without front/back sides
+ foreach(Linedef line in changedlines)
+ {
+ if(line.Back == null) linesmissingback.Add(line);
+ if(line.Front == null) linesmissingfront.Add(line);
+ }
+
+ // Find sectors to join singlesided lines
+ Dictionary linefrontsectorref = new Dictionary();
+ foreach(Linedef line in linesmissingfront)
+ {
+ // Line is now inside a sector? (check from the missing side!)
+ Sector nearest = Tools.FindPotentialSector(line, true);
+
+ // We can reattach our line!
+ if(nearest != null) linefrontsectorref[line] = nearest;
+ }
+
+ Dictionary linebacksectorref = new Dictionary();
+ foreach(Linedef line in linesmissingback)
+ {
+ // Line is now inside a sector? (check from the missing side!)
+ Sector nearest = Tools.FindPotentialSector(line, false);
+
+ // We can reattach our line!
+ if(nearest != null) linebacksectorref[line] = nearest;
+ }
+
+ // Check single-sided lines. Add new sidedefs if necessary
+ // Key is dragged single-sided line, value is a sector dragged line ended up in.
+ foreach(KeyValuePair group in linefrontsectorref)
+ {
+ Linedef line = group.Key;
+
+ // Create new sidedef
+ Sidedef newside = General.Map.Map.CreateSidedef(line, true, group.Value);
+
+ // Copy props from the other side
+ Sidedef propssource = (line.Front ?? line.Back);
+ propssource.CopyPropertiesTo(newside);
+
+ // Correct the linedef
+ if((line.Front == null) && (line.Back != null))
+ {
+ line.FlipVertices();
+ line.FlipSidedefs();
+ }
+ }
+
+ foreach(KeyValuePair group in linebacksectorref)
+ {
+ Linedef line = group.Key;
+
+ // Create new sidedef
+ Sidedef newside = General.Map.Map.CreateSidedef(line, false, group.Value);
+
+ // Copy props from the other side
+ Sidedef propssource = (line.Front ?? line.Back);
+ propssource.CopyPropertiesTo(newside);
+
+ // Correct the linedef
+ if((line.Front == null) && (line.Back != null))
+ {
+ line.FlipVertices();
+ line.FlipSidedefs();
+ }
+ }
+
+ // Adjust textures
+ foreach(Linedef l in changedlines)
+ {
+ if(l.Front != null) l.Front.RemoveUnneededTextures(l.Back != null);
+ if(l.Back != null) l.Back.RemoveUnneededTextures(l.Front != null);
+
+ // Correct the sided flags
+ l.ApplySidedFlags();
+ }
+ }
+
+ //mxd
+ private static string GetAdjacentMiddleTexture(Vertex v)
+ {
+ // Go through adjacent lines
+ foreach(Linedef l in v.Linedefs)
+ {
+ if(l.Front != null && l.Front.MiddleTexture != "-") return l.Front.MiddleTexture;
+ if(l.Back != null && l.Back.MiddleTexture != "-") return l.Back.MiddleTexture;
+ }
+
+ return "-";
+ }
#endregion
@@ -2422,10 +2781,11 @@ namespace CodeImp.DoomBuilder.Map
/// This splits the given lines with the given vertices. All affected lines
/// will be added to changedlines. Returns false when the operation failed.
- public static bool SplitLinesByVertices(ICollection lines, ICollection verts, float splitdist, ICollection changedlines)
+ public static bool SplitLinesByVertices(ICollection lines, ICollection verts, float splitdist, ICollection changedlines) { return SplitLinesByVertices(lines, verts, splitdist, changedlines, false); }
+ public static bool SplitLinesByVertices(ICollection lines, ICollection verts, float splitdist, ICollection changedlines, bool removeinnerlines)
{
- if(verts.Count == 0 || lines.Count == 0) return true; //mxd
-
+ if (verts.Count == 0 || lines.Count == 0) return true; //mxd
+
float splitdist2 = splitdist * splitdist;
//mxd. Create blockmap
@@ -2492,7 +2852,127 @@ namespace CodeImp.DoomBuilder.Map
}
}
}
+ }
+ }
+ }
+ return true;
+ }
+
+ /// Splits lines by lines. Adds new lines to the second collection. Returns false when the operation failed.
+ public static bool SplitLinesByLines(IList lines, IList changedlines, bool removeinnerlines) //mxd
+ {
+ if(lines.Count == 0 || changedlines.Count == 0) return true;
+
+ // Create blockmap
+ RectangleF area = RectangleF.Union(CreateArea(lines), CreateArea(changedlines));
+ BlockMap blockmap = new BlockMap(area);
+ blockmap.AddLinedefsSet(lines);
+ blockmap.AddLinedefsSet(changedlines);
+ int bmWidth = blockmap.Size.Width;
+ int bmHeight = blockmap.Size.Height;
+ BlockEntry[,] bmap = blockmap.Map;
+
+ //mxd
+ HashSet splitverts = new HashSet();
+ HashSet changedsectors = (removeinnerlines ? General.Map.Map.GetSectorsFromLinedefs(changedlines) : new HashSet());
+ HashSet initialchanedlines = new HashSet(changedlines);
+
+ // Check for intersections
+ for(int w = 0; w < bmWidth; w++)
+ {
+ for(int h = 0; h < bmHeight; h++)
+ {
+ BlockEntry block = bmap[w, h];
+ if(block.Lines.Count == 0) continue;
+
+ for(int i = 0; i < block.Lines.Count; i++)
+ {
+ Linedef l1 = block.Lines[i];
+ for(int c = 0; c < block.Lines.Count; c++)
+ {
+ if(i == c) continue;
+
+ Linedef l2 = block.Lines[c];
+ if(l1 == l2
+ || l1.Start.Position == l2.Start.Position
+ || l1.Start.Position == l2.End.Position
+ || l1.End.Position == l2.Start.Position
+ || l1.End.Position == l2.End.Position) continue;
+
+ // Check for intersection
+ Vector2D intersection = Line2D.GetIntersectionPoint(new Line2D(l1), new Line2D(l2), true);
+ if(!float.IsNaN(intersection.x))
+ {
+ // Create split vertex
+ Vertex splitvertex = General.Map.Map.CreateVertex(intersection);
+ if(splitvertex == null) return false;
+
+ // Split both lines
+ Linedef nl1 = l1.Split(splitvertex);
+ if(nl1 == null) return false;
+
+ Linedef nl2 = l2.Split(splitvertex);
+ if(nl2 == null) return false;
+
+ // Mark split vertex
+ splitvertex.Marked = true;
+ splitverts.Add(splitvertex); //mxd
+
+ // Add to the second collection
+ changedlines.Add(nl1);
+ changedlines.Add(nl2);
+
+ // And to the block entry
+ blockmap.AddLinedef(nl1);
+ blockmap.AddLinedef(nl2);
+ }
+ }
+ }
+ }
+ }
+
+ //mxd. Remove lines, which are inside affected sectors
+ if(removeinnerlines)
+ {
+ HashSet alllines = new HashSet(lines);
+ alllines.UnionWith(changedlines);
+
+ foreach(Linedef l in alllines) l.UpdateCache();
+ foreach(Sector s in changedsectors) s.UpdateBBox();
+ foreach(Linedef l in alllines)
+ {
+ // Remove line when both it's start and end are inside a changed sector and neither side references it
+ if(l.Start != null && l.End != null && !initialchanedlines.Contains(l) &&
+ (l.Front == null || !changedsectors.Contains(l.Front.Sector)) &&
+ (l.Back == null || !changedsectors.Contains(l.Back.Sector)))
+ {
+ foreach(Sector s in changedsectors)
+ {
+ if(s.Intersect(l.Start.Position) && s.Intersect(l.End.Position))
+ {
+ Vertex[] tocheck = new[] { l.Start, l.End };
+ l.Dispose();
+
+ foreach(Vertex v in tocheck)
+ {
+ // If the vertex only has 2 linedefs attached, then merge the linedefs
+ if(!v.IsDisposed && v.Linedefs.Count == 2)
+ {
+ Linedef ld1 = General.GetByIndex(v.Linedefs, 0);
+ Linedef ld2 = General.GetByIndex(v.Linedefs, 1);
+ Vertex v2 = (ld2.Start == v) ? ld2.End : ld2.Start;
+ if(ld1.Start == v) ld1.SetStartVertex(v2); else ld1.SetEndVertex(v2);
+ ld2.Dispose();
+
+ // Trash vertex
+ v.Dispose();
+ }
+ }
+
+ break;
+ }
+ }
}
}
}
@@ -3028,7 +3508,7 @@ namespace CodeImp.DoomBuilder.Map
/// This makes a list of lines related to marked vertices.
/// A line is unstable when one vertex is marked and the other isn't.
- public ICollection LinedefsFromMarkedVertices(bool includeunselected, bool includestable, bool includeunstable)
+ public List LinedefsFromMarkedVertices(bool includeunmarked, bool includestable, bool includeunstable)
{
List list = new List((numlinedefs / 2) + 1);
@@ -3038,7 +3518,7 @@ namespace CodeImp.DoomBuilder.Map
// Check if this is to be included
if((includestable && (l.Start.Marked && l.End.Marked)) ||
(includeunstable && (l.Start.Marked ^ l.End.Marked)) ||
- (includeunselected && (!l.Start.Marked && !l.End.Marked)))
+ (includeunmarked && (!l.Start.Marked && !l.End.Marked)))
{
// Add to list
list.Add(l);
@@ -3137,6 +3617,37 @@ namespace CodeImp.DoomBuilder.Map
return result;
}
+ //mxd
+ /// Gets sectors, which have all their linedefs selected
+ public HashSet GetSectorsFromLinedefs(IEnumerable lines)
+ {
+ HashSet result = new HashSet();
+ Dictionary> sectorsbysides = new Dictionary>();
+
+ // Collect unselected sectors, which sidedefs belong to selected lines
+ foreach(Linedef line in lines)
+ {
+ if(line.Front != null && line.Front.Sector != null)
+ {
+ if(!sectorsbysides.ContainsKey(line.Front.Sector)) sectorsbysides.Add(line.Front.Sector, new HashSet());
+ sectorsbysides[line.Front.Sector].Add(line.Front);
+ }
+ if(line.Back != null && line.Back.Sector != null)
+ {
+ if(!sectorsbysides.ContainsKey(line.Back.Sector)) sectorsbysides.Add(line.Back.Sector, new HashSet());
+ sectorsbysides[line.Back.Sector].Add(line.Back);
+ }
+ }
+
+ // Add sectors, which have all their lines selected
+ foreach(var group in sectorsbysides)
+ {
+ if(group.Key.Sidedefs.Count == group.Value.Count) result.Add(group.Key);
+ }
+
+ return result;
+ }
+
/// This finds the line closest to the specified position.
public Linedef NearestLinedef(Vector2D pos) { return MapSet.NearestLinedef(linedefs, pos); }
diff --git a/Source/Core/Map/Sector.cs b/Source/Core/Map/Sector.cs
index 259ca97..f6c1533 100644
--- a/Source/Core/Map/Sector.cs
+++ b/Source/Core/Map/Sector.cs
@@ -524,44 +524,36 @@ namespace CodeImp.DoomBuilder.Map
}
// This checks if the given point is inside the sector polygon
+ // See: http://paulbourke.net/geometry/polygonmesh/index.html#insidepoly
public bool Intersect(Vector2D p)
{
//mxd. Check bounding box first
if(p.x < bbox.Left || p.x > bbox.Right || p.y < bbox.Top || p.y > bbox.Bottom) return false;
uint c = 0;
+ Vector2D v1, v2;
// Go for all sidedefs
foreach(Sidedef sd in sidedefs)
{
// Get vertices
- Vector2D v1 = sd.Line.Start.Position;
- Vector2D v2 = sd.Line.End.Position;
+ v1 = sd.Line.Start.Position;
+ v2 = sd.Line.End.Position;
//mxd. On top of a vertex?
if(p == v1 || p == v2) return true;
-
- // Determine min/max values
- float miny = Math.Min(v1.y, v2.y);
- float maxy = Math.Max(v1.y, v2.y);
- float maxx = Math.Max(v1.x, v2.x);
-
+
// Check for intersection
- if((p.y > miny) && (p.y <= maxy))
- {
- if(p.x <= maxx)
- {
- if(v1.y != v2.y)
- {
- float xint = (p.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x;
- if((v1.x == v2.x) || (p.x <= xint)) c++;
- }
- }
- }
+ if(v1.y != v2.y //mxd. If line is not horizontal...
+ && p.y > (v1.y < v2.y ? v1.y : v2.y) //mxd. ...And test point y intersects with the line y bounds...
+ && p.y <= (v1.y > v2.y ? v1.y : v2.y) //mxd
+ && (p.x < (v1.x < v2.x ? v1.x : v2.x) || (p.x <= (v1.x > v2.x ? v1.x : v2.x) //mxd. ...And test point x is to the left of the line, or is inside line x bounds and intersects it
+ && (v1.x == v2.x || p.x <= ((p.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x)))))
+ c++; //mxd. ...Count the line as crossed
}
-
- // Inside this polygon?
- return ((c & 0x00000001UL) != 0);
+
+ // Inside this polygon when we crossed odd number of polygon lines
+ return (c % 2 != 0);
}
// This creates a bounding box rectangle
@@ -605,6 +597,12 @@ namespace CodeImp.DoomBuilder.Map
// Return rectangle
return new RectangleF(left, top, right - left, bottom - top);
}
+
+ //mxd
+ internal void UpdateBBox()
+ {
+ bbox = CreateBBox();
+ }
// This joins the sector with another sector
// This sector will be disposed
diff --git a/Source/Core/Map/SectorBuilder.cs b/Source/Core/Map/SectorBuilder.cs
new file mode 100644
index 0000000..94adc13
--- /dev/null
+++ b/Source/Core/Map/SectorBuilder.cs
@@ -0,0 +1,549 @@
+#region ================== Namespaces
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using CodeImp.DoomBuilder.Geometry;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.Map
+{
+ //mxd. Shameless Slade 3 SectorBuilder::SectorBuilder ripoff...
+ //TODO: There are lots of overlaps with already existing code.
+ //TODO: Replace with existing implementations if results are the same & existing code performs faster
+ internal sealed class SectorBuilder
+ {
+ #region ================== Variables
+
+ private List sector_edges;
+ private HashSet vertex_valid;
+
+ // Current outline
+ private List o_edges;
+ private bool o_clockwise;
+ private RectangleF o_bbox;
+ private Vertex vertex_right;
+
+ #endregion
+
+ #region ================== Properties
+
+ public List SectorEdges { get { return sector_edges; } }
+
+ #endregion
+
+ #region ================== Constructor
+
+ public SectorBuilder()
+ {
+ sector_edges = new List();
+ vertex_valid = new HashSet();
+ o_edges = new List();
+ }
+
+ #endregion
+
+ #region ================== Methods
+
+ ///Traces all edges to build a closed sector starting from [line]
+ internal bool TraceSector(Linedef line, bool front)
+ {
+ if(line == null) return false;
+
+ //DebugConsole.WriteLine(" ");
+ //DebugConsole.WriteLine("TraceSector for line " + line.Index + (front ? " (front)" : " (back)"));
+
+ // Init
+ sector_edges.Clear();
+
+ // Create valid vertices list
+ vertex_valid = new HashSet(General.Map.Map.Vertices);
+
+ // Find outmost outline
+ for(int a = 0; a < 10000; a++)
+ {
+ // Trace outline
+ if(!TraceOutline(line, front)) break;
+
+ // Discard any vertices outside the traced outline
+ vertex_valid.RemoveWhere(PointOutsideOutline);
+
+ // If it is clockwise, we've found the outmost outline
+ if(o_clockwise) break;
+
+ // Otherwise, find the next edge outside the outline
+ LinedefSide next = FindOuterEdge();
+
+ // If none was found, we're outside the map
+ if(next == null) return false;
+
+ // 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) break;
+
+ // Trace outline from edge
+ if(!TraceOutline(edge.Line, edge.Front)) break;
+
+ // Discard any vertices outside the traced outline
+ vertex_valid.RemoveWhere(PointOutsideOutline);
+ }
+
+ //DebugConsole.WriteLine("FindInnerEdge: " + o_edges.Count + " lines");
+
+ return true;
+ }
+
+ ///Traces the sector outline from lines beginning at [line],
+ /// on either the front or back side ([front])
+ 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();
+ LinedefSide start = new LinedefSide(line, front);
+ o_edges.Add(start);
+ int edge_sum = 0;
+ Dictionary visited_lines = new Dictionary();
+
+ // 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 " + 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);
+
+ // 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
+
+ o_bbox = (o_bbox.IsEmpty ? l_bbox : RectangleF.Union(o_bbox, l_bbox));
+ }
+
+ // Check if outline is clockwise
+ o_clockwise = (edge_sum < 0);
+
+ // Add outline edges to sector edge list
+ sector_edges.AddRange(o_edges);
+
+ // Trace complete
+ return true;
+ }
+
+ /// Finds the next closest edge outside of the current outline (that isn't part of the current outline)
+ 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;
+
+ // Go through map lines
+ 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(dist < min_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!!!
+ //mxd. I've spent 2 days figuring this out... :(
+ }
+
+ /// Find the closest edge within the current outline (that isn't part of the current outline)
+ private LinedefSide FindInnerEdge()
+ {
+ // 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) 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);
+ return FindInnerEdge();
+ }
+
+ // Determine appropriate side
+ return new LinedefSide(eline, (vertex_right == eline.Start));
+ }
+
+ ///Finds the next adjacent edge to [edge], ie the adjacent edge that creates the smallest angle
+ private static LinedefSide NextEdge(LinedefSide edge, Dictionary 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;
+ }
+
+ /// Returns true if the vertex is outside the current outline
+ 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) return false;
+ if(side <= 0 && !o_edges[nearest].Front) return false;
+ }
+
+ // Not within the 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;
+ }
+
+ /// Checks if the traced sector is valid (ie. all edges are currently referencing the same (existing) sector)
+ public bool IsValidSector()
+ {
+ if(sector_edges.Count == 0) return false;
+
+ // Get first edge's sector
+ Sector sector = (sector_edges[0].Front ?
+ (sector_edges[0].Line.Front != null ? sector_edges[0].Line.Front.Sector : null) :
+ (sector_edges[0].Line.Back != null ? sector_edges[0].Line.Back.Sector : null));
+
+ // Sector is invalid if any edge has no current sector
+ if(sector == null) return false;
+
+ // Go through subsequent edges
+ for(int a = 1; a < sector_edges.Count; a++)
+ {
+ // Get edge sector
+ Sector ssector = (sector_edges[a].Front ?
+ (sector_edges[a].Line.Front != null ? sector_edges[a].Line.Front.Sector : null) :
+ (sector_edges[a].Line.Back != null ? sector_edges[a].Line.Back.Sector : null));
+
+ // Check if different
+ if(sector != ssector) return false;
+ }
+
+ // Return true if the entire sector was traced
+ return (sector.Sidedefs.Count == sector_edges.Count);
+ }
+
+ /// Finds any existing sector that is already part of the traced new sector
+ internal Sector FindExistingSector(HashSet sides_ignore)
+ {
+ // Go through new sector edges
+ Sector sector = null;
+ Sector sector_priority = null;
+ foreach(LinedefSide edge in sector_edges)
+ {
+ // Check if the edge's corresponding MapSide has a front sector
+ if(edge.Front && edge.Line.Front != null && edge.Line.Front.Sector != null)
+ {
+ if(sides_ignore.Contains(edge.Line.Front))
+ sector = edge.Line.Front.Sector;
+ else
+ sector_priority = edge.Line.Front.Sector;
+ }
+
+ // Check if the edge's corresponding MapSide has a back sector
+ if(!edge.Front && edge.Line.Back != null && edge.Line.Back.Sector != null)
+ {
+ if(sides_ignore.Contains(edge.Line.Back))
+ sector = edge.Line.Back.Sector;
+ else
+ sector_priority = edge.Line.Back.Sector;
+ }
+ }
+
+ return (sector_priority ?? sector);
+ }
+
+ /// Sets all traced edges to [sector], or creates a new sector using properties
+ /// from [sector_copy] if none given
+ internal void CreateSector(Sector sector, Sector sector_copy)
+ {
+ // Create the sector if needed
+ if(sector == null)
+ {
+ sector = General.Map.Map.CreateSector();
+ if(sector == null) return;
+ sector.Marked = true; //mxd
+
+ // Find potential sector to copy if none specified
+ if(sector_copy == null) sector_copy = FindCopySector();
+ if(sector_copy != null) sector_copy.CopyPropertiesTo(sector);
+ }
+
+ //DebugConsole.WriteLine("Creating sector " + sector.Index + " from " + sector_edges.Count + " lines");
+
+ // Set sides to new sector
+ foreach(LinedefSide edge in sector_edges)
+ {
+ Sidedef target = (edge.Front ? edge.Line.Front : edge.Line.Back);
+ if(target != null)
+ {
+ if(target.Sector != sector)
+ {
+ target.SetSector(sector); //mxd. Reattach side
+ target.Marked = true; //mxd. Mark it
+ }
+ }
+ else
+ {
+ target = General.Map.Map.CreateSidedef(edge.Line, edge.Front, sector); //mxd. Create new side
+ target.Marked = true; //mxd. Mark it
+ }
+ }
+ }
+
+ /// Finds an appropriate existing sector to copy properties from, for the new sector being built
+ private Sector FindCopySector()
+ {
+ // Go through new sector edges
+ Sector sector_copy = null;
+ foreach(LinedefSide edge in sector_edges)
+ {
+ // Check if the edge's corresponding MapSide has a front sector
+ if(edge.Line.Front != null && edge.Line.Front.Sector != null)
+ {
+ // Set sector to copy
+ sector_copy = edge.Line.Front.Sector;
+
+ // If the edge is a front edge, use this sector and ignore all else
+ if(edge.Front) break;
+ }
+
+ // Check if the edge's corresponding MapSide has a back sector
+ if(edge.Line.Back != null && edge.Line.Back.Sector != null)
+ {
+ // Set sector to copy
+ sector_copy = edge.Line.Back.Sector;
+
+ // If the edge is a back edge, use this sector and ignore all else
+ if(!edge.Front) break;
+ }
+ }
+
+ return sector_copy;
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Core/Map/Sidedef.cs b/Source/Core/Map/Sidedef.cs
index 9aeb6cf..cd94be8 100644
--- a/Source/Core/Map/Sidedef.cs
+++ b/Source/Core/Map/Sidedef.cs
@@ -341,7 +341,7 @@ namespace CodeImp.DoomBuilder.Map
if(force || ((linedef.Tag == 0) && (linedef.Action == 0) && (sector.Tag == 0) &&
((Other == null) || (Other.sector.Tag == 0))))
{
- if(!HighRequired())
+ /*if(!HighRequired())
{
BeforePropsChange(); //mxd
changed = true;
@@ -349,13 +349,13 @@ namespace CodeImp.DoomBuilder.Map
this.longtexnamehigh = MapSet.EmptyLongName;
General.Map.IsChanged = true;
}
- else if(shiftmiddle && this.longtexnamehigh == MapSet.EmptyLongName) //mxd
+ else*/ if(shiftmiddle && this.longtexnamehigh == MapSet.EmptyLongName && HighRequired()) //mxd
{
SetTextureHigh(this.texnamemid);
changed = true;
}
- if(!LowRequired())
+ /*if(!LowRequired())
{
if(!changed) //mxd
{
@@ -365,8 +365,8 @@ namespace CodeImp.DoomBuilder.Map
this.texnamelow = "-";
this.longtexnamelow = MapSet.EmptyLongName;
General.Map.IsChanged = true;
- }
- else if(shiftmiddle && this.longtexnamelow == MapSet.EmptyLongName) //mxd
+ }
+ else*/ if(shiftmiddle && this.longtexnamelow == MapSet.EmptyLongName && LowRequired()) //mxd
{
SetTextureLow(this.texnamemid);
changed = true;
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs
index 998e61c..7ba62d2 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs
@@ -419,14 +419,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Move selected geometry to final position
MoveGeometryRelative(mousemappos - dragstartmappos, snaptogrid, snaptogridincrement, snaptonearest, snaptocardinaldirection);
- //mxd. Used in Linedef.Join()...
- General.Map.Map.MarkSelectedSectors(true, true);
-
// Stitch geometry
- General.Map.Map.StitchGeometry();
-
- // Make corrections for backward linedefs
- MapSet.FlipBackwardLinedefs(General.Map.Map.Linedefs);
+ General.Map.Map.StitchGeometry(true);
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy();
@@ -435,11 +429,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(General.Map.UDMF)
{
Vector2D offset = dragitem.Position - dragitemposition;
+
+ // Sectors may've been created/removed when applying dragging...
+ HashSet draggedsectors = new HashSet(General.Map.Map.GetMarkedSectors(true));
+ foreach(Sector ss in selectedsectors) if(!ss.IsDisposed) draggedsectors.Add(ss);
// Update floor/ceiling texture offsets?
if(BuilderPlug.Me.LockSectorTextureOffsetsWhileDragging)
{
- foreach(Sector s in selectedsectors)
+
+ foreach(Sector s in draggedsectors)
{
s.Fields.BeforeFieldsChange();
@@ -501,7 +500,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
// Update slopes
- foreach(Sector s in selectedsectors)
+ foreach(Sector s in draggedsectors)
{
// Update floor slope?
if(s.FloorSlope.GetLengthSq() > 0 && !float.IsNaN(s.FloorSlopeOffset / s.FloorSlope.z))
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs
index 8448171..4ab6b69 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs
@@ -102,10 +102,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true);
foreach(Vertex v in verts) v.Selected = true;
- //mxd. Mark moved sectors (used in Linedef.Join())
- HashSet draggeddsectors = General.Map.Map.GetUnselectedSectorsFromLinedefs(selectedlines);
- foreach(Sector s in draggeddsectors) s.Marked = true;
-
// Perform normal disengage
base.OnDisengage();
@@ -115,31 +111,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
// When not cancelled
if(!cancelled)
{
- //mxd. Get new lines from linedef marks...
- HashSet newlines = new HashSet(General.Map.Map.GetMarkedLinedefs(true));
-
- //mxd. Marked lines were created during linedef splitting
- HashSet changedlines = new HashSet(selectedlines);
- changedlines.UnionWith(newlines);
- foreach(Linedef l in unstablelines) if(!l.IsDisposed) changedlines.Add(l);
-
- //mxd. Add sectors, which have all their linedefs selected (otherwise those would be destroyed after moving the selection)
- HashSet toadjust = General.Map.Map.GetUnselectedSectorsFromLinedefs(changedlines);
-
- //mxd. Reattach/add/remove outer sidedefs
- HashSet adjustedsides = Tools.AdjustOuterSidedefs(toadjust, changedlines);
-
- //mxd. Split outer sectors
- Tools.SplitOuterSectors(changedlines);
-
- //mxd. Remove unneeded textures
- foreach(Sidedef side in adjustedsides)
- {
- if(side.IsDisposed) continue;
- side.RemoveUnneededTextures(true, true, true);
- if(side.Other != null) side.Other.RemoveUnneededTextures(true, true, true);
- }
-
// If only a single linedef was selected, deselect it now
if(selectedlines.Count == 1) General.Map.Map.ClearSelectedLinedefs();
}
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs
index 33a59e6..6d7b76e 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs
@@ -118,31 +118,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
// When not cancelled
if(!cancelled)
{
- //mxd. Collect changed lines
- HashSet changedlines = new HashSet(selectedlines);
- foreach(Linedef l in unstablelines) if(!l.IsDisposed) changedlines.Add(l);
-
- //mxd. Collect changed sectors
- HashSet toadjust = new HashSet(selectedsectors);
-
- //mxd. Add sectors, which are not selected, but have all their linedefs selected
- // (otherwise those would be destroyed after moving the selection)
- toadjust.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(changedlines));
-
- //mxd. Process outer sidedefs
- HashSet adjustedsides = Tools.AdjustOuterSidedefs(toadjust, changedlines);
-
- //mxd. Split outer sectors
- Tools.SplitOuterSectors(changedlines);
-
- //mxd. Remove unneeded textures
- foreach(Sidedef side in adjustedsides)
- {
- if(side.IsDisposed) continue;
- side.RemoveUnneededTextures(true, true, true);
- if(side.Other != null) side.Other.RemoveUnneededTextures(true, true, true);
- }
-
// If only a single sector was selected, deselect it now
if(selectedsectors.Count == 1)
{
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs
index 66e31d8..a2e8983 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs
@@ -92,62 +92,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.Map.ClearSelectedVertices();
General.Map.Map.SelectMarkedVertices(true, true);
- //mxd. Mark stable lines now (marks will be carried to split lines by MapSet.StitchGeometry())
- HashSet stablelines = (!cancelled ? new HashSet(General.Map.Map.LinedefsFromMarkedVertices(false, true, false)) : new HashSet());
- foreach(Linedef l in stablelines) l.Marked = true;
-
- //mxd. Mark moved sectors (used in Linedef.Join())
- HashSet draggeddsectors = (!cancelled ? General.Map.Map.GetUnselectedSectorsFromLinedefs(stablelines) : new HashSet());
- foreach(Sector s in draggeddsectors) s.Marked = true;
-
// Perform normal disengage
base.OnDisengage();
// When not cancelled
if(!cancelled)
{
- //mxd. Get new lines from linedef marks...
- HashSet newlines = new HashSet(General.Map.Map.GetMarkedLinedefs(true));
-
- //mxd. Marked lines were created during linedef splitting
- HashSet changedlines = new HashSet(stablelines);
- changedlines.UnionWith(newlines);
- foreach(Linedef l in unstablelines) if(!l.IsDisposed) changedlines.Add(l);
-
- //mxd. Get sectors, which have all their linedefs selected (otherwise those would be destroyed after moving the selection)
- HashSet toadjust = General.Map.Map.GetUnselectedSectorsFromLinedefs(changedlines);
-
- //mxd. If linedefs were dragged, reattach/add/remove sidedefs
- if(changedlines.Count > 0)
- {
- // Reattach/add/remove outer sidedefs
- HashSet adjustedsides = Tools.AdjustOuterSidedefs(toadjust, changedlines);
-
- // Split outer sectors
- Tools.SplitOuterSectors(changedlines);
-
- // Remove unneeded textures
- foreach(Sidedef side in adjustedsides)
- {
- if(side.IsDisposed) continue;
- side.RemoveUnneededTextures(true, true, true);
- if(side.Other != null) side.Other.RemoveUnneededTextures(true, true, true);
- }
-
- // Additional verts may've been created
- if(selectedverts.Count > 1)
- {
- foreach(Linedef l in changedlines)
- {
- if(!unstablelines.Contains(l))
- {
- l.Start.Selected = true;
- l.End.Selected = true;
- }
- }
- }
- }
-
// If only a single vertex was selected, deselect it now
if(selectedverts.Count == 1) General.Map.Map.ClearSelectedVertices();
}
diff --git a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
index a3c2527..8a52da0 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
@@ -911,7 +911,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(scaleoffsets)
{
fields["xscale" + si.Part] = new UniValue(UniversalType.Float, (float) Math.Round(si.Scale.x * scale.x, General.Map.FormatInterface.VertexDecimals));
- fields["yscale" + si.Part] = new UniValue(UniversalType.Float, (float) Math.Round(si.Scale.y * scale.y, General.Map.FormatInterface.VertexDecimals));
+ fields["yscale" + si.Part] = new UniValue(UniversalType.Float, (float) Math.Round(-si.Scale.y * scale.y, General.Map.FormatInterface.VertexDecimals));
}
// Restore scale
else
@@ -1041,41 +1041,66 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Sector height adjust methods (mxd)
- private static Sector GetOutsideSector(IEnumerable sectors)
+ //x = floor height, y = ceiling height
+ private static Point GetOutsideHeights(HashSet sectors)
{
- Sector result = null;
+ Sector target = null;
+ Point result = new Point { X = int.MinValue, Y = int.MinValue };
foreach(Sector s in sectors)
{
foreach(Sidedef side in s.Sidedefs)
{
- if(side.Other == null || side.Other.Sector == null) continue;
- if(result == null) result = side.Other.Sector;
- else if(result != side.Other.Sector) return null;
+ // Don't compare with our own stuff, among other things
+ if(side.Other == null || side.Other.Sector == null || sectors.Contains(side.Other.Sector)) continue;
+ if(target == null)
+ {
+ target = side.Other.Sector;
+ result.X = target.FloorHeight;
+ result.Y = target.CeilHeight;
+ }
+ else if(target != side.Other.Sector)
+ {
+ // Compare heights
+ if(target.FloorHeight != side.Other.Sector.FloorHeight)
+ result.X = int.MinValue;
+ if(target.CeilHeight != side.Other.Sector.CeilHeight)
+ result.Y = int.MinValue;
+
+ // We can stop now...
+ if(result.X == int.MinValue && result.Y == int.MinValue)
+ return result;
+ }
}
}
return result;
}
- private static void AdjustSectorsHeight(ICollection toadjust, HeightAdjustMode adjustmode, int oldfloorheight, int oldceilheight)
+ private static void AdjustSectorsHeight(HashSet toadjust, HeightAdjustMode adjustmode, int oldfloorheight, int oldceilheight)
{
// Adjust only when selection is inside a single sector
if(adjustmode == HeightAdjustMode.NONE || oldfloorheight == int.MinValue || oldceilheight == int.MinValue) return;
- Sector outsidesector = GetOutsideSector(toadjust);
- if(outsidesector == null) return;
+ Point outsideheights = GetOutsideHeights(toadjust);
+ if(outsideheights.X == int.MinValue && outsideheights.Y == int.MinValue) return;
// Height differences
- int floorheightdiff = outsidesector.FloorHeight - oldfloorheight;
- int ceilheightdiff = outsidesector.CeilHeight - oldceilheight;
+ int floorheightdiff = (outsideheights.X == int.MinValue ? int.MinValue : outsideheights.X - oldfloorheight);
+ int ceilheightdiff = (outsideheights.Y == int.MinValue ? int.MinValue : outsideheights.Y - oldceilheight);
switch(adjustmode)
{
case HeightAdjustMode.ADJUST_FLOORS:
- foreach(Sector s in toadjust) AdjustSectorHeight(s, floorheightdiff, int.MinValue);
+ if(floorheightdiff != int.MinValue)
+ {
+ foreach(Sector s in toadjust) AdjustSectorHeight(s, floorheightdiff, int.MinValue);
+ }
break;
case HeightAdjustMode.ADJUST_CEILINGS:
- foreach(Sector s in toadjust) AdjustSectorHeight(s, int.MinValue, ceilheightdiff);
+ if(ceilheightdiff != int.MinValue)
+ {
+ foreach(Sector s in toadjust) AdjustSectorHeight(s, int.MinValue, ceilheightdiff);
+ }
break;
case HeightAdjustMode.ADJUST_BOTH:
@@ -1222,7 +1247,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
foreach(Linedef l in markedlines) l.Selected = true;
selectedlines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false);
unselectedlines = General.Map.Map.LinedefsFromMarkedVertices(true, false, false);
- unstablelines = (pasting ? new Collection() : General.Map.Map.LinedefsFromMarkedVertices(false, false, true)); //mxd
+ unstablelines = (pasting ? new List() : General.Map.Map.LinedefsFromMarkedVertices(false, false, true)); //mxd
// Array to keep original coordinates
vertexpos = new List(selectedvertices.Count);
@@ -1601,8 +1626,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Do we have a virtual and parent sector?
if((vsector != null) && (parent != null))
{
- //mxd. Apply HeightAdjustMode
- AdjustSectorsHeight(General.Map.Map.GetMarkedSectors(true), heightadjustmode, vsector.FloorHeight, vsector.CeilHeight);
+ //mxd. Store floor/ceiling height
+ oldoutsidefloorheight = vsector.FloorHeight;
+ oldoutsideceilingheight = vsector.CeilHeight;
}
// Remove any virtual sectors
@@ -1610,64 +1636,41 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
else
{
- //mxd. Get floor/ceiling height from outside sector
+ //mxd. Get floor/ceiling height from outside sectors
if(unstablelines.Count == 0 && heightadjustmode != HeightAdjustMode.NONE)
{
// Get affected sectors
HashSet affectedsectors = new HashSet(General.Map.Map.GetSelectedSectors(true));
- Sector curoutsidesector = GetOutsideSector(affectedsectors);
- if(curoutsidesector != null)
- {
- oldoutsidefloorheight = curoutsidesector.FloorHeight;
- oldoutsideceilingheight = curoutsidesector.CeilHeight;
- }
+ Point outsideheights = GetOutsideHeights(affectedsectors);
+ oldoutsidefloorheight = outsideheights.X;
+ oldoutsideceilingheight = outsideheights.Y;
}
}
// Stitch geometry
- General.Map.Map.StitchGeometry();
-
- // Make corrections for backward linedefs
- MapSet.FlipBackwardLinedefs(General.Map.Map.Linedefs);
+ General.Map.Map.StitchGeometry(true);
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy(General.Map.UDMF && usepreciseposition);
- //mxd. Update cached values
- General.Map.Map.Update();
-
//mxd. Get new lines from linedef marks...
HashSet newlines = new HashSet(General.Map.Map.GetMarkedLinedefs(true));
//mxd. Marked lines were created during linedef splitting
HashSet changedlines = new HashSet(selectedlines);
changedlines.UnionWith(newlines);
- foreach(Linedef l in unstablelines) if(!l.IsDisposed) changedlines.Add(l);
- //mxd. Update outer sides of the selection
- if(changedlines.Count > 0)
+ //mxd. Update sector height?
+ if(changedlines.Count > 0 && heightadjustmode != HeightAdjustMode.NONE
+ && oldoutsidefloorheight != int.MinValue && oldoutsideceilingheight != int.MinValue)
{
- // Get affected sectors
- HashSet affectedsectors = new HashSet(General.Map.Map.GetSelectedSectors(true));
- affectedsectors.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(changedlines));
+ // Sectors may've been created/removed when applying dragging...
+ HashSet draggedsectors = new HashSet(General.Map.Map.GetMarkedSectors(true));
+ foreach(Sector ss in selectedsectors.Keys) if(!ss.IsDisposed) draggedsectors.Add(ss);
- // Reattach/add/remove outer sidedefs
- HashSet adjustedsides = Tools.AdjustOuterSidedefs(affectedsectors, changedlines);
-
- // Change floor/ceiling height?
- if(!pasting) AdjustSectorsHeight(affectedsectors, heightadjustmode, oldoutsidefloorheight, oldoutsideceilingheight);
-
- // Split outer sectors
- Tools.SplitOuterSectors(changedlines);
-
- // Remove unneeded textures (needs to be done AFTER adjusting floor/ceiling height)
- foreach(Sidedef side in adjustedsides)
- {
- if(side.IsDisposed) continue;
- side.RemoveUnneededTextures(true, true, true);
- if(side.Other != null) side.Other.RemoveUnneededTextures(true, true, true);
- }
+ // Change floor/ceiling height
+ AdjustSectorsHeight(draggedsectors, heightadjustmode, oldoutsidefloorheight, oldoutsideceilingheight);
}
// Update cached values