nice automatic label positions for sectors

This commit is contained in:
codeimp 2008-10-07 12:54:15 +00:00
parent f941f7a7ad
commit 09da6dbefd
6 changed files with 120 additions and 58 deletions

View file

@ -114,6 +114,7 @@
<Compile Include="Actions\SpecialKeys.cs" /> <Compile Include="Actions\SpecialKeys.cs" />
<Compile Include="Config\NodebuilderInfo.cs" /> <Compile Include="Config\NodebuilderInfo.cs" />
<Compile Include="Geometry\Angle2D.cs" /> <Compile Include="Geometry\Angle2D.cs" />
<Compile Include="Geometry\LabelPositionInfo.cs" />
<Compile Include="Geometry\LinedefsTracePath.cs" /> <Compile Include="Geometry\LinedefsTracePath.cs" />
<Compile Include="Geometry\LinedefAngleSorter.cs" /> <Compile Include="Geometry\LinedefAngleSorter.cs" />
<Compile Include="Geometry\Tools.cs" /> <Compile Include="Geometry\Tools.cs" />

View file

@ -56,7 +56,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
private Sector highlighted; private Sector highlighted;
// Labels // Labels
private ICollection<Vector2D> labelpos; private ICollection<LabelPositionInfo> labelpos;
#endregion #endregion
@ -146,8 +146,8 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
{ {
if(labelpos != null) if(labelpos != null)
{ {
foreach(Vector2D v in labelpos) foreach(LabelPositionInfo lb in labelpos)
renderer.RenderRectangleFilled(new RectangleF(v.x - 5, v.y - 5, 10, 10), General.Colors.Indication, true); 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(); renderer.Finish();
@ -191,8 +191,8 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
{ {
if(labelpos != null) if(labelpos != null)
{ {
foreach(Vector2D v in labelpos) foreach(LabelPositionInfo lb in labelpos)
renderer.RenderRectangleFilled(new RectangleF(v.x - 5, v.y - 5, 10, 10), General.Colors.Indication, true); 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(); renderer.Finish();
@ -293,6 +293,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
{ {
ICollection<Sector> selected; ICollection<Sector> selected;
TriangleList triangles; TriangleList triangles;
PixelColor c;
base.OnMouseUp(e); base.OnMouseUp(e);
@ -309,12 +310,20 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
renderer.PlotLinedefSet(General.Map.Map.Linedefs); renderer.PlotLinedefSet(General.Map.Map.Linedefs);
renderer.PlotVerticesSet(General.Map.Map.Vertices); 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) for(int i = 0; i < t.Vertices.Count; i += 3)
{ {
renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], General.Colors.Selection); if(t.Sidedefs[i + 0] == null) renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.DeepSkyBlue));
renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], General.Colors.Selection); if(t.Sidedefs[i + 1] == null) renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.DeepSkyBlue));
renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], General.Colors.Selection); 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 // Done

View file

@ -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;
}
}
}

View file

