public static void RenderTickLabels(PlotDimensions dims, Graphics gfx, TickCollection tc, Drawing.Font tickFont, Edge edge, float rotation, bool rulerMode, float PixelOffset, float MajorTickLength, float MinorTickLength) { 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) { if (rotation == 0) { 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, brush, format: sf, x: dims.GetPixelX(tc.tickPositionsMajor[i]), y: dims.DataOffsetY + dims.DataHeight + PixelOffset + MajorTickLength); } sf.Alignment = StringAlignment.Far; gfx.DrawString(tc.cornerLabel, font, brush, format: sf, x: dims.DataOffsetX + dims.DataWidth, y: dims.DataOffsetY + dims.DataHeight + MajorTickLength + tc.maxLabelHeight); } else { for (int i = 0; i < tc.tickPositionsMajor.Length; i++) { float x = dims.GetPixelX(tc.tickPositionsMajor[i]); float y = dims.DataOffsetY + dims.DataHeight + MajorTickLength + 3; gfx.TranslateTransform(x, y); gfx.RotateTransform(-rotation); sf.Alignment = StringAlignment.Far; sf.LineAlignment = StringAlignment.Center; gfx.DrawString(tc.tickLabels[i], font, brush, 0, 0, sf); gfx.ResetTransform(); } } } else if (edge == Edge.Top) { sf.Alignment = rulerMode ? StringAlignment.Near : StringAlignment.Center; sf.LineAlignment = StringAlignment.Far; for (int i = 0; i < tc.tickPositionsMajor.Length; i++) { gfx.DrawString(tc.tickLabels[i], font, brush, format: sf, x: dims.GetPixelX(tc.tickPositionsMajor[i]), y: dims.DataOffsetY - PixelOffset - MajorTickLength); } } else if (edge == Edge.Left) { if (rotation == 0) { sf.LineAlignment = rulerMode ? StringAlignment.Far : StringAlignment.Center; sf.Alignment = StringAlignment.Far; for (int i = 0; i < tc.tickPositionsMajor.Length; i++) { gfx.DrawString(tc.tickLabels[i], font, brush, format: sf, x: dims.DataOffsetX - PixelOffset - MajorTickLength, y: dims.GetPixelY(tc.tickPositionsMajor[i])); } sf.LineAlignment = StringAlignment.Far; sf.Alignment = StringAlignment.Near; gfx.DrawString(tc.cornerLabel, font, brush, dims.DataOffsetX, dims.DataOffsetY, sf); } else { for (int i = 0; i < tc.tickPositionsMajor.Length; i++) { float x = dims.DataOffsetX - PixelOffset - MajorTickLength; float y = dims.GetPixelY(tc.tickPositionsMajor[i]); gfx.TranslateTransform(x, y); gfx.RotateTransform(-rotation); sf.Alignment = StringAlignment.Far; sf.LineAlignment = StringAlignment.Center; gfx.DrawString(tc.tickLabels[i], font, brush, 0, 0, sf); gfx.ResetTransform(); } } } else if (edge == Edge.Right) { 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, brush, format: sf, x: dims.DataOffsetX + PixelOffset + MajorTickLength + dims.DataWidth, y: dims.GetPixelY(tc.tickPositionsMajor[i])); } } else { throw new NotImplementedException(); } } }
public static void RenderTickLabels(PlotDimensions dims, Graphics gfx, TickCollection tc, Drawing.Font tickFont, Edge edge, float rotation, bool rulerMode, float PixelOffset, float MajorTickLength, float MinorTickLength) { 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(); Tick[] visibleMajorTicks = tc.GetVisibleMajorTicks(dims); switch (edge) { case Edge.Bottom: for (int i = 0; i < visibleMajorTicks.Length; i++) { float x = dims.GetPixelX(visibleMajorTicks[i].Position); float y = dims.DataOffsetY + dims.DataHeight + MajorTickLength; gfx.TranslateTransform(x, y); gfx.RotateTransform(-rotation); sf.Alignment = rotation == 0 ? StringAlignment.Center : StringAlignment.Far; if (rulerMode) { sf.Alignment = StringAlignment.Near; } sf.LineAlignment = rotation == 0 ? StringAlignment.Near : StringAlignment.Center; gfx.DrawString(visibleMajorTicks[i].Label, font, brush, 0, 0, sf); GDI.ResetTransformPreservingScale(gfx, dims); } break; case Edge.Top: for (int i = 0; i < visibleMajorTicks.Length; i++) { float x = dims.GetPixelX(visibleMajorTicks[i].Position); float y = dims.DataOffsetY - MajorTickLength; gfx.TranslateTransform(x, y); gfx.RotateTransform(-rotation); sf.Alignment = rotation == 0 ? StringAlignment.Center : StringAlignment.Near; if (rulerMode) { sf.Alignment = StringAlignment.Near; } sf.LineAlignment = rotation == 0 ? StringAlignment.Far : StringAlignment.Center; gfx.DrawString(visibleMajorTicks[i].Label, font, brush, 0, 0, sf); GDI.ResetTransformPreservingScale(gfx, dims); } break; case Edge.Left: for (int i = 0; i < visibleMajorTicks.Length; i++) { float x = dims.DataOffsetX - PixelOffset - MajorTickLength; float y = dims.GetPixelY(visibleMajorTicks[i].Position); gfx.TranslateTransform(x, y); gfx.RotateTransform(-rotation); sf.Alignment = StringAlignment.Far; sf.LineAlignment = rulerMode ? StringAlignment.Far : StringAlignment.Center; if (rotation == 90) { sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Far; } gfx.DrawString(visibleMajorTicks[i].Label, font, brush, 0, 0, sf); GDI.ResetTransformPreservingScale(gfx, dims); } break; case Edge.Right: for (int i = 0; i < visibleMajorTicks.Length; i++) { float x = dims.DataOffsetX + PixelOffset + MajorTickLength + dims.DataWidth; float y = dims.GetPixelY(visibleMajorTicks[i].Position); gfx.TranslateTransform(x, y); gfx.RotateTransform(-rotation); sf.Alignment = StringAlignment.Near; sf.LineAlignment = rulerMode ? StringAlignment.Far : StringAlignment.Center; if (rotation == 90) { sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Near; } gfx.DrawString(visibleMajorTicks[i].Label, font, brush, 0, 0, sf); GDI.ResetTransformPreservingScale(gfx, dims); } break; default: throw new NotImplementedException($"unsupported edge type {edge}"); } if (!string.IsNullOrWhiteSpace(tc.CornerLabel)) { switch (edge) { case Edge.Bottom: sf.Alignment = StringAlignment.Far; sf.LineAlignment = StringAlignment.Near; gfx.DrawString(s: "\n" + tc.CornerLabel, x: dims.DataOffsetX + dims.DataWidth, y: dims.DataOffsetY + dims.DataHeight + MajorTickLength, font: font, brush: brush, format: sf); break; case Edge.Left: sf.Alignment = StringAlignment.Near; sf.LineAlignment = StringAlignment.Far; gfx.DrawString(s: "\n" + tc.CornerLabel, x: dims.DataOffsetX, y: dims.DataOffsetY, font: font, brush: brush, format: sf); break; case Edge.Top: throw new NotImplementedException("multiplier and offset notation is not supported for right and top axes"); case Edge.Right: throw new NotImplementedException("multiplier and offset notation is not supported for right and top axes"); default: throw new NotImplementedException($"unsupported edge type {edge}"); } } }