#region Namespaces //Downloaded from //Visual C# Kicks - http://vckicks.110mb.com //The Code Project - http://www.codeproject.com using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using CodeImp.DoomBuilder.Geometry; using System.ComponentModel; #endregion //JBR Loops implementation by me, rest remains untouched namespace CodeImp.DoomBuilder.GZBuilder.Controls { public partial class AngleControl : UserControl { #region Variables private int angle; private int angleoffset; private bool allowLoops = true; //JBR private bool startAtOne = false; //JBR private int turnThreshold = 16; //JBR private Rectangle drawRegion; private const int drawOffset = 2; private const int markScaler = 5; private Point origin; private Point startClick; //JBR private bool doomangleclamping; //UI colors private readonly Color fillColor = SystemColors.Window; private readonly Color fillInactiveColor = SystemColors.Control; private readonly Color outlineColor = SystemColors.WindowFrame; private readonly Color outlineInactiveColor = SystemColors.ControlDarkDark; private readonly Color needleColor = SystemColors.ControlText; private readonly Color needleInactiveColor = SystemColors.ControlDarkDark; private readonly Color marksColor = SystemColors.ActiveBorder; private readonly Color marksInactiveColor = SystemColors.ControlDark; private readonly Color turnTextColor = SystemColors.ControlText; private readonly Color turnTextInactiveColor = SystemColors.ControlDarkDark; #endregion #region Properties public event EventHandler AngleChanged; public int Angle { get { return (angle == NO_ANGLE ? NO_ANGLE : angle - angleoffset); } set { angle = (value == NO_ANGLE ? NO_ANGLE : value + angleoffset); this.Refresh(); } } public int AngleOffset { get { return angleoffset; } set { angleoffset = value; this.Refresh(); } } public bool DoomAngleClamping { get { return doomangleclamping; } set { doomangleclamping = value; } } public const int NO_ANGLE = int.MinValue; [Description("Allow loop changing, setting to false will restore old behaviour.")] [DefaultValue(true)] public bool AllowLoops //JBR { get { return allowLoops; } set { allowLoops = value; if (value) { this.toolTip.SetToolTip(this, "Left-click (and drag) to set snapped angle.\r\nRight-click (and drag) to set precise angle.\r\nMiddle-click (and drag) to set loop number. Hold Shift for larger step size. Hold Ctrl to reset loops."); } else { this.toolTip.SetToolTip(this, "Left-click (and drag) to set snapped angle.\r\nRight-click (and drag) to set precise angle."); } } } [Description("Start at loop 1 instead of loop 0, useful for checkpoint number match in SRB2.")] [DefaultValue(false)] public bool StartAtOne { get { return startAtOne; } set { startAtOne = value; } } //JBR [Description("Drag distance in pixels for user to change the loop number.")] [DefaultValue(16)] public int TurnThreshold { get { return turnThreshold; } set { turnThreshold = value; } } //JBR #endregion public AngleControl() { InitializeComponent(); this.DoubleBuffered = true; } #region Methods private void SetDrawRegion() { drawRegion = new Rectangle(0, 0, this.Width, this.Height); drawRegion.X += 2; drawRegion.Y += 2; drawRegion.Width -= 4; drawRegion.Height -= 4; origin = new Point(drawRegion.Width / 2 + drawOffset, drawRegion.Height / 2 + drawOffset); this.Refresh(); } private static PointF DegreesToXY(float degrees, float radius, Point origin) { PointF xy = new PointF(); float radians = degrees * Angle2D.PI / 180.0f; xy.X = (float)Math.Cos(radians) * radius + origin.X; xy.Y = (float)Math.Sin(-radians) * radius + origin.Y; return xy; } private static int XYToDegrees(Point xy, Point origin) { float xDiff = xy.X - origin.X; float yDiff = xy.Y - origin.Y; return ((int)Math.Round(Math.Atan2(-yDiff, xDiff) * 180.0 / Angle2D.PI) + 360) % 360; } private static int GetNumLoops(int angle) //JBR { if (angle == NO_ANGLE) return 0; if (angle > 0) return angle / 360; return (angle - 359) / 360; } #endregion #region Events private void AngleSelector_Load(object sender, EventArgs e) { SetDrawRegion(); } private void AngleSelector_SizeChanged(object sender, EventArgs e) { this.Height = this.Width; // Keep it there and keep it square! SetDrawRegion(); } protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; Pen outline; Pen needle; Pen marks; SolidBrush fill; Brush center; Brush text; if (this.Enabled) { outline = new Pen(outlineColor, 2.0f); fill = new SolidBrush(fillColor); needle = new Pen(needleColor); center = new SolidBrush(needleColor); marks = new Pen(marksColor); text = new SolidBrush(turnTextColor); } else { outline = new Pen(outlineInactiveColor, 2.0f); fill = new SolidBrush(fillInactiveColor); needle = new Pen(needleInactiveColor); center = new SolidBrush(needleInactiveColor); marks = new Pen(marksInactiveColor); text = new SolidBrush(turnTextInactiveColor); } Rectangle originSquare = new Rectangle(origin.X - 1, origin.Y - 1, 3, 3); //Draw circle g.SmoothingMode = SmoothingMode.AntiAlias; g.DrawEllipse(outline, drawRegion); g.FillEllipse(fill, drawRegion); // Draw angle marks int offset = this.Height / markScaler; for (int i = 0; i < 360; i += 45) { PointF p1 = DegreesToXY(i, origin.X - 6, origin); PointF p2 = DegreesToXY(i, origin.X - offset, origin); g.DrawLine(marks, p1, p2); } //JBR Draw loop number if (allowLoops) { int loop = GetNumLoops(angle); if (startAtOne && loop >= 0) loop++; string loopStr = "↺" + loop.ToString(); string baseAngle = (angle % 360).ToString() + "°"; StringFormat strFormat = new StringFormat(); strFormat.LineAlignment = StringAlignment.Far; strFormat.Alignment = StringAlignment.Far; int hpos = drawRegion.Right; if (loop != (startAtOne ? 1 : 0)) { g.DrawString(loopStr, Font, fill, hpos - 1, drawRegion.Bottom - 1, strFormat); g.DrawString(loopStr, Font, fill, hpos + 1, drawRegion.Bottom - 1, strFormat); g.DrawString(loopStr, Font, fill, hpos - 1, drawRegion.Bottom + 1, strFormat); g.DrawString(loopStr, Font, fill, hpos + 1, drawRegion.Bottom + 1, strFormat); g.DrawString(loopStr, Font, text, hpos, drawRegion.Bottom, strFormat); if (this.Height > 64) { g.DrawString(baseAngle, Font, fill, hpos - 1, drawRegion.Bottom - 13, strFormat); g.DrawString(baseAngle, Font, fill, hpos + 1, drawRegion.Bottom - 13, strFormat); g.DrawString(baseAngle, Font, fill, hpos - 1, drawRegion.Bottom - 11, strFormat); g.DrawString(baseAngle, Font, fill, hpos + 1, drawRegion.Bottom - 11, strFormat); g.DrawString(baseAngle, Font, text, hpos, drawRegion.Bottom - 12, strFormat); } } } // Draw needle if (angle != NO_ANGLE) { PointF anglePoint = DegreesToXY(angle, origin.X - 4, origin); g.DrawLine(needle, origin, anglePoint); } g.SmoothingMode = SmoothingMode.HighSpeed; //Make the square edges sharp g.FillRectangle(center, originSquare); //mxd. Dispose brushes fill.Dispose(); center.Dispose(); outline.Dispose(); marks.Dispose(); needle.Dispose(); base.OnPaint(e); } private void AngleSelector_MouseDown(object sender, MouseEventArgs e) //JBR supports looping { startClick = new Point(e.X, e.Y); int thisAngle = XYToDegrees(startClick, origin); if (e.Button == MouseButtons.Left) { thisAngle = (int)Math.Round(thisAngle / 45f) * 45; if(thisAngle == 360) thisAngle = 0; } if (allowLoops) thisAngle += GetNumLoops(angle) * 360; if (e.Button == MouseButtons.Middle) { if ((ModifierKeys & Keys.Control) == Keys.Control) thisAngle = angle%360 + (angle < 0 ? 360 : 0); else return; } if (thisAngle != angle) { angle = thisAngle; if(!this.DesignMode && AngleChanged != null) AngleChanged(this, EventArgs.Empty); //Raise event this.Refresh(); } } private void AngleSelector_MouseMove(object sender, MouseEventArgs e) //JBR supports looping { if (allowLoops && e.Button == MouseButtons.Middle) { int dist = (e.X - startClick.X) - (e.Y - startClick.Y); if (dist < -turnThreshold || dist >= turnThreshold) { int mult = ((ModifierKeys & Keys.Shift) == Keys.Shift) ? 5 : 1; startClick = new Point(e.X, e.Y); int thisAngle = angle + (360 * mult); if (dist < 0) thisAngle = angle - (360 * mult); if (thisAngle != angle) { angle = thisAngle; if (!this.DesignMode && AngleChanged != null) AngleChanged(this, EventArgs.Empty); //Raise event this.Refresh(); } } } if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right) { startClick = new Point(e.X, e.Y); int thisAngle = XYToDegrees(startClick, origin); if(e.Button == MouseButtons.Left || doomangleclamping) { thisAngle = (int)Math.Round(thisAngle / 45f) * 45; if(thisAngle == 360) thisAngle = 0; } if (allowLoops) thisAngle += GetNumLoops(angle) * 360; if (thisAngle != angle) { angle = thisAngle; if(!this.DesignMode && AngleChanged != null) AngleChanged(this, EventArgs.Empty); //Raise event this.Refresh(); } } } #endregion } }