mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-26 22:01:45 +00:00
nice automatic label positions for sectors
This commit is contained in:
parent
f941f7a7ad
commit
09da6dbefd
6 changed files with 120 additions and 58 deletions
|
@ -114,6 +114,7 @@
|
|||
<Compile Include="Actions\SpecialKeys.cs" />
|
||||
<Compile Include="Config\NodebuilderInfo.cs" />
|
||||
<Compile Include="Geometry\Angle2D.cs" />
|
||||
<Compile Include="Geometry\LabelPositionInfo.cs" />
|
||||
<Compile Include="Geometry\LinedefsTracePath.cs" />
|
||||
<Compile Include="Geometry\LinedefAngleSorter.cs" />
|
||||
<Compile Include="Geometry\Tools.cs" />
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
private Sector highlighted;
|
||||
|
||||
// Labels
|
||||
private ICollection<Vector2D> labelpos;
|
||||
private ICollection<LabelPositionInfo> 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<Sector> 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();
|
||||
|
|
42
Source/Geometry/LabelPositionInfo.cs
Normal file
42
Source/Geometry/LabelPositionInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -622,9 +622,9 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
#region ================== Sector Labels
|
||||
|
||||
// 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;
|
||||
|
||||
// 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<Sidedef, Linedef> sides = new Dictionary<Sidedef, Linedef>(triangles.IslandVertices[island] >> 1);
|
||||
List<Line2D> candidatelines = new List<Line2D>(triangles.IslandVertices[island] >> 1);
|
||||
Dictionary<Sidedef, Linedef> sides = new Dictionary<Sidedef, Linedef>(triangles.IslandVertices[i] >> 1);
|
||||
List<Vector2D> candidatepositions = new List<Vector2D>(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<Sidedef, Linedef> 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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<LabelPositionInfo> 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<LabelPositionInfo> 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<Thing> AttachThing(Thing t) { return things.AddLast(t); }
|
||||
|
||||
|
||||
// This detaches a thing
|
||||
public void DetachThing(LinkedListNode<Thing> 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<LabelPositionInfo>(Tools.FindLabelPositions(this).ToArray());
|
||||
}
|
||||
|
||||
// Brightness color (alpha is opaque)
|
||||
|
|
Loading…
Reference in a new issue