@ -622,9 +622,9 @@ namespace CodeImp.DoomBuilder.Geometry
#region ================== Sector Labels #region ================== Sector Labels
// This finds the ideal label positions for a sector // This finds the ideal label positions for a sector
public static ICollection<Vector2D> FindLabelPositions(Sector s) public static List<LabelPositionInfo> FindLabelPositions(Sector s)
{ {
List<Vector2D> positions = new List<Vector2D>(2); List<LabelPositionInfo> positions = new List<LabelPositionInfo>(2);
int islandoffset = 0; int islandoffset = 0;
// Do we have a triangulation? // Do we have a triangulation?
@ -632,10 +632,10 @@ namespace CodeImp.DoomBuilder.Geometry
if(triangles != null) if(triangles != null)
{ {
// Go for all islands // Go for all islands
for(int island = 0; island < triangles.IslandVertices.Count; island++) for(int i = 0; i < triangles.IslandVertices.Count; i++)
{ {
Dictionary<Sidedef, Linedef> sides = new Dictionary<Sidedef, Linedef>(triangles.IslandVertices[island] >> 1); Dictionary<Sidedef, Linedef> sides = new Dictionary<Sidedef, Linedef>(triangles.IslandVertices[i] >> 1);
List<Line2D> candidatelines = new List<Line2D>(triangles.IslandVertices[island] >> 1); List<Vector2D> candidatepositions = new List<Vector2D>(triangles.IslandVertices[i] >> 1);
float founddistance = float.MinValue; float founddistance = float.MinValue;
Vector2D foundposition = new Vector2D(); Vector2D foundposition = new Vector2D();
float minx = float.MaxValue; float minx = float.MaxValue;
@ -646,24 +646,26 @@ namespace CodeImp.DoomBuilder.Geometry
// Make candidate lines that are not along sidedefs // Make candidate lines that are not along sidedefs
// We do this before testing the candidate against the sidedefs so that // We do this before testing the candidate against the sidedefs so that
// we can collect the relevant sidedefs first in the same run // 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]; int triangleoffset = islandoffset + t;
for(int k = 0; k < 3; k++) 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]; Vector2D v2 = triangles.Vertices[triangleoffset + v];
Sidedef sd = triangles.Sidedefs[islandoffset + i + k];
// Not along a sidedef? Then this line is across the sector // Not along a sidedef? Then this line is across the sector
// and guaranteed to be inside the sector! // and guaranteed to be inside the sector!
if(sd == null) if(sd == null)
{ {
// Make the line // Make the line
candidatelines.Add(new Line2D(v1, v2)); candidatepositions.Add(v1 + (v2 - v1) * 0.5f);
} }
else 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; sides[sd] = sd.Line;
} }
@ -674,67 +676,68 @@ namespace CodeImp.DoomBuilder.Geometry
maxy = Math.Max(maxy, v1.y); maxy = Math.Max(maxy, v1.y);
// Next // Next
sd = triangles.Sidedefs[triangleoffset + v];
v1 = v2; v1 = v2;
} }
} }
// Any candidate lines found at all? // Any candidate lines found at all?
if(candidatelines.Count > 0) if(candidatepositions.Count > 0)
{ {
// Start with the first line // 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 // Check distance against other lines
float smallestdist = int.MaxValue; float smallestdist = int.MaxValue;
foreach(KeyValuePair<Sidedef, Linedef> sd in sides) foreach(KeyValuePair<Sidedef, Linedef> sd in sides)
{ {
// Check the distance // Check the distance
float distance = sd.Value.DistanceToSq(candidateposition, true); float distance = sd.Value.DistanceToSq(candidatepos, true);
smallestdist = Math.Min(smallestdist, distance); smallestdist = Math.Min(smallestdist, distance);
} }
// Keep this candidate if it is better than previous // Keep this candidate if it is better than previous
if(smallestdist > founddistance) if(smallestdist > founddistance)
{ {
foundposition = candidateposition; foundposition = candidatepos;
founddistance = smallestdist; founddistance = smallestdist;
} }
} }
// No cceptable line found, just use the first! // No cceptable line found, just use the first!
positions.Add(foundposition); positions.Add(new LabelPositionInfo(foundposition, (float)Math.Sqrt(founddistance)));
} }
else else
{ {
// No candidate lines found. // No candidate lines found.
// Check to see if the island is a triangle // 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 // Use the center of the triangle
Vector2D v = triangles.Vertices[islandoffset] + triangles.Vertices[islandoffset + 1] + triangles.Vertices[islandoffset + 2]; // TODO: Use the 'incenter' instead, see http://mathworld.wolfram.com/Incenter.html
positions.Add(v / 3.0f); 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 else
{ {
// Use the center of this island. // 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 // Done with this island
islandoffset += triangles.IslandVertices[island]; islandoffset += triangles.IslandVertices[i];
} }
} }
else else
{ {
// No triangulation was made. Just return the center point. // No triangulation was made. FAIL!
//RectangleF rect = s.CreateBBox(); General.Fail("No triangulation exists for sector " + s, "Triangulation is required to create label positions for a sector.");
//return new Vector2D(rect.X + (rect.Width * 0.5f), rect.Y + (rect.Height * 0.5f));
positions.Add(new Vector2D());
} }
// Done // Done

View file

@ -505,11 +505,13 @@ namespace CodeImp.DoomBuilder.Geometry
// Found anything? // Found anything?
if(insertbefore != null) 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 // Find the position where we have to split the outer polygon
split = new EarClipVertex(starttoright.GetCoordinatesAt(foundu), null); split = new EarClipVertex(starttoright.GetCoordinatesAt(foundu), null);
// Insert manual split vertices // 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?) // Start inserting from the start (do I make sense this time?)
v1 = start; v1 = start;
@ -522,8 +524,7 @@ namespace CodeImp.DoomBuilder.Geometry
while(v1 != start); while(v1 != start);
// Insert manual split vertices // 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, sd));
p.AddBefore(insertbefore, new EarClipVertex(start.Value, null));
p.AddBefore(insertbefore, new EarClipVertex(split, sd)); p.AddBefore(insertbefore, new EarClipVertex(split, sd));
} }
} }

View file

@ -25,6 +25,7 @@ using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Geometry;
using System.Drawing; using System.Drawing;
using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.Rendering;
using System.Collections.ObjectModel;
#endregion #endregion
@ -74,6 +75,7 @@ namespace CodeImp.DoomBuilder.Map
private bool triangulationneeded; private bool triangulationneeded;
private Triangulation triangles; private Triangulation triangles;
private FlatVertex[] flatvertices; private FlatVertex[] flatvertices;
private ReadOnlyCollection<LabelPositionInfo> labels;
#endregion #endregion
@ -98,6 +100,7 @@ namespace CodeImp.DoomBuilder.Map
public Sector Clone { get { return clone; } set { clone = value; } } public Sector Clone { get { return clone; } set { clone = value; } }
public Triangulation Triangles { get { return triangles; } } public Triangulation Triangles { get { return triangles; } }
public FlatVertex[] FlatVertices { get { return flatvertices; } } public FlatVertex[] FlatVertices { get { return flatvertices; } }
public ReadOnlyCollection<LabelPositionInfo> Labels { get { return labels; } }
#endregion #endregion
@ -219,6 +222,9 @@ namespace CodeImp.DoomBuilder.Map
{ {
// Triangulate sector // Triangulate sector
triangles = Triangulation.Create(this); triangles = Triangulation.Create(this);
// Make label positions
labels = Array.AsReadOnly<LabelPositionInfo>(Tools.FindLabelPositions(this).ToArray());
} }
// Brightness color (alpha is opaque) // Brightness color (alpha is opaque)