diff --git a/Source/Builder.csproj b/Source/Builder.csproj index a3ba0fdf..36e56856 100644 --- a/Source/Builder.csproj +++ b/Source/Builder.csproj @@ -114,6 +114,7 @@ + diff --git a/Source/BuilderModes/Testing/TriangulatorMode.cs b/Source/BuilderModes/Testing/TriangulatorMode.cs index ba0ba412..f5880025 100644 --- a/Source/BuilderModes/Testing/TriangulatorMode.cs +++ b/Source/BuilderModes/Testing/TriangulatorMode.cs @@ -56,7 +56,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing private Sector highlighted; // Labels - private ICollection labelpos; + private ICollection labelpos; #endregion @@ -140,14 +140,14 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing renderer.PlotSector(highlighted, General.Colors.Highlight); renderer.Finish(); } - + // Render labels if(renderer.StartOverlay(true)) { if(labelpos != null) { - foreach(Vector2D v in labelpos) - renderer.RenderRectangleFilled(new RectangleF(v.x - 5, v.y - 5, 10, 10), General.Colors.Indication, true); + foreach(LabelPositionInfo lb in labelpos) + renderer.RenderRectangleFilled(new RectangleF(lb.position.x - lb.radius / 2, lb.position.y - lb.radius / 2, lb.radius, lb.radius), General.Colors.Indication, true); } renderer.Finish(); @@ -181,20 +181,20 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing // Render highlighted item if((highlighted != null) && !highlighted.IsDisposed) renderer.PlotSector(highlighted, General.Colors.Highlight); - + // Done renderer.Finish(); } - + // Render labels if(renderer.StartOverlay(true)) { if(labelpos != null) { - foreach(Vector2D v in labelpos) - renderer.RenderRectangleFilled(new RectangleF(v.x - 5, v.y - 5, 10, 10), General.Colors.Indication, true); + foreach(LabelPositionInfo lb in labelpos) + renderer.RenderRectangleFilled(new RectangleF(lb.position.x - lb.radius / 2, lb.position.y - lb.radius / 2, lb.radius, lb.radius), General.Colors.Indication, true); } - + renderer.Finish(); } @@ -293,6 +293,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing { ICollection selected; TriangleList triangles; + PixelColor c; base.OnMouseUp(e); @@ -301,22 +302,30 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing { // Get a triangulator and bind events Triangulation t = Triangulation.Create(highlighted); - + // Start with a clear display if(renderer.StartPlotter(true)) { // Render lines and vertices renderer.PlotLinedefSet(General.Map.Map.Linedefs); renderer.PlotVerticesSet(General.Map.Map.Vertices); - - // Go for all triangle vertices + + // Go for all triangle vertices to render the inside lines only for(int i = 0; i < t.Vertices.Count; i += 3) { - renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], General.Colors.Selection); - renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], General.Colors.Selection); - renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], General.Colors.Selection); + if(t.Sidedefs[i + 0] == null) renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.DeepSkyBlue)); + if(t.Sidedefs[i + 1] == null) renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.DeepSkyBlue)); + if(t.Sidedefs[i + 2] == null) renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.DeepSkyBlue)); } - + + // Go for all triangle vertices to renderthe outside lines only + for(int i = 0; i < t.Vertices.Count; i += 3) + { + if(t.Sidedefs[i + 0] != null) renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.Red)); + if(t.Sidedefs[i + 1] != null) renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.Red)); + if(t.Sidedefs[i + 2] != null) renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.Red)); + } + // Done renderer.Finish(); renderer.Present(); diff --git a/Source/Geometry/LabelPositionInfo.cs b/Source/Geometry/LabelPositionInfo.cs new file mode 100644 index 00000000..186af49e --- /dev/null +++ b/Source/Geometry/LabelPositionInfo.cs @@ -0,0 +1,42 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct LabelPositionInfo + { + // Members + public Vector2D position; + public float radius; + + // Constructor + public LabelPositionInfo(Vector2D position, float radius) + { + this.position = position; + this.radius = radius; + } + } +} diff --git a/Source/Geometry/Tools.cs b/Source/Geometry/Tools.cs index cb15c213..5cda7d46 100644 --- a/Source/Geometry/Tools.cs +++ b/Source/Geometry/Tools.cs @@ -622,9 +622,9 @@ namespace CodeImp.DoomBuilder.Geometry #region ================== Sector Labels // This finds the ideal label positions for a sector - public static ICollection FindLabelPositions(Sector s) + public static List FindLabelPositions(Sector s) { - List positions = new List(2); + List positions = new List(2); int islandoffset = 0; // Do we have a triangulation? @@ -632,10 +632,10 @@ namespace CodeImp.DoomBuilder.Geometry if(triangles != null) { // Go for all islands - for(int island = 0; island < triangles.IslandVertices.Count; island++) + for(int i = 0; i < triangles.IslandVertices.Count; i++) { - Dictionary sides = new Dictionary(triangles.IslandVertices[island] >> 1); - List candidatelines = new List(triangles.IslandVertices[island] >> 1); + Dictionary sides = new Dictionary(triangles.IslandVertices[i] >> 1); + List candidatepositions = new List(triangles.IslandVertices[i] >> 1); float founddistance = float.MinValue; Vector2D foundposition = new Vector2D(); float minx = float.MaxValue; @@ -646,24 +646,26 @@ namespace CodeImp.DoomBuilder.Geometry // Make candidate lines that are not along sidedefs // We do this before testing the candidate against the sidedefs so that // we can collect the relevant sidedefs first in the same run - for(int i = 0; i < triangles.IslandVertices[island]; i += 3) + for(int t = 0; t < triangles.IslandVertices[i]; t += 3) { - Vector2D v1 = triangles.Vertices[islandoffset + i + 2]; - for(int k = 0; k < 3; k++) + int triangleoffset = islandoffset + t; + Vector2D v1 = triangles.Vertices[triangleoffset + 2]; + Sidedef sd = triangles.Sidedefs[triangleoffset + 2]; + for(int v = 0; v < 3; v++) { - Vector2D v2 = triangles.Vertices[islandoffset + i + k]; - Sidedef sd = triangles.Sidedefs[islandoffset + i + k]; + Vector2D v2 = triangles.Vertices[triangleoffset + v]; // Not along a sidedef? Then this line is across the sector // and guaranteed to be inside the sector! if(sd == null) { // Make the line - candidatelines.Add(new Line2D(v1, v2)); + candidatepositions.Add(v1 + (v2 - v1) * 0.5f); } else { - // This sidedefs is part of this island and must be checked against + // This sidedefs is part of this island and must be checked + // so add it to the dictionary sides[sd] = sd.Line; } @@ -674,67 +676,68 @@ namespace CodeImp.DoomBuilder.Geometry maxy = Math.Max(maxy, v1.y); // Next + sd = triangles.Sidedefs[triangleoffset + v]; v1 = v2; } } // Any candidate lines found at all? - if(candidatelines.Count > 0) + if(candidatepositions.Count > 0) { // Start with the first line - foreach(Line2D sourceline in candidatelines) + foreach(Vector2D candidatepos in candidatepositions) { - // Get center point - Vector2D candidateposition = sourceline.GetCoordinatesAt(0.5f); - // Check distance against other lines float smallestdist = int.MaxValue; foreach(KeyValuePair sd in sides) { // Check the distance - float distance = sd.Value.DistanceToSq(candidateposition, true); + float distance = sd.Value.DistanceToSq(candidatepos, true); smallestdist = Math.Min(smallestdist, distance); } // Keep this candidate if it is better than previous if(smallestdist > founddistance) { - foundposition = candidateposition; + foundposition = candidatepos; founddistance = smallestdist; } } // No cceptable line found, just use the first! - positions.Add(foundposition); + positions.Add(new LabelPositionInfo(foundposition, (float)Math.Sqrt(founddistance))); } else { // No candidate lines found. // Check to see if the island is a triangle - if(triangles.IslandVertices[island] == 3) + if(triangles.IslandVertices[i] == 3) { // Use the center of the triangle - Vector2D v = triangles.Vertices[islandoffset] + triangles.Vertices[islandoffset + 1] + triangles.Vertices[islandoffset + 2]; - positions.Add(v / 3.0f); + // TODO: Use the 'incenter' instead, see http://mathworld.wolfram.com/Incenter.html + Vector2D v = (triangles.Vertices[islandoffset] + triangles.Vertices[islandoffset + 1] + triangles.Vertices[islandoffset + 2]) / 3.0f; + float d = Line2D.GetDistanceToLineSq(triangles.Vertices[islandoffset], triangles.Vertices[islandoffset + 1], v, false); + d = Math.Min(d, Line2D.GetDistanceToLineSq(triangles.Vertices[islandoffset + 1], triangles.Vertices[islandoffset + 2], v, false)); + d = Math.Min(d, Line2D.GetDistanceToLineSq(triangles.Vertices[islandoffset + 2], triangles.Vertices[islandoffset], v, false)); + positions.Add(new LabelPositionInfo(v, (float)Math.Sqrt(d))); } else { // Use the center of this island. - positions.Add(new Vector2D(minx + (maxx - minx) * 0.5f, miny + (maxy - miny) * 0.5f)); + float d = Math.Min((maxx - minx) * 0.5f, (maxy - miny) * 0.5f); + positions.Add(new LabelPositionInfo(new Vector2D(minx + (maxx - minx) * 0.5f, miny + (maxy - miny) * 0.5f), d)); } } // Done with this island - islandoffset += triangles.IslandVertices[island]; + islandoffset += triangles.IslandVertices[i]; } } else { - // No triangulation was made. Just return the center point. - //RectangleF rect = s.CreateBBox(); - //return new Vector2D(rect.X + (rect.Width * 0.5f), rect.Y + (rect.Height * 0.5f)); - positions.Add(new Vector2D()); + // No triangulation was made. FAIL! + General.Fail("No triangulation exists for sector " + s, "Triangulation is required to create label positions for a sector."); } // Done diff --git a/Source/Geometry/Triangulation.cs b/Source/Geometry/Triangulation.cs index d9785104..cdfa9553 100644 --- a/Source/Geometry/Triangulation.cs +++ b/Source/Geometry/Triangulation.cs @@ -501,16 +501,18 @@ namespace CodeImp.DoomBuilder.Geometry v1 = v2; v2 = v2.Next; } - + // Found anything? if(insertbefore != null) { + Sidedef sd = (insertbefore.Previous == null) ? insertbefore.List.Last.Value.Sidedef : insertbefore.Previous.Value.Sidedef; + // Find the position where we have to split the outer polygon split = new EarClipVertex(starttoright.GetCoordinatesAt(foundu), null); - + // Insert manual split vertices - p.AddBefore(insertbefore, new EarClipVertex(split, null)); - + p.AddBefore(insertbefore, new EarClipVertex(split, sd)); + // Start inserting from the start (do I make sense this time?) v1 = start; do @@ -520,10 +522,9 @@ namespace CodeImp.DoomBuilder.Geometry if(v1.Next != null) v1 = v1.Next; else v1 = v1.List.First; } while(v1 != start); - + // Insert manual split vertices - Sidedef sd = (insertbefore.Previous == null) ? insertbefore.List.Last.Value.Sidedef : insertbefore.Previous.Value.Sidedef; - p.AddBefore(insertbefore, new EarClipVertex(start.Value, null)); + p.AddBefore(insertbefore, new EarClipVertex(start.Value, sd)); p.AddBefore(insertbefore, new EarClipVertex(split, sd)); } } @@ -696,7 +697,7 @@ namespace CodeImp.DoomBuilder.Geometry sidedefslist.Add(triangle[1].Sidedef); verticeslist.Add(triangle[2].Position); if(!last) sidedefslist.Add(null); else sidedefslist.Add(triangle[2].Sidedef); - + // Modify the first earclipvertex of this triangle, it no longer lies along a sidedef triangle[0].Sidedef = null; } diff --git a/Source/Map/Sector.cs b/Source/Map/Sector.cs index 005fde0c..17399808 100644 --- a/Source/Map/Sector.cs +++ b/Source/Map/Sector.cs @@ -25,6 +25,7 @@ using CodeImp.DoomBuilder.IO; using CodeImp.DoomBuilder.Geometry; using System.Drawing; using CodeImp.DoomBuilder.Rendering; +using System.Collections.ObjectModel; #endregion @@ -74,7 +75,8 @@ namespace CodeImp.DoomBuilder.Map private bool triangulationneeded; private Triangulation triangles; private FlatVertex[] flatvertices; - + private ReadOnlyCollection labels; + #endregion #region ================== Properties @@ -98,7 +100,8 @@ namespace CodeImp.DoomBuilder.Map public Sector Clone { get { return clone; } set { clone = value; } } public Triangulation Triangles { get { return triangles; } } public FlatVertex[] FlatVertices { get { return flatvertices; } } - + public ReadOnlyCollection Labels { get { return labels; } } + #endregion #region ================== Constructor / Disposer @@ -201,13 +204,13 @@ namespace CodeImp.DoomBuilder.Map } } } - + // This attaches a thing and returns the listitem public LinkedListNode AttachThing(Thing t) { return things.AddLast(t); } - + // This detaches a thing public void DetachThing(LinkedListNode l) { if(!isdisposed) things.Remove(l); } - + // This updates the sector when changes have been made public void UpdateCache() { @@ -219,6 +222,9 @@ namespace CodeImp.DoomBuilder.Map { // Triangulate sector triangles = Triangulation.Create(this); + + // Make label positions + labels = Array.AsReadOnly(Tools.FindLabelPositions(this).ToArray()); } // Brightness color (alpha is opaque)