using PaintDotNet.SystemLayer; using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace PaintDotNet { /// /// 顶部和左侧的标尺控件 /// public sealed class Ruler : UserControl { /// /// 默认单位,英寸 /// private MeasurementUnit measurementUnit = MeasurementUnit.Inch; public MeasurementUnit MeasurementUnit { get { return measurementUnit; } set { if (value != measurementUnit) { measurementUnit = value; Invalidate(); } } } /// /// 默认方向 水平 /// private Orientation orientation = Orientation.Horizontal; [DefaultValue(Orientation.Horizontal)] public Orientation Orientation { get { return orientation; } set { if (orientation != value) { orientation = value; Invalidate(); } } } /// /// 每英寸长度内的像素点数 /// 默认写死的dpi,96没有实际意义 /// 需要从标尺读取 /// private double dpu = 96; [DefaultValue(96.0)] public double Dpu { get { return dpu; } set { if (value != dpu) { dpu = value; Invalidate(); } } } /// /// 封装了缩放功能的结构体 /// private ScaleFactor scaleFactor = ScaleFactor.OneToOne; [Browsable(false)] public ScaleFactor ScaleFactor { get { return scaleFactor; } set { if (scaleFactor != value) { scaleFactor = value; Invalidate(); } } } private float offset = 0; [DefaultValue(0)] public float Offset { get { return offset; } set { if (offset != value) { offset = value; Invalidate(); } } } private float rulerValue = 0.0f; [DefaultValue(0)] public float Value { get { return rulerValue; } set { if (this.rulerValue != value) { float oldStart = this.scaleFactor.ScaleScalar(this.rulerValue - offset) - 1; float oldEnd = this.scaleFactor.ScaleScalar(this.rulerValue + 1 - offset) + 1; RectangleF oldRect; if (this.orientation == Orientation.Horizontal) { oldRect = new RectangleF(oldStart, this.ClientRectangle.Top, oldEnd - oldStart, this.ClientRectangle.Height); } else // if (this.orientation == Orientation.Vertical) { oldRect = new RectangleF(this.ClientRectangle.Left, oldStart, this.ClientRectangle.Width, oldEnd - oldStart); } float newStart = this.scaleFactor.ScaleScalar(value - offset); float newEnd = this.scaleFactor.ScaleScalar(value + 1 - offset); RectangleF newRect; if (this.orientation == Orientation.Horizontal) { newRect = new RectangleF(newStart, this.ClientRectangle.Top, newEnd - newStart, this.ClientRectangle.Height); } else // if (this.orientation == Orientation.Vertical) { newRect = new RectangleF(this.ClientRectangle.Left, newStart, this.ClientRectangle.Width, newEnd - newStart); } this.rulerValue = value; Invalidate(Utility.RoundRectangle(oldRect)); Invalidate(Utility.RoundRectangle(newRect)); } } } private float highlightStart = 0.0f; public float HighlightStart { get { return this.highlightStart; } set { if (this.highlightStart != value) { this.highlightStart = value; Invalidate(); } } } private float highlightLength = 0.0f; public float HighlightLength { get { return this.highlightLength; } set { if (this.highlightLength != value) { this.highlightLength = value; Invalidate(); } } } private bool highlightEnabled = false; public bool HighlightEnabled { get { return this.highlightEnabled; } set { if (this.highlightEnabled != value) { this.highlightEnabled = value; Invalidate(); } } } /// /// 构造方法 /// public Ruler() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); } protected override void OnPaint(PaintEventArgs e) { float valueStart = this.scaleFactor.ScaleScalar(this.rulerValue - offset); float valueEnd = this.scaleFactor.ScaleScalar(this.rulerValue + 1.0f - offset); float highlightStartPx = this.scaleFactor.ScaleScalar(this.highlightStart - offset); float highlightEndPx = this.scaleFactor.ScaleScalar(this.highlightStart + this.highlightLength - offset); RectangleF highlightRect; RectangleF valueRect; if (this.orientation == Orientation.Horizontal) { valueRect = new RectangleF(valueStart, this.ClientRectangle.Top, valueEnd - valueStart, this.ClientRectangle.Height); highlightRect = new RectangleF(highlightStartPx, this.ClientRectangle.Top, highlightEndPx - highlightStartPx, this.ClientRectangle.Height); } else // if (this.orientation == Orientation.Vertical) { valueRect = new RectangleF(this.ClientRectangle.Left, valueStart, this.ClientRectangle.Width, valueEnd - valueStart); highlightRect = new RectangleF(this.ClientRectangle.Left, highlightStartPx, this.ClientRectangle.Width, highlightEndPx - highlightStartPx); } if (!this.highlightEnabled) { highlightRect = RectangleF.Empty; } if (this.orientation == Orientation.Horizontal) { e.Graphics.DrawLine( SystemPens.WindowText, UI.ScaleWidth(15), ClientRectangle.Top, UI.ScaleWidth(15), ClientRectangle.Bottom); string abbStringName = "MeasurementUnit." + this.MeasurementUnit.ToString() + ".Abbreviation"; string abbString = PdnResources.GetString(abbStringName); //e.Graphics.DrawString(abbString, Font, SystemBrushes.WindowText, UI.ScaleWidth(-2), 0); } Region clipRegion = new Region(highlightRect); clipRegion.Xor(valueRect); if (this.orientation == Orientation.Horizontal) { clipRegion.Exclude(new Rectangle(0, 0, UI.ScaleWidth(16), ClientRectangle.Height)); } e.Graphics.SetClip(clipRegion, CombineMode.Replace); DrawRuler(e, true); clipRegion.Xor(this.ClientRectangle); if (this.orientation == Orientation.Horizontal) { clipRegion.Exclude(new Rectangle(0, 0, UI.ScaleWidth(16), ClientRectangle.Height - 1)); } e.Graphics.SetClip(clipRegion, CombineMode.Replace); DrawRuler(e, false); clipRegion.Dispose(); } private static readonly float[] majorDivisors = new float[] { 10.0f, 10.0f, 10.0f }; private int[] GetSubdivs(MeasurementUnit unit) { switch (unit) { case MeasurementUnit.Inch: { return new int[] { 2 }; } case MeasurementUnit.Mil: { return new int[] { 2 }; } case MeasurementUnit.Centimeter: { return new int[] { 2 }; } case MeasurementUnit.Millimeter: { return new int[] { 2 }; } case MeasurementUnit.Micron: { return new int[] { 2 }; } case MeasurementUnit.Nano: { return new int[] { 2 }; } default: { return null; } } } /// /// 分割x轴 /// /// /// /// /// /// /// /// /// private void SubdivideX( Graphics g, Pen pen, float x, float delta, int index, float y, float height, int[] subdivs) { g.DrawLine(pen, x, y, x, y + height); if (index > 10) { return; } float div; if (subdivs != null && index >= 0) { div = subdivs[index % subdivs.Length]; } else if (index < 0) { div = majorDivisors[(-index - 1) % majorDivisors.Length]; } else { return; } for (int i = 0; i < div; i++) { if ((delta / div) > 3.5) { SubdivideX(g, pen, x + delta * i / div, delta / div, index + 1, y, height / div + 0.5f, subdivs); } } } /// /// 分割y轴 /// /// /// /// /// /// /// /// /// private void SubdivideY( Graphics g, Pen pen, float y, float delta, int index, float x, float width, int[] subdivs) { g.DrawLine(pen, x, y, x + width, y); if (index > 10) { return; } float div; if (subdivs != null && index >= 0) { div = subdivs[index % subdivs.Length]; } else if (index < 0) { div = majorDivisors[(-index - 1) % majorDivisors.Length]; } else { return; } for (int i = 0; i < div; i++) { if ((delta / div) > 3.5) { SubdivideY(g, pen, y + delta * i / div, delta / div, index + 1, x, width / div + 0.5f, subdivs); } } } /// /// 绘制标尺 /// /// /// private void DrawRuler(PaintEventArgs e, bool highlighted) { Pen pen; Brush cursorBrush; Brush textBrush; StringFormat textFormat = new StringFormat(); int maxPixel; Color cursorColor; /** if (highlighted) { e.Graphics.Clear(SystemColors.Highlight); pen = SystemPens.HighlightText; textBrush = SystemBrushes.HighlightText; cursorColor = SystemColors.Window; } else { e.Graphics.Clear(SystemColors.Window); pen = SystemPens.WindowText; textBrush = SystemBrushes.WindowText; cursorColor = SystemColors.Highlight; }**/ pen = SystemPens.WindowText; textBrush = SystemBrushes.WindowText; cursorColor = SystemColors.Highlight; cursorColor = Color.FromArgb(128, cursorColor); cursorBrush = new SolidBrush(cursorColor); if (orientation == Orientation.Horizontal) { maxPixel = ScaleFactor.UnscaleScalar(ClientRectangle.Width); textFormat.Alignment = StringAlignment.Near; textFormat.LineAlignment = StringAlignment.Far; } else // if (orientation == Orientation.Vertical) { maxPixel = ScaleFactor.UnscaleScalar(ClientRectangle.Height); textFormat.Alignment = StringAlignment.Near; textFormat.LineAlignment = StringAlignment.Near; textFormat.FormatFlags |= StringFormatFlags.DirectionVertical; } float majorSkip = 1f; int majorSkipPower = 0; float majorDivisionLength = (float)dpu; float majorDivisionPixels = (float)ScaleFactor.ScaleScalar(majorDivisionLength); int[] subdivs = GetSubdivs(measurementUnit); float offsetPixels = ScaleFactor.ScaleScalar((float)offset); int startMajor = (int)(offset / majorDivisionLength) - 1; int endMajor = (int)((offset + maxPixel) / majorDivisionLength) + 1; if (orientation == Orientation.Horizontal) { // draw Value if (!highlighted) { PointF pt = scaleFactor.ScalePointJustX(new PointF(ClientRectangle.Left + Value - Offset, ClientRectangle.Top)); SizeF size = new SizeF(Math.Max(1, scaleFactor.ScaleScalar(1.0f)), ClientRectangle.Height); pt.X -= 0.5f; CompositingMode oldCM = e.Graphics.CompositingMode; e.Graphics.CompositingMode = CompositingMode.SourceOver; e.Graphics.FillRectangle(cursorBrush, new RectangleF(pt, size)); e.Graphics.CompositingMode = oldCM; } // draw border e.Graphics.DrawLine(SystemPens.WindowText, new Point(ClientRectangle.Left, ClientRectangle.Bottom - 1), new Point(ClientRectangle.Right - 1, ClientRectangle.Bottom - 1)); } else if (orientation == Orientation.Vertical) { // draw Value if (!highlighted) { PointF pt = scaleFactor.ScalePointJustY(new PointF(ClientRectangle.Left, ClientRectangle.Top + Value - Offset)); SizeF size = new SizeF(ClientRectangle.Width, Math.Max(1, scaleFactor.ScaleScalar(1.0f))); pt.Y -= 0.5f; CompositingMode oldCM = e.Graphics.CompositingMode; e.Graphics.CompositingMode = CompositingMode.SourceOver; e.Graphics.FillRectangle(cursorBrush, new RectangleF(pt, size)); e.Graphics.CompositingMode = oldCM; } // draw border e.Graphics.DrawLine(SystemPens.WindowText, new Point(ClientRectangle.Right - 1, ClientRectangle.Top), new Point(ClientRectangle.Right - 1, ClientRectangle.Bottom - 1)); } while (majorDivisionPixels * majorSkip < 60) { majorSkip *= majorDivisors[majorSkipPower % majorDivisors.Length]; ++majorSkipPower; } startMajor = (int)Math.Round((majorSkip * Math.Floor(startMajor / (double)majorSkip))); //decimal skip = Math.Round(Convert.ToDecimal(majorSkip), 1, MidpointRounding.AwayFromZero); //if(skip == 0) //{ // skip = Math.Round(Convert.ToDecimal(majorSkip), 4, MidpointRounding.AwayFromZero); //} for (float major = startMajor; major <= endMajor; major += majorSkip)//skip { float majorMarkPos = ((float)major * majorDivisionPixels) - offsetPixels; //string majorText = major.ToString();//(Math.Round(Convert.ToDecimal(major), 4, MidpointRounding.AwayFromZero)).ToString(); if (orientation == Orientation.Horizontal) { SubdivideX(e.Graphics, pen, ClientRectangle.Left + majorMarkPos, majorDivisionPixels * majorSkip, -majorSkipPower, ClientRectangle.Top, ClientRectangle.Height, subdivs); //e.Graphics.DrawString(majorText, Font, textBrush, new PointF(ClientRectangle.Left + majorMarkPos, ClientRectangle.Bottom), textFormat); } else // if (orientation == Orientation.Vertical) { SubdivideY(e.Graphics, pen, ClientRectangle.Top + majorMarkPos, majorDivisionPixels * majorSkip, -majorSkipPower, ClientRectangle.Left, ClientRectangle.Width, subdivs); // e.Graphics.DrawString(majorText, Font, textBrush, new PointF(ClientRectangle.Left, ClientRectangle.Top + majorMarkPos), textFormat); } } // // 以下是绘制文字 // decimal skip = this.SkipNumberCalc(measurementUnit); int k = 0; int f = this.SkipInterval(measurementUnit); for (decimal major = startMajor; major <= endMajor; major += skip)//majorSkip { float majorMarkPos = ((float)major * majorDivisionPixels) - offsetPixels; string majorText = major.ToString();//(Math.Round(Convert.ToDecimal(major), 4, MidpointRounding.AwayFromZero)).ToString(); if (orientation == Orientation.Horizontal) { if(k % f == 0) { e.Graphics.DrawString(majorText, Font, textBrush, new PointF(ClientRectangle.Left + majorMarkPos, ClientRectangle.Bottom), textFormat); } } else // if (orientation == Orientation.Vertical) { if (k % f == 0) { e.Graphics.DrawString(majorText, Font, textBrush, new PointF(ClientRectangle.Left, ClientRectangle.Top + majorMarkPos), textFormat); } } k++; } textFormat.Dispose(); } /// /// 根据不同单位返回不同的间隔长度 /// /// /// private decimal SkipNumberCalc(MeasurementUnit unit) { decimal skip = 100M; if (unit == MeasurementUnit.Inch) { skip = 0.001M; } else if (unit == MeasurementUnit.Mil) { skip = 0.1M;//###Mil } else if (unit == MeasurementUnit.Centimeter) { skip = 0.01M; } else if (unit == MeasurementUnit.Millimeter) { skip = 0.1M; } else if (unit == MeasurementUnit.Micron) { skip = 10M; } else if (unit == MeasurementUnit.Nano) { skip = 10000M; } return skip; } /// /// 不同的缩放比列下,间隔多少单元格绘制文字 /// /// /// private int SkipInterval(MeasurementUnit unit) { int f = 1; if (scaleFactor.Ratio <= 0.05f) { switch (unit) { case MeasurementUnit.Pixel: case MeasurementUnit.Centimeter: case MeasurementUnit.Millimeter: f = 100; break; case MeasurementUnit.Inch: case MeasurementUnit.Mil://###Mil case MeasurementUnit.Micron: case MeasurementUnit.Nano: f = 1000; break; } } else if (scaleFactor.Ratio <= 0.1f) { switch (unit) { case MeasurementUnit.Pixel: case MeasurementUnit.Centimeter: case MeasurementUnit.Millimeter: f = 10; break; case MeasurementUnit.Inch: case MeasurementUnit.Mil://###Mil case MeasurementUnit.Micron: case MeasurementUnit.Nano: f = 100; break; } } else if (scaleFactor.Ratio <= 0.5f) { switch (unit) { case MeasurementUnit.Pixel: case MeasurementUnit.Centimeter: case MeasurementUnit.Millimeter: f = 5; break; case MeasurementUnit.Inch: case MeasurementUnit.Mil://###Mil case MeasurementUnit.Micron: case MeasurementUnit.Nano: f = 50; break; } } else { switch (unit) { case MeasurementUnit.Pixel: case MeasurementUnit.Centimeter: case MeasurementUnit.Millimeter: f = 1; break; case MeasurementUnit.Inch: case MeasurementUnit.Mil://###Mil case MeasurementUnit.Micron: case MeasurementUnit.Nano: f = 10; break; } } return f; } } }