using PaintDotNet.SystemLayer; using System; using System.Drawing; using System.Drawing.Drawing2D; namespace PaintDotNet.Measurement { public class MoveNubRenderer : CanvasControl { private Matrix transform; private float transformAngle; private int alpha; private MoveNubShape shape; public MoveNubShape Shape { get { return this.shape; } set { InvalidateOurself(); this.shape = value; InvalidateOurself(); } } protected override void OnLocationChanging() { InvalidateOurself(); base.OnLocationChanging(); } protected override void OnLocationChanged() { InvalidateOurself(); base.OnLocationChanged(); } protected override void OnSizeChanging() { InvalidateOurself(); base.OnSizeChanging(); } protected override void OnSizeChanged() { InvalidateOurself(); base.OnSizeChanged(); } public Matrix Transform { get { return this.transform.Clone(); } set { InvalidateOurself(); if (value == null) { throw new ArgumentNullException(); } if (this.transform != null) { this.transform.Dispose(); this.transform = null; } this.transform = value.Clone(); this.transformAngle = Utility.GetAngleOfTransform(this.transform); InvalidateOurself(); } } public int Alpha { get { return this.alpha; } set { if (value < 0 || value > 255) { throw new ArgumentOutOfRangeException("value", value, "value must be [0, 255]"); } if (this.alpha != value) { this.alpha = value; InvalidateOurself(); } } } private RectangleF GetOurRectangle() { PointF[] ptFs = new PointF[1] { this.Location }; this.transform.TransformPoints(ptFs); float ratio = (float)Math.Ceiling(1.0 / OwnerList.ScaleFactor.Ratio); float ourWidth = UI.ScaleWidth(this.Size.Width); float ourHeight = UI.ScaleHeight(this.Size.Height); if (!Single.IsNaN(ratio)) { RectangleF rectF = new RectangleF(ptFs[0], new SizeF(0, 0)); rectF.Inflate(ratio * ourWidth, ratio * ourHeight); return rectF; } else { return RectangleF.Empty; } } private void InvalidateOurself() { InvalidateOurself(false); } private void InvalidateOurself(bool force) { if (this.Visible || force) { RectangleF rectF = GetOurRectangle(); Rectangle rect = Utility.RoundRectangle(rectF); rect.Inflate(1, 1); Invalidate(rect); } } public bool IsPointTouching(PointF ptF, bool pad) { RectangleF rectF = GetOurRectangle(); if (pad) { float padding = 2.0f * 1.0f / (float)this.OwnerList.ScaleFactor.Ratio; rectF.Inflate(padding + 1.0f, padding + 1.0f); } return rectF.Contains(ptF); } public bool IsPointTouching(Point pt, bool pad) { RectangleF rectF = GetOurRectangle(); if (pad) { float padding = 2.0f * 1.0f / (float)this.OwnerList.ScaleFactor.Ratio; rectF.Inflate(padding + 1.0f, padding + 1.0f); } return pt.X >= rectF.Left && pt.Y >= rectF.Top && pt.X < rectF.Right && pt.Y < rectF.Bottom; } protected override void OnVisibleChanged() { InvalidateOurself(true); } protected override void OnRender(Graphics g, Point offset) { lock (this) { float ourSize = UI.ScaleWidth(Math.Min(Width, Height)); g.SmoothingMode = SmoothingMode.AntiAlias; g.TranslateTransform(-offset.X, -offset.Y, MatrixOrder.Append); PointF ptF = (PointF)this.Location; ptF = Utility.TransformOnePoint(this.transform, ptF); ptF.X *= (float)OwnerList.ScaleFactor.Ratio; ptF.Y *= (float)OwnerList.ScaleFactor.Ratio; PointF[] pts = new PointF[8] { new PointF(-1, -1), // up+left new PointF(+1, -1), // up+right new PointF(+1, +1), // down+right new PointF(-1, +1), // down+left new PointF(-1, 0), // left new PointF(+1, 0), // right new PointF(0, -1), // up new PointF(0, +1) // down }; Utility.RotateVectors(pts, this.transformAngle); Utility.NormalizeVectors(pts); using (Pen white = new Pen(Color.FromArgb(this.alpha, Color.White), -1.0f), black = new Pen(Color.FromArgb(this.alpha, Color.Black), -1.0f)) { PixelOffsetMode oldPOM = g.PixelOffsetMode; g.PixelOffsetMode = PixelOffsetMode.None; if (this.shape != MoveNubShape.Circle) { PointF[] outer = new PointF[4] { Utility.AddVectors(ptF, Utility.MultiplyVector(pts[0], ourSize)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[1], ourSize)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[2], ourSize)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[3], ourSize)) }; PointF[] middle = new PointF[4] { Utility.AddVectors(ptF, Utility.MultiplyVector(pts[0], ourSize - 1)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[1], ourSize - 1)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[2], ourSize - 1)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[3], ourSize - 1)) }; PointF[] inner = new PointF[4] { Utility.AddVectors(ptF, Utility.MultiplyVector(pts[0], ourSize - 2)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[1], ourSize - 2)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[2], ourSize - 2)), Utility.AddVectors(ptF, Utility.MultiplyVector(pts[3], ourSize - 2)) }; g.DrawPolygon(white, outer); g.DrawPolygon(black, middle); g.DrawPolygon(white, inner); } else if (this.shape == MoveNubShape.Circle) { RectangleF rect = new RectangleF(ptF, new SizeF(0, 0)); rect.Inflate(ourSize - 1, ourSize - 1); g.DrawEllipse(white, rect); rect.Inflate(-1.0f, -1.0f); g.DrawEllipse(black, rect); rect.Inflate(-1.0f, -1.0f); g.DrawEllipse(white, rect); } if (this.shape == MoveNubShape.Compass) { black.SetLineCap(LineCap.Round, LineCap.DiamondAnchor, DashCap.Flat); black.EndCap = LineCap.ArrowAnchor; black.StartCap = LineCap.ArrowAnchor; white.SetLineCap(LineCap.Round, LineCap.DiamondAnchor, DashCap.Flat); white.EndCap = LineCap.ArrowAnchor; white.StartCap = LineCap.ArrowAnchor; PointF ul = Utility.AddVectors(ptF, Utility.MultiplyVector(pts[0], ourSize - 1)); PointF ur = Utility.AddVectors(ptF, Utility.MultiplyVector(pts[1], ourSize - 1)); PointF lr = Utility.AddVectors(ptF, Utility.MultiplyVector(pts[2], ourSize - 1)); PointF ll = Utility.AddVectors(ptF, Utility.MultiplyVector(pts[3], ourSize - 1)); PointF top = Utility.MultiplyVector(Utility.AddVectors(ul, ur), 0.5f); PointF left = Utility.MultiplyVector(Utility.AddVectors(ul, ll), 0.5f); PointF right = Utility.MultiplyVector(Utility.AddVectors(ur, lr), 0.5f); PointF bottom = Utility.MultiplyVector(Utility.AddVectors(ll, lr), 0.5f); using (SolidBrush whiteBrush = new SolidBrush(white.Color)) { PointF[] poly = new PointF[] { ul, ur, lr, ll }; g.FillPolygon(whiteBrush, poly, FillMode.Winding); } g.DrawLine(black, top, bottom); g.DrawLine(black, left, right); } g.PixelOffsetMode = oldPOM; } } } public MoveNubRenderer(SurfaceBoxRendererList ownerList) : base(ownerList) { this.shape = MoveNubShape.Square; this.transform = new Matrix(); this.transform.Reset(); this.alpha = 255; Size = new SizeF(5, 5); } } }