#region ================== Namespaces using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.Controls; 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 Grid Mode", SwitchAction = "drawgridmode", ButtonImage = "DrawGridMode.png", //mxd ButtonOrder = int.MinValue + 5, //mxd ButtonGroup = "000_drawing", //mxd AllowCopyPaste = false, Volatile = true, Optional = false)] public class DrawGridMode : DrawGeometryMode { #region ================== Variables private static int horizontalSlices = 3; private static int verticalSlices = 3; private static bool triangulate; private static bool gridlock; private List gridpoints; private HintLabel hintLabel; private int width; private int height; private int slicesH; private int slicesV; private int initialGridSize; private Vector2D start; private Vector2D end; //interface private Docker settingsdocker; private DrawGridOptionsPanel panel; #endregion #region ================== Constructor public DrawGridMode() { snaptogrid = true; //Options docker panel = new DrawGridOptionsPanel(); panel.OnValueChanged += OptionsPanelOnValueChanged; panel.OnGridLockChanged += OptionsPanelOnOnGridLockChanged; settingsdocker = new Docker("drawgrid", "Draw Grid Settings", panel); } #endregion #region ================== Events public override void OnEngage() { base.OnEngage(); General.Interface.AddDocker(settingsdocker); General.Interface.SelectDocker(settingsdocker); initialGridSize = General.Map.Grid.GridSize; //setup settings panel panel.Triangulate = triangulate; panel.LockToGrid = gridlock; panel.HorizontalSlices = horizontalSlices - 1; panel.VerticalSlices = verticalSlices - 1; } public override void OnDisengage() { General.Interface.RemoveDocker(settingsdocker); base.OnDisengage(); } override public void OnAccept() { Cursor.Current = Cursors.AppStarting; General.Settings.FindDefaultDrawSettings(); // When we have a shape... if(gridpoints.Count > 0) { // Make undo for the draw General.Map.UndoRedo.CreateUndo("Grid draw"); // Make an analysis and show info string[] adjectives = new[] { "gloomy", "sad", "unhappy", "lonely", "troubled", "depressed", "heartsick", "glum", "pessimistic", "bitter", "downcast" }; // aaand my english vocabulary ends here :) string word = adjectives[new Random().Next(adjectives.Length - 1)]; string a = (word[0] == 'u' ? "an " : "a "); General.Interface.DisplayStatus(StatusType.Action, "Created " + a + word + " grid."); List newsectors = new List(); foreach (DrawnVertex[] shape in gridpoints) { if(!Tools.DrawLines(shape, 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; } // Update cached values after each step... General.Map.Map.Update(); newsectors.AddRange(General.Map.Map.GetMarkedSectors(true)); } // Snap to map format accuracy General.Map.Map.SnapAllToAccuracy(); // Clear selection General.Map.Map.ClearAllSelected(); // Edit new sectors? if(BuilderPlug.Me.EditNewSector && (newsectors.Count > 0)) General.Interface.ShowEditSectors(newsectors); // Update the used textures General.Map.Data.UpdateUsedTextures(); // Map is changed General.Map.IsChanged = true; } // Done Cursor.Current = Cursors.Default; // Return to original mode General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); } public override void OnHelp() { General.ShowHelp("/gzdb/features/classic_modes/mode_drawgrid.html"); } private void OptionsPanelOnValueChanged(object sender, EventArgs eventArgs) { triangulate = panel.Triangulate; horizontalSlices = panel.HorizontalSlices + 1; verticalSlices = panel.VerticalSlices + 1; Update(); } private void OptionsPanelOnOnGridLockChanged(object sender, EventArgs eventArgs) { gridlock = panel.LockToGrid; Update(); } #endregion #region ================== Methods override protected void Update() { PixelColor stitchcolor = General.Colors.Highlight; PixelColor losecolor = General.Colors.Selection; snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; DrawnVertex curp = GetCurrentPosition(); float vsize = (renderer.VertexSize + 1.0f) / renderer.Scale; // Render drawing lines if(renderer.StartOverlay(true)) { PixelColor color = snaptonearest ? stitchcolor : losecolor; if(points.Count == 1) { updateReferencePoints(points[0], curp); List shapes = getShapes(start, end); //render shape foreach (Vector2D[] shape in shapes) { for(int i = 1; i < shape.Length; i++) renderer.RenderLine(shape[i - 1], shape[i], LINE_THICKNESS, color, true); } //vertices foreach (Vector2D[] shape in shapes) { for (int i = 0; i < shape.Length; i++) renderer.RenderRectangleFilled(new RectangleF(shape[i].x - vsize, shape[i].y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); } //and labels Vector2D[] labelCoords = new[] { start, new Vector2D(end.x, start.y), end, new Vector2D(start.x, end.y), start }; for(int i = 1; i < 5; i++) { labels[i - 1].Start = labelCoords[i - 1]; labels[i - 1].End = labelCoords[i]; renderer.RenderText(labels[i - 1].TextLabel); } //render hint if (horizontalSlices > 1 || verticalSlices > 1) { hintLabel.Text = "H: " + (slicesH - 1) + "; V: " + (slicesV - 1); if(width > hintLabel.Text.Length * vsize && height > 16 * vsize) { float vPos = start.y + height / 2.0f; hintLabel.Start = new Vector2D(start.x, vPos); hintLabel.End = new Vector2D(end.x, vPos); renderer.RenderText(hintLabel.TextLabel); } } } else { // Render vertex at cursor renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); } // Done renderer.Finish(); } // Done renderer.Present(); } // This draws a point at a specific location override 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 = true; newpoint.stitchline = stitchline; points.Add(newpoint); if(points.Count == 1) { //add point and labels labels.AddRange(new[] { new LineLengthLabel(false), new LineLengthLabel(false), new LineLengthLabel(false), new LineLengthLabel(false) }); hintLabel = new HintLabel(); Update(); } else if(points[0].pos == points[1].pos) { //nothing is drawn points = new List(); FinishDraw(); } else { //create vertices for final shape. updateReferencePoints(points[0], newpoint); gridpoints = new List(); List shapes = getShapes(start, end); foreach (Vector2D[] shape in shapes) { DrawnVertex[] verts = new DrawnVertex[shape.Length]; for(int i = 0; i < shape.Length; i++) { newpoint = new DrawnVertex(); newpoint.pos = shape[i]; newpoint.stitch = true; newpoint.stitchline = stitchline; verts[i] = newpoint; } gridpoints.Add(verts); } FinishDraw(); } return true; } protected List getShapes(Vector2D s, Vector2D e) { //no shape if(s == e) return new List(); //line if (e.x == s.x || e.y == s.y) return new List{ new[] {s, e} }; //rectangles if(gridlock) { //rendering will be screwed if start point is not aligned to current grid size if (General.Map.Grid.GridSize > initialGridSize && General.Map.Grid.SnappedToGrid(start) == start) { initialGridSize = General.Map.Grid.GridSize; } int gs = (General.Map.Grid.GridSize > initialGridSize && General.Map.Grid.SnappedToGrid(start) != start ? initialGridSize : General.Map.Grid.GridSize); slicesH = width / gs; slicesV = height / gs; } else { slicesH = horizontalSlices; slicesV = verticalSlices; } float rectWidth = Math.Max(1, (slicesH > 0 ? (float)width / slicesH : width)); float rectHeight = Math.Max(1, (slicesV > 0 ? (float)height / slicesV : height)); //create shape List rect = new List { s, new Vector2D((int)e.x, (int)s.y), e, new Vector2D((int)s.x, (int)e.y), s }; if(!gridlock && slicesH == 1 && slicesV == 1) { if(triangulate) rect.AddRange(new[] { s, e }); return new List{ rect.ToArray() }; } //create blocks List shapes = new List { rect.ToArray() }; RectangleF[,] blocks = new RectangleF[slicesH, slicesV]; for(int w = 0; w < slicesH; w++) { for(int h = 0; h < slicesV; h++) { blocks[w, h] = RectangleF.FromLTRB((int)Math.Round(s.x + rectWidth * w), (int)Math.Round(s.y + rectHeight * h), (int)Math.Round(s.x + rectWidth * (w + 1)), (int)Math.Round(s.y + rectHeight * (h + 1))); } } //add subdivisions if(slicesH > 1) { for(int w = 1; w < slicesH; w++) { int px = (int) Math.Round(blocks[w, 0].X); shapes.Add(new[] {new Vector2D(px, s.y), new Vector2D(px, e.y)}); } } if(slicesV > 1) { for(int h = 1; h < slicesV; h++) { int py = (int) Math.Round(blocks[0, h].Y); shapes.Add(new[] { new Vector2D(s.x, py), new Vector2D(e.x, py) }); } } //triangulate? if (triangulate) { bool startflip = false; bool flip = false; for(int w = 0; w < slicesH; w++) { for(int h = 0; h < slicesV; h++) { if (flip) { shapes.Add(new[] { new Vector2D(blocks[w, h].X, blocks[w, h].Y), new Vector2D(blocks[w, h].Right, blocks[w, h].Bottom) }); } else { shapes.Add(new[] { new Vector2D(blocks[w, h].Right, blocks[w, h].Y), new Vector2D(blocks[w, h].X, blocks[w, h].Bottom) }); } flip = !flip; } startflip = !startflip; flip = startflip; } } return shapes; } //update top-left and bottom-right points, which define drawing shape private void updateReferencePoints(DrawnVertex p1, DrawnVertex p2) { if(!p1.pos.IsFinite() || !p2.pos.IsFinite()) return; if(p1.pos.x < p2.pos.x) { start.x = p1.pos.x; end.x = p2.pos.x; } else { start.x = p2.pos.x; end.x = p1.pos.x; } if(p1.pos.y < p2.pos.y) { start.y = p1.pos.y; end.y = p2.pos.y; } else { start.y = p2.pos.y; end.y = p1.pos.y; } width = (int)(end.x - start.x); height = (int)(end.y - start.y); } #endregion #region ================== Actions [BeginAction("increasebevel")] protected void increaseBevel() { if(!gridlock && (points.Count < 2 || horizontalSlices < width - 2)) { horizontalSlices++; panel.HorizontalSlices = horizontalSlices - 1; Update(); } } [BeginAction("decreasebevel")] protected void decreaseBevel() { if(!gridlock && horizontalSlices > 1) { horizontalSlices--; panel.HorizontalSlices = horizontalSlices - 1; Update(); } } [BeginAction("increasesubdivlevel")] protected void increaseSubdivLevel() { if(!gridlock && (points.Count < 2 || verticalSlices < height - 2)) { verticalSlices++; panel.VerticalSlices = verticalSlices - 1; Update(); } } [BeginAction("decreasesubdivlevel")] protected void decreaseSubdivLevel() { if(!gridlock && verticalSlices > 1) { verticalSlices--; panel.VerticalSlices = verticalSlices - 1; Update(); } } #endregion } }