PumpRenderer.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. using Avalonia;
  2. using Avalonia.Media;
  3. using System;
  4. using System.Globalization;
  5. namespace YZWater.Avalonia.Controls.Renderers;
  6. /// <summary>
  7. /// 泵渲染器 - 绘制圆形泵体 + 旋转叶片 + 状态光晕。
  8. /// 抽取自 PumpStation2DControl,供多个控件复用。
  9. /// </summary>
  10. public static class PumpRenderer
  11. {
  12. /// <summary>
  13. /// 绘制一个泵。
  14. /// </summary>
  15. /// <param name="context">绘图上下文</param>
  16. /// <param name="center">泵体中心(屏幕坐标)</param>
  17. /// <param name="radius">泵体半径</param>
  18. /// <param name="isRunning">是否运行中</param>
  19. /// <param name="statusColor">状态颜色(运行=Success色, 停止=Disabled色)</param>
  20. /// <param name="rotationAngle">当前叶片旋转角度(度)</param>
  21. /// <param name="text">标签文字(如 P1)</param>
  22. /// <param name="bgBrush">泵体背景画刷</param>
  23. /// <param name="borderBrush">边框画刷</param>
  24. /// <param name="textBrush">文字画刷</param>
  25. /// <param name="bladeCount">叶片数量</param>
  26. /// <param name="bladePenThickness">叶片画笔粗细</param>
  27. public static void Draw(
  28. DrawingContext context,
  29. Point center,
  30. double radius,
  31. bool isRunning,
  32. Color statusColor,
  33. double rotationAngle,
  34. string text,
  35. IBrush bgBrush,
  36. IBrush borderBrush,
  37. IBrush textBrush,
  38. int bladeCount = 4,
  39. double bladePenThickness = 2.5)
  40. {
  41. var cx = center.X;
  42. var cy = center.Y;
  43. var statusBrush = new SolidColorBrush(statusColor);
  44. // 运行光晕
  45. if (isRunning)
  46. context.DrawEllipse(new SolidColorBrush(statusColor, 0.18), null,
  47. new Rect(cx - radius * 1.4, cy - radius * 1.4, radius * 2.8, radius * 2.8));
  48. // 泵体圆
  49. context.DrawEllipse(bgBrush, new Pen(statusBrush, isRunning ? 2 : 1.2),
  50. new Rect(cx - radius, cy - radius, radius * 2, radius * 2));
  51. // 旋转叶片
  52. for (int i = 0; i < bladeCount; i++)
  53. {
  54. var angle = (isRunning ? rotationAngle : 0) + i * (360.0 / bladeCount);
  55. var rad = angle * Math.PI / 180.0;
  56. var bx = cx + Math.Cos(rad) * radius * 0.65;
  57. var by = cy + Math.Sin(rad) * radius * 0.65;
  58. var bladeBrush = new SolidColorBrush(statusColor, isRunning ? 0.9 : 0.45);
  59. context.DrawLine(new Pen(bladeBrush, bladePenThickness),
  60. new Point(cx, cy), new Point(bx, by));
  61. }
  62. // 中心圆
  63. context.DrawEllipse(statusBrush, null,
  64. new Rect(cx - radius * 0.22, cy - radius * 0.22, radius * 0.44, radius * 0.44));
  65. // 标签
  66. if (!string.IsNullOrEmpty(text))
  67. {
  68. var fontSize = Math.Max(8, radius * 0.56);
  69. var formatted = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
  70. new Typeface("Microsoft YaHei", FontStyle.Normal, FontWeight.Bold), fontSize, textBrush);
  71. context.DrawText(formatted, new Point(cx - formatted.Width / 2, cy + radius + 3));
  72. }
  73. }
  74. // ════════════════════════════════════════════════════════
  75. // 立式泵(拟真)
  76. // ════════════════════════════════════════════════════════
  77. /// <summary>
  78. /// 绘制潜水排污泵(仿污水泵站现场):顶部电机仓 + 吊环 + 泵壳圆筒(叶轮观察窗旋转)
  79. /// + 底部进水喇叭口 + 自动耦合导轨。头顶一根出水管直通总管,泵体沉入集水池液面。
  80. /// </summary>
  81. /// <param name="context">绘图上下文</param>
  82. /// <param name="voluteCenter">泵壳筒体中心(屏幕坐标)</param>
  83. /// <param name="radius">筒体半径</param>
  84. /// <param name="isRunning">运行中(叶轮旋转 + 光晕)</param>
  85. /// <param name="statusColor">状态色(运行绿 / 停止灰 / 故障红)</param>
  86. /// <param name="rotationAngle">叶轮角度(度)</param>
  87. /// <param name="text">标签(泵名)</param>
  88. /// <param name="borderBrush">边框画刷</param>
  89. /// <param name="textBrush">文字画刷</param>
  90. /// <param name="faultFlashing">故障闪烁相位 0~1(&lt;0 表示无故障)</param>
  91. public static void DrawVertical(
  92. DrawingContext context,
  93. Point voluteCenter,
  94. double radius,
  95. bool isRunning,
  96. Color statusColor,
  97. double rotationAngle,
  98. string text,
  99. IBrush borderBrush,
  100. IBrush textBrush,
  101. double faultFlashing = -1)
  102. {
  103. var cx = voluteCenter.X;
  104. var cy = voluteCenter.Y;
  105. var statusBrush = new SolidColorBrush(statusColor);
  106. // 圆筒金属渐变:左高光 → 中亮 → 右暗(模拟圆柱体光照)
  107. var shellMetal = new LinearGradientBrush
  108. {
  109. StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
  110. EndPoint = new RelativePoint(1, 0, RelativeUnit.Relative),
  111. GradientStops =
  112. {
  113. new GradientStop(Color.FromRgb(198, 206, 216), 0), // 左高光
  114. new GradientStop(Color.FromRgb(150, 160, 174), 0.22),
  115. new GradientStop(Color.FromRgb(120, 130, 144), 0.78),
  116. new GradientStop(Color.FromRgb(88, 96, 110), 1), // 右暗
  117. }
  118. };
  119. var metalDark = new SolidColorBrush(Color.FromRgb(96, 105, 118));
  120. var steel = new SolidColorBrush(Color.FromRgb(112, 122, 136));
  121. var cavity = new SolidColorBrush(Color.FromRgb(52, 60, 72));
  122. // ═══ 纵向尺寸规划(自顶向下)═══
  123. // 顶部出管法兰 cy - 2.55r ~ cy - 2.40r
  124. // 电机仓(顶盖) cy - 2.40r ~ cy - 1.85r
  125. // 泵壳圆筒 cy - 1.85r ~ cy + 1.05r
  126. // 进水喇叭口 cy + 1.05r ~ cy + 1.60r
  127. var flangeTop = cy - radius * 2.55;
  128. var motorTop = cy - radius * 2.40;
  129. var shellTop = cy - radius * 1.85;
  130. var shellBottom = cy + radius * 1.05;
  131. var bellBottom = cy + radius * 1.60;
  132. // ─── 顶部出管法兰(衔接排出竖管)───
  133. var flangeRect = new Rect(cx - radius * 0.42, flangeTop, radius * 0.84, radius * 0.15);
  134. context.DrawRectangle(metalDark, new Pen(borderBrush, 1), flangeRect, 1, 1);
  135. // ─── 电机仓(短粗顶盖 + 散热筋 + 吊环 + 接线盒)───
  136. var motorRect = new Rect(cx - radius * 0.62, motorTop, radius * 1.24, radius * 0.55);
  137. context.DrawRectangle(shellMetal, new Pen(borderBrush, 1.2), motorRect, 3, 3);
  138. // 散热筋(横线)
  139. for (int i = 1; i <= 3; i++)
  140. {
  141. var fy = motorRect.Y + motorRect.Height * i / 4.0;
  142. context.DrawLine(new Pen(metalDark, 1),
  143. new Point(motorRect.X + 3, fy), new Point(motorRect.Right - 3, fy));
  144. }
  145. // 接线盒(顶盖上方小箱,带线管)
  146. var boxRect = new Rect(cx - radius * 0.3, motorTop - radius * 0.22, radius * 0.6, radius * 0.22);
  147. context.DrawRectangle(metalDark, new Pen(borderBrush, 1), boxRect, 2, 2);
  148. // 吊环(接线盒顶,供起吊检修)
  149. var hookY = motorTop - radius * 0.30;
  150. context.DrawEllipse(null, new Pen(metalDark, 1.6),
  151. new Rect(cx - radius * 0.13, hookY - radius * 0.2, radius * 0.26, radius * 0.4));
  152. // ─── 运行光晕(筒体后方,呼吸脉冲)───
  153. if (isRunning)
  154. {
  155. var breathe = 1.5 + Math.Sin((rotationAngle * Math.PI / 180) * 2) * 0.22;
  156. var glowAlpha = (byte)(36 + Math.Sin((rotationAngle * Math.PI / 180) * 2) * 18);
  157. var glowH = (shellBottom - shellTop) * 1.5;
  158. context.DrawEllipse(new SolidColorBrush(Color.FromArgb(glowAlpha, 76, 175, 80)), null,
  159. new Rect(cx - radius * breathe, cy - glowH / 2, radius * 2 * breathe, glowH));
  160. }
  161. // ─── 泵壳圆筒(主体,细长筒体 + 上下盖法兰 + 加强筋)───
  162. context.DrawRectangle(shellMetal, new Pen(statusBrush, isRunning ? 2.4 : 1.4),
  163. new Rect(cx - radius, shellTop, radius * 2, shellBottom - shellTop), 3, 3);
  164. // 顶部/底部加强法兰环
  165. context.DrawRectangle(metalDark, new Pen(borderBrush, 1),
  166. new Rect(cx - radius, shellTop, radius * 2, radius * 0.14), 1, 1);
  167. context.DrawRectangle(metalDark, new Pen(borderBrush, 1),
  168. new Rect(cx - radius, shellBottom - radius * 0.14, radius * 2, radius * 0.14), 1, 1);
  169. // 纵向加强筋(筒体表面 4 条)
  170. for (int i = 1; i <= 4; i++)
  171. {
  172. var fx = cx - radius + radius * 2 * (i / 5.0);
  173. context.DrawLine(new Pen(new SolidColorBrush(Color.FromArgb(120, 200, 210, 220)), 1),
  174. new Point(fx, shellTop + radius * 0.1), new Point(fx, shellBottom - radius * 0.1));
  175. }
  176. // ─── 叶轮观察窗(筒体上部圆形舱窗,透过窗看到旋转叶轮)───
  177. var winCx = cx;
  178. var winCy = cy - radius * 0.45;
  179. var winR = radius * 0.48;
  180. context.DrawEllipse(cavity, new Pen(borderBrush, 1.2),
  181. new Rect(winCx - winR, winCy - winR, winR * 2, winR * 2));
  182. // 窗框高光(体现鼓出的玻璃窗)
  183. context.DrawEllipse(null, new Pen(new SolidColorBrush(Color.FromArgb(70, 220, 230, 240)), 1),
  184. new Rect(winCx - winR * 0.86, winCy - winR * 0.86, winR * 1.72, winR * 1.72));
  185. // 叶轮(运行时旋转,停止时定格 45°)
  186. for (int i = 0; i < 3; i++)
  187. {
  188. var angle = (isRunning ? rotationAngle : 45) + i * 120;
  189. var rad = angle * Math.PI / 180.0;
  190. var bx = winCx + Math.Cos(rad) * winR * 0.72;
  191. var by = winCy + Math.Sin(rad) * winR * 0.72;
  192. var bladeBrush = new SolidColorBrush(isRunning ? Color.FromRgb(120, 220, 130) : Color.FromRgb(150, 160, 172),
  193. isRunning ? 0.95 : 0.6);
  194. context.DrawLine(new Pen(bladeBrush, 2), new Point(winCx, winCy), new Point(bx, by));
  195. }
  196. // 轮毂
  197. context.DrawEllipse(statusBrush, null,
  198. new Rect(winCx - radius * 0.12, winCy - radius * 0.12, radius * 0.24, radius * 0.24));
  199. // ─── 自动耦合导轨(筒体两侧竖直导杆,直通池底)───
  200. var guideBrush = new Pen(steel, Math.Max(1.2, radius * 0.09));
  201. context.DrawLine(guideBrush, new Point(cx - radius * 1.18, shellTop + radius * 0.2),
  202. new Point(cx - radius * 1.18, bellBottom));
  203. context.DrawLine(guideBrush, new Point(cx + radius * 1.18, shellTop + radius * 0.2),
  204. new Point(cx + radius * 1.18, bellBottom));
  205. // ─── 底部进水喇叭口(锥形收口 + 滤网)───
  206. var bell = new StreamGeometry();
  207. using (var g = bell.Open())
  208. {
  209. g.BeginFigure(new Point(cx - radius, shellBottom), true);
  210. g.LineTo(new Point(cx - radius * 0.78, bellBottom), false);
  211. g.LineTo(new Point(cx + radius * 0.78, bellBottom), false);
  212. g.LineTo(new Point(cx + radius, shellBottom), false);
  213. g.EndFigure(true);
  214. }
  215. context.DrawGeometry(shellMetal, new Pen(borderBrush, 1.1), bell);
  216. // 喇叭口箍环 + 滤网格线
  217. context.DrawLine(new Pen(metalDark, 1.2),
  218. new Point(cx - radius * 0.82, bellBottom - radius * 0.12),
  219. new Point(cx + radius * 0.82, bellBottom - radius * 0.12));
  220. for (int i = -2; i <= 2; i++)
  221. {
  222. context.DrawLine(new Pen(new SolidColorBrush(Color.FromArgb(130, 60, 68, 80)), 1),
  223. new Point(cx + i * radius * 0.18, bellBottom - radius * 0.18),
  224. new Point(cx + i * radius * 0.18, bellBottom));
  225. }
  226. // ─── 故障标记(筒体左上方红色三角 + 闪烁)───
  227. if (faultFlashing >= 0)
  228. {
  229. var flash = Math.Sin(faultFlashing * Math.PI * 2) > 0;
  230. if (flash)
  231. {
  232. var triSize = radius * 0.5;
  233. var triTop = cy - radius * 2.9;
  234. var geo = new StreamGeometry();
  235. using (var g = geo.Open())
  236. {
  237. g.BeginFigure(new Point(cx, triTop), true);
  238. g.LineTo(new Point(cx - triSize / 2, triTop + triSize * 0.9), false);
  239. g.LineTo(new Point(cx + triSize / 2, triTop + triSize * 0.9), false);
  240. g.EndFigure(true);
  241. }
  242. context.DrawGeometry(new SolidColorBrush(Color.FromRgb(244, 67, 54)), null, geo);
  243. var exFt = new FormattedText("!", CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
  244. new Typeface("Consolas", FontStyle.Normal, FontWeight.Bold), triSize * 0.8,
  245. Brushes.White);
  246. context.DrawText(exFt, new Point(cx - exFt.Width / 2, triTop + triSize * 0.05));
  247. }
  248. }
  249. // ─── 标签(喇叭口下方)───
  250. if (!string.IsNullOrEmpty(text))
  251. {
  252. var fontSize = Math.Max(8, radius * 0.5);
  253. var formatted = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
  254. new Typeface("Microsoft YaHei", FontStyle.Normal, FontWeight.Bold), fontSize, textBrush);
  255. context.DrawText(formatted, new Point(cx - formatted.Width / 2, bellBottom + 2));
  256. }
  257. }
  258. }