#region ================== Namespaces using System; using System.Collections.Generic; using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.Geometry; #endregion namespace CodeImp.DoomBuilder.BuilderModes { [EditMode(DisplayName = "Draw Ellipse Mode", SwitchAction = "drawellipsemode", ButtonImage = "DrawEllipseMode.png", //mxd ButtonOrder = int.MinValue + 4, //mxd ButtonGroup = "000_drawing", //mxd AllowCopyPaste = false, Volatile = true, Optional = false)] public class DrawEllipseMode : DrawRectangleMode { #region ================== Variables // Interface private DrawEllipseOptionsPanel panel; // Drawing private float angle; // in radians #endregion #region ================== Constructor public DrawEllipseMode() { autoclosedrawing = false; } #endregion #region ================== Settings panel override protected void SetupInterface() { maxsubdivisions = 512; minsubdivisions = 3; minpointscount = 3; alwaysrendershapehints = true; // Load stored settings subdivisions = General.Clamp(General.Settings.ReadPluginSetting("drawellipsemode.subdivisions", 8), minsubdivisions, maxsubdivisions); bevelwidth = General.Settings.ReadPluginSetting("drawellipsemode.bevelwidth", 0); int angledeg = General.Settings.ReadPluginSetting("drawellipsemode.angle", 0); angle = Angle2D.DegToRad(angledeg); currentbevelwidth = bevelwidth; //Add options docker panel = new DrawEllipseOptionsPanel(); panel.MaxSubdivisions = maxsubdivisions; panel.MinSubdivisions = minsubdivisions; panel.MinSpikiness = (int)General.Map.FormatInterface.MinCoordinate; panel.MaxSpikiness = (int)General.Map.FormatInterface.MaxCoordinate; panel.Spikiness = bevelwidth; panel.Angle = angledeg; panel.Subdivisions = subdivisions; panel.OnValueChanged += OptionsPanelOnValueChanged; panel.OnContinuousDrawingChanged += OnContinuousDrawingChanged; panel.OnShowGuidelinesChanged += OnShowGuidelinesChanged; // Needs to be set after adding the OnContinuousDrawingChanged event... panel.ContinuousDrawing = General.Settings.ReadPluginSetting("drawellipsemode.continuousdrawing", false); panel.ShowGuidelines = General.Settings.ReadPluginSetting("drawellipsemode.showguidelines", false); } override protected void AddInterface() { panel.Register(); } override protected void RemoveInterface() { // Store settings General.Settings.WritePluginSetting("drawellipsemode.subdivisions", subdivisions); General.Settings.WritePluginSetting("drawellipsemode.bevelwidth", bevelwidth); General.Settings.WritePluginSetting("drawellipsemode.angle", panel.Angle); General.Settings.WritePluginSetting("drawellipsemode.continuousdrawing", panel.ContinuousDrawing); General.Settings.WritePluginSetting("drawellipsemode.showguidelines", panel.ShowGuidelines); // Remove the buttons panel.Unregister(); } #endregion #region ================== Methods override protected Vector2D[] GetShape(Vector2D pStart, Vector2D pEnd) { // No shape if(pEnd.x == pStart.x && pEnd.y == pStart.y) return new Vector2D[0]; // Line if(pEnd.x == pStart.x || pEnd.y == pStart.y) return new[] { pStart, pEnd }; // Got shape if(subdivisions < 6) currentbevelwidth = 0; // Works strange otherwise else if(bevelwidth < 0) currentbevelwidth = -Math.Min(Math.Abs(bevelwidth), Math.Min(width, height) / 2) + 1; else currentbevelwidth = bevelwidth; Vector2D[] shape = new Vector2D[subdivisions + 1]; bool doBevel = false; float hw = width / 2.0f; float hh = height / 2.0f; Vector2D center = new Vector2D(pStart.x + hw, pStart.y + hh); float curAngle = angle; float angleStep = -Angle2D.PI / subdivisions * 2; for(int i = 0; i < subdivisions; i++) { float px, py; if(doBevel) { px = (center.x - (float)Math.Sin(curAngle) * (hw + currentbevelwidth)); py = (center.y - (float)Math.Cos(curAngle) * (hh + currentbevelwidth)); } else { px = (center.x - (float)Math.Sin(curAngle) * hw); py = (center.y - (float)Math.Cos(curAngle) * hh); } doBevel = !doBevel; shape[i] = new Vector2D(px, py); curAngle += angleStep; } // Add final point shape[subdivisions] = shape[0]; // Now fit it inside the bounding box float minx = float.MaxValue; float miny = float.MaxValue; float maxx = float.MinValue; float maxy = float.MinValue; // Calculate shape extents foreach(Vector2D v in shape) { if(v.x < minx) minx = v.x; if(v.x > maxx) maxx = v.x; if(v.y < miny) miny = v.y; if(v.y > maxy) maxy = v.y; } // Calculate scalers float scalerx = 1.0f; float scalery = 1.0f; if(minx != pStart.x || maxx != pEnd.x) scalerx = (pEnd.x - pStart.x) / (maxx - minx); if(miny != pStart.y) scalery = (pEnd.y - pStart.y) / (maxy - miny); // Apply scalers if(scalerx != 1.0f || scalery != 1.0f) { for(int i = 0; i < shape.Length; i++) { shape[i].x = ((center.x - shape[i].x) * scalerx + center.x); shape[i].y = ((center.y - shape[i].y) * scalery + center.y); } } // Calculate shape extents again... minx = float.MaxValue; miny = float.MaxValue; foreach(Vector2D v in shape) { if(v.x < minx) minx = v.x; if(v.y < miny) miny = v.y; } // Calculate shape offset... Vector2D offset = new Vector2D(); if(minx != pStart.x) offset.x = pStart.x - minx; if(miny != pStart.y) offset.y = pStart.y - miny; // Apply offset... if(offset.x != 0.0f || offset.y != 0.0f) for(int i = 0; i < shape.Length; i++) shape[i] += offset; // Done return shape; } protected override string GetHintText() { List result = new List(); if(bevelwidth != 0) result.Add("BVL: " + bevelwidth); if(subdivisions != 0) result.Add("VERTS: " + subdivisions); if(panel.Angle != 0) result.Add("ANGLE: " + panel.Angle); return string.Join("; ", result.ToArray()); } #endregion #region ================== Events public override void OnAccept() { switch(points.Count - 1) // Last point matches the first one { case 3: undoname = "Triangle draw"; shapename = "triangle"; break; case 4: undoname = "Rhombus draw"; shapename = "rhombus"; break; case 5: undoname = "Pentagon draw"; shapename = "pentagon"; break; case 6: undoname = "Hexagon draw"; shapename = "hexagon"; break; case 7: undoname = "Heptagon draw"; shapename = "heptagon"; break; case 8: undoname = "Octagon draw"; shapename = "octagon"; break; case 9: undoname = "Enneagon draw"; shapename = "enneagon"; break; case 10: undoname = "Decagon draw"; shapename = "decagon"; break; case 11: undoname = "Hendecagon draw"; shapename = "hendecagon"; break; case 12: undoname = "Dodecagon draw"; shapename = "dodecagon"; break; case 13: undoname = "Tridecagon draw"; shapename = "tridecagon"; break; case 14: undoname = "Tetradecagon draw"; shapename = "tetradecagon"; break; case 15: undoname = "Pentadecagon draw"; shapename = "pentadecagon"; break; case 16: undoname = "Hexadecagon draw"; shapename = "hexadecagon"; break; case 17: undoname = "Heptadecagon draw"; shapename = "heptadecagon"; break; case 18: undoname = "Octadecagon draw"; shapename = "octadecagon"; break; case 19: undoname = "Enneadecagon draw"; shapename = "enneadecagon"; break; case 20: undoname = "Icosagon draw"; shapename = "icosagon"; break; default: undoname = "Ellipse draw"; shapename = "ellipse"; break; } base.OnAccept(); } private void OptionsPanelOnValueChanged(object sender, EventArgs eventArgs) { bevelwidth = panel.Spikiness; subdivisions = Math.Min(maxsubdivisions, panel.Subdivisions); angle = Angle2D.DegToRad(panel.Angle); Update(); } public override void OnHelp() { General.ShowHelp("/gzdb/features/classic_modes/mode_drawellipse.html"); } #endregion #region ================== Actions override protected void IncreaseSubdivLevel() { if(maxsubdivisions - subdivisions > 1) { subdivisions += (subdivisions % 2 != 0 ? 1 : 2); panel.Subdivisions = subdivisions; Update(); } } override protected void DecreaseSubdivLevel() { if(subdivisions - minsubdivisions > 1) { subdivisions -= (subdivisions % 2 != 0 ? 1 : 2); panel.Subdivisions = subdivisions; Update(); } } protected override void IncreaseBevel() { if(points.Count < 2 || currentbevelwidth == bevelwidth || bevelwidth < 0) { bevelwidth = Math.Min(bevelwidth + General.Map.Grid.GridSize, panel.MaxSpikiness); panel.Spikiness = bevelwidth; Update(); } } protected override void DecreaseBevel() { if(bevelwidth > 0 || currentbevelwidth <= bevelwidth + 1) { bevelwidth = Math.Max(bevelwidth - General.Map.Grid.GridSize, panel.MinSpikiness); panel.Spikiness = bevelwidth; Update(); } } [BeginAction("rotateclockwise")] private void IncreaseAngle() { panel.Angle = General.ClampAngle(panel.Angle + 5); angle = Angle2D.DegToRad(panel.Angle); Update(); } [BeginAction("rotatecounterclockwise")] private void DecreaseAngle() { panel.Angle = General.ClampAngle(panel.Angle - 5); angle = Angle2D.DegToRad(panel.Angle); Update(); } #endregion } }