2019-10-23 18:55:41 +00:00
|
|
|
|
|
|
|
#region ================== Copyright (c) 2007 Pascal vd Heiden
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
|
|
|
|
* Copyright (c) 2014 Boris Iwanski
|
|
|
|
* 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.Drawing;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Text;
|
|
|
|
using System.Windows.Forms;
|
|
|
|
using System.IO;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using CodeImp.DoomBuilder.Windows;
|
|
|
|
using CodeImp.DoomBuilder.Data;
|
|
|
|
using CodeImp.DoomBuilder.IO;
|
|
|
|
using CodeImp.DoomBuilder.Map;
|
|
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
|
|
using CodeImp.DoomBuilder.Editing;
|
|
|
|
using CodeImp.DoomBuilder.Actions;
|
|
|
|
using CodeImp.DoomBuilder.Config;
|
|
|
|
using CodeImp.DoomBuilder.Types;
|
|
|
|
using CodeImp.DoomBuilder.BuilderModes;
|
|
|
|
// using CodeImp.DoomBuilder.GZBuilder.Geometry;
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.ThreeDFloorMode
|
|
|
|
{
|
|
|
|
public class SlopeObject
|
|
|
|
{
|
|
|
|
private ThreeDFloor threedfloor;
|
|
|
|
private Vector2D position;
|
|
|
|
private int v;
|
|
|
|
|
|
|
|
public ThreeDFloor ThreeDFloor { get { return threedfloor; } set { threedfloor = value; } }
|
|
|
|
public Vector2D Position { get { return position; } set { position = value; } }
|
|
|
|
public int V { get { return v; } set { v = value; } }
|
|
|
|
}
|
|
|
|
|
|
|
|
[EditMode(DisplayName = "Slope Mode",
|
|
|
|
SwitchAction = "threedslopemode", // Action name used to switch to this mode
|
|
|
|
ButtonImage = "SlopeModeIcon.png", // Image resource name for the button
|
|
|
|
ButtonOrder = int.MinValue + 501, // Position of the button (lower is more to the left)
|
|
|
|
ButtonGroup = "000_editing",
|
|
|
|
SupportedMapFormats = new[] { "UniversalMapSetIO" },
|
2021-03-21 21:58:06 +00:00
|
|
|
RequiredMapFeatures = new[] { "PlaneEquationSupport" },
|
|
|
|
UseByDefault = true,
|
2021-03-21 20:19:14 +00:00
|
|
|
IsDeprecated = true,
|
|
|
|
DeprecationMessage = "Please use the visual sloping functionality instead.")]
|
2019-10-23 18:55:41 +00:00
|
|
|
|
|
|
|
public class SlopeMode : ClassicMode
|
|
|
|
{
|
|
|
|
#region ================== Constants
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Variables
|
|
|
|
|
|
|
|
// Highlighted item
|
|
|
|
private SlopeVertex highlightedslope;
|
|
|
|
private Sector highlightedsector;
|
|
|
|
private Association[] association = new Association[Thing.NUM_ARGS];
|
|
|
|
private List<SlopeVertexGroup> copyslopevertexgroups;
|
|
|
|
|
|
|
|
private List<ThreeDFloor> threedfloors;
|
|
|
|
bool dragging = false;
|
|
|
|
|
|
|
|
private List<TextLabel> labels;
|
|
|
|
private FlatVertex[] overlaygeometry;
|
|
|
|
private FlatVertex[] overlaytaggedgeometry;
|
|
|
|
private FlatVertex[] selectedsectorgeometry;
|
|
|
|
|
|
|
|
private Vector2D dragstartmappos;
|
|
|
|
private List<Vector2D> oldpositions;
|
|
|
|
|
|
|
|
private bool contextmenuclosing = false;
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Properties
|
|
|
|
|
|
|
|
public Sector HighlightedSector { get { return highlightedsector; } }
|
|
|
|
public bool ContextMenuClosing { get { return contextmenuclosing; } set { contextmenuclosing = value; } }
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Constructor / Disposer
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Methods
|
|
|
|
|
|
|
|
public override void OnHelp()
|
|
|
|
{
|
2019-11-02 13:50:52 +00:00
|
|
|
General.ShowHelp("gzdb/features/classic_modes/mode_slopes.html");
|
2019-10-23 18:55:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Cancel mode
|
|
|
|
public override void OnCancel()
|
|
|
|
{
|
|
|
|
base.OnCancel();
|
|
|
|
|
|
|
|
// Return to previous stable mode
|
|
|
|
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mode engages
|
|
|
|
public override void OnEngage()
|
|
|
|
{
|
|
|
|
base.OnEngage();
|
|
|
|
|
|
|
|
if (BuilderPlug.Me.SlopeDataSector == null || BuilderPlug.Me.SlopeDataSector.IsDisposed)
|
|
|
|
{
|
|
|
|
General.Map.UndoRedo.CreateUndo("Set up slope data sector");
|
|
|
|
|
|
|
|
SlopeDataSectorDialog sdsd = new SlopeDataSectorDialog();
|
|
|
|
DialogResult dr = sdsd.ShowDialog();
|
|
|
|
|
|
|
|
if (dr == DialogResult.Cancel)
|
|
|
|
{
|
|
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
|
|
General.Editing.CancelMode();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dr == DialogResult.OK)
|
|
|
|
{
|
|
|
|
BuilderPlug.Me.SlopeDataSector = General.Map.Map.GetMarkedSectors(true)[0];
|
|
|
|
BuilderPlug.Me.StoreSlopeVertexGroupsInSector();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BuilderPlug.Me.LoadSlopeVertexGroupsFromSector();
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer.SetPresentation(Presentation.Things);
|
|
|
|
|
|
|
|
General.Interface.AddButton(BuilderPlug.Me.MenusForm.UpdateSlopes);
|
|
|
|
|
|
|
|
// Convert geometry selection to sectors
|
|
|
|
General.Map.Map.ConvertSelection(SelectionType.Sectors);
|
|
|
|
|
|
|
|
// Get all 3D floors in the map
|
|
|
|
threedfloors = BuilderPlug.GetThreeDFloors(General.Map.Map.Sectors.ToList());
|
|
|
|
|
|
|
|
SetupLabels();
|
|
|
|
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
svg.FindSectors();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update overlay surfaces, so that selected sectors are drawn correctly
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mode disengages
|
|
|
|
public override void OnDisengage()
|
|
|
|
{
|
|
|
|
base.OnDisengage();
|
|
|
|
|
|
|
|
General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.UpdateSlopes);
|
|
|
|
|
|
|
|
// Hide highlight info
|
|
|
|
General.Interface.HideInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
// This redraws the display
|
|
|
|
public override void OnRedrawDisplay()
|
|
|
|
{
|
|
|
|
renderer.RedrawSurface();
|
|
|
|
|
|
|
|
// Render lines and vertices
|
|
|
|
if(renderer.StartPlotter(true))
|
|
|
|
{
|
|
|
|
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
|
|
|
|
renderer.PlotVerticesSet(General.Map.Map.Vertices);
|
|
|
|
|
|
|
|
foreach (Sector s in General.Map.Map.GetSelectedSectors(true).ToList())
|
|
|
|
renderer.PlotSector(s, General.Colors.Selection);
|
|
|
|
|
|
|
|
if ((highlightedsector != null) && !highlightedsector.IsDisposed)
|
|
|
|
renderer.PlotSector(highlightedsector, General.Colors.Highlight);
|
|
|
|
|
|
|
|
renderer.Finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render things
|
|
|
|
if(renderer.StartThings(true))
|
|
|
|
{
|
|
|
|
renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA);
|
|
|
|
renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f);
|
|
|
|
|
|
|
|
renderer.Finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateOverlay();
|
|
|
|
|
|
|
|
renderer.Present();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetupLabels()
|
|
|
|
{
|
|
|
|
Dictionary<Sector, SectorLabelInfo> sectorlabels = new Dictionary<Sector, SectorLabelInfo>();
|
|
|
|
PixelColor white = new PixelColor(255, 255, 255, 255);
|
|
|
|
|
|
|
|
if (labels != null)
|
|
|
|
{
|
|
|
|
// Dispose old labels
|
|
|
|
foreach (TextLabel l in labels)
|
|
|
|
l.Dispose();
|
|
|
|
|
|
|
|
labels.Clear();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
labels = new List<TextLabel>();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go through all sectors that belong to a SVG and set which SVG their floor and
|
|
|
|
// ceiling belongs to
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
// Process all directly affected sectors
|
|
|
|
foreach (Sector s in svg.Sectors)
|
|
|
|
{
|
|
|
|
if(!sectorlabels.ContainsKey(s))
|
|
|
|
sectorlabels.Add(s, new SectorLabelInfo());
|
|
|
|
|
|
|
|
if ((svg.SectorPlanes[s] & PlaneType.Floor) == PlaneType.Floor)
|
|
|
|
sectorlabels[s].AddSlopeVertexGroup(PlaneType.Floor, svg);
|
|
|
|
|
|
|
|
if ((svg.SectorPlanes[s] & PlaneType.Ceiling) == PlaneType.Ceiling)
|
|
|
|
sectorlabels[s].AddSlopeVertexGroup(PlaneType.Ceiling, svg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process all tagged sectors
|
|
|
|
foreach(Sector s in svg.TaggedSectors)
|
|
|
|
{
|
|
|
|
if (!sectorlabels.ContainsKey(s))
|
|
|
|
sectorlabels.Add(s, new SectorLabelInfo());
|
|
|
|
|
|
|
|
// Bottom and Top are just virtual, the control sector has Floor and Ceiling
|
|
|
|
if ((svg.SectorPlanes[s] & PlaneType.Floor) == PlaneType.Floor)
|
|
|
|
sectorlabels[s].AddSlopeVertexGroup(PlaneType.Bottom, svg);
|
|
|
|
|
|
|
|
if ((svg.SectorPlanes[s] & PlaneType.Ceiling) == PlaneType.Ceiling)
|
|
|
|
sectorlabels[s].AddSlopeVertexGroup(PlaneType.Top, svg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the labels for each sector and add them to the label list
|
|
|
|
if (BuilderPlug.Me.SectorLabelDisplayOption != LabelDisplayOption.Never || General.Interface.AltState == true)
|
|
|
|
{
|
|
|
|
foreach (Sector s in sectorlabels.Keys)
|
|
|
|
{
|
|
|
|
bool showlabel = true;
|
|
|
|
|
|
|
|
if (BuilderPlug.Me.SectorLabelDisplayOption == LabelDisplayOption.WhenHighlighted && General.Interface.AltState == false)
|
|
|
|
{
|
|
|
|
showlabel = false;
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
if ((svg.Sectors.Contains(s) || svg.TaggedSectors.Contains(s)) && svg.Vertices.Contains(highlightedslope))
|
|
|
|
showlabel = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(showlabel)
|
|
|
|
labels.AddRange(sectorlabels[s].CreateLabels(s, highlightedslope, renderer.Scale));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Z position labels for slope vertices
|
|
|
|
if (BuilderPlug.Me.SlopeVertexLabelDisplayOption != LabelDisplayOption.Never || General.Interface.AltState == true)
|
|
|
|
{
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < svg.Vertices.Count; i++)
|
|
|
|
{
|
|
|
|
if (BuilderPlug.Me.SlopeVertexLabelDisplayOption == LabelDisplayOption.Always || General.Interface.AltState == true || svg.Vertices.Contains(highlightedslope))
|
|
|
|
{
|
|
|
|
SlopeVertex sv = svg.Vertices[i];
|
|
|
|
float scale = 1 / renderer.Scale;
|
2020-05-21 12:20:02 +00:00
|
|
|
double x = sv.Pos.x;
|
|
|
|
double y = sv.Pos.y - 14 * scale;
|
2019-10-23 18:55:41 +00:00
|
|
|
string value = String.Format("Z: {0}", sv.Z);
|
|
|
|
bool showlabel = true;
|
|
|
|
|
|
|
|
// Rearrange labels if they'd be (exactly) on each other
|
|
|
|
// TODO: do something like that also for overlapping labels
|
|
|
|
foreach (TextLabel l in labels)
|
|
|
|
{
|
|
|
|
if (l.Location.x == x && l.Location.y == y) {
|
|
|
|
// Reduce visual clutter by de-duping stacked labels, when "show all labels" is enabled
|
|
|
|
if (l.Text == value) {
|
|
|
|
showlabel = false; //dedupe
|
|
|
|
|
|
|
|
// If any of the shared label lines are highlighted/selected, then override the color
|
|
|
|
if (svg.Vertices.Contains(highlightedslope))
|
|
|
|
l.Color = General.Colors.Highlight.WithAlpha(255);
|
|
|
|
else if (sv.Selected)
|
|
|
|
l.Color = General.Colors.Selection.WithAlpha(255);
|
|
|
|
} else {
|
|
|
|
// Adjust the label position down one line
|
|
|
|
y -= l.TextSize.Height * scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only proceed if the label was not deduped
|
|
|
|
if (showlabel)
|
|
|
|
{
|
|
|
|
TextLabel label = new TextLabel();
|
|
|
|
label.TransformCoords = true;
|
|
|
|
label.Location = new Vector2D(x, y);
|
|
|
|
label.AlignX = TextAlignmentX.Center;
|
|
|
|
label.AlignY = TextAlignmentY.Middle;
|
|
|
|
label.BackColor = General.Colors.Background.WithAlpha(128);
|
|
|
|
label.Text = value;
|
|
|
|
|
|
|
|
if (svg.Vertices.Contains(highlightedslope))
|
|
|
|
label.Color = General.Colors.Highlight.WithAlpha(255);
|
|
|
|
else if (sv.Selected)
|
|
|
|
label.Color = General.Colors.Selection.WithAlpha(255);
|
|
|
|
else
|
|
|
|
label.Color = white;
|
|
|
|
|
|
|
|
labels.Add(label);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This updates the overlay
|
|
|
|
private void UpdateOverlay()
|
|
|
|
{
|
|
|
|
float size = 9 / renderer.Scale;
|
|
|
|
|
|
|
|
SetupLabels();
|
|
|
|
|
|
|
|
if (renderer.StartOverlay(true))
|
|
|
|
{
|
|
|
|
if(overlaygeometry != null)
|
|
|
|
renderer.RenderHighlight(overlaygeometry, General.Colors.ModelWireframe.WithAlpha(64).ToInt());
|
|
|
|
|
|
|
|
if (overlaytaggedgeometry != null)
|
|
|
|
renderer.RenderHighlight(overlaytaggedgeometry, General.Colors.Vertices.WithAlpha(64).ToInt());
|
|
|
|
|
|
|
|
if (selectedsectorgeometry != null)
|
|
|
|
renderer.RenderHighlight(selectedsectorgeometry, General.Colors.Selection.WithAlpha(64).ToInt());
|
|
|
|
|
|
|
|
if (BuilderPlug.Me.UseHighlight && highlightedsector != null)
|
|
|
|
{
|
|
|
|
renderer.RenderHighlight(highlightedsector.FlatVertices, General.Colors.Highlight.WithAlpha(64).ToInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
List<SlopeVertex> vertices = new List<SlopeVertex>();
|
|
|
|
List<Line2D> highlightlines = new List<Line2D>();
|
|
|
|
|
|
|
|
// TMP
|
|
|
|
foreach(Line3D l in BuilderPlug.Me.drawlines)
|
|
|
|
{
|
|
|
|
renderer.RenderLine(
|
|
|
|
new Vector2D(l.Start.x, l.Start.z),
|
|
|
|
new Vector2D(l.End.x, l.End.z),
|
|
|
|
1, new PixelColor(255, 255, 255, 255), true
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(Vector3D v in BuilderPlug.Me.drawpoints)
|
|
|
|
{
|
|
|
|
renderer.RenderLine(
|
|
|
|
new Vector2D(v.x, v.z+2),
|
|
|
|
new Vector2D(v.x, v.z-2),
|
|
|
|
1, new PixelColor(255, 255, 0, 0), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store all slope vertices and draw the lines between them. If the lines connect highlighted slope vertices
|
|
|
|
// draw them later, so they are on top
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
bool highlighted = svg.Vertices.Where(o => o == highlightedslope).Count() > 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < svg.Vertices.Count; i++)
|
|
|
|
{
|
|
|
|
vertices.Add(svg.Vertices[i]);
|
|
|
|
|
|
|
|
if (i < svg.Vertices.Count - 1)
|
|
|
|
{
|
|
|
|
if (highlighted)
|
|
|
|
highlightlines.Add(new Line2D(svg.Vertices[0].Pos, svg.Vertices[i + 1].Pos));
|
|
|
|
else
|
|
|
|
renderer.RenderLine(svg.Vertices[0].Pos, svg.Vertices[i + 1].Pos, 1, new PixelColor(255, 255, 255, 255), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw highlighted lines
|
|
|
|
foreach (Line2D line in highlightlines)
|
|
|
|
renderer.RenderLine(line.v1, line.v2, 1, General.Colors.Highlight, true);
|
|
|
|
|
|
|
|
// Sort the slope vertex list and draw them. The sorting ensures that selected vertices are always drawn on top
|
|
|
|
foreach(SlopeVertex sv in vertices.OrderBy(o=>o.Selected))
|
|
|
|
{
|
|
|
|
PixelColor c = General.Colors.Indication;
|
|
|
|
Vector3D v = sv.Pos;
|
|
|
|
|
|
|
|
if (sv.Selected)
|
|
|
|
c = General.Colors.Selection;
|
|
|
|
|
2020-05-21 12:20:02 +00:00
|
|
|
renderer.RenderRectangleFilled(new RectangleF((float)(v.x - size / 2), (float)(v.y - size / 2), size, size), General.Colors.Background, true);
|
|
|
|
renderer.RenderRectangle(new RectangleF((float)(v.x - size / 2), (float)(v.y - size / 2), size, size), 2, c, true);
|
2019-10-23 18:55:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw highlighted slope vertex
|
|
|
|
if (highlightedslope != null)
|
|
|
|
{
|
2020-05-21 12:20:02 +00:00
|
|
|
renderer.RenderRectangleFilled(new RectangleF((float)(highlightedslope.Pos.x - size / 2), (float)(highlightedslope.Pos.y - size / 2), size, size), General.Colors.Background, true);
|
|
|
|
renderer.RenderRectangle(new RectangleF((float)(highlightedslope.Pos.x - size / 2), (float)(highlightedslope.Pos.y - size / 2), size, size), 2, General.Colors.Highlight, true);
|
2019-10-23 18:55:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach (TextLabel l in labels)
|
|
|
|
renderer.RenderText(l);
|
|
|
|
|
|
|
|
if (selecting)
|
|
|
|
RenderMultiSelection();
|
|
|
|
|
|
|
|
renderer.Finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updateOverlaySurfaces()
|
|
|
|
{
|
|
|
|
string[] fieldnames = new string[] { "user_floorplane_id", "user_ceilingplane_id" };
|
|
|
|
ICollection<Sector> orderedselection = General.Map.Map.GetSelectedSectors(true);
|
|
|
|
List<FlatVertex> vertslist = new List<FlatVertex>();
|
|
|
|
List<Sector> highlightedsectors = new List<Sector>();
|
|
|
|
List<Sector> highlightedtaggedsectors = new List<Sector>();
|
|
|
|
|
|
|
|
// Highlighted slope
|
|
|
|
if (highlightedslope != null)
|
|
|
|
{
|
|
|
|
SlopeVertexGroup svg = BuilderPlug.Me.GetSlopeVertexGroup(highlightedslope);
|
|
|
|
|
|
|
|
// All sectors the slope applies to
|
|
|
|
foreach (Sector s in svg.Sectors)
|
|
|
|
{
|
|
|
|
if (s != null && !s.IsDisposed)
|
|
|
|
{
|
|
|
|
vertslist.AddRange(s.FlatVertices);
|
|
|
|
highlightedsectors.Add(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
overlaygeometry = vertslist.ToArray();
|
|
|
|
|
|
|
|
// All sectors that are tagged because of 3D floors
|
|
|
|
vertslist = new List<FlatVertex>();
|
|
|
|
|
|
|
|
foreach (Sector s in svg.TaggedSectors)
|
|
|
|
{
|
|
|
|
if (s != null && !s.IsDisposed)
|
|
|
|
{
|
|
|
|
vertslist.AddRange(s.FlatVertices);
|
|
|
|
highlightedtaggedsectors.Add(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
overlaytaggedgeometry = vertslist.ToArray();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
overlaygeometry = new FlatVertex[0];
|
|
|
|
overlaytaggedgeometry = new FlatVertex[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Selected sectors
|
|
|
|
vertslist = new List<FlatVertex>();
|
|
|
|
|
|
|
|
foreach (Sector s in orderedselection)
|
|
|
|
if(!highlightedsectors.Contains(s))
|
|
|
|
vertslist.AddRange(s.FlatVertices);
|
|
|
|
|
|
|
|
selectedsectorgeometry = vertslist.ToArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
// This highlights a new item
|
|
|
|
protected void HighlightSector(Sector s)
|
|
|
|
{
|
|
|
|
// Update display
|
|
|
|
|
|
|
|
highlightedsector = s;
|
|
|
|
/*
|
|
|
|
if (renderer.StartPlotter(false))
|
|
|
|
{
|
|
|
|
// Undraw previous highlight
|
|
|
|
if ((highlightedsector != null) && !highlightedsector.IsDisposed)
|
|
|
|
renderer.PlotSector(highlightedsector);
|
|
|
|
|
|
|
|
// Set new highlight
|
|
|
|
highlightedsector = s;
|
|
|
|
|
|
|
|
// Render highlighted item
|
|
|
|
if ((highlightedsector != null) && !highlightedsector.IsDisposed)
|
|
|
|
renderer.PlotSector(highlightedsector, General.Colors.Highlight);
|
|
|
|
|
|
|
|
// Done
|
|
|
|
renderer.Finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateOverlay();
|
|
|
|
renderer.Present();
|
|
|
|
*/
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
|
|
|
|
// Show highlight info
|
|
|
|
if ((highlightedsector != null) && !highlightedsector.IsDisposed)
|
|
|
|
General.Interface.ShowSectorInfo(highlightedsector);
|
|
|
|
else
|
|
|
|
General.Interface.HideInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
// This selectes or deselects a sector
|
|
|
|
protected void SelectSector(Sector s, bool selectstate)
|
|
|
|
{
|
|
|
|
bool selectionchanged = false;
|
|
|
|
|
|
|
|
if (!s.IsDisposed)
|
|
|
|
{
|
|
|
|
// Select the sector?
|
|
|
|
if (selectstate && !s.Selected)
|
|
|
|
{
|
|
|
|
s.Selected = true;
|
|
|
|
selectionchanged = true;
|
|
|
|
}
|
|
|
|
// Deselect the sector?
|
|
|
|
else if (!selectstate && s.Selected)
|
|
|
|
{
|
|
|
|
s.Selected = false;
|
|
|
|
selectionchanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Selection changed?
|
|
|
|
if (selectionchanged)
|
|
|
|
{
|
|
|
|
// Make update lines selection
|
|
|
|
foreach (Sidedef sd in s.Sidedefs)
|
|
|
|
{
|
|
|
|
bool front, back;
|
|
|
|
if (sd.Line.Front != null) front = sd.Line.Front.Sector.Selected; else front = false;
|
|
|
|
if (sd.Line.Back != null) back = sd.Line.Back.Sector.Selected; else back = false;
|
|
|
|
sd.Line.Selected = front | back;
|
|
|
|
}
|
|
|
|
|
|
|
|
//mxd. Also (de)select things?
|
|
|
|
if (General.Interface.AltState)
|
|
|
|
{
|
|
|
|
foreach (Thing t in General.Map.ThingsFilter.VisibleThings)
|
|
|
|
{
|
|
|
|
t.DetermineSector();
|
|
|
|
if (t.Sector != s) continue;
|
|
|
|
t.Selected = s.Selected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void ResetHighlightedSector()
|
|
|
|
{
|
|
|
|
HighlightSector(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Selection
|
|
|
|
protected override void OnSelectBegin()
|
|
|
|
{
|
|
|
|
// Item highlighted?
|
|
|
|
if(highlightedslope != null)
|
|
|
|
{
|
|
|
|
// Flip selection
|
|
|
|
highlightedslope.Selected = !highlightedslope.Selected;
|
|
|
|
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
}
|
|
|
|
|
|
|
|
base.OnSelectBegin();
|
|
|
|
}
|
|
|
|
|
|
|
|
// End selection
|
|
|
|
protected override void OnSelectEnd()
|
|
|
|
{
|
|
|
|
// Not ending from a multi-selection?
|
|
|
|
if(!selecting)
|
|
|
|
{
|
|
|
|
// Item highlighted?
|
|
|
|
if (highlightedslope != null)
|
|
|
|
{
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (highlightedsector != null)
|
|
|
|
{
|
|
|
|
if (!contextmenuclosing)
|
|
|
|
{
|
|
|
|
SelectSector(highlightedsector, !highlightedsector.Selected);
|
|
|
|
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
contextmenuclosing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
base.OnSelectEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done editing
|
|
|
|
protected override void OnEditEnd()
|
|
|
|
{
|
|
|
|
base.OnEditEnd();
|
|
|
|
|
|
|
|
if (dragging) return;
|
|
|
|
|
|
|
|
if (highlightedslope != null)
|
|
|
|
{
|
|
|
|
SlopeVertex sv = highlightedslope;
|
|
|
|
|
|
|
|
List<SlopeVertex> vertices = GetSelectedSlopeVertices();
|
|
|
|
|
|
|
|
if (!vertices.Contains(highlightedslope))
|
|
|
|
vertices.Add(highlightedslope);
|
|
|
|
|
|
|
|
SlopeVertexEditForm svef = new SlopeVertexEditForm();
|
|
|
|
svef.Setup(vertices);
|
|
|
|
|
|
|
|
DialogResult result = svef.ShowDialog((Form)General.Interface);
|
|
|
|
|
|
|
|
if (result == DialogResult.OK)
|
|
|
|
{
|
|
|
|
General.Map.IsChanged = true;
|
|
|
|
|
|
|
|
BuilderPlug.Me.UpdateSlopes();
|
|
|
|
}
|
|
|
|
|
|
|
|
highlightedslope = null;
|
|
|
|
}
|
|
|
|
else if(highlightedsector != null)
|
|
|
|
{
|
|
|
|
if (General.Map.Map.SelectedSectorsCount == 0)
|
|
|
|
{
|
|
|
|
BuilderPlug.Me.MenusForm.AddSectorsContextMenu.Tag = new List<Sector>() { highlightedsector };
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BuilderPlug.Me.MenusForm.AddSectorsContextMenu.Tag = General.Map.Map.GetSelectedSectors(true).ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
BuilderPlug.Me.MenusForm.AddSectorsContextMenu.Show(Cursor.Position);
|
|
|
|
}
|
|
|
|
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
|
|
|
|
//Build a list of the closest svs, that share the same distance away from the mouse cursor
|
|
|
|
private List<SlopeVertex> GetVertexStack()
|
|
|
|
{
|
|
|
|
List<SlopeVertex> stack = new List<SlopeVertex>();
|
2020-05-21 12:20:02 +00:00
|
|
|
double d, last = double.MaxValue;
|
2019-10-23 18:55:41 +00:00
|
|
|
|
|
|
|
foreach(SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups) {
|
|
|
|
foreach(SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
d = Vector2D.Distance(sv.Pos, mousemappos);
|
|
|
|
if (d <= BuilderModes.BuilderPlug.Me.HighlightRange / renderer.Scale) {
|
|
|
|
if (d > last)
|
|
|
|
continue; //discard
|
|
|
|
else if (d < last)
|
|
|
|
stack.Clear();
|
|
|
|
|
|
|
|
stack.Add(sv);
|
|
|
|
last = d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stack;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse moves
|
|
|
|
public override void OnMouseMove(MouseEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnMouseMove(e);
|
|
|
|
|
|
|
|
if (selectpressed && !editpressed && !selecting)
|
|
|
|
{
|
|
|
|
// Check if moved enough pixels for multiselect
|
|
|
|
Vector2D delta = mousedownpos - mousepos;
|
|
|
|
if ((Math.Abs(delta.x) > 2) || (Math.Abs(delta.y) > 2))
|
|
|
|
{
|
|
|
|
// Start multiselecting
|
|
|
|
StartMultiSelection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(e.Button == MouseButtons.None)
|
|
|
|
{
|
|
|
|
SlopeVertex oldhighlight = highlightedslope;
|
|
|
|
Sector oldhighlightedsector = highlightedsector;
|
|
|
|
|
|
|
|
//select the closest handle within grabbing distance
|
|
|
|
List<SlopeVertex> stack = GetVertexStack();
|
|
|
|
if (stack.Count > 0) {
|
|
|
|
SlopeVertex sv = stack[0];
|
|
|
|
if (sv != highlightedslope)
|
|
|
|
{
|
|
|
|
//if the "closest" handle is the same distance away as the already highlighted handle, then do nothing
|
|
|
|
if (highlightedslope == null || (Vector2D.Distance(sv.Pos, mousemappos) != Vector2D.Distance(highlightedslope.Pos, mousemappos))) {
|
|
|
|
highlightedslope = sv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//nothing within distance, so reset the highlight
|
|
|
|
highlightedslope = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no slope vertex is highlighted, check if a sector should be
|
|
|
|
if (highlightedslope == null)
|
|
|
|
{
|
|
|
|
// Find the nearest linedef within highlight range
|
|
|
|
Linedef l = General.Map.Map.NearestLinedef(mousemappos);
|
|
|
|
if (l != null)
|
|
|
|
{
|
|
|
|
// Check on which side of the linedef the mouse is
|
2020-05-21 12:20:02 +00:00
|
|
|
double side = l.SideOfLine(mousemappos);
|
2019-10-23 18:55:41 +00:00
|
|
|
if (side > 0)
|
|
|
|
{
|
|
|
|
// Is there a sidedef here?
|
|
|
|
if (l.Back != null)
|
|
|
|
{
|
|
|
|
// Highlight if not the same
|
|
|
|
if (l.Back.Sector != highlightedsector) HighlightSector(l.Back.Sector);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Highlight nothing
|
|
|
|
if (highlightedsector != null) HighlightSector(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Is there a sidedef here?
|
|
|
|
if (l.Front != null)
|
|
|
|
{
|
|
|
|
// Highlight if not the same
|
|
|
|
if (l.Front.Sector != highlightedsector) HighlightSector(l.Front.Sector);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Highlight nothing
|
|
|
|
if (highlightedsector != null) HighlightSector(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HighlightSector(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (highlightedslope != oldhighlight)
|
|
|
|
{
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (dragging && highlightedslope != null)
|
|
|
|
{
|
|
|
|
Vector2D newpos = SnapToNearest(mousemappos);
|
|
|
|
Vector2D offset = highlightedslope.Pos - newpos;
|
|
|
|
|
|
|
|
foreach (SlopeVertex sl in GetSelectedSlopeVertices())
|
|
|
|
sl.Pos -= offset;
|
|
|
|
|
|
|
|
highlightedslope.Pos = newpos;
|
|
|
|
|
|
|
|
General.Map.IsChanged = true;
|
|
|
|
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
else if (selecting)
|
|
|
|
{
|
|
|
|
UpdateOverlay();
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse leaves
|
|
|
|
public override void OnMouseLeave(EventArgs e)
|
|
|
|
{
|
|
|
|
base.OnMouseLeave(e);
|
|
|
|
|
|
|
|
// Highlight nothing
|
|
|
|
highlightedslope = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void OnKeyDown(KeyEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnKeyDown(e);
|
|
|
|
|
|
|
|
if (e.Alt)
|
|
|
|
{
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void OnKeyUp(KeyEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnKeyUp(e);
|
|
|
|
|
|
|
|
if (!e.Alt)
|
|
|
|
{
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse wants to drag
|
|
|
|
protected override void OnDragStart(MouseEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnDragStart(e);
|
|
|
|
|
|
|
|
if (e.Button == MouseButtons.Right)
|
|
|
|
{
|
|
|
|
dragging = true;
|
|
|
|
dragstartmappos = mousemappos;
|
|
|
|
|
|
|
|
oldpositions = new List<Vector2D>();
|
|
|
|
|
|
|
|
foreach(SlopeVertex sl in GetSelectedSlopeVertices())
|
|
|
|
if(sl.Selected)
|
|
|
|
oldpositions.Add(sl.Pos);
|
|
|
|
|
|
|
|
|
|
|
|
if(highlightedslope != null)
|
|
|
|
oldpositions.Add(highlightedslope.Pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//retrieves the current mouse position on the grid, snapped as necessary
|
|
|
|
private Vector2D SnapToNearest(Vector2D vm)
|
|
|
|
{
|
2020-05-21 12:20:02 +00:00
|
|
|
double vrange = 20f / renderer.Scale;
|
2019-10-23 18:55:41 +00:00
|
|
|
bool snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; //allow temporary disable of snap by holding shift
|
|
|
|
|
|
|
|
if (General.Interface.AutoMerge) //only snap to geometry if the option is enabled
|
|
|
|
{
|
|
|
|
// Try the nearest slope vertex
|
|
|
|
SlopeVertex nh = NearestSlopeVertexSquareRange(vm, vrange);
|
|
|
|
if (nh != null)
|
|
|
|
return nh.Pos;
|
|
|
|
|
|
|
|
// Try the nearest map vertex
|
|
|
|
Vertex nv = General.Map.Map.NearestVertexSquareRange(vm, vrange);
|
|
|
|
if (nv != null)
|
|
|
|
return nv.Position;
|
|
|
|
|
|
|
|
// Try the nearest linedef
|
|
|
|
Linedef nl = General.Map.Map.NearestLinedefRange(vm, vrange);
|
|
|
|
if (nl != null)
|
|
|
|
{
|
|
|
|
// Snap to grid?
|
|
|
|
if (snaptogrid)
|
|
|
|
{
|
|
|
|
// Get grid intersection coordinates
|
|
|
|
List<Vector2D> coords = nl.GetGridIntersections();
|
|
|
|
|
|
|
|
// Find nearest grid intersection
|
|
|
|
bool found = false;
|
2020-05-21 12:20:02 +00:00
|
|
|
double found_distance = float.MaxValue;
|
2019-10-23 18:55:41 +00:00
|
|
|
Vector2D found_coord = new Vector2D();
|
|
|
|
foreach (Vector2D v in coords)
|
|
|
|
{
|
|
|
|
Vector2D delta = vm - v;
|
|
|
|
if (delta.GetLengthSq() < found_distance)
|
|
|
|
{
|
|
|
|
found_distance = delta.GetLengthSq();
|
|
|
|
found_coord = v;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
return found_coord;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return nl.NearestOnLine(vm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Just get the current mouse location instead
|
|
|
|
if (snaptogrid)
|
|
|
|
return General.Map.Grid.SnappedToGrid(vm);
|
|
|
|
return vm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>This finds the thing closest to the specified position.</summary>
|
2020-05-21 12:20:02 +00:00
|
|
|
public SlopeVertex NearestSlopeVertexSquareRange(Vector2D pos, double maxrange)
|
2019-10-23 18:55:41 +00:00
|
|
|
{
|
|
|
|
List<SlopeVertex> verts = GetUnSelectedSlopeVertices();
|
|
|
|
if (highlightedslope != null)
|
|
|
|
verts.Remove(highlightedslope);
|
|
|
|
|
|
|
|
return NearestSlopeVertexSquareRange(verts, pos, maxrange);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>This finds the slope vertex closest to the specified position.</summary>
|
2020-05-21 12:20:02 +00:00
|
|
|
public SlopeVertex NearestSlopeVertexSquareRange(ICollection<SlopeVertex> selection, Vector2D pos, double maxrange)
|
2019-10-23 18:55:41 +00:00
|
|
|
{
|
2020-05-21 12:20:02 +00:00
|
|
|
RectangleF range = RectangleF.FromLTRB((float)(pos.x - maxrange), (float)(pos.y - maxrange), (float)(pos.x + maxrange), (float)(pos.y + maxrange));
|
2019-10-23 18:55:41 +00:00
|
|
|
SlopeVertex closest = null;
|
2020-05-21 12:20:02 +00:00
|
|
|
double distance = double.MaxValue;
|
2019-10-23 18:55:41 +00:00
|
|
|
|
|
|
|
// Go for all vertices in selection
|
|
|
|
foreach (SlopeVertex v in selection)
|
|
|
|
{
|
2020-05-21 12:20:02 +00:00
|
|
|
double px = v.Pos.x;
|
|
|
|
double py = v.Pos.y;
|
2019-10-23 18:55:41 +00:00
|
|
|
|
|
|
|
//mxd. Within range?
|
|
|
|
if ((v.Pos.x < range.Left) || (v.Pos.x > range.Right)
|
|
|
|
|| (v.Pos.y < range.Top) || (v.Pos.y > range.Bottom))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Close than previous find?
|
2020-05-21 12:20:02 +00:00
|
|
|
double d = Math.Abs(px - pos.x) + Math.Abs(py - pos.y);
|
2019-10-23 18:55:41 +00:00
|
|
|
if (d < distance)
|
|
|
|
{
|
|
|
|
// This one is closer
|
|
|
|
closest = v;
|
|
|
|
distance = d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return result
|
|
|
|
return closest;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse wants to drag
|
|
|
|
protected override void OnDragStop(MouseEventArgs e)
|
|
|
|
{
|
|
|
|
base.OnDragStop(e);
|
|
|
|
|
|
|
|
General.Map.UndoRedo.CreateUndo("Drag slope vertex");
|
|
|
|
|
|
|
|
BuilderPlug.Me.StoreSlopeVertexGroupsInSector();
|
|
|
|
General.Map.Map.Update();
|
|
|
|
|
|
|
|
BuilderPlug.Me.UpdateSlopes();
|
|
|
|
|
|
|
|
dragging = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This is called wheh selection ends
|
|
|
|
protected override void OnEndMultiSelection()
|
|
|
|
{
|
|
|
|
bool selectionvolume = ((Math.Abs(base.selectionrect.Width) > 0.1f) && (Math.Abs(base.selectionrect.Height) > 0.1f));
|
|
|
|
|
|
|
|
if(BuilderPlug.Me.AutoClearSelection && !selectionvolume)
|
|
|
|
General.Map.Map.ClearSelectedThings();
|
|
|
|
|
|
|
|
if(selectionvolume)
|
|
|
|
{
|
|
|
|
if(General.Interface.ShiftState ^ BuilderPlug.Me.AdditiveSelect)
|
|
|
|
{
|
|
|
|
// Go for all slope vertices
|
|
|
|
foreach (SlopeVertex sl in GetAllSlopeVertices())
|
|
|
|
{
|
|
|
|
sl.Selected |= ((sl.Pos.x >= selectionrect.Left) &&
|
|
|
|
(sl.Pos.y >= selectionrect.Top) &&
|
|
|
|
(sl.Pos.x <= selectionrect.Right) &&
|
|
|
|
(sl.Pos.y <= selectionrect.Bottom));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Go for all slope vertices
|
|
|
|
foreach (SlopeVertex sl in GetAllSlopeVertices())
|
|
|
|
{
|
|
|
|
sl.Selected |= ((sl.Pos.x >= selectionrect.Left) &&
|
|
|
|
(sl.Pos.y >= selectionrect.Top) &&
|
|
|
|
(sl.Pos.x <= selectionrect.Right) &&
|
|
|
|
(sl.Pos.y <= selectionrect.Bottom));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
base.OnEndMultiSelection();
|
|
|
|
|
|
|
|
// Clear overlay
|
|
|
|
if(renderer.StartOverlay(true)) renderer.Finish();
|
|
|
|
|
|
|
|
// Redraw
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is called when the selection is updated
|
|
|
|
protected override void OnUpdateMultiSelection()
|
|
|
|
{
|
|
|
|
base.OnUpdateMultiSelection();
|
|
|
|
|
|
|
|
UpdateOverlay();
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnCopyBegin()
|
|
|
|
{
|
|
|
|
copyslopevertexgroups = new List<SlopeVertexGroup>();
|
|
|
|
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
bool copy = false;
|
|
|
|
|
|
|
|
// Check if the current SVG has to be copied
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
if (sv.Selected)
|
|
|
|
{
|
|
|
|
copy = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy)
|
|
|
|
{
|
|
|
|
List<SlopeVertex> newsv = new List<SlopeVertex>();
|
|
|
|
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
newsv.Add(new SlopeVertex(sv.Pos, sv.Z));
|
|
|
|
|
|
|
|
// Use -1 for id, since a real id will be assigned when pasting
|
|
|
|
copyslopevertexgroups.Add(new SlopeVertexGroup(-1, newsv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnPasteBegin(PasteOptions options)
|
|
|
|
{
|
|
|
|
if (copyslopevertexgroups == null || copyslopevertexgroups.Count == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Unselect all slope vertices, so the pasted vertices can be selected
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
svg.SelectVertices(false);
|
|
|
|
|
2020-05-21 12:20:02 +00:00
|
|
|
double l = copyslopevertexgroups[0].Vertices[0].Pos.x;
|
|
|
|
double r = copyslopevertexgroups[0].Vertices[0].Pos.x;
|
|
|
|
double t = copyslopevertexgroups[0].Vertices[0].Pos.y;
|
|
|
|
double b = copyslopevertexgroups[0].Vertices[0].Pos.y;
|
2019-10-23 18:55:41 +00:00
|
|
|
|
|
|
|
// Find the outer dimensions of all SVGs to paste
|
|
|
|
foreach (SlopeVertexGroup svg in copyslopevertexgroups)
|
|
|
|
{
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
if (sv.Pos.x < l) l = sv.Pos.x;
|
|
|
|
if (sv.Pos.x > r) r = sv.Pos.x;
|
|
|
|
if (sv.Pos.y > t) t = sv.Pos.y;
|
|
|
|
if (sv.Pos.y < b) b = sv.Pos.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector2D center = new Vector2D(l + ((r - l) / 2), b + ((t - b) / 2));
|
|
|
|
Vector2D diff = center - General.Map.Grid.SnappedToGrid(mousemappos);
|
|
|
|
|
|
|
|
foreach (SlopeVertexGroup svg in copyslopevertexgroups)
|
|
|
|
{
|
|
|
|
int id;
|
|
|
|
List<SlopeVertex> newsv = new List<SlopeVertex>();
|
|
|
|
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
newsv.Add(new SlopeVertex(new Vector2D(sv.Pos.x - diff.x, sv.Pos.y - diff.y), sv.Z));
|
|
|
|
}
|
|
|
|
|
|
|
|
SlopeVertexGroup newsvg = BuilderPlug.Me.AddSlopeVertexGroup(newsv, out id);
|
|
|
|
newsvg.SelectVertices(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Redraw the display, so that pasted SVGs are shown immediately
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
|
|
|
|
// Don't go into the standard process for pasting, so tell the core that
|
|
|
|
// pasting should not proceed
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<SlopeVertex> GetSelectedSlopeVertices()
|
|
|
|
{
|
|
|
|
List<SlopeVertex> selected = new List<SlopeVertex>();
|
|
|
|
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
if (sv.Selected)
|
|
|
|
selected.Add(sv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return selected;
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<SlopeVertex> GetUnSelectedSlopeVertices()
|
|
|
|
{
|
|
|
|
List<SlopeVertex> notselected = new List<SlopeVertex>();
|
|
|
|
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
if (!sv.Selected)
|
|
|
|
notselected.Add(sv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return notselected;
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<SlopeVertex> GetAllSlopeVertices()
|
|
|
|
{
|
|
|
|
List<SlopeVertex> selected = new List<SlopeVertex>();
|
|
|
|
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
selected.Add(sv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return selected;
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<SlopeVertexGroup> GetSelectedSlopeVertexGroups()
|
|
|
|
{
|
|
|
|
List<SlopeVertexGroup> svgs = new List<SlopeVertexGroup>();
|
|
|
|
|
|
|
|
foreach (SlopeVertex sv in GetSelectedSlopeVertices())
|
|
|
|
{
|
|
|
|
SlopeVertexGroup svg = BuilderPlug.Me.GetSlopeVertexGroup(sv);
|
|
|
|
|
|
|
|
if (!svgs.Contains(svg))
|
|
|
|
svgs.Add(svg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return svgs;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ================== Actions
|
|
|
|
|
|
|
|
[BeginAction("drawfloorslope")]
|
|
|
|
public void DrawFloorSlope()
|
|
|
|
{
|
|
|
|
BuilderPlug.Me.MenusForm.CeilingSlope.Checked = false;
|
|
|
|
BuilderPlug.Me.MenusForm.FloorSlope.Checked = true;
|
|
|
|
BuilderPlug.Me.MenusForm.FloorAndCeilingSlope.Checked = false;
|
|
|
|
|
|
|
|
General.Interface.DisplayStatus(StatusType.Info, "Applying drawn slope to floor");
|
|
|
|
}
|
|
|
|
|
|
|
|
[BeginAction("drawceilingslope")]
|
|
|
|
public void DrawCeilingSlope()
|
|
|
|
{
|
|
|
|
BuilderPlug.Me.MenusForm.CeilingSlope.Checked = true;
|
|
|
|
BuilderPlug.Me.MenusForm.FloorSlope.Checked = false;
|
|
|
|
BuilderPlug.Me.MenusForm.FloorAndCeilingSlope.Checked = false;
|
|
|
|
|
|
|
|
General.Interface.DisplayStatus(StatusType.Info, "Applying drawn slope to ceiling");
|
|
|
|
}
|
|
|
|
|
|
|
|
[BeginAction("drawfloorandceilingslope")]
|
|
|
|
public void DrawFloorAndCeilingSlope()
|
|
|
|
{
|
|
|
|
BuilderPlug.Me.MenusForm.CeilingSlope.Checked = false;
|
|
|
|
BuilderPlug.Me.MenusForm.FloorSlope.Checked = false;
|
|
|
|
BuilderPlug.Me.MenusForm.FloorAndCeilingSlope.Checked = true;
|
|
|
|
|
|
|
|
General.Interface.DisplayStatus(StatusType.Info, "Applying drawn slope to floor and ceiling");
|
|
|
|
}
|
|
|
|
|
|
|
|
[BeginAction("threedflipslope")]
|
|
|
|
public void FlipSlope()
|
|
|
|
{
|
|
|
|
if (highlightedslope == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
MessageBox.Show("Flipping temporarily removed");
|
|
|
|
|
|
|
|
/*
|
|
|
|
if (highlightedslope.IsOrigin)
|
|
|
|
{
|
|
|
|
origin = highlightedslope.ThreeDFloor.Slope.Origin + highlightedslope.ThreeDFloor.Slope.Direction;
|
|
|
|
direction = highlightedslope.ThreeDFloor.Slope.Direction * (-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
origin = highlightedslope.ThreeDFloor.Slope.Origin + highlightedslope.ThreeDFloor.Slope.Direction;
|
|
|
|
direction = highlightedslope.ThreeDFloor.Slope.Direction * (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
highlightedslope.ThreeDFloor.Slope.Origin = origin;
|
|
|
|
highlightedslope.ThreeDFloor.Slope.Direction = direction;
|
|
|
|
|
|
|
|
highlightedslope.ThreeDFloor.Rebuild = true;
|
|
|
|
|
|
|
|
BuilderPlug.ProcessThreeDFloors(new List<ThreeDFloor> { highlightedslope.ThreeDFloor }, highlightedslope.ThreeDFloor.TaggedSectors);
|
|
|
|
|
|
|
|
UpdateSlopeObjects();
|
|
|
|
|
|
|
|
// Redraw
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
// This clears the selection
|
|
|
|
[BeginAction("clearselection", BaseAction = true)]
|
|
|
|
public void ClearSelection()
|
|
|
|
{
|
|
|
|
int numselected = 0;
|
|
|
|
// Clear selection
|
|
|
|
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
|
|
|
|
{
|
|
|
|
foreach (SlopeVertex sv in svg.Vertices)
|
|
|
|
{
|
|
|
|
if (sv.Selected)
|
|
|
|
{
|
|
|
|
sv.Selected = false;
|
|
|
|
numselected++;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear selected sectors when no SVGs are selected
|
|
|
|
if (numselected == 0)
|
|
|
|
General.Map.Map.ClearAllSelected();
|
|
|
|
|
|
|
|
// Redraw
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[BeginAction("deleteitem", BaseAction = true)]
|
|
|
|
public void DeleteItem()
|
|
|
|
{
|
|
|
|
// Make list of selected things
|
|
|
|
List<SlopeVertex> selected = new List<SlopeVertex>(GetSelectedSlopeVertices());
|
|
|
|
|
|
|
|
if(highlightedslope != null)
|
|
|
|
{
|
|
|
|
selected.Add(highlightedslope);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Anything to do?
|
|
|
|
if(selected.Count > 0)
|
|
|
|
{
|
|
|
|
List<SlopeVertexGroup> groups = new List<SlopeVertexGroup>();
|
|
|
|
|
|
|
|
General.Map.UndoRedo.CreateUndo("Delete slope");
|
|
|
|
|
|
|
|
foreach (SlopeVertex sv in selected)
|
|
|
|
{
|
|
|
|
SlopeVertexGroup svg = BuilderPlug.Me.GetSlopeVertexGroup(sv);
|
|
|
|
|
|
|
|
if (!groups.Contains(svg))
|
|
|
|
groups.Add(svg);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (SlopeVertexGroup svg in groups)
|
|
|
|
{
|
|
|
|
svg.RemovePlanes();
|
|
|
|
svg.RemoveUndoRedoUDMFFields(BuilderPlug.Me.SlopeDataSector);
|
|
|
|
|
|
|
|
BuilderPlug.Me.SlopeVertexGroups.Remove(svg);
|
|
|
|
}
|
|
|
|
|
|
|
|
General.Map.IsChanged = true;
|
|
|
|
|
|
|
|
// Invoke a new mousemove so that the highlighted item updates
|
|
|
|
MouseEventArgs e = new MouseEventArgs(MouseButtons.None, 0, (int)mousepos.x, (int)mousepos.y, 0);
|
|
|
|
OnMouseMove(e);
|
|
|
|
|
|
|
|
// Redraw screen
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[BeginAction("cyclehighlighted3dfloorup")]
|
|
|
|
public void CycleHighlighted3DFloorUp()
|
|
|
|
{
|
|
|
|
if (highlightedslope == null)
|
|
|
|
return;
|
|
|
|
List<SlopeVertex> stack = GetVertexStack();
|
|
|
|
if (stack.Count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int idx = stack.IndexOf(highlightedslope) + 1;
|
|
|
|
if (idx >= stack.Count)
|
|
|
|
idx = 0;
|
|
|
|
highlightedslope = stack[idx];
|
|
|
|
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
|
|
|
|
[BeginAction("cyclehighlighted3dfloordown")]
|
|
|
|
public void CycleHighlighted3DFloorDown()
|
|
|
|
{
|
|
|
|
if (highlightedslope == null)
|
|
|
|
return;
|
|
|
|
List<SlopeVertex> stack = GetVertexStack();
|
|
|
|
if (stack.Count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int idx = stack.IndexOf(highlightedslope) - 1;
|
|
|
|
if (idx < 0)
|
|
|
|
idx = stack.Count - 1;
|
|
|
|
highlightedslope = stack[idx];
|
|
|
|
|
|
|
|
updateOverlaySurfaces();
|
|
|
|
UpdateOverlay();
|
|
|
|
General.Interface.RedrawDisplay();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|