UltimateZoneBuilder/Source/Plugins/BuilderModes/General/LineLengthLabel.cs
MaxED f646dd7a57 Classic modes: rewritten and optimized some parts of text label rendering logic.
Internal, API: TextLabels can now display multi-line text, their font can be changed, they can have a background.
2016-03-30 23:25:03 +00:00

208 lines
5.6 KiB
C#

#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 CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using System.Drawing;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
public class LineLengthLabel : IDisposable
{
#region ================== Constants
private const string VALUE_FORMAT = "0";
#endregion
#region ================== Variables
protected TextLabel label;
protected Vector2D start;
protected Vector2D end;
//mxd. Display options
private bool showangle;
private bool offsetposition;
#endregion
#region ================== Properties
public TextLabel TextLabel { get { return label; } }
//mxd. Display options
public bool ShowAngle { get { return showangle; } set { showangle = value; UpdateText(); } }
public bool OffsetPosition { get { return offsetposition; } set { offsetposition = value; Move(start, end); } }
public PixelColor TextColor { get { return label.Color; } set { label.Color = value; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public LineLengthLabel()
{
this.showangle = true;
this.offsetposition = true;
// Initialize
Initialize();
}
//mxd. Constructor
public LineLengthLabel(bool showangle, bool offsetposition)
{
this.showangle = showangle;
this.offsetposition = offsetposition;
// Initialize
Initialize();
}
// Constructor
public LineLengthLabel(Vector2D start, Vector2D end)
{
this.showangle = true; //mxd
this.offsetposition = true; //mxd
// Initialize
Initialize();
Move(start, end);
}
//mxd. Constructor
public LineLengthLabel(Vector2D start, Vector2D end, bool showangle, bool offsetposition)
{
this.showangle = showangle;
this.offsetposition = offsetposition;
// Initialize
Initialize();
Move(start, end);
}
// Initialization
private void Initialize()
{
label = new TextLabel();
label.AlignX = TextAlignmentX.Center;
label.AlignY = TextAlignmentY.Middle;
label.Color = General.Colors.Highlight;
label.Backcolor = General.Colors.Background;
label.TransformCoords = true;
}
// Disposer
public void Dispose()
{
label.Dispose();
}
#endregion
#region ================== Methods
// This updates the text
protected virtual void UpdateText()
{
Vector2D delta = end - start;
// Update label text
float length = delta.GetLength();
if(showangle)
{
int displayangle = General.ClampAngle((int)Math.Round(Angle2D.RadToDeg(delta.GetAngle())));
label.Text = "L:" + length.ToString(VALUE_FORMAT) + "; A:" + displayangle;
}
else
{
label.Text = length.ToString(VALUE_FORMAT);
}
}
//mxd. This moves the label so it stays on screen and offsets it vertically so it doesn't overlap the line
public virtual void Move(Vector2D start, Vector2D end)
{
// Store before making any adjustments to start/end...
this.start = start;
this.end = end;
// Update text label
UpdateText();
// Check if start/end point is on screen...
Vector2D lt = General.Map.Renderer2D.DisplayToMap(new Vector2D(0.0f, General.Interface.Display.Size.Height));
Vector2D rb = General.Map.Renderer2D.DisplayToMap(new Vector2D(General.Interface.Display.Size.Width, 0.0f));
RectangleF viewport = new RectangleF(lt.x, lt.y, rb.x - lt.x, rb.y - lt.y);
bool startvisible = viewport.Contains(start.x, start.y);
bool endvisible = viewport.Contains(end.x, end.y);
// Do this only when one point is visible, an the other isn't
if((!startvisible && endvisible) || (startvisible && !endvisible))
{
Line2D drawnline = new Line2D(start, end);
Line2D[] viewportsides = new[] {
new Line2D(lt, rb.x, lt.y), // top
new Line2D(lt.x, rb.y, rb.x, rb.y), // bottom
new Line2D(lt, lt.x, rb.y), // left
new Line2D(rb.x, lt.y, rb.x, rb.y), // right
};
foreach(Line2D side in viewportsides)
{
// Modify the start point so it stays on screen
float u;
if(!startvisible && side.GetIntersection(drawnline, out u))
{
start = drawnline.GetCoordinatesAt(u);
break;
}
// Modify the end point so it stays on screen
if(!endvisible && side.GetIntersection(drawnline, out u))
{
end = drawnline.GetCoordinatesAt(u);
break;
}
}
}
// Update label position
if(offsetposition)
{
Vector2D perpendicular = (end - start).GetPerpendicular();
float angle = perpendicular.GetAngle();
SizeF textsize = General.Interface.MeasureString(label.Text, label.Font);
float offset = textsize.Width * Math.Abs((float)Math.Sin(angle)) + textsize.Height * Math.Abs((float)Math.Cos(angle));
perpendicular = perpendicular.GetNormal().GetScaled(offset / 2.0f / General.Map.Renderer2D.Scale);
start += perpendicular;
end += perpendicular;
}
// Apply changes
Vector2D delta = end - start;
label.Rectangle = new RectangleF(start.x + delta.x * 0.5f, start.y + delta.y * 0.5f, 0f, 0f);
}
#endregion
}
}