#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 CodeImp.DoomBuilder.Interface; using CodeImp.DoomBuilder.IO; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.Geometry; using System.Drawing; using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.Controls; #endregion namespace CodeImp.DoomBuilder.BuilderModes.Editing { [EditMode(SwitchAction = "drawlinesmode")] public class DrawGeometryMode : ClassicMode { #region ================== Structures private struct DrawnVertex { public Vector2D pos; public bool stitch; } #endregion #region ================== Constants private const float LINE_THICKNESS = 0.8f; #endregion #region ================== Variables // Mode to return to private EditMode basemode; // Drawing points private List 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 #endregion #region ================== Properties // Just keep the base mode button checked public override string EditModeButtonName { get { return basemode.GetType().Name; } } #endregion #region ================== Constructor / Disposer // Constructor public DrawGeometryMode() { // Initialize this.basemode = General.Map.Mode; points = new List(); // No selection in this mode General.Map.Map.ClearAllSelected(); // We have no destructor GC.SuppressFinalize(this); } // Disposer public override void Dispose() { // Not already disposed? if(!isdisposed) { // Clean up // Done base.Dispose(); } } #endregion #region ================== Methods // Cancelled public override void Cancel() { // Cancel base class base.Cancel(); // Return to original mode Type t = basemode.GetType(); basemode = (EditMode)Activator.CreateInstance(t); General.Map.ChangeMode(basemode); } // Disenagaging public override void Disengage() { MapSet map = General.Map.Map; base.Disengage(); Cursor.Current = Cursors.AppStarting; // When not cancelled and points have been drawn if(!cancelled && (points.Count > 0)) { // Make undo for the draw General.Map.UndoRedo.CreateUndo("line draw", UndoGroup.None, 0); // STEP 1: Create the new geometry List newverts = new List(); List newlines = new List(); List mergeverts = new List(); List nonmergeverts = new List(map.Vertices); Vertex v1 = map.CreateVertex((int)points[0].pos.x, (int)points[0].pos.y); newverts.Add(v1); if(points[0].stitch) mergeverts.Add(v1); else nonmergeverts.Add(v1); for(int i = 1; i < points.Count; i++) { Vertex v2 = map.CreateVertex((int)points[i].pos.x, (int)points[i].pos.y); newverts.Add(v2); if(points[i].stitch) mergeverts.Add(v2); else nonmergeverts.Add(v2); Linedef ld = map.CreateLinedef(v1, v2); ld.Selected = true; newlines.Add(ld); v1 = v2; } // STEP 2: Merge the new geometry foreach(Vertex v in mergeverts) v.Marked = true; map.StitchGeometry(); // STEP 3: Make sectors where possible // Update cached values map.Update(); // Map is changed General.Map.IsChanged = true; } // Hide highlight info General.Interface.HideInfo(); // Done Cursor.Current = Cursors.Default; } // 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 redraws the display public override void RedrawDisplay() { // 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.SetThingsRenderOrder(false); renderer.RenderThingSet(General.Map.Map.Things); renderer.Finish(); } // Normal update Update(); } // This updates the dragging private void Update() { snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; Vector2D lastp = new Vector2D(0, 0); DrawnVertex curp = GetCurrentPosition(); float vsize = ((float)renderer.VertexSize + 1.0f) / renderer.Scale; // Render drawing lines if(renderer.StartOverlay(true)) { // Go for all points to draw lines if(points.Count > 0) { // Render lines lastp = points[0].pos; for(int i = 1; i < points.Count; i++) { renderer.RenderLine(lastp, points[i].pos, LINE_THICKNESS, General.Colors.Selection, true); lastp = points[i].pos; } // Render line to cursor renderer.RenderLine(lastp, curp.pos, LINE_THICKNESS, General.Colors.Highlight, true); // Render vertices for(int i = 0; i < points.Count; i++) { if(points[i].stitch) { renderer.RenderRectangleFilled(new RectangleF(points[i].pos.x - vsize, points[i].pos.y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true); } else { renderer.RenderRectangleFilled(new RectangleF(points[i].pos.x - vsize, points[i].pos.y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Selection, true); } } } // Render vertex at cursor renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true); // Done renderer.Finish(); } // Done renderer.Present(); } // This gets the aligned and snapped draw position private DrawnVertex GetCurrentPosition() { DrawnVertex p = new DrawnVertex(); // Snap to nearest? if(snaptonearest) { float vrange = VerticesMode.VERTEX_HIGHLIGHT_RANGE / renderer.Scale; // Go for all drawn points foreach(DrawnVertex v in points) { Vector2D delta = mousemappos - v.pos; if(delta.GetLengthSq() < (vrange * vrange)) { p.pos = v.pos; p.stitch = 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; return p; } // Try the nearest linedef Linedef nl = General.Map.Map.NearestLinedefRange(mousemappos, LinedefsMode.LINEDEF_HIGHLIGHT_RANGE / renderer.Scale); if(nl != null) { // Snap to grid? if(snaptogrid) { // Get grid intersection coordinates List coords = nl.GetGridIntersections(); // Find nearest grid intersection float 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; } } // Align to the closest grid intersection p.pos = found_coord; p.stitch = true; return p; } else { // Aligned to line p.pos = nl.NearestOnLine(mousemappos); p.stitch = true; return p; } } } // Snap to grid? if(snaptogrid) { // Aligned to grid p.pos = General.Map.Grid.SnappedToGrid(mousemappos); p.stitch = snaptonearest; return p; } else { // Normal position p.pos = mousemappos; p.stitch = snaptonearest; return p; } } // Mouse moving public override void MouseMove(MouseEventArgs e) { base.MouseMove(e); Update(); } // Drawing a point [BeginAction("drawpoint")] public void DrawPoint() { // Mouse inside window? if(General.Interface.MouseInDisplay) { points.Add(GetCurrentPosition()); Update(); } } // Remove a point [BeginAction("removepoint")] public void RemovePoint() { if(points.Count > 0) points.RemoveAt(points.Count - 1); Update(); } // Finish drawing [BeginAction("finishdraw")] public void FinishDraw() { // Just return to base mode, Disengage will be called automatically. General.Map.ChangeMode(basemode); } // When a key is released public override void KeyUp(KeyEventArgs e) { base.KeyUp(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 KeyDown(KeyEventArgs e) { base.KeyDown(e); if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); } #endregion } }