UltimateZoneBuilder/Source/Plugins/3DFloorMode/DrawSlopesMode.cs
2024-05-28 14:46:01 +02:00

895 lines
26 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 System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Linq;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.Geometry;
using System.Drawing;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Actions;
#endregion
namespace CodeImp.DoomBuilder.ThreeDFloorMode
{
enum SlopeDrawingMode { Floor, Ceiling, FloorAndCeiling };
[EditMode(DisplayName = "Draw Slopes Mode",
SwitchAction = "drawslopesmode",
ButtonImage = "DrawSlopeModeIcon.png", // Image resource name for the button
ButtonOrder = int.MinValue + 501, // Position of the button (lower is more to the left)
ButtonGroup = "000_editing",
AllowCopyPaste = false,
SupportedMapFormats = new[] { "UniversalMapSetIO" },
RequiredMapFeatures = new[] { "PlaneEquationSupport" },
Volatile = true,
UseByDefault = false,
Optional = true,
IsDeprecated = true,
DeprecationMessage = "Please use the visual sloping functionality instead.")]
public class DrawSlopesMode : ClassicMode
{
#region ================== Constants
private const float LINE_THICKNESS = 0.8f;
#endregion
#region ================== Variables
// Drawing points
private List<DrawnVertex> points;
// Keep track of view changes
private float lastoffsetx;
private float lastoffsety;
private float lastscale;
// Options
private bool snaptogrid; // SHIFT to toggle
private bool snaptonearest; // CTRL to enable
private FlatVertex[] overlayGeometry;
private List<TextLabel> labels;
private static SlopeDrawingMode slopedrawingmode = SlopeDrawingMode.Floor;
#endregion
#region ================== Properties
// Just keep the base mode button checked
public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public DrawSlopesMode()
{
// Initialize
points = new List<DrawnVertex>();
// No selection in this mode
//General.Map.Map.ClearAllSelected();
//General.Map.Map.ClearAllMarks(false);
// We have no destructor
GC.SuppressFinalize(this);
}
// Disposer
public override void Dispose()
{
// Not already disposed?
if (!isdisposed)
{
// Dispose old labels
if (labels != null)
foreach (TextLabel l in labels)
l.Dispose();
// Done
base.Dispose();
}
}
#endregion
#region ================== Methods
// This checks if the view offset/zoom changed and updates the check
protected bool CheckViewChanged()
{
bool viewchanged = false;
// View changed?
if (renderer.OffsetX != lastoffsetx) viewchanged = true;
if (renderer.OffsetY != lastoffsety) viewchanged = true;
if (renderer.Scale != lastscale) viewchanged = true;
// Keep view information
lastoffsetx = renderer.OffsetX;
lastoffsety = renderer.OffsetY;
lastscale = renderer.Scale;
// Return result
return viewchanged;
}
// This sets up new labels
/*
private void SetupLabels()
{
if (labels != null)
{
// Dispose old labels
foreach (KeyValuePair<Sector, TextLabel[]> lbl in labels)
foreach (TextLabel l in lbl.Value) l.Dispose();
}
// Make text labels for sectors
labels = new Dictionary<Sector, TextLabel[]>(General.Map.Map.Sectors.Count);
foreach (Sector s in General.Map.Map.Sectors)
{
// Setup labels
TextLabel[] labelarray = new TextLabel[s.Labels.Count];
for (int i = 0; i < s.Labels.Count; i++)
{
Vector2D v = s.Labels[i].position;
labelarray[i] = new TextLabel(20);
labelarray[i].TransformCoords = true;
labelarray[i].Rectangle = new RectangleF(v.x, v.y, 0.0f, 0.0f);
labelarray[i].AlignX = TextAlignmentX.Center;
labelarray[i].AlignY = TextAlignmentY.Middle;
labelarray[i].Scale = 14f;
labelarray[i].Color = General.Colors.Highlight.WithAlpha(255);
labelarray[i].Backcolor = General.Colors.Background.WithAlpha(255);
}
labels.Add(s, labelarray);
}
}
*/
private void SetupLabels()
{
labels = new List<TextLabel>();
PixelColor white = new PixelColor(255, 255, 255, 255);
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)
{
SlopeVertex sv = svg.Vertices[i];
double scale = 1 / renderer.Scale;
double x = sv.Pos.x;
double y = sv.Pos.y - 14 * scale;
string value = String.Format("Z: {0}", sv.Z);
bool showlabel = true;
// Rearrange labels if they'd be (exactly) on each other
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
} 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;
label.Color = white;
labels.Add(label);
}
}
}
}
}
// This updates the dragging
private void Update()
{
PixelColor stitchcolor = General.Colors.Highlight;
PixelColor losecolor = General.Colors.Selection;
PixelColor color;
snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid;
snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge;
DrawnVertex lastp = new DrawnVertex();
DrawnVertex curp = GetCurrentPosition();
float vsize = ((float)renderer.VertexSize + 1.0f) / renderer.Scale;
float vsizeborder = ((float)renderer.VertexSize + 3.0f) / renderer.Scale;
SetupLabels();
// Render drawing lines
if (renderer.StartOverlay(true))
{
float size = 9 / renderer.Scale;
if (BuilderPlug.Me.UseHighlight)
{
renderer.RenderHighlight(overlayGeometry, General.Colors.Selection.WithAlpha(64).ToInt());
}
List<SlopeVertex> vertices = new List<SlopeVertex>();
// Store all slope vertices and draw the lines between them
foreach (SlopeVertexGroup svg in BuilderPlug.Me.SlopeVertexGroups)
{
for (int i = 0; i < svg.Vertices.Count; i++)
{
vertices.Add(svg.Vertices[i]);
if (i < svg.Vertices.Count - 1)
renderer.RenderLine(svg.Vertices[0].Pos, svg.Vertices[i + 1].Pos, 1, new PixelColor(255, 255, 255, 255), 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;
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);
}
// Go for all points to draw lines
if (points.Count > 0)
{
// Render lines
lastp = points[0];
for (int i = 1; i < points.Count; i++)
{
// Determine line color
if (lastp.stitchline && points[i].stitchline) color = stitchcolor;
else color = losecolor;
// Render line
renderer.RenderLine(points[0].pos, points[i].pos, LINE_THICKNESS, color, true);
lastp = points[i];
}
// Determine line color
if (lastp.stitchline && snaptonearest) color = stitchcolor;
else color = losecolor;
// Render line to cursor
renderer.RenderLine(points[0].pos, curp.pos, LINE_THICKNESS, color, true);
// Render vertices
for (int i = 0; i < points.Count; i++)
{
// Determine vertex color
if (points[i].stitch) color = stitchcolor;
else color = losecolor;
// Render vertex
//renderer.RenderRectangleFilled(new RectangleF(points[i].pos.x - vsize, points[i].pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
renderer.RenderRectangleFilled(new RectangleF((float)(points[i].pos.x - size / 2), (float)(points[i].pos.y - size / 2), size, size), General.Colors.Background, true);
renderer.RenderRectangle(new RectangleF((float)(points[i].pos.x - size / 2), (float)(points[i].pos.y - size / 2), size, size), 2, General.Colors.Indication, true);
}
}
foreach (TextLabel l in labels)
renderer.RenderText(l);
// Determine point color
if (snaptonearest) color = stitchcolor;
else color = losecolor;
// Render vertex at cursor
//renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
renderer.RenderRectangleFilled(new RectangleF((float)(curp.pos.x - size / 2), (float)(curp.pos.y - size / 2), size, size), General.Colors.Background, true);
renderer.RenderRectangle(new RectangleF((float)(curp.pos.x - size / 2), (float)(curp.pos.y - size / 2), size, size), 2, General.Colors.Indication, true);
// Done
renderer.Finish();
}
// Done
renderer.Present();
}
private void updateOverlaySurfaces()
{
ICollection<Sector> orderedselection = General.Map.Map.GetSelectedSectors(true);
List<FlatVertex> vertsList = new List<FlatVertex>();
// Go for all selected sectors
foreach (Sector s in orderedselection) vertsList.AddRange(s.FlatVertices);
overlayGeometry = vertsList.ToArray();
}
// This returns the aligned and snapped draw position
public static DrawnVertex GetCurrentPosition(Vector2D mousemappos, bool snaptonearest, bool snaptogrid, IRenderer2D renderer, List<DrawnVertex> points)
{
DrawnVertex p = new DrawnVertex();
Vector2D vm = mousemappos;
float vrange = BuilderPlug.Me.StitchRange / renderer.Scale;
// Snap to nearest?
if (snaptonearest)
{
// Go for all drawn points
foreach (DrawnVertex v in points)
{
if (Vector2D.DistanceSq(mousemappos, v.pos) < (vrange * vrange))
{
p.pos = v.pos;
p.stitch = true;
p.stitchline = true;
return p;
}
}
// Try the nearest vertex
Vertex nv = General.Map.Map.NearestVertexSquareRange(mousemappos, vrange);
if (nv != null)
{
p.pos = nv.Position;
p.stitch = true;
p.stitchline = true;
return p;
}
// Try the nearest linedef
Linedef nl = General.Map.Map.NearestLinedefRange(mousemappos, BuilderPlug.Me.StitchRange / renderer.Scale);
if (nl != null)
{
// Snap to grid?
if (snaptogrid)
{
// Get grid intersection coordinates
List<Vector2D> coords = nl.GetGridIntersections();
// Find nearest grid intersection
bool found = false;
double found_distance = float.MaxValue;
Vector2D found_coord = new Vector2D();
foreach (Vector2D v in coords)
{
Vector2D delta = mousemappos - v;
if (delta.GetLengthSq() < found_distance)
{
found_distance = delta.GetLengthSq();
found_coord = v;
found = true;
}
}
if (found)
{
// Align to the closest grid intersection
p.pos = found_coord;
p.stitch = true;
p.stitchline = true;
return p;
}
}
else
{
// Aligned to line
p.pos = nl.NearestOnLine(mousemappos);
p.stitch = true;
p.stitchline = true;
return p;
}
}
}
else
{
// Always snap to the first drawn vertex so that the user can finish a complete sector without stitching
if (points.Count > 0)
{
if (Vector2D.DistanceSq(mousemappos, points[0].pos) < (vrange * vrange))
{
p.pos = points[0].pos;
p.stitch = true;
p.stitchline = false;
return p;
}
}
}
// if the mouse cursor is outside the map bondaries check if the line between the last set point and the
// mouse cursor intersect any of the boundary lines. If it does, set the position to this intersection
if (points.Count > 0 &&
(mousemappos.x < General.Map.Config.LeftBoundary || mousemappos.x > General.Map.Config.RightBoundary ||
mousemappos.y > General.Map.Config.TopBoundary || mousemappos.y < General.Map.Config.BottomBoundary))
{
Line2D dline = new Line2D(mousemappos, points[points.Count - 1].pos);
bool foundintersection = false;
double u = 0.0f;
List<Line2D> blines = new List<Line2D>();
// lines for left, top, right and bottom bondaries
blines.Add(new Line2D(General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary, General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary));
blines.Add(new Line2D(General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary, General.Map.Config.RightBoundary, General.Map.Config.TopBoundary));
blines.Add(new Line2D(General.Map.Config.RightBoundary, General.Map.Config.TopBoundary, General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary));
blines.Add(new Line2D(General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary, General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary));
// check for intersections with boundaries
for (int i = 0; i < blines.Count; i++)
{
if (!foundintersection)
{
// only check for intersection if the last set point is not on the
// line we are checking against
if (blines[i].GetSideOfLine(points[points.Count - 1].pos) != 0.0f)
{
foundintersection = blines[i].GetIntersection(dline, out u);
}
}
}
// if there was no intersection set the position to the last set point
if (!foundintersection)
vm = points[points.Count - 1].pos;
else
vm = dline.GetCoordinatesAt(u);
}
// Snap to grid?
if (snaptogrid)
{
// Aligned to grid
p.pos = General.Map.Grid.SnappedToGrid(vm);
// special handling
if (p.pos.x > General.Map.Config.RightBoundary) p.pos.x = General.Map.Config.RightBoundary;
if (p.pos.y < General.Map.Config.BottomBoundary) p.pos.y = General.Map.Config.BottomBoundary;
p.stitch = snaptonearest;
p.stitchline = snaptonearest;
return p;
}
else
{
// Normal position
p.pos = vm;
p.stitch = snaptonearest;
p.stitchline = snaptonearest;
return p;
}
}
// This gets the aligned and snapped draw position
private DrawnVertex GetCurrentPosition()
{
return GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, renderer, points);
}
// This draws a point at a specific location
public bool DrawPointAt(DrawnVertex p)
{
return DrawPointAt(p.pos, p.stitch, p.stitchline);
}
// This draws a point at a specific location
public bool DrawPointAt(Vector2D pos, bool stitch, bool stitchline)
{
if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary ||
pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary)
return false;
DrawnVertex newpoint = new DrawnVertex();
newpoint.pos = pos;
newpoint.stitch = stitch;
newpoint.stitchline = stitchline;
points.Add(newpoint);
updateOverlaySurfaces();
Update();
if (points.Count == 3)
FinishDraw();
return true;
}
private bool IsControlSector(Sector s)
{
//Determine whether or not the sector is actually a control sector for a 3D floor
foreach (Sidedef sd in s.Sidedefs)
{
if (sd.Line.Action >= 100 && sd.Line.Action < 300)
return true;
}
return false;
}
#endregion
#region ================== Events
public override void OnHelp()
{
General.ShowHelp("gzdb/features/classic_modes/mode_slopes.html");
}
// Engaging
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();
}
EnableAutoPanning();
renderer.SetPresentation(Presentation.Standard);
// Convert geometry selection to sectors only
General.Map.Map.ConvertSelection(SelectionType.Sectors);
General.Interface.AddButton(BuilderPlug.Me.MenusForm.FloorSlope);
General.Interface.AddButton(BuilderPlug.Me.MenusForm.CeilingSlope);
General.Interface.AddButton(BuilderPlug.Me.MenusForm.FloorAndCeilingSlope);
BuilderPlug.Me.MenusForm.FloorSlope.Checked = false;
BuilderPlug.Me.MenusForm.CeilingSlope.Checked = false;
BuilderPlug.Me.MenusForm.FloorAndCeilingSlope.Checked = false;
if (slopedrawingmode == SlopeDrawingMode.Floor)
BuilderPlug.Me.MenusForm.FloorSlope.Checked = true;
else if (slopedrawingmode == SlopeDrawingMode.Ceiling)
BuilderPlug.Me.MenusForm.CeilingSlope.Checked = true;
else
BuilderPlug.Me.MenusForm.FloorAndCeilingSlope.Checked = true;
// Make text labels for sectors
SetupLabels();
updateOverlaySurfaces();
Update();
// Set cursor
General.Interface.SetCursor(Cursors.Cross);
}
// Disengaging
public override void OnDisengage()
{
base.OnDisengage();
General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.FloorSlope);
General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.CeilingSlope);
General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.FloorAndCeilingSlope);
DisableAutoPanning();
}
// Cancelled
public override void OnCancel()
{
// Cancel base class
base.OnCancel();
// Return to previous stable mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// Accepted
public override void OnAccept()
{
Cursor.Current = Cursors.AppStarting;
General.Settings.FindDefaultDrawSettings();
// When points have been drawn
if (points.Count > 1)
{
// Make undo for the draw
General.Map.UndoRedo.CreateUndo("Draw slope");
List<SlopeVertex> sv_floor = new List<SlopeVertex>();
List<SlopeVertex> sv_ceiling = new List<SlopeVertex>();
// Fills the slope vertex list for both floor and ceiling slopes. Alos tried
// to determine the default z position of the vertex
List<Sector> selected = (List<Sector>)General.Map.Map.GetSelectedSectors(true);
if (selected.Count == 1 && IsControlSector(selected[0]))
{
//If a 3D floor control sector is selected, then just use the height of it directly
float zf = selected[0].FloorHeight;
float zc = selected[0].CeilHeight;
for (int i = 0; i < points.Count; i++)
{
sv_floor.Add(new SlopeVertex(points[i].pos, zf));
sv_ceiling.Add(new SlopeVertex(points[i].pos, zc));
}
} else {
//For normal sectors, grab the height of the sector each control handle lies within
for (int i = 0; i < points.Count; i++)
{
float zf = 0;
float zc = 0;
Sector s = General.Map.Map.GetSectorByCoordinates(points[i].pos);
if (s != null)
{
foreach (Sidedef sd in s.Sidedefs)
{
if (sd.Line.Line.GetSideOfLine(points[i].pos) == 0)
{
if (sd.Line.Back != null && !selected.Contains(sd.Line.Back.Sector))
{
zf = sd.Line.Back.Sector.FloorHeight;
zc = sd.Line.Back.Sector.CeilHeight;
}
else
{
zf = sd.Line.Front.Sector.FloorHeight;
zc = sd.Line.Front.Sector.CeilHeight;
}
}
}
}
sv_floor.Add(new SlopeVertex(points[i].pos, zf));
sv_ceiling.Add(new SlopeVertex(points[i].pos, zc));
}
}
// Create the floor slope vertex group and add it to all selected sectors
if (slopedrawingmode == SlopeDrawingMode.Floor || slopedrawingmode == SlopeDrawingMode.FloorAndCeiling)
{
int id = -1;
SlopeVertexGroup svg = BuilderPlug.Me.AddSlopeVertexGroup(sv_floor, out id);
svg.Sectors.Clear();
foreach (Sector s in selected)
{
// Make sure the field work with undo/redo
s.Fields.BeforeFieldsChange();
if (s.Fields.ContainsKey("user_floorplane_id"))
s.Fields.Remove("user_floorplane_id");
s.Fields.Add("user_floorplane_id", new UniValue(UniversalType.Integer, id));
// svg.Sectors.Add(s);
svg.AddSector(s, PlaneType.Floor);
}
}
// Create the ceiling slope vertex group and add it to all selected sectors
if (slopedrawingmode == SlopeDrawingMode.Ceiling || slopedrawingmode == SlopeDrawingMode.FloorAndCeiling)
{
int id = -1;
SlopeVertexGroup svg = BuilderPlug.Me.AddSlopeVertexGroup(sv_ceiling, out id);
svg.Sectors.Clear();
foreach (Sector s in selected)
{
// Make sure the field work with undo/redo
s.Fields.BeforeFieldsChange();
if (s.Fields.ContainsKey("user_ceilingplane_id"))
s.Fields.Remove("user_ceilingplane_id");
s.Fields.Add("user_ceilingplane_id", new UniValue(UniversalType.Integer, id));
// svg.Sectors.Add(s);
svg.AddSector(s, PlaneType.Ceiling);
}
}
BuilderPlug.Me.StoreSlopeVertexGroupsInSector();
// BuilderPlug.Me.UpdateSlopes();
// Clear selection
General.Map.Map.ClearAllSelected();
// Update cached values
General.Map.Map.Update();
// Map is changed
General.Map.IsChanged = true;
}
// Done
Cursor.Current = Cursors.Default;
// Return to original mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// This redraws the display
public override void OnRedrawDisplay()
{
renderer.RedrawSurface();
// Render lines
if (renderer.StartPlotter(true))
{
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
renderer.PlotVerticesSet(General.Map.Map.Vertices);
renderer.Finish();
}
// Render things
if (renderer.StartThings(true))
{
renderer.RenderThingSet(General.Map.Map.Things, 1.0f);
renderer.RenderSRB2Extras();
renderer.Finish();
}
// Normal update
updateOverlaySurfaces();
Update();
}
// Mouse moving
public override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
updateOverlaySurfaces();
Update();
}
// When a key is released
public override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) ||
(snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update();
}
// When a key is pressed
public override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) ||
(snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update();
}
#endregion
#region ================== Actions
[BeginAction("drawfloorslope")]
public void DrawFloorSlope()
{
slopedrawingmode = SlopeDrawingMode.Floor;
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()
{
slopedrawingmode = SlopeDrawingMode.Ceiling;
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()
{
slopedrawingmode = SlopeDrawingMode.FloorAndCeiling;
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");
}
// Drawing a point
[BeginAction("drawslopepoint")]
public void DrawPoint()
{
// Mouse inside window?
if (General.Interface.MouseInDisplay)
{
DrawnVertex newpoint = GetCurrentPosition();
if (!DrawPointAt(newpoint)) General.Interface.DisplayStatus(StatusType.Warning, "Failed to draw point: outside of map boundaries.");
}
}
// Remove a point
//[BeginAction("removepoint")]
public void RemovePoint()
{
if (points.Count > 0) points.RemoveAt(points.Count - 1);
updateOverlaySurfaces();
Update();
}
// Finish drawing
[BeginAction("finishslopedraw")]
public void FinishDraw()
{
// Accept the changes
General.Editing.AcceptMode();
}
#endregion
}
}