Increased performance when creating new map geometry (for example, when applying changes made by "Draw Line", "Draw Rectangle" etc. actions).

This commit is contained in:
MaxED 2013-12-17 13:18:44 +00:00
parent 9df67e8b22
commit b1b3dda28b
8 changed files with 163 additions and 100 deletions

View file

@ -130,15 +130,14 @@ namespace CodeImp.DoomBuilder.Actions
public static string GetShortcutKeyDesc(int key) public static string GetShortcutKeyDesc(int key)
{ {
KeysConverter conv = new KeysConverter(); KeysConverter conv = new KeysConverter();
int ctrl, button;
string ctrlprefix = ""; string ctrlprefix = "";
// When key is 0, then return an empty string // When key is 0, then return an empty string
if(key == 0) return ""; if(key == 0) return "";
// Split the key in Control and Button // Split the key in Control and Button
ctrl = key & ((int)Keys.Control | (int)Keys.Shift | (int)Keys.Alt); int ctrl = key & ((int)Keys.Control | (int)Keys.Shift | (int)Keys.Alt);
button = key & ~((int)Keys.Control | (int)Keys.Shift | (int)Keys.Alt); int button = key & ~((int)Keys.Control | (int)Keys.Shift | (int)Keys.Alt);
// When the button is a control key, then remove the control itsself // When the button is a control key, then remove the control itsself
if((button == (int)Keys.ControlKey) || if((button == (int)Keys.ControlKey) ||

View file

@ -1540,9 +1540,10 @@ namespace CodeImp.DoomBuilder
// This outputs log information // This outputs log information
public static void WriteLogLine(string line) public static void WriteLogLine(string line)
{ {
#if DEBUG
// Output to console // Output to console
Console.WriteLine(line); Console.WriteLine(line);
#endif
// Write to log file // Write to log file
try { File.AppendAllText(logfile, line + Environment.NewLine); } try { File.AppendAllText(logfile, line + Environment.NewLine); }
catch(Exception) { } catch(Exception) { }
@ -1551,8 +1552,10 @@ namespace CodeImp.DoomBuilder
// This outputs log information // This outputs log information
public static void WriteLog(string text) public static void WriteLog(string text)
{ {
#if DEBUG
// Output to console // Output to console
Console.Write(text); Console.Write(text);
#endif
// Write to log file // Write to log file
try { File.AppendAllText(logfile, text); } try { File.AppendAllText(logfile, text); }

View file

@ -125,7 +125,8 @@ namespace CodeImp.DoomBuilder.Geometry
u_ray = ((v2.x - v1.x) * (v1.y - y3) - (v2.y - v1.y) * (v1.x - x3)) / div; u_ray = ((v2.x - v1.x) * (v1.y - y3) - (v2.y - v1.y) * (v1.x - x3)) / div;
// Return if intersecting // Return if intersecting
return (u_ray >= 0.0f) && (u_ray <= 1.0f) && (u_line >= 0.0f) && (u_line <= 1.0f); if (u_ray <= 0.0f || u_ray >= 1.0f || u_line <= 0.0f || u_line >= 1.0f) return false; //mxd
return true;
} }
else else
{ {

View file

@ -910,17 +910,25 @@ namespace CodeImp.DoomBuilder.Geometry
// Check if any other lines intersect this line // Check if any other lines intersect this line
List<float> intersections = new List<float>(); List<float> intersections = new List<float>();
Line2D measureline = ld.Line; Line2D measureline = ld.Line;
foreach(Linedef ld2 in map.Linedefs) Dictionary<Linedef, bool> processed = new Dictionary<Linedef, bool>(); //mxd
{
// Intersecting? //mxd
// We only keep the unit length from the start of the line and foreach (Sector s in map.Sectors) {
// do the real splitting later, when all intersections are known //line intersects with sector's bounding box?
if((MapSet.GetCSFieldBits(measureline.v1, s.BBox) & MapSet.GetCSFieldBits(measureline.v2, s.BBox)) == 0) {
foreach (Sidedef side in s.Sidedefs) {
if(processed.ContainsKey(side.Line)) continue;
if(side.Line == ld) continue;
float u; float u;
if(ld2.Line.GetIntersection(measureline, out u)) if(side.Line.Line.GetIntersection(measureline, out u)) {
{ if(float.IsNaN(u) || (u < 0.0f) || (u > 1.0f)) continue;
if(!float.IsNaN(u) && (u > 0.0f) && (u < 1.0f) && (ld2 != ld))
intersections.Add(u); intersections.Add(u);
} }
processed.Add(side.Line, false);
}
}
} }
// Sort the intersections // Sort the intersections
@ -960,7 +968,7 @@ namespace CodeImp.DoomBuilder.Geometry
// Join merge vertices so that overlapping vertices in the draw become one. // Join merge vertices so that overlapping vertices in the draw become one.
map.BeginAddRemove(); map.BeginAddRemove();
MapSet.JoinVertices(mergeverts, mergeverts, false, MapSet.STITCH_DISTANCE); MapSet.JoinVertices(mergeverts, MapSet.STITCH_DISTANCE); //mxd
map.EndAddRemove(); map.EndAddRemove();
/***************************************************\ /***************************************************\
@ -1187,7 +1195,7 @@ namespace CodeImp.DoomBuilder.Geometry
drawingclosed = true; drawingclosed = true;
// Join merge vertices so that overlapping vertices in the draw become one. // Join merge vertices so that overlapping vertices in the draw become one.
MapSet.JoinVertices(mergeverts, mergeverts, false, MapSet.STITCH_DISTANCE); MapSet.JoinVertices(mergeverts, MapSet.STITCH_DISTANCE); //mxd
} }
} }
} }
@ -1417,7 +1425,8 @@ namespace CodeImp.DoomBuilder.Geometry
for(int i = newlines.Count - 1; i >= 0; i--) for(int i = newlines.Count - 1; i >= 0; i--)
{ {
// Remove the line if it has no sides // Remove the line if it has no sides
if((newlines[i].Front == null) && (newlines[i].Back == null)) newlines[i].Dispose(); if((newlines[i].Front != null) || (newlines[i].Back != null)) continue;
newlines[i].Dispose();
} }
} }

View file

@ -52,6 +52,7 @@ namespace CodeImp.DoomBuilder.Map
public Size Size { get { return size; } } public Size Size { get { return size; } }
public RectangleF Range { get { return range; } } public RectangleF Range { get { return range; } }
public int BlockSize { get { return blocksize; } } public int BlockSize { get { return blocksize; } }
internal BE[,] Map { get { return blockmap; } }
#endregion #endregion

View file

@ -17,7 +17,6 @@
#region ================== Namespaces #region ================== Namespaces
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Windows; using CodeImp.DoomBuilder.Windows;
@ -1907,7 +1906,7 @@ namespace CodeImp.DoomBuilder.Map
foreach(Linedef l in lines) foreach(Linedef l in lines)
{ {
// Check the cs field bits // Check the cs field bits
if ((GetCSFieldBits(l.Start, ref area) & GetCSFieldBits(l.End, ref area)) == 0) if ((GetCSFieldBits(l.Start.Position, area) & GetCSFieldBits(l.End.Position, area)) == 0)
{ {
// The line could be in the area // The line could be in the area
newlines.Add(l); newlines.Add(l);
@ -1918,14 +1917,14 @@ namespace CodeImp.DoomBuilder.Map
return newlines; return newlines;
} }
// This returns the cohen-sutherland field bits for a vertex in a rectangle area /// <summary> This returns the cohen-sutherland field bits for a vector in a rectangle area</summary>
private static int GetCSFieldBits(Vertex v, ref RectangleF area) public static int GetCSFieldBits(Vector2D v, RectangleF area)
{ {
int bits = 0; int bits = 0;
if(v.Position.y < area.Top) bits |= 0x01; if(v.y < area.Top) bits |= 0x01;
if(v.Position.y > area.Bottom) bits |= 0x02; if(v.y > area.Bottom) bits |= 0x02;
if(v.Position.x < area.Left) bits |= 0x04; if(v.x < area.Left) bits |= 0x04;
if(v.Position.x > area.Right) bits |= 0x08; if(v.x > area.Right) bits |= 0x08;
return bits; return bits;
} }
@ -2270,6 +2269,48 @@ namespace CodeImp.DoomBuilder.Map
return joinsdone; return joinsdone;
} }
/// <summary>This joins nearby vertices in the same collection </summary>
public static int JoinVertices(List<Vertex> set, float joindist) {
float joindist2 = joindist * joindist;
int joinsdone = 0;
bool joined;
Vertex v1, v2;
do {
// No joins yet
joined = false;
// Go for all vertices in the first set
for(int i = 0; i < set.Count - 1; i++) {
for(int c = i + 1; c < set.Count; c++) {
v1 = set[i];
v2 = set[c];
// Check if vertices are close enough
if (v1.DistanceToSq(v2.Position) <= joindist2) {
// Check if not the same vertex
if (v1.Index != v2.Index) {
// Move the second vertex to match the first
v2.Move(v1.Position);
// Join the second into the first
v2.Join(v1);
set.Remove(v2);
// Count the join
joinsdone++;
joined = true;
break;
}
}
}
}
} while(joined);
// Return result
return joinsdone;
}
/// <summary>This corrects lines that have a back sidedef but no front sidedef by flipping them. Returns the number of flips made.</summary> /// <summary>This corrects lines that have a back sidedef but no front sidedef by flipping them. Returns the number of flips made.</summary>
public static int FlipBackwardLinedefs(ICollection<Linedef> lines) public static int FlipBackwardLinedefs(ICollection<Linedef> lines)
{ {
@ -2299,28 +2340,38 @@ namespace CodeImp.DoomBuilder.Map
float splitdist2 = splitdist * splitdist; float splitdist2 = splitdist * splitdist;
bool splitted; bool splitted;
//mxd. Create blockmap
RectangleF area = CreateArea(verts);
BlockMap<BlockEntry> blockmap = new BlockMap<BlockEntry>(area);
blockmap.AddVerticesSet(verts);
blockmap.AddLinedefsSet(lines);
int bmWidth = blockmap.Size.Width;
int bmHeight = blockmap.Size.Height;
BlockEntry[,] bmap = blockmap.Map;
BlockEntry block;
int w, h;
do do
{ {
// No split yet // No split yet
splitted = false; splitted = false;
for(w = 0; w < bmWidth; w++) {
for(h = 0; h < bmHeight; h++) {
block = bmap[w, h];
if(block.Vertices.Count == 0 || block.Lines.Count == 0) continue;
// Go for all the lines // Go for all the lines
foreach(Linedef l in lines) foreach(Linedef l in block.Lines) {
{
// Go for all the vertices // Go for all the vertices
foreach(Vertex v in verts) foreach(Vertex v in block.Vertices) {
{
// Check if v is close enough to l for splitting // Check if v is close enough to l for splitting
if(l.DistanceToSq(v.Position, true) <= splitdist2) if(l.DistanceToSq(v.Position, true) <= splitdist2) {
{
// Line is not already referencing v? // Line is not already referencing v?
Vector2D deltastart = l.Start.Position - v.Position; Vector2D deltastart = l.Start.Position - v.Position;
Vector2D deltaend = l.End.Position - v.Position; Vector2D deltaend = l.End.Position - v.Position;
if(((Math.Abs(deltastart.x) > 0.001f) || if(((Math.Abs(deltastart.x) > 0.001f) || (Math.Abs(deltastart.y) > 0.001f)) &&
(Math.Abs(deltastart.y) > 0.001f)) && ((Math.Abs(deltaend.x) > 0.001f) || (Math.Abs(deltaend.y) > 0.001f))) {
((Math.Abs(deltaend.x) > 0.001f) ||
(Math.Abs(deltaend.y) > 0.001f)))
{
// Split line l with vertex v // Split line l with vertex v
Linedef nl = l.Split(v); Linedef nl = l.Split(v);
if(nl == null) return false; if(nl == null) return false;
@ -2334,8 +2385,7 @@ namespace CodeImp.DoomBuilder.Map
nl.UpdateCache(); nl.UpdateCache();
// Add both lines to changedlines // Add both lines to changedlines
if(changedlines != null) if(changedlines != null) {
{
changedlines.Add(l); changedlines.Add(l);
changedlines.Add(nl); changedlines.Add(nl);
} }
@ -2353,6 +2403,8 @@ namespace CodeImp.DoomBuilder.Map
if(splitted) break; if(splitted) break;
} }
} }
}
}
while(splitted); while(splitted);
return true; return true;

View file

@ -269,31 +269,30 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Methods #region ================== Methods
// This checks and returns a flag without creating it // This checks and returns a flag without creating it
public bool IsFlagSet(string flagname) { public bool IsFlagSet(string flagname)
if(flags.ContainsKey(flagname)) {
return flags[flagname]; return (flags.ContainsKey(flagname) && flags[flagname]);
else
return false;
} }
// This sets a flag // This sets a flag
public void SetFlag(string flagname, bool value) { public void SetFlag(string flagname, bool value)
{
if(!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value)) { if(!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value)) {
BeforePropsChange(); BeforePropsChange();
flags[flagname] = value; flags[flagname] = value;
} }
} }
// This returns a copy of the flags dictionary // This returns a copy of the flags dictionary
public Dictionary<string, bool> GetFlags() { public Dictionary<string, bool> GetFlags()
{
return new Dictionary<string, bool>(flags); return new Dictionary<string, bool>(flags);
} }
// This clears all flags // This clears all flags
public void ClearFlags() { public void ClearFlags()
{
BeforePropsChange(); BeforePropsChange();
flags.Clear(); flags.Clear();
} }
@ -306,11 +305,13 @@ namespace CodeImp.DoomBuilder.Map
// This removes textures that are not required // This removes textures that are not required
public void RemoveUnneededTextures(bool removemiddle, bool force) public void RemoveUnneededTextures(bool removemiddle, bool force)
{ {
BeforePropsChange(); bool changed = false; //mxd
// The middle texture can be removed regardless of any sector tag or linedef action // The middle texture can be removed regardless of any sector tag or linedef action
if(!MiddleRequired() && removemiddle) if(!MiddleRequired() && removemiddle)
{ {
BeforePropsChange(); //mxd
changed = true; //mxd
this.texnamemid = "-"; this.texnamemid = "-";
this.longtexnamemid = MapSet.EmptyLongName; this.longtexnamemid = MapSet.EmptyLongName;
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -324,6 +325,10 @@ namespace CodeImp.DoomBuilder.Map
{ {
if(!HighRequired()) if(!HighRequired())
{ {
if(!changed) { //mxd
BeforePropsChange();
changed = true;
}
this.texnamehigh = "-"; this.texnamehigh = "-";
this.longtexnamehigh = MapSet.EmptyLongName; this.longtexnamehigh = MapSet.EmptyLongName;
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -331,6 +336,7 @@ namespace CodeImp.DoomBuilder.Map
if(!LowRequired()) if(!LowRequired())
{ {
if(!changed) BeforePropsChange(); //mxd
this.texnamelow = "-"; this.texnamelow = "-";
this.longtexnamelow = MapSet.EmptyLongName; this.longtexnamelow = MapSet.EmptyLongName;
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -349,11 +355,9 @@ namespace CodeImp.DoomBuilder.Map
// Texture is required when ceiling of other side is lower // Texture is required when ceiling of other side is lower
return (Other.sector.CeilHeight < this.sector.CeilHeight); return (Other.sector.CeilHeight < this.sector.CeilHeight);
} }
else
{
return false; return false;
} }
}
/// <summary> /// <summary>
/// This checks if a texture is required /// This checks if a texture is required
@ -375,11 +379,9 @@ namespace CodeImp.DoomBuilder.Map
// Texture is required when floor of other side is higher // Texture is required when floor of other side is higher
return (Other.sector.FloorHeight > this.sector.FloorHeight); return (Other.sector.FloorHeight > this.sector.FloorHeight);
} }
else
{
return false; return false;
} }
}
/// <summary> /// <summary>
/// This returns the height of the upper wall part. Returns 0 when no upper part exists. /// This returns the height of the upper wall part. Returns 0 when no upper part exists.
@ -394,11 +396,9 @@ namespace CodeImp.DoomBuilder.Map
int height = top - bottom; int height = top - bottom;
return (height > 0) ? height : 0; return (height > 0) ? height : 0;
} }
else
{
return 0; return 0;
} }
}
/// <summary> /// <summary>
/// This returns the height of the middle wall part. /// This returns the height of the middle wall part.
@ -435,11 +435,9 @@ namespace CodeImp.DoomBuilder.Map
int height = top - bottom; int height = top - bottom;
return (height > 0) ? height : 0; return (height > 0) ? height : 0;
} }
else
{
return 0; return 0;
} }
}
// This creates a checksum from the sidedef properties // This creates a checksum from the sidedef properties
// Used for faster sidedefs compression // Used for faster sidedefs compression

View file

@ -81,14 +81,14 @@ namespace CodeImp.DoomBuilder.BuilderModes.ClassicModes
if (block == null) continue; if (block == null) continue;
foreach (Vertex blockVert in block.Vertices) { foreach (Vertex blockVert in block.Vertices) {
if(!blockVert.IsDisposed && blockVert.Index != v.Index && blockVert.Position == v.Position) { if(blockVert.IsDisposed || blockVert.Index == v.Index || blockVert.Position != v.Position) continue;
foreach(Linedef l in blockVert.Linedefs) foreach(Linedef l in blockVert.Linedefs)
if(!movedLines.Contains(l)) movedLines.Add(l); if(!movedLines.Contains(l)) movedLines.Add(l);
v.Join(blockVert); v.Join(blockVert);
break; break;
} }
} }
}
// Update cached values of lines because we may need their length/angle // Update cached values of lines because we may need their length/angle
General.Map.Map.Update(true, false); General.Map.Map.Update(true, false);