#region ================== Namespaces using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.Windows; #endregion namespace CodeImp.DoomBuilder.BuilderModes { [EditMode(DisplayName = "Draw Curve Mode", SwitchAction = "drawcurvemode", ButtonImage = "DrawCurveMode.png", //mxd ButtonOrder = int.MinValue + 2, //mxd ButtonGroup = "000_drawing", //mxd AllowCopyPaste = false, Volatile = true, Optional = false)] public class DrawCurveMode : DrawGeometryMode { #region ================== Variables private readonly HintLabel hintlabel; private Curve curve; private int segmentlength; private const int MIN_SEGMENT_LENGTH = 16; private const int MAX_SEGMENT_LENGTH = 4096; //just some arbitrary number // Interface private DrawCurveOptionsPanel panel; #endregion #region ================== Constructor/Disposer public DrawCurveMode() { hintlabel = new HintLabel(General.Colors.InfoLine); labeluseoffset = false; } public override void Dispose() { // Not already disposed? if(!isdisposed) { // Clean up if(hintlabel != null) hintlabel.Dispose(); // Done base.Dispose(); } } #endregion #region ================== Methods protected override void Update() { PixelColor stitchcolor = General.Colors.Highlight; PixelColor losecolor = General.Colors.Selection; snaptocardinaldirection = General.Interface.ShiftState && General.Interface.AltState; snaptogrid = snaptocardinaldirection || General.Interface.ShiftState ^ General.Interface.SnapToGrid; snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; DrawnVertex curp = GetCurrentPosition(); float vsize = (renderer.VertexSize + 1.0f) / renderer.Scale; // Update label positions (mxd) if(labels.Count > 0) { // Update labels for already drawn lines for(int i = 0; i < labels.Count - 1; i++) labels[i].Move(points[i].pos, points[i + 1].pos); // Update label for active line labels[labels.Count - 1].Move(points[points.Count - 1].pos, curp.pos); } // Render drawing lines if(renderer.StartOverlay(true)) { // Go for all points to draw lines if(points.Count > 0) { //update curve List verts = new List(); for(int i = 0; i < points.Count; i++) verts.Add(points[i].pos); if(curp.pos != verts[verts.Count-1]) verts.Add(curp.pos); curve = CurveTools.CurveThroughPoints(verts, 0.5f, 0.75f, segmentlength); // Render lines for(int i = 1; i < curve.Shape.Count; i++) { // Determine line color PixelColor c = snaptonearest ? stitchcolor : losecolor; // Render line renderer.RenderLine(curve.Shape[i - 1], curve.Shape[i], LINE_THICKNESS, c, true); } //render "inactive" vertices for(int i = 1; i < curve.Shape.Count - 1; i++) { // Determine vertex color PixelColor c = !snaptonearest ? stitchcolor : losecolor; // Render vertex renderer.RenderRectangleFilled(new RectangleF((float)(curve.Shape[i].x - vsize), (float)(curve.Shape[i].y - vsize), vsize * 2.0f, vsize * 2.0f), c, true); } } if(points.Count > 0) { // Render vertices for(int i = 0; i < points.Count; i++) { // Determine vertex color PixelColor c = points[i].stitch ? stitchcolor : losecolor; // Render vertex renderer.RenderRectangleFilled(new RectangleF((float)(points[i].pos.x - vsize), (float)(points[i].pos.y - vsize), vsize * 2.0f, vsize * 2.0f), c, true); } } // Determine point color PixelColor color = snaptonearest ? stitchcolor : losecolor; // Render vertex at cursor renderer.RenderRectangleFilled(new RectangleF((float)(curp.pos.x - vsize), (float)(curp.pos.y - vsize), vsize * 2.0f, vsize * 2.0f), color, true); // Render labels renderer.RenderText(labels.ToArray()); //Render info label Vector2D start = new Vector2D(mousemappos.x + (32 / renderer.Scale), mousemappos.y - (16 / renderer.Scale)); Vector2D end = new Vector2D(mousemappos.x + (96 / renderer.Scale), mousemappos.y); hintlabel.Move(start, end); hintlabel.Text = "SEG LEN: " + segmentlength; renderer.RenderText(hintlabel.TextLabel); // Done renderer.Finish(); } // Done renderer.Present(); } #endregion #region ================== Events public override void OnEngage() { base.OnEngage(); //setup settings panel panel.SegmentLength = segmentlength; panel.Register(); } public override void OnAccept() { Cursor.Current = Cursors.AppStarting; General.Settings.FindDefaultDrawSettings(); // When points have been drawn if(points.Count > 0) { // Make undo for the draw General.Map.UndoRedo.CreateUndo("Curve draw"); // Make an analysis and show info string[] adjectives = { "beautiful", "lovely", "romantic", "stylish", "cheerful", "comical", "awesome", "accurate", "adorable", "adventurous", "attractive", "cute", "elegant", "glamorous", "gorgeous", "handsome", "magnificent", "unusual", "outstanding", "mysterious", "amusing", "charming", "fantastic", "jolly" }; string word = adjectives[points.Count % adjectives.Length]; word = (points.Count > adjectives.Length) ? "very " + word : word; string a = ((word[0] == 'a') || (word[0] == 'e') || (word[0] == 'o') || (word[0] == 'u')) ? "an " : "a "; General.Interface.DisplayStatus(StatusType.Action, "Created " + a + word + " curve."); List verts = new List(); // If we have a curve... if(points.Count > 2) { // Is it an (auto)closed curve? int lastpoint; if(drawingautoclosed || points[0].pos == points[points.Count - 1].pos) lastpoint = curve.Segments.Count; else lastpoint = curve.Segments.Count - 1; for(int i = 0; i < lastpoint; i++) { int next = (i == curve.Segments.Count - 1 ? 0 : i + 1); bool stitch = points[i].stitch && points[next].stitch; bool stitchline = points[i].stitchline && points[next].stitchline; // Add segment points except the last one for(int c = 0; c < curve.Segments[i].Points.Length - 1; c++) { DrawnVertex dv = new DrawnVertex(); dv.pos = curve.Segments[i].Points[c]; dv.stitch = stitch; dv.stitchline = stitchline; verts.Add(dv); } } // Add the last point DrawnVertex end = new DrawnVertex(); end.pos = curve.Segments[lastpoint - 1].End; end.stitch = verts[verts.Count - 1].stitch; end.stitchline = verts[verts.Count - 1].stitchline; verts.Add(end); } else { verts = points; } // Make the drawing if(Tools.DrawLines(verts, true, BuilderPlug.Me.AutoAlignTextureOffsetsOnCreate)) //mxd { // Snap to map format accuracy General.Map.Map.SnapAllToAccuracy(); // Clear selection General.Map.Map.ClearAllSelected(); // Update cached values General.Map.Map.Update(); //mxd. Outer sectors may require some splittin... if(General.Settings.SplitJoinedSectors) Tools.SplitOuterSectors(General.Map.Map.GetMarkedLinedefs(true)); // 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; } else { // 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(); } } // Done Cursor.Current = Cursors.Default; if(continuousdrawing) { // Reset settings points.Clear(); labels.Clear(); drawingautoclosed = false; // Redraw display General.Interface.RedrawDisplay(); } else { // Return to original mode General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); } } private void OptionsPanelOnValueChanged(object sender, EventArgs eventArgs) { segmentlength = panel.SegmentLength; Update(); } public override void OnHelp() { General.ShowHelp("/gzdb/features/classic_modes/mode_drawcurve.html"); } #endregion #region ================== mxd. Settings panel protected override void SetupInterface() { // Load stored settings segmentlength = General.Clamp(General.Settings.ReadPluginSetting("drawcurvemode.segmentlength", 32), MIN_SEGMENT_LENGTH, MAX_SEGMENT_LENGTH); // Add options docker panel = new DrawCurveOptionsPanel(MIN_SEGMENT_LENGTH, MAX_SEGMENT_LENGTH); panel.SegmentLength = segmentlength; panel.OnValueChanged += OptionsPanelOnValueChanged; panel.OnContinuousDrawingChanged += OnContinuousDrawingChanged; panel.OnAutoCloseDrawingChanged += OnAutoCloseDrawingChanged; // Needs to be set after adding the events... panel.ContinuousDrawing = General.Settings.ReadPluginSetting("drawcurvemode.continuousdrawing", false); panel.AutoCloseDrawing = General.Settings.ReadPluginSetting("drawlinesmode.autoclosedrawing", false); } protected override void AddInterface() { panel.Register(); } protected override void RemoveInterface() { // Store settings General.Settings.WritePluginSetting("drawcurvemode.segmentlength", segmentlength); General.Settings.WritePluginSetting("drawcurvemode.continuousdrawing", panel.ContinuousDrawing); General.Settings.WritePluginSetting("drawlinesmode.autoclosedrawing", panel.AutoCloseDrawing); // Remove the buttons panel.Unregister(); } #endregion #region ================== Actions [BeginAction("increasesubdivlevel")] protected virtual void IncreaseSubdivLevel() { if(segmentlength < MAX_SEGMENT_LENGTH) { int increment = Math.Max(MIN_SEGMENT_LENGTH, segmentlength / 32 * 16); segmentlength += increment; if(segmentlength > MAX_SEGMENT_LENGTH) segmentlength = MAX_SEGMENT_LENGTH; panel.SegmentLength = segmentlength; Update(); } } [BeginAction("decreasesubdivlevel")] protected virtual void DecreaseSubdivLevel() { if(segmentlength > MIN_SEGMENT_LENGTH) { int increment = Math.Max(MIN_SEGMENT_LENGTH, segmentlength / 32 * 16); segmentlength -= increment; if(segmentlength < MIN_SEGMENT_LENGTH) segmentlength = MIN_SEGMENT_LENGTH; panel.SegmentLength = segmentlength; Update(); } } #endregion } }