using ScottPlot.Drawing; using ScottPlot.Ticks; using System; using System.Drawing; using System.Linq; namespace ScottPlot.Renderable { static class AxisTicksRender { private static bool EdgeIsVertical(Edge edge) => (edge == Edge.Left || edge == Edge.Right); private static bool EdgeIsHorizontal(Edge edge) => (edge == Edge.Top || edge == Edge.Bottom); public static void RenderGridLines(PlotDimensions dims, Graphics gfx, double[] majorPositions, double[] minorPositions, double[] tickPositionSmajor, LineStyle gridLineStyle, Color gridLineColor, float gridLineWidth, Edge edge) { if (majorPositions is null || majorPositions.Length == 0 || minorPositions is null || minorPositions.Length == 0 || gridLineStyle == LineStyle.None) return; // don't draw grid lines on the last pixel to prevent drawing over the data frame float xEdgeLeft = dims.DataOffsetX + 1; float xEdgeRight = dims.DataOffsetX + dims.DataWidth - 1; float yEdgeTop = dims.DataOffsetY + 1; float yEdgeBottom = dims.DataOffsetY + dims.DataHeight - 1; if (EdgeIsVertical(edge)) { //float x = (edge == Edge.Left) ? dims.DataOffsetX : dims.DataOffsetX + dims.DataWidth; //float x2 = (edge == Edge.Left) ? dims.DataOffsetX + dims.DataWidth : dims.DataOffsetX; var ys = majorPositions.Select(i => dims.GetPixelY(i)).Where(y => yEdgeTop < y && y < yEdgeBottom); if (gridLineStyle != LineStyle.None) using (var pen = GDI.Pen(gridLineColor, gridLineWidth, gridLineStyle)) { //foreach (float y in ys) // gfx.DrawLine(pen, x, y, x2, y); Brush brush = new SolidBrush(gridLineColor); foreach (var itemx in tickPositionSmajor) { foreach (var itemy in minorPositions) { gfx.FillRectangle(brush, dims.GetPixelX(itemx), dims.GetPixelY(itemy), 1, 1); } foreach (float y in ys) { gfx.FillRectangle(brush, dims.GetPixelX(itemx), y, 1, 1); } } } } if (EdgeIsHorizontal(edge)) { //float y = (edge == Edge.Top) ? dims.DataOffsetY : dims.DataOffsetY + dims.DataHeight; //float y2 = (edge == Edge.Top) ? dims.DataOffsetY + dims.DataHeight : dims.DataOffsetY; var xs = majorPositions.Select(i => dims.GetPixelX(i)).Where(x => xEdgeLeft < x && x < xEdgeRight); if (gridLineStyle != LineStyle.None) using (var pen = GDI.Pen(gridLineColor, gridLineWidth, gridLineStyle)) { Brush brush = new SolidBrush(gridLineColor); foreach (var itemy in tickPositionSmajor) { foreach (var itemx in minorPositions) { gfx.FillRectangle(brush, dims.GetPixelX(itemx), dims.GetPixelY(itemy), 1, 1); } foreach (float x in xs) { gfx.FillRectangle(brush, x, dims.GetPixelY(itemy), 1, 1); } } } } } public static void RenderTickMarks(PlotDimensions dims, Graphics gfx, double[] positions,double[] tickPositionsMajor, float tickLength, Color tickColor, Edge edge, float pixelOffset) { if (positions is null || positions.Length == 0) return; if (EdgeIsVertical(edge)) { float x = (edge == Edge.Left) ? dims.DataOffsetX - pixelOffset : dims.DataOffsetX + dims.DataWidth + pixelOffset; float tickDelta = (edge == Edge.Left) ? -tickLength : tickLength; var ys = positions.Select(i => dims.GetPixelY(i)); var tickmajorys = tickPositionsMajor.Select(i => dims.GetPixelY(i)); using (var pen = GDI.Pen(tickColor)) { foreach (float y in ys) { //if (y > dims.DataOffsetY + dims.DataHeight || y < dims.DataOffsetY) // continue; gfx.DrawLine(pen, x, y, x - tickDelta, y); } foreach (var ty in tickmajorys) { gfx.DrawLine(pen, x, ty, x - tickDelta, ty); } } } if (EdgeIsHorizontal(edge)) { float y = (edge == Edge.Top) ? dims.DataOffsetY - pixelOffset : dims.DataOffsetY + dims.DataHeight + pixelOffset; float tickDelta = (edge == Edge.Top) ? -tickLength : tickLength; var xs = positions.Select(i => dims.GetPixelX(i)); var tickmajorxs = tickPositionsMajor.Select(i => dims.GetPixelX(i)); using (var pen = GDI.Pen(tickColor)) { foreach (float x in xs) { //if (x > dims.DataOffsetX + dims.DataWidth || y < dims.DataOffsetX) // continue; gfx.DrawLine(pen, x, y, x, y - tickDelta); } foreach (var tx in tickmajorxs) { gfx.DrawLine(pen, tx, y, tx, y - tickDelta); } } } } /// /// 绘制刻度的label /// /// /// /// /// /// /// /// /// /// /// /// /// public static void RenderTickLabels(PlotDimensions dims, Graphics gfx, Color axisColor, TickCollection tc, Drawing.Font tickFont, Edge edge, float rotation, bool rulerMode, float PixelOffset, float MajorTickLength, float MinorTickLength, bool rectangle) { if (tc.tickLabels is null || tc.tickLabels.Length == 0) return; using (var font = GDI.Font(tickFont)) using (var brush = GDI.Brush(tickFont.Color)) using (var sf = GDI.StringFormat()) { // TODO: Refactor to improve rotated tick label rendering: // 1) rotation should always be assumed // 2) a separate function should translate/rotate/render/reset // 3) all edges should support rotation if (edge == Edge.Bottom)//画在底部 { sf.Alignment = rulerMode ? StringAlignment.Near : StringAlignment.Center; sf.LineAlignment = StringAlignment.Near; for (int i = 0; i < tc.tickPositionsMajor.Length; i++) { if (rectangle) { gfx.FillRectangle(GDI.Brush(Color.Black), x: dims.GetPixelX(tc.tickPositionsMajor[i]) - tc.tickLabels[i].Length * font.Height / 4, y: dims.DataOffsetY + dims.DataHeight - PixelOffset - MajorTickLength - font.Height, width: tc.tickLabels[i].Length * font.Height / 2, height: font.Height + 2); } gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf, x: dims.GetPixelX(tc.tickPositionsMajor[i]), y: dims.DataOffsetY + dims.DataHeight - PixelOffset - MajorTickLength - font.Height); } } else if (edge == Edge.Top)//画在上部 { sf.Alignment = rulerMode ? StringAlignment.Near : StringAlignment.Center; sf.LineAlignment = StringAlignment.Near; for (int i = 0; i < tc.tickPositionsMajor.Length; i++) gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf, x: dims.GetPixelX(tc.tickPositionsMajor[i]), y: dims.DataOffsetY + PixelOffset - MajorTickLength + 10 + font.Height); } else if (edge == Edge.Left)//画在左方 { sf.LineAlignment = rulerMode ? StringAlignment.Far : StringAlignment.Center; sf.Alignment = StringAlignment.Near; for (int i = 0; i < tc.tickPositionsMajor.Length; i++) gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf, x: dims.DataOffsetX + PixelOffset + 40, y: dims.GetPixelY(tc.tickPositionsMajor[i])); } else if (edge == Edge.Right)//画在右方 { sf.LineAlignment = rulerMode ? StringAlignment.Far : StringAlignment.Center; sf.Alignment = StringAlignment.Far; for (int i = 0; i < tc.tickPositionsMajor.Length; i++) { var maxLabelSize = GDI.MeasureString(tc.tickLabels[i].Trim(), tickFont); if (rectangle) { gfx.FillRectangle(GDI.Brush(Color.Black), x: dims.DataOffsetX + dims.DataWidth - PixelOffset - 34 - maxLabelSize.Width, y: dims.GetPixelY(tc.tickPositionsMajor[i]) - font.Height / 2 - 3, width: tc.tickLabels[i].Length * font.Height / 2, height: font.Height + 2); } gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf, x: dims.DataOffsetX - PixelOffset - 30 + dims.DataWidth, y: dims.GetPixelY(tc.tickPositionsMajor[i])); } } else { throw new NotImplementedException(); } } } } }