mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2024-11-10 06:41:49 +00:00
Rewritten vertex/linedef/sector dragging logic. Now it processes line-line intersections. And handles more corner cases. And has less bugs. Probably.
Fixed, Edit Selection mode: texture scale was flipped when "Floor/Ceiling Transform" -> "Scale" was enabled. Changed: a warning is no longer displayed when trying to compile an empty SCRIPTS lump. Updated ZDoom_DECORATE.cfg.
This commit is contained in:
parent
d45e71ce21
commit
e69163a646
18 changed files with 1257 additions and 500 deletions
|
@ -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";
|
||||
|
|
|
@ -917,6 +917,7 @@
|
|||
<Compile Include="GZBuilder\Rendering\SizelessVisualThingCage.cs" />
|
||||
<Compile Include="GZBuilder\Rendering\ThingBoundingBox.cs" />
|
||||
<Compile Include="GZBuilder\Data\ThingCopyData.cs" />
|
||||
<Compile Include="Map\SectorBuilder.cs" />
|
||||
<Compile Include="GZBuilder\Rendering\VisualVertexHandle.cs" />
|
||||
<Compile Include="GZBuilder\Geometry\Line3D.cs" />
|
||||
<Compile Include="GZBuilder\GZDoom\DecorateParserSE.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;
|
||||
}
|
||||
|
|
|
@ -83,6 +83,53 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<EarClipVertex> 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,12 +149,10 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
// Inside polygon!
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Not inside the polygon
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This inserts a polygon if it is a child of this one
|
||||
public bool InsertChild(EarClipPolygon p)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#region ================== Namespaces
|
||||
|
||||
using System;
|
||||
using CodeImp.DoomBuilder.Map;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -66,6 +67,13 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
this.v2 = new Vector2D(x2, y2);
|
||||
}
|
||||
|
||||
//mxd. Constructor
|
||||
public Line2D(Linedef line)
|
||||
{
|
||||
this.v1 = line.Start.Position;
|
||||
this.v2 = line.End.Position;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Statics
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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<Sidedef> AdjustOuterSidedefs(HashSet<Sector> selectedsectors, HashSet<Linedef> selectedlines)
|
||||
{
|
||||
HashSet<Sidedef> adjustedsides = new HashSet<Sidedef>();
|
||||
HashSet<Sidedef> outersides = new HashSet<Sidedef>();
|
||||
HashSet<Sidedef> innersides = new HashSet<Sidedef>();
|
||||
HashSet<Linedef> singlesidedlines = new HashSet<Linedef>();
|
||||
HashSet<Linedef> lineswithoutsides = new HashSet<Linedef>();
|
||||
|
||||
// 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<Linedef, Sector> linesectorref = new Dictionary<Linedef, Sector>();
|
||||
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<Sidedef, Sector> sidesectorref = new Dictionary<Sidedef, Sector>();
|
||||
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<Linedef, Sector> 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<Sidedef, Sector> 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<Sidedef> sidestoexclude)
|
||||
public static Sector FindPotentialSector(Linedef line, bool front)
|
||||
{
|
||||
List<LinedefSide> 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<Sidedef> sectorsidesset = new HashSet<Sidedef>();
|
||||
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...
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1989,9 +1989,9 @@ namespace CodeImp.DoomBuilder.Map
|
|||
}
|
||||
|
||||
/// <summary>This filters lines by a rectangular area.</summary>
|
||||
public static ICollection<Linedef> FilterByArea(ICollection<Linedef> lines, ref RectangleF area)
|
||||
public static List<Linedef> FilterByArea(ICollection<Linedef> lines, ref RectangleF area)
|
||||
{
|
||||
ICollection<Linedef> newlines = new List<Linedef>(lines.Count);
|
||||
List<Linedef> newlines = new List<Linedef>(lines.Count);
|
||||
|
||||
// Go for all lines
|
||||
foreach(Linedef l in lines)
|
||||
|
@ -2046,17 +2046,18 @@ namespace CodeImp.DoomBuilder.Map
|
|||
/// <summary>
|
||||
/// Stitches marked geometry with non-marked geometry. Returns false when the operation failed.
|
||||
/// </summary>
|
||||
public bool StitchGeometry()
|
||||
public bool StitchGeometry() { return StitchGeometry(false); } //mxd. Compatibility
|
||||
public bool StitchGeometry(bool correctsectorrefs)
|
||||
{
|
||||
// Find vertices
|
||||
ICollection<Vertex> movingverts = General.Map.Map.GetMarkedVertices(true);
|
||||
ICollection<Vertex> fixedverts = General.Map.Map.GetMarkedVertices(false);
|
||||
|
||||
// Find lines that moved during the drag
|
||||
ICollection<Linedef> movinglines = LinedefsFromMarkedVertices(false, true, true);
|
||||
List<Linedef> movinglines = LinedefsFromMarkedVertices(false, true, true);
|
||||
|
||||
// Find all non-moving lines
|
||||
ICollection<Linedef> fixedlines = LinedefsFromMarkedVertices(true, false, false);
|
||||
List<Linedef> fixedlines = LinedefsFromMarkedVertices(true, false, false);
|
||||
|
||||
// Determine area in which we are editing
|
||||
RectangleF editarea = CreateArea(movinglines);
|
||||
|
@ -2075,26 +2076,384 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
// Split moving lines with unselected vertices
|
||||
ICollection<Vertex> 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<Linedef> changedlines = LinedefsFromMarkedVertices(false, true, true);
|
||||
CorrectSectorReferences(changedlines, true);
|
||||
CorrectOuterSides(new HashSet<Linedef>(changedlines));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//mxd. Shameless SLADEMap::correctSectors ripoff... Corrects/builds sectors for all lines in [lines]
|
||||
private static void CorrectSectorReferences(List<Linedef> lines, bool existing_only)
|
||||
{
|
||||
// Create a list of sidedefs to perform sector creation with
|
||||
List<LinedefSide> edges = new List<LinedefSide>();
|
||||
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<Sidedef> sides_correct = new HashSet<Sidedef>();
|
||||
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<Sector> affectedsectors = new HashSet<Sector>(General.Map.Map.GetSelectedSectors(true));
|
||||
affectedsectors.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(lines));
|
||||
|
||||
//mxd. Collect their lines
|
||||
HashSet<Linedef> sectorlines = new HashSet<Linedef>();
|
||||
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<Sector> sectors_reused = new List<Sector>();
|
||||
|
||||
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<LinedefSide> edges_in_sector = new List<LinedefSide>();
|
||||
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<Sector> 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<Sidedef> 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<Linedef> changedlines)
|
||||
{
|
||||
HashSet<Linedef> linesmissingfront = new HashSet<Linedef>();
|
||||
HashSet<Linedef> linesmissingback = new HashSet<Linedef>();
|
||||
|
||||
// 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<Linedef, Sector> linefrontsectorref = new Dictionary<Linedef, Sector>();
|
||||
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<Linedef, Sector> linebacksectorref = new Dictionary<Linedef, Sector>();
|
||||
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<Linedef, Sector> 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<Linedef, Sector> 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
|
||||
|
||||
#region ================== Geometry Tools
|
||||
|
@ -2422,9 +2781,10 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
/// <summary>This splits the given lines with the given vertices. All affected lines
|
||||
/// will be added to changedlines. Returns false when the operation failed.</summary>
|
||||
public static bool SplitLinesByVertices(ICollection<Linedef> lines, ICollection<Vertex> verts, float splitdist, ICollection<Linedef> changedlines)
|
||||
public static bool SplitLinesByVertices(ICollection<Linedef> lines, ICollection<Vertex> verts, float splitdist, ICollection<Linedef> changedlines) { return SplitLinesByVertices(lines, verts, splitdist, changedlines, false); }
|
||||
public static bool SplitLinesByVertices(ICollection<Linedef> lines, ICollection<Vertex> verts, float splitdist, ICollection<Linedef> 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;
|
||||
|
||||
|
@ -2492,7 +2852,127 @@ namespace CodeImp.DoomBuilder.Map
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Splits lines by lines. Adds new lines to the second collection. Returns false when the operation failed.</summary>
|
||||
public static bool SplitLinesByLines(IList<Linedef> lines, IList<Linedef> changedlines, bool removeinnerlines) //mxd
|
||||
{
|
||||
if(lines.Count == 0 || changedlines.Count == 0) return true;
|
||||
|
||||
// Create blockmap
|
||||
RectangleF area = RectangleF.Union(CreateArea(lines), CreateArea(changedlines));
|
||||
BlockMap<BlockEntry> blockmap = new BlockMap<BlockEntry>(area);
|
||||
blockmap.AddLinedefsSet(lines);
|
||||
blockmap.AddLinedefsSet(changedlines);
|
||||
int bmWidth = blockmap.Size.Width;
|
||||
int bmHeight = blockmap.Size.Height;
|
||||
BlockEntry[,] bmap = blockmap.Map;
|
||||
|
||||
//mxd
|
||||
HashSet<Vertex> splitverts = new HashSet<Vertex>();
|
||||
HashSet<Sector> changedsectors = (removeinnerlines ? General.Map.Map.GetSectorsFromLinedefs(changedlines) : new HashSet<Sector>());
|
||||
HashSet<Linedef> initialchanedlines = new HashSet<Linedef>(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<Linedef> alllines = new HashSet<Linedef>(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
|
|||
|
||||
/// <summary>This makes a list of lines related to marked vertices.
|
||||
/// A line is unstable when one vertex is marked and the other isn't.</summary>
|
||||
public ICollection<Linedef> LinedefsFromMarkedVertices(bool includeunselected, bool includestable, bool includeunstable)
|
||||
public List<Linedef> LinedefsFromMarkedVertices(bool includeunmarked, bool includestable, bool includeunstable)
|
||||
{
|
||||
List<Linedef> list = new List<Linedef>((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
|
||||
/// <summary>Gets sectors, which have all their linedefs selected</summary>
|
||||
public HashSet<Sector> GetSectorsFromLinedefs(IEnumerable<Linedef> lines)
|
||||
{
|
||||
HashSet<Sector> result = new HashSet<Sector>();
|
||||
Dictionary<Sector, HashSet<Sidedef>> sectorsbysides = new Dictionary<Sector, HashSet<Sidedef>>();
|
||||
|
||||
// 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<Sidedef>());
|
||||
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<Sidedef>());
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>This finds the line closest to the specified position.</summary>
|
||||
public Linedef NearestLinedef(Vector2D pos) { return MapSet.NearestLinedef(linedefs, pos); }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -606,6 +598,12 @@ namespace CodeImp.DoomBuilder.Map
|
|||
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
|
||||
public void Join(Sector other)
|
||||
|
|
549
Source/Core/Map/SectorBuilder.cs
Normal file
549
Source/Core/Map/SectorBuilder.cs
Normal file
|
@ -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<LinedefSide> sector_edges;
|
||||
private HashSet<Vertex> vertex_valid;
|
||||
|
||||
// Current outline
|
||||
private List<LinedefSide> o_edges;
|
||||
private bool o_clockwise;
|
||||
private RectangleF o_bbox;
|
||||
private Vertex vertex_right;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
public List<LinedefSide> SectorEdges { get { return sector_edges; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor
|
||||
|
||||
public SectorBuilder()
|
||||
{
|
||||
sector_edges = new List<LinedefSide>();
|
||||
vertex_valid = new HashSet<Vertex>();
|
||||
o_edges = new List<LinedefSide>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
///<summary>Traces all edges to build a closed sector starting from [line]</summary>
|
||||
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<Vertex>(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;
|
||||
}
|
||||
|
||||
///<summary>Traces the sector outline from lines beginning at [line],
|
||||
/// on either the front or back side ([front])</summary>
|
||||
private bool TraceOutline(Linedef line, bool front)
|
||||
{
|
||||
// Check line was given
|
||||
if(line == null) return false;
|
||||
|
||||
//DebugConsole.WriteLine(" ");
|
||||
//DebugConsole.WriteLine("Tracing line " + line.Index + (front ? " (front)" : " (back)"));
|
||||
|
||||
// Init outline
|
||||
o_edges.Clear();
|
||||
LinedefSide start = new LinedefSide(line, front);
|
||||
o_edges.Add(start);
|
||||
int edge_sum = 0;
|
||||
Dictionary<Linedef, int> visited_lines = new Dictionary<Linedef, int>();
|
||||
|
||||
// Begin tracing
|
||||
LinedefSide edge = new LinedefSide(line, front);
|
||||
vertex_right = edge.Line.Start;
|
||||
for(int a = 0; a < 10000; a++)
|
||||
{
|
||||
// Update edge sum (for clockwise detection)
|
||||
if(edge.Front)
|
||||
edge_sum += (int)(edge.Line.Start.Position.x * edge.Line.End.Position.y - edge.Line.End.Position.x * edge.Line.Start.Position.y);
|
||||
else
|
||||
edge_sum += (int)(edge.Line.End.Position.x * edge.Line.Start.Position.y - edge.Line.Start.Position.x * edge.Line.End.Position.y);
|
||||
|
||||
// Update rightmost vertex
|
||||
if(edge.Line.Start.Position.x > vertex_right.Position.x)
|
||||
vertex_right = edge.Line.Start;
|
||||
if(edge.Line.End.Position.x > vertex_right.Position.x)
|
||||
vertex_right = edge.Line.End;
|
||||
|
||||
// Get next edge. If no valid next edge was found, go back along the current line
|
||||
LinedefSide edge_next = (NextEdge(edge, visited_lines) ?? new LinedefSide(edge.Line, !edge.Front));
|
||||
|
||||
//DebugConsole.WriteLine("Next line " + 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;
|
||||
}
|
||||
|
||||
/// <summary>Finds the next closest edge outside of the current outline (that isn't part of the current outline)</summary>
|
||||
private LinedefSide FindOuterEdge()
|
||||
{
|
||||
// Check we have a rightmost vertex
|
||||
if(vertex_right == null) return null;
|
||||
|
||||
// Init
|
||||
float vr_x = vertex_right.Position.x;
|
||||
float vr_y = vertex_right.Position.y;
|
||||
float min_dist = float.MaxValue;
|
||||
Linedef nearest = null;
|
||||
|
||||
// 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... :(
|
||||
}
|
||||
|
||||
/// <summary>Find the closest edge within the current outline (that isn't part of the current outline)</summary>
|
||||
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));
|
||||
}
|
||||
|
||||
///<summary>Finds the next adjacent edge to [edge], ie the adjacent edge that creates the smallest angle</summary>
|
||||
private static LinedefSide NextEdge(LinedefSide edge, Dictionary<Linedef, int> visited_lines)
|
||||
{
|
||||
// Get relevant vertices
|
||||
Vertex vertex; // Vertex to be tested
|
||||
Vertex vertex_prev; // 'Previous' vertex
|
||||
if(edge.Front)
|
||||
{
|
||||
vertex = edge.Line.End;
|
||||
vertex_prev = edge.Line.Start;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex = edge.Line.Start;
|
||||
vertex_prev = edge.Line.End;
|
||||
}
|
||||
|
||||
// Find next connected line with the lowest angle
|
||||
float min_angle = Angle2D.PI2;
|
||||
LinedefSide next = null;
|
||||
foreach(Linedef line in vertex.Linedefs)
|
||||
{
|
||||
// Ignore original line
|
||||
if(line == edge.Line) continue;
|
||||
|
||||
// Ignore if zero-length
|
||||
if(line.Start.Position == line.End.Position) continue;
|
||||
|
||||
// Get next vertex
|
||||
Vertex vertex_next;
|
||||
bool front = true;
|
||||
if(line.Start == vertex)
|
||||
{
|
||||
vertex_next = line.End;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex_next = line.Start;
|
||||
front = false;
|
||||
}
|
||||
|
||||
// Ignore already-traversed lines
|
||||
int side = (front ? 1 : 2);
|
||||
if(visited_lines.ContainsKey(line) && (visited_lines[line] & side) == side) continue;
|
||||
|
||||
|
||||
// Determine angle between lines
|
||||
float angle = Angle2D.GetAngle(new Vector2D(vertex_prev.Position.x, vertex_prev.Position.y),
|
||||
new Vector2D(vertex.Position.x, vertex.Position.y),
|
||||
new Vector2D(vertex_next.Position.x, vertex_next.Position.y));
|
||||
|
||||
// Check if minimum angle
|
||||
if(angle < min_angle)
|
||||
{
|
||||
min_angle = angle;
|
||||
|
||||
if(next == null)
|
||||
{
|
||||
next = new LinedefSide(line, front);
|
||||
}
|
||||
else
|
||||
{
|
||||
next.Line = line;
|
||||
next.Front = front;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the next edge found
|
||||
if(next == null) return null;
|
||||
if(!visited_lines.ContainsKey(next.Line)) visited_lines.Add(next.Line, 0);
|
||||
visited_lines[next.Line] |= (next.Front ? 1 : 2);
|
||||
return next;
|
||||
}
|
||||
|
||||
/// <summary>Returns true if the vertex is outside the current outline</summary>
|
||||
private bool PointOutsideOutline(Vertex v)
|
||||
{
|
||||
// Check with bounding box
|
||||
Vector2D point = v.Position;
|
||||
bool pointwithin = (point.x >= o_bbox.Left && point.x <= o_bbox.Right && point.y >= o_bbox.Top && point.y <= o_bbox.Bottom);
|
||||
if(!pointwithin)
|
||||
{
|
||||
// If the point is not within the bbox and the outline is clockwise, it can't be within the outline
|
||||
// On the other hand, if the outline is anticlockwise, the point *must* be 'within' the outline
|
||||
return o_clockwise;
|
||||
}
|
||||
|
||||
// Find nearest edge
|
||||
int nearest = NearestEdge(point);
|
||||
if(nearest >= 0)
|
||||
{
|
||||
// Check what side of the edge the point is on
|
||||
float side = -o_edges[nearest].Line.SideOfLine(point); //mxd. SideOfLine logic is inverted in Slade 3
|
||||
//mxd. The meaning of 0.0 is also inverted!!!
|
||||
|
||||
// Return false if it is on the correct side
|
||||
if(side > 0 && o_edges[nearest].Front) 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;
|
||||
}
|
||||
|
||||
/// <summary>Checks if the traced sector is valid (ie. all edges are currently referencing the same (existing) sector)</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>Finds any existing sector that is already part of the traced new sector</summary>
|
||||
internal Sector FindExistingSector(HashSet<Sidedef> 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);
|
||||
}
|
||||
|
||||
/// <summary>Sets all traced edges to [sector], or creates a new sector using properties
|
||||
/// from [sector_copy] if none given</summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Finds an appropriate existing sector to copy properties from, for the new sector being built</summary>
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
@ -366,7 +366,7 @@ namespace CodeImp.DoomBuilder.Map
|
|||
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;
|
||||
|
|
|
@ -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();
|
||||
|
@ -436,10 +430,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
{
|
||||
Vector2D offset = dragitem.Position - dragitemposition;
|
||||
|
||||
// Sectors may've been created/removed when applying dragging...
|
||||
HashSet<Sector> draggedsectors = new HashSet<Sector>(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))
|
||||
|
|
|
@ -102,10 +102,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
ICollection<Vertex> verts = General.Map.Map.GetVerticesFromLinesMarks(true);
|
||||
foreach(Vertex v in verts) v.Selected = true;
|
||||
|
||||
//mxd. Mark moved sectors (used in Linedef.Join())
|
||||
HashSet<Sector> 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<Linedef> newlines = new HashSet<Linedef>(General.Map.Map.GetMarkedLinedefs(true));
|
||||
|
||||
//mxd. Marked lines were created during linedef splitting
|
||||
HashSet<Linedef> changedlines = new HashSet<Linedef>(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<Sector> toadjust = General.Map.Map.GetUnselectedSectorsFromLinedefs(changedlines);
|
||||
|
||||
//mxd. Reattach/add/remove outer sidedefs
|
||||
HashSet<Sidedef> 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();
|
||||
}
|
||||
|
|
|
@ -118,31 +118,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
// When not cancelled
|
||||
if(!cancelled)
|
||||
{
|
||||
//mxd. Collect changed lines
|
||||
HashSet<Linedef> changedlines = new HashSet<Linedef>(selectedlines);
|
||||
foreach(Linedef l in unstablelines) if(!l.IsDisposed) changedlines.Add(l);
|
||||
|
||||
//mxd. Collect changed sectors
|
||||
HashSet<Sector> toadjust = new HashSet<Sector>(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<Sidedef> 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)
|
||||
{
|
||||
|
|
|
@ -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<Linedef> stablelines = (!cancelled ? new HashSet<Linedef>(General.Map.Map.LinedefsFromMarkedVertices(false, true, false)) : new HashSet<Linedef>());
|
||||
foreach(Linedef l in stablelines) l.Marked = true;
|
||||
|
||||
//mxd. Mark moved sectors (used in Linedef.Join())
|
||||
HashSet<Sector> draggeddsectors = (!cancelled ? General.Map.Map.GetUnselectedSectorsFromLinedefs(stablelines) : new HashSet<Sector>());
|
||||
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<Linedef> newlines = new HashSet<Linedef>(General.Map.Map.GetMarkedLinedefs(true));
|
||||
|
||||
//mxd. Marked lines were created during linedef splitting
|
||||
HashSet<Linedef> changedlines = new HashSet<Linedef>(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<Sector> 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<Sidedef> 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();
|
||||
}
|
||||
|
|
|
@ -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<Sector> sectors)
|
||||
//x = floor height, y = ceiling height
|
||||
private static Point GetOutsideHeights(HashSet<Sector> 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<Sector> toadjust, HeightAdjustMode adjustmode, int oldfloorheight, int oldceilheight)
|
||||
private static void AdjustSectorsHeight(HashSet<Sector> 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:
|
||||
if(floorheightdiff != int.MinValue)
|
||||
{
|
||||
foreach(Sector s in toadjust) AdjustSectorHeight(s, floorheightdiff, int.MinValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case HeightAdjustMode.ADJUST_CEILINGS:
|
||||
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<Linedef>() : General.Map.Map.LinedefsFromMarkedVertices(false, false, true)); //mxd
|
||||
unstablelines = (pasting ? new List<Linedef>() : General.Map.Map.LinedefsFromMarkedVertices(false, false, true)); //mxd
|
||||
|
||||
// Array to keep original coordinates
|
||||
vertexpos = new List<Vector2D>(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<Sector> affectedsectors = new HashSet<Sector>(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<Linedef> newlines = new HashSet<Linedef>(General.Map.Map.GetMarkedLinedefs(true));
|
||||
|
||||
//mxd. Marked lines were created during linedef splitting
|
||||
HashSet<Linedef> changedlines = new HashSet<Linedef>(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<Sector> affectedsectors = new HashSet<Sector>(General.Map.Map.GetSelectedSectors(true));
|
||||
affectedsectors.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(changedlines));
|
||||
// Sectors may've been created/removed when applying dragging...
|
||||
HashSet<Sector> draggedsectors = new HashSet<Sector>(General.Map.Map.GetMarkedSectors(true));
|
||||
foreach(Sector ss in selectedsectors.Keys) if(!ss.IsDisposed) draggedsectors.Add(ss);
|
||||
|
||||
// Reattach/add/remove outer sidedefs
|
||||
HashSet<Sidedef> 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
|
||||
|
|
Loading…
Reference in a new issue