#region ================== Namespaces using System; using System.Collections.Generic; using System.Windows.Forms; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Editing; using System.Drawing; using CodeImp.DoomBuilder.Windows; #endregion //JBR Perpendicular Linedef mode namespace CodeImp.DoomBuilder.BuilderModes { [EditMode(DisplayName = "Perpendicular Linedef", AllowCopyPaste = false, Volatile = true)] public sealed class PerpendicularLinedefMode : BaseClassicMode { #region ================== Constants private const float LINE_THICKNESS = 0.6f; private const int LINKED_LINEDEFS = 0; private const int UNLINKED_LINEDEFS = 1; private const int LINEDEFS = 1; private const int VERTICES_PERPENDICULAR = 2; private const int VERTICES_BOTH = 3; private const int THINGS = 4; #endregion #region ================== Variables private EditMode basemode; private ICollection selected; private bool clearlinedefs; #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 PerpendicularLinedefMode(EditMode basemode) { this.basemode = basemode; } // Disposer public override void Dispose() { // Not already disposed? if (!isdisposed) { // Clean up // Done base.Dispose(); } } #endregion #region ================== Methods /// Join into a near vertex on map private Vector2D JoinIntoNearMapVertex(Vector2D pos, float epsilon) { foreach (Vertex va in General.Map.Map.Vertices) { if (va.IsDisposed) continue; if (pos.CloseTo(va.Position, epsilon)) { return va.Position; } } return pos; } // Join into a near vertex on map private bool JoinIntoNearMapVertex(Vertex v, float epsilon) { foreach (Vertex va in General.Map.Map.Vertices) { if (va == v || va.IsDisposed) continue; if (v.Position.CloseTo(va.Position, epsilon)) { v.Join(va); return true; } } return false; } #endregion #region ================== Events public override void OnHelp() { General.ShowHelp("e_perpendicularlinedef.html"); } // Cancelled public override void OnCancel() { // Cancel base class base.OnCancel(); // Clear selection that is not the current mode if (clearlinedefs) General.Map.Map.ClearSelectedLinedefs(); // Return to base mode General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); } // Mode engages public override void OnEngage() { base.OnEngage(); renderer.SetPresentation(Presentation.Standard); // Make collections by selection if (basemode is VerticesMode) { selected = new List(); ICollection selectedvertices = General.Map.Map.GetSelectedVertices(true); foreach (Vertex v in selectedvertices) { foreach (Linedef ld in v.Linedefs) { if (ld.Start.Selected && ld.End.Selected) { ld.Selected = true; if (!selected.Contains(ld)) selected.Add(ld); } } } clearlinedefs = true; } if (basemode is LinedefsMode) { selected = General.Map.Map.GetSelectedLinedefs(true); } if (basemode is SectorsMode) { selected = new List(); ICollection selectedsectors = General.Map.Map.GetSelectedSectors(true); foreach (Sector sc in selectedsectors) { foreach (Sidedef sd in sc.Sidedefs) { sd.Line.Selected = true; if (!selected.Contains(sd.Line)) selected.Add(sd.Line); } } clearlinedefs = true; } // Show toolbox window BuilderPlug.Me.PerpendicularLinedefForm.Show((Form)General.Interface); } // Disenagaging public override void OnDisengage() { base.OnDisengage(); // Hide toolbox window BuilderPlug.Me.PerpendicularLinedefForm.Hide(); } // This applies the curves and returns to the base mode public override void OnAccept() { // Create undo General.Map.UndoRedo.CreateUndo("Perpendicular (Linedef)"); int createas = BuilderPlug.Me.PerpendicularLinedefForm.CreateAs; float distance = BuilderPlug.Me.PerpendicularLinedefForm.Distance; float offset = BuilderPlug.Me.PerpendicularLinedefForm.OffsetPerc / 100.0f; bool backwards = BuilderPlug.Me.PerpendicularLinedefForm.Backwards; ThingBrowser2Form.Result thingresult = new ThingBrowser2Form.Result(); float snapmp = BuilderPlug.Me.PerpendicularVertexForm.SnapMP; // Ask for thing type... if (createas == THINGS) { BuilderPlug.Me.PerpendicularLinedefForm.Hide(); // Pick the thing to add... thingresult = ThingBrowser2Form.BrowseThing(BuilderPlug.Me.PerpendicularVertexForm); if (!thingresult.OK) { General.Map.UndoRedo.WithdrawUndo(); General.Map.UndoRedo.WithdrawUndo(); General.Interface.DisplayStatus(StatusType.Warning, "Operation aborted."); return; } BuilderPlug.Me.PerpendicularLinedefForm.Show(); BuilderPlug.Me.PerpendicularLinedefForm.Activate(); } // Go for all selected lines int numcreated = 0; foreach (Linedef ld in selected) { Vector2D centerpos = ld.Start.Position.LinearInto(ld.End.Position, offset); Vector2D deltapos = ld.Start.Position - ld.End.Position; if (backwards) deltapos = -deltapos; Vector2D normalunit = deltapos.GetPerpendicular().GetNormal(); Vector2D perppos = centerpos + normalunit * distance; // Check if is near some other vertex on map //perppos = JoinIntoNearVertex(perppos, 0.1f); numcreated++; if (createas == LINKED_LINEDEFS) { // Linked linedef between 2 vertices List dvl = new List(); DrawnVertex dv = new DrawnVertex(); // struct dv.pos = centerpos; dv.stitch = true; dv.stitchline = true; dvl.Add(dv); dv.pos = perppos; dvl.Add(dv); if (!Tools.DrawLines(dvl, true, BuilderPlug.Me.AutoAlignTextureOffsetsOnCreate)) { // Drawing failed // NOTE: I have to call this twice, because the first time only cancels this volatile mode General.Map.UndoRedo.WithdrawUndo(); General.Map.UndoRedo.WithdrawUndo(); return; } if (snapmp > 0f) { // Manually join with vertices already on map since DrawLines fail to do so... List marked = General.Map.Map.GetMarkedVertices(true); foreach (Vertex vm in marked) JoinIntoNearMapVertex(vm, snapmp); } } else if (createas == UNLINKED_LINEDEFS) { // Unlinked linedef Vertex perpv = General.Map.Map.CreateVertex(perppos); Vertex centerv = General.Map.Map.CreateVertex(centerpos); General.Map.Map.CreateLinedef(centerv, perpv); } else if (createas == VERTICES_PERPENDICULAR) { // Vertex Vertex perpv = General.Map.Map.CreateVertex(perppos); if (snapmp > 0f) JoinIntoNearMapVertex(perpv, snapmp); } else if (createas == VERTICES_BOTH) { Vertex perpv = General.Map.Map.CreateVertex(perppos); if (snapmp > 0f) JoinIntoNearMapVertex(perpv, snapmp); if (offset > 0f && offset < 1f) { // Split linedef Vertex centerv = General.Map.Map.CreateVertex(centerpos); ld.Split(centerv); } } else if (createas == THINGS) { Thing tp = General.Map.Map.CreateThing(); if (tp != null) { // Check if is near some other vertex on map if (snapmp > 0f) perppos = JoinIntoNearMapVertex(perppos, snapmp); General.Settings.ApplyDefaultThingSettings(tp); thingresult.Apply(tp); tp.Move(perppos); tp.UpdateConfiguration(); } } } if (numcreated == 0) { // If nothing created then abort General.Map.UndoRedo.WithdrawUndo(); General.Map.UndoRedo.WithdrawUndo(); General.Interface.DisplayStatus(StatusType.Warning, "No perpendiculars created."); return; } // Announce how many we created General.Interface.DisplayStatus(StatusType.Action, "Created " + numcreated.ToString() + " perpendicular(s)."); if (createas == THINGS) { //Update Things filter General.Map.ThingsFilter.Update(); } // Snap to map format accuracy General.Map.Map.SnapAllToAccuracy(); // Clear selection General.Map.Map.ClearAllSelected(); // Update cached values General.Map.Map.Update(); // Edit new sectors? List newsectors = General.Map.Map.GetMarkedSectors(true); if (BuilderPlug.Me.EditNewSector && (newsectors.Count > 0)) General.Interface.ShowEditSectors(newsectors); // Update the used textures General.Map.Data.UpdateUsedTextures(); //mxd General.Map.Renderer2D.UpdateExtraFloorFlag(); // Map is changed General.Map.IsChanged = true; // Return to base mode General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); } // Redrawing display public override void OnRedrawDisplay() { renderer.RedrawSurface(); float vsize = (renderer.VertexSize + 1.0f) / renderer.Scale; // 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, Presentation.THINGS_ALPHA); renderer.RenderNiGHTSPath(); renderer.Finish(); } int createas = BuilderPlug.Me.PerpendicularLinedefForm.CreateAs; float distance = BuilderPlug.Me.PerpendicularLinedefForm.Distance; float offset = BuilderPlug.Me.PerpendicularLinedefForm.OffsetPerc / 100.0f; bool backwards = BuilderPlug.Me.PerpendicularLinedefForm.Backwards; // Render overlay if (renderer.StartOverlay(true)) { // Go for all selected lines foreach (Linedef ld in selected) { Vector2D centerpos = ld.Start.Position.LinearInto(ld.End.Position, offset); Vector2D deltapos = ld.Start.Position - ld.End.Position; if (backwards) deltapos = -deltapos; Vector2D normalunit = deltapos.GetPerpendicular().GetNormal(); Vector2D perppos = centerpos + normalunit * distance; if (createas <= LINEDEFS) { renderer.RenderLine(centerpos, perppos, LINE_THICKNESS, General.Colors.Highlight, true); } else { renderer.RenderRectangleFilled(new RectangleF(perppos.x - vsize, perppos.y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true); } if (createas == LINKED_LINEDEFS || createas == VERTICES_BOTH) { renderer.RenderRectangleFilled(new RectangleF(centerpos.x - vsize, centerpos.y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true); } } renderer.Finish(); } renderer.Present(); } #endregion } }