AxisTicksRender.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. using ScottPlot.Drawing;
  2. using ScottPlot.Ticks;
  3. using System;
  4. using System.Drawing;
  5. using System.Linq;
  6. namespace ScottPlot.Renderable
  7. {
  8. static class AxisTicksRender
  9. {
  10. private static bool EdgeIsVertical(Edge edge) => (edge == Edge.Left || edge == Edge.Right);
  11. private static bool EdgeIsHorizontal(Edge edge) => (edge == Edge.Top || edge == Edge.Bottom);
  12. public static void RenderGridLines(PlotDimensions dims, Graphics gfx, double[] majorPositions, double[] minorPositions, double[] tickPositionSmajor,
  13. LineStyle gridLineStyle, Color gridLineColor, float gridLineWidth, Edge edge)
  14. {
  15. if (majorPositions is null || majorPositions.Length == 0 || minorPositions is null || minorPositions.Length == 0 || gridLineStyle == LineStyle.None)
  16. return;
  17. // don't draw grid lines on the last pixel to prevent drawing over the data frame
  18. float xEdgeLeft = dims.DataOffsetX + 1;
  19. float xEdgeRight = dims.DataOffsetX + dims.DataWidth - 1;
  20. float yEdgeTop = dims.DataOffsetY + 1;
  21. float yEdgeBottom = dims.DataOffsetY + dims.DataHeight - 1;
  22. if (EdgeIsVertical(edge))
  23. {
  24. //float x = (edge == Edge.Left) ? dims.DataOffsetX : dims.DataOffsetX + dims.DataWidth;
  25. //float x2 = (edge == Edge.Left) ? dims.DataOffsetX + dims.DataWidth : dims.DataOffsetX;
  26. var ys = majorPositions.Select(i => dims.GetPixelY(i)).Where(y => yEdgeTop < y && y < yEdgeBottom);
  27. if (gridLineStyle != LineStyle.None)
  28. using (var pen = GDI.Pen(gridLineColor, gridLineWidth, gridLineStyle))
  29. {
  30. //foreach (float y in ys)
  31. // gfx.DrawLine(pen, x, y, x2, y);
  32. Brush brush = new SolidBrush(gridLineColor);
  33. foreach (var itemx in tickPositionSmajor)
  34. {
  35. foreach (var itemy in minorPositions)
  36. {
  37. gfx.FillRectangle(brush, dims.GetPixelX(itemx), dims.GetPixelY(itemy), 1, 1);
  38. }
  39. foreach (float y in ys)
  40. {
  41. gfx.FillRectangle(brush, dims.GetPixelX(itemx), y, 1, 1);
  42. }
  43. }
  44. }
  45. }
  46. if (EdgeIsHorizontal(edge))
  47. {
  48. //float y = (edge == Edge.Top) ? dims.DataOffsetY : dims.DataOffsetY + dims.DataHeight;
  49. //float y2 = (edge == Edge.Top) ? dims.DataOffsetY + dims.DataHeight : dims.DataOffsetY;
  50. var xs = majorPositions.Select(i => dims.GetPixelX(i)).Where(x => xEdgeLeft < x && x < xEdgeRight);
  51. if (gridLineStyle != LineStyle.None)
  52. using (var pen = GDI.Pen(gridLineColor, gridLineWidth, gridLineStyle))
  53. {
  54. Brush brush = new SolidBrush(gridLineColor);
  55. foreach (var itemy in tickPositionSmajor)
  56. {
  57. foreach (var itemx in minorPositions)
  58. {
  59. gfx.FillRectangle(brush, dims.GetPixelX(itemx), dims.GetPixelY(itemy), 1, 1);
  60. }
  61. foreach (float x in xs)
  62. {
  63. gfx.FillRectangle(brush, x, dims.GetPixelY(itemy), 1, 1);
  64. }
  65. }
  66. }
  67. }
  68. }
  69. public static void RenderTickMarks(PlotDimensions dims, Graphics gfx, double[] positions,double[] tickPositionsMajor, float tickLength, Color tickColor, Edge edge, float pixelOffset)
  70. {
  71. if (positions is null || positions.Length == 0)
  72. return;
  73. if (EdgeIsVertical(edge))
  74. {
  75. float x = (edge == Edge.Left) ? dims.DataOffsetX - pixelOffset : dims.DataOffsetX + dims.DataWidth + pixelOffset;
  76. float tickDelta = (edge == Edge.Left) ? -tickLength : tickLength;
  77. var ys = positions.Select(i => dims.GetPixelY(i));
  78. var tickmajorys = tickPositionsMajor.Select(i => dims.GetPixelY(i));
  79. using (var pen = GDI.Pen(tickColor))
  80. {
  81. foreach (float y in ys)
  82. {
  83. //if (y > dims.DataOffsetY + dims.DataHeight || y < dims.DataOffsetY)
  84. // continue;
  85. gfx.DrawLine(pen, x, y, x - tickDelta, y);
  86. }
  87. foreach (var ty in tickmajorys)
  88. {
  89. gfx.DrawLine(pen, x, ty, x - tickDelta, ty);
  90. }
  91. }
  92. }
  93. if (EdgeIsHorizontal(edge))
  94. {
  95. float y = (edge == Edge.Top) ? dims.DataOffsetY - pixelOffset : dims.DataOffsetY + dims.DataHeight + pixelOffset;
  96. float tickDelta = (edge == Edge.Top) ? -tickLength : tickLength;
  97. var xs = positions.Select(i => dims.GetPixelX(i));
  98. var tickmajorxs = tickPositionsMajor.Select(i => dims.GetPixelX(i));
  99. using (var pen = GDI.Pen(tickColor))
  100. {
  101. foreach (float x in xs)
  102. {
  103. //if (x > dims.DataOffsetX + dims.DataWidth || y < dims.DataOffsetX)
  104. // continue;
  105. gfx.DrawLine(pen, x, y, x, y - tickDelta);
  106. }
  107. foreach (var tx in tickmajorxs)
  108. {
  109. gfx.DrawLine(pen, tx, y, tx, y - tickDelta);
  110. }
  111. }
  112. }
  113. }
  114. /// <summary>
  115. /// 绘制刻度的label
  116. /// </summary>
  117. /// <param name="dims"></param>
  118. /// <param name="gfx"></param>
  119. /// <param name="axisColor"></param>
  120. /// <param name="tc"></param>
  121. /// <param name="tickFont"></param>
  122. /// <param name="edge"></param>
  123. /// <param name="rotation"></param>
  124. /// <param name="rulerMode"></param>
  125. /// <param name="PixelOffset"></param>
  126. /// <param name="MajorTickLength"></param>
  127. /// <param name="MinorTickLength"></param>
  128. /// <param name="rectangle"></param>
  129. 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)
  130. {
  131. if (tc.tickLabels is null || tc.tickLabels.Length == 0)
  132. return;
  133. using (var font = GDI.Font(tickFont))
  134. using (var brush = GDI.Brush(tickFont.Color))
  135. using (var sf = GDI.StringFormat())
  136. {
  137. // TODO: Refactor to improve rotated tick label rendering:
  138. // 1) rotation should always be assumed
  139. // 2) a separate function should translate/rotate/render/reset
  140. // 3) all edges should support rotation
  141. if (edge == Edge.Bottom)//画在底部
  142. {
  143. sf.Alignment = rulerMode ? StringAlignment.Near : StringAlignment.Center;
  144. sf.LineAlignment = StringAlignment.Near;
  145. for (int i = 0; i < tc.tickPositionsMajor.Length; i++)
  146. {
  147. if (rectangle)
  148. {
  149. gfx.FillRectangle(GDI.Brush(Color.Black),
  150. x: dims.GetPixelX(tc.tickPositionsMajor[i]) - tc.tickLabels[i].Length * font.Height / 4,
  151. y: dims.DataOffsetY + dims.DataHeight - PixelOffset - MajorTickLength - font.Height,
  152. width: tc.tickLabels[i].Length * font.Height / 2,
  153. height: font.Height + 2);
  154. }
  155. gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf,
  156. x: dims.GetPixelX(tc.tickPositionsMajor[i]),
  157. y: dims.DataOffsetY + dims.DataHeight - PixelOffset - MajorTickLength - font.Height);
  158. }
  159. }
  160. else if (edge == Edge.Top)//画在上部
  161. {
  162. sf.Alignment = rulerMode ? StringAlignment.Near : StringAlignment.Center;
  163. sf.LineAlignment = StringAlignment.Near;
  164. for (int i = 0; i < tc.tickPositionsMajor.Length; i++)
  165. gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf,
  166. x: dims.GetPixelX(tc.tickPositionsMajor[i]),
  167. y: dims.DataOffsetY + PixelOffset - MajorTickLength + 10 + font.Height);
  168. }
  169. else if (edge == Edge.Left)//画在左方
  170. {
  171. sf.LineAlignment = rulerMode ? StringAlignment.Far : StringAlignment.Center;
  172. sf.Alignment = StringAlignment.Near;
  173. for (int i = 0; i < tc.tickPositionsMajor.Length; i++)
  174. gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf,
  175. x: dims.DataOffsetX + PixelOffset + 40,
  176. y: dims.GetPixelY(tc.tickPositionsMajor[i]));
  177. }
  178. else if (edge == Edge.Right)//画在右方
  179. {
  180. sf.LineAlignment = rulerMode ? StringAlignment.Far : StringAlignment.Center;
  181. sf.Alignment = StringAlignment.Far;
  182. for (int i = 0; i < tc.tickPositionsMajor.Length; i++)
  183. {
  184. var maxLabelSize = GDI.MeasureString(tc.tickLabels[i].Trim(), tickFont);
  185. if (rectangle)
  186. {
  187. gfx.FillRectangle(GDI.Brush(Color.Black),
  188. x: dims.DataOffsetX + dims.DataWidth - PixelOffset - 34 - maxLabelSize.Width,
  189. y: dims.GetPixelY(tc.tickPositionsMajor[i]) - font.Height / 2 - 3,
  190. width: tc.tickLabels[i].Length * font.Height / 2,
  191. height: font.Height + 2);
  192. }
  193. gfx.DrawString(tc.tickLabels[i], font, GDI.Brush(axisColor), format: sf,
  194. x: dims.DataOffsetX - PixelOffset - 30 + dims.DataWidth,
  195. y: dims.GetPixelY(tc.tickPositionsMajor[i]));
  196. }
  197. }
  198. else
  199. {
  200. throw new NotImplementedException();
  201. }
  202. }
  203. }
  204. }
  205. }