UltimateZoneBuilder/Source/Plugins/BuilderEffects/Interface/JitterVerticesForm.cs
2020-05-21 14:20:02 +02:00

287 lines
8 KiB
C#
Executable file

#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.VisualModes;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.Geometry;
#endregion
namespace CodeImp.DoomBuilder.BuilderEffects
{
public partial class JitterVerticesForm : DelayedForm
{
#region ================== Variables
private readonly string editingModeName;
private readonly List<Vertex> selection;
private readonly List<VisualSector> visualSectors;
private readonly List<VisualVertexPair> visualVerts;
private readonly VertexData[] vertexData;
private readonly int MaxSafeDistance;
#endregion
#region ================== Structs
private struct VertexData
{
public Vector2D Position;
public int SafeDistance;
public float JitterAngle;
}
#endregion
#region ================== Constructor / Setup
public JitterVerticesForm(string editingModeName)
{
this.editingModeName = editingModeName;
this.HelpRequested += JitterVerticesForm_HelpRequested;
InitializeComponent();
//get selection
selection = new List<Vertex>();
if(editingModeName == "BaseVisualMode")
{
VisualMode vm = (VisualMode)General.Editing.Mode;
List<VisualGeometry> visualSelection = vm.GetSelectedSurfaces();
visualSectors = new List<VisualSector>();
int linesCount = 0;
foreach(VisualGeometry vg in visualSelection)
{
if(vg.Sidedef != null && vm.VisualSectorExists(vg.Sidedef.Sector))
{
if(!selection.Contains(vg.Sidedef.Line.Start))
selection.Add(vg.Sidedef.Line.Start);
if(!selection.Contains(vg.Sidedef.Line.End))
selection.Add(vg.Sidedef.Line.End);
linesCount++;
visualSectors.Add(vm.GetVisualSector(vg.Sidedef.Sector));
if(vg.Sidedef.Other != null && vg.Sidedef.Other.Sector != null && vm.VisualSectorExists(vg.Sidedef.Other.Sector))
visualSectors.Add(vm.GetVisualSector(vg.Sidedef.Other.Sector));
}
}
visualVerts = new List<VisualVertexPair>();
foreach(Vertex vert in selection)
{
if(vm.VisualVertices.ContainsKey(vert)) visualVerts.Add(vm.VisualVertices[vert]);
}
//update window header
this.Text = "Randomize " + linesCount + (linesCount > 1 ? " linedefs" : " linedef");
}
else if(editingModeName == "LinedefsMode")
{
ICollection<Linedef> list = General.Map.Map.GetSelectedLinedefs(true);
int linesCount = 0;
foreach(Linedef l in list)
{
if(!selection.Contains(l.Start)) selection.Add(l.Start);
if(!selection.Contains(l.End)) selection.Add(l.End);
linesCount++;
}
//update window header
this.Text = "Randomize " + linesCount + (linesCount > 1 ? " linedefs" : " linedef");
}
else
{
ICollection<Vertex> list = General.Map.Map.GetSelectedVertices(true);
foreach(Vertex v in list) selection.Add(v);
//update window header
this.Text = "Randomize " + selection.Count + (selection.Count > 1 ? " vertices" : " vertex");
}
if(selection.Count == 0)
{
General.Interface.DisplayStatus(StatusType.Warning, "Unable to get vertices from selection!");
return;
}
//create undo
General.Map.UndoRedo.ClearAllRedos();
General.Map.UndoRedo.CreateUndo("Randomize " + selection.Count + (selection.Count > 1 ? " vertices" : " vertex"));
Dictionary<Vertex, VertexData> data = new Dictionary<Vertex, VertexData>();
foreach(Vertex v in selection)
{
VertexData vd = new VertexData {Position = v.Position};
data.Add(v, vd);
}
foreach(Vertex v in selection)
{
if(v.Linedefs == null) continue;
//get nearest linedef
Linedef closestLine = null;
double distance = double.MaxValue;
// Go for all linedefs in selection
foreach(Linedef l in General.Map.Map.Linedefs)
{
if(v.Linedefs.Contains(l)) continue;
// Calculate distance and check if closer than previous find
double d = l.SafeDistanceToSq(v.Position, true);
if(d < distance)
{
// This one is closer
closestLine = l;
distance = d;
}
}
if(closestLine == null) continue;
double closestLineDistance = Vector2D.Distance(v.Position, closestLine.NearestOnLine(v.Position));
//check SafeDistance of closest line
if(data.ContainsKey(closestLine.Start)
&& data[closestLine.Start].SafeDistance > closestLineDistance)
{
VertexData vd = data[closestLine.Start];
vd.SafeDistance = (int)Math.Floor(closestLineDistance);
data[closestLine.Start] = vd;
}
if(data.ContainsKey(closestLine.End)
&& data[closestLine.End].SafeDistance > closestLineDistance)
{
VertexData vd = data[closestLine.End];
vd.SafeDistance = (int)Math.Floor(closestLineDistance);
data[closestLine.End] = vd;
}
//save SafeDistance
int dist = (int)Math.Floor(closestLineDistance);
if(data[v].SafeDistance == 0 || data[v].SafeDistance > dist)
{
VertexData vd = data[v];
vd.SafeDistance = dist;
data[v] = vd;
}
}
//store properties
vertexData = new VertexData[data.Values.Count];
data.Values.CopyTo(vertexData, 0);
for(int i = 0; i < vertexData.Length; i++)
{
if(vertexData[i].SafeDistance > 0)
vertexData[i].SafeDistance /= 2;
if(MaxSafeDistance < vertexData[i].SafeDistance)
MaxSafeDistance = vertexData[i].SafeDistance;
}
positionJitterAmmount.Maximum = MaxSafeDistance;
UpdateAngles();
}
#endregion
#region ================== Utility
private void ApplyTranslationJitter(int ammount)
{
for(int i = 0; i < selection.Count; i++)
{
int curAmmount = ammount > vertexData[i].SafeDistance ? vertexData[i].SafeDistance : ammount;
selection[i].Move(new Vector2D(vertexData[i].Position.x + (int)(Math.Sin(vertexData[i].JitterAngle) * curAmmount), vertexData[i].Position.y + (int)(Math.Cos(vertexData[i].JitterAngle) * curAmmount)));
}
//update view
if(editingModeName == "BaseVisualMode")
{
General.Map.Map.Update();
General.Map.IsChanged = true;
foreach(VisualSector vs in visualSectors) vs.UpdateSectorGeometry(true);
foreach(VisualSector vs in visualSectors) vs.UpdateSectorData();
foreach(VisualVertexPair pair in visualVerts)
{
pair.Changed = true;
pair.Update();
}
}
else
{
General.Interface.RedrawDisplay();
}
}
private void UpdateAngles()
{
for(int i = 0; i < vertexData.Length; i++)
{
VertexData vd = vertexData[i];
vd.JitterAngle = (float)(General.Random(0, 359) * Math.PI / 180f);
vertexData[i] = vd;
}
}
#endregion
#region ================== Events
private void bApply_Click(object sender, EventArgs e)
{
// Update cached values
General.Map.Map.Update();
General.Map.IsChanged = true;
// Clear selection
General.Actions.InvokeAction("builder_clearselection");
this.DialogResult = DialogResult.OK;
Close();
}
private void bCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
Close();
}
private void JitterVerticesForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(this.DialogResult == DialogResult.Cancel)
General.Map.UndoRedo.WithdrawUndo(); //undo changes
}
private void positionJitterAmmount_OnValueChanging(object sender, EventArgs e)
{
ApplyTranslationJitter(positionJitterAmmount.Value);
}
private void bUpdateTranslation_Click(object sender, EventArgs e)
{
UpdateAngles();
ApplyTranslationJitter(positionJitterAmmount.Value);
}
private void JitterVerticesForm_HelpRequested(object sender, HelpEventArgs hlpevent)
{
General.ShowHelp("gzdb/features/all_modes/jitter.html");
hlpevent.Handled = true;
}
#endregion
}
}