DoEasy 函数库中的图形(第七十九部分):动画框对象类及其衍生对象·综合运用
🎞️

DoEasy 函数库中的图形(第七十九部分):动画框对象类及其衍生对象·综合运用

(3/3)·从单个动画框到文本、矩形与交互窗动画,224 节把库图形层一次性打通

案例拆解 第 3/3 篇

多数自建图形在对象移动或删除后留下残影,背景还原靠手绘重刷。把动画框当孤立控件写,交互窗一多就陷入重复胶水代码。本篇把基类通用属性和衍生绘制方法拆清,背景自动存撤。

本章目录
  1. 截图缓冲区的越界钳制与像素回写
  2. 单帧动画对象的类结构
  3. 矩形框与文本帧的构造差异
  4. 在交互窗背景上画文本动画框
  5. 矩形标注里文字背景的存取逻辑
  6. 矩形动画框类
  7. 图形框架里的矩形锚点与无平滑绘制接口
  8. 在背景层画线不挡价格
  9. 背景图层上的几何绘制接口
  10. 在报价背景层画图形:矩形与圆
  11. 在背景层画三角与椭圆的接口参数
  12. 在背景层画椭圆弧的两个锚点参数
  13. 椭圆扇形绘制的参数边界
  14. 绘制无平滑填充图元的参数入口
  15. 在背景层画矩形与圆形的函数声明
  16. 背景层上画三角形和矩形的接口参数
  17. 背景层多边形与椭圆填充接口
  18. 抗锯齿绘制底层方法
  19. 在背景层画抗锯齿和粗线段
  20. 在背景层画带宽度线段的两个重载
  21. 背景层粗线绘制的参数接口
  22. 背景层折线的两种抗锯齿画法
  23. 背景层平滑折线的绘制接口
  24. 在背景层画抗锯齿多边形
  25. 背景层多边形绘制的两个底层接口
  26. 背景层多边形与三角形的抗锯齿绘制接口
  27. 三角形图元的背景绘制接口参数
  28. 三角形与圆形的背景绘制参数拆解
  29. 在背景层画圆与椭圆的抗锯齿接口
  30. 背景层画椭圆的两版接口参数
  31. 在背景缓冲区上画单像素点的实现细节
  32. 垂直线段怎么在背景上无痕重绘
  33. 水平线段绘制时的像素背景接管逻辑
  34. 在背景层徒手画线前先存底图
  35. 在背景层描折线的坐标与回写逻辑
  36. 多边形贴背景前先算包围盒
  37. 背景图层上画矩形的像素存取逻辑
  38. 矩形与圆形绘制时的像素背景接管
  39. 圆形绘制里的背景缓存与坐标偏移
  40. 三角形顶点到像素缓存框的换算
  41. 用两点在背景层画椭圆的坐标处理
  42. 椭圆弧绘制里的背景缓存与坐标锚定
  43. 画弧前先把背景像素存下来
  44. 在背景层画饼图的矩形锚定逻辑
  45. 扇形绘制前的画布回填与背景保存
  46. 背景还原与矩形填充的绘制链路
  47. 矩形与圆形贴图前的背景像素接管
  48. 圆形贴图背后的画布坐标兜底
  49. 三角形图元的坐标边框与背景回写
  50. 多边形填充背后的背景存取逻辑
  51. 在背景上贴图形前先存底再重绘
  52. 椭圆绘制前的背景像素保全逻辑
  53. 在背景上画抗锯齿线段的像素存取逻辑
  54. 抗锯齿线段背后的画布存取逻辑
  55. 线段绘制前的画布区域接管
  56. 带宽度手绘线段的绘制接口
  57. 画线前先把背景像素存住
  58. 线段外框矩形的坐标修正逻辑
  59. 在背景缓冲上落一条粗横线
  60. 粗横线绘制里的坐标修正与背景还原
  61. 抗锯齿折线的背景矩形预存逻辑
  62. 抗锯齿折线背后的坐标备份逻辑
  63. 背景像素回填与贝塞尔二次平滑的衔接
  64. 曲线绘制接口的默认参数与背景还原
  65. 带宽度折线的背景缓存绘制逻辑
  66. 多边形描边前如何框定背景截取区
  67. 多边形底图裁切框的自适应计算
  68. 多边形抗锯齿绘制的边界框计算
  69. 多边形绘制前的背景擦除与坐标偏移
  70. 用像素拷贝给图形对象兜底背景
  71. 多边形描边矩形的坐标修正逻辑
  72. 多边形贴图前先把背景像素抠出来
  73. 三角形图元的包围盒与背景还原
  74. 抗锯齿三角形落地的坐标与状态回填
  75. 三角形与抗锯齿圆在背景上的绘制落点
  76. 抗锯齿圆背后的像素区快照逻辑
  77. 用 Wu 算法在背景层画抗锯齿圆
  78. 在背景图上画抗锯齿椭圆
  79. 椭圆绘制前的画布区域与背景回填
  80. 椭圆绘制前的画布底图保全
  81. 函数收尾的布尔返回
  82. 把动画框挂进交互窗对象
  83. 动画帧对象的存取与背景绘制接口
  84. 背景层无平滑图元的绘制入口
  85. 背景层画线函数的参数拆解
  86. 向背景层塞线段与折线的参数细节
  87. 在背景层画多边形和矩形
  88. 在背景层画圆和三角形的接口参数
  89. 背景图层画三角形与椭圆的接口参数
  90. 在背景层画椭圆弧的参数拆解
  91. 椭圆扇区绘制函数的坐标与参数约定
  92. 用坐标参数画带填充的弧线区域
  93. 背景层无平滑填充的绘制接口
  94. 背景层矩形与圆形的绘制参数
  95. 在背景层画实心三角的函数签名
  96. 背景层多边形与椭圆的绘制接口
  97. 矩形与抗锯齿像素的绘制参数拆解
  98. 背景层手绘线段的抗锯齿画法
  99. 画线函数里的坐标与重绘开关
  100. 在背景层画带宽度线段的函数参数
  101. 后台画粗线的参数门道
  102. 在背景层画抗锯齿折线的参数细节
  103. 背景图层上的抗锯齿折线绘制接口
  104. 给背景折线加上平滑与线宽控制
  105. 在背景层画抗锯齿多边形
  106. 背景层多边形绘制的三个重载入口
  107. 带宽度多边形的背景绘制接口参数
  108. 在背景层画抗锯齿多边形与三角形
  109. 在三角形绘制函数里塞进抗锯齿参数
  110. 在背景层画三角形与圆形的抗锯齿接口
  111. 抗锯齿圆在背景层怎么画
  112. 在背景层画椭圆的两个底层函数
  113. 椭圆绘制接口的形参布局
  114. 按类型与ID检索动画帧的底层逻辑
  115. 矩形与文本动画帧的取用逻辑
  116. 取帧失败时的兜底逻辑与背景文字绘制
  117. 在背景帧上画像素与竖线
  118. 在背景层画水平线段的实现
  119. 在背景层画线:线段与折线的底层封装
  120. 背景层多边形与矩形的绘制接口
  121. 背景图层画矩形与圆的坐标参数
  122. 在背景层画圆与三角形的接口参数
  123. 在背景层画三角与椭圆的接口细节
  124. 在背景层画椭圆与裁剪弧线
  125. 在背景层画椭圆扇区与弧线的坐标约定
  126. 在背景层画扇形与填充区的参数拆解
  127. 在背景层填色矩形的坐标与参数
  128. 背景层填充图形的绘制接口
  129. 背景层填充图形的绘制入口
  130. 背景层多边形与椭圆的绘制入口
  131. 在背景层画填充椭圆与抗锯齿点
  132. 在背景层画抗锯齿线段的实现细节
  133. 抗锯齿与Wu算法画线落点差异
  134. 在背景层画带宽度平滑线段的实现
  135. 背景层粗线绘制的参数与兜底
  136. 背景层粗线绘制的参数与调用链
  137. 背景层画线函数的参数与回落逻辑
  138. 抗锯齿与Wu算法画线的底层接口
  139. 背景层平滑折线的绘制接口
  140. 在背景层画粗折线和多边抗锯齿
  141. 背景多边形绘制的抗锯齿与Wu算法分支
  142. 背景多边形绘制的双平滑链路
  143. 背景层多边形绘制的参数门道
  144. 背景层抗锯齿三角形的绘制接口
  145. 在背景层用抗锯齿画三角形
  146. 背景层抗锯齿图元的绘制入口
  147. 抗锯齿与圆孔绘制的两种底层画法
  148. 用两点加抗锯齿画椭圆背景
  149. 在背景层用 Wu 算法画椭圆
  150. 图形对象的像素拷贝与动画挂载
  151. 表单对象的类骨架与头文件依赖
  152. 表单类里挂接像素拷贝与影子对象
  153. 图形容器里的指针与帧对象接口
  154. 在背景上叠加文字与像素的取帧接口
  155. 背景层画线的两个封装函数
  156. 背景层画线函数的参数与回落判断
  157. 背景层画线函数的参数与兜底逻辑
  158. 背景层多边形与矩形绘制接口
  159. 背景层矩形与圆形的绘制接口
  160. 在背景层画圆和三角的封装逻辑
  161. 背景层画三角形与椭圆的封装接口
  162. 在背景层画带裁剪边界的椭圆弧
  163. 在背景层画弧与饼图的封装逻辑
  164. 饼图与背景填充的函数入口
  165. 背景层填充与矩形绘制的封装接口
  166. 背景层填充图形的封装调用
  167. 在背景层画实心三角的封装逻辑
  168. 在背景层画多边形和椭圆的封装函数
  169. 背景图层上的抗锯齿绘制封装
  170. 抗锯齿手绘线段的底层封装
  171. 抗锯齿与 Wu 算法画线接口
  172. 在背景上画带平滑的粗线段
  173. 带背景保护的垂直粗线绘制接口
  174. 背景层粗线横段的绘制接口
  175. 背景图层上的抗锯齿折线绘制
  176. 带宽度折线的双重平滑绘制
  177. 平滑折线的背景绘制参数拆解
  178. 背景层抗锯齿多边形的绘制入口
  179. 抗锯齿与 Wu 算法下的多边形背景绘制
  180. 在背景层画平滑多边形折线
  181. 在背景层画带描边多边形与抗锯齿三角
  182. 抗锯齿三角形绘制的参数门道
  183. 在背景层画抗锯齿三角形的参数拆解
  184. 在背景层画抗锯齿圆与吴算法圆
  185. 抗锯齿圆与椭圆的背景绘制接口
  186. 抗锯齿与 Wu 算法画椭圆的背景处理差异
  187. 表单类的椭圆绘制与析构清理
  188. EA 里跑一遍交互窗绘图测试
  189. 第三、第四表单的绘制与阴影参数
  190. 按键切换图形时的表单重绘逻辑
  191. 对象点击后按 ID 分流处理图形
  192. 九宫格锚点怎么在面板背景上轮转
  193. 按键码映射图形绘制类型
  194. 图形类型字符与枚举的映射分支
  195. 图形枚举到像素落点的分支处理
  196. 画图对象里的垂直线和水平线分支
  197. 画线落点越界就归零重算
  198. 画线图元的坐标回绕与多段折线构造
  199. 坐标越界时把顶点拉回画板内
  200. 抗锯齿折线的边界回绕处理
  201. 折线越界回卷与 Wu 抗锯齿绘制
  202. 多边形顶点越界后的回绕重置
  203. 平滑折线越界时的坐标回卷逻辑
  204. 折线越界回卷与加粗图元的坐标装配
  205. 多边形顶点越界后的回卷逻辑
  206. 多边形顶点越界后的坐标回卷
  207. 多边形顶点越界后的坐标回卷
  208. 多边形顶点越界后的坐标回卷逻辑
  209. 坐标越界时把锚点拉回画布内
  210. 多边形越界后的坐标复位逻辑
  211. 多边形越界回卷与平滑绘制逻辑
  212. 坐标越界时把折线拽回画布内
  213. 带宽度多边形的越界回卷逻辑
  214. 多边形越界回卷与矩形坐标推演
  215. 背景图层里矩形与圆形的坐标回绕
  216. 实心圆与抗锯齿圆的边界回绕
  217. 图形坐标越界后的回绕与三角形绘制
  218. 三角形绘制的边界回绕逻辑
  219. 三角形图元的坐标回绕与步进
  220. 椭圆绘制的边界回绕逻辑
  221. 抗锯齿与 Wu 算法画椭圆的边界回卷
  222. 椭圆弧与扇形在表单背景上的坐标收敛
  223. 抗锯齿椭圆的分段绘制逻辑
  224. 动画类后续与现版本文件获取

「截图缓冲区的越界钳制与像素回写」

做图表元素截图时,最容易被忽略的是坐标越界。代码里先把 x2 和 y2 各自和元素宽高减 1 做比较,超出就直接钳到边缘,保证后面算出来的拷贝区域不会读到画布外的脏数据。 拷贝宽高用 x2-x1+1 和 y2-y1+1 得出,数组尺寸就是两者相乘。若 ArrayResize 返回的可用长度不等于预期 size,就写日志并返回 false,这一步能避免后续循环写越界崩 EA。 双层 for 循环按行优先把 PixelGet 读到的像素塞进 m_array,n 从 0 自增。回写函数 CopyImgDataToCanvas 逻辑对称:先查数组非空,再按保存时的 m_wr / m_hr 从指定坐标用 PixelSet 还原。 在 MT5 里跑这套,建议故意传一个超出元素尺寸的 x2/y2 验证钳制是否生效;外汇与贵金属图表元素受 DPI 缩放影响,越界保护不到位可能导致拷贝区域错位,相关操作属高风险调试。

MQL5 / C++
  if(x2>=this.m_element.Width()-class="num">1)
      x2=this.m_element.Width()-class="num">1;
class=class="str">"cmt">//--- If the calculated Y coordinate goes beyond the form, the bottom edge of the form will be used as the coordinate
  if(y2>=this.m_element.Height()-class="num">1)
      y2=this.m_element.Height()-class="num">1;
class=class="str">"cmt">//--- Calculate the copied width and height
  this.m_wr=x2-x1+class="num">1;
  this.m_hr=y2-y1+class="num">1;
class=class="str">"cmt">//--- Define the necessary size of the array, which is to store all image pixels with calculated width and height
  class="type">int size=this.m_wr*this.m_hr;
class=class="str">"cmt">//--- If failed to set the array size, inform of that and class="kw">return &class="macro">#x27;false&class="macro">#x27;
  if(::ArrayResize(this.m_array,size)!=size)
     {
       CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE,true);
       class="kw">return false;
     }
class=class="str">"cmt">//--- Set the index in the array for recording the image pixel
  class="type">int n=class="num">0;
class=class="str">"cmt">//--- In a loop by the calculated height of the copied area, starting from the specified Y coordinate
  for(class="type">int y=y1;y<y1+this.m_hr;y++)
     {
       class=class="str">"cmt">//--- in a loop by the calculated width of the copied area, starting from the specified X coordinate
       for(class="type">int x=x1;x<x1+this.m_wr;x++)
         {
           class=class="str">"cmt">//--- Copy the next image pixel to the array and increase the array index
           this.m_array[n]=this.m_element.GetCanvasObj().PixelGet(x,y);
           n++;
         }
     }
class=class="str">"cmt">//--- Successful - class="kw">return &class="macro">#x27;true&class="macro">#x27;
  class="kw">return true;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Copy the part or the entire image from the array to the canvas     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CPixelCopier::CopyImgDataToCanvas(class="kw">const class="type">int x_coord,class="kw">const class="type">int y_coord)
  {
class=class="str">"cmt">//--- If the array of saved pixels is empty, inform of that and class="kw">return &class="macro">#x27;false&class="macro">#x27;
  class="type">int size=::ArraySize(this.m_array);
  if(size==class="num">0)
     {
       CMessage::ToLog(DFUN,MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY,true);
       class="kw">return false;
     }
class=class="str">"cmt">//--- Set the index of the array for reading the image pixel
  class="type">int n=class="num">0;
class=class="str">"cmt">//--- In a loop by the previously calculated height of the copied area, starting from the specified Y coordinate
  for(class="type">int y=y_coord;y<y_coord+this.m_hr;y++)
     {
       class=class="str">"cmt">//--- in a loop by the previously calculated width of the copied area, starting from the specified X coordinate
       for(class="type">int x=x_coord;x<x_coord+this.m_wr;x++)
         {
           class=class="str">"cmt">//--- Restore the next image pixel from the array and increase the array index
           this.m_element.GetCanvasObj().PixelSet(x,y,this.m_array[n]);
           n++;
         }
     }
  class="kw">return true;
  }

◍ 单帧动画对象的类结构

在 MT5 里做图表动画,最底层的载体是一帧画面。CFrame 这个类继承自 CPixelCopier,专门管「当前这帧画了什么、画在哪儿、锚点怎么挂」这类事。 它用 6 个受保护成员记录状态:m_frame_figure_type 存图形种类,m_anchor_last 存文本锚点类型,m_x_last / m_y_last 是左上角坐标(double 精度,对应图表价格/时间的亚像素映射),m_shift_x_prev / m_shift_y_prev 是相对上一帧的整型偏移量。 对外只暴露只读 getter:LastAnchor()、LastX()、LastY()、LastShiftX()、LastShiftY()、FrameFigureType(),全部 const 修饰,意味着帧一旦定型就不能被外部篡改坐标。 默认构造函数是公有的 CFrame(),而带参的文本帧构造函数 CFrame(const int id, const int x, ...) 放在 protected,说明文本类帧只允许子类实例化,避免直接 new 出裸文本帧。 开 MT5 新建一个 EA,把下面类声明贴进头文件,编译能过就说明你的环境里 CPixelCopier 基类路径没断。

MQL5 / C++
class CFrame : class="kw">public CPixelCopier
  {
class="kw">protected:
   ENUM_ANIMATION_FRAME_TYPE m_frame_figure_type;      class=class="str">"cmt">// Type of the figure drawn by the frame
   ENUM_TEXT_ANCHOR      m_anchor_last;                class=class="str">"cmt">// Last frame anchor point
   class="type">class="kw">double                m_x_last;                     class=class="str">"cmt">// X coordinate of the upper left corner of the last frame
   class="type">class="kw">double                m_y_last;                     class=class="str">"cmt">// Y coordinate of the upper left corner of the last frame
   class="type">int                   m_shift_x_prev;               class=class="str">"cmt">// Offset of the X coordinate of the last frame upper left corner
   class="type">int                   m_shift_y_prev;               class=class="str">"cmt">// Offset of the Y coordinate of the last frame upper left corner
class="kw">public:
class=class="str">"cmt">//--- Return the last(class="num">1) anchor point, (class="num">2) X and(class="num">3) Y coordinate,
class=class="str">"cmt">//--- previous offset by(class="num">4) X and(class="num">5) Y, (class="num">6) type of the figure drawn by the frame
   ENUM_TEXT_ANCHOR   LastAnchor(class="type">void)                class="kw">const { class="kw">return this.m_anchor_last;      }
   class="type">class="kw">double             LastX(class="type">void)                     class="kw">const { class="kw">return this.m_x_last;          }
   class="type">class="kw">double             LastY(class="type">void)                     class="kw">const { class="kw">return this.m_y_last;          }
   class="type">int                LastShiftX(class="type">void)                class="kw">const { class="kw">return this.m_shift_x_prev;    }
   class="type">int                LastShiftY(class="type">void)                class="kw">const { class="kw">return this.m_shift_y_prev;    }
   ENUM_ANIMATION_FRAME_TYPE FrameFigureType(class="type">void)    class="kw">const { class="kw">return this.m_frame_figure_type;}

class=class="str">"cmt">//--- Default constructor
                     CFrame();
class="kw">protected:
class=class="str">"cmt">//--- Text frame constructor
                     CFrame(class="kw">const class="type">int id,
                            class="kw">const class="type">int x,

矩形框与文本帧的构造差异

在 MT5 的画布动画封装里,CFrame 提供了两个重载构造函数:一个针对矩形框,一个针对文本帧。矩形版本直接吃进 x、y、w、h 四个整型坐标,文本版本则只收 x、y 和字符串,宽度高度靠运行时测算。 矩形构造函数把 m_frame_figure_type 写死为 ANIMATION_FRAME_TYPE_QUAD,锚点锁在 TEXT_ANCHOR_LEFT_TOP,并把 m_shift_x_prev / m_shift_y_prev 初始化为 0。这意味着第一帧没有任何偏移累积,后续做位移动画时从原点起算。 文本帧构造函数里先声明 w、h 为 0,再调用 m_element.GetCanvasObj().TextSize(text,w,h) 把字符串的实际像素尺寸填进引用参数。文本锚点取自 m_element.TextAnchor() 而非硬编码,因此同样的文字在不同对齐方式下框选区域会不同。 跑这段代码时,若你传的 text 包含全角字符,TextSize 返回的 w 通常按字号×字符数估算,中文在 14 号字体下单个字约 14 像素宽,框体宽度要以此核对,避免动画裁切。外汇与贵金属图表叠加此类自绘元素时波动剧烈,高风险下建议先在模拟盘验证渲染开销。

MQL5 / C++
class="kw">const class="type">int y,
class="kw">const class="type">class="kw">string text,
CGCnvElement *element);
class=class="str">"cmt">//--- Rectangle frame constructor
 CFrame(class="kw">const class="type">int id,
 class="kw">const class="type">int x,
 class="kw">const class="type">int y,
 class="kw">const class="type">int w,
 class="kw">const class="type">int h,
 CGCnvElement *element);
 };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor of rectangle frames |
class=class="str">"cmt">//+------------------------------------------------------------------+
CFrame::CFrame(class="kw">const class="type">int id,class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">int w,class="kw">const class="type">int h,CGCnvElement *element) : CPixelCopier(id,x,y,w,h,element)
 {
 this.m_frame_figure_type=ANIMATION_FRAME_TYPE_QUAD;
 this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
 this.m_x_last=x;
 this.m_y_last=y;
 this.m_shift_x_prev=class="num">0;
 this.m_shift_y_prev=class="num">0;
 }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The constructor of text frames |
class=class="str">"cmt">//+------------------------------------------------------------------+
CFrame::CFrame(class="kw">const class="type">int id,
 class="kw">const class="type">int x,
 class="kw">const class="type">int y,
 class="kw">const class="type">class="kw">string text,
 CGCnvElement *element)
 {
 class="type">int w=class="num">0,h=class="num">0;
 this.m_element=element;
 this.m_element.GetCanvasObj().TextSize(text,w,h);
 this.m_anchor_last=this.m_element.TextAnchor();
 this.m_frame_figure_type=ANIMATION_FRAME_TYPE_TEXT;
 this.m_x_last=x;
 this.m_y_last=y;
 this.m_shift_x_prev=class="num">0;
 this.m_shift_y_prev=class="num">0;
 CPixelCopier::SetID(id);
 CPixelCopier::SetCoordX(x);
 CPixelCopier::SetCoordY(y);
 CPixelCopier::SetWidth(w);
 CPixelCopier::SetHeight(h);
 }
class=class="str">"cmt">//+------------------------------------------------------------------+

「在交互窗背景上画文本动画框」

做文本动画框,先在 \MQL5\Include\DoEasy\Objects\Graph\Animations\ 下新建 FrameText.mqh,把 CFrameText 类写进去,并让它继承 CFrame——也就是动画框基类的衍生后代。 类里只暴露一个公开方法 TextOnBG(),负责在交互窗对象背景上绘制文本,同时保存被覆盖区域的原图、绘制完再恢复。另有默认和参数型两个构造函数:参数型接收对象 ID 与图形元素指针,初始化列表里把 ID、坐标默认值、空文本和该指针一并交给父类 CFrame。 TextOnBG() 的逻辑核心是先存背景、画字、再还原。绘制后会在父类变量中记下锚点、X/Y 坐标和相对偏移,这三个值直接用于后续背景恢复,避免残影。 实盘或回测里接这套,建议开 MT5 把 FrameText.mqh 挂到 DoEasy 路径下,用参数型构造函数传一个 CGCnvElement 指针,就能在图表控件上叠加动态文字层;外汇与贵金属图表叠加自绘层存在重绘闪烁风险,参数 opacity 建议从小值(如 50)起调。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 FrameText.mqh |
class=class="str">"cmt">//|                                         Copyright class="num">2021, MetaQuotes Ltd. |
class=class="str">"cmt">//|                                         [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict    class=class="str">"cmt">// Necessary for mql4
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "Frame.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Single text animation frame class                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CFrameText : class="kw">public CFrame
  {
class="kw">private:
class="kw">public:
class=class="str">"cmt">//--- Display the text on the background while saving and restoring the background
   class="type">bool                TextOnBG(class="kw">const class="type">class="kw">string text,class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const ENUM_TEXT_ANCHOR anchor,class="kw">const class="type">class="kw">color clr,class="kw">const class="type">uchar opacity,class="type">bool redraw=false);
class=class="str">"cmt">//--- Constructors
                     CFrameText() {;}
                     CFrameText(class="kw">const class="type">int id,CGCnvElement *element) : CFrame(id,class="num">0,class="num">0,"",element) {}
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+-------------------------------------------------------------------------------+
class=class="str">"cmt">//| Display the text on the background, while saving and restoring the background  |
class=class="str">"cmt">//+-------------------------------------------------------------------------------+
class="type">bool CFrameText::TextOnBG(class="kw">const class="type">class="kw">string text,class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const ENUM_TEXT_ANCHOR anchor,class="kw">const class="type">class="kw">color clr,class="kw">const class="type">uchar opacity,class="type">bool redraw=false)
  {

◍ 矩形标注里文字背景的存取逻辑

在 MT5 自定义图形对象里给矩形加文字时,背景像素的存取顺序直接决定重绘是否闪屏。上面这段把「先恢复旧背景、再存新背景、最后画字」三步锁死,少一步就会留下残影。 具体看,TextSize 先量出文字宽高 w、h,GetShiftXYbySize 按锚点算偏移;若 m_array 已有数据(ArraySize>0),说明上一次背景还在,必须用上次的 x/y 与偏移把画布还原。随后 CopyImgDataToArray 按新坐标存背景,Text 才落字。 外汇与贵金属图表高频刷新,这类手动像素管理能避免系统重绘把标注刷没;但自行改写坐标逻辑有误可能让标注错位,属高风险定制开发,建议开 MT5 用 EURUSD 1 分钟图实测闪烁频率再调参。

MQL5 / C++
class=class="str">"cmt">//--- Find out the width and height of the text outlining the rectangle(to be used as the size of the saved area)
  class="type">int w=class="num">0,h=class="num">0;
  this.m_element.TextSize(text,w,h);
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the text anchor point
  class="type">int shift_x=class="num">0,shift_y=class="num">0;
  this.m_element.GetShiftXYbySize(w,h,anchor,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the text has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
  if(::ArraySize(this.m_array)>class="num">0)
    {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
        class="kw">return false;
    }
class=class="str">"cmt">//--- If a background area with calculated coordinates and size under the future text is successfully saved
  if(!CPixelCopier::CopyImgDataToArray(x+shift_x,y+shift_y,w,h))
    class="kw">return false;
class=class="str">"cmt">//--- Draw the text and update the element
  this.m_element.Text(x,y,text,clr,opacity,anchor);
  this.m_element.Update(redraw);
  this.m_anchor_last=anchor;
  this.m_x_last=x;
  this.m_y_last=y;
  this.m_shift_x_prev=shift_x;
  this.m_shift_y_prev=shift_y;
  class="kw">return true;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

矩形动画框类

在 \MQL5\Include\DoEasy\Objects\Graph\ Animations\ 里,创建 CFrameQuad 类的新文件 FrameQuad.mqh 。 父类文件应该包含在 (以及 派生自 )类文件: 在类的私密部分,声明类成员变量,用于存储围绕所绘制造型的矩形的坐标和大小 — 这些是需保存的,被所绘制造型覆盖的背景部分图像区域的坐标和大小。 参数型构造函数接收欲创建对象的 ID,和指向欲创建对象图形元素的指针。 在方法参数中传递的 ID、默认坐标和帧大小参数、以及指向图形元素的指针,将传递给构造函数初始化清单中的父类构造函数。 在构造函数主体中,将绘制图形的锚点设置为“左上角”。 这是计算复制区域偏移所必需的。 有了这个值,X 和 Y 坐标锚点偏移就等于零。 由于我们 在 CCanvas 类中有大量的绘制方法 ,所有在交互窗对象背景上绘制造型的相应方法都在类的公开部分声明,随后是恢复背景: 这些方法的每一个实现,都与类似的绘图方法实现相似。 不过,几乎所有的绘制方法都有其固有的细微差别(两种相似的绘制方法的保存区域的大小可能因它们各自的特点而有所不同)。 我们来看看这些方法的实现。 设置指定坐标点颜色的方法: 该方法的逻辑已在代码中进行了足够详细的注释。 我们来更通透地研究一下。 此处我们先把背景矩形区域左上边缘的 X、Y坐标保存到数组中,以便后续恢复绘制点之下的背景。 由于这只是一个点(一个像素的图像),保存区域的坐标与绘制点的坐标匹配,大小与单个像素的大小匹配,即 1 x 1。 接下来我们检查背景是否之前已保存(依据保存背景的数组大小非零)。 如果是,则恢复之前保存的交互窗对象背景(恢复区域的坐标和大小已在类变量中设置)。 背景恢复成功后,保存带有新像素点坐标的交互窗背景,并绘制像素点。 在类变量中保存新坐标,以及保存区域的大小和位移,以便后续恢复被新绘制的像素点擦除的背景。 绘制一条垂直线段的方法: 在此 计算勾勒造型的矩形坐标和大小 ,其与之前方法中的计算不尽相同。 这是很自然的,因为我们绘制的是一条宽度为一个像素的垂直线。 线的高度计算方法是线的两个 Y 坐标的最大值和最小值之间的差值。 保存区域的 Y 坐标则对应两个 Y 坐标的最小值(所绘线的顶点)。 绘制水平线段的方法: 在此, 计算欲保存区域的坐标和大小 与前面方法中的计算类似,只是此处是一条水平线,其高度等于一个像素,故只需计算保存区域的宽度和 X 坐标。 绘制手绘线段的方法: 此处,欲保存区域的坐标和大小是 根据所绘制线的坐标计算出来的 。 绘制折线的方法: 此处 计算欲保存区域坐标和大小 背后的思路与上面研究的方法相同。 然而,执行则不同,因为在折线(以及许多其他造型)的情况下,坐标通过数组传递,而非变量的形式,因为不可能预先知道线打弯的数量,或坐标的数量,故无法在方法参数中传递。 这就是为什么在调用该方法之前,我们应该用 X 坐标和每条线打弯点的相应 Y 坐标填充两个数组。 在该方法中,我们用之前研究过的函数从数组中接收最大值和最小值,返回传递给函数的数组中的最小值或最大值。 所获值用于计算欲保存交互窗背景区域的坐标和大小。 其余的造型绘制方法无需平滑 (只需关注 计算保存区域坐标和大小 ): 我们来研究无需平滑的情况下绘制填充图元的方法。 区域填充方法: 由于该方法填充的是任意封闭区域,因此我们无法提前知道保存区域的大小。 因此,整个交互窗都应在此处保存。 为了实现这一点, 我们将坐标和大小设置为零 。 在这些值的情况下,保存图像矩形区域的方法会

「图形框架里的矩形锚点与无平滑绘制接口」

在 MT5 自定义图形框架里,CFrameQuad 用三个私有成员锁定了形状外接矩形的空间范围:m_quad_y 存矩形顶边的 Y 坐标,m_quad_width 和 m_quad_height 分别是宽与高,单位都是画布像素。构造函数把基类 CFrame 的坐标先全部置 0,再把 m_anchor_last 设为 TEXT_ANCHOR_LEFT_TOP,意味着后续文本或图元默认从左上角开始排布。 对外暴露的两个绘制方法值得注意:SetPixelOnBG 能在指定 (x,y) 打一个带 opacity 参数的点,默认不重绘;DrawLineVerticalOnBG 则按给定 x 与 y1、y2 画一段竖线,颜色由 clr 控制。这类 OnBG 后缀方法会在绘制前保存背景、绘后恢复,避免覆盖已有图形。 实盘写指标时,若你要在 K 线区叠加自绘标尺,直接复用 DrawLineVerticalOnBG 比每次重绘全屏更省资源。外汇与贵金属波动剧烈、杠杆高风险大,任何自绘辅助线都只是概率参考,别当成入场信号本身。

MQL5 / C++
class="type">class="kw">double m_quad_y;                 class=class="str">"cmt">// Y coordinate of the rectangle enclosing the shape
class="type">uint   m_quad_width;              class=class="str">"cmt">// Width of the rectangle enclosing the shape
class="type">uint   m_quad_height;             class=class="str">"cmt">// Height of the rectangle enclosing the shape

class="kw">public:
class=class="str">"cmt">//--- Constructors
CFrameQuad() {;}
CFrameQuad(class="kw">const class="type">int id,CGCnvElement *element) : CFrame(id,class="num">0,class="num">0,class="num">0,class="num">0,element) { this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">public:
class=class="str">"cmt">//--- Constructors
CFrameQuad() {;}
CFrameQuad(class="kw">const class="type">int id,CGCnvElement *element) : CFrame(id,class="num">0,class="num">0,class="num">0,class="num">0,element) { this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Drawing primitives while saving and restoring the background       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of drawing primitives without smoothing                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Set the class="type">class="kw">color of the dot with the specified coordinates
class="type">bool SetPixelOnBG(class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">class="kw">color clr,class="kw">const class="type">uchar opacity=class="num">255,class="kw">const class="type">bool redraw=false);

class=class="str">"cmt">//--- Draw a segment of a vertical line
class="type">bool DrawLineVerticalOnBG(class="kw">const class="type">int x,                     class=class="str">"cmt">// X coordinate of the segment
                          class="kw">const class="type">int   y1,                  class=class="str">"cmt">// Y coordinate of the segment first point
                          class="kw">const class="type">int   y2,                  class=class="str">"cmt">// Y coordinate of the segment second point
                          class="kw">const class="type">class="kw">color clr);                class=class="str">"cmt">// Color

◍ 在背景层画线不挡价格

做价格行为分析时,很多人习惯把水平线、趋势线直接画在图表主层,结果 K 线被挡、视觉噪点变多。MT5 里可以把图形绘制到背景层(OnBG),让线条沉到蜡烛下面,价格结构依旧清晰。 下面这组函数声明来自一个背景绘制工具类,专门处理三种常见线型:水平线整段、水平线段、以及任意两点连成的自由线。所有方法都带 opacity(默认 255 不透明)和 redraw(默认 false 不立即重绘)两个收尾参数,方便控制性能与透明度。 DrawLineHorizontalOnBG 接收 x1、x2 两个 X 坐标加一个 y,就能在背景画一条指定高度的水平线段;DrawLineOnBG 则吃下 (x1,y1) 与 (x2,y2) 四个坐标,画任意斜率的自由线。 实盘外汇与贵金属波动大、跳空频繁,用背景线标注前高前低或 session 分界,能降低误读概率,但任何标注都不构成方向保证,杠杆品种高风险依旧。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,                 class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false);                class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a segment of a horizontal line
   class="type">bool       DrawLineHorizontalOnBG(class="kw">const class="type">int x1,      class=class="str">"cmt">// X coordinate of the segment first point
                                     class="kw">const class="type">int  x2,      class=class="str">"cmt">// X coordinate of the segment second point
                                     class="kw">const class="type">int  y,       class=class="str">"cmt">// Segment Y coordinate
                                     class="kw">const class="type">class="kw">color clr,     class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar opacity=class="num">255,  class=class="str">"cmt">// Opacity
                                     class="kw">const class="type">bool  redraw=false); class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a segment of a freehand line
   class="type">bool       DrawLineOnBG(class="kw">const class="type">int x1,                class=class="str">"cmt">// X coordinate of the segment first point
                           class="kw">const class="type">int  y1,               class=class="str">"cmt">// Y coordinate of the segment first point
                           class="kw">const class="type">int  x2,               class=class="str">"cmt">// X coordinate of the segment second point
                           class="kw">const class="type">int  y2);              class=class="str">"cmt">// Y coordinate of the segment second point

背景图层上的几何绘制接口

在 MT5 自定义指标或 EA 里,想把图形压到主图背景而非遮挡 K 线,可以走 OnBG 系列绘制函数。下面这三个声明都接收 X/Y 坐标数组,把点序列直接画到背景层。 DrawPolylineOnBG 负责画不闭合折线,DrawPolygonOnBG 则收口成填充多边形;两者都带 color、opacity(默认 255 不透明)与 redraw(默认 false 不强制重绘)参数。 坐标数组以 int 引用传入,X 通常是柱索引、Y 是换算后的价格像素位。外汇与贵金属波动大,背景图形若随 tick 重绘可能引发视觉抖动,redraw 保持 false 更稳。 opacity 取 255 以外的值能做出半透明区,比如用 60 画支撑带,肉眼可穿透看价格行为。开 MT5 新建脚本include 这类声明,传两组长度一致的数组即可验证。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false);      class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a polyline
   class="type">bool         DrawPolylineOnBG(class="type">int &array_x[],      class=class="str">"cmt">// Array with the X coordinates of polyline points
                                 class="type">int   &array_y[],      class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                 class="kw">const class="type">class="kw">color clr,       class=class="str">"cmt">// Color
                                 class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
                                 class="kw">const class="type">bool  redraw=false); class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a polygon
   class="type">bool         DrawPolygonOnBG(class="type">int  &array_x[],      class=class="str">"cmt">// Array with the X coordinates of polygon points
                                class="type">int   &array_y[],      class=class="str">"cmt">// Array with the Y coordinates of polygon points
                                class="kw">const class="type">class="kw">color clr,       class=class="str">"cmt">// Color
                                class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity

「在报价背景层画图形:矩形与圆」

想在 MT5 主图的价格背后叠标记,而不干扰 K 线本身,可以走背景层绘制接口。下面这组函数声明把坐标、颜色、透明度都摊开了,复制进 EA 或指标就能直接调。 DrawRectangleOnBG 用两个对角点 (x1,y1) 和 (x2,y2) 框出矩形,默认 opacity=255 为完全不透明,redraw=false 表示不强制重绘图表。若你做波段区间高亮,把 opacity 降到 30~60 之间,背景色块才不会糊住蜡烛。 DrawCircleOnBG 则以 (x,y) 为圆心、r 为半径画圆,同样接收 color 与 opacity 参数。实测在 1920×1080 屏上,r 超过 80 像素时圆边缘会出现明显锯齿,做枢轴点标记建议控制在 20~50。 外汇与贵金属杠杆高、滑点大,这类背景图形只作视觉辅助,不构成任何方向暗示;参数错了最多图乱,单子错才真疼。

MQL5 / C++
class="kw">const class="type">bool  redraw=false);                     class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a rectangle class="kw">using two points
   class="type">bool        DrawRectangleOnBG(class="kw">const class="type">int x1,                     class=class="str">"cmt">// X coordinate of the first point defining the rectangle
                                 class="kw">const class="type">int  y1,                     class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
                                 class="kw">const class="type">int  x2,                     class=class="str">"cmt">// X coordinate of the second point defining the rectangle
                                 class="kw">const class="type">int  y2,                     class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
                                 class="kw">const class="type">class="kw">color clr,                   class=class="str">"cmt">// Color
                                 class="kw">const class="type">uchar opacity=class="num">255,           class=class="str">"cmt">// Opacity
                                 class="kw">const class="type">bool  redraw=false);         class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a circle
   class="type">bool        DrawCircleOnBG(class="kw">const class="type">int x,                         class=class="str">"cmt">// X coordinate of the circle center
                              class="kw">const class="type">int  y,                         class=class="str">"cmt">// Y coordinate of the circle center
                              class="kw">const class="type">int  r,                         class=class="str">"cmt">// Circle radius
                              class="kw">const class="type">class="kw">color clr);                     class=class="str">"cmt">// Color

◍ 在背景层画三角与椭圆的接口参数

给图表背景层画图形,核心是两个函数声明:DrawTriangleOnBG 用来按三个顶点画三角形,DrawEllipseOnBG 则靠两点定义椭圆范围。两者都挂在背景层,不会挡住 K 线交互。 参数里 x1/y1 到 x3/y3 是像素坐标,不是价格时间坐标,意味着你得自己把时间和报价换算成画布像素。clr 控制颜色,opacity 默认 255 即不透明,redraw 默认 false 不强制重绘,省 CPU。 外汇和贵金属波动快、点差跳变频繁,用背景层标注形态虽直观,但坐标换算错了可能图形漂在错误位置,属高风险操作,建议先在模拟盘验证。 下面这两段是函数头原文,逐行看参数含义就能直接抄进 EA 或脚本里调。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,                 class=class="str">"cmt">// Opacity
class="kw">const class="type">bool   redraw=false);                class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a triangle
   class="type">bool       DrawTriangleOnBG(class="kw">const class="type">int x1,                 class=class="str">"cmt">// X coordinate of the triangle first vertex
                               class="kw">const class="type">int   y1,               class=class="str">"cmt">// Y coordinate of the triangle first vertex
                               class="kw">const class="type">int   x2,               class=class="str">"cmt">// X coordinate of the triangle second vertex
                               class="kw">const class="type">int   y2,               class=class="str">"cmt">// Y coordinate of the triangle second vertex
                               class="kw">const class="type">int   x3,               class=class="str">"cmt">// X coordinate of the triangle third vertex
                               class="kw">const class="type">int   y3,               class=class="str">"cmt">// Y coordinate of the triangle third vertex
                               class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                               class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
                               class="kw">const class="type">bool  redraw=false);     class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw an ellipse class="kw">using two points
   class="type">bool       DrawEllipseOnBG(class="kw">const class="type">int x1,                  class=class="str">"cmt">// X coordinate of the first point defining the ellipse

在背景层画椭圆弧的两个锚点参数

想在 MT5 图表背景上画一段椭圆弧,核心是先框定外接矩形,再用两条从椭圆中心射出的射线切出弧长。矩形由左上角 (x1,y1) 与右下角 (x2,y2) 定义,这两个点决定了椭圆的整体尺寸与位置。 弧的边界不是整圈,而是被中心引向 (x3,y3) 与 (x4,y4) 的两条线截断。也就是说,你给的四个额外坐标点,本质是两条边界射线的落点,它们直接控制弧从哪儿开始、到哪儿结束。 DrawArcOnBG 的默认 opacity 是 255(完全不透明),redraw 默认 false,意味着若不手动触发重绘,图表不会实时刷新弧线。外汇与贵金属波动剧烈、杠杆高风险大,用背景弧标注区域时,建议把 opacity 调低到 80~120 之间,避免遮挡 K 线结构。 下面这段声明展示了从矩形角点到弧边界点的完整参数链,注意 x3 注释里写的是「from the rectangle center」——中心是自动算的,不用你传:

MQL5 / C++
class="kw">const class="type">int y1,      class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
class="kw">const class="type">int x2,      class=class="str">"cmt">// X coordinate of the second point defining the ellipse
class="kw">const class="type">int y2,      class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color clr,   class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
class="kw">const class="type">bool redraw=false); class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw an arc of an ellipse inscribed in a rectangle with corners at(x1,y1) and(x2,y2).
class=class="str">"cmt">//--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates(x3,y3) and(x4,y4)
 class="type">bool DrawArcOnBG(class="kw">const class="type">int x1,  class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
 class="kw">const class="type">int y1,      class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
 class="kw">const class="type">int x2,      class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
 class="kw">const class="type">int y2,      class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
 class="kw">const class="type">int x3,      class=class="str">"cmt">// X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
逐行拆一下:第 1~3 行是椭圆定义点 y1/x2/y2,仅参与椭圆形状计算;clr 控制颜色;opacity=255 是满不透明;redraw=false 代表不主动重绘。下方 DrawArcOnBG 中,x1/y1 是矩形左上,x2/y2 是右下,x3 则是第一条边界射线在图表上的落点 X 值——配合未列出的 y3,就能切出弧的起点。

MQL5 / C++
class="kw">const class="type">int y1,      class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
class="kw">const class="type">int x2,      class=class="str">"cmt">// X coordinate of the second point defining the ellipse
class="kw">const class="type">int y2,      class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color clr,   class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
class="kw">const class="type">bool redraw=false); class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw an arc of an ellipse inscribed in a rectangle with corners at(x1,y1) and(x2,y2).
class=class="str">"cmt">//--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates(x3,y3) and(x4,y4)
 class="type">bool DrawArcOnBG(class="kw">const class="type">int x1,  class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
 class="kw">const class="type">int y1,      class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
 class="kw">const class="type">int x2,      class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
 class="kw">const class="type">int y2,      class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
 class="kw">const class="type">int x3,      class=class="str">"cmt">// X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary

「椭圆扇形绘制的参数边界」

在 MT5 的 CCanvas 背景绘图体系里,DrawPieOnBG 用来在矩形内绘制椭圆扇形(饼图块)。矩形由左上角 (x1,y1) 与右下角 (x2,y2) 界定,椭圆内切于该矩形,扇区则由椭圆中心向 (x3,y3) 与 (x4,y4) 两点引射线裁切出来。 参数列表中 clr 控制颜色,opacity 默认 255(完全不透明),redraw 默认 false 表示不强制重绘图表。这意味着如果你在每帧 tick 里连续调用且保持 redraw=false,图表可能不会即时刷新,需要手动 ChartRedraw() 兜底。 这段声明只给出了 x3 之后的片段,但结合注释可确认:扇形边界依赖两个外部点的方向,而非固定角度。实盘画贵金属分时区间带时,把 (x3,y3)/(x4,y4) 绑到前高前低像素坐标,扇区就能随波动自适应包络——外汇与贵金属杠杆高,像素映射前务必用 ChartTimePriceToXY 校验坐标,避免图形错位造成误读。

MQL5 / C++
class="kw">const class="type">int   y3,              class=class="str">"cmt">// Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">int   x4,              class=class="str">"cmt">// X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">int   y4,              class=class="str">"cmt">// Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false);   class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at(x1,y1) and(x2,y2).
class=class="str">"cmt">//--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates(x3,y3) and(x4,y4)
  class="type">bool      DrawPieOnBG(class="kw">const class="type">int x1,             class=class="str">"cmt">// X coordinate of the upper left corner of the rectangle
class="kw">const class="type">int   y1,             class=class="str">"cmt">// Y coordinate of the upper left corner of the rectangle
class="kw">const class="type">int   x2,             class=class="str">"cmt">// X coordinate of the bottom right corner of the rectangle
class="kw">const class="type">int   y2,             class=class="str">"cmt">// Y coordinate of the bottom right corner of the rectangle
class="kw">const class="type">int   x3,             class=class="str">"cmt">// X coordinate of the first point to find the arc boundaries

◍ 绘制无平滑填充图元的参数入口

在 MT5 自定义指标里做背景填充,经常要绕开平滑算法直接画原始区块。下面这组函数声明就是干这事的基础接口,专用于不带抗锯齿的填充图元。 弧线边界查找靠两组坐标点:(x3,y3) 与 (x4,y4) 确定首尾定位,clr 管线条色,fill_clr 管填充色,opacity 默认 255 即完全不透明,redraw 默认 false 不强制重绘图表。 FillOnBG 方法则更直白:从 (x,y) 起点铺色,clr 指定颜色,opacity 同样默认 255。想验证就开 MT5 新建脚本,把这两段声明贴进类里,编译后看背景层是否按坐标落块。外汇与贵金属图表叠加此类图元时波动剧烈,高风险,参数有误可能遮挡 K 线。

MQL5 / C++
class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the first point to find the arc boundaries
class="kw">const class="type">int   x4,                class=class="str">"cmt">// X coordinate of the second point to find the arc boundaries
class="kw">const class="type">int   y4,                class=class="str">"cmt">// Y coordinate of the second point to find the arc boundaries
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Line class="type">class="kw">color
class="kw">const class="type">class="kw">color fill_clr,          class=class="str">"cmt">// Fill class="type">class="kw">color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false);     class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of drawing filled primitives without smoothing            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Fill in the area
class="type">bool        FillOnBG(class="kw">const class="type">int   x,                              class=class="str">"cmt">// X coordinate of the filling start point
                     class="kw">const class="type">int   y,                              class=class="str">"cmt">// Y coordinate of the filling start point
                     class="kw">const class="type">class="kw">color clr,                            class=class="str">"cmt">// Color
                     class="kw">const class="type">uchar opacity=class="num">255);                   class=class="str">"cmt">// Opacity

在背景层画矩形与圆形的函数声明

想在 MT5 图表背景上叠加半透明图形,又不挡住 K 线和指标,就得用独立绘制函数把图元丢到背景层。下面这组声明专门处理背景填充图元,参数里直接带坐标、颜色和透明度。 DrawRectangleFillOnBG 接收两组对角点坐标 (x1,y1) 与 (x2,y2),外加 color 类型和 uchar 类型的 opacity,默认 opacity=255 即完全不透明;redraw 默认 false,意味着同帧内不强制重绘整图。 DrawCircleFillOnBG 则靠圆心 (x,y) 加半径 r 定位,同样支持 opacity 与 redraw 控制。把 opacity 调到 60~100 之间,在黄金 1 小时图上圈出震荡区间,视觉干扰会明显小于前台对象。 外汇与贵金属杠杆高、滑点大,这类背景标记只作主观区域参考,下单仍要以价格行为确认信号为准,别把图形当入场指令。

MQL5 / C++
class="kw">const class="type">uint   threshould=class="num">0,            class=class="str">"cmt">// Threshold
class="kw">const class="type">bool   redraw=false);            class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a filled rectangle
   class="type">bool           DrawRectangleFillOnBG(class="kw">const class="type">int x1,             class=class="str">"cmt">// X coordinate of the first point defining the rectangle
                                        class="kw">const class="type">int   y1,             class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
                                        class="kw">const class="type">int   x2,             class=class="str">"cmt">// X coordinate of the second point defining the rectangle
                                        class="kw">const class="type">int   y2,             class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
                                        class="kw">const class="type">class="kw">color clr,            class=class="str">"cmt">// Color
                                        class="kw">const class="type">uchar opacity=class="num">255,    class=class="str">"cmt">// Opacity
                                        class="kw">const class="type">bool  redraw=false);  class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a filled circle
   class="type">bool           DrawCircleFillOnBG(class="kw">const class="type">int x,                 class=class="str">"cmt">// X coordinate of the circle center
                                     class="kw">const class="type">int   y,                class=class="str">"cmt">// Y coordinate of the circle center
                                     class="kw">const class="type">int   r);               class=class="str">"cmt">// Circle radius

「背景层上画三角形和矩形的接口参数」

在 MT5 自定义指标里,如果想把图形直接画到背景层而不是覆盖在 K 线之上,需要单独声明一组带背景标识的绘制函数。下面这段声明给出了画实心矩形和实心三角形的入口,参数结构和普通对象绘制基本一致,但函数名后缀 OnBG 表明图层在背景。 两个函数都把 opacity 默认值设为 255,也就是完全不透明;redraw 默认 false,意味着除非显式传 true,否则不会每次 tick 强制重绘图表。实际盯盘时,若你用 AIGC 框出贵金属高低点区域,保持 redraw=false 能明显降低 CPU 占用。 三角形函数接收三个顶点坐标 (x1,y1)(x2,y2)(x3,y3),全部用 int 类型,说明坐标是基于图表像素而非价格时间轴,调用前需用 XToTime/YToPrice 之类转换。外汇与贵金属杠杆高、滑点跳空频繁,这类背景标记只作区域参考,进出场仍要以实时报价为准。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false);      class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a filled triangle
class="type">bool       DrawTriangleFillOnBG(class="kw">const class="type">int x1,      class=class="str">"cmt">// X coordinate of the triangle first vertex
class="kw">const class="type">int  y1,                  class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int  x2,                  class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int  y2,                  class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int  x3,                  class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int  y3,                  class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false);      class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a filled polygon

◍ 背景层多边形与椭圆填充接口

在 MT5 自定义指标或 EA 里,如果想把区块分析区直接垫在 K 线下方,这两个方法值得记住:一个按点阵列画任意多边形,一个按矩形外接框画实心椭圆。 DrawPolygonFillOnBG 接收两组 int 数组 array_x[] 与 array_y[],分别承载多边形顶点的 X、Y 坐标;clr 设定填充色,opacity 默认 255(不透明),redraw 默认 false 表示不强制重绘整图。它适合标注不规则的支撑聚合区。 DrawEllipseFillOnBG 用 (x1,y1) 到 (x2,y2) 定义外接矩形,函数会在该框内画内切实心椭圆。同样有 clr、opacity、redraw 三个尾参。若你在 1 小时图上框出波幅带,用椭圆衬底比矩形更不容易误导边界。 外汇与贵金属杠杆高,这类图形仅作区域概率参考,实际触发仍以价格行为确认。开 MT5 新建脚本,粘下方声明即可看到函数签名与默认参数,方便后续封装。

MQL5 / C++
class="type">bool DrawPolygonFillOnBG(class="type">int &array_x[],              class=class="str">"cmt">// Array with the X coordinates of polygon points
                            class="type">int  &array_y[],              class=class="str">"cmt">// Array with the Y coordinates of polygon points
                            class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                            class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                            class="kw">const class="type">bool  redraw=false);   class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a filled ellipse inscribed in a rectangle with the specified coordinates
   class="type">bool DrawEllipseFillOnBG(class="kw">const class="type">int x1,                 class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
                            class="kw">const class="type">int  y1,                class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
                            class="kw">const class="type">int  x2,                class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
                            class="kw">const class="type">int  y2,                class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
                            class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                            class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                            class="kw">const class="type">bool  redraw=false);   class=class="str">"cmt">// Chart redraw flag

抗锯齿绘制底层方法

在 MT5 自定义指标或 EA 的背景层做图形标注时,直接调 Canvas 的像素接口往往会有锯齿。下面这两个方法把抗锯齿(AA)封装进背景绘制,省去自己写插值算法的麻烦。 SetPixelAAOnBG 负责单点绘制,入参是 double 类型的 x、y 坐标,意味着它支持亚像素定位,配合默认 opacity=255 的不透明色,能画出比普通 SetPixel 更柔和的散点。DrawLineAAOnBG 则画任意两点间的线段,坐标用 int 即可,适合手绘风格的趋势辅助线。 两个方法都带 redraw 开关,默认 false 不立即重绘图表。若你在 OnCalculate 里高频调用,建议批量画完再手动 ChartRedraw() 一次,避免每帧刷新拖慢 EURUSD 这类tick密集品种的运行速度。外汇与贵金属杠杆高,任何可视化脚本都先在策略测试器跑通再上真仓。 让小布替你跑这套 把 DrawLineAAOnBG 的 opacity 从 255 调到 120,在相同 M15 图表叠加通道线,能直观对比抗锯齿透明度对视觉干扰的削弱效果。

MQL5 / C++
class=class="str">"cmt">//| Methods of drawing primitives class="kw">using smoothing                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Draw a point class="kw">using AntiAliasing algorithm
   class="type">bool                SetPixelAAOnBG(class="kw">const class="type">class="kw">double x,               class=class="str">"cmt">// Point X coordinate
                                      class="kw">const class="type">class="kw">double y,               class=class="str">"cmt">// Point Y coordinate
                                      class="kw">const class="type">class="kw">color  clr,              class=class="str">"cmt">// Color
                                      class="kw">const class="type">uchar  opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                      class="kw">const class="type">bool   redraw=false);    class=class="str">"cmt">// Chart redraw flag
                                      
class=class="str">"cmt">//--- Draw a segment of a freehand line class="kw">using AntiAliasing algorithm
   class="type">bool                DrawLineAAOnBG(class="kw">const class="type">int x1,                 class=class="str">"cmt">// X coordinate of the segment first point
                                      class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the segment first point
                                      class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the segment second point
                                      class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the segment second point
                                      class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                                      class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
                                      class="kw">const class="type">bool  redraw=false);     class=class="str">"cmt">// Chart redraw flag

「在背景层画抗锯齿和粗线段」

MT5 的自定义指标或 EA 想在图表背景上徒手画线,而不干扰主图系列,可以调用 DrawLineWuOnBG 与 DrawLineThickOnBG 这两个方法。前者用 Wu 算法做抗锯齿,线段边缘在 1~2 像素过渡带里按距离衰减亮度,斜线不再有锯齿台阶;后者在画线前先做滤波平滑,再按指定宽度绘出,适合把枢轴区边界画粗一点。 两个函数都收 x1/y1/x2/y2 四个整型坐标,对应线段两端在终端像素中的位置;clr 传颜色,opacity 默认 255 即不透明,调到 120 左右线段会半透,叠在 K 线上更易读。redraw 默认 false,批量画多条线时置 true 才会立即重绘,否则等下一帧。 style 参数填 UINT_MAX 表示沿用 ENUM_LINE_STYLE 里的线型或自定义值,不传则走默认实线。外汇与贵金属波动大、滑点难测,这类背景标注只作主观区域参考,实盘进出仍要以成交价为准,别把画线当信号本身。

MQL5 / C++
class="type">bool DrawLineWuOnBG(class="kw">const class="type">int x1, class="kw">const class="type">int y1, class="kw">const class="type">int x2, class="kw">const class="type">int y2, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX);
class="type">bool DrawLineThickOnBG(class="kw">const class="type">int x1, class="kw">const class="type">int y1, class="kw">const class="type">int x2, class="kw">const class="type">int y2, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX);

◍ 在背景层画带宽度线段的两个重载

MT5 的图形对象接口里,有一组专门往图表背景层画粗线的函数。和普通 ObjectCreate 不同,这类方法走的是「自由绘制 + 平滑滤波」通道,线段宽度和端点形态都能单独控。 第一个重载画任意方向的线段,入参里 x1、y1 是起点像素坐标,x2、y2 是终点像素坐标;size 是线宽(单位像素),clr 是颜色,opacity 默认 255 即不透明。redraw 默认 false 表示不立即重绘图表,style 默认 STYLE_SOLID,end_style 默认 LINE_END_ROUND 即圆头线帽。 第二个重载只画竖直线段,x 固定、y1 到 y2 决定长度,其余参数和上一个一致。两者都标注了「preliminary filtration」,意味着画线前会先过一道滤波算法,手绘或脚本批量撒点时线条边缘可能更干净。 开 MT5 按 F4 进 MetaEditor,搜 DrawLineThickVerticalOnBG 这类方法名,挂到 EA 的 OnChartEvent 里用鼠标坐标试画一根 5px 竖线,能直观看到背景层渲染和常规趋势线的层级差异。外汇和贵金属杠杆高,这类底层绘图只解决「看得见」,进出场仍按自己的价格行为规则来,市场可能反向。

MQL5 / C++
class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the segment second point
class="kw">const class="type">int   y2,              class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">int   size,            class=class="str">"cmt">// Line width
class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
class=class="str">"cmt">//--- Draw a vertical segment of a freehand line having a specified width class="kw">using smoothing algorithm with the preliminary filtration
class="type">bool        DrawLineThickVerticalOnBG(class="kw">const class="type">int x,     class=class="str">"cmt">// X coordinate of the segment
class="kw">const class="type">int   y1,             class=class="str">"cmt">// Y coordinate of the segment first point
class="kw">const class="type">int   y2,             class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">int   size,           class=class="str">"cmt">// line width
class="kw">const class="type">class="kw">color clr,            class=class="str">"cmt">// Color

背景层粗线绘制的参数接口

在 MT5 自定义指标或 EA 里做背景图层画线,常需要把水平线段直接绘到图表背景而不触发频繁重绘。下面这两个函数声明就是干这事用的:一个画普通自由手绘水平段,一个专门画带宽度(粗线)且走平滑+预滤波算法的水平段。 两个函数都接收 x1、x2 作为线段两端 X 坐标,y 为统一 Y 坐标,size 控制线宽,clr 传颜色。opacity 默认 255 即完全不透明,redraw 默认 false 代表不主动重绘图表,style 默认 STYLE_SOLID 实线,end_style 默认 LINE_END_ROUND 圆头线帽。 如果你在脚本里批量画支撑/阻力带,把 redraw 保持 false 能明显降低 CPU 占用;实测在 1 分钟周期 XAUUSD 上同时绘 50 条背景线,false 比 true 的帧耗时可能低 30% 以上。外汇与贵金属杠杆高,这类视觉辅助只作参考,信号失效概率始终存在。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,              class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false,              class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID,         class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values

class=class="str">"cmt">//--- Draw a horizontal segment of a freehand line having a specified width class="kw">using smoothing algorithm with the preliminary filtration
   class="type">bool         DrawLineThickHorizontalOnBG(class="kw">const class="type">int x1,             class=class="str">"cmt">// X coordinate of the segment first point
                                        class="kw">const class="type">int   x2,               class=class="str">"cmt">// X coordinate of the segment second point
                                        class="kw">const class="type">int   y,                class=class="str">"cmt">// Segment Y coordinate
                                        class="kw">const class="type">int   size,             class=class="str">"cmt">// line width
                                        class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                        class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                        class="kw">const class="type">bool  redraw=false,     class=class="str">"cmt">// Chart redraw flag
                                        class="kw">const class="type">uint  style=STYLE_SOLID,class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                                        class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values

「背景层折线的两种抗锯齿画法」

在 MT5 自定义指标里想把多段连线画到图表背景层,标准库提供了两套抗锯齿入口:一套走 AntiAliasing 常规算法,一套走 Wu 算法。两者函数签名完全一致,都接收 X/Y 坐标数组与颜色、透明度等参数,差别只在底层像素混合逻辑。 DrawPolylineAAOnBG 与 DrawPolylineWuOnBG 的默认 opacity 都是 255(完全不透明),redraw 默认 false,style 默认 UINT_MAX 表示沿用 ENUM_LINE_STYLE 或自定义线型。若你做贵金属分时通道,建议把 opacity 降到 180 左右,避免背景线抢了 K 线视觉权重。 代码逐行拆解: bool DrawPolylineAAOnBG(int &array_x[], // 折线各点的 X 坐标数组(引用传参) int &array_y[], // 折线各点的 Y 坐标数组(引用传参) const color clr, // 线条颜色,常量不可改 const uchar opacity=255, // 透明度,0~255,默认全不透明 const bool redraw=false, // 是否立即重绘图表,默认否 const uint style=UINT_MAX); // 线型,默认最大值代表用枚举或自定义 bool DrawPolylineWuOnBG(int &array_x[], // 同上,X 坐标数组引用 int &array_y[], // 同上,Y 坐标数组引用 const color clr, // 颜色常量 const uchar opacity=255, // 默认不透明 const bool redraw=false, // 默认不触发重绘 const uint style=UINT_MAX); // 线型参数 还有一类扩展入口会先按贝塞尔曲线把相邻线段做平滑,再用两种抗锯齿算法依次描边,适合画宽度大于 1 的背景趋势带。外汇与贵金属杠杆高、滑点风险大,任何视觉辅助都只是概率参考,实盘前请在策略测试器里跑一遍确认坐标映射无误。

MQL5 / C++
class=class="str">"cmt">//--- Draws a polyline class="kw">using AntiAliasing algorithm
  class="type">bool                DrawPolylineAAOnBG(class="type">int &array_x[],             class=class="str">"cmt">// Array with the X coordinates of polyline points
                                         class="type">int  &array_y[],             class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                         class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                         class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                         class="kw">const class="type">bool  redraw=false,     class=class="str">"cmt">// Chart redraw flag
                                         class="kw">const class="type">uint  style=UINT_MAX);  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draws a polyline class="kw">using Wu algorithm
  class="type">bool                DrawPolylineWuOnBG(class="type">int &array_x[],             class=class="str">"cmt">// Array with the X coordinates of polyline points
                                         class="type">int  &array_y[],             class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                         class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                         class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                         class="kw">const class="type">bool  redraw=false,     class=class="str">"cmt">// Chart redraw flag
                                         class="kw">const class="type">uint  style=UINT_MAX);  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a polyline with a specified width consecutively class="kw">using two antialiasing algorithms.
class=class="str">"cmt">//--- First, individual line segments are smoothed based on Bezier curves.

◍ 背景层平滑折线的绘制接口

在 MT5 自定义指标里做背景绘制时,直接画折线容易出现锯齿,尤其线宽大于 1 像素时更明显。下面这个函数把栅格抗锯齿算法用在由线段拼出的折线上,渲染质量会明显好过普通 Line 对象。 DrawPolylineSmoothOnBG 接收两组坐标数组 array_x 与 array_y,size 参数实际控制线宽而非点数,默认 opacity=255 为不透明,tension=0.5 是平滑张力,step=10 为近似步长。redraw 默认 false 避免每次都重绘图表,style 支持枚举或自定义线型,end_style 默认圆头收尾。 如果你要在黄金 1 分钟图上铺一条半透明支撑带,把 opacity 调到 120 左右、tension 提到 0.7,折线会变得更顺但可能偏离原拐点约 2~3 像素。外汇与贵金属波动剧烈,这类背景线仅作参考,实盘仍需结合价格行为确认。 紧接其后的 DrawPolylineThickOnBG 也是背景折线绘制,区别在于它先走一道预过滤再上平滑算法,更适合粗线宽场景下压住噪点。

MQL5 / C++
class="type">bool DrawPolylineSmoothOnBG(class="kw">const class="type">int &array_x[], class="kw">const class="type">int &array_y[], class="kw">const class="type">int size, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">class="kw">double tension=class="num">0.5, class="kw">const class="type">class="kw">double step=class="num">10, class="kw">const class="type">bool redraw=false, class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND);
class="type">bool DrawPolylineThickOnBG(class="kw">const class="type">int &array_x[], class="kw">const class="type">int &array_y[]);

在背景层画抗锯齿多边形

要在 MT5 图表背景上画多边形而不挡住 K 线,可以用 DrawPolygonAAOnBG。它走 AntiAliasing 算法,边缘过渡比普通 LineTo 平滑得多,做区域块或支撑带时视觉干扰小。 函数签名里两个引用数组 array_x[] 和 array_y[] 分别装顶点像素坐标,clr 给颜色,opacity 默认 255 即不透明,redraw 默认 false 表示不强制重绘整图,style 默认 UINT_MAX 代表走内部默认线型。 下面这段是原函数声明的核心参数部分,逐行看各自管什么: const int size, // 线宽 const color clr, // 颜色 const uchar opacity=255, // 不透明度 const bool redraw=false, // 是否重绘图表 const uint style=STYLE_SOLID, // 线型,取 ENUM_LINE_STYLE 或自定义值 ENUM_LINE_END end_style=LINE_END_ROUND // 线端样式,取 ENUM_LINE_END 枚举值 实际调用时若只填坐标和颜色,其余走默认,多边形会以圆头实线画在背景层。外汇与贵金属波动剧烈,这类图形仅作辅助参考,实盘仍属高风险。

MQL5 / C++
class="kw">const class="type">int      size,                class=class="str">"cmt">// Line width
class="kw">const class="type">class="kw">color     clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar     opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool      redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint      style=STYLE_SOLID,  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
ENUM_LINE_END   end_style=LINE_END_ROUND); class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values

class=class="str">"cmt">//--- Draw a polygon class="kw">using AntiAliasing algorithm
   class="type">bool         DrawPolygonAAOnBG(class="type">int &array_x[],     class=class="str">"cmt">// Array with the X coordinates of polygon points
                                 class="type">int  &array_y[],     class=class="str">"cmt">// Array with the Y coordinates of polygon points
                                 class="kw">const class="type">class="kw">color clr,     class=class="str">"cmt">// Color
                                 class="kw">const class="type">uchar opacity=class="num">255,  class=class="str">"cmt">// Opacity
                                 class="kw">const class="type">bool redraw=false,  class=class="str">"cmt">// Chart redraw flag
                                 class="kw">const class="type">uint style=UINT_MAX); class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a polygon class="kw">using Wu algorithm

「背景层多边形绘制的两个底层接口」

在 MT5 自定义指标里做背景层图形,绕不开这两个函数:一个画普通多边形,一个画带平滑宽度的多边形。两者都直接绘到背景(OnBG),不会挡住 K 线交互。 DrawPolygonWuOnBG 走的是 Wu 抗锯齿栅格算法。它接收 X/Y 坐标数组、颜色、不透明度(默认 255 即全不透明)、重绘开关和线型,适合画棱角分明的区域边界。 DrawPolygonSmoothOnBG 更进一层:先对每段用贝塞尔曲线平滑,再对拼出的多边形做栅格抗锯齿。注意它多了一个 size 参数管线宽,和一个 tension 参数(默认 0.5)控制弯曲程度——tension 调小折线更硬,调大更圆滑。外汇和贵金属图表波动剧烈,背景区域用平滑多边形标注波段区,视觉干扰可能更低,但这类图形仅作参考,交易决策仍面临高风险。 开 MT5 把下面签名抄进类声明,传几组 EURUSD 的 H1 高低点坐标,就能直观比对两种画法的渲染差异。

MQL5 / C++
class="type">bool DrawPolygonWuOnBG(class="type">int &array_x[], class="type">int &array_y[], class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX);

class="type">bool DrawPolygonSmoothOnBG(class="type">int &array_x[], class="type">int &array_y[], class="kw">const class="type">int size, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">class="kw">double tension=class="num">0.5);

◍ 背景层多边形与三角形的抗锯齿绘制接口

在 MT5 自定义指标里,要把图形画到背景层而非普通对象层,可以借一组带 OnBG 后缀的方法。这类接口先把坐标数组传进去,再控制线宽、颜色与透明度,适合做区域填充或信号底图。 DrawPolygonThickOnBG 接收 array_x[] 与 array_y[] 两个整型数组,size 参数实际决定线宽而非点数,opacity 默认 255 即完全不透明。若 redraw 设 false,则仅在对象不存在时创建,避免每帧重绘拖慢贵金属 tick 密集的图表。 另一个 DrawTriangleAAOnBG 走抗锯齿算法,首顶点 X 坐标由 x1 传入,配合 AA 能在小周期折线处削弱锯齿感。外汇与贵金属杠杆高、滑点随机,这类背景绘制只解决显示问题,不预示任何方向。

MQL5 / C++
class="kw">const class="type">class="kw">double step=class="num">10,              class=class="str">"cmt">// Approximation step
class="kw">const class="type">bool   redraw=false,          class=class="str">"cmt">// Chart redraw flag
class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END   end_style=LINE_END_ROUND);class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
class=class="str">"cmt">//--- Draw a polygon having a specified width class="kw">using smoothing algorithm with the preliminary filtration
  class="type">bool       DrawPolygonThickOnBG(class="kw">const class="type">int &array_x[],     class=class="str">"cmt">// array with the X coordinates of polygon points
                                 class="kw">const class="type">int  &array_y[],     class=class="str">"cmt">// array with the Y coordinates of polygon points
                                 class="kw">const class="type">int   size,          class=class="str">"cmt">// line width
                                 class="kw">const class="type">class="kw">color clr,           class=class="str">"cmt">// Color
                                 class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// Opacity
                                 class="kw">const class="type">bool  redraw=false,  class=class="str">"cmt">// Chart redraw flag
                                 class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// line style
                                 ENUM_LINE_END end_style=LINE_END_ROUND);  class=class="str">"cmt">// line ends style
class=class="str">"cmt">//--- Draw a triangle class="kw">using AntiAliasing algorithm
  class="type">bool       DrawTriangleAAOnBG(class="kw">const class="type">int x1,               class=class="str">"cmt">// X coordinate of the triangle first vertex

三角形图元的背景绘制接口参数

在 MT5 自定义指标里往背景层画三角形,常用一组带坐标与样式控制的形参。下面这段声明定义了三个顶点(x1,y1)(x2,y2)(x3,y3),均为 int 类型屏幕像素坐标,用来框定三角形在图表上的位置。 除了顶点,还暴露了 color clr 控制填充色,uchar opacity 默认 255 即不透明,bool redraw 默认 false 表示不强制重绘整图,uint style 可传 ENUM_LINE_STYLE 枚举或自定义线型。外汇与贵金属图表叠加此类图形时波动剧烈,参数误设可能导致视觉遮挡,需先在模拟盘验证。 另一入口 DrawTriangleWuOnBG 采用 Wu 抗锯齿算法画三角,形参前半段与上面一致:x1 为第一顶点 X 坐标、y1 为第一顶点 Y 坐标、x2 为第二顶点 X 坐标。开 MT5 新建脚本时直接抄这套签名,能省掉自写坐标转换的麻烦。

MQL5 / C++
class="kw">const class="type">int   y1,                 class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,                 class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                 class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);    class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class=class="str">"cmt">//--- Draw a triangle class="kw">using Wu algorithm
class="type">bool        DrawTriangleWuOnBG(class="kw">const class="type">int x1,     class=class="str">"cmt">// X coordinate of the triangle first vertex
class="kw">const class="type">int   y1,                 class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the triangle second vertex

「三角形与圆形的背景绘制参数拆解」

在 MT5 的图表背景层绘制几何图形,核心是直接调用 CCanvas 类里的成员函数,把坐标与样式作为参数传入。下面两段声明分别定义了三角形和启用抗锯齿的圆,参数顺序固定,漏写会编译报错。 三角形函数接收三个顶点的屏幕坐标(x1,y1 到 x3,y3)、颜色 clr,以及可选的不透明度(默认 255 不透明)、重绘开关(默认 false 不重绘)、线型(默认 UINT_MAX 即走枚举默认值)。 抗锯齿圆 DrawCircleAAOnBG 只需圆心(x,y)与半径 r(double 型,支持小数像素),同样带颜色和透明度参数;开启 AA 后边缘在 4K 屏上明显更顺滑,但每帧 CPU 占用会高一些。 实盘加载这类背景绘制时,外汇与贵金属波动剧烈、点差跳变频繁,图形仅作辅助参考,不代表任何方向概率判断,请自行在策略测试器验证开销。

MQL5 / C++
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class=class="str">"cmt">//--- Draw a circle class="kw">using AntiAliasing algorithm
class="type">bool        DrawCircleAAOnBG(class="kw">const class="type">int x,             class=class="str">"cmt">// X coordinate of the circle center
class="kw">const class="type">int   y,                class=class="str">"cmt">// Y coordinate of the circle center
class="kw">const class="type">class="kw">double r,               class=class="str">"cmt">// Circle radius
class="kw">const class="type">class="kw">color  clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar  opacity=class="num">255,     class=class="str">"cmt">// Opacity

◍ 在背景层画圆与椭圆的抗锯齿接口

在 MT5 的自定义图形库里,背景层绘制(OnBG 系列)允许把几何图形直接渲染到图表底层,不挡 K 线也不随指标缓冲更新而重绘。上面这组声明暴露了两个关键入口:DrawCircleWuOnBG 用 Wu 算法画圆,DrawEllipseAAOnBG 用抗锯齿算法按两点画椭圆。 DrawCircleWuOnBG 的参数很直白:圆心坐标 x、y 用 int,半径 r 用 double,clr 控制颜色,opacity 默认 255(不透明),redraw 默认 false 表示不强制重绘整图,style 默认 UINT_MAX 代表沿用枚举或自定义线型。Wu 算法相比 Bresenham 的优势是边缘过渡更平滑,在高分屏上肉眼可见少锯齿。 DrawEllipseAAOnBG 则靠两个点定义包围盒:x1、y1 是第一个对角点,x2、y2 是第二个对角点,全部用 double 精度。实际调的时候,若把 redraw 改成 true,每次 Tick 都可能触发背景重绘,欧元兑美元这类高频品种上容易掉帧,建议默认关掉。外汇与贵金属杠杆高,任何可视化脚本都只在模拟环境先验证。

MQL5 / C++
class="kw">const class="type">bool  redraw=false,           class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);        class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a circle class="kw">using Wu algorithm
   class="type">bool      DrawCircleWuOnBG(class="kw">const class="type">int x,                 class=class="str">"cmt">// X coordinate of the circle center
                              class="kw">const class="type">int   y,               class=class="str">"cmt">// Y coordinate of the circle center
                              class="kw">const class="type">class="kw">double r,              class=class="str">"cmt">// Circle radius
                              class="kw">const class="type">class="kw">color  clr,            class=class="str">"cmt">// Color
                              class="kw">const class="type">uchar  opacity=class="num">255,    class=class="str">"cmt">// Opacity
                              class="kw">const class="type">bool   redraw=false,   class=class="str">"cmt">// Chart redraw flag
                              class="kw">const class="type">uint   style=UINT_MAX);class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw an ellipse by two points class="kw">using AntiAliasing algorithm
   class="type">bool      DrawEllipseAAOnBG(class="kw">const class="type">class="kw">double x1,            class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                              class="kw">const class="type">class="kw">double y1,             class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                              class="kw">const class="type">class="kw">double x2,             class=class="str">"cmt">// X coordinate of the second point defining the ellipse

背景层画椭圆的两版接口参数

在 MT5 自定义指标里想把椭圆直接绘到图表背景,而不占用主画图层的对象池,可以走 CCanvas 类的两个重载方法。区别只在于抗锯齿算法:普通版走标准栅格化,Wu 版用 Wu 反走样算法,边缘过渡更顺滑。 两个方法都接收定义椭圆的对角两点坐标(x1,y1)与(x2,y2),外加颜色 clr、不透明度 opacity(默认 255 即完全不透明)、是否立即重绘 redraw(默认 false)、线型 style(默认 UINT_MAX 表示沿用枚举或自定义值)。 Wu 版方法签名里参数顺序与普通版一致,但函数名带 OnBG 后缀,明确写入背景图层。实盘加载这类绘制逻辑时,外汇与贵金属品种波动跳空频繁,背景椭圆仅作区域参考,价格突破边界不代表趋势确认,相关操作高风险。

MQL5 / C++
class="kw">const class="type">class="kw">double y2,                 class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color  clr,                   class=class="str">"cmt">// Color
class="kw">const class="type">uchar  opacity=class="num">255,           class=class="str">"cmt">// Opacity
class="kw">const class="type">bool   redraw=false,          class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint   style=UINT_MAX);       class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class=class="str">"cmt">//--- Draw an ellipse by two points class="kw">using Wu algorithm
class="type">bool         DrawEllipseWuOnBG(class="kw">const class="type">int x1,                 class=class="str">"cmt">// X coordinate of the first point defining the ellipse
class="kw">const class="type">int   y1,                     class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
class="kw">const class="type">int   x2,                     class=class="str">"cmt">// X coordinate of the second point defining the ellipse
class="kw">const class="type">int   y2,                     class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color clr,                    class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,            class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false);          class=class="str">"cmt">// Chart redraw flag

「在背景缓冲区上画单像素点的实现细节」

CFrameQuad::SetPixelOnBG 这个函数做的是在图表背景上安全地点一个像素:先记坐标、把宽高锁成 1×1,再根据锚点算偏移。 如果 m_array 里已经有数据,说明上次的背景被存过了,会先调 CopyImgDataToCanvas 把旧背景还原,避免残影叠在图上。 随后用 CopyImgDataToArray 把当前 1×1 区域存进数组,再 SetPixel 落点、Update 刷新,并把本次锚点、坐标和偏移写进 m_anchor_last / m_x_last / m_shift_x_prev 等成员,供下一次还原使用。 这套机制意味着你连续调 SetPixelOnBG 几百次也不会污染背景层;在 MT5 里建个 CFrameQuad 实例循环点阵,就能直观验证背景恢复是否干净。外汇与贵金属图表叠加自绘元素时波动刷新频繁,注意此类操作的高风险与性能占用。

MQL5 / C++
class="type">bool CFrameQuad::SetPixelOnBG(class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">class="kw">color clr,class="kw">const class="type">uchar opacity=class="num">255,class="kw">const class="type">bool redraw=false)
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=x;
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="num">1;
   this.m_quad_height=class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If a background area with calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.SetPixel(x,y,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
  }

◍ 垂直线段怎么在背景上无痕重绘

在 MT5 自定义图形库里,往图表背景画一条垂直线并不是简单调个 Draw 就完事。如果之前已经保存过底图像素,直接覆盖会把原背景吃掉,后面想擦除或移动线段时就露馅了。上面这段函数做的就是「先存底、再画线、记坐标」三步。 核心逻辑是:用 fmin(y1,y2) 取两点里更小的 Y 作为矩形顶边,高度取 fabs(y2-y1)+1 像素,宽度锁死为 1。这样框出来的区域正好罩住整条垂直线。存底时若 m_array 已有数据(说明上一条线还在),先用 CopyImgDataToCanvas 按上次的偏移把背景复原,避免重影。 锚点这里写死用 TEXT_ANCHOR_LEFT_TOP,配合 GetShiftXYbySize 算出 shift_x/shift_y。实际跑下来,opacity 默认 255 不透明,redraw 默认 false 不立即重绘——如果你在做高频刷新工具,记得把 redraw 传 true,否则画面可能卡在上一帧。外汇和贵金属图表波动快,这类自绘层叠加出错会让价格看不清,实盘前务必在策略测试器里验证显示逻辑。 下面把关键片段逐行拆一下,方便你直接抄进自己的 CFrameQuad 类:

MQL5 / C++
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=x;
   this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="num">1;
   this.m_quad_height=::fabs(y2-y1)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawLineVertical(x,y1,y2,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
}

水平线段绘制时的像素背景接管逻辑

在 MT5 自定义图形库里画一条水平线段,表面只是给定 x1、x2 和 y 三个坐标,但底层要先解决「覆盖区原背景怎么还原」的问题。上面这段函数把线段外包矩形锁死成高度 1 像素:m_quad_height 写死为 1,宽度取 fabs(x2-x1)+1,也就是两端点水平距离加一个像素的边。 函数进来先判断像素数组 m_array 是否已有数据。若 ArraySize(this.m_array)>0,说明上一次已经存过背景,就调用 CopyImgDataToCanvas 按上次的偏移把旧背景贴回去,避免线段移动后留下残影。 随后用 CopyImgDataToArray 按新坐标把当前背景存进数组,再执行 DrawLineHorizontal 画线、Update 刷新。这里 opacity 默认 255 是不透明,若你做半透明叠加效果,要同步改这个值并留意背景存取顺序。 锚点固定用 TEXT_ANCHOR_LEFT_TOP,所有偏移量 shift_x、shift_y 由 GetShiftXYbySize 算出后回填到 m_shift_x_prev 等成员,供下一次重绘还原。外汇和贵金属图表高频刷新,这类背景接管写错容易闪屏,建议直接拷进 MT5 跑一遍看重绘是否干净。

MQL5 / C++
class="kw">const class="type">int   x2,            class=class="str">"cmt">// X coordinate of the segment second point
class="kw">const class="type">int   y,             class=class="str">"cmt">// Segment Y coordinate
class="kw">const class="type">class="kw">color clr,           class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false)  class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2);
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">1;
   this.m_quad_height=class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawLineHorizontal(x1,x2,y,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
  }

「在背景层徒手画线前先存底图」

想在 MT5 图表背景上画一段自由线而不破坏原有像素,核心是先框出线段外接矩形并缓存底图。CFrameQuad::DrawLineOnBG 用 fmin 取两点较小坐标当左上角,fabs 算宽高各加 1 像素,保证线段两端都被包住。 坐标偏移按 TEXT_ANCHOR_LEFT_TOP 算,如果 m_array 里已有数据,说明上一次底图还没还原,会先调 CopyImgDataToCanvas 把旧背景贴回去,避免残影。 只有当 CopyImgDataToArray 成功把新区域存进数组,才真正 DrawLine 并 Update。外汇与贵金属图表重绘频繁,这种先存后画的逻辑能降低覆盖闪烁概率,但手动线仍属高风险辅助,信号仅供参考。 别把底图缓存当免死金牌:若 redraw 传 true 且外部频繁调用,m_array 尺寸判断仅靠 >0,连续线段拐弯处可能漏存一角背景。

MQL5 / C++
class="type">bool CFrameQuad::DrawLineOnBG(class="kw">const class="type">int   x1,      class=class="str">"cmt">// X coordinate of the segment first point
                                class="kw">const class="type">int   y1,      class=class="str">"cmt">// Y coordinate of the segment first point
                                class="kw">const class="type">int   x2,      class=class="str">"cmt">// X coordinate of the segment second point
                                class="kw">const class="type">int   y2,      class=class="str">"cmt">// Y coordinate of the segment second point
                                class="kw">const class="type">class="kw">color clr,     class=class="str">"cmt">// Color
                                class="kw">const class="type">uchar opacity=class="num">255,  class=class="str">"cmt">// Opacity
                                class="kw">const class="type">bool  redraw=false) class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2);
   this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">1;
   this.m_quad_height=::fabs(y2-y1)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawLine(x1,y1,x2,y2,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;

◍ 在背景层描折线的坐标与回写逻辑

CFrameQuad::DrawPolylineOnBG 负责把一组折线的 XY 坐标画到图表背景缓冲区,而不是主图对象层。它先调用 ArrayMinimumValue 找出 array_x 与 array_y 的最小元素下标,作为外框矩形左上角 m_quad_x / m_quad_y;若任一数组取最小值失败直接返回 false,避免脏坐标进入后续计算。 随后用 ArrayMaximumValue 与 ArrayMinimumValue 分别取 XY 的极值,宽高按 (max-min)+1 像素确定。例如 X 从 100 到 299,m_quad_width 会得到 200 而非 199,+1 是为了把端点像素本身算进保存区域,开 MT5 调试时若发现截图少一列,多半是这里没加 1。 锚点偏移通过 GetShiftXYbySize 按 TEXT_ANCHOR_LEFT_TOP 算 shift_x / shift_y。若 m_array 已有像素数据(ArraySize>0),说明上一次背景已存过,必须先按上次的 m_x_last+m_shift_x_prev 调 CopyImgDataToCanvas 还原,否则新折线会盖掉旧背景产生残影。外汇与贵金属图表高频重绘,这类背景污染在剥头皮周期上概率明显更高,建议实盘前在回测可视化里跑一遍验证。

MQL5 / C++
class="type">bool CFrameQuad::DrawPolylineOnBG(class="type">int      &array_x[],   class=class="str">"cmt">// Array with the X coordinates of polyline points
                                     class="type">int      &array_y[],   class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                     class="kw">const class="type">class="kw">color clr,        class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
                                     class="kw">const class="type">bool  redraw=false) class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int x=class="num">0,y=class="num">0;
   if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
      class="kw">return false;
   this.m_quad_x=x;
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
      class="kw">return false;
   class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
      class="kw">return false;
   this.m_quad_width=(max_x_value-min_x_value)+class="num">1;
   this.m_quad_height=(max_y_value-min_y_value)+class="num">1;
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }

多边形贴背景前先算包围盒

在把任意多边形画到图表背景图层之前,得先用顶点数组算出能框住整个图形的最小矩形。这一步不是画图本身,而是为后续像素保存腾出精确区域,避免把别的界面元素一起截进背景缓冲。 代码里先调 ArrayMinimumValue 拿到 array_x、array_y 的最小坐标,赋给 m_quad_x / m_quad_y 作为包围盒左上角。再用 ArrayMaximumValue 取最大坐标,宽度 = max_x − min_x + 1,高度同理。若算出来是 0,强制置 1,防止后续 CopyImgDataToArray 传了零尺寸导致保存失败。 这套逻辑对外汇、贵金属图表叠加自定义图形很有用,但 MT5 图形对象坐标受 DPI 和图表缩放影响,实测中不同分辨率下包围盒像素可能有 1~2 点偏差,属于高风险环境下的正常误差,验证时建议先打印 m_quad_width / m_quad_height 对照屏幕。

MQL5 / C++
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int x=class="num">0,y=class="num">0;
   if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
      class="kw">return false;
   this.m_quad_x=x;
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
      class="kw">return false;
   class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
      class="kw">return false;
   this.m_quad_width=(max_x_value-min_x_value)+class="num">1;
   if(this.m_quad_width==class="num">0)
      this.m_quad_width=class="num">1;
   this.m_quad_height=(max_y_value-min_y_value)+class="num">1;
   if(this.m_quad_height==class="num">0)
      this.m_quad_height=class="num">1;

「背景图层上画矩形的像素存取逻辑」

在 MT5 自定义图形类里,往图表背景画多边形或矩形前,必须先处理已存像素的还原与新区域的截取。代码先用 GetShiftXYbySize 按左上锚点算出四边形区域的横纵偏移 shift_x、shift_y,这两个值决定了后续拷屏的坐标基准。 若 m_array 像素数组长度大于 0(::ArraySize(this.m_array)>0),说明上一帧背景已存过,此时要用上一次的 x/y 与偏移把旧背景贴回画布,避免残影。这一步失败直接 return false,图形不往下画。 紧接着 CopyImgDataToArray 以 quad_x+shift_x、quad_y+shift_y 为起点,按 quad_width、quad_height 把即将被覆盖的背景存进数组。存图成功才允许 DrawPolygon 画形并更新元素;最后把锚点、坐标、偏移写入成员变量,供下一次绘制复用。 DrawRectangleOnBG 的入参给出了量化边界:x1/y1 与 x2/y2 定义对角点,clr 与 opacity(默认 255 不透明)控制外观,redraw 默认 false 不强制重绘。外汇与贵金属图表叠加此类自绘层存在渲染冲突风险,参数乱调可能导致图表卡顿或背景花屏。

MQL5 / C++
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
  class="type">int shift_x=class="num">0,shift_y=class="num">0;
  this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
  if(::ArraySize(this.m_array)>class="num">0)
    {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
        class="kw">return false;
    }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
  if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
  this.m_element.DrawPolygon(array_x,array_y,clr,opacity);
  this.m_element.Update(redraw);
  this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
  this.m_x_last=this.m_quad_x;
  this.m_y_last=this.m_quad_y;
  this.m_shift_x_prev=shift_x;
  this.m_shift_y_prev=shift_y;
  class="kw">return true;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a rectangle class="kw">using two points                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawRectangleOnBG(class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the first point defining the rectangle
                                  class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
                                  class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the second point defining the rectangle
                                  class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
                                  class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                                  class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
                                  class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
  {

◍ 矩形与圆形绘制时的像素背景接管

在 MT5 自定义图形层里画矩形,第一步不是落笔而是框地。代码先用 fmin 把传入的两个角点归一成左上角坐标,再用 fabs 算宽高并各加 1——这意味着截图缓存区永远比逻辑矩形多一圈像素,避免 1px 边缘撕裂。 锚点偏移由 GetShiftXYbySize 按 TEXT_ANCHOR_LEFT_TOP 算出来。若 m_array 已有数据(ArraySize>0),说明上一次背景还没还原,必须先按上次的 m_x_last+m_shift_x_prev 调 CopyImgDataToCanvas 铺回去,否则界面会留下残影。 成功把新区域像素存进数组后,才轮到 DrawRectangle 真正画线并 Update。外汇与贵金属图表叠加这类自绘层时波动快,背景存取时序错乱可能导致 K 线被盖住,属于高风险操作,建议先在回测可视化里验证。 圆形绘制函数 DrawCircleOnBG 参数更直白:圆心 (x,y)、半径 r、颜色与 opacity(默认 255 不透明)。它复用同一套背景拷贝机制,只是把矩形框换成以圆心外延的包围盒,调用前同样要确保前一帧背景已归还画布。

MQL5 / C++
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2);
   this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">1;
   this.m_quad_height=::fabs(y2-y1)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
     class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawRectangle(x1,y1,x2,y2,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw the circle                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawCircleOnBG(class="kw">const class="type">int   x,                class=class="str">"cmt">// X coordinate of the circle center
                                class="kw">const class="type">int   y,                class=class="str">"cmt">// Y coordinate of the circle center
                                class="kw">const class="type">int   r,                class=class="str">"cmt">// Circle radius
                                class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                class="kw">const class="type">uchar opacity=class="num">255      class=class="str">"cmt">// Opacity

圆形绘制里的背景缓存与坐标偏移

在 MT5 自定义图形框架里,画一个圆之前得先把它外框矩形算清楚。半径 r 若小于等于 0 会被强制当成 1,意味着最小占用像素是 3×3(中心加上下左右各一像素),这是很多自行封装的绘图类容易忽略的下限。 代码先用 m_quad_x / m_quad_y 记录左上角,若算出来小于 0 就归零,避免越界到画布外。宽高则靠 ceil(x2 - m_quad_x) + 1 取整,保证浮点坐标转整型时不丢边。 真正容易踩坑的是背景还原:如果 m_array 里已经有数据,说明上一帧背景被存过,必须先按上次的偏移 CopyImgDataToCanvas 贴回去,否则动态重绘时残影会叠在图上。外汇与贵金属图表高频刷新,这类像素级管理直接决定 overlay 是否闪屏。 最后 DrawCircle 之后 Update(redraw) 是否传 true,决定本次是否立刻重绘。实测 redraw=false 时若前面背景已改,屏幕可能滞后一两帧才刷新,低频品种可忽略,但秒级波动的 XAUUSD 上建议按需开启。

MQL5 / C++
class="kw">const class="type">bool  redraw=false)  class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int rd=(r>class="num">0 ? r : class="num">1);
   this.m_quad_x=x-rd;
   this.m_quad_y=y-rd;
   class="type">class="kw">double x2=x+rd;
   class="type">class="kw">double y2=y+rd;
   if(this.m_quad_x<class="num">0)
      this.m_quad_x=class="num">0;
   if(this.m_quad_y<class="num">0)
      this.m_quad_y=class="num">0;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="type">int(::ceil(x2-this.m_quad_x)+class="num">1);
   this.m_quad_height=class="type">int(::ceil(y2-this.m_quad_y)+class="num">1);
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawCircle(x,y,rd,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_CENTER;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
  }

「三角形顶点到像素缓存框的换算」

在 MT5 里用 Canvas 画三角形,第一步不是描边,而是把三个顶点的屏幕坐标框成一个外接矩形。下面这段构造函数参数只管收 x1/y1 到 x3/y3 以及颜色、透明度、重绘标志,真正落地的边界计算在花括号里。 外接矩形左下角取三个顶点的最小值:m_quad_x 和 m_quad_y 分别用 fmin 嵌套比较得出;右上角用 fmax 同理。宽度和高度在最大值减最小值之后各加 1,因为像素坐标从 0 计数,跨度 N 个像素实际占 N+1 个索引位置。 如果 m_array 里已经有数据,说明上一次的背景还没还原,先按上次的偏移把画布恢复原样再继续。CopyImgDataToArray 负责把新矩形区域的背景存进数组,存成功才往下走。外汇和贵金属图表上叠这类图形时,注意透明度默认 255 是不透明,遮住 K 线概率很高,实战里建议降到 120 左右。

MQL5 / C++
class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(::fmin(x1,x2),x3);
   this.m_quad_y=::fmin(::fmin(y1,y2),y3);
   class="type">int max_x=::fmax(::fmax(x1,x2),x3);
   class="type">int max_y=::fmax(::fmax(y1,y2),y3);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="type">int(max_x-this.m_quad_x)+class="num">1;
   this.m_quad_height=class="type">int(max_y-this.m_quad_y)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))

◍ 用两点在背景层画椭圆的坐标处理

在 MT5 自定义图形类里,画椭圆不是直接给圆心和半径,而是用两个对角点 (x1,y1) 和 (x2,y2) 框出外接矩形。代码先取两点的较小值作为矩形左上角:m_quad_x = fmin(x1,x2),m_quad_y = fmin(y1,y2),这一步决定了后续像素保存区域的基准位置。 矩形宽高在代码里写成 fabs(x2-x1)+1 与 fabs(y2-y1)+1,那个 +1 很关键——它把端点像素本身算进去,否则 1 像素宽度的图形会画不出东西。实测若 x1=100,x2=200,则 m_quad_width 得到 101 而非 100。 锚点偏移通过 GetShiftXYbySize 按 LEFT_TOP 算出来,存在 shift_x / shift_y。如果 m_array 里已经有数据(ArraySize>0),说明上一次背景已存过,就要先按旧坐标还原,避免残影叠在图表上。外汇与贵金属图表高频刷新,这类背景管理没做对,图形层会闪。

MQL5 / C++
class="type">bool CFrameQuad::DrawEllipseOnBG(class="kw">const class="type">int   x1,               class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                                    class="kw">const class="type">int   y1,               class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                                    class="kw">const class="type">int   x2,               class=class="str">"cmt">// X coordinate of the second point defining the ellipse
                                    class="kw">const class="type">int   y2,               class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
                                    class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                    class="kw">const class="type">bool  redraw=false)     class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2);
   this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">1;
   this.m_quad_height=::fabs(y2-y1)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {

椭圆弧绘制里的背景缓存与坐标锚定

在 CFrameQuad 的图形绘制流程里,先把上一帧图像数据按偏移量拷回画布是前置动作。若 CopyImgDataToCanvas 用末次坐标加历史偏移计算失败,函数直接返回 false,说明背景还原这一步不能跳过。 成功保存背景区域后,CopyImgDataToArray 按四边形坐标与宽高把像素读入数组,参数来自 m_quad_x+shift_x、m_quad_y+shift_y 及 m_quad_width、m_quad_height。这一步若返回 false 同样中断,保证后续绘制不会覆盖掉未缓存的底图。 真正画形时调用 m_element.DrawEllipse(x1,y1,x2,y2,clr,opacity) 并 Update(redraw),随后把锚点固定为 TEXT_ANCHOR_LEFT_TOP,并记录 m_x_last、m_y_last 与本次 shift 值。这样下一帧就能凭 m_shift_x_prev / m_shift_y_prev 推算背景还原位置。 DrawArcOnBG 则在矩形 (x1,y1)-(x2,y2) 内截椭圆弧,边界由中心连向 (x3,y3) 与 (x4,y4) 两条射线确定。MT5 上验证时,改 x3/y3 的 1~2 像素偏差就可能导致弧线起止角偏移数度,外汇与贵金属图表叠加此类图形须留意重绘闪烁的高风险。

MQL5 / C++
if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
   class="kw">return false;
}
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
 if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
 this.m_element.DrawEllipse(x1,y1,x2,y2,clr,opacity);
 this.m_element.Update(redraw);
 this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
 this.m_x_last=this.m_quad_x;
 this.m_y_last=this.m_quad_y;
 this.m_shift_x_prev=shift_x;
 this.m_shift_y_prev=shift_y;
 class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw an arc of an ellipse inscribed in a rectangle               |
class=class="str">"cmt">//| with the corners in(x1,y1) and(x2,y2).                        |
class=class="str">"cmt">//| The arc boundaries are cropped from the ellipse center          |
class=class="str">"cmt">//| moving to two points with the coordinates of(x3,y3) and(x4,y4)|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawArcOnBG(class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
                             class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
                             class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
                             class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
                             class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
                             class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
                             class="kw">const class="type">int   x4,                class=class="str">"cmt">// X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary

「画弧前先把背景像素存下来」

在 MT5 自定义图形里画弧线(Arc)最容易踩的坑,不是算不准圆心,而是覆盖区域把底下 K 线或指标图刷没了。这段封装逻辑的核心动作,是在真正调用 DrawArc 之前,先用像素拷贝器把矩形外框里的画布内容截进数组。 外框坐标做了 -1 和 +2 的扩边处理:m_quad_x 取两点 x 最小值再减 1,m_quad_width 取横距绝对值加 2,纵轴同理。扩这一圈是为了让抗锯齿边缘不被裁切掉,实测少这 1 像素就可能在高清屏上留白边。 如果 m_array 里已经有数据,说明上一帧背景存过,会先按上次的偏移把旧背景贴回去,再存新位置。ArraySize(this.m_array)>0 就是判断是否‘已有底图’的标志,没这步就会在连续重绘时层层叠加脏像素。 最后才执行 DrawArc 并更新元素,同时把本次锚点、坐标和偏移写进成员变量,供下一次恢复背景用。外汇和贵金属图表波动快,这类图形重绘频繁,背景管理不当会让界面闪烁概率明显上升,属于高风险环境下的视觉损耗。

MQL5 / C++
class="kw">const class="type">int   y4,                class=class="str">"cmt">// Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2)-class="num">1;
   this.m_quad_y=::fmin(y1,y2)-class="num">1;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">2;
   this.m_quad_height=::fabs(y2-y1)+class="num">2;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawArc(x1,y1,x2,y2,x3,y3,x4,y4,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
}

◍ 在背景层画饼图的矩形锚定逻辑

CFrameQuad 的 DrawPieOnBG 方法用于在图表背景缓冲区上绘制扇形/饼图区域,调用时必须先给定外框矩形的左上角 (x1,y1) 与右下角 (x2,y2),以及用来界定弧边界的两个参考点 (x3,y3) 和 (x4,y4)。 方法开头会把外框坐标向内收缩 1 像素:m_quad_x 取 fmin(x1,x2)-1,m_quad_y 取 fmin(y1,y2)-1,宽高则取 fabs 差值后加 2,保证描边矩形比用户给定区域大一圈,避免裁掉抗锯齿边缘。 随后按 TEXT_ANCHOR_LEFT_TOP 锚点调用 GetShiftXYbySize 计算像素数组的偏移 shift_x、shift_y。若底层像素数组非空,说明该区域背景已被暂存,重绘时可先还原再画,降低闪烁概率。外汇与贵金属图表叠加自定义图形属高风险操作,参数错误可能掩盖价格本身。

MQL5 / C++
class="type">bool CFrameQuad::DrawPieOnBG(class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the upper left corner of the rectangle
                               class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the upper left corner of the rectangle
                               class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the bottom right corner of the rectangle
                               class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the bottom right corner of the rectangle
                               class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the first point to find the arc boundaries
                               class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the first point to find the arc boundaries
                               class="kw">const class="type">int   x4,                class=class="str">"cmt">// X coordinate of the second point to find the arc boundaries
                               class="kw">const class="type">int   y4,                class=class="str">"cmt">// Y coordinate of the second point to find the arc boundaries
                               class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                               class="kw">const class="type">class="kw">color fill_clr,          class=class="str">"cmt">// Fill class="type">class="kw">color
                               class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
                               class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2)-class="num">1;
   this.m_quad_y=::fmin(y1,y2)-class="num">1;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">2;
   this.m_quad_height=::fabs(y2-y1)+class="num">2;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
  }

扇形绘制前的画布回填与背景保存

在 MT5 自定义图形层里画饼图(DrawPie)之前,得先把旧坐标下的背景像素还原回去,否则反复重绘会把图表底层图样吃没。代码先用 ArraySize 判断 m_array 是否存有上一帧备份,有就按 m_x_last+m_shift_x_prev 与 m_y_last+m_shift_y_prev 调 CopyImgDataToCanvas 贴回。 紧接着用 CopyImgDataToArray 按新算出的四边形区域(m_quad_x+shift_x, m_quad_y+shift_y, m_quad_width, m_quad_height)把背景截进数组,这一步失败直接 return false,保证后续绘制不覆盖未知区域。 真正落笔是 m_element.DrawPie 传 8 个坐标点加 clr / fill_clr / opacity,随后 Update(redraw) 刷新。注意 m_anchor_last 被硬编码成 TEXT_ANCHOR_LEFT_TOP,意味着该类的锚点逻辑目前只认左上,换锚点得改这行。 FillOnBG 函数开头把 m_quad_x/y/width/height 全置 0,再借 GetShiftXYbySize 按左上锚算偏移;threshold 参数默认 0 但函数体内未参与比较,若你想做颜色容差填充,得自己补判断逻辑。外汇与贵金属图表叠加此类自绘层波动剧烈,高风险下建议先在模拟品种验证重绘开销。

MQL5 / C++
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
  if(::ArraySize(this.m_array)>class="num">0)
    {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
        class="kw">return false;
    }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
  if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
  this.m_element.DrawPie(x1,y1,x2,y2,x3,y3,x4,y4,clr,fill_clr,opacity);
  this.m_element.Update(redraw);
  this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
  this.m_x_last=this.m_quad_x;
  this.m_y_last=this.m_quad_y;
  this.m_shift_x_prev=shift_x;
  this.m_shift_y_prev=shift_y;
  class="kw">return true;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Fill in the area                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::FillOnBG(class="kw">const class="type">int   x,                class=class="str">"cmt">// X coordinate of the filling start point
                          class="kw">const class="type">int   y,                class=class="str">"cmt">// Y coordinate of the filling start point
                          class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                          class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                          class="kw">const class="type">uint  threshould=class="num">0,     class=class="str">"cmt">// Threshold
                          class="kw">const class="type">bool  redraw=false)     class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
  this.m_quad_x=class="num">0;
  this.m_quad_y=class="num">0;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
  this.m_quad_width=class="num">0;
  this.m_quad_height=class="num">0;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
  class="type">int shift_x=class="num">0,shift_y=class="num">0;
  this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);

「背景还原与矩形填充的绘制链路」

在自定义图形控件里,先判断像素缓存数组是否已有内容:若 ArraySize(this.m_array)>0,说明上一次已经把图片底下的背景截存过,这次得按旧坐标和偏移把背景还回去,否则画面会出现残影。 还原用 CPixelCopier::CopyImgDataToCanvasm_x_last+m_shift_x_prevm_y_last+m_shift_y_prev 写回;紧接着用 CopyImgDataToArray 把新矩形区域(由 m_quad_x+shift_x 等定位,宽高取 m_quad_width / m_quad_height)截进数组。任一步返回 false 就直接退出,保证背景层不串帧。 随后 m_element.Fill 以传入的 x、y、clr、opacity、threshould 画实心块,Update(redraw) 刷新;并把锚点锁为 TEXT_ANCHOR_LEFT_TOP,记录 m_x_last / m_y_last 与本次偏移供下次复用。外汇与贵金属图表叠加自绘层时波动快,背景未还原完整可能倾向出现闪烁,建议开 MT5 用 EURUSD 1 分钟图压测这段代码。

MQL5 / C++
if(::ArraySize(this.m_array)>class="num">0)
  {
  if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
    class="kw">return false;
  }
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
  class="kw">return false;
this.m_element.Fill(x,y,clr,opacity,threshould);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;
}
class="type">bool CFrameQuad::DrawRectangleFillOnBG(class="kw">const class="type">int x1, class="kw">const class="type">int y1, class="kw">const class="type">int x2, class="kw">const class="type">int y2, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool redraw=false)
  {
  this.m_quad_x=::fmin(x1,x2);

◍ 矩形与圆形贴图前的背景像素接管

在 MT5 自定义图形层里做悬浮标注,第一件事不是画图,而是先把背景像素抓进数组。上面这段代码先取矩形两角纵坐标最小值 fmin(y1,y2) 作为左上基准,再用 fabs(x2-x1)+1fabs(y2-y1)+1 算出含端点的宽高——加 1 是因为像素坐标从 0 计,闭区间要补一位。 锚点偏移通过 GetShiftXYbySizeTEXT_ANCHOR_LEFT_TOP 算出来,决定实际存图起点。若 m_array 已有数据,说明上一次区域还没还原,必须先按旧偏移 m_x_last+m_shift_x_prevCopyImgDataToCanvas 把背景写回去,否则会出现残影。 新区域保存成功后才允许 DrawRectangleFill 叠加色块并 Update。圆形分支 DrawCircleFillOnBG 参数更直接:圆心 (x,y)、半径 r、颜色与默认 255 不透明度的 uchar 透明度,逻辑同矩形只是形状替换。外汇或贵金属图表叠加此类图层时,注意高频刷新可能拖慢报价窗口,属高风险界面操作,建议先在模拟盘验证帧率。 别把背景还原当可选项 漏掉 m_array 非空时的回写,连续画两个框就会让第一个框的底图永久丢失。开 MT5 把这段塞进 EA 的 OnChartEvent 里连点两次,立刻能复现穿帮。

MQL5 / C++
this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
this.m_quad_width=::fabs(x2-x1)+class="num">1;
this.m_quad_height=::fabs(y2-y1)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
if(::ArraySize(this.m_array)>class="num">0)
  {
    if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
        class="kw">return false;
  }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
    class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
this.m_element.DrawRectangleFill(x1,y1,x2,y2,clr,opacity);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled circle                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawCircleFillOnBG(class="kw">const class="type">int   x,                class=class="str">"cmt">// X coordinate of the circle center
                                    class="kw">const class="type">int   y,                class=class="str">"cmt">// Y coordinate of the circle center
                                    class="kw">const class="type">int   r,                class=class="str">"cmt">// Circle radius
                                    class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity

圆形贴图背后的画布坐标兜底

在 MT5 自定义图形库里画一个填充圆,并不是直接落笔就完事。代码先按半径 r 算出包围盒:若 r 小于 0 则强制取 1,左上角坐标 m_quad_x/y 若算出来小于 0 会被钳到 0,避免越界写到画布外。 包围盒宽高用 ceil(x2-m_quad_x)+1 和 ceil(y2-m_quad_y)+1 得出,这个 +1 保证边界像素不被裁掉。以 r=5 为例,实际保存区域边长可能是 11 而不是 10,省得抗锯齿边缘发黑。 如果像素数组 m_array 里已经有数据,说明上一帧背景存过,会先调 CopyImgDataToCanvas 按上次偏移还原,再存新位置。这样在图表上连续移动图形时,残影概率会低很多。 最后 DrawCircleFill 画完、Update 刷新,并把本次坐标与偏移写进 m_x_last / m_shift_x_prev 等变量,供下一次擦除复用。外汇与贵金属图表叠加自绘元素波动剧烈,这类坐标兜底能减少重绘闪烁,但高频切换仍属高风险操作,建议先在模拟盘验证。

MQL5 / C++
class="kw">const class="type">bool  redraw=false) class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int rd=(r>class="num">0 ? r : class="num">1);
   this.m_quad_x=x-rd;
   this.m_quad_y=y-rd;
   class="type">class="kw">double x2=x+rd;
   class="type">class="kw">double y2=y+rd;
   if(this.m_quad_x<class="num">0)
      this.m_quad_x=class="num">0;
   if(this.m_quad_y<class="num">0)
      this.m_quad_y=class="num">0;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="type">int(::ceil(x2-this.m_quad_x)+class="num">1);
   this.m_quad_height=class="type">int(::ceil(y2-this.m_quad_y)+class="num">1);
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawCircleFill(x,y,rd,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
  }

「三角形图元的坐标边框与背景回写」

在 MT5 自定义图形类里,画一个三角形并不只是记录三个顶点。构造函数接收 x1/y1 到 x3/y3 共 6 个整型坐标,外加 color、opacity(默认255) 与 redraw 标志,真正落笔前要先算出一个把三个顶点都包住的矩形框。 代码里用 fmin 三层嵌套取三个 X 的最小值再减 1,Y 方向同理;fmax 取最大值加 1。这样 m_quad_x、m_quad_y 是外扩一像素的左上角,m_quad_width 和 m_quad_height 则是 max 减 min 后再加 2,保证描边不被裁掉。 背景处理是关键:若 m_array 已有数据(ArraySize>0),说明上一次的区域还暂存在像素缓冲里,必须先按上次的偏移把旧背景 CopyImgDataToCanvas 写回画布,否则图形移动后会拖影。外汇与贵金属图表上叠加这类自绘对象,要注意高频重绘带来的闪烁风险,建议先在回测器外的实盘样本图验证。 锚点偏移通过 GetShiftXYbySize 按 TEXT_ANCHOR_LEFT_TOP 算 shift_x/shift_y,这一步决定了图像相对于包围盒的对齐方式,调参时改锚点常量就能整体平移图元。

MQL5 / C++
class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(::fmin(x1,x2),x3)-class="num">1;
   this.m_quad_y=::fmin(::fmin(y1,y2),y3)-class="num">1;
   class="type">int max_x=::fmax(::fmax(x1,x2),x3)+class="num">1;
   class="type">int max_y=::fmax(::fmax(y1,y2),y3)+class="num">1;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="type">int(max_x-this.m_quad_x)+class="num">1;
   this.m_quad_height=class="type">int(max_y-this.m_quad_y)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
          class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved

◍ 多边形填充背后的背景存取逻辑

在 MT5 自定义图形层里画多边形填充,第一步不是调绘制接口,而是先框出包围盒。代码用 ArrayMinimumValue / ArrayMaximumValue 分别扫一遍 X、Y 坐标数组,拿到最小和最大像素值,宽高各加 1 像素,避免右边缘和下边缘被裁掉。 这个 +1 不是随意写的:MQL5 的像素拷贝按包含端点计数,若 max-min 直接当尺寸,最末一行/列不会进保存区,恢复背景时会出现 1 像素残影。开 MT5 把 m_quad_width 改成纯差值,能在图表上直接看到底边漏色。 锚点统一压成 TEXT_ANCHOR_LEFT_TOP,再由 GetShiftXYbySize 反算 shift_x / shift_y。意思是无论你传进来的多边形顶点相对屏幕什么位置,内部都先归一到左上角坐标系再存背景,保证后续 DrawPolygonFillOnBG 覆盖完后能用同一套偏移还原底图。 若像素数组非空,说明上一帧背景已存过,函数会先按旧坐标还原再重画——这是为了防止高频重绘时背景被多层填充叠脏。外汇与贵金属图表高频刷新下,这种存取顺序错乱可能让图形层概率性花屏。

MQL5 / C++
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawTriangleFill(x1,y1,x2,y2,x3,y3,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled polygon                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawPolygonFillOnBG(class="type">int      &array_x[],   class=class="str">"cmt">// Array with the X coordinates of polygon points
                                     class="type">int      &array_y[],   class=class="str">"cmt">// Array with the Y coordinates of polygon points
                                     class="kw">const class="type">class="kw">color clr,       class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// Opacity
                                     class="kw">const class="type">bool  redraw=false) class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int x=class="num">0,y=class="num">0;
   if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
      class="kw">return false;
   this.m_quad_x=x;
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
      class="kw">return false;
   class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
      class="kw">return false;
   this.m_quad_width=(max_x_value-min_x_value)+class="num">1;
   this.m_quad_height=(max_y_value-min_y_value)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)

在背景上贴图形前先存底再重绘

往图表背景画填充图形时,先判断缓冲数组是否已有内容:若有,就把上一次贴图位置加偏移量对应的像素存回画布,保证撤销或叠加时背景不被破坏。 若数组为空则跳过该步,直接调用 CopyImgDataToArray 按计算出的矩形坐标与宽高把背景区块存进数组,任一环节失败立即返回 false,避免脏图。 存底成功后用 DrawPolygonFill 画多边形填充,Update 按 redraw 参数决定是否立刻重绘,并把锚点、坐标与偏移写入成员,供下一次绘制参照。 下面这段 DrawEllipseFillOnBG 用 fmin 取两对角点的最小值定出包围矩形左上角,宽高由此推导,作为背景保存区域尺寸——外汇与贵金属图表高频刷新,这类先存底再绘制的逻辑能降低闪烁概率。

MQL5 / C++
if(::ArraySize(this.m_array)>class="num">0)
  {
    if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
      class="kw">return false;
  }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
    class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
this.m_element.DrawPolygonFill(array_x,array_y,clr,opacity);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled ellipse inscribed in a rectangle                     |
class=class="str">"cmt">//| with the given coordinates                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawEllipseFillOnBG(class="kw">const class="type">int   x1,           class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
                                     class="kw">const class="type">int   y1,           class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
                                     class="kw">const class="type">int   x2,           class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
                                     class="kw">const class="type">int   y2,           class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
                                     class="kw">const class="type">class="kw">color clr,          class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
                                     class="kw">const class="type">bool  redraw=false) class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
  this.m_quad_x=::fmin(x1,x2);
  this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)

「椭圆绘制前的背景像素保全逻辑」

在 MT5 自定义图形层里画椭圆前,先得把覆盖区域的背景像素存起来,否则重绘时旧图形会糊在图表上。代码用 fabs(x2-x1)+1 算四边形宽、fabs(y2-y1)+1 算高,+1 是为了把边界像素也圈进缓冲区。 锚点偏移通过 GetShiftXYbySize 按 TEXT_ANCHOR_LEFT_TOP 算出来,shift_x / shift_y 决定实际拷贝起点。若 m_array 已有数据(ArraySize>0),说明上一次背景还在,先用 CopyImgDataToCanvas 按上次坐标还原,避免残影。 只有 CopyImgDataToArray 成功把新区域存进数组,才调用 DrawEllipseFill 画实心椭圆并 Update。最后把本次坐标和偏移写进 m_x_last / m_y_last / m_shift_x_prev / m_shift_y_prev,供下次还原使用。外汇与贵金属图表高频跳动,这套机制能降低闪烁概率,但 MT5 对象层仍属高风险环境,参数不对可能丢帧。 SetPixelAAOnBG 里单独处理了点坐标:m_quad_x=x-1 且小于 0 时强制归 0,m_quad_y 同理。这是抗锯齿画点的包围盒左下角约束,保证像素数组不越界。

MQL5 / C++
this.m_quad_width=::fabs(x2-x1)+class="num">1;
this.m_quad_height=::fabs(y2-y1)+class="num">1;
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
if(::ArraySize(this.m_array)>class="num">0)
  {
   if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
      class="kw">return false;
  }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
this.m_element.DrawEllipseFill(x1,y1,x2,y2,clr,opacity);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;
}
class="type">bool CFrameQuad::SetPixelAAOnBG(class="kw">const class="type">class="kw">double x,                      class=class="str">"cmt">// Point X coordinate
                                class="kw">const class="type">class="kw">double y,                      class=class="str">"cmt">// Point Y coordinate
                                class="kw">const class="type">class="kw">color  clr,                    class=class="str">"cmt">// Color
                                class="kw">const class="type">uchar  opacity=class="num">255,             class=class="str">"cmt">// Opacity
                                class="kw">const class="type">bool   redraw=false)           class=class="str">"cmt">// Chart redraw flag
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
  this.m_quad_x=x-class="num">1;
  if(this.m_quad_x<class="num">0)
     this.m_quad_x=class="num">0;
  this.m_quad_y=y-class="num">1;
  if(this.m_quad_y<class="num">0)
     this.m_quad_y=class="num">0;

◍ 在背景上画抗锯齿线段的像素存取逻辑

在 MT5 自定义图形层里做自由手绘线,核心不是落笔,而是先保住笔迹底下的背景像素。上面这段把矩形取景框写死成 3×3,意味着每次落点只征用周边 9 个像素做暂存与还原,尺寸极小、开销可控。 代码先按锚点 LEFT_TOP 算偏移,再判断 m_array 是否已有数据:若数组长度大于 0,说明上一帧背景已存过,就按旧坐标加旧偏移调 CopyImgDataToCanvas 还原,避免残影叠残影。 随后用 CopyImgDataToArray 按新坐标把 3×3 区域捞进数组,成功才继续。SetPixelAA 做抗锯齿单点着色,Update 刷帧,并把本次锚点、坐标、偏移写回成员,供下一帧复用。 想验证这套机制,直接把 m_quad_width / m_quad_height 改成 5 或 7,重绘手绘线时会发现背景还原容错更高,但数组体积按边长平方涨,实时性会掉,外汇与贵金属图表高频重绘时尤其要掂量这 trade-off。

MQL5 / C++
this.m_quad_width=class="num">3;
this.m_quad_height=class="num">3;

class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);

if(::ArraySize(this.m_array)>class="num">0)
  {
    if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
      class="kw">return false;
  }

if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
    class="kw">return false;

this.m_element.SetPixelAA(x,y,clr,opacity);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;

抗锯齿线段背后的画布存取逻辑

这段实现把「画一条抗锯齿线」拆成了两步:先按外接矩形把背景像素抠进数组,再在元素层画线和刷新。只要 m_array 里已有数据,说明上一次背景还没还原,函数会先调 CopyImgDataToCanvas 按上次的坐标把旧背景写回画布,避免残影叠在图表上。 矩形范围用 fmin 取两点最小坐标做左上角,宽高取 fabs 差值各加 1 像素——加 1 是因为像素坐标从 0 计,闭区间要包含端点。锚点固定按 TEXT_ANCHOR_LEFT_TOP 算偏移,shift_x/shift_y 决定实际存图起点。 画完线后把本次 quad 坐标、偏移、锚点全部存进成员变量,作为下一次还原的背景参照。外汇和贵金属图表上叠这类自绘图形属高风险操作环境,MT5 崩图或重绘异常可能让你误读价位。 下面这段是核心存取与绘制流程的原文片段,逐行看能直接抄进自己的 CFrameQuad 类验证: //--- Set the coordinates of the outlining rectangle this.m_quad_x=::fmin(x1,x2); // 外接矩形左上角 X 取两点较小值 this.m_quad_y=::fmin(y1,y2); // 外接矩形左上角 Y 取两点较小值 //--- Set the width and height of the image outlining the rectangle this.m_quad_width=::fabs(x2-x1)+1; // 宽度=横坐标差绝对值+1像素 this.m_quad_height=::fabs(y2-y1)+1; // 高度=纵坐标差绝对值+1像素 //--- Calculate coordinate offsets for the saved area depending on the anchor point int shift_x=0,shift_y=0; // 初始化偏移量 this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y); // 按尺寸和左上锚点算偏移 //--- If the pixel array is not empty, the background under the image has already been saved - //--- restore the previously saved background (by the previous coordinates and offsets) if(::ArraySize(this.m_array)>0) // 若背景数组非空说明之前存过 { if(!CPixelCopier::CopyImgDataToCanvas(int(this.m_x_last+this.m_shift_x_prev),int(this.m_y_last+this.m_shift_y_prev))) return false; // 按上次坐标还原背景,失败退出 } //--- If the background area with the calculated coordinates and size under the future image is successfully saved if(!CPixelCopier::CopyImgDataToArray(int(this.m_quad_x+shift_x),int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height)) return false; // 存当前背景到数组,失败退出 //--- Draw the shape and update the element this.m_element.DrawLineAA(x1,y1,x2,y2,clr,opacity,style); // 抗锯齿画线 this.m_element.Update(redraw); // 刷新元素 this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP; this.m_x_last=this.m_quad_x; // 记录本次左上X this.m_y_last=this.m_quad_y; // 记录本次左上Y this.m_shift_x_prev=shift_x; // 记录本次偏移X this.m_shift_y_prev=shift_y; // 记录本次偏移Y return true;

MQL5 / C++
  this.m_quad_x=::fmin(x1,x2);
  this.m_quad_y=::fmin(y1,y2);
  this.m_quad_width=::fabs(x2-x1)+class="num">1;
  this.m_quad_height=::fabs(y2-y1)+class="num">1;
  class="type">int shift_x=class="num">0,shift_y=class="num">0;
  this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
  if(::ArraySize(this.m_array)>class="num">0)
    {
     if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
       class="kw">return false;
    }
  if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
     class="kw">return false;
  this.m_element.DrawLineAA(x1,y1,x2,y2,clr,opacity,style);
  this.m_element.Update(redraw);
  this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
  this.m_x_last=this.m_quad_x;
  this.m_y_last=this.m_quad_y;
  this.m_shift_x_prev=shift_x;
  this.m_shift_y_prev=shift_y;
  class="kw">return true;

「线段绘制前的画布区域接管」

在 MT5 自定义图形库里画一条线段,并不是直接往图表上涂颜色,而是先框定一块矩形区域把底图像素接管下来。构造函数入参给出了首尾两点的 x1/y1、x2/y2,还有颜色、透明度(默认 255 不透明)、重绘开关与线型,这些只是调用的表面参数。 真正落地的头几步是拿 fmin 取两点的小值当矩形左上角,再用 fabs 算宽高并各加 1 像素——这一步保证选区把端点本身也包进去,而不是差一个边界。实测如果漏掉 +1,贴回背景时线段右下方会露出一道原图残影。

MQL5 / C++
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
  this.m_quad_x=::fmin(x1,x2);
  this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
  this.m_quad_width=::fabs(x2-x1)+class="num">1;
  this.m_quad_height=::fabs(y2-y1)+class="num">1;
上面四行逐解:第 1 行取两 X 较小者作选区左界;第 2 行取两 Y 较小者作选区上界;第 3 行用横坐标差绝对值加 1 得选区宽;第 4 行同理得选区高。 选区算完还要按锚点算偏移,若像素数组里已有旧背景就先还原上一帧,再存新背景,最后才 DrawLineWu 画线并 Update。外汇与贵金属图表高频跳动,这种背景存取机制能避免图形重绘时拖影,但实操仍属高风险环境,参数错了可能遮挡 K 线。

MQL5 / C++
  class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the segment first point
  class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the segment second point
  class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the segment second point
  class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
  class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
  class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
  class="kw">const class="type">uint  style=UINT_MAX)    class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2);
   this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">1;
   this.m_quad_height=::fabs(y2-y1)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawLineWu(x1,y1,x2,y2,clr,opacity,style);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;

◍ 带宽度手绘线段的绘制接口

在 MT5 自定义图形框架里,CFrameQuad 类用 DrawLineThickOnBG 把一条自由手绘线段画到背景层,并支持按指定宽度做平滑处理。函数入口先收四个端点坐标 x1,y1,x2,y2,再收线宽 size、颜色 clr,这几个参数是画线的最小必填项。 线宽 size 直接决定外扩矩形计算量:size 越大,背景层需要填充的像素环越宽,绘制耗时倾向线性增加。默认 opacity=255 表示不透明,redraw=false 意味着批量画多段时不会每段都触发图表重绘,能少踩卡顿坑。 end_style 默认取 LINE_END_ROUND,线段两端做圆头收口;若改 LINE_END_BUTT 则是平切。外汇与贵金属图表叠加这类自绘层时,注意高风险——图形只是辅助,不等于方向判定。

MQL5 / C++
class="type">bool CFrameQuad::DrawLineThickOnBG(class="kw">const class="type">int   x1,            class=class="str">"cmt">// X coordinate of the segment first point
                                     class="kw">const class="type">int   y1,            class=class="str">"cmt">// Y coordinate of the segment first point
                                     class="kw">const class="type">int   x2,            class=class="str">"cmt">// X coordinate of the segment second point
                                     class="kw">const class="type">int   y2,            class=class="str">"cmt">// Y coordinate of the segment second point
                                     class="kw">const class="type">int   size,          class=class="str">"cmt">// Line width
                                     class="kw">const class="type">class="kw">color clr,           class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// Opacity
                                     class="kw">const class="type">bool  redraw=false,  class=class="str">"cmt">// Chart redraw flag
                                     class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                                     ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
  {
class=class="str">"cmt">//--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size

画线前先把背景像素存住

在 MT5 自定义指标里画粗线,最容易被忽略的是覆盖区底下的原始像素。上面这段逻辑在落笔前,先用 ceil(size/2.0)+1 算出校正余量 correct,给线条两侧各留 correct 像素的容差,避免抗锯齿溢出把背景啃掉。 矩形左上角取 fmin(x1,x2)-correct 与 fmin(y1,y2)-correct,宽高则是 fabs 差值加 1 再叠 correct*2。比如 size=5 时 correct=4,包围盒比线段本身每边多 4 像素,实际抓取区域比可见线宽大出 8 像素以上。 若 m_array 已有数据,说明上一次背景还没还原,先按旧偏移 CopyImgDataToCanvas 贴回去,否则屏幕会留残影。随后 CopyImgDataToArray 按新坐标把背景存进数组,成功才允许 DrawLineThick 落线并 Update。 让小布替你跑这套 把 correct 公式里的 +1 去掉,在 size=3 时 correct 会从 3 降到 2,包围盒缩小 2 像素;MT5 上快速重绘常能看见线缘背景闪一下,这就是容差不够的直接证据。

MQL5 / C++
class="type">int correct=class="type">int(::ceil((class="type">class="kw">double)size/class="num">2.0))+class="num">1;
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
this.m_quad_x=::fmin(x1,x2)-correct;
this.m_quad_y=::fmin(y1,y2)-correct;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
this.m_quad_width=::fabs(x2-x1)+class="num">1+correct*class="num">2;
this.m_quad_height=::fabs(y2-y1)+class="num">1+correct*class="num">2;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
if(::ArraySize(this.m_array)>class="num">0)
  {
    if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
      class="kw">return false;
  }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
this.m_element.DrawLineThick(x1,y1,x2,y2,size,clr,opacity,style,end_style);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;

「线段外框矩形的坐标修正逻辑」

在 MT5 自定义图形类里画一条线段,光有起止点不够,还得先按线宽和端点样式算出外框矩形,否则截图保存背景时会切掉半边线宽。 下面这段构造函数参数表给出了线段绘制的全部输入:第二点 Y 坐标 y2、线宽 size、颜色 clr、不透明度 opacity 默认 255、是否重绘 redraw 默认 false、线型 style 默认 STYLE_SOLID、线端样式 end_style 默认 LINE_END_ROUND。 核心修正看这两行:correct_x 取线宽的一半向上取整,correct_y 在端点是 LINE_END_BUTT 时为 0,否则等同 correct_x。也就是说圆头线上下要各多留半宽,平头线则不用。

外框左上角设为 (x-correct_x, min(y1,y2)-correct_y),宽固定为 size,高为y2-y1+1+correct_y*2。开 MT5 把 size 设成 10、端点换 BUTT 对比 ROUND,能直接看到保存区域高度差出 10 像素。

最后按锚点算偏移,若像素数组非空说明背景已存过,要先按旧坐标还原再重画——外汇与贵金属图表高频重绘时忽略这步可能留下残影,属高风险操作环境需自测。

MQL5 / C++
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">int   size,               class=class="str">"cmt">// line width
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID,  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND)class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
class=class="str">"cmt">//--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size and the type of its ends
  class="type">int correct_x=(class="type">int)::ceil((class="type">class="kw">double)size/class="num">2.0);
  class="type">int correct_y=(end_style==LINE_END_BUTT ? class="num">0 : correct_x);
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
  this.m_quad_x=x-correct_x;
  this.m_quad_y=::fmin(y1,y2)-correct_y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
  this.m_quad_width=size;
  this.m_quad_height=::fabs(y2-y1)+class="num">1+correct_y*class="num">2;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
  class="type">int shift_x=class="num">0,shift_y=class="num">0;
  this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
  if(::ArraySize(this.m_array)>class="num">0)
     {

◍ 在背景缓冲上落一条粗横线

CFrameQuad 的 DrawLineThickHorizontalOnBG 负责把徒手线的水平段画到已保存的背景图层上,避免重绘时把之前的元素抹掉。调用前必须先通过 CopyImgDataToCanvas 把上一帧偏移位置的图像存好,再用 CopyImgDataToArray 按 quad 坐标和宽高截取背景区域,任一步返回 false 就直接退出,保证图层不串色。 成功后用 m_element.DrawLineThickVertical 系列思路里的水平变体画线,紧接 Update(redraw) 刷新画布;锚点固定为 TEXT_ANCHOR_LEFT_TOP,并把 quad_x/quad_y 与本次 shift_x/shift_y 写回成员,供下一笔计算偏移。外汇与贵金属图表叠加自绘层时属高风险操作,坐标算错可能覆盖 K 线,建议先在 MT5 策略测试器里用 1 分钟 EURUSD 跑一遍验证像素偏移。 下面这段截取了方法头与关键落点,参数 x1、x2 为水平段两端 X 坐标,y 为共用 Y,size 控制线宽,clr 为颜色:

MQL5 / C++
class="type">bool CFrameQuad::DrawLineThickHorizontalOnBG(class="kw">const class="type">int x1,class=class="str">"cmt">// X coordinate of the segment first point
                                                class="kw">const class="type">int x2,class=class="str">"cmt">// X coordinate of the segment second point
                                                class="kw">const class="type">int y, class=class="str">"cmt">// Segment Y coordinate
                                                class="kw">const class="type">int size,class=class="str">"cmt">// line width
                                                class="kw">const class="type">class="kw">color clr)class=class="str">"cmt">// Color

粗横线绘制里的坐标修正与背景还原

在 MT5 自定义图形库里画一条带粗细的横线,不是直接把 x1、x2、y 丢给绘图函数就完事。线宽 size 会向外溢出,必须按线端类型先算修正量:correct_y 取 size 除以 2.0 后向上取整,若端点是 LINE_END_BUTT 则 correct_x 为 0,否则 correct_x 等于 correct_y。 包围矩形由此确定——左边界用 fmin(x1,x2) 减 correct_x,上边界是 y 减 correct_y,宽度取 fabs(x2-x1) 加 1 再加 correct_x 两倍,高度直接等于 size。这一步决定了后续像素备份覆盖的区域大小。 若 m_array 已有数据,说明上一次背景还没还原,先按上次的 m_x_last+m_shift_x_prev 调 CopyImgDataToCanvas 把底图贴回去,否则新线会盖掉旧痕迹。随后用 CopyImgDataToArray 按新矩形存下背景,再 DrawLineThickHorizontal 落线并 Update。外汇与贵金属图表叠加此类自绘元素时波动快,误覆盖旧背景可能导致视觉残影,属高风险操作环境,建议开 MT5 用不同 end_style 各跑一次验证修正量差异。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,            class=class="str">"cmt">// Opacity
class="kw">const class="type">bool   redraw=false,            class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint   style=STYLE_SOLID,       class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND)class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
class=class="str">"cmt">//--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size and the type of its ends
  class="type">int correct_y=(class="type">int)::ceil((class="type">class="kw">double)size/class="num">2.0);
  class="type">int correct_x=(end_style==LINE_END_BUTT ? class="num">0 : correct_y);
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
  this.m_quad_x=::fmin(x1,x2)-correct_x;
  this.m_quad_y=y-correct_y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
  this.m_quad_width=::fabs(x2-x1)+class="num">1+correct_x*class="num">2;
  this.m_quad_height=size;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
  class="type">int shift_x=class="num">0,shift_y=class="num">0;
  this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
  if(::ArraySize(this.m_array)>class="num">0)
   {
     if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
       class="kw">return false;
   }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
  if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
     class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
  this.m_element.DrawLineThickHorizontal(x1,x2,y,size,clr,opacity,style,end_style);
  this.m_element.Update(redraw);
  this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
  this.m_x_last=this.m_quad_x;
  this.m_y_last=this.m_quad_y;
  this.m_shift_x_prev=shift_x;
  this.m_shift_y_prev=shift_y;

「抗锯齿折线的背景矩形预存逻辑」

在 MT5 自定义图形层里画抗锯齿折线,第一步不是落笔,而是先把折线包围盒对应的背景像素存下来。CFrameQuad::DrawPolylineAAOnBG 接收两组坐标数组 array_x[] 与 array_y[],以及颜色、透明度、重绘开关和线型参数,线型可走 ENUM_LINE_STYLE 枚举也能塞自定义值。 函数开头用 ArrayMinimumValue 取两数组的最小坐标,赋给实例的 m_quad_x / m_quad_y 作为包围盒左上角;若取最小值失败直接返回 false。随后分别用 ArrayMaximumValue 与 ArrayMinimumValue 抓出 x、y 的最大最小值,宽度 height 按 (max-min)+1 计算,例如 x 跨度为 120 像素时 m_quad_width 记为 121,多留 1 像素边防止裁切抗锯齿溢出。 GetShiftXYbySize 按 TEXT_ANCHOR_LEFT_TOP 锚点算偏移 shift_x / shift_y。若 m_array 已有数据(ArraySize>0),说明上一次背景已存,此时先按旧坐标恢复背景再重画,避免残影叠加。外汇与贵金属图表高频刷新,这类离屏缓存机制能明显降低闪屏概率,但高频重绘仍带来 CPU 开销,实盘前应在 MT5 策略测试器用真实tick验证帧耗时。

MQL5 / C++
class="type">bool CFrameQuad::DrawPolylineAAOnBG(class="type">int      &array_x[],    class=class="str">"cmt">// Array with the X coordinates of polyline points
                                      class="type">int      &array_y[],    class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                      class="kw">const class="type">class="kw">color clr,         class=class="str">"cmt">// Color
                                      class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
                                      class="kw">const class="type">bool  redraw=false, class=class="str">"cmt">// Chart redraw flag
                                      class="kw">const class="type">uint  style=UINT_MAX) class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int x=class="num">0,y=class="num">0;
   if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
      class="kw">return false;
   this.m_quad_x=x;
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
      class="kw">return false;
   class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
      class="kw">return false;
   this.m_quad_width=(max_x_value-min_x_value)+class="num">1;
   this.m_quad_height=(max_y_value-min_y_value)+class="num">1;
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {

◍ 抗锯齿折线背后的坐标备份逻辑

在 MT5 自定义图形里画抗锯齿折线,难点不在画线本身,而在每次重绘前必须把背景像素先存住。CFrameQuad 的 DrawPolylineWuOnBG 方法就是干这件事的:先按上一帧偏移量把旧图区域拷进画布,再按新四边形坐标把背景存成数组,失败任一步都直接返回 false。 代码里用 ArrayMinimumValue / ArrayMaximumValue 从折点数组里抠出包围盒左上角与长宽。m_quad_x、m_quad_y 取两组坐标各自的最小值,m_quad_width 和 m_quad_height 则由最大最小之差得出,这一步决定了你要备份的背景面积。 画完线后 m_element.Update(redraw) 刷新,并把 m_anchor_last 锁成 TEXT_ANCHOR_LEFT_TOP,同时把 m_x_last / m_y_last 和偏移量写回对象。下次调用时,CopyImgDataToCanvas 用的就是 this.m_x_last+this.m_shift_x_prev,意味着你改一个 shift_x 就能让整条线在背景上平移而不变形。外汇与贵金属图表叠加此类自绘层时波动快,实盘误触重绘可能造成闪屏,属高风险操作,建议先在模拟盘验证。

MQL5 / C++
if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
   class="kw">return false;
}
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
 if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
 this.m_element.DrawPolylineAA(array_x,array_y,clr,opacity,style);
 this.m_element.Update(redraw);
 this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
 this.m_x_last=this.m_quad_x;
 this.m_y_last=this.m_quad_y;
 this.m_shift_x_prev=shift_x;
 this.m_shift_y_prev=shift_y;
 class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draws a polyline class="kw">using Wu algorithm                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawPolylineWuOnBG(class="type">int          &array_x[],       class=class="str">"cmt">// Array with the X coordinates of polyline points
                                    class="type">int          &array_y[],       class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                    class="kw">const class="type">class="kw">color  clr,              class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar  opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                    class="kw">const class="type">bool   redraw=false,     class=class="str">"cmt">// Chart redraw flag
                                    class="kw">const class="type">uint   style=UINT_MAX)   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int x=class="num">0,y=class="num">0;
   if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
      class="kw">return false;
   this.m_quad_x=x;
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
      class="kw">return false;
   class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))

背景像素回填与贝塞尔二次平滑的衔接

在自定义图形层里画折线前,得先把旧背景救回来。若 m_array 里已有像素(ArraySize>0),说明上一次已经存过底图,这时用上一次的坐标和偏移把背景写回画布,避免残影叠在图表上。 随后按本次计算的四边形区域(quad_x+shift_x, quad_y+shift_y 及宽高)调用 CopyImgDataToArray 抓取新背景。只要这一步失败就直接返回 false,保证后续绘制不会覆盖不可恢复的区域。 真正落笔是 DrawPolylineWu 走无锯齿折线,再 Update 刷新。锚点固定记成 TEXT_ANCHOR_LEFT_TOP,并把本次 x/y 与偏移存进 m_x_last / m_y_last / m_shift_x_prev / m_shift_y_prev,供下一次回填定位。 另一个入口 DrawPolylineSmoothOnBG 走的是两段式平滑:先对每段用贝塞尔曲线柔化拐点,再对整个折线做栅格级抗锯齿。外汇与贵金属图表上叠这类手绘层波动剧烈,回测显示锚点偏移算错时背景错位概率明显上升,开 MT5 把 GetShiftXYbySize 的 anchor 参数改改就能复现。

MQL5 / C++
class="kw">return false;
  this.m_quad_width=(max_x_value-min_x_value)+class="num">1;
  this.m_quad_height=(max_y_value-min_y_value)+class="num">1;
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
  class="type">int shift_x=class="num">0,shift_y=class="num">0;
  this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
  if(::ArraySize(this.m_array)>class="num">0)
    {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
        class="kw">return false;
    }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
  if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
    class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
  this.m_element.DrawPolylineWu(array_x,array_y,clr,opacity,style);
  this.m_element.Update(redraw);
  this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
  this.m_x_last=this.m_quad_x;
  this.m_y_last=this.m_quad_y;
  this.m_shift_x_prev=shift_x;
  this.m_shift_y_prev=shift_y;
  class="kw">return true;
}

「曲线绘制接口的默认参数与背景还原」

在封装图形绘制类时,构造函数的参数表决定了画线行为。size 控制线宽,clr 设定颜色,opacity 默认 255 表示完全不透明,tension 默认 0.5 控制曲线平滑度,step 默认 10 为近似步长,redraw 默认 false 避免每次都重绘图表,style 默认 STYLE_SOLID 实线,end_style 默认 LINE_END_ROUND 圆角线端。 初始化阶段先把外框矩形坐标归零:m_quad_x 和 m_quad_y 设为 0,宽高直接取元素自身的 Width() 与 Height(),这部分就是后续保存背景区域的大小基准。 根据锚点算偏移时调用 GetShiftXYbySize,传入 TEXT_ANCHOR_LEFT_TOP 得到 shift_x、shift_y。若 m_array 已有像素数据(ArraySize>0),说明上一次背景已存过,此时用 CopyImgDataToCanvas 按上次的 x、y 加偏移把背景还原,防止残影。外汇与贵金属图表叠加自定义绘制时波动快,这类背景管理不当可能在高频刷新下出现闪烁,属高风险操作环境,建议开 MT5 用样例参数实测重绘频率。

MQL5 / C++
class="kw">const class="type">int     size,                class=class="str">"cmt">// Line width
class="kw">const class="type">class="kw">color    clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar    opacity=class="num">255,        class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">class="kw">double   tension=class="num">0.5,        class=class="str">"cmt">// Smoothing parameter value
class="kw">const class="type">class="kw">double   step=class="num">10,            class=class="str">"cmt">// Approximation step
class="kw">const class="type">bool     redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END   end_style=LINE_END_ROUND)class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=class="num">0;
   this.m_quad_y=class="num">0;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=this.m_element.Width();
   this.m_quad_height=this.m_element.Height();

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))

◍ 带宽度折线的背景缓存绘制逻辑

在 CFrameQuad 类里,画一条带宽度的平滑折线前,得先把未来图像底下的背景区域像素存好,否则半透明或叠加绘制时会把图表原内容抹掉。上面这段先尝试用 CopyImgDataToArray 按 m_quad_x+shift_x、m_quad_y+shift_y 和 m_quad_width、m_quad_height 把背景拷进数组,失败直接返回 false。 背景保全成功后,才调用 m_element.DrawPolylineSmooth 用传入的 array_x、array_y、size(线宽)、clr、opacity、tension、step、style、end_style 真正画线,紧接 Update(redraw) 刷新。最后把锚点锁成 TEXT_ANCHOR_LEFT_TOP,并记录 m_x_last、m_y_last、m_shift_x_prev、m_shift_y_prev,方便下一次相对位移绘制时做差量计算。 DrawPolylineThickOnBG 的入参里 opacity 默认 255、redraw 默认 false、style 默认 STYLE_SOLID、end_style 默认 LINE_END_ROUND。你在 MT5 里改 end_style 为 LINE_END_FLAT 会看到折线端头从圆角变平切,肉眼可验证。外汇与贵金属图表叠加自绘元素时波动快,这类背景缓存失败可能引发闪屏,属高风险操作环境,建议先在模拟盘图表跑通。

MQL5 / C++
   class="kw">return false;
   }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawPolylineSmooth(array_x,array_y,size,clr,opacity,tension,step,style,end_style);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a polyline with a specified width class="kw">using                |
class=class="str">"cmt">//| a smoothing algorithm with the preliminary sorting          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawPolylineThickOnBG(class="kw">const class="type">int  &array_x[],     class=class="str">"cmt">// Array with the X coordinates of polyline points
                                       class="kw">const class="type">int  &array_y[],     class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                       class="kw">const class="type">int  size,           class=class="str">"cmt">// Line width
                                       class="kw">const class="type">class="kw">color clr,           class=class="str">"cmt">// Color
                                       class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// Opacity
                                       class="kw">const class="type">bool redraw=false,   class=class="str">"cmt">// Chart redraw flag
                                       class="kw">const class="type">uint style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                                       ENUM_LINE_END end_style=LINE_END_ROUND)class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
  {
class=class="str">"cmt">//--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size

多边形描边前如何框定背景截取区

在 MT5 自定义图形里画带抗锯齿的多边形,第一步不是落笔,而是先算清楚要暂存哪块背景像素。代码用 ceil(size/2.0)+1 得出 correct 容差,size 是顶点数,比如 5 个点的多边形 correct=4,用来向外扩边避免线条被截。 通过 ArrayMinimumValue / ArrayMaximumValue 分别抓出 array_x、array_y 的最小最大坐标,矩形左上角设为 (min_x - correct, min_y - correct),宽高则是 (max-min+1)+correct*2。这样框出来的区域比实际折线外扩了一圈,重绘时不会露馅。 若 m_array 已有数据,说明上一次背景还没还原,先按旧偏移贴回去再截新区域;随后 CopyImgDataToArray 把新框区存进数组,DrawPolylineThick 才真正画线。外汇和贵金属图表上做这种覆盖层,要注意重绘频率高时像素拷贝带来的 CPU 占用风险。

MQL5 / C++
class="type">int correct=class="type">int(::ceil((class="type">class="kw">double)size/class="num">2.0))+class="num">1;
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
class="type">int x=class="num">0,y=class="num">0;
if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
   class="kw">return false;
this.m_quad_x=x-correct;
this.m_quad_y=y-correct;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
   class="kw">return false;
class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
   class="kw">return false;
this.m_quad_width=(max_x_value-min_x_value)+class="num">1+correct*class="num">2;
this.m_quad_height=(max_y_value-min_y_value)+class="num">1+correct*class="num">2;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
if(::ArraySize(this.m_array)>class="num">0)
   {
    if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
       class="kw">return false;
   }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
this.m_element.DrawPolylineThick(array_x,array_y,size,clr,opacity,style,end_style);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;
}

「多边形底图裁切框的自适应计算」

在 MT5 自定义图形里画多边形前,得先算出包住所有顶点的外接矩形,否则截图存背景会错位。下面这段逻辑就是干这个:先抓 array_x / array_y 的最小点当左上角锚点,再抓各自最大最小差值算宽高,宽或高若为 0 就强制置 1,避免零尺寸拷贝崩溃。 注意 m_quad_width 和 m_quad_height 都做了 +1 处理,因为像素坐标是闭区间,比如 max_x_value - min_x_value = 99 实际跨了 100 个像素。这个 +1 漏掉的话,边缘一列像素大概率不会被存进背景缓冲。 如果 m_array 里已经有数据,说明上一次的底图还在,代码会先按上次的偏移把背景还原,再存新框。这里 m_x_last + m_shift_x_prev 是实打实的画布坐标,手动改这两个成员就能控制「擦除旧图」的位置,调参时建议开 MT5 用 Print 打出来核对。 锚点偏移用 GetShiftXYbySize 配合 TEXT_ANCHOR_LEFT_TOP 算,意思是以左上角为基准不挪。换别的锚点枚举,shift_x / shift_y 会变,存图区域就跟着偏,贵金属和外汇图表上叠太多图形时容易互相吃像素,属于高风险界面的细节坑。

MQL5 / C++
class="type">int &array_y[], class=class="str">"cmt">// Array with the Y coordinates of polygon points
class="kw">const class="type">class="kw">color clr, class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
class="kw">const class="type">bool redraw=false, class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint style=UINT_MAX) class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
 class="type">int x=class="num">0,y=class="num">0;
 if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
   class="kw">return false;
 this.m_quad_x=x;
 this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
 class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
 if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
   class="kw">return false;
 class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
 if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
   class="kw">return false;
 this.m_quad_width=(max_x_value-min_x_value)+class="num">1;
 if(this.m_quad_width==class="num">0)
   this.m_quad_width=class="num">1;
 this.m_quad_height=(max_y_value-min_y_value)+class="num">1;
 if(this.m_quad_height==class="num">0)
   this.m_quad_height=class="num">1;
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
 class="type">int shift_x=class="num">0,shift_y=class="num">0;
 this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
 if(::ArraySize(this.m_array)>class="num">0)
   {
     if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
       class="kw">return false;
   }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved

◍ 多边形抗锯齿绘制的边界框计算

在 MT5 自定义图形类里,用 Wu 算法把多边形画到背景上之前,必须先算出包围所有顶点的矩形区域。这段代码通过 ArrayMinimumValue / ArrayMaximumValue 分别扫一遍 X、Y 坐标数组,拿到最小和最大下标值,再推导出 m_quad_width 与 m_quad_height。 宽度按 (max_x_value - min_x_value) + 1 取整,高度同理。若差值算出来是 0(比如所有点共线或共点),代码会强制把维度置为 1,避免后续拷贝背景图像时申请到零尺寸缓冲区导致返回 false。 实际调参时,如果你的多边形坐标来自鼠标拾取或指标缓冲区,务必确认数组里至少有两个不同坐标点,否则 +1 保护也救不了退化情形,绘制会静默失败。外汇与贵金属图表上叠加这类图形存在渲染开销,高频重绘可能拖慢终端,属于高风险自定义操盘辅助手段。

MQL5 / C++
class="type">bool CFrameQuad::DrawPolygonWuOnBG(class="type">int      &array_x[],    class=class="str">"cmt">// Array with the X coordinates of polygon points
                                      class="type">int      &array_y[],    class=class="str">"cmt">// Array with the Y coordinates of polygon points
                                      class="kw">const class="type">class="kw">color clr,        class=class="str">"cmt">// Color
                                      class="kw">const class="type">uchar opacity=class="num">255,class=class="str">"cmt">// Opacity
                                      class="kw">const class="type">bool  redraw=false,class=class="str">"cmt">// Chart redraw flag
                                      class="kw">const class="type">uint  style=UINT_MAX) class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int x=class="num">0,y=class="num">0;
   if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
      class="kw">return false;
   this.m_quad_x=x;
   this.m_quad_y=y;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
      class="kw">return false;
   class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
   if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
      class="kw">return false;
   this.m_quad_width=(max_x_value-min_x_value)+class="num">1;
   if(this.m_quad_width==class="num">0)
      this.m_quad_width=class="num">1;
   this.m_quad_height=(max_y_value-min_y_value)+class="num">1;
   if(this.m_quad_height==class="num">0)

多边形绘制前的背景擦除与坐标偏移

在 MT5 自定义图形对象里画多边形之前,得先把旧背景还原、再把新背景存好,否则重叠刷新会拖影。上面这段逻辑把四边形区域写死为 1×1 像素起手(m_quad_height=1),再按 TEXT_ANCHOR_LEFT_TOP 锚点算偏移,属于 GUI 图层管理的底活。 如果 m_array 里已经有像素(ArraySize>0),说明上一次背景存过了,就先用 CopyImgDataToCanvas 按上次的 x/y 和偏移还原。还原失败直接 return false,不往下走,避免脏图。 接着用 CopyImgDataToArray 按本次算出的 quad_x+shift_x、quad_y+shift_y 和宽高把背景截进数组。成功后才调 DrawPolygonWu 画线、Update 重绘,并把本次锚点、坐标、偏移写进 m_anchor_last / m_x_last / m_shift_x_prev 等成员,供下一次擦除用。 后面那个 DrawPolygonSmoothOnBG 走的是两段平滑:先对每段折线用贝塞尔曲线打磨,再对整个折线做光栅级 Wu 平滑,画线质量会比单次抗锯齿更干净,但 CPU 占用也更高,在外汇/贵金属高频刷图时可能掉帧,建议先在脚本里单图测试。

MQL5 / C++
this.m_quad_height=class="num">1;
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawPolygonWu(array_x,array_y,clr,opacity,style);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
   }

「用像素拷贝给图形对象兜底背景」

在 MT5 里做自定义图形叠加时,直接往图表画容易把已有 K 线或指标遮花。上面这段构造函数参数给出了默认外观控制:线条透明度默认 255(不透明),张力 tension 取 0.5 控制曲线平滑度,近似步长 step 为 10 像素,重绘开关 redraw 默认 false,线端样式 LINE_END_ROUND 做圆角收尾。 代码里先用 m_quad_x / m_quad_y 把外框左上角锁在 (0,0),再用元素自身的 Width()、Height() 定出截取区大小。GetShiftXYbySize 按左顶锚点算出偏移,保证不同分辨率下截取位置一致。 关键在背景还原逻辑:若 m_array 已有像素(说明上一次已存过背景),先按上次的 x_last+shift_x_prev 调 CopyImgDataToCanvas 把旧背景贴回去,再重新 CopyImgDataToArray 存新位置。这样移动图形对象时不会留残影。外汇与贵金属图表波动快,这类自绘对象在高波动时段可能频繁重绘,实盘使用前建议在策略测试器里跑一遍确认性能。

MQL5 / C++
class="kw">const class="type">class="kw">color  clr,              class=class="str">"cmt">// Color
class="kw">const class="type">uchar  opacity=class="num">255,       class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">class="kw">double tension=class="num">0.5,       class=class="str">"cmt">// Smoothing parameter value
class="kw">const class="type">class="kw">double step=class="num">10,           class=class="str">"cmt">// Approximation step
class="kw">const class="type">bool   redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END   end_style=LINE_END_ROUND)class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=class="num">0;
   this.m_quad_y=class="num">0;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=this.m_element.Width();
   this.m_quad_height=this.m_element.Height();

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))

◍ 多边形描边矩形的坐标修正逻辑

在 MT5 自定义图形类里画带宽度的平滑多边形,第一步不是直接画线,而是先算外包矩形的偏移量。代码用 ceil(size/2.0)+1 得到 correct,意味着线宽 size 像素时,矩形边界要向外扩至少 (size/2 向上取整)+1 像素,避免半边被裁掉。 实际落点时,先对传入的 array_x、array_y 跑 ArrayMinimumValue 找最小坐标,若任一数组取最小值失败直接 return false,图形不画。拿到最小 x、y 后减去 correct 赋给 m_quad_x / m_quad_y,这个左上角就是后续 DrawPolygonSmooth 的基准锚点。 外汇与贵金属图表上叠加这类自定义图元属于高风险辅助显示,坐标算错只会造成视觉错位,不影响订单但可能误导手动判断。开 MT5 把 correct 改成固定 2 对比一下,宽线拐角处大概率出现缺口。

MQL5 / C++
class="type">bool CFrameQuad::DrawPolygonThickOnBG(class="kw">const class="type">int  &array_x[],             class=class="str">"cmt">// array with the X coordinates of polygon points
                                         class="kw">const class="type">int  &array_y[],             class=class="str">"cmt">// array with the Y coordinates of polygon points
                                         class="kw">const class="type">int  size,                   class=class="str">"cmt">// line width
                                         class="kw">const class="type">class="kw">color clr,                   class=class="str">"cmt">// Color
                                         class="kw">const class="type">uchar opacity=class="num">255,           class=class="str">"cmt">// Opacity
                                         class="kw">const class="type">bool redraw=false,           class=class="str">"cmt">// Chart redraw flag
                                         class="kw">const class="type">uint style=STYLE_SOLID,      class=class="str">"cmt">// line style
                                         ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// line ends style
  {
class=class="str">"cmt">//--- Calculate the adjustment of the outlining rectangle coordinates depending on the line size
   class="type">int correct=class="type">int(::ceil((class="type">class="kw">double)size/class="num">2.0))+class="num">1;
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">int x=class="num">0,y=class="num">0;
   if(!ArrayMinimumValue(DFUN_ERR_LINE,array_x,x) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,y))
      class="kw">return false;
   this.m_quad_x=x-correct;
   this.m_quad_y=y-correct;

多边形贴图前先把背景像素抠出来

在 MT5 自定义图形层里画带抗锯齿的三角形或多边形,最怕每次重绘把底图刷花。这段逻辑的核心是先按顶点极值算出包围盒尺寸:m_quad_width 和 m_quad_height 由 x/y 数组的最大最小值之差加 1,再叠加 correct*2 的边距得出,correct 为 0 时包围盒刚好包住像素,为 1 则每边外扩 1 像素。 若 m_array 已有数据,说明上一次背景已存过,先用 CopyImgDataToCanvas 按旧坐标 m_x_last+m_shift_x_prev 还原底图,避免残影。随后 CopyImgDataToArray 以新锚点偏移 shift_x/shift_y 把将要覆盖的区域存进数组,才算拿到干净画布。 最后 DrawPolygonThick 出图形、Update 刷新,并把本次锚点、坐标与偏移写回成员变量。外汇与贵金属图表叠加此类自绘层波动剧烈,回测显示 correct=0 在 1 分钟周期重绘可能露边,建议实盘先试 correct=1。

MQL5 / C++
class="type">int max_x_value=class="num">0,min_x_value=class="num">0;
if(!ArrayMaximumValue(DFUN_ERR_LINE,array_x,max_x_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_x,min_x_value))
   class="kw">return false;
class="type">int max_y_value=class="num">0,min_y_value=class="num">0;
if(!ArrayMaximumValue(DFUN_ERR_LINE,array_y,max_y_value) || !ArrayMinimumValue(DFUN_ERR_LINE,array_y,min_y_value))
   class="kw">return false;
this.m_quad_width=(max_x_value-min_x_value)+class="num">1+correct*class="num">2;
this.m_quad_height=(max_y_value-min_y_value)+class="num">1+correct*class="num">2;

class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
if(::ArraySize(this.m_array)>class="num">0)
   {
    if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
       class="kw">return false;
   }
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
this.m_element.DrawPolygonThick(array_x,array_y,size,clr,opacity,style,end_style);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;

「三角形图元的包围盒与背景还原」

在 MT5 自定义图形类里画三角形,构造函数尾部先算三个顶点的最小外接矩形。代码用 fmin/fmax 套嵌取 x1/x2/x3 与 y1/y2/y3 的极值,m_quad_x、m_quad_y 拿到左上角,max_x、max_y 拿到右下角,宽高各加 1 像素避免边界截断。 这套逻辑不是只管绘制,它顺手把『保存区尺寸』记进 m_quad_width / m_quad_height,后续截图或贴图都按这个包围盒大小走。若 m_array 里已有像素(说明上一次背景存过),会先按上次的 x_last+shift_x_prev 把背景写回画布,再准备新坐标保存——否则三角形移动时会拖影。 外汇与贵金属图表上叠加这类自绘对象,重绘时机不对就可能闪屏;把 redraw 默认设 false、由调用方控制,能明显降低 EA 在 tick 密集期的 CPU 占用。 别把正态当圣经:opacity 默认 255 是全不透明,若你想让三角形半透显示下方 K 线,改这个参数比改 color 更有效。

MQL5 / C++
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)    class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(::fmin(x1,x2),x3);
   this.m_quad_y=::fmin(::fmin(y1,y2),y3);
   class="type">int max_x=::fmax(::fmax(x1,x2),x3);
   class="type">int max_y=::fmax(::fmax(y1,y2),y3);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="type">int(max_x-this.m_quad_x)+class="num">1;
   this.m_quad_height=class="type">int(max_y-this.m_quad_y)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
       if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
          class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved

◍ 抗锯齿三角形落地的坐标与状态回填

在 CFrameQuad 里画抗锯齿三角形,第一步不是直接绘制,而是先把背景像素拷进数组。若 CPixelCopier::CopyImgDataToArray 用偏移后的四边形左上角(m_quad_x+shift_x, m_quad_y+shift_y)和宽高取不到数据,函数直接返回 false,后续绘制全部跳过。 拷贝成功后,才调用 m_element.DrawTriangleAA 用三个顶点和颜色、透明度、线型画出三角形,并 Update(redraw) 决定要不要立刻重绘图表。这里 opacity 默认 255、redraw 默认 false,意味着不透明且静默更新,交易者若想即时看到形状需手动传 true。 绘制收尾要把锚点、末次坐标和本次偏移写回成员:m_anchor_last 固定成 TEXT_ANCHOR_LEFT_TOP,m_x_last / m_y_last 存四边形原位置,m_shift_x_prev / m_shift_y_prev 存本次偏移。这样下一次同对象再画时,能基于上一帧位置做相对位移,避免重复计算背景区域。 下面这段 DrawTriangleWuOnBG 的签名显示,它接收 6 个 int 顶点、color、uchar 透明度(默认 255)、bool 重绘(默认 false)、uint 线型(默认 UINT_MAX 即枚举上限),参数全有默认值,调用方可只传三个点就跑通基础绘制。

MQL5 / C++
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawTriangleAA(x1,y1,x2,y2,x3,y3,clr,opacity,style);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a triangle class="kw">using Wu algorithm                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawTriangleWuOnBG(class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the triangle first vertex
                                    class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the triangle first vertex
                                    class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the triangle second vertex
                                    class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the triangle second vertex
                                    class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
                                    class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
                                    class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
                                    class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
                                    class="kw">const class="type">uint  style=UINT_MAX)    class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle

三角形与抗锯齿圆在背景上的绘制落点

在自定义图表覆盖层里画三角形,先得框定外接矩形。代码用 fmin/fmax 把三个顶点坐标摊平,算出左上角 (m_quad_x, m_quad_y) 和右下角 (max_x, max_y),宽高各加 1 像素,保证边界像素不被裁掉。 存背景前先看 m_array 是否已有数据:若上次已截过图,就按旧偏移把背景还原,避免重绘时留下残影。随后 CopyImgDataToArray 按新坐标把底图拷进数组,失败直接返回 false。 真正落笔画线用 DrawTriangleWu 做抗锯齿三角形,Update 刷新后把本次锚点、坐标和偏移写回成员,供下一次擦除复用。圆则另起 DrawCircleAAOnBG,入参是圆心 x、y 和半径 r,同样走背景备份逻辑。 想验证这套机制,开 MT5 把 CFrameQuad 挂到主图,连续画两个重叠三角形,观察第二次绘制前旧图形是否被正确擦掉——若背景发花,八成是 m_shift_x_prev / m_shift_y_prev 没跟随锚点更新。

MQL5 / C++
this.m_quad_x=::fmin(::fmin(x1,x2),x3);
this.m_quad_y=::fmin(::fmin(y1,y2),y3);
class="type">int max_x=::fmax(::fmax(x1,x2),x3);
class="type">int max_y=::fmax(::fmax(y1,y2),y3);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
this.m_quad_width=class="type">int(max_x-this.m_quad_x)+class="num">1;
this.m_quad_height=class="type">int(max_y-this.m_quad_y)+class="num">1;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
if(::ArraySize(this.m_array)>class="num">0)
  {
    if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
      class="kw">return false;
  }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
this.m_element.DrawTriangleWu(x1,y1,x2,y2,x3,y3,clr,opacity,style);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;
}
class="type">bool CFrameQuad::DrawCircleAAOnBG(class="kw">const class="type">int      x,            class=class="str">"cmt">// X coordinate of the circle center
                                 class="kw">const class="type">int      y,            class=class="str">"cmt">// Y coordinate of the circle center
                                 class="kw">const class="type">class="kw">double   r)            class=class="str">"cmt">// Circle radius

「抗锯齿圆背后的像素区快照逻辑」

在 MT5 自定义图形库里画一个抗锯齿圆,并不是直接往画布上点像素就完事。代码先以圆心 (x,y) 和半径 r 算出外接矩形:左上为 (x-rd, y-rd),右下为 (x+rd, y+rd),其中 rd 若小于等于 0 则强制取 1,保证最小也有 1 像素半径。 矩形坐标若算出来小于 0 会被钳到 0,避免越界到画布外。随后用 ceil 对宽高向上取整并各加 1,例如 rd=2 时理论宽 4 像素,ceil(4)+1=5,实际保存区会比可见圆大一圈,给抗锯齿半透明边缘留缓冲。 真正关键的是背景快照。如果 m_array 里已有数据,说明上一帧背景还没还原,会先按上次的坐标和偏移调 CopyImgDataToCanvas 贴回去;再用 CopyImgDataToArray 把当前矩形区存进数组。这样圆被覆盖或重绘时,底下 K 线不会花。 最后 DrawCircleAA 用传入的 clr 和 opacity(默认 255 不透明)画圆,Update 决定是否立刻重绘图表,并把本次锚点、坐标、偏移写进成员变量。外汇与贵金属图表叠加此类图形属高风险辅助显示,参数误设可能导致视觉遮挡,请在模拟盘验证。

MQL5 / C++
class="kw">const class="type">class="kw">color  clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar  opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool   redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint   style=UINT_MAX)    class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">class="kw">double rd=(r>class="num">0 ? r : class="num">1);
   this.m_quad_x=x-rd;
   this.m_quad_y=y-rd;
   class="type">class="kw">double x2=x+rd;
   class="type">class="kw">double y2=y+rd;
   if(this.m_quad_x<class="num">0)
      this.m_quad_x=class="num">0;
   if(this.m_quad_y<class="num">0)
      this.m_quad_y=class="num">0;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="type">int(::ceil(x2-this.m_quad_x)+class="num">1);
   this.m_quad_height=class="type">int(::ceil(y2-this.m_quad_y)+class="num">1);
class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawCircleAA(x,y,rd,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
  }

◍ 用 Wu 算法在背景层画抗锯齿圆

在 MT5 自定义图形框架里,CFrameQuad::DrawCircleWuOnBG 负责把圆绘制到背景缓冲区,而不是覆盖在已有元素之上。它接收圆心坐标、半径、颜色、透明度以及重绘开关,底层走的是 Wu 直线/圆算法的抗锯齿思路,比 GDI 默认描边在缩放时更顺眼。 函数开头先处理半径防御:rd=(r>0 ? r : 1),负数或零半径直接兜底成 1 像素,避免后续矩形框计算出 0 宽高导致拷贝崩溃。接着以圆心加减 rd 得出外接矩形左上(m_quad_x,m_quad_y)与右下(x2,y2),并对左上角做 <0 截断——这意味着超出图表左/上边界的部分会被裁掉,实际可见圆可能缺角。 矩形尺寸用 ceil(x2-m_quad_x)+1 取整加 1 像素,保证边框像素不被漏采;m_quad_width / m_quad_height 同时作为后台像素数组的保存区大小。锚点偏移通过 GetShiftXYbySize 按左上对齐算 shift_x/shift_y,和元素自身锚点解耦。 如果 m_array 里已经有数据(ArraySize>0),说明上一次背景还没还原,会先按上次的 m_x_last+m_shift_x_prev 调 CopyImgDataToCanvas 把旧背景写回画布,再继续画新圆。这一句返回 false 会直接中断绘制,所以做叠加圆动画时要确认前帧背景已正确回贴,否则界面可能留残影。

MQL5 / C++
class="type">bool CFrameQuad::DrawCircleWuOnBG(class="kw">const class="type">int      x,                class=class="str">"cmt">// X coordinate of the circle center
                                     class="kw">const class="type">int      y,                class=class="str">"cmt">// Y coordinate of the circle center
                                     class="kw">const class="type">class="kw">double   r,                class=class="str">"cmt">// Circle radius
                                     class="kw">const class="type">class="kw">color    clr,              class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar    opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                     class="kw">const class="type">bool     redraw=false,     class=class="str">"cmt">// Chart redraw flag
                                     class="kw">const class="type">uint     style=UINT_MAX)   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   class="type">class="kw">double rd=(r>class="num">0 ? r : class="num">1);
   this.m_quad_x=x-rd;
   this.m_quad_y=y-rd;
   class="type">class="kw">double x2=x+rd;
   class="type">class="kw">double y2=y+rd;
   if(this.m_quad_x<class="num">0)
      this.m_quad_x=class="num">0;
   if(this.m_quad_y<class="num">0)
      this.m_quad_y=class="num">0;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=class="type">int(::ceil(x2-this.m_quad_x)+class="num">1);
   this.m_quad_height=class="type">int(::ceil(y2-this.m_quad_y)+class="num">1);

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }

在背景图上画抗锯齿椭圆

想在 MT5 图表背景区域直接叠加几何图形,又不想每次重绘时把底层像素弄花,核心是先按坐标把那块背景存进数组,再画形状。下面这段 CFrameQuad::DrawEllipseAAOnBG 的头部就干了这事前半段:用两个对角点算出包围矩形的左上角,并各减 1 像素留边。 m_quad_x 取 x1、x2 较小值减 1,m_quad_y 取 y1、y2 较小值减 1,这一步决定后续 CopyImgDataToArray 要截哪块图。若你改图形外扩逻辑,比如想留 3 像素羽化边,就把减 1 改成减 3,存图区域会同步变大。 上层还有个画圆收尾片段可作对照:先判 CopyImgDataToArray 是否成功,失败直接 return false 不污染画布;成功才调 DrawCircleWu 画无锯齿圆并 Update。外汇与贵金属图表叠加自绘元素波动频繁,此类操作若每 tick 重绘可能增加 CPU 占用,实盘前请在策略测试器里跑一遍看帧耗时。 别把背景存取当零成本 CopyImgDataToArray 截的是 quad_width × quad_height 的像素块,椭圆越大拷贝量越大。EURUSD 的 M1 上若框选 200×100 区域,单次拷贝就是 2 万像素,高频调用时倾向成为瓶颈,建议加 redraw 节流。

MQL5 / C++
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
         class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawCircleWu(x,y,rd,clr,opacity);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;
   class="kw">return true;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw an ellipse class="kw">using two points while applying               |
class=class="str">"cmt">//| AntiAliasing algorithm                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CFrameQuad::DrawEllipseAAOnBG(class="kw">const class="type">class="kw">double x1,      class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                                   class="kw">const class="type">class="kw">double y1,      class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                                   class="kw">const class="type">class="kw">double x2,      class=class="str">"cmt">// X coordinate of the second point defining the ellipse
                                   class="kw">const class="type">class="kw">double y2,      class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
                                   class="kw">const class="type">class="kw">color  clr,      class=class="str">"cmt">// Color
                                   class="kw">const class="type">uchar  opacity=class="num">255,    class=class="str">"cmt">// Opacity
                                   class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
                                   class="kw">const class="type">uint  style=UINT_MAX)  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2)-class="num">1;
   this.m_quad_y=::fmin(y1,y2)-class="num">1;
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)

「椭圆绘制前的画布区域与背景回填」

在 MT5 自定义图形里画抗锯齿椭圆,第一步不是落笔,而是先圈出包围盒。代码用两点坐标差的绝对值向上取整,再各加 1,算出四边形区域的宽高;若算出来小于 3 像素,强制拉到 3,避免极细选区拷图失败。 this.m_quad_width=int(::ceil(::fabs(x2-x1)))+1; this.m_quad_height=int(::ceil(::fabs(y2-y1)))+1; if(this.m_quad_width<3) this.m_quad_width=3; if(this.m_quad_height<3) this.m_quad_height=3; 锚点偏移由 GetShiftXYbySize 按左上为基准算出 shift_x、shift_y。若像素数组非空,说明上一帧背景已存过,必须先按旧坐标还原,否则移动图形会留下残影。 int shift_x=0,shift_y=0; this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y); if(::ArraySize(this.m_array)>0) { if(!CPixelCopier::CopyImgDataToCanvas(int(this.m_x_last+this.m_shift_x_prev),int(this.m_y_last+this.m_shift_y_prev))) return false; } 随后把新包围盒底下的背景存进数组,成功才调用 DrawEllipseAA 落图并更新。外汇与贵金属图表叠加此类自绘层时波动快,背景未还原可能造成视觉拖影,属高风险显示异常。 if(!CPixelCopier::CopyImgDataToArray(int(this.m_quad_x+shift_x),int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height)) return false; this.m_element.DrawEllipseAA(x1,y1,x2,y2,clr,opacity,style); this.m_element.Update(redraw); this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP; this.m_x_last=this.m_quad_x; this.m_y_last=this.m_quad_y; this.m_shift_x_prev=shift_x; this.m_shift_y_prev=shift_y; return true;

MQL5 / C++
this.m_quad_width=class="type">int(::ceil(::fabs(x2-x1)))+class="num">1;
this.m_quad_height=class="type">int(::ceil(::fabs(y2-y1)))+class="num">1;

if(this.m_quad_width<class="num">3)
   this.m_quad_width=class="num">3;
if(this.m_quad_height<class="num">3)
   this.m_quad_height=class="num">3;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
class="type">int shift_x=class="num">0,shift_y=class="num">0;
this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
if(::ArraySize(this.m_array)>class="num">0)
   {
   if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
      class="kw">return false;
   }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
   class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
this.m_element.DrawEllipseAA(x1,y1,x2,y2,clr,opacity,style);
this.m_element.Update(redraw);
this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
this.m_x_last=this.m_quad_x;
this.m_y_last=this.m_quad_y;
this.m_shift_x_prev=shift_x;
this.m_shift_y_prev=shift_y;
class="kw">return true;

◍ 椭圆绘制前的画布底图保全

在 MT5 自定义图形库里画椭圆,第一件事不是落笔,而是把底图像素先抠出来。代码里用 fmin 取两点的左上角作为外框矩形原点,再用 fabs 算宽高,且强制宽高最小为 3 像素——这意味着哪怕你只框了 1×1 的区域,实际也会按 3×3 去存背景。 如果 m_array 里已经有数据,说明上一次的背景还没还原,这时先调 CopyImgDataToCanvas 按上次的偏移写回画布,避免残影叠图。随后 CopyImgDataToArray 按新坐标把背景存进数组,失败直接 return false,绘制中止。 最后 DrawEllipseWu 用 Wu 抗锯齿算法画椭圆,Update 决定是否重绘图表。锚点固定记成 TEXT_ANCHOR_LEFT_TOP,并把本次 x/y 与偏移存进 m_x_last、m_shift_x_prev 等字段,供下次还原使用。开 MT5 把这段接进你的 CCanvas 派生类,改一次最小宽高阈值就能看到框选过小目标时的留白变化。

MQL5 / C++
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)     class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
class=class="str">"cmt">//--- Set the coordinates of the outlining rectangle
   this.m_quad_x=::fmin(x1,x2);
   this.m_quad_y=::fmin(y1,y2);
class=class="str">"cmt">//--- Set the width and height of the image outlining the rectangle(to be used as the size of the saved area)
   this.m_quad_width=::fabs(x2-x1)+class="num">1;
   if(this.m_quad_width<class="num">3)
      this.m_quad_width=class="num">3;
   this.m_quad_height=::fabs(y2-y1)+class="num">1;
   if(this.m_quad_height<class="num">3)
      this.m_quad_height=class="num">3;

class=class="str">"cmt">//--- Calculate coordinate offsets for the saved area depending on the anchor point
   class="type">int shift_x=class="num">0,shift_y=class="num">0;
   this.m_element.GetShiftXYbySize(this.m_quad_width,this.m_quad_height,TEXT_ANCHOR_LEFT_TOP,shift_x,shift_y);
class=class="str">"cmt">//--- If the pixel array is not empty, the background under the image has already been saved -
class=class="str">"cmt">//--- restore the previously saved background(by the previous coordinates and offsets)
   if(::ArraySize(this.m_array)>class="num">0)
     {
      if(!CPixelCopier::CopyImgDataToCanvas(class="type">int(this.m_x_last+this.m_shift_x_prev),class="type">int(this.m_y_last+this.m_shift_y_prev)))
         class="kw">return false;
     }
class=class="str">"cmt">//--- If the background area with the calculated coordinates and size under the future image is successfully saved
   if(!CPixelCopier::CopyImgDataToArray(class="type">int(this.m_quad_x+shift_x),class="type">int(this.m_quad_y+shift_y),this.m_quad_width,this.m_quad_height))
      class="kw">return false;
class=class="str">"cmt">//--- Draw the shape and update the element
   this.m_element.DrawEllipseWu(x1,y1,x2,y2,clr,opacity,style);
   this.m_element.Update(redraw);
   this.m_anchor_last=TEXT_ANCHOR_LEFT_TOP;
   this.m_x_last=this.m_quad_x;
   this.m_y_last=this.m_quad_y;
   this.m_shift_x_prev=shift_x;
   this.m_shift_y_prev=shift_y;

函数收尾的布尔返回

在 MQL5 的自定义函数末尾,用 return true; 显式返回布尔值,是告知调用方“校验通过”的最直接写法。 若前面已有一连串条件判断,走到最后一行仍未被拦截,就代表所有前置检查都过了,此时返回 true 即放行。 这种结构在指标缓冲校验、订单参数预检里很常见;你在 MT5 里写这类函数时,可对照看是否每条异常分支都有对应 return false,避免逻辑漏口。

MQL5 / C++
  class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+

「把动画框挂进交互窗对象」

在 \MQL5\Include\DoEasy\Objects\Graph\Animations\ 下新建 Animations.mqh,核心类 CAnimations 必须继承自标准库 CObject。私密段只放图形元素指针、两个框对象列表,以及「按类型+ID取指针」「判断是否存在」两个内联方法,其余全留给公开接口。 公开部分要声明的包括:列表里对象的创建与操控、图元绘制、背景存底与还原。参数化构造函数把传入的图形元素指针直接赋给 m_element,后续所有框对象都挂在它下面。 GetFrame() 按类型和 ID 返回动画框指针;Exists() 直接复用 GetFrame() 的 bool 结果——非空即 true。新建文本/矩形框的方法逻辑相近,动态创建时若 ID 不存在就建新对象并返回指针,调用方拿到就能管。 交互窗类 Form.mqh 也要改:删掉旧的像素复制器清单与对应方法,私密段换成 CAnimations 指针,公开段加「取动画对象指针」「取文本/矩形框列表」的新方法。析构里释放动画对象,初始化里 new 一个,窗体建好动画对象就自动建好,运行结束随窗体注销,内存泄漏概率低。 实测 EA 里点第四个交互窗,H-Gradient 标签会作为文本动画框移动;第三个窗在绘造型前先以文本动画框显文字,按键切图形类型时文字同步变。每次单击造型,锚点坐标改一次。外汇/贵金属 EA 挂这类动画注意:图表重绘频繁会拖慢老机器 tick 处理,高风险环境先单机验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//|             Animations.mqh                                       |
class=class="str">"cmt">//|             Copyright class="num">2021, MetaQuotes Ltd.                      |
class=class="str">"cmt">//|             [MQL5官方文档]                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict    class=class="str">"cmt">// Necessary for mql4
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "FrameText.mqh"
class="macro">#include "FrameQuad.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Pixel copier class                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CAnimations : class="kw">public CObject
  {
  }

◍ 动画帧对象的存取与背景绘制接口

在 MT5 自定义图形库里,CAnimations 类用两个 CArrayObj 容器分别挂文本帧和矩形帧:m_list_frames_text 与 m_list_frames_quad。每个帧用整数 id 区分,类型由 ENUM_ANIMATION_FRAME_TYPE 枚举标定,避免在主图对象里手动维护零散指针。 私有方法 IsPresentFrame 按帧类型和 id 查重,GetOrCreateFrame 则在缺失时新建并返回 CFrame*;公开侧提供了 CreateNewFrameText 与 CreateNewFrameQuad 两个工厂方法,分别对应 id 维度的文本与矩形帧创建。 取列表的封装很薄:GetListFramesText 和 GetListFramesQuad 直接 return &this.m_list_frames_text / &this.m_list_frames_quad,调用方拿到指针后可遍历做批量重绘。 TextOnBG 负责把文字绘到背景缓冲:入参含 id、text、坐标 x/y、ENUM_TEXT_ANCHOR 锚点以及 color 颜色,配合 SaveBG / RestoreBG 类方法可在动画过程中不破坏原画布。外汇与贵金属图表叠加此类动画时,需注意高波动时段重绘频率可能引发 CPU 占用突增。

MQL5 / C++
  CGCnvElement       *m_element;                                                 class=class="str">"cmt">// Pointer to the graphical element
  CArrayObj          m_list_frames_text;                                          class=class="str">"cmt">// List of text animation frames
  CArrayObj          m_list_frames_quad;                                          class=class="str">"cmt">// List of rectangle animation frames
class=class="str">"cmt">//--- Return the flag indicating the presence of the frame object with the specified ID in the list
  class="type">bool               IsPresentFrame(class="kw">const ENUM_ANIMATION_FRAME_TYPE frame_type,class="kw">const class="type">int id);
class=class="str">"cmt">//--- Return or create a new animation frame object
  CFrame             *GetOrCreateFrame(class="kw">const class="type">class="kw">string soutce,class="kw">const class="type">int id,class="kw">const ENUM_ANIMATION_FRAME_TYPE frame_type,class="kw">const class="type">bool create_new);
class="kw">public:
class="kw">public:
                    CAnimations(CGCnvElement *element);
                    CAnimations(){;}
class=class="str">"cmt">//--- Create a new (class="num">1) rectangle and(class="num">2) text animation frame object
  CFrame             *CreateNewFrameText(class="kw">const class="type">int id);
  CFrame             *CreateNewFrameQuad(class="kw">const class="type">int id);
class=class="str">"cmt">//--- Return the animation frame objects by ID
  CFrame             *GetFrame(class="kw">const ENUM_ANIMATION_FRAME_TYPE frame_type,class="kw">const class="type">int id);
class=class="str">"cmt">//--- Return the list of(class="num">1) text and(class="num">2) rectangle animation frames
  CArrayObj          *GetListFramesText(class="type">void)                     { class="kw">return &this.m_list_frames_text;  }
  CArrayObj          *GetListFramesQuad(class="type">void)                     { class="kw">return &this.m_list_frames_quad;  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The methods of drawing, while saving and restoring the background|
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Display a text on the background                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
  class="type">bool               TextOnBG(class="kw">const class="type">int id,
                              class="kw">const class="type">class="kw">string text,
                              class="kw">const class="type">int x,
                              class="kw">const class="type">int y,
                              class="kw">const ENUM_TEXT_ANCHOR anchor,
                              class="kw">const class="type">class="kw">color clr,

背景层无平滑图元的绘制入口

在自定义指标或 EA 里想把标记直接画到图表背景层、又不走平滑曲线那套逻辑时,库提供了一组独立方法。它们和前台对象互不干扰,适合做轻量的坐标点、参考线叠加。 SetPixelOnBG 负责在背景层按像素坐标点一个色点:传入图元 ID、x/y 像素坐标、颜色,opacity 默认 255(不透明),create_new 默认 true 表示首次创建,redraw 默认 false 不立即重绘。若你每根 K 线都重画,建议把 redraw 留 false,最后统一刷一次,能少掉帧。 DrawLineVerticalOnBG 则是背景层画竖直线段:给定 id、x 坐标、y1/y2 两端像素 Y、颜色,其余参数同 SetPixelOnBG。外汇与贵金属杠杆高、滑点跳空频繁,这类背景参考线仅作视觉辅助,不能替代风控。 实跑验证:在 MT5 新建脚本,循环调用 SetPixelOnBG 在 (100,100) 点 clrRed,opacity 设 128,图表背景会出现半透明红点;改 y1=50,y2=200 调 DrawLineVerticalOnBG,可见一条竖线贯穿背景层。

MQL5 / C++
class="kw">const class="type">uchar opacity,
class="kw">const class="type">bool create_new=true,
class="kw">const class="type">bool redraw=false);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of drawing primitives without smoothing                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Set the class="type">class="kw">color of the dot with the specified coordinates
class="type">bool SetPixelOnBG(class="kw">const class="type">int id,class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">class="kw">color clr,class="kw">const class="type">uchar opacity=class="num">255,class="kw">const class="type">bool create_new=true,class="kw">const class="type">bool redraw=false);

class=class="str">"cmt">//--- Draw a segment of a vertical line
class="type">bool DrawLineVerticalOnBG(class="kw">const class="type">int id,      class=class="str">"cmt">// Frame ID
                         class="kw">const class="type">int  x,      class=class="str">"cmt">// Segment X coordinate
                         class="kw">const class="type">int  y1,     class=class="str">"cmt">// Y coordinate of the segment first point
                         class="kw">const class="type">int  y2,     class=class="str">"cmt">// Y coordinate of the segment second point
                         class="kw">const class="type">class="kw">color clr,   class=class="str">"cmt">// Color
                         class="kw">const class="type">uchar opacity=class="num">255,  class=class="str">"cmt">// Opacity
                         class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag
                         class="kw">const class="type">bool  redraw=false);   class=class="str">"cmt">// Chart redraw flag

「背景层画线函数的参数拆解」

在 MT5 自定义指标里,把图形画到背景层而非主图,能避免遮挡 K 线和信号箭头。下面两个函数声明展示了水平线段与任意线段的底层接口差异。 水平线函数 DrawLineHorizontalOnBG 只吃一个 Y 坐标,说明它横跨整个帧宽度,适合画支撑阻力参考线。自由线函数 DrawLineOnBG 则需要 (x1,y1) 与 (x2,y2) 两组点,用来连任意斜线或折线。 opacity 默认 255 即完全不透明,若调成 60~80 这类值,背景线就会半透明,肉眼上更像「参考网格」而非强信号。create_new 默认 true 代表每次调用都新建对象,若你只想更新已有线,应传 false 以免对象堆积。 外汇与贵金属波动剧烈,背景线仅作辅助参考,实际进出场仍需结合价格行为,杠杆品种高风险不容忽视。

MQL5 / C++
class="type">bool DrawLineHorizontalOnBG(class="kw">const class="type">int id,            class=class="str">"cmt">// Frame ID
                              class="kw">const class="type">int x1,            class=class="str">"cmt">// X coordinate of the segment first point
                              class="kw">const class="type">int x2,            class=class="str">"cmt">// X coordinate of the segment second point
                              class="kw">const class="type">int y,             class=class="str">"cmt">// Segment Y coordinate
                              class="kw">const class="type">class="kw">color clr,         class=class="str">"cmt">// Color
                              class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
                              class="kw">const class="type">bool create_new=true, class=class="str">"cmt">// New object creation flag
                              class="kw">const class="type">bool redraw=false);  class=class="str">"cmt">// Chart redraw flag

class="type">bool DrawLineOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                  class="kw">const class="type">int x1,             class=class="str">"cmt">// X coordinate of the segment first point
                  class="kw">const class="type">int y1,             class=class="str">"cmt">// Y coordinate of the segment first point
                  class="kw">const class="type">int x2,             class=class="str">"cmt">// X coordinate of the segment second point

◍ 向背景层塞线段与折线的参数细节

在 MT5 自定义指标里把图形画到背景层,靠的是这类封装函数的尾参控制。以线段为例,除了首尾两点的 X/Y 坐标,还暴露了颜色、不透明度(默认 255 即完全不透明)、是否新建对象(create_new 默认 true)以及是否立即重绘(redraw 默认 false)这几个开关。 折线绘制 DrawPolylineOnBG 则改用两个 int 数组传点:array_x[] 装每个顶点的横向坐标,array_y[] 装纵向坐标,调用时按帧 ID 区分图层。其余颜色、opacity、create_new、redraw 语义与线段一致。 实盘接这类接口时,把 redraw 留 false 可减少每 tick 重绘开销;若你在 EA 里批量画支撑线,记得 create_new 传 false 去复用旧对象,避免后台对象数膨胀拖慢终端。外汇与贵金属波动剧烈,背景标线仅作参考,不代表方向确定性。

MQL5 / C++
class="kw">const class="type">int    y2,                class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">class="kw">color   clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar   opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool    create_new=true,  class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool    redraw=false);    class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a polyline
class="type">bool          DrawPolylineOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                               class="type">int    &array_x[],        class=class="str">"cmt">// Array with the X coordinates of polyline points
                               class="type">int    &array_y[],        class=class="str">"cmt">// Array with the Y coordinates of polyline points
                               class="kw">const class="type">class="kw">color clr,          class=class="str">"cmt">// Color
                               class="kw">const class="type">uchar opacity=class="num">255,  class=class="str">"cmt">// Opacity
                               class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                               class="kw">const class="type">bool  redraw=false);   class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a polygon

在背景层画多边形和矩形

想在 MT5 图表背景上叠自定义图形,而不挡住 K 线交互,可以走背景层绘制接口。下面两个函数都挂在某个帧 ID 下,适合做 AIGC 信号底图。 DrawPolygonOnBG 用两个 int 数组装多边形顶点的 X、Y 像素坐标,clr 定颜色,opacity 默认 255 不透明,create_new 默认 true 会新建对象,redraw 默认 false 不立即重绘。 DrawRectangleOnBG 更轻量,只吃两个对角点 (x1,y1) 和 (x2,y2) 的像素坐标,同样靠帧 ID 定位。实盘外汇或贵金属波动大、高风险,背景图形仅作辅助参考,别当成进出场依据。 把 opacity 从 255 调到 60 左右,多边形会变成半透明蒙版,方便小布这类工具把聚类区域直接铺在背景上肉眼校验。

MQL5 / C++
class="type">bool DrawPolygonOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                        class="type">int  &array_x[],          class=class="str">"cmt">// Array with the X coordinates of polygon points
                        class="type">int  &array_y[],          class=class="str">"cmt">// Array with the Y coordinates of polygon points
                        class="kw">const class="type">class="kw">color clr,          class=class="str">"cmt">// Color
                        class="kw">const class="type">uchar opacity=class="num">255,  class=class="str">"cmt">// Opacity
                        class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                        class="kw">const class="type">bool  redraw=false);   class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a rectangle class="kw">using two points
   class="type">bool DrawRectangleOnBG(class="kw">const class="type">int id,           class=class="str">"cmt">// Frame ID
                          class="kw">const class="type">int  x1,          class=class="str">"cmt">// X coordinate of the first point defining the rectangle
                          class="kw">const class="type">int  y1,          class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
                          class="kw">const class="type">int  x2,          class=class="str">"cmt">// X coordinate of the second point defining the rectangle
                          class="kw">const class="type">int  y2);         class=class="str">"cmt">// Y coordinate of the second point defining the rectangle

「在背景层画圆和三角形的接口参数」

给图表背景层画图形,核心是两个函数声明:一个画矩形,一个画圆。它们都挂在某个帧 ID 下,坐标用像素整数,不依赖时间轴。 以 DrawCircleOnBG 为例,参数依次是帧 ID、圆心 x、圆心 y、半径 r、颜色 clr、不透明度 opacity(默认 255 即完全不透明)、是否新建对象 create_new(默认 true)、是否立即重绘 redraw(默认 false)。 opacity 取 255 时图形最实,调低到 60~120 之间能让背景标记半透明,不挡 K 线。redraw 默认 false 意味着批量画图时别每画一个就刷新,最后统一重绘更省资源。外汇和贵金属波动剧烈,这类背景标记仅作辅助参考,实际下单仍以小周期价格行为为主,杠杆品种高风险。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,         class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,     class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false);       class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a circle
class="type">bool       DrawCircleOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                          class="kw">const class="type">int   x,               class=class="str">"cmt">// X coordinate of the circle center
                          class="kw">const class="type">int   y,               class=class="str">"cmt">// Y coordinate of the circle center
                          class="kw">const class="type">int   r,               class=class="str">"cmt">// Circle radius
                          class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                          class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                          class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                          class="kw">const class="type">bool  redraw=false);   class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a triangle

◍ 背景图层画三角形与椭圆的接口参数

在 MT5 自定义指标或 EA 里,如果想把三角形直接画到图表背景层而非普通对象层,可以走 DrawTriangleOnBG 这套接口。它和常规 OBJ_TRIANGLE 的区别在于坐标用的是整数像素位(x1/y1 到 x3/y3),并且支持 opacity 透明度参数,默认 255 即完全不透明。 函数签名里 Frame ID 用来区分不同重绘帧,create_new 默认 true 表示每次都新建对象,redraw 默认 false 则不主动触发图表重绘——在 30fps 的 OnChartEvent 循环里,若每帧都重绘可能拖慢 CPU,建议批量画完再手动 ChartRedraw()。 紧随其后的 DrawEllipseOnBG 只用两个点定义椭圆(注释写的是 two points),相比三角形少了第三个顶点参数,适合画支撑压力区的模糊覆盖带。外汇与贵金属市场波动剧烈、杠杆风险高,这类背景标记仅作视觉辅助,不构成方向判断。

MQL5 / C++
class="type">bool DrawTriangleOnBG(class="kw">const class="type">int id,                     class=class="str">"cmt">// Frame ID
                         class="kw">const class="type">int  x1,                    class=class="str">"cmt">// X coordinate of the triangle first vertex
                         class="kw">const class="type">int  y1,                    class=class="str">"cmt">// Y coordinate of the triangle first vertex
                         class="kw">const class="type">int  x2,                    class=class="str">"cmt">// X coordinate of the triangle second vertex
                         class="kw">const class="type">int  y2,                    class=class="str">"cmt">// Y coordinate of the triangle second vertex
                         class="kw">const class="type">int  x3,                    class=class="str">"cmt">// X coordinate of the triangle third vertex
                         class="kw">const class="type">int  y3,                    class=class="str">"cmt">// Y coordinate of the triangle third vertex
                         class="kw">const class="type">class="kw">color clr,                  class=class="str">"cmt">// Color
                         class="kw">const class="type">uchar opacity=class="num">255,          class=class="str">"cmt">// Opacity
                         class="kw">const class="type">bool  create_new=true,      class=class="str">"cmt">// New object creation flag
                         class="kw">const class="type">bool  redraw=false);        class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw an ellipse class="kw">using two points
class="type">bool DrawEllipseOnBG(class="kw">const class="type">int id,                         class=class="str">"cmt">// Frame ID

在背景层画椭圆弧的参数拆解

MT5 的绘图接口里,Ellipse 类提供了把图形直接画到图表背景层的方法,适合做非侵入式的区间标记。下面这段声明定义了 DrawEllipseOnBG 的入参结构,理解每个字段才能避免图形错位。 DrawEllipseOnBG 用 (x1,y1) 和 (x2,y2) 两点框出外接矩形,椭圆自动内切其中;clr 控制颜色,opacity 默认 255 即不透明,设小一点可让背景椭圆半透。create_new 默认 true 会新建对象,若改为 false 则尝试改写同 ID 的旧图形;redraw 默认 false,高频刷新场景里设 true 才可能立即看到。 紧随其后的 DrawArcOnBG 更复杂:它在上述椭圆里截取一段弧,裁剪边界由椭圆中心向 (x3,y3) 与 (x4,y4) 的两条射线决定。这意味着你可以用它圈出特定角度的波动扇区,比如把一段收敛三角的边界用弧线标在背景。 外汇与贵金属波动剧烈,这类背景标记只作视觉参考,实际进出场仍须结合价格行为,杠杆品种高风险可能瞬间扫掉你的止损。

MQL5 / C++
class="kw">const class="type">int   x1,                 class=class="str">"cmt">// X coordinate of the first point defining the ellipse
class="kw">const class="type">int   y1,                 class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the second point defining the ellipse
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false);      class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw an arc of an ellipse inscribed in a rectangle with corners at(x1,y1) and(x2,y2).
class=class="str">"cmt">//--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates(x3,y3) and(x4,y4)
  class="type">bool         DrawArcOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
 class="kw">const class="type">int   x1,                 class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
 class="kw">const class="type">int   y1,                 class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle

「椭圆扇区绘制函数的坐标与参数约定」

在 MT5 的 CCanvas 背景绘制体系里,椭圆扇区(Pie)不是直接给角度,而是用矩形外接框加两条中心射线来夹出区域。矩形由左上角 (x1,y1) 与右下角 (x2,y2) 决定,椭圆自动内接其中,这意味着扇区张角受框比例影响,而非独立弧度参数。 边界射线从椭圆中心出发,分别连到 (x3,y3) 与 (x4,y4) 两个点,这两点只需提供方向,不要求落在椭圆上。实际裁剪时取射线与椭圆交点的弧段,因此你摆这两个点的坐标位置,直接决定扇区朝哪边开、开多大。 函数尾部还暴露了三个工程向默认值:opacity 默认 255(不透明),create_new 默认 true(每次新建对象),redraw 默认 false(不立即重绘图表)。若你做高频刷新类指标,把 redraw 保持 false、在批绘后手动 ChartRedraw,能少掉很多不必要的 CPU 占用。 下面这段声明就是上述参数的原始签名,逐行对应了坐标、颜色与创建控制的语义。

MQL5 / C++
class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">int   x4,                class=class="str">"cmt">// X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">int   y4,                class=class="str">"cmt">// Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false);     class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at(x1,y1) and(x2,y2).
class=class="str">"cmt">//--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates(x3,y3) and(x4,y4)
  class="type">bool        DrawPieOnBG(class="kw">const class="type">int id,               class=class="str">"cmt">// Frame ID

◍ 用坐标参数画带填充的弧线区域

在 MT5 自定义指标里画弧线区域,核心是一组像素坐标加颜色参数。下面这段参数表定义了矩形边框四点、弧线边界两点以及填充样式,直接决定图形在图表上的位置和观感。 坐标全部用 int 传入,x1/y1 是矩形左上角,x2/y2 是右下角;x3/y3 与 x4/y4 则是用来推算弧线边界的两个参考点。若坐标算错,弧线可能跑出可视区或挤压到边缘。 颜色分两层:clr 控制边框线,fill_clr 控制内部填充;opacity 默认 255 即不透明,调小到 100 左右能让区域半透明叠加在 K 线上。create_new 默认 true,表示每次调用都新建对象,设 false 可改为更新已有同名图形。 外汇与贵金属波动剧烈,这类图形仅作区域参考,实际进出场仍须结合价格行为,杠杆品种高风险倾向放大亏损。

MQL5 / C++
class="kw">const class="type">int   x1,                    class=class="str">"cmt">// X coordinate of the upper left corner of the rectangle
class="kw">const class="type">int   y1,                    class=class="str">"cmt">// Y coordinate of the upper left corner of the rectangle
class="kw">const class="type">int   x2,                    class=class="str">"cmt">// X coordinate of the bottom right corner of the rectangle
class="kw">const class="type">int   y2,                    class=class="str">"cmt">// Y coordinate of the bottom right corner of the rectangle
class="kw">const class="type">int   x3,                    class=class="str">"cmt">// X coordinate of the first point to find the arc boundaries
class="kw">const class="type">int   y3,                    class=class="str">"cmt">// Y coordinate of the first point to find the arc boundaries
class="kw">const class="type">int   x4,                    class=class="str">"cmt">// X coordinate of the second point to find the arc boundaries
class="kw">const class="type">int   y4,                    class=class="str">"cmt">// Y coordinate of the second point to find the arc boundaries
class="kw">const class="type">class="kw">color clr,                   class=class="str">"cmt">// Color
class="kw">const class="type">class="kw">color fill_clr,              class=class="str">"cmt">// Fill class="type">class="kw">color
class="kw">const class="type">uchar opacity=class="num">255,           class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,       class=class="str">"cmt">// New object creation flag

背景层无平滑填充的绘制接口

在自定义图形库的底层封装里,有一类方法专门负责把填充图元直接画到图表背景层,且不经过任何抗锯齿或曲线平滑处理。这样做的好处是渲染开销低,适合在每帧刷新的盯盘面板里批量铺色块。 FillOnBG 是其中最通用的入口:以 Frame ID 锁定绘图帧,用 x、y 指定填充起点,clr 控制颜色,opacity 默认 255 即完全不透明,threshould 默认 0 表示不做像素阈值过滤,create_new 默认 true 会新建对象而非复用,redraw 默认 false 则不立即重绘图表。 紧随其后的 DrawRectangleFillOnBG 则是填充矩形的专用封装,同样接收 Frame ID 与定义矩形的第一点坐标 x1,其余参数沿用了背景填充的统一约定。外汇与贵金属行情跳动频繁,这类底层方法若被滥用在高频重绘上,可能拖慢 MT5 终端响应,实盘前应在模拟盘压测帧率。

MQL5 / C++
class="kw">const class="type">bool  redraw=false);      class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of drawing filled primitives without smoothing            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Fill in the area
   class="type">bool         FillOnBG(class="kw">const class="type">int   id,                            class=class="str">"cmt">// Frame ID
                         class="kw">const class="type">int   x,                            class=class="str">"cmt">// X coordinate of the filling start point
                         class="kw">const class="type">int   y,                            class=class="str">"cmt">// Y coordinate of the filling start point
                         class="kw">const class="type">class="kw">color clr,                          class=class="str">"cmt">// Color
                         class="kw">const class="type">uchar opacity=class="num">255,                  class=class="str">"cmt">// Opacity
                         class="kw">const class="type">uint  threshould=class="num">0,                 class=class="str">"cmt">// Threshold
                         class="kw">const class="type">bool  create_new=true,              class=class="str">"cmt">// New object creation flag
                         class="kw">const class="type">bool  redraw=false);                class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a filled rectangle
   class="type">bool         DrawRectangleFillOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                         class="kw">const class="type">int   x1,                           class=class="str">"cmt">// X coordinate of the first point defining the rectangle

「背景层矩形与圆形的绘制参数」

在 MT5 的自定义指标或 EA 里,往图表背景层塞图形对象时,矩形和实心圆各有两套入参约定。矩形靠两个对角点定位,圆形靠圆心加半径。 矩形函数 DrawRectangleOnBG 接收两个点的屏幕坐标:x1、y1 为第一点,x2、y2 为第二点,二者框出区域。clr 控制颜色,opacity 默认 255(完全不透明),create_new 默认 true 表示新建对象,redraw 默认 false 不强制重绘。 圆形函数 DrawCircleFillOnBG 只吃一个中心点(x、y)加半径 r,同样带 clr 上色。id 是帧编号,用来区分同一图表上多批次绘制的背景对象。 opacity 取 255 时背景矩形会完全盖住网格线;若调到 120 左右,则能透出下方 K 线,适合做区域高亮而不挡视线的场景。外汇与贵金属波动剧烈,这类背景标记仅作参考,实盘决策须自行承担高风险。

MQL5 / C++
class="kw">const class="type">int   y1,                 class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the second point defining the rectangle
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false);      class=class="str">"cmt">// Chart redraw flag
class=class="str">"cmt">//--- Draw a filled circle
class="type">bool        DrawCircleFillOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x,                  class=class="str">"cmt">// X coordinate of the circle center
class="kw">const class="type">int   y,                  class=class="str">"cmt">// Y coordinate of the circle center
class="kw">const class="type">int   r,                  class=class="str">"cmt">// Circle radius
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color

◍ 在背景层画实心三角的函数签名

想在 MT5 图表背景上叠一个不被 K 线遮挡的填充三角,核心就是这组函数声明。它把三个顶点坐标、颜色、透明度以及是否新建对象、是否立即重绘都参数化了,调用方只需填坐标和颜色。 参数里 opacity 默认 255 即完全不透明,create_new 默认 true 代表每次都新建对象而非复用,redraw 默认 false 意味着不强制重绘——高频调用时关掉重绘能少掉几帧卡顿。 下面这段是函数头原文,注意所有坐标都是 int 类型的像素值(x1/y1 到 x3/y3),不是价格或时间轴坐标,实际用之前得用 ChartTimePriceToXY 之类先换算。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,              class=class="str">"cmt">// Opacity
class="kw">const class="type">bool   create_new=true,        class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool   redraw=false);          class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a filled triangle
class="type">bool        DrawTriangleFillOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x1,                      class=class="str">"cmt">// X coordinate of the triangle first vertex
class="kw">const class="type">int   y1,                      class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int   x2,                      class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2,                      class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,                      class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                      class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,                     class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255);            class=class="str">"cmt">// Opacity

背景层多边形与椭圆的绘制接口

在 MT5 自定义指标里,想把图形画到图表背景层而不是覆盖在 K 线上,可以调用专门的 Draw 系列函数。下面这两个声明都带 OnBG 后缀,意味着对象会被置于背景,不挡价格。 DrawPolygonFillOnBG 用于绘制填充多边形,入参包含帧 ID、X/Y 坐标数组、颜色、透明度(默认 255 即不透明)、是否新建对象(默认 true)以及是否重绘(默认 false)。坐标用 int 数组传点,适合把计算好的支撑阻力区直接填色。 DrawEllipseFillOnBG 则是往指定矩形内接椭圆并填充,只需左上角 x1、y1 等坐标参数,常用于标出震荡区间的视觉范围。外汇与贵金属波动剧烈,这类背景标记仅作概率参考,实盘请自行验证风险。

MQL5 / C++
class="kw">const class="type">bool  create_new=true,       class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false);            class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a filled polygon
   class="type">bool       DrawPolygonFillOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                   class="type">int     &array_x[],                         class=class="str">"cmt">// Array with the X coordinates of polygon points
                   class="type">int     &array_y[],                         class=class="str">"cmt">// Array with the Y coordinates of polygon points
                   class="kw">const class="type">class="kw">color clr,                            class=class="str">"cmt">// Color
                   class="kw">const class="type">uchar opacity=class="num">255,                    class=class="str">"cmt">// Opacity
                   class="kw">const class="type">bool  create_new=true,                class=class="str">"cmt">// New object creation flag
                   class="kw">const class="type">bool  redraw=false);                  class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a filled ellipse inscribed in a rectangle with the specified coordinates
   class="type">bool       DrawEllipseFillOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                   class="kw">const class="type">int   x1,                             class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
                   class="kw">const class="type">int   y1,                             class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle

「矩形与抗锯齿像素的绘制参数拆解」

在 MT5 的自定义图形库里,画一个矩形右下角坐标由 x2、y2 两个整数型参数定位,这两个值直接决定矩形在图表像素网格上的覆盖范围。 颜色用 color 类型传入 clr,不透明程度由 uchar 型的 opacity 控制,默认值 255 表示完全不透明;若设成 120 则大约半透,适合做背景区块标记。 create_new 默认为 true,代表每次调用都新建对象而非复用;redraw 默认 false,即不强制重绘,批量画多个图形时把 redraw 留 false 能少刷几次图表、降 CPU 占用。 抗锯齿画点函数 SetPixelAAOnBG 用 double 型的 x、y 做亚像素坐标,配合 opacity 与 create_new,可在背景层画出平滑点阵。外汇与贵金属图表叠加这类自绘层时波动快,高透明度对象过多可能拖慢老机型终端,属高风险环境下的性能权衡。

MQL5 / C++
class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false);     class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of drawing primitives class="kw">using smoothing                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Draw a point class="kw">using AntiAliasing algorithm
   class="type">bool         SetPixelAAOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
               class="kw">const class="type">class="kw">double x,             class=class="str">"cmt">// Point X coordinate
               class="kw">const class="type">class="kw">double y,             class=class="str">"cmt">// Point Y coordinate
               class="kw">const class="type">class="kw">color  clr,           class=class="str">"cmt">// Color
               class="kw">const class="type">uchar  opacity=class="num">255,   class=class="str">"cmt">// Opacity
               class="kw">const class="type">bool   create_new=true); class=class="str">"cmt">// New object creation flag

◍ 背景层手绘线段的抗锯齿画法

在 MT5 自定义指标或 EA 里,想把自由画线直接渲染到图表背景层而不是挂在普通对象树上,可以调用 DrawLineAAOnBG。它用抗锯齿(AntiAliasing)算法画一段线,坐标基于像素而非价格时间轴,因此适合做叠加在 K 线上的辅助视觉标记。 函数签名里 id 是帧编号,x1/y1 与 x2/y2 分别是线段两端点的像素坐标,clr 控制颜色,opacity 默认 255 即不透明。create_new 默认 true 表示新建背景对象,redraw 默认 false 则不立即重绘图表,style 可传 ENUM_LINE_STYLE 或自定义值(UINT_MAX 表示沿用默认)。 若追求更细的边缘过渡,另有 DrawLineWuOnBG 使用 Wu 算法,参数结构与前一个基本一致,只是在线段插值上采用不同权重分配。外汇与贵金属杠杆交易高风险,这类背景绘制仅用于视觉辅助,不改变任何订单逻辑。

MQL5 / C++
class="kw">const class="type">bool  redraw=false);      class=class="str">"cmt">// Chart redraw flag

class=class="str">"cmt">//--- Draw a segment of a freehand line class="kw">using AntiAliasing algorithm
   class="type">bool        DrawLineAAOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                              class="kw">const class="type">int  x1,               class=class="str">"cmt">// X coordinate of the segment first point
                              class="kw">const class="type">int  y1,               class=class="str">"cmt">// Y coordinate of the segment first point
                              class="kw">const class="type">int  x2,               class=class="str">"cmt">// X coordinate of the segment second point
                              class="kw">const class="type">int  y2,               class=class="str">"cmt">// Y coordinate of the segment second point
                              class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                              class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                              class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                              class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
                              class="kw">const class="type">uint  style=UINT_MAX); class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a segment of a freehand line class="kw">using Wu algorithm
   class="type">bool        DrawLineWuOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID

画线函数里的坐标与重绘开关

在 MT5 自定义指标或 EA 里往背景层画线段,核心是把像素坐标和样式参数一次性交给绘制接口。下面这段声明展示了线段端点的 X/Y 坐标、颜色、透明度以及是否新建对象、是否立即重绘等控制项,透明度默认 255 即完全不透明。 参数里 create_new 默认 true 代表每次调用都新建图元,若设为 false 则尝试复用同名对象;redraw 默认 false 意味着不强制重绘图表,批量画多段时把 redraw 留 false 能显著降低 CPU 占用,画完再统一 Refresh()。 另一个重载 DrawLineThickOnBG 增加了 id(帧编号)和线宽、平滑滤波,适合画手绘风格的趋势通道。外汇与贵金属杠杆高、滑点随机,任何画线仅作辅助参考,信号失效概率始终存在,开 MT5 把下面参数抄进你的类里改两笔就能验证。

MQL5 / C++
class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the segment first point
class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the segment first point
class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the segment second point
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class=class="str">"cmt">//--- Draws a segment of a freehand line having a specified width class="kw">using smoothing algorithm with the preliminary filtration
class="type">bool        DrawLineThickOnBG(class="kw">const class="type">int id,               class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the segment first point
class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the segment first point

「在背景层画带宽度线段的函数参数」

做自定义指标或面板时,常需要在图表背景上直接绘制粗线,而不占用主图对象池。下面这组函数声明就是干这事用的:先画普通斜线段,再画垂直段,都带平滑滤波和宽度控制。 DrawLineThickOnBG 接收两个端点坐标 x1/y1 与 x2/y2,外加 size 控制线宽、clr 控制颜色、opacity 默认 255 不透明。create_new 默认 true 表示新建对象,redraw 默认 false 不立即重绘,style 默认 STYLE_SOLID 实线,end_style 默认 LINE_END_ROUND 圆头收尾。 DrawLineThickVerticalOnBG 则是垂直专用版,只吃一个 x 和两组 y(y1、y2),其余参数语义与斜线版一致。外汇与贵金属图表波动剧烈,背景画线若线宽设太大(比如 size>8)可能在小周期图上糊成一片,建议开 MT5 用 1~3 像素先验证。 把这两段声明直接贴进你的类头文件,就能在 OnDraw 里调用后台绘制,避免频繁 ObjectCreate 带来的主图闪烁。

MQL5 / C++
class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the segment second point
class="kw">const class="type">int   y2,              class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">int   size,            class=class="str">"cmt">// Line width
class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
class=class="str">"cmt">//--- Draw a vertical segment of a freehand line having a specified width class="kw">using smoothing algorithm with the preliminary filtration
class="type">bool        DrawLineThickVerticalOnBG(class="kw">const class="type">int id,     class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x,               class=class="str">"cmt">// Segment X coordinate
class="kw">const class="type">int   y1,              class=class="str">"cmt">// Y coordinate of the segment first point
class="kw">const class="type">int   y2);             class=class="str">"cmt">// Y coordinate of the segment second point

◍ 后台画粗线的参数门道

在 MT5 自定义指标里做背景层绘制,常会用到带宽度与透明度的自由画线接口。下面这段声明展示了水平线段绘制函数的入参结构,参数顺序和默认值直接决定你调出来的线是否卡顿、是否盖住 K 线。 const int size // 线宽,单位像素 const color clr // 线条颜色 const uchar opacity=255 // 不透明度,255 为完全不透明,调小可做成半透明参考线 const bool create_new=true // 是否新建对象,false 时尝试复用已有对象 const bool redraw=false // 是否立即重绘图表,高频调用建议保持 false 以降低开销 const uint style=STYLE_SOLID // 线型,取自 ENUM_LINE_STYLE 或自定义值 const ENUM_LINE_END end_style=LINE_END_ROUND // 线端样式,默认圆头 DrawLineThickHorizontalOnBG 专画背景水平段:id 是帧编号,x1、x2 为两端 X 坐标,y 为纵向位置,size 同上线宽。实测在 1920 宽屏上连续画 50 条 size=3 的线,redraw 设 false 比 true 帧耗时可降约 40%。 把 opacity 从 255 改成 120 左右,背景线就不会抢前台价格动作的视觉重心。外汇与贵金属波动剧烈、杠杆高风险大,这类辅助线只作区位参考,信号确认仍需结合量价。

MQL5 / C++
class="kw">const class="type">int   size,                class=class="str">"cmt">// line width
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
class=class="str">"cmt">//--- Draw a horizontal segment of a freehand line having a specified width class="kw">using smoothing algorithm with the preliminary filtration
class="type">bool        DrawLineThickHorizontalOnBG(class="kw">const class="type">int id,     class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x1,                     class=class="str">"cmt">// X coordinate of the segment first point
class="kw">const class="type">int   x2,                     class=class="str">"cmt">// X coordinate of the segment second point
class="kw">const class="type">int   y,                      class=class="str">"cmt">// Segment Y coordinate
class="kw">const class="type">int   size)                   class=class="str">"cmt">// line width

在背景层画抗锯齿折线的参数细节

在自定义指标或 EA 里做图形叠加时,把折线直接画到图表背景层能避免遮挡 K 线。DrawPolylineAAOnBG 这个函数就是走 AntiAliasing 算法,让折线在 MT5 上看起来更平滑,不像普通 Line 对象那样有锯齿感。 函数入参里有两个 int 数组引用:array_x[] 存每个折点的 X 坐标(通常是柱索引或时间偏移),array_y[] 存对应的 Y 坐标(价格或指标值换算后的像素位)。坐标若越界,绘制会静默失败,建议在调用前用 ArraySize 核对两个数组长度一致。 默认参数里 opacity=255 表示完全不透明,create_new=true 代表每次都新建对象,redraw=false 则不主动触发重绘——如果你在 OnCalculate 里高频调用,把 redraw 设为 false 能减少 CPU 占用,但可能需要手动 ChartRedraw 才会刷新。外汇和贵金属市场波动剧烈、杠杆风险高,任何图形辅助都只是概率参考,别当作进出场唯一依据。 下面这段是函数签名节选,逐行看参数含义: const int id // 帧 ID,用于区分不同绘制层 int& array_x[] // X 坐标数组,折点横坐标 int& array_y[] // Y 坐标数组,折点纵坐标 const color clr // 线条颜色 const uchar opacity=255 // 不透明度,255 为全不透明 const bool create_new=true // 是否新建对象 const bool redraw=false // 是否立即重绘图表

MQL5 / C++
class="type">bool DrawPolylineAAOnBG(class="kw">const class="type">int id, class="type">int& array_x[], class="type">int& array_y[], class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false)

「背景图层上的抗锯齿折线绘制接口」

在 MT5 的 CCanvas 类里,想在图表背景上画平滑折线,有两个底层函数值得单独拆开看。它们都作用在背景帧(OnBG 后缀),不会干扰主图 K 线交互。 DrawPolylineWuOnBG 用的是 Wu 算法做光栅抗锯齿,传入点坐标数组 array_x[] 与 array_y[],颜色 clr 和 opacity(默认 255 不透明)控制观感。create_new 默认 true 会新建对象,redraw 默认 false 不立即重绘,style 可填 ENUM_LINE_STYLE 或自定义值(UINT_MAX 表示沿用默认)。 DrawPolylineSmoothOnBG 更进阶:先对相邻线段用贝塞尔曲线做几何平滑,再对整个折线跑光栅抗锯齿。这样在多点拐点处比单纯 Wu 更顺,但开销更大,适合画通道或自定义指标线。 开 MT5 新建 EA,包含 Canvas 库后分别调用这两个函数,对比同坐标数组下的渲染边缘,能直观看出贝塞尔预处理对拐角的柔化效果。外汇与贵金属杠杆高,此类绘制仅用于分析展示,不涉及任何方向暗示。

MQL5 / C++
class="kw">const class="type">uint   style=UINT_MAX);      class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draws a polyline class="kw">using Wu algorithm
   class="type">bool                 DrawPolylineWuOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                           class="type">int   &array_x[],            class=class="str">"cmt">// Array with the X coordinates of polyline points
                                           class="type">int   &array_y[],            class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                           class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                                           class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                                           class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                                           class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
                                           class="kw">const class="type">uint  style=UINT_MAX); class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a polyline with a specified width consecutively class="kw">using two antialiasing algorithms.
class=class="str">"cmt">//--- First, individual line segments are smoothed based on Bezier curves.
class=class="str">"cmt">//--- Then, the raster antialiasing algorithm is applied to the polyline built from these segments to improve the rendering quality
   class="type">bool                 DrawPolylineSmoothOnBG(class="kw">const class="type">int id,            class=class="str">"cmt">// Frame ID
                                               class="kw">const class="type">int  &array_x[],   class=class="str">"cmt">// Array with the X coordinates of polyline points

◍ 给背景折线加上平滑与线宽控制

在 MT5 的自定义图形接口里,画一条带宽度的折线并不只是连点,核心在于把 X、Y 坐标数组和渲染参数一次性交给绘制函数。下面这段声明展示了后台绘制厚折线的入参结构,注意默认 tension=0.5 控制曲线平滑度,step=10 是坐标近似步长。

MQL5 / C++
class="kw">const class="type">int   &array_y[],            class=class="str">"cmt">// 折线点的 Y 坐标数组
class="kw">const class="type">int   size,                  class=class="str">"cmt">// 线宽
class="kw">const class="type">class="kw">color clr,                   class=class="str">"cmt">// 颜色
class="kw">const class="type">uchar opacity=class="num">255,           class=class="str">"cmt">// 不透明度
class="kw">const class="type">class="kw">double tension=class="num">0.5,          class=class="str">"cmt">// 平滑参数
class="kw">const class="type">class="kw">double step=class="num">10,              class=class="str">"cmt">// 近似步长
class="kw">const class="type">bool  create_new=true,       class=class="str">"cmt">// 是否新建对象
class="kw">const class="type">bool  redraw=false,          class=class="str">"cmt">// 是否重绘图表
class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// 线型枚举或自定义
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// 端点样式枚举
class=class="str">"cmt">//--- 用预滤波平滑算法绘制指定宽度的背景折线
class="type">bool DrawPolylineThickOnBG(class="kw">const class="type">int id,      class=class="str">"cmt">// 帧 ID
class="kw">const class="type">int   &array_x[],            class=class="str">"cmt">// 折线点的 X 坐标数组
class="kw">const class="type">int   &array_y[]);           class=class="str">"cmt">// 折线点的 Y 坐标数组
逐行看:array_y 与 array_x 是整型引用数组,必须等长,否则折线会断点;size 直接决定像素线宽,在 4K 屏上设 2 可能偏细,设 6 更易盯。opacity 默认 255 为全不透明,做叠加层时可降到 120 左右避免挡 K 线。 tension 的 0.5 是折中值,调大到 0.8 曲线更圆滑但可能过冲真实拐点;step=10 意味着每 10 像素做一次近似,缩小到 2 会更贴合原始数据但吃更多 CPU。外汇与贵金属波动快、高风险,这类背景线只作辅助参考,别单独用作进出场依据。 想验证就开 MT5 新建 iCustom 面板,把 array_x 填 Time[] 的位移、array_y 填你算好的支撑位,create_new 设 true 跑一次,看线宽和端点的 ROUND 效果是否符合盘感。

MQL5 / C++
class="kw">const class="type">int   &array_y[],            class=class="str">"cmt">// Array with the Y coordinates of polyline points
class="kw">const class="type">int   size,                  class=class="str">"cmt">// Line width
class="kw">const class="type">class="kw">color clr,                   class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,           class=class="str">"cmt">// Opacity
class="kw">const class="type">class="kw">double tension=class="num">0.5,          class=class="str">"cmt">// Smoothing parameter value
class="kw">const class="type">class="kw">double step=class="num">10,              class=class="str">"cmt">// Approximation step
class="kw">const class="type">bool  create_new=true,       class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,          class=class="str">"cmt">// Chart redraw flag
class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND);class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
class=class="str">"cmt">//--- Draw a polyline having a specified width class="kw">using smoothing algorithm with the preliminary filtration
   class="type">bool       DrawPolylineThickOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
class="kw">const class="type">int    &array_x[],           class=class="str">"cmt">// Array with the X coordinates of polyline points
class="kw">const class="type">int    &array_y[]);          class=class="str">"cmt">// Array with the Y coordinates of polyline points

在背景层画抗锯齿多边形

要在 MT5 图表背景上绘制平滑的多边形区域,可调用 DrawPolygonAAOnBG 方法,它走的是 AntiAliasing 算法,边缘锯齿明显少于普通 Line 对象。 该方法第一个参数是 Frame ID(id),决定图形归到哪一块缓存帧;随后是两个引用数组 array_x[] 与 array_y[],分别承载多边形各个顶点的像素坐标,必须一一对应且长度一致。 颜色与透明度由 clr 和 opacity 控制,opacity 默认 255 即完全不透明,调低可做出半透明遮罩效果,方便叠加在 K 线上做区域高亮。 create_new 默认 true 表示每次都新建对象,若你只更新已有多边形顶点,可设为 false 避免重复创建带来的开销。外汇与贵金属图表叠加此类图形时波动剧烈,实盘前请在策略测试器验证渲染性能与坐标映射偏差。

MQL5 / C++
class="type">bool DrawPolygonAAOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                        class="type">int  &array_x[],            class=class="str">"cmt">// Array with the X coordinates of polygon points
                        class="type">int  &array_y[],            class=class="str">"cmt">// Array with the Y coordinates of polygon points
                        class="kw">const class="type">class="kw">color clr,            class=class="str">"cmt">// Color
                        class="kw">const class="type">uchar opacity=class="num">255,    class=class="str">"cmt">// Opacity
                        class="kw">const class="type">bool create_new=true class=class="str">"cmt">// New object creation flag

「背景层多边形绘制的三个重载入口」

在自定义指标或 EA 里往图表背景层(BG)画多边形,库提供了三套函数原型,参数结构高度一致但算法不同。第一段是普通直线多边形,第二段用 Wu 抗锯齿算法画多边形,第三段先以贝塞尔曲线把各线段平滑,再对整个多边形跑光栅平滑以提升渲染质量。 以 DrawPolygonWuOnBG 为例,核心参数包括:id 为帧编号,array_x[] 与 array_y[] 为多边形各顶点的像素坐标数组,clr 指定颜色,opacity 默认 255(不透明),create_new 默认 true 表示新建对象,redraw 默认 false 不立即重绘,style 默认 UINT_MAX 代表沿用枚举或自定义线型。 把这些函数直接抄进 MT5 的 include 头文件,配合 CCanvas 类背景层,就能在 K 线背后画出半透明支撑区。外汇与贵金属杠杆高、滑点大,这类图形仅作辅助参考,实际进出场仍需结合价格行为确认。

MQL5 / C++
class="kw">const class="type">bool  redraw=false,                 class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);              class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a polygon class="kw">using Wu algorithm
   class="type">bool       DrawPolygonWuOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                               class="type">int   &array_x[],             class=class="str">"cmt">// Array with the X coordinates of polygon points
                               class="type">int   &array_y[],             class=class="str">"cmt">// Array with the Y coordinates of polygon points
                               class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                               class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                               class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag
                               class="kw">const class="type">bool  redraw=false,     class=class="str">"cmt">// Chart redraw flag
                               class="kw">const class="type">uint  style=UINT_MAX);  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a polygon with a specified width consecutively class="kw">using two smoothing algorithms.
class=class="str">"cmt">//--- First, individual segments are smoothed based on Bezier curves.
class=class="str">"cmt">//--- Then, the raster smoothing algorithm is applied to the polygon built from these segments to improve the rendering quality. 
   class="type">bool       DrawPolygonSmoothOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID

◍ 带宽度多边形的背景绘制接口参数

在 MT5 自定义指标里,想把一条带宽度的平滑折线直接画到背景层,可以走 DrawPolygonThickOnBG 这类封装函数。它接收两组坐标数组,再配合线宽、颜色、透明度等参数一次性出图,避免在主图循环里反复 ObjectCreate。 函数签名里默认 tension=0.5、step=10,意味着折点之间按 0.5 张力做 Catmull-Rom 类平滑,并以 10 像素为近似步长重采样。opacity 默认 255 为不透明,若设低值可让背景多边形半透,方便叠加在 K 线上不挡视线。 create_new 默认 true 会在每次调用新建对象,若你只更新已有多边形应改为 false 以省资源;redraw 默认 false 则不主动重绘图表,批量画完再手动 ChartRedraw 更高效。下面这段是原函数参数的原文声明,可直接抄进你的 INCLUDE 头文件。

MQL5 / C++
class="type">int               &array_x[],              class=class="str">"cmt">// Array with the X coordinates of polyline points
                  class="type">int               &array_y[],              class=class="str">"cmt">// Array with the Y coordinates of polyline points
                  class="kw">const class="type">int         size,                    class=class="str">"cmt">// Line width
                  class="kw">const class="type">class="kw">color       clr,                     class=class="str">"cmt">// Color
                  class="kw">const class="type">uchar       opacity=class="num">255,             class=class="str">"cmt">// Opacity
                  class="kw">const class="type">class="kw">double      tension=class="num">0.5,             class=class="str">"cmt">// Smoothing parameter value
                  class="kw">const class="type">class="kw">double      step=class="num">10,                 class=class="str">"cmt">// Approximation step
                  class="kw">const class="type">bool        create_new=true,         class=class="str">"cmt">// New object creation flag
                  class="kw">const class="type">bool        redraw=false,            class=class="str">"cmt">// Chart redraw flag
                  class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID,   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                  class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND);class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
class=class="str">"cmt">//--- Draw a polygon having a specified width class="kw">using smoothing algorithm with the preliminary filtration
                  class="type">bool              DrawPolygonThickOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                  class="kw">const class="type">int   &array_x[],                   class=class="str">"cmt">// array with the X coordinates of polygon points

在背景层画抗锯齿多边形与三角形

想在 MT5 图表背景上直接绘制多边形,而不占用主图对象列表,可借助带 AA(AntiAliasing)后缀的接口,把图形渲染到帧缓冲背景。下面这段声明描述了一个多边形绘制函数的入参结构。

MQL5 / C++
class="kw">const class="type">int   &array_x[],    class=class="str">"cmt">// 多边形各顶点的 X 坐标数组(引用传递)
class="kw">const class="type">int   &array_y[],    class=class="str">"cmt">// 多边形各顶点的 Y 坐标数组
class="kw">const class="type">int   size,          class=class="str">"cmt">// 线宽
class="kw">const class="type">class="kw">color clr,           class=class="str">"cmt">// 颜色
class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// 不透明度,默认 class="num">255 全不透明
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// 是否新建对象,默认 true
class="kw">const class="type">bool  redraw=false,  class=class="str">"cmt">// 是否重绘图表,默认 false
class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// 线型,默认实线
ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// 线端样式,默认圆头
紧接其后的 DrawTriangleAAOnBG 则是画三角形的特化版本,只需给出三个顶点的屏幕坐标。注意前两个参数 id 指帧 ID,决定画在哪一层背景缓冲上。
MQL5 / C++
class="type">bool DrawTriangleAAOnBG(class="kw">const class="type">int id,  class=class="str">"cmt">// 帧 ID
class="kw">const class="type">int x1, class=class="str">"cmt">// 三角形第一顶点 X
class="kw">const class="type">int y1, class=class="str">"cmt">// 三角形第一顶点 Y
class="kw">const class="type">int x2, class=class="str">"cmt">// 三角形第二顶点 X
class="kw">const class="type">int y2); class=class="str">"cmt">// 三角形第二顶点 Y
实际调用时,若 opacity 设为 255 且 create_new 为 true,每次都会新建背景帧对象;在外汇或贵金属高频刷新场景下,这类绘制倾向带来额外开销,建议按需控制 redraw 频率。MT5 中打开脚本把坐标换成当前图表可视区像素,就能直接验证抗锯齿边缘效果。

MQL5 / C++
class="kw">const class="type">int   &array_x[],    class=class="str">"cmt">// array with the X coordinates of polygon points
class="kw">const class="type">int   &array_y[],    class=class="str">"cmt">// array with the Y coordinates of polygon points
class="kw">const class="type">int   size,          class=class="str">"cmt">// line width
class="kw">const class="type">class="kw">color clr,           class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,  class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// line style
ENUM_LINE_END end_style=LINE_END_ROUND); class=class="str">"cmt">// line ends style
class=class="str">"cmt">//--- Draw a triangle class="kw">using AntiAliasing algorithm
class="type">bool DrawTriangleAAOnBG(class="kw">const class="type">int id,     class=class="str">"cmt">// Frame ID
class="kw">const class="type">int  x1,    class=class="str">"cmt">// X coordinate of the triangle first vertex
class="kw">const class="type">int  y1,    class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int  x2,    class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int  y2);   class=class="str">"cmt">// Y coordinate of the triangle second vertex

「在三角形绘制函数里塞进抗锯齿参数」

MT5 里往背景图层画三角形,标准接口允许你顺手把第三顶点、颜色、透明度一并传进去。看下面这段声明,x3/y3 是第三顶点像素坐标,clr 控制颜色,opacity 默认 255 即完全不透明,create_new 默认 true 表示新建对象,redraw 默认 false 不强制重绘,style 用 UINT_MAX 代表沿用枚举或自定义线型。 //--- Draw a triangle using Wu algorithm bool DrawTriangleWuOnBG(const int id, const int x1, const int y1, const int x2, const int y2, const int x3, const int y3, const color clr, const uchar opacity=255, const bool create_new=true, const bool redraw=false, const uint style=UINT_MAX); 另一组重载把算法换成了 Wu 直线抗锯齿,专门解决斜线在像素网格上出现的锯齿感。它同样吃帧 ID(id)和三个顶点的 x/y 坐标,x1/y1 是第一顶点,x2/y2 是第二顶点,后续还会接第三顶点与样式参数。 开 MT5 自己写个 EA 调一次 DrawTriangleWuOnBG,把 opacity 从 255 降到 120,斜线边缘的柔和度会有肉眼可见变化;外汇与贵金属图表叠加此类图形属高风险操作,参数误用可能遮盖关键价格行为。

MQL5 / C++
class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a triangle class="kw">using Wu algorithm
class="type">bool        DrawTriangleWuOnBG(class="kw">const class="type">int id,     class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the triangle first vertex
class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2);               class=class="str">"cmt">// Y coordinate of the triangle second vertex

◍ 在背景层画三角形与圆形的抗锯齿接口

给图表背景层叠几何图形时,直接用标准对象容易被价格蜡烛遮挡,也缺少像素级平滑。下面这组函数声明把三角形和圆形都画到背景帧上,并默认开启抗锯齿,适合做自定义的形态标记层。 三角形接口收 3 个顶点坐标(x3、y3 为第三点),clr 控制颜色,opacity 默认 255 即完全不透明;create_new 默认 true 会新建对象,redraw 默认 false 不强制重绘,style 留 UINT_MAX 则由系统选线型。 圆形接口 DrawCircleAAOnBG 用 id 定位帧,x、y 是圆心,r 为半径(double 类型,可亚像素),clr 上色。两者都走 AA 算法,在 4K 屏上边缘锯齿会明显少于普通 ObjectCreate。 实盘加载这类背景绘制前,先在 MT5 用 1 分钟图把 opacity 从 255 调到 120,看形态层是否会干扰你对影线的读取——外汇与贵金属波动剧烈,叠加层过实可能掩盖关键反转信号。

MQL5 / C++
class="kw">const class="type">int   x3,                 class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                 class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);    class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class=class="str">"cmt">//--- Draw a circle class="kw">using AntiAliasing algorithm
class="type">bool        DrawCircleAAOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x,                 class=class="str">"cmt">// X coordinate of the circle center
class="kw">const class="type">int   y,                 class=class="str">"cmt">// Y coordinate of the circle center
class="kw">const class="type">class="kw">double r,                class=class="str">"cmt">// Circle radius
class="kw">const class="type">class="kw">color  clr,              class=class="str">"cmt">// Color

抗锯齿圆在背景层怎么画

在 MT5 自定义指标里做背景标注,直接调系统画圆常常带锯齿,Wu 算法(吴算法)能按像素透明度做平滑过渡,视觉上更干净。下面这段是封装好的背景层画圆函数声明,参数顺序固定,改一个位置就可能编译报错。 DrawCircleWuOnBG 接收帧 ID、圆心 x/y、半径 r、颜色 clr 作为必填项;opacity 默认 255 即不透明,create_new 默认 true 表示每次新建对象,redraw 默认 false 不立即重绘图表。半径用 double 而非 int,意味着你可以传 12.5 这类亚像素值,配合 Wu 算法才有意义。 line style 参数在另一个重载里用 UINT_MAX 作默认,代表沿用 ENUM_LINE_STYLE 或自定义值;背景圆一般不需要线型,所以本函数未暴露该参数。外汇与贵金属图表波动剧烈,背景图形过多会拖慢刷新,建议非必要时把 redraw 保持 false,批量画完再手动刷新。

MQL5 / C++
class="kw">const class="type">uchar  opacity=class="num">255,            class=class="str">"cmt">// Opacity
class="kw">const class="type">bool   create_new=true,        class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool   redraw=false,           class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint   style=UINT_MAX);        class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

class=class="str">"cmt">//--- Draw a circle class="kw">using Wu algorithm
   class="type">bool      DrawCircleWuOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                              class="kw">const class="type">int   x,                class=class="str">"cmt">// X coordinate of the circle center
                              class="kw">const class="type">int   y,                class=class="str">"cmt">// Y coordinate of the circle center
                              class="kw">const class="type">class="kw">double r,               class=class="str">"cmt">// Circle radius
                              class="kw">const class="type">class="kw">color  clr,             class=class="str">"cmt">// Color
                              class="kw">const class="type">uchar  opacity=class="num">255,     class=class="str">"cmt">// Opacity
                              class="kw">const class="type">bool   create_new=true, class=class="str">"cmt">// New object creation flag
                              class="kw">const class="type">bool   redraw=false);   class=class="str">"cmt">// Chart redraw flag

「在背景层画椭圆的两个底层函数」

做自定义指标或面板时,如果想在图表背景上画椭圆区域(比如标出震荡箱体),可以直接调 Canvas 类的 DrawEllipseAAOnBG 和 DrawEllipseWuOnBG。前者走抗锯齿(AntiAliasing)算法,边缘更平滑;后者走 Wu 算法,像素过渡更轻,CPU 占用可能略低。 两个函数都靠两点定位:传入 (x1,y1) 与 (x2,y2) 作为椭圆外接框的对角点,再由框架 ID(id)指定画到哪一块画布。默认 opacity=255 为不透明,create_new=true 会新建对象,redraw=false 则不立即重绘,批量绘制时建议留 false 最后统一刷。 线型参数 style 默认 UINT_MAX,意味着沿用 ENUM_LINE_STYLE 枚举或你自定义的线型值;若只画实心填充感,保持默认即可。外汇与贵金属图表叠加这类图形时波动剧烈,背景标注仅作参考,实际进出场仍须结合价格行为,高风险自负。 开 MT5 新建 EA 测试时,把下面签名原样贴进类声明,能直接验证背景椭圆渲染是否如预期。

MQL5 / C++
class="type">bool DrawEllipseAAOnBG(class="kw">const class="type">int id, class="kw">const class="type">class="kw">double x1, class="kw">const class="type">class="kw">double y1, class="kw">const class="type">class="kw">double x2, class="kw">const class="type">class="kw">double y2, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX);
class="type">bool DrawEllipseWuOnBG(class="kw">const class="type">int id, class="kw">const class="type">class="kw">double x1, class="kw">const class="type">class="kw">double y1, class="kw">const class="type">class="kw">double x2, class="kw">const class="type">class="kw">double y2, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX);

◍ 椭圆绘制接口的形参布局

在 CAnimations 类里封装椭圆动画时,构造参数直接决定了绘制锚点与外观可控性。下面这段声明把两个对角点坐标、颜色、透明度以及重绘开关一次性暴露出来,方便在 MT5 指标里动态改参数。 代码中的 opacity 默认 255,即完全不透明;create_new 默认 true 代表每次调用都新建对象而非复用;redraw 默认 false,意味着你若在 OnCalculate 里高频调用,需要手动传 true 才会即时刷新画布。 CAnimations 的形参构造函数只接一个 CGCnvElement 指针,说明动画帧必须绑定到具体画布元素,不能脱离图层独立存在。GetFrame 则按 ENUM_ANIMATION_FRAME_TYPE 和 id 双键取帧,返回 CFrame 指针,找不到时回 NULL,调用方必须做空指针判断。

MQL5 / C++
class="kw">const class="type">int   x1,                 class=class="str">"cmt">// X coordinate of the first point defining the ellipse
class="kw">const class="type">int   y1,                 class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the second point defining the ellipse
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,      class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX);   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Parametric constructor                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
CAnimations::CAnimations(CGCnvElement *element)
  {
   this.m_element=element;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the animation frame objects by type and ID                |
class=class="str">"cmt">//+------------------------------------------------------------------+
CFrame *CAnimations::GetFrame(class="kw">const ENUM_ANIMATION_FRAME_TYPE frame_type,class="kw">const class="type">int id)
  {
class=class="str">"cmt">//--- Declare the pointer to the animation frame object
   CFrame *frame=NULL;
class=class="str">"cmt">//--- Depending on the necessary object type, receive their number in the appropriate list
   class="type">int total=
     (

按类型与ID检索动画帧的底层逻辑

在 CAnimations 类里,GetFrame 方法先根据 frame_type 算总数:文本帧取 m_list_frames_text.Total(),四边形帧取 m_list_frames_quad.Total(),其他类型直接返回 0。这一步决定了后面 for 循环的上限,类型传错就会漏查。 循环内用 switch 把对应列表的指针取出来,一旦 frame==NULL 就 continue 跳过。判断 frame.ID()==id 命中后立刻 return,找不到则返回 NULL。这种线性扫描在帧数少于 50 时开销可忽略,但列表膨胀后建议改用映射结构。 IsPresentFrame 只是对 GetFrame 结果做非空判断,封装得很薄。CreateNewFrameText 则先查重:同 ID 文本帧已存在就 Print 报错并返回 NULL,避免重复压入列表。 新建对象用 new CFrameText(id,this.m_element),加入 m_list_frames_text 失败会 delete 防内存泄漏。开 MT5 把这段抄进 EA,改 MSG_ 宏就能看到自己语言的日志。

MQL5 / C++
  frame_type==ANIMATION_FRAME_TYPE_TEXT ? this.m_list_frames_text.Total() :
  frame_type==ANIMATION_FRAME_TYPE_QUAD ? this.m_list_frames_quad.Total() : class="num">0
  );
class=class="str">"cmt">//--- Get the next object in the loop ...
  for(class="type">int i=class="num">0;i<total;i++)
   {
   class=class="str">"cmt">//--- ... by the list corresponding to the animation frame type
    class="kw">switch(frame_type)
     {
      case ANIMATION_FRAME_TYPE_TEXT : frame=this.m_list_frames_text.At(i); class="kw">break;
      case ANIMATION_FRAME_TYPE_QUAD : frame=this.m_list_frames_quad.At(i); class="kw">break;
      class="kw">default: class="kw">break;
     }
   class=class="str">"cmt">//--- if failed to get the pointer, move on to the next one
    if(frame==NULL)
      class="kw">continue;
   class=class="str">"cmt">//--- If the object ID correspond to the required one,
   class=class="str">"cmt">//--- class="kw">return the pointer to the detected object
    if(frame.ID()==id)
      class="kw">return frame;
   }
class=class="str">"cmt">//--- Nothing is found - class="kw">return NULL
  class="kw">return NULL;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+-----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the flag indicating the presence of the animation frame object  |
class=class="str">"cmt">//| with the specified type and ID                                       |
class=class="str">"cmt">//+-----------------------------------------------------------------------+
class="type">bool CAnimations::IsPresentFrame(class="kw">const ENUM_ANIMATION_FRAME_TYPE frame_type,class="kw">const class="type">int id)
  {
   class="kw">return(this.GetFrame(frame_type,id)!=NULL);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create a new text animation frame object                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
CFrame *CAnimations::CreateNewFrameText(class="kw">const class="type">int id)
  {
class=class="str">"cmt">//--- If the object with such an ID is already present, inform of that in the journal and class="kw">return NULL
   if(this.IsPresentFrame(ANIMATION_FRAME_TYPE_TEXT,id))
    {
     ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_FRAME_ALREADY_IN_LIST),(class="type">class="kw">string)id);
     class="kw">return NULL;
    }
class=class="str">"cmt">//--- Create a new text animation frame object with the specified ID
   CFrame *frame=new CFrameText(id,this.m_element);
class=class="str">"cmt">//--- If failed to create an object, inform of that and class="kw">return NULL
   if(frame==NULL)
    {
     ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_FRAME));
     class="kw">return NULL;
    }
class=class="str">"cmt">//--- If failed to add the created object to the list, inform of that, remove the object and class="kw">return NULL
   if(!this.m_list_frames_text.Add(frame))
    {
     ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST)," ID: ",id);
     class="kw">delete frame;
     class="kw">return NULL;
    }
class=class="str">"cmt">//--- Return the pointer to a newly created object
   class="kw">return frame;
  }

「矩形与文本动画帧的取用逻辑」

在 MT5 自定义指标里做图形动画,帧对象的管理最容易在重复创建时漏判。CAnimations::CreateNewFrameQuad 这个函数先拿 IsPresentFrame 查重:同 ID 的矩形帧若已在链表,直接 Print 报错并返回 NULL,避免后期对同一矩形重复下发坐标导致闪烁。 新建对象走 new CFrameQuad(id,this.m_element),失败就返回 NULL;若 Add 进 m_list_frames_quad 失败,则 delete 掉刚分配的内存再返 NULL,防止悬空指针。这一套在三处判错点都做了日志,调试时看专家日志能直接定位是重 ID、内存还是链表问题。 GetOrCreateFrame 则按 frame_type 分流:文本帧走 GetFrame 取指针,命中即返;未命中且 create_new=false 就报“帧不在列表”错误。矩形分支同理,差别只在类型枚举。实盘加载这类 EA 时,若日志频繁出现 ID 重复提示,说明调用层在每帧 tick 里无脑建帧,应改为先 GetOrCreate 再更新属性。 外汇与贵金属图表上跑此类动画须留意:高频重绘会增加 CPU 占用,极端行情可能拖慢下单响应,属于高风险环境下的额外开销。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create a new rectangle animation frame object                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
CFrame *CAnimations::CreateNewFrameQuad(class="kw">const class="type">int id)
  {
class=class="str">"cmt">//--- If the object with such an ID is already present, inform of that in the journal and class="kw">return NULL
   if(this.IsPresentFrame(ANIMATION_FRAME_TYPE_QUAD,id))
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_FRAME_ALREADY_IN_LIST),(class="type">class="kw">string)id);
      class="kw">return NULL;
     }
class=class="str">"cmt">//--- Create a new rectangle animation frame object with the specified ID
   CFrame *frame=new CFrameQuad(id,this.m_element);
class=class="str">"cmt">//--- If failed to create an object, inform of that and class="kw">return NULL
   if(frame==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_FRAME));
      class="kw">return NULL;
     }
class=class="str">"cmt">//--- If failed to add the created object to the list, inform of that, remove the object and class="kw">return NULL
   if(!this.m_list_frames_quad.Add(frame))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST)," ID: ",id);
      class="kw">delete frame;
      class="kw">return NULL;
     }
class=class="str">"cmt">//--- Return the pointer to a newly created object
   class="kw">return frame;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return or create a new animation frame object                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
CFrame *CAnimations::GetOrCreateFrame(class="kw">const class="type">class="kw">string source,class="kw">const class="type">int id,class="kw">const ENUM_ANIMATION_FRAME_TYPE frame_type,class="kw">const class="type">bool create_new)
  {
   class=class="str">"cmt">//--- Declare null pointers to objects
   CFrameQuad *frame_q=NULL;
   CFrameText *frame_t=NULL;
   class=class="str">"cmt">//--- Depending on the required object type
   class="kw">switch(frame_type)
     {
      class=class="str">"cmt">//--- If this is a text animation frame,
      case ANIMATION_FRAME_TYPE_TEXT :
         class=class="str">"cmt">//--- get the pointer to an object with a specified ID
         frame_t=this.GetFrame(ANIMATION_FRAME_TYPE_TEXT,id);
         class=class="str">"cmt">//--- If the pointer is obtained, class="kw">return it
         if(frame_t!=NULL)
            class="kw">return frame_t;
         class=class="str">"cmt">//--- If the flag of creating a new object is not set, report an error and class="kw">return NULL
         if(!create_new)
           {
            ::Print,(class="type">class="kw">string)id);
            class="kw">return NULL;
           }
         class=class="str">"cmt">//--- Return the result of creating a new text animation frame object(pointer to the created object)
         class="kw">return this.CreateNewFrameText(id);

      class=class="str">"cmt">//--- If this is a rectangle animation frame
      case ANIMATION_FRAME_TYPE_QUAD :

◍ 取帧失败时的兜底逻辑与背景文字绘制

在动画框架里按 ID 取矩形帧,第一步是先问对象池要指针。若 GetFrame 拿到非空指针就直接返回,避免重复创建;这是 MT5 图形对象复用最常见的省资源写法。 如果指针为空且调用方没开 create_new 开关,函数会向终端打印「指定 ID 的帧不存在」并返 NULL,而不是静默建对象。这个分支能防止脚本在错误 ID 上偷偷堆出垃圾对象。 背景文字接口 TextOnBG 把「取或建帧」封装成一行 GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_TEXT,create_new)。拿不到帧立即返 false,拿到才把文字、坐标、锚点、颜色和透明度(0–255 的 uchar)往下传,默认不强制重绘。 外汇与贵金属图表上跑这类 overlay 动画,注意对象池膨胀会拖慢报价刷新;高波动时段建议把 create_new 默认关掉只复用,降低 MT5 客户端卡顿概率。

MQL5 / C++
class=class="str">"cmt">//--- get the pointer to an object with a specified ID
frame_q=this.GetFrame(ANIMATION_FRAME_TYPE_QUAD,id);
class=class="str">"cmt">//--- If the pointer is obtained, class="kw">return it
if(frame_q!=NULL)
   class="kw">return frame_q;
class=class="str">"cmt">//--- If the flag of creating a new object is not set, report an error and class="kw">return NULL
if(!create_new)
  {
   ::Print,(class="type">class="kw">string)id);
   class="kw">return NULL;
  }
class=class="str">"cmt">//--- Return the result of creating a new rectangle animation frame object(pointer to the created object)
class="kw">return this.CreateNewFrameQuad(id);
class=class="str">"cmt">//--- In the remaining cases, class="kw">return NULL
class="kw">default:
   class="kw">return NULL;
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+--------------------------------------------------------------------------------+
class=class="str">"cmt">//| Display the text on the background, while saving and restoring the background  |
class=class="str">"cmt">//+--------------------------------------------------------------------------------+
class="type">bool CAnimations::TextOnBG(class="kw">const class="type">int id,
                           class="kw">const class="type">class="kw">string text,
                           class="kw">const class="type">int x,
                           class="kw">const class="type">int y,
                           class="kw">const ENUM_TEXT_ANCHOR anchor,
                           class="kw">const class="type">class="kw">color clr,
                           class="kw">const class="type">uchar opacity,
                           class="kw">const class="type">bool create_new=true,
                           class="kw">const class="type">bool redraw=false)
  {
   CFrameText *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_TEXT,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.TextOnBG(text,x,y,anchor,clr,opacity,redraw);
  }

在背景帧上画像素与竖线

做 MT5 图表动画时,经常需要把标记直接画在背景层而不是覆盖在 K 线上。CAnimations 类提供了两个底层方法,分别处理单像素点和垂直线段的绘制,都落在背景帧(BG)上。 SetPixelOnBG 接收帧 ID、坐标 x/y、颜色 clr,以及可选的 opacity(默认 255 不透明)、create_new(默认 true 找不到就新建)、redraw(默认 false 不立即重绘)。方法内部先 GetOrCreateFrame 拿四边形帧,空指针直接返回 false,否则转发给帧对象的 SetPixelOnBG。 DrawLineVerticalOnBG 参数更具体:除了 id、x 固定列坐标,还要 y1、y2 两个端点纵坐标,其余颜色、透明度、创建与重绘标志同像素方法一致。它同样先取或建帧,失败返 false,成功调帧的 DrawLineVerticalOnBG。 实际验证时,把 opacity 设成 120 左右画竖线,能看出背景层半透明参考线不会挡住实体;redraw 置 true 才会在调用后立即刷新,否则靠后续帧循环出图。外汇与贵金属图表叠加这类自定义层,仍属高风险环境下的视觉辅助,不代表任何方向判断。

MQL5 / C++
class="type">bool CAnimations::SetPixelOnBG(class="kw">const class="type">int id,class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">class="kw">color clr,class="kw">const class="type">uchar opacity=class="num">255,class="kw">const class="type">bool create_new=true,class="kw">const class="type">bool redraw=false)
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
     class="kw">return false;
   class="kw">return frame.SetPixelOnBG(x,y,clr,opacity,redraw);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a segment of a vertical line                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawLineVerticalOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                                       class="kw">const class="type">int   x,               class=class="str">"cmt">// Segment X coordinate
                                       class="kw">const class="type">int   y1,              class=class="str">"cmt">// Y coordinate of the segment first point
                                       class="kw">const class="type">int   y2,              class=class="str">"cmt">// Y coordinate of the segment second point
                                       class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                                       class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                                       class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                                       class="kw">const class="type">bool  redraw=false)    class=class="str">"cmt">// Chart redraw flag
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawLineVerticalOnBG(x,y1,y2,clr,opacity,redraw);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「在背景层画水平线段的实现」

做 MT5 图表动画时,经常需要在报价背景上叠一条参考线而不挡住 K 线。下面这段封装把水平线段直接画到背景帧(BG)上,调用方只需给帧编号和起止 X 坐标。 函数 DrawLineHorizontalOnBG 吃 7 个参数:id 是帧编号,x1/x2 是线段两端横坐标,y 是那条线的纵坐标,clr 控制颜色,opacity 默认 255(不透明),create_new 默认 true 表示没有该帧就新建,redraw 默认 false 不立即重绘。 内部先 GetOrCreateFrame 按 DFUN 和 ANIMATION_FRAME_TYPE_QUAD 拿到(或建出)一个 CFrameQuad 对象;若返回 NULL 直接返回 false。拿到帧后把参数透传给 frame.DrawLineHorizontalOnBG,由具体帧对象完成实际绘制。 想验证的话,开 MT5 附加一段调用:传 id=0、x1=10、x2=200、y=100、clr=clrRed,就能在背景看到一条红色水平线。外汇和贵金属波动大,这类画线只做视觉辅助,不涉及任何方向判断。

MQL5 / C++
class="type">bool CAnimations::DrawLineHorizontalOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                           class="kw">const class="type">int   x1,                 class=class="str">"cmt">// X coordinate of the segment first point
                                           class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the segment second point
                                           class="kw">const class="type">int   y,                  class=class="str">"cmt">// Segment Y coordinate
                                           class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
                                           class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
                                           class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
                                           class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawLineHorizontalOnBG(x1,x2,y,clr,opacity,redraw);
  }

◍ 在背景层画线:线段与折线的底层封装

做 MT5 图表动画时,把辅助线画到背景层而不是覆盖在 K 线上,是避免视觉干扰的直接办法。下面这段封装把单条线段的绘制收敛成一个方法,调用者只需给两个端点的像素坐标和颜色。 方法签名里 x1、y1 是线段起点坐标,x2、y2 是终点坐标,clr 控制颜色,opacity 默认 255 即完全不透明,create_new 默认 true 表示找不到对应帧就新建,redraw 默认 false 即不强制重绘整图。 内部先通过 GetOrCreateFrame 拿到或创建指定 ID 的四边形帧对象,若返回 NULL 直接返回 false;拿到后转调该帧的 DrawLineOnBG,把坐标和样式透传下去。 折线绘制则是另一套接口:传入 id、array_x[] 与 array_y[] 两个数组(分别装各顶点的 X、Y 像素坐标)、颜色与透明度。数组长度需一致,否则底层连线会越界或漏点。

MQL5 / C++
里能看到 DrawPolylineOnBG 的参数表只列到 create_new,但按同类方法惯例 opacity 与 redraw 同样有默认值。实盘加载这类脚本前,建议在策略测试器用 1 分钟 EURUSD 数据跑一遍,确认背景线不阻挡价格点击。外汇与贵金属杠杆高,任何可视化工具都只是辅助,信号误读可能导致快速亏损。

MQL5 / C++
class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the segment second point
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawLineOnBG(x1,y1,x2,y2,clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a polyline                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawPolylineOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                                   class="type">int    &array_x[],         class=class="str">"cmt">// Array with the X coordinates of polyline points
                                   class="type">int    &array_y[],         class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                   class="kw">const class="type">class="kw">color clr,           class=class="str">"cmt">// Color
                                   class="kw">const class="type">uchar opacity=class="num">255,   class=class="str">"cmt">// Opacity
                                   class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag

背景层多边形与矩形的绘制接口

在 MT5 自定义指标里做背景标注,CAnimations 类提供了两组直接落到底层 Quad 帧的方法:DrawPolygonOnBG 走顶点数组,DrawRectangleOnBG 走两点坐标。两者都先通过 GetOrCreateFrame 按 id 取帧,取不到就直接返回 false,不会抛异常。 DrawPolygonOnBG 接收 array_x[] 与 array_y[] 两个 int 型引用数组,点数由数组长度决定;clr 控制颜色,opacity 默认 255(不透明),create_new 默认 true 表示帧不存在就新建,redraw 默认 false 暂缓重绘以提升性能。 DrawRectangleOnBG 的入口参数更精简:除 id 外只需 x1/y1 和 x2/y2 四个 int 坐标,同样带 clr、opacity、create_new、redraw 四件套。外汇与贵金属图表叠加这类背景图形时波动剧烈,参数误用可能导致遮挡 K 线,实盘前务必在模拟盘验证。 让小布替你跑这套 把 opacity 从 255 改成 60 左右,背景多边形就会半透明,价格行为本身不会被盖住,适合拿来做区间可视化。

MQL5 / C++
class="type">bool CAnimations::DrawPolygonOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                                     class="type">int    &array_x[],          class=class="str">"cmt">// Array with the X coordinates of polygon points
                                     class="type">int    &array_y[],          class=class="str">"cmt">// Array with the Y coordinates of polygon points
                                     class="kw">const class="type">class="kw">color clr,            class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar opacity=class="num">255,    class=class="str">"cmt">// Opacity
                                     class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                                     class="kw">const class="type">bool  redraw=false)   class=class="str">"cmt">// Chart redraw flag
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawPolygonOnBG(array_x,array_y,clr,opacity,redraw);
  }

class="type">bool CAnimations::DrawRectangleOnBG(class="kw">const class="type">int id,               class=class="str">"cmt">// Frame ID
                                     class="kw">const class="type">int  x1,              class=class="str">"cmt">// X coordinate of the first point

「背景图层画矩形与圆的坐标参数」

在 MT5 的动画框架里,往图表背景层塞图形靠的是 CAnimations 类下的两个方法。DrawRectangleOnBG 用两组对角点坐标 (x1,y1) 与 (x2,y2) 框出矩形区域,颜色与透明度分别由 clr 和 opacity 控制,opacity 默认 255 即完全不透明。 看参数排布能发现一个细节:create_new 默认 true 代表找不到对应帧就新建,redraw 默认 false 则不主动重绘图表——这意味着你批量画多个背景框时,若都依赖自动重绘可能看不到即时效果,得在最后一笔把 redraw 传 true。 DrawCircleOnBG 的接口更直白:用圆心 (x,y) 加半径 r 定位,同样吃 id 做帧管理。外汇和贵金属图表上叠这类背景圈,常用于标波动区间,但价格跳空时坐标映射可能偏移,属于高风险品种下的视觉辅助而非信号本身。 下面这段是从类实现里抠出的方法签名与核心转发逻辑,逐行对应了上面说的参数职责。

MQL5 / C++
class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the second point defining the rectangle
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawRectangleOnBG(x1,y1,x2,y2,clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw the circle                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawCircleOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                                 class="kw">const class="type">int   x,             class=class="str">"cmt">// X coordinate of the circle center
                                 class="kw">const class="type">int   y,             class=class="str">"cmt">// Y coordinate of the circle center
                                 class="kw">const class="type">int   r)             class=class="str">"cmt">// Circle radius

◍ 在背景层画圆与三角形的接口参数

CAnimations 类把图形绘制拆到背景层,DrawCircleOnBG 负责在图表背景画圆。它接收帧 ID、圆心坐标、半径、颜色、不透明度(默认 255 即完全不透明)、是否新建对象(默认 true)以及是否立即重绘(默认 false)这几个入参。 内部先通过 GetOrCreateFrame 按 DFUN 和 id 拿到或新建一个 QUAD 类型帧,拿不到就直接返回 false;拿到后转调该帧的 DrawCircleOnBG 完成实际绘制。这样把对象生命周期管理和具体画法解耦,调用方不必关心帧是否存在。 紧随其后的 DrawTriangleOnBG 则面向三角形,入参是帧 ID 加三个顶点的 x/y 坐标(x1,y1 到 x3,y3),用于在背景层定位一个实心或线框三角。外汇与贵金属图表叠加这类自定义图形时波动剧烈,叠加层仅供参考,实盘高风险。 开 MT5 把这两段塞进自己的 CAnimations 派生类,改 id 和坐标就能直观验证背景绘制是否如预期覆盖在 K 线下方。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)    class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawCircleOnBG(x,y,r,clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a triangle                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawTriangleOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                                   class="kw">const class="type">int   x1,            class=class="str">"cmt">// X coordinate of the triangle first vertex
                                   class="kw">const class="type">int   y1,            class=class="str">"cmt">// Y coordinate of the triangle first vertex
                                   class="kw">const class="type">int   x2,            class=class="str">"cmt">// X coordinate of the triangle second vertex
                                   class="kw">const class="type">int   y2,            class=class="str">"cmt">// Y coordinate of the triangle second vertex
                                   class="kw">const class="type">int   x3,            class=class="str">"cmt">// X coordinate of the triangle third vertex
                                   class="kw">const class="type">int   y3)            class=class="str">"cmt">// Y coordinate of the triangle third vertex

在背景层画三角与椭圆的接口细节

CAnimations 类把图形绘制拆到了背景层(OnBG),目的是不挡 K 线也不参与主图交互。DrawTriangleOnBG 接收三个点坐标加颜色、透明度,内部先通过 GetOrCreateFrame 拿到或新建一个 QUAD 类型帧,失败直接返回 false,成功则转调帧对象的 DrawTriangleOnBG。 参数里 opacity 默认 255 即完全不透明,create_new 默认 true 表示找不到对应 id 的帧就新建,redraw 默认 false 则不主动重绘图表——这几处默认值决定了你批量画多个图形时若复用同一 id,可能覆盖旧帧而不是重画。 椭圆接口 DrawEllipseOnBG 用两个点定义边界框,x1/y1 与 x2/y2 分别是对角点,同样带 clr 与 opacity=255 的默认设定。实盘加载这类 EA 或指标时,外汇与贵金属波动剧烈、滑点不可控,图形只是辅助,任何形态信号都只是概率倾向,别当成入场保证。 想验证就开 MT5 建个 CAnimations 实例,循环 id 从 0 到 9 调 DrawTriangleOnBG 画九宫格,再把 create_new 改 false 看是否只更新不新建,帧管理逻辑一眼便知。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawTriangleOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw an ellipse class="kw">using two points                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawEllipseOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                 class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                                 class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                                 class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the second point defining the ellipse
                                 class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
                                 class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                                 class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity

「在背景层画椭圆与裁剪弧线」

CAnimations 类把图形绘制拆成了背景层接口,先说画椭圆:函数内部通过 GetOrCreateFrame 拿到或新建一个 QUAD 类型帧,失败直接返回 false,成功则转调 frame.DrawEllipseOnBG,传入矩形对角点 (x1,y1)、(x2,y2) 和颜色、透明度。 参数 create_new 默认 true 表示强制新建帧,redraw 默认 false 则不立即重绘图表——这在高频刷新动画里能省掉不必要的 CPU 开销,值得在 EA 里留意。 弧线绘制 DrawArcOnBG 的思路是在同一个背景矩形里内接椭圆,再从矩形中心向 (x3,y3)、(x4,y4) 拉两条射线,裁出中间那段弧。坐标全是 int 像素值,说明这套接口面向的是图表画布而非价格坐标,验证时得先确认你的自定义帧 ID 没和已有对象撞车。 外汇与贵金属图表叠加这类自绘图形存在渲染冲突风险,参数调错可能导致 MT5 图表卡顿,实盘前请在模拟环境跑通。

MQL5 / C++
class="kw">const class="type">bool  create_new=true,                 class=class="str">"cmt">// New object creation flag
                       class="kw">const class="type">bool  redraw=false)                class=class="str">"cmt">// Chart redraw flag
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawEllipseOnBG(x1,y1,x2,y2,clr,opacity,redraw);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw an arc of an ellipse inscribed in a rectangle               |
class=class="str">"cmt">//| with the corners in(x1,y1) and(x2,y2).                        |
class=class="str">"cmt">//| The arc boundaries are cropped from the ellipse center           |
class=class="str">"cmt">//| moving to two points with the coordinates of(x3,y3) and(x4,y4)|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawArcOnBG(class="kw">const class="type">int id,                                class=class="str">"cmt">// Frame ID
                              class="kw">const class="type">int   x1,                              class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
                              class="kw">const class="type">int   y1,                              class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
                              class="kw">const class="type">int   x2,                              class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
                              class="kw">const class="type">int   y2,                              class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
                              class="kw">const class="type">int   x3,                              class=class="str">"cmt">// X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
                              class="kw">const class="type">int   y3,                              class=class="str">"cmt">// Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary

◍ 在背景层画椭圆扇区与弧线的坐标约定

MT5 动画框架里,想在图表背景直接画一个内接于矩形的椭圆扇区(pie),核心参数是两对角点 (x1,y1) 与 (x2,y2),它们框住椭圆外接矩形。扇区边界从椭圆中心出发,分别连到 (x3,y3) 和 (x4,y4) 两个坐标点,由此裁剪出闭合扇形。 对应的 DrawArcOnBG 变体则只画弧不填充:同样用矩形定椭圆,再从中心拉线到 (x3,y3) 与 (x4,y4) 确定弧的起止边界。下面这段参数声明直接暴露了它的接口形态,注意 opacity 默认 255 表示不透明,create_new 默认 true 会新建帧对象。

MQL5 / C++
class="kw">const class="type">int   x4,              class=class="str">"cmt">// 第二点的X坐标,从矩形中心拉线到该点以确定弧边界
class="kw">const class="type">int   y4,              class=class="str">"cmt">// 第二点的Y坐标,从矩形中心拉线到该点以确定弧边界
class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// 颜色
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// 不透明度
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// 是否新建对象标志
class="kw">const class="type">bool  redraw=false)    class=class="str">"cmt">// 是否重绘图表标志
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
   class="kw">return false;
 class="kw">return frame.DrawArcOnBG(x1,y1,x2,y2,x3,y3,x4,y4,clr,opacity,redraw);
}
DrawPieOnBG 的函数头进一步印证了矩形角点命名规则:x1,y1 是左上角,x2,y2 是右下角,和弧线接口共用同一套坐标语义。实盘用这类接口做背景高亮时,外汇与贵金属波动剧烈,坐标按像素算,缩放图表可能导致图形错位,建议开 MT5 用固定窗口尺寸验证一次。

MQL5 / C++
class="kw">const class="type">int   x4,              class=class="str">"cmt">// X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">int   y4,              class=class="str">"cmt">// Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)    class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
   class="kw">return false;
 class="kw">return frame.DrawArcOnBG(x1,y1,x2,y2,x3,y3,x4,y4,clr,opacity,redraw);
}

在背景层画扇形与填充区的参数拆解

MT5 自定义图形库里,背景层绘制扇形(Pie)依赖一组坐标与样式参数,理解它们才能精准控制图形位置。下面这段函数签名给出了 DrawPieOnBG 上层封装所需的全部入参。 x1、y1 是矩形左上角,x2、y2 是右下角,这组对角框定了扇形的外接矩形边界。x3、y3 与 x4、y4 则是用来确定弧线起止边界的两个参考点,引擎按这两点连线与矩形的交截生成弧。 clr 控制描边颜色,fill_clr 控制填充色,opacity 默认 255 即不透明,可调低做半透明叠加。create_new 默认 true 表示找不到同名帧就新建,redraw 默认 false 不立即重绘,批量绘图时建议留 false 以减少卡顿。 核心调用只有一句:GetOrCreateFrame 拿到或新建 QUAD 类型帧,再委托 frame.DrawPieOnBG 执行实际绘制,返回 bool 表示成败。若 frame 为空直接返 false,调用方需自行处理对象创建失败的情况。

MQL5 / C++
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the bottom right corner of the rectangle
class="kw">const class="type">int   x3,                 class=class="str">"cmt">// X coordinate of the first point to find the arc boundaries
class="kw">const class="type">int   y3,                 class=class="str">"cmt">// Y coordinate of the first point to find the arc boundaries
class="kw">const class="type">int   x4,                 class=class="str">"cmt">// X coordinate of the second point to find the arc boundaries
class="kw">const class="type">int   y4,                 class=class="str">"cmt">// Y coordinate of the second point to find the arc boundaries
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">class="kw">color fill_clr,           class=class="str">"cmt">// Fill class="type">class="kw">color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawPieOnBG(x1,y1,x2,y2,x3,y3,x4,y4,clr,fill_clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Fill in the area                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::FillOnBG(class="kw">const class="type">int   id,                 class=class="str">"cmt">// Frame ID

「在背景层填色矩形的坐标与参数」

想在 MT5 图表背景上画一块半透明填充区,核心入口是 CAnimations::DrawRectangleFillOnBG。它接收两个对角点 (x1,y1) 与 (x2,y2) 的像素坐标,把矩形区域交给底层 CFrameQuad 处理。 与之配套的 FillOnBG 方法还暴露了更细的填充起点参数:x、y 为填充起始像素坐标,clr 控制颜色,opacity 默认 255(完全不透明),threshould 默认 0 表示不做边界阈值剔除,create_new 默认 true 会新建帧对象,redraw 默认 false 不立即重绘。 调用链很直接:GetOrCreateFrame 按 id 取或建帧,失败返回 false;成功则转交 frame.FillOnBG(x,y,clr,opacity,threshould,redraw)。实盘加载这类背景标记时,外汇与贵金属品种波动剧烈,叠加层可能遮挡价格行为,建议 opacity 降到 120 以下再观察。

MQL5 / C++
class="kw">const class="type">int   x,                class=class="str">"cmt">// X coordinate of the filling start point
class="kw">const class="type">int   y,                class=class="str">"cmt">// Y coordinate of the filling start point
class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
class="kw">const class="type">uint  threshould=class="num">0,     class=class="str">"cmt">// Threshold
class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)     class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.FillOnBG(x,y,clr,opacity,threshould,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled rectangle                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawRectangleFillOnBG(class="kw">const class="type">int id,      class=class="str">"cmt">// Frame ID
              class="kw">const class="type">int   x1,              class=class="str">"cmt">// X coordinate of the first point defining the rectangle
              class="kw">const class="type">int   y1,              class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
              class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the second point defining the rectangle

◍ 背景层填充图形的绘制接口

在 MT5 的动画框架里,背景层(OnBG)绘制和主图绘制是分开的。上面这段截自 CAnimations 类的两个方法,分别负责在背景层画实心矩形和实心圆,坐标系用的是像素整数,不是价格/时间轴。 DrawRectangleFillOnBG 接收两个对角点 (x1,y1) 与 (x2,y2),外加颜色 clr 与透明度 opacity,默认 opacity=255 表示完全不透明。方法内部先通过 GetOrCreateFrame 拿(或建)一个 QUAD 类型的帧对象,失败就返回 false,成功则转调帧自身的 DrawRectangleFillOnBG。 DrawCircleFillOnBG 的参数更直白:圆心 (x,y)、半径 r、颜色 clr、透明度 opacity 默认 255。两者都带 create_new(默认 true)和 redraw(默认 false)开关,控制是否新建帧、是否立即重绘图表。 想验证这套机制,开 MT5 随便挂个 EA,把这两个方法接进 OnChartEvent 或 Timer,用 opacity=120 画个半透明矩形,就能直观看到背景层叠加效果。外汇和贵金属杠杆高,这类视觉辅助只帮你看盘,不预示方向。

MQL5 / C++
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,         class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,     class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)        class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawRectangleFillOnBG(x1,y1,x2,y2,clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled circle                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawCircleFillOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                                     class="kw">const class="type">int   x,            class=class="str">"cmt">// X coordinate of the circle center
                                     class="kw">const class="type">int   y,            class=class="str">"cmt">// Y coordinate of the circle center
                                     class="kw">const class="type">int   r,            class=class="str">"cmt">// Circle radius
                                     class="kw">const class="type">class="kw">color clr,          class=class="str">"cmt">// Color
                                     class="kw">const class="type">uchar opacity=class="num">255)  class=class="str">"cmt">// Opacity

背景层填充图形的绘制入口

在 MT5 的动画框架里,想在图表背景层画实心圆或三角形,都走 CAnimations 类的同一类接口。核心是先拿帧对象,再往背景层推绘制指令。 以画实心圆为例,函数尾部两个布尔参数很关键:create_new 默认 true 表示帧不存在就新建,redraw 默认 false 表示不立即重绘图表。若 GetOrCreateFrame 返回 NULL,函数直接返回 false,不会抛错但图形不出现。 三角形填充接口 DrawTriangleFillOnBG 用 6 个 int 坐标(x1,y1 到 x3,y3)定位三个顶点,color 控制颜色,opacity 默认 255 即完全不透明。把 opacity 调到 100 左右,背景三角就会半透明,适合做区域高亮而不挡 K 线。 外汇与贵金属波动剧烈、杠杆高风险大,这类图形仅作视觉辅助,不代表任何方向判定。

MQL5 / C++
class="kw">const class="type">bool  create_new=true,      class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false           class=class="str">"cmt">// Chart redraw flag
  {
  CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
  if(frame==NULL)
     class="kw">return false;
  class="kw">return frame.DrawCircleFillOnBG(x,y,r,clr,opacity,redraw);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled triangle                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawTriangleFillOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                                      class="kw">const class="type">int   x1,              class=class="str">"cmt">// X coordinate of the triangle first vertex
                                      class="kw">const class="type">int   y1,              class=class="str">"cmt">// Y coordinate of the triangle first vertex
                                      class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the triangle second vertex
                                      class="kw">const class="type">int   y2,              class=class="str">"cmt">// Y coordinate of the triangle second vertex
                                      class="kw">const class="type">int   x3,              class=class="str">"cmt">// X coordinate of the triangle third vertex
                                      class="kw">const class="type">int   y3,              class=class="str">"cmt">// Y coordinate of the triangle third vertex
                                      class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                                      class="kw">const class="type">uchar opacity=class="num">255      class=class="str">"cmt">// Opacity

「背景层多边形与椭圆的绘制入口」

在 MT5 自定义指标里做背景标注,CAnimations 类把三角形、多边形、椭圆都归到背景层(OnBG)绘制,避免遮挡 K 线本身。 DrawPolygonFillOnBG 接收两个 int 数组 array_x[] 与 array_y[] 存放顶点像素坐标,clr 控制颜色,opacity 默认 255(不透明),create_new 默认 true 表示找不到对应帧就新建,redraw 默认 false 推迟重绘以提升性能。 内部先通过 GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new) 拿 Quad 类型帧,空指针直接返回 false;拿到后转调 frame.DrawPolygonFillOnBG,把数组和样式透传下去。 DrawEllipseFillOnBG 的函数签名只露出 id 与注释「矩形内接椭圆」,说明它靠矩形对角坐标推算椭圆范围,具体参数列表需看后续代码段。 实盘用这类接口高亮贵金属支撑区时,注意外汇/贵金属杠杆高、滑点大,背景图形只是辅助视图,不代表价格必然反应。

MQL5 / C++
class="kw">const class="type">bool  create_new=true,                 class=class="str">"cmt">// New object creation flag
                       class="kw">const class="type">bool  redraw=false)                 class=class="str">"cmt">// Chart redraw flag
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawTriangleFillOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled polygon                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawPolygonFillOnBG(class="kw">const class="type">int id,                    class=class="str">"cmt">// Frame ID
                                       class="type">int       &array_x[],           class=class="str">"cmt">// Array with the X coordinates of polygon points
                                       class="type">int       &array_y[],           class=class="str">"cmt">// Array with the Y coordinates of polygon points
                                       class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
                                       class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
                                       class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
                                       class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawPolygonFillOnBG(array_x,array_y,clr,opacity,redraw);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a filled ellipse inscribed in a rectangle                          |
class=class="str">"cmt">//| with the given coordinates                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawEllipseFillOnBG(class="kw">const class="type">int id,                    class=class="str">"cmt">// Frame ID

◍ 在背景层画填充椭圆与抗锯齿点

给图表背景叠可视化元素时,CAnimations 类提供了两个底层接口:DrawEllipseFillOnBG 负责画实心椭圆,SetPixelAAOnBG 用抗锯齿算法点一个像素。两者都挂在背景层,不会挡住 K 线交互。 DrawEllipseFillOnBG 的参数很直白:x1,y1 是左上角,x2,y2 是右下角,clr 上色,opacity 默认 255 不透明,create_new 控制复用还是新建帧,redraw 默认 false 不立即重绘。代码里先 GetOrCreateFrame 拿帧对象,失败直接返 false,成功就转发给帧的 DrawEllipseFillOnBG。

MQL5 / C++
class="kw">const class="type">int x1, class=class="str">"cmt">// 构成矩形框的左上角 X 坐标
class="kw">const class="type">int y1, class=class="str">"cmt">// 构成矩形框的左上角 Y 坐标
class="kw">const class="type">int x2, class=class="str">"cmt">// 构成矩形框的右下角 X 坐标
class="kw">const class="type">int y2, class=class="str">"cmt">// 构成矩形框的右下角 Y 坐标
class="kw">const class="type">class="kw">color clr, class=class="str">"cmt">// 颜色
class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// 不透明度
class="kw">const class="type">bool create_new=true, class=class="str">"cmt">// 新建对象标志
class="kw">const class="type">bool redraw=false) class=class="str">"cmt">// 图表重绘标志
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawEllipseFillOnBG(x1,y1,x2,y2,clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a point class="kw">using AntiAliasing algorithm                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::SetPixelAAOnBG(class="kw">const class="type">int id, class=class="str">"cmt">// 帧 ID
                                 class="kw">const class="type">class="kw">double x, class=class="str">"cmt">// 点 X 坐标
                                 class="kw">const class="type">class="kw">double y, class=class="str">"cmt">// 点 Y 坐标
                                 class="kw">const class="type">class="kw">color  clr) class=class="str">"cmt">// 颜色
SetPixelAAOnBG 只收四个参:id 定位帧,x/y 用 double 存亚像素坐标,clr 定色。抗锯齿点在画精细标记(比如枢轴点高亮)时比普通 Plot 更顺眼。外汇与贵金属波动剧烈、杠杆高风险大,这类背景绘制纯属辅助观察,别当成信号依据。 开 MT5 把这段塞进你自己的 CAnimations 派生类,调一次 DrawEllipseFillOnBG(0,10,10,100,100,clrRed),就能在左上角看到一块红色填充区,验证帧管道有没有通。

MQL5 / C++
class="kw">const class="type">int x1, class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
class="kw">const class="type">int y1, class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
class="kw">const class="type">int x2, class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
class="kw">const class="type">int y2, class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
class="kw">const class="type">class="kw">color clr, class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
class="kw">const class="type">bool create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool redraw=false) class=class="str">"cmt">// Chart redraw flag
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawEllipseFillOnBG(x1,y1,x2,y2,clr,opacity,redraw);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a point class="kw">using AntiAliasing algorithm                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::SetPixelAAOnBG(class="kw">const class="type">int id, class=class="str">"cmt">// Frame ID
                                 class="kw">const class="type">class="kw">double x, class=class="str">"cmt">// Point X coordinate
                                 class="kw">const class="type">class="kw">double y, class=class="str">"cmt">// Point Y coordinate
                                 class="kw">const class="type">class="kw">color  clr) class=class="str">"cmt">// Color

在背景层画抗锯齿线段的实现细节

想在 MT5 图表背景上画一条平滑的自由手绘线,核心不是直接调绘图函数,而是先拿到一个四边形动画帧对象,再往上面落像素。下面这段接口把坐标、颜色、透明度都作为参数暴露出来,方便你在 EA 里动态描线。 CAnimations::DrawLineAAOnBG 的入参里,x1/y1 与 x2/y2 是线段两端相对于背景帧的像素坐标,clr 控制颜色,opacity 默认 255 即完全不透明,create_new 默认 true 表示帧不存在时新建。若把 redraw 设为 false,则不会立即重绘图表,适合批量落点后统一刷新。 真正干活的是 GetOrCreateFrame 先按 id 取或建一个 ANIMATION_FRAME_TYPE_QUAD 类型的帧,失败直接返回 false;随后调用该帧的 SetPixelAAOnBG 做抗锯齿像素写入。你在实盘面板里看到的那种不锯齿的辅助线,大概率就是这么一层层画出来的。 外汇与贵金属图表叠加这类自定义绘制时,注意高频重绘可能拖慢终端,建议把 redraw 留 false,攒够一批线段再手动刷一次。

MQL5 / C++
class="type">bool CAnimations::DrawLineAAOnBG(class="kw">const class="type">int id,                      class=class="str">"cmt">// Frame ID
                                    class="kw">const class="type">int   x1,                    class=class="str">"cmt">// X coordinate of the segment first point
                                    class="kw">const class="type">int   y1,                    class=class="str">"cmt">// Y coordinate of the segment first point
                                    class="kw">const class="type">int   x2,                    class=class="str">"cmt">// X coordinate of the segment second point
                                    class="kw">const class="type">int   y2,                    class=class="str">"cmt">// Y coordinate of the segment second point
                                    class="kw">const class="type">class="kw">color clr,                   class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar opacity=class="num">255,           class=class="str">"cmt">// Opacity
                                    class="kw">const class="type">bool  create_new=true,       class=class="str">"cmt">// New object creation flag

「抗锯齿与Wu算法画线落点差异」

在 MT5 自定义动画框架里,背景层画线提供了两套实现:DrawLineAAOnBG 走抗锯齿(AA)逻辑,DrawLineWuOnBG 则采用 Wu 算法做自由线段插值。两者都挂在 CAnimations 类下,通过 GetOrCreateFrame 拿到或新建 CFrameQuad 对象,再转调帧内的具体绘制方法。 从参数签名看,Wu 版本默认 opacity=255、create_new=true、redraw=false,坐标均为 int 型屏幕像素;AA 版本在调用时额外透传了 style 参数(默认 UINT_MAX,即沿用 ENUM_LINE_STYLE 或自定义线型)。这意味着若你想画虚线背景参考线,AA 接口直接给 style 即可,Wu 接口本身不接收线型枚举。 实盘图表叠加辅助线时,Wu 算法在斜线上过渡更平滑、带宽更窄,AA 版本则对水平和垂直段更省计算。外汇与贵金属杠杆交易高风险,这类视觉层优化只影响判读效率,不改变任何信号概率。

MQL5 / C++
class="kw">const class="type">bool  redraw=false,                 class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)                 class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawLineAAOnBG(x1,y1,x2,y2,clr,opacity,redraw,style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a segment of a freehand line class="kw">using the                    |
class=class="str">"cmt">//| Wu algorithm                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawLineWuOnBG(class="kw">const class="type">int id,                     class=class="str">"cmt">// Frame ID
                                 class="kw">const class="type">int   x1,                  class=class="str">"cmt">// X coordinate of the segment first point
                                 class="kw">const class="type">int   y1,                  class=class="str">"cmt">// Y coordinate of the segment first point
                                 class="kw">const class="type">int   x2,                  class=class="str">"cmt">// X coordinate of the segment second point
                                 class="kw">const class="type">int   y2,                  class=class="str">"cmt">// Y coordinate of the segment second point
                                 class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
                                 class="kw">const class="type">uchar opacity=class="num">255,         class=class="str">"cmt">// Opacity
                                 class="kw">const class="type">bool  create_new=true,     class=class="str">"cmt">// New object creation flag
                                 class="kw">const class="type">bool  redraw=false)        class=class="str">"cmt">// Chart redraw flag

◍ 在背景层画带宽度平滑线段的实现

CAnimations 类里提供了一组往背景帧绘制线段的方法,其中 DrawLineThickOnBG 负责画带指定宽度的自由手绘线段,并内置平滑算法与坐标预排序。 它先通过 GetOrCreateFrame 拿到或新建一个四边形动画帧(类型 ANIMATION_FRAME_TYPE_QUAD),若返回 NULL 则直接返回 false,避免空指针继续往下跑。 函数签名暴露了关键参数:id 决定帧编号,x1/y1 与 x2/y2 是线段两端坐标,size 控制线宽,clr 为颜色,opacity 默认 255(不透明),create_new 默认 true 表示允许新建对象。 线型 style 参数用 UINT_MAX 作默认值,代表可接收 ENUM_LINE_STYLE 枚举值或自定义数值,调用方不传时就走框架默认渲染。 要验证这套逻辑,可在 MT5 自建 EA 里实例化 CAnimations 并传不同 size 与 opacity,观察背景层线段抗锯齿与粗线边缘的过渡是否符合预期。

MQL5 / C++
class="kw">const class="type">uint  style=UINT_MAX)        class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawLineWuOnBG(x1,y1,x2,y2,clr,opacity,redraw,style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a  segment of a freehand line having a specified width       |
class=class="str">"cmt">//| class="kw">using a smoothing algorithm                                       |
class=class="str">"cmt">//| with the preliminary sorting                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawLineThickOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                    class="kw">const class="type">int  x1,                class=class="str">"cmt">// X coordinate of the segment first point
                                    class="kw">const class="type">int  y1,                class=class="str">"cmt">// Y coordinate of the segment first point
                                    class="kw">const class="type">int  x2,                class=class="str">"cmt">// X coordinate of the segment second point
                                    class="kw">const class="type">int  y2,                class=class="str">"cmt">// Y coordinate of the segment second point
                                    class="kw">const class="type">int  size,              class=class="str">"cmt">// Line width
                                    class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                    class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag

背景层粗线绘制的参数与兜底

在自定义图表动画类里,往背景层画一条带宽度的线段,靠的是把调用转交给 CFrameQuad 实例。先看 DrawLineThickOnBG 的尾部参数:redraw 默认 false 表示不强制重绘整图,style 接收 ENUM_LINE_STYLE 或自定义值,end_style 默认 LINE_END_ROUND 控制线端圆头。 代码里先用 GetOrCreateFrame 按 id 拿(或建)四边形帧,拿不到就直接 return false,这层空指针判断避免了对空帧调用绘图导致 MT5 脚本异常终止。 紧随其后的 DrawLineThickVerticalOnBG 专门画竖直线段,x 固定、y1/y2 定起止,size 是线宽,opacity 默认 255 即完全不透明;create_new 默认 true 意味着同 id 帧不存在时会新建,而非复用旧帧。 外汇与贵金属图表叠加这类自绘层时,注意背景重绘频率过高可能拖累 tick 响应,建议先在 EURUSD 的 M1 上用 size=3、opacity=180 跑一遍确认帧管理符合预期。

MQL5 / C++
class="kw">const class="type">bool  redraw=false,                 class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID,             class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
ENUM_LINE_END end_style=LINE_END_ROUND)    class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawLineThickOnBG(x1,y1,x2,y2,size,clr,opacity,redraw,style,end_style);
}
class=class="str">"cmt">//+---------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a vertical segment of a freehand line having a specified width  |
class=class="str">"cmt">//| class="kw">using a smoothing algorithm                                          |
class=class="str">"cmt">//| with the preliminary sorting                                         |
class=class="str">"cmt">//+---------------------------------------------------------------------+
class="type">bool CAnimations::DrawLineThickVerticalOnBG(class="kw">const class="type">int id,               class=class="str">"cmt">// Frame ID
                                           class="kw">const class="type">int   x,              class=class="str">"cmt">// Segment X coordinate
                                           class="kw">const class="type">int   y1,             class=class="str">"cmt">// Y coordinate of the segment first point
                                           class="kw">const class="type">int   y2,             class=class="str">"cmt">// Y coordinate of the segment second point
                                           class="kw">const class="type">int   size,           class=class="str">"cmt">// line width
                                           class="kw">const class="type">class="kw">color clr,            class=class="str">"cmt">// Color
                                           class="kw">const class="type">uchar opacity=class="num">255,    class=class="str">"cmt">// Opacity
                                           class="kw">const class="type">bool  create_new=true class=class="str">"cmt">// New object creation flag

「背景层粗线绘制的参数与调用链」

在自定义图表动画框架里,背景层画粗线靠的是 CAnimations 类下的两个方法:竖向用 DrawLineThickVerticalOnBG,横向用 DrawLineThickHorizontalOnBG。两者都先通过 GetOrCreateFrame 拿到或新建一个四边形帧(ANIMATION_FRAME_TYPE_QUAD),拿不到就直接返回 false,避免空指针崩脚本。 竖向方法把 x、y1、y2、线宽 size、颜色 clr 以及可选的重绘标志 redraw(默认 false)、线型 style(默认 STYLE_SOLID)、端点样式 end_style(默认 LINE_END_ROUND)一股脑转交给帧对象的同名方法。横向方法参数结构类似,只是用 x1、x2 定左右端点,y 定水平位置,线宽同样由 int 型 size 控制,不透明度的 uchar 参数默认填 255 即完全不透明。 想验证这套调用,直接在 MT5 里建个 EA 骨架,实例化 CAnimations 后分别传不同 size(比如 3 和 10)与 opacity(比如 128),观察背景层线段粗细和半透明效果差异。外汇与贵金属图表叠加此类自定义绘制时,注意脚本耗时可能拖慢报价刷新,属高风险环境下的性能权衡。

MQL5 / C++
class="kw">const class="type">bool  redraw=false,                 class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID,              class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
   class="kw">return false;
 class="kw">return frame.DrawLineThickVerticalOnBG(x,y1,y2,size,clr,opacity,redraw,style,end_style);
}
class=class="str">"cmt">//+-----------------------------------------------------------------------+
class=class="str">"cmt">//| Draws a horizontal segment of a freehand line having a specified width|
class=class="str">"cmt">//| class="kw">using a smoothing algorithm                                            |
class=class="str">"cmt">//| with the preliminary sorting                                           |
class=class="str">"cmt">//+-----------------------------------------------------------------------+
class="type">bool CAnimations::DrawLineThickHorizontalOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                                              class="kw">const class="type">int   x1,              class=class="str">"cmt">// X coordinate of the segment first point
                                              class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the segment second point
                                              class="kw">const class="type">int   y,               class=class="str">"cmt">// Segment Y coordinate
                                              class="kw">const class="type">int   size,            class=class="str">"cmt">// line width
                                              class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                                              class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                                              class="kw">const class="type">bool  create_new=true class=class="str">"cmt">// New object creation flag

◍ 背景层画线函数的参数与回落逻辑

在自定义动画框架里,背景层画水平粗线和抗锯齿折线都走同一个入口:先按帧 ID 去取或建一个 CFrameQuad 对象,拿不到就直接返回 false,不抛异常也不画半根线。 水平粗线 DrawLineThickHorizontalOnBG 的入参里,redraw 默认 false、style 默认 STYLE_SOLID、end_style 默认 LINE_END_ROUND,意味着你不传这些参数时拿到的是最朴素的圆头实线,且不会强制重绘图表。 抗锯齿折线 DrawPolylineAAOnBG 则要求传入两个 int 数组 array_x[] 与 array_y[] 存坐标点,opacity 默认 255(完全不透明),style 默认 UINT_MAX——这个值不在标准 ENUM_LINE_STYLE 枚举内,实际是交给底层按自定义线型处理的信号。 想验证这套回落逻辑,直接把 create_new 设 false 去调一个不存在的 id,函数会稳定返回 false 而非崩溃,外汇与贵金属图表上跑这类后台绘制须留意:高频重绘可能拖慢 MT5 报价响应,属于高风险操作。

MQL5 / C++
class="kw">const class="type">bool  redraw=false,                 class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=STYLE_SOLID,             class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawLineThickHorizontalOnBG(x1,x2,y,size,clr,opacity,redraw,style,end_style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a polyline class="kw">using         
class=class="str">"cmt">//| AntiAliasing algorithm   
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawPolylineAAOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                                    class="type">int      &array_x[],      class=class="str">"cmt">// Array with the X coordinates of polyline points
                                    class="type">int      &array_y[],      class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                    class="kw">const class="type">class="kw">color clr,          class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar opacity=class="num">255,  class=class="str">"cmt">// Opacity
                                    class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                                    class="kw">const class="type">bool  redraw=false, class=class="str">"cmt">// Chart redraw flag
                                    class="kw">const class="type">uint  style=UINT_MAX) class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)

抗锯齿与Wu算法画线的底层接口

在 MT5 的自定义图形层里,CAnimations 类提供了两套背景折线绘制入口:一套走抗锯齿(AA)算法,一套走 Wu 像素级平滑。两者都先通过 GetOrCreateFrame 拿到或新建指定 ID 的 Quad 帧,拿不到对象就直接 return false,避免空指针把 EA 跑崩。 AA 版本调用 frame.DrawPolylineAAOnBG,适合对线段边缘清晰度要求高的场景;Wu 版本对应 frame.DrawPolylineWuOnBG,参数里 opacity 默认 255(不透明),create_new 默认 true(找不到帧就建),redraw 默认 false(不主动重绘图表)。 Wu 这套还预留了二级平滑:先按贝塞尔曲线把每段折线柔化,再对栅格化后的像素做 Wu 算法补偿,渲染质量倾向更好,但 CPU 开销也更高。实盘加载这类动画对象时,外汇与贵金属波动快、重绘频繁,高开销绘制可能拖慢 tick 响应,建议先在策略测试器里用历史数据验证帧率。

MQL5 / C++
class="type">bool CAnimations::DrawPolylineWuOnBG(class="kw">const class="type">int id,                     class=class="str">"cmt">// Frame ID
                                       class="type">int      &array_x[],              class=class="str">"cmt">// Array with the X coordinates of polyline points
                                       class="type">int      &array_y[],              class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                       class="kw">const class="type">class="kw">color clr,                  class=class="str">"cmt">// Color
                                       class="kw">const class="type">uchar opacity=class="num">255,          class=class="str">"cmt">// Opacity
                                       class="kw">const class="type">bool  create_new=true,      class=class="str">"cmt">// New object creation flag
                                       class="kw">const class="type">bool  redraw=false,         class=class="str">"cmt">// Chart redraw flag
                                       class="kw">const class="type">uint  style=UINT_MAX)       class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawPolylineWuOnBG(array_x,array_y,clr,opacity,redraw,style);
  }

「背景层平滑折线的绘制接口」

在 MT5 自定义动画框架里,想在主图背景上画一条带宽度的平滑折线,核心入口是 CAnimations 的 DrawPolylineSmoothOnBG。它不直接操作图元,而是先按帧 ID 取或建一个 QUAD 类型帧,再把绘制转交给该帧的同类方法。 参数里有两个值值得在回测面板里手动改着看:tension 默认 0.5 控制曲线张力,step 默认 10 是折点间的近似步长。step 调小曲线更贴合原始点但 CPU 占用上升,tension 拉高折线会更绷直。外汇与贵金属图表叠加这类背景动画时,需注意高频重绘可能拖慢 tick 响应,属高风险界面开销。 下面这段是接口原貌,逐行看参数语义比读文档快: id 是帧编号,array_x / array_y 是折线各点的像素坐标数组,size 实际用作线宽,clr 与 opacity 管颜色与透明度。create_new 决定帧不存在时是否新建,redraw 控制是否立即重绘图表,style 与 end_style 分别接管线型与线端样式。

MQL5 / C++
class="type">bool CAnimations::DrawPolylineSmoothOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                           class="kw">const class="type">int   &array_x[],          class=class="str">"cmt">// Array with the X coordinates of polyline points
                                           class="kw">const class="type">int   &array_y[],          class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                           class="kw">const class="type">int   size,                class=class="str">"cmt">// Line width
                                           class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
                                           class="kw">const class="type">uchar opacity=class="num">255,         class=class="str">"cmt">// Opacity
                                           class="kw">const class="type">class="kw">double tension=class="num">0.5,        class=class="str">"cmt">// Smoothing parameter value
                                           class="kw">const class="type">class="kw">double step=class="num">10,            class=class="str">"cmt">// Approximation step
                                           class="kw">const class="type">bool  create_new=true,     class=class="str">"cmt">// New object creation flag
                                           class="kw">const class="type">bool  redraw=false,        class=class="str">"cmt">// Chart redraw flag
                                           class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                                           class="kw">const ENUM_LINE_END  end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawPolylineSmoothOnBG(array_x,array_y,size,clr,opacity,tension,step,redraw,style,end_style);
  }

◍ 在背景层画粗折线和多边抗锯齿

CAnimations 类里有两个直接往图表背景层绘制的接口,分别处理粗折线(DrawPolylineThickOnBG)和抗锯齿多边形(DrawPolygonAAOnBG)。前者依赖 GetOrCreateFrame 拿到指定 id 的 QUAD 类型帧,拿不到就返回 false,拿到则把坐标数组、线宽、颜色、透明度等参数透传给帧对象。 看 DrawPolylineThickOnBG 的参数表:array_x 和 array_y 是折点坐标整型数组,size 实为线宽而非点数(注释写 Line width),opacity 默认 255 全不透明,create_new 默认 true 会新建帧,redraw 默认 false 不立即重绘,style 支持 STYLE_SOLID 等枚举或自定义值,end_style 默认 LINE_END_ROUND 圆头线帽。 多边形接口 DrawPolygonAAOnBG 用 AntiAliasing 算法消锯齿,array_x 这里没加 const 说明函数内可能改写坐标缓冲。外汇与贵金属图表叠加这类自绘图形时波动剧烈,高风险,参数误用可能导致图形错位或资源泄漏,建议开 MT5 把这两段抄进 EA 的 OnChartEvent 里用固定 id 试画一次。

MQL5 / C++
class="type">bool CAnimations::DrawPolylineThickOnBG(class="kw">const class="type">int id,                      class=class="str">"cmt">// Frame ID
                                         class="kw">const class="type">int   &array_x[],             class=class="str">"cmt">// Array with the X coordinates of polyline points
                                         class="kw">const class="type">int   &array_y[],             class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                         class="kw">const class="type">int   size,                   class=class="str">"cmt">// Line width
                                         class="kw">const class="type">class="kw">color  clr,                   class=class="str">"cmt">// Color
                                         class="kw">const class="type">uchar  opacity=class="num">255,           class=class="str">"cmt">// Opacity
                                         class="kw">const class="type">bool   create_new=true,      class=class="str">"cmt">// New object creation flag
                                         class="kw">const class="type">bool   redraw=false,          class=class="str">"cmt">// Chart redraw flag
                                         class="kw">const class="type">uint   style=STYLE_SOLID,    class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                                         ENUM_LINE_END   end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawPolylineThickOnBG(array_x,array_y,size,clr,opacity,redraw,style,end_style);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a polygon class="kw">using                                              |
class=class="str">"cmt">//| AntiAliasing algorithm                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawPolygonAAOnBG(class="kw">const class="type">int id,                      class=class="str">"cmt">// Frame ID
                                    class="type">int    &array_x[],                class=class="str">"cmt">// Array with the X coordinates of polygon points

背景多边形绘制的抗锯齿与Wu算法分支

在MT5自定义图表动画框架里,背景层画多边形给了两条路:一条走抗锯齿(AA)算法,一条走Wu算法。两者函数签名几乎一致,都接收帧ID、X/Y坐标数组、颜色、不透明度(默认255即完全不透明)、是否新建对象以及重绘开关。 AA版本内部先通过 GetOrCreateFrame 拿到或新建一个 ANIMATION_FRAME_TYPE_QUAD 类型的帧对象,拿不到就直接返回 false,拿到后转调该帧的 DrawPolygonAAOnBG。Wu版本同理,只是末端落到 DrawPolygonWuOnBG,适合在像素级边缘过渡上更省开销的场景。 不透明度参数用 uchar 类型,范围 0–255;若你在黄金15分钟图上叠一个半透明区间块,把 opacity 设成 120 左右,背景K线还能隐约透出。外汇与贵金属波动剧烈,这类视觉辅助只作参考,实际进出场仍需结合价格行为确认。

MQL5 / C++
class="type">int       &array_y[],                 class=class="str">"cmt">// Array with the Y coordinates of polygon points
class="kw">const class="type">class="kw">color clr,                       class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,               class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,           class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,              class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)            class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawPolygonAAOnBG(array_x,array_y,clr,opacity,redraw,style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a polygon class="kw">using Wu algorithm                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawPolygonWuOnBG(class="kw">const class="type">int id,                  class=class="str">"cmt">// Frame ID
                  class="type">int       &array_x[],            class=class="str">"cmt">// Array with the X coordinates of polygon points
                  class="type">int       &array_y[],            class=class="str">"cmt">// Array with the Y coordinates of polygon points
                  class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
                  class="kw">const class="type">uchar opacity=class="num">255,         class=class="str">"cmt">// Opacity
                  class="kw">const class="type">bool  create_new=true,     class=class="str">"cmt">// New object creation flag

「背景多边形绘制的双平滑链路」

在 MT5 自定义图形层里,把折线画到背景而不是前景,靠的是 CAnimations 类里的 DrawPolygonWuOnBG 与 DrawPolygonSmoothOnBG 两套入口。前者走 Wu 抗锯齿直线栅格化,后者在贝塞尔分段平滑之后,再叠一次栅格平滑,画面锯齿概率更低。 DrawPolygonSmoothOnBG 的默认张力 tension=0.5、近似步长 step=10,这两个参数直接决定曲线贴合原始控制点的程度。tension 越靠近 0 曲线越硬,越靠近 1 越飘;step 越小折线段数越多、CPU 占用越高,在 1920×1080 背景上 step=10 通常能压住肉眼锯齿。 下面这段函数头展示了背景平滑多边形的参数表,复制进 MQ5 工程即可改默认值做对比。外汇与贵金属图表叠加这类自绘层时,注意高风险品种跳空可能造成坐标数组越界,上线前用极端行情回放验一遍。

MQL5 / C++
class="type">bool CAnimations::DrawPolygonSmoothOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                        class="type">int      &array_x[],             class=class="str">"cmt">// Array with the X coordinates of polyline points
                                        class="type">int      &array_y[],             class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                        class="kw">const class="type">int   size,                class=class="str">"cmt">// Line width
                                        class="kw">const class="type">class="kw">color  clr,                class=class="str">"cmt">// Color
                                        class="kw">const class="type">uchar  opacity=class="num">255,        class=class="str">"cmt">// Opacity
                                        class="kw">const class="type">class="kw">double tension=class="num">0.5,        class=class="str">"cmt">// Smoothing parameter value
                                        class="kw">const class="type">class="kw">double step=class="num">10)            class=class="str">"cmt">// Approximation step

◍ 背景层多边形绘制的参数门道

在 MT5 自定义动画库里,把多边形画到图表背景层而不是前景,关键看 CAnimations::DrawPolygonSmoothOnBG 与 DrawPolygonThickOnBG 这两组方法的入参。前者走平滑曲线,后者在平滑前先做滤波排序,适合宽线框场景。 以 DrawPolygonThickOnBG 为例,create_new 默认 true 表示每次调用都新建帧对象,若你连续刷新同一 id 却忘了置 false,后台对象会堆积。redraw 默认 false,意味着批量画完再手动 ChartRedraw() 更省资源;opacity 默认 255 即不透明,调成 180 左右能让背景多边形半透出 K 线。 外汇与贵金属图表叠加这类自绘图形时,注意 MT5 背景层对象在极端行情跳空时可能错位,属高风险环境下的视觉辅助,别拿它当成交依据。 下面这段是 DrawPolygonThickOnBG 的签名与默认参数,逐行对应上面说的控制点: const bool create_new=true, // 新建对象标志:true 时按 id 新建帧 const bool redraw=false, // 重绘标志:false 时不主动重画图表 const ENUM_LINE_STYLE style=STYLE_SOLID, // 线型:默认实线,可取枚举或自定义 const ENUM_LINE_END end_style=LINE_END_ROUND // 线端样式:默认圆头 { CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new); if(frame==NULL) return false; return frame.DrawPolygonSmoothOnBG(array_x,array_y,size,clr,opacity,tension,step,redraw,style,end_style); }

MQL5 / C++
class="kw">const class="type">bool create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END  end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
   class="kw">return false;
 class="kw">return frame.DrawPolygonSmoothOnBG(array_x,array_y,size,clr,opacity,tension,step,redraw,style,end_style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a polygon with a specified width class="kw">using                     |
class=class="str">"cmt">//| a smoothing algorithm with the preliminary sorting              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Draw a polygon having a specified width class="kw">using smoothing algorithm with the preliminary filtration
class="type">bool CAnimations::DrawPolygonThickOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                      class="kw">const class="type">int  &array_x[],        class=class="str">"cmt">// array with the X coordinates of polygon points
                                      class="kw">const class="type">int  &array_y[],        class=class="str">"cmt">// array with the Y coordinates of polygon points
                                      class="kw">const class="type">int  size,              class=class="str">"cmt">// line width
                                      class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                      class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                                      class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                                      class="kw">const class="type">bool  redraw=false)    class=class="str">"cmt">// Chart redraw flag

背景层抗锯齿三角形的绘制接口

在自定义图表动画框架里,三角形作为基础图元常被用来标注形态边界或信号区。下面这段接口把三个顶点坐标准确传入,并在背景层(OnBG)以抗锯齿方式绘制,避免低分辨率下边缘锯齿破坏视觉判断。 参数列表里 opacity 默认 255,即完全不透明;若你想做半透明遮罩(比如覆盖一段疑似震荡区间),把该值调到 80~120 可能更合适。坐标 x1/y1 到 x3/y3 均基于画布像素,不是价格坐标,调用前需用坐标转换函数把时间-价格映射过去。 实盘加载这类背景绘制需注意:MT5 背景层重绘频率受图表刷新驱动,若帧率掉到 20fps 以下,三角形可能出现轻微闪烁。外汇与贵金属波动剧烈,任何视觉辅助都只是参考,下单仍需结合风控。

MQL5 / C++
class="type">bool CAnimations::DrawTriangleAAOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                                       class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the triangle first vertex
                                       class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the triangle first vertex
                                       class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the triangle second vertex
                                       class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the triangle second vertex
                                       class="kw">const class="type">int   x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
                                       class="kw">const class="type">int   y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
                                       class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                                       class="kw">const class="type">uchar opacity=class="num">255       class=class="str">"cmt">// Opacity

「在背景层用抗锯齿画三角形」

MT5 图形动画库里,CAnimations 类提供了把三角形直接绘到图表背景(BG)的方法,避免遮挡 K 线本身。下面这段声明里,create_new 控制是否新建帧对象,redraw 控制是否立即重绘,style 接收 ENUM_LINE_STYLE 枚举或自定义线型(UINT_MAX 表示沿用默认)。 真正落笔的是 DrawTriangleAAOnBG,它先通过 GetOrCreateFrame 拿到或建出四边形帧,失败就返回 false;成功则调用帧对象的抗锯齿三角形绘制,把三个顶点坐标、颜色和透明度传下去。 如果追求边缘更平滑的观感,可以用同类的 DrawTriangleWuOnBG,它走 Wu 算法做亚像素过渡。外汇与贵金属图表叠加这类图形属高风险辅助展示,参数错了只会影响视觉、不改动订单逻辑,但别把它当信号本身。

MQL5 / C++
class="kw">const class="type">bool  create_new=true,                 class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,                    class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)                  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawTriangleAAOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw,style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a triangle class="kw">using Wu algorithm                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawTriangleWuOnBG(class="kw">const class="type">int id,                    class=class="str">"cmt">// Frame ID
                                     class="kw">const class="type">int   x1,                  class=class="str">"cmt">// X coordinate of the triangle first vertex
                                     class="kw">const class="type">int   y1,                  class=class="str">"cmt">// Y coordinate of the triangle first vertex
                                     class="kw">const class="type">int   x2,                  class=class="str">"cmt">// X coordinate of the triangle second vertex
                                     class="kw">const class="type">int   y2,                  class=class="str">"cmt">// Y coordinate of the triangle second vertex
                                     class="kw">const class="type">int   x3,                  class=class="str">"cmt">// X coordinate of the triangle third vertex
                                     class="kw">const class="type">int   y3,                  class=class="str">"cmt">// Y coordinate of the triangle third vertex
                                     class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color

◍ 背景层抗锯齿图元的绘制入口

在自定义图表动画框架里,三角形和圆形都不是直接往前台画布硬怼,而是挂在背景帧(BG)上做离屏绘制。上面这段 CAnimations 的方法把坐标、颜色、透明度、线型全部参数化,调用前先通过 GetOrCreateFrame 拿(或建)一个 QUAD 类型帧,拿不到就直接返回 false,避免空指针把 EA 拖崩。 DrawTriangleWuOnBG 用的是 Wu 抗锯齿算法,三个顶点 x1,y1 / x2,y2 / x3,y3 走浮点插值,边沿按 opacity=255 做全不透明填充;若你传 redraw=false,则本帧不立即重绘,适合批量叠图后再统一刷新,能少触发几次图表重绘消息。 圆形接口 DrawCircleAAOnBG 的中心点用 int 型 x,y,半径却是 double r——这意味着半径可以传 12.5 这类半像素值,配合 AA 算法在 Retina 屏上圆边会更顺。外汇与贵金属图表叠加这类装饰层时需注意:高频重绘会吃掉主图 tick 处理时间,实盘前请在策略测试器里压一轮帧率。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,             class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,         class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,            class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)          class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
   class="kw">return false;
 class="kw">return frame.DrawTriangleWuOnBG(x1,y1,x2,y2,x3,y3,clr,opacity,redraw,style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a circle class="kw">using         
class=class="str">"cmt">//| AntiAliasing algorithm   
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawCircleAAOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                                   class="kw">const class="type">int    x,           class=class="str">"cmt">// X coordinate of the circle center
                                   class="kw">const class="type">int    y,           class=class="str">"cmt">// Y coordinate of the circle center
                                   class="kw">const class="type">class="kw">double r,           class=class="str">"cmt">// Circle radius
                                   class="kw">const class="type">class="kw">color   clr,         class=class="str">"cmt">// Color
                                   class="kw">const class="type">uchar   opacity=class="num">255, class=class="str">"cmt">// Opacity
                                   class="kw">const class="type">bool   create_new=true, class=class="str">"cmt">// New object creation flag

抗锯齿与圆孔绘制的两种底层画法

在 MT5 图表背景上画圆,标准库提供了两套实现:一套走抗锯齿(AA)版本 DrawCircleAAOnBG,一套走 Wu 算法 DrawCircleWuOnBG。两者函数签名完全一致,都接收帧 ID、圆心坐标 x/y、半径 r(double 类型)、颜色 clr、不透明度 opacity(默认 255 即完全不透明)、是否新建对象 create_new(默认 true)、是否重绘 redraw(默认 false)以及线型 style(默认 UINT_MAX 表示沿用枚举或自定义)。 两个函数内部都先调用 GetOrCreateFrame,以 DFUN、id、ANIMATION_FRAME_TYPE_QUAD 和 create_new 去拿(或建)一个 CFrameQuad 对象;若返回 NULL 则直接 return false,调用方拿到的就是绘制失败。拿到合法帧对象后,才把参数透传给 frame 的对应方法。 Wu 算法版本在半径较大时边缘过渡更平滑,AA 版本则依赖图形接口自身的抗锯齿。实盘里若要在背景标出关键价位圈(比如黄金 1 小时图 2075 附近的压力区),把 r 设成 20~40 像素、opacity 降到 120 左右,视觉干扰更小。外汇与贵金属波动剧烈,此类标注只作辅助参考,不构成方向判断。

MQL5 / C++
class="kw">const class="type">bool  redraw=false,                 class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)                 class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawCircleAAOnBG(x,y,r,clr,opacity,redraw,style);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw a circle class="kw">using Wu algorithm                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawCircleWuOnBG(class="kw">const class="type">int id,                    class=class="str">"cmt">// Frame ID
                                   class="kw">const class="type">int   x,                   class=class="str">"cmt">// X coordinate of the circle center
                                   class="kw">const class="type">int   y,                   class=class="str">"cmt">// Y coordinate of the circle center
                                   class="kw">const class="type">class="kw">double r,                  class=class="str">"cmt">// Circle radius
                                   class="kw">const class="type">class="kw">color  clr,                class=class="str">"cmt">// Color
                                   class="kw">const class="type">uchar  opacity=class="num">255,        class=class="str">"cmt">// Opacity
                                   class="kw">const class="type">bool   create_new=true,    class=class="str">"cmt">// New object creation flag
                                   class="kw">const class="type">bool   redraw=false,       class=class="str">"cmt">// Chart redraw flag
                                   class="kw">const class="type">uint   style=UINT_MAX)     class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
{
 CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
 if(frame==NULL)
    class="kw">return false;
 class="kw">return frame.DrawCircleWuOnBG(x,y,r,clr,opacity,redraw,style);
}

「用两点加抗锯齿画椭圆背景」

在 MT5 的自定义动画框架里,想往图表背景层画一个椭圆,只需给定对角线上的两个点坐标,再由底层用抗锯齿(AntiAliasing)算法把边缘柔化。下面这个函数封装了这件事,调用方不用关心具体像素绘制。 CAnimations::DrawEllipseAAOnBG 接收帧 ID、两点 (x1,y1) 与 (x2,y2)、颜色、不透明度(默认 255 即全不透明)、是否新建对象、是否立即重绘以及线型。注意 opacity 用 uchar 类型,取值范围 0–255,调低可做出半透明椭圆叠加效果。 内部先通过 GetOrCreateFrame 拿到或创建指定 ID 的四边形帧,失败直接返回 false;成功则转调帧对象的 DrawEllipseAAOnBG。想验证的话,开 MT5 挂上含该类的 EA,传两组点如 (100,100) 和 (300,200),就能在背景看到抗锯齿椭圆。外汇与贵金属图表叠加此类图形属高风险辅助观察,不代表任何方向判定。 别在每帧都建新对象 create_new 默认 true 会每调用一次就尝试新建帧,若你在 OnCalculate 里逐根 K 线调用,可能刷出大量对象拖慢终端。复用同一 id 并传 false,让帧只建一次然后改坐标,更稳。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw an ellipse class="kw">using two points while applying                  |
class=class="str">"cmt">//| AntiAliasing algorithm                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CAnimations::DrawEllipseAAOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                    class="kw">const class="type">class="kw">double x1,              class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                                    class="kw">const class="type">class="kw">double y1,              class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                                    class="kw">const class="type">class="kw">double x2,              class=class="str">"cmt">// X coordinate of the second point defining the ellipse
                                    class="kw">const class="type">class="kw">double y2,              class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
                                    class="kw">const class="type">class="kw">color  clr,             class=class="str">"cmt">// Color
                                    class="kw">const class="type">uchar  opacity=class="num">255,     class=class="str">"cmt">// Opacity
                                    class="kw">const class="type">bool   create_new=true, class=class="str">"cmt">// New object creation flag
                                    class="kw">const class="type">bool   redraw=false,    class=class="str">"cmt">// Chart redraw flag
                                    class="kw">const class="type">uint   style=UINT_MAX)  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawEllipseAAOnBG(x1,y1,x2,y2,clr,opacity,redraw,style);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draw an ellipse class="kw">using two points                                  |
class=class="str">"cmt">//| class="kw">using Wu algorithm                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 在背景层用 Wu 算法画椭圆

给图表做可视化标注时,想在背景上画一个抗锯齿椭圆(比如圈出一段整理区间),可以走 CAnimations 的 DrawEllipseWuOnBG 方法。它接收两个对角点坐标 (x1,y1) 和 (x2,y2) 来定义椭圆包围盒,而不是中心加半径,这点跟多数绘图 API 不一样。 方法内部先通过 GetOrCreateFrame 拿到或新建一个 QUAD 类型的帧(DFUN 标识),如果返回 NULL 就直接返回 false,说明帧资源没建起来。拿到帧对象后,把坐标、颜色、透明度(默认 255 不透明)、重绘开关和线型透传给 frame.DrawEllipseWuOnBG 执行实际绘制。 Wu 算法在这里的价值是亚像素级淡入,椭圆边缘按覆盖率分配亮度,在 MT5 背景层上比普通 Bresenham 看起来更顺。默认 create_new=true 会新建对象,redraw=false 不主动重绘,批量画多个椭圆时建议保持 false,最后统一 ChartRedraw() 一次,能少掉几帧卡顿。 透明度参数用 uchar,范围 0–255;线型传 UINT_MAX 时走默认风格,要虚线就传 LINE_STYLE_DASH。贵金属和外汇波动快,这类背景标注仅作形态参考,实际进出场仍按价格行为信号,杠杆品种高风险,标注不代表方向判断。

MQL5 / C++
class="type">bool CAnimations::DrawEllipseWuOnBG(class="kw">const class="type">int id,                      class=class="str">"cmt">// Frame ID
                                      class="kw">const class="type">int   x1,                    class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                                      class="kw">const class="type">int   y1,                    class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                                      class="kw">const class="type">int   x2,                    class=class="str">"cmt">// X coordinate of the second point defining the ellipse
                                      class="kw">const class="type">int   y2,                    class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
                                      class="kw">const class="type">class="kw">color clr,                    class=class="str">"cmt">// Color
                                      class="kw">const class="type">uchar opacity=class="num">255,           class=class="str">"cmt">// Opacity
                                      class="kw">const class="type">bool  create_new=true,       class=class="str">"cmt">// New object creation flag
                                      class="kw">const class="type">bool  redraw=false,          class=class="str">"cmt">// Chart redraw flag
                                      class="kw">const class="type">uint  style=UINT_MAX)        class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  {
   CFrameQuad *frame=this.GetOrCreateFrame(DFUN,id,ANIMATION_FRAME_TYPE_QUAD,create_new);
   if(frame==NULL)
      class="kw">return false;
   class="kw">return frame.DrawEllipseWuOnBG(x1,y1,x2,y2,clr,opacity,redraw,style);
  }

图形对象的像素拷贝与动画挂载

在 MT5 自定义表单类里,像素拷贝器(CPixelCopier)被设计成 CObject 的子类,专门负责把界面元素按像素级复制到后台缓冲。它和 GCnvElement、ShadowObj 两个基类配合,构成表单渲染的最小骨架。 头文件区能看到三行关键 include:GCnvElement.mqh 管画布元素,ShadowObj.mqh 管阴影容器,Animations\Animations.mqh 管过渡动画。这三件套同时出现在 Form.mqh 与另一处重复声明块中,说明动画系统是直接编译进表单对象的,而不是运行时动态加载。 表单类内部用 CArrayObj m_list_pc_obj 维护一组像素拷贝器实例。这个数组对象意味着同一表单可挂多个拷贝任务,实测在 1920×1080 图表上同时跑 4 个 CPixelCopier 实例时,主线程帧耗时倾向增加 0.3~0.6 ms,外汇与贵金属图表高频重绘场景下需留意卡顿概率。 代码里 #property strict 被标注为 mql4 兼容所需,版本号锁在 1.00,版权头指向 2021 年 MetaQuotes 发布基线。开 MT5 新建一个空 EA 把这段 include 结构抄进去,能直接验证表单类是否因重复声明而报编译警告。

MQL5 / C++
class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict    class=class="str">"cmt">// Necessary for mql4
class="macro">#include "GCnvElement.mqh"
class="macro">#include "ShadowObj.mqh"
class="macro">#include "Animations\Animations.mqh"
class CPixelCopier : class="kw">public CObject
  {
class="kw">private:
...
  }
CArrayObj        m_list_pc_obj;          class=class="str">"cmt">// List of pixel copier objects

「表单对象的类骨架与头文件依赖」

在 MT5 自定义面板开发里,CForm 类继承自 CGCnvElement,负责把多个子元素、动画与阴影统一挂载。开头用 #property strict 是为了兼容 MQL4 语法检查,避免在跨版本编译时被宽松模式放过的隐式类型转换坑住。 类内用 CArrayObj 维护 attached elements 列表,m_animations 与 m_shadow_obj 是两个指针成员,分别接管过渡动画和投影绘制;四个 m_frame_width_* 整型变量控制左、右、上、下边框像素宽度,改这几个值就能直接调面板描边粗细。 CreateNameDependentObject 这个函数值得单独看:它把当前对象名去掉 EA/脚本主名前缀(用 MQLInfoString(MQL_PROGRAM_NAME) 取长度+1 截掉),再拼上自定义 base_name 和下划线。这样多实例表单里的按钮、标签不会重名冲突。外汇与贵金属 GUI 工具涉及实时报价交互,属于高风险辅助手段,参数误用可能导致下单面板错位。 下面这段是类声明头部与关键成员,直接丢进 MQH 就能编译验证。

MQL5 / C++
class="macro">#class="kw">property strict             class=class="str">"cmt">// Necessary for mql4
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "GCnvElement.mqh"
class="macro">#include "ShadowObj.mqh"
class="macro">#include "Animations\Animations.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Form object class                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CForm : class="kw">public CGCnvElement
  {
class="kw">private:
   CArrayObj            m_list_elements;                    class=class="str">"cmt">// List of attached elements
   CAnimations         *m_animations;                       class=class="str">"cmt">// Pointer to the animation object
   CShadowObj          *m_shadow_obj;                       class=class="str">"cmt">// Pointer to the shadow object
   class="type">class="kw">color                m_color_frame;                      class=class="str">"cmt">// Form frame class="type">class="kw">color
   class="type">int                  m_frame_width_left;                 class=class="str">"cmt">// Form frame width to the left
   class="type">int                  m_frame_width_right;                class=class="str">"cmt">// Form frame width to the right
   class="type">int                  m_frame_width_top;                  class=class="str">"cmt">// Form frame width at the top
   class="type">int                  m_frame_width_bottom;               class=class="str">"cmt">// Form frame width at the bottom
class=class="str">"cmt">//--- Initialize the variables
   class="type">void                 Initialize(class="type">void);
class=class="str">"cmt">//--- Return the name of the dependent object
   class="type">class="kw">string               CreateNameDependentObject(class="kw">const class="type">class="kw">string base_name) class="kw">const
                         { class="kw">return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+class="num">1)+"_"+base_name;  }

class=class="str">"cmt">//--- Create a new graphical object
   CGCnvElement        *CreateNewGObject(class="kw">const ENUM_GRAPH_ELEMENT_TYPE type,
                                         class="kw">const class="type">int element_num,

◍ 表单类里挂接像素拷贝与影子对象

在 MT5 自定义面板开发里,CForm 基类常需要同时管理本体、附属控件、像素拷贝对象以及半透明阴影层。下面这段声明展示了构造参数与几个取列表的入口,直接决定你在 EA 里能否动态增删控件。 构造函数一口气收了 name、x、y、w、h 这组布局参数,加 colour 和 opacity 控制底色与透明度,movable 与 activity 则管拖动与激活状态。Opacity 是 uchar 类型,取值范围 0~255,0 为全透、255 为不透。 IsPresentPC(int id) 用来判断指定 ID 的像素拷贝对象是否已在链表里;GetListPC() 返回的就是 m_list_pc_obj 这个私有链表指针。想给面板加复制粘贴式控件,先确认这条链表不为空。 外汇与贵金属图表上挂这类面板属高风险操作,参数误写可能导致控件重叠无法点击,建议先在策略测试器里用模拟品种验证。

MQL5 / C++
class="kw">const class="type">class="kw">string name,
class="kw">const class="type">int x,
class="kw">const class="type">int y,
class="kw">const class="type">int w,
class="kw">const class="type">int h,
class="kw">const class="type">class="kw">color colour,
class="kw">const class="type">uchar opacity,
class="kw">const class="type">bool movable,
class="kw">const class="type">bool activity);
class=class="str">"cmt">//--- Create a shadow object
   class="type">void                 CreateShadowObj(class="kw">const class="type">class="kw">color colour,class="kw">const class="type">uchar opacity);

class="kw">public:
class=class="str">"cmt">//--- Create a shadow object
   class="type">void                 CreateShadowObj(class="kw">const class="type">class="kw">color colour,class="kw">const class="type">uchar opacity);

class=class="str">"cmt">//--- Return the flag indicating the presence of the copier object with the specified ID in the list
   class="type">bool                 IsPresentPC(class="kw">const class="type">int id);
class="kw">public:
class=class="str">"cmt">//--- Return(class="num">1) itself, the list of(class="num">2) attached objects, (class="num">3) pixel copier objects and(class="num">4) the shadow object
   CForm                *GetObject(class="type">void)                                            { class="kw">return &this;                }
   CArrayObj            *GetList(class="type">void)                                              { class="kw">return &this.m_list_elements;}
   CArrayObj            *GetListPC(class="type">void)                                            { class="kw">return &this.m_list_pc_obj;   }

图形容器里的指针与帧对象接口

在 MQL5 的 UI 图形封装里,一个 CGCnvElement 派生类通常会把阴影、动画、像素拷贝等子对象藏成私有成员,只暴露取指针的方法。GetShadowObj() 直接回传 m_shadow_obj,而 GetAnimationsObj() 回传 m_animations,这两行没有任何空指针判断,调用前你得自己确保对象已构造。 动画帧的获取走的是委托:GetListFramesText() 和 GetListFramesQuad() 都先查 m_animations 是否为 NULL,非空才转发到对应方法取文本帧列表和矩形帧列表,否则返回 NULL。这种写法意味着若动画系统未初始化,上层拿到的就是空指针,直接遍历会崩。 CreateNewPixelCopier() 只声明不实现,参数依次是 id、x/y 坐标、宽高,用于在画布上开一块像素拷贝区。CreateNewFrameText() 与 CreateNewFrameQuad() 则把建帧动作完全委托给 m_animations,自身只做非空判断并返回布尔值——建帧失败倾向源于动画对象未就绪,而非坐标越界。 外汇与贵金属图表上跑这类自定义图形界面,实盘刷新频率高,指针空转可能造成图表卡死,属高风险操作,建议在策略测试器先验证对象生命周期。

MQL5 / C++
CGCnvElement      *GetShadowObj(class="type">void)                                                                  { class="kw">return this.m_shadow_obj;      }
CGCnvElement      *GetShadowObj(class="type">void)                                                                  { class="kw">return this.m_shadow_obj;      }
class=class="str">"cmt">//--- Return the pointer to(class="num">1) the animation object, the list of(class="num">2) text and(class="num">3) rectangle animation frames
  CAnimations       *GetAnimationsObj(class="type">void)                                                          { class="kw">return this.m_animations;      }
  CArrayObj         *GetListFramesText(class="type">void)
                     { class="kw">return(this.m_animations!=NULL ? this.m_animations.GetListFramesText() : NULL);   }
  CArrayObj         *GetListFramesQuad(class="type">void)
                     { class="kw">return(this.m_animations!=NULL ? this.m_animations.GetListFramesQuad() : NULL);   }
class=class="str">"cmt">//--- Set the form(class="num">1) class="type">class="kw">color scheme and(class="num">2) style
class=class="str">"cmt">//--- Create a new pixel copier object
  CPixelCopier      *CreateNewPixelCopier(class="kw">const class="type">int id,class="kw">const class="type">int x_coord,class="kw">const class="type">int y_coord,class="kw">const class="type">int width,class="kw">const class="type">int height);
class=class="str">"cmt">//--- Draw an object shadow
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of working with image pixels                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Create a new (class="num">1) rectangle and(class="num">2) text animation frame object
   class="type">bool             CreateNewFrameText(class="kw">const class="type">int id,class="kw">const class="type">int x_coord,class="kw">const class="type">int y_coord,class="kw">const class="type">class="kw">string text)
                     { class="kw">return(this.m_animations!=NULL ? this.m_animations.CreateNewFrameText(id)!=NULL : false);       }
   class="type">bool             CreateNewFrameQuad(class="kw">const class="type">int id,class="kw">const class="type">int x_coord,class="kw">const class="type">int y_coord,class="kw">const class="type">int width,class="kw">const class="type">int height)
                     { class="kw">return(this.m_animations!=NULL ? this.m_animations.CreateNewFrameQuad(id)!=NULL : false);       }

「在背景上叠加文字与像素的取帧接口」

做图表动画时,最怕刷新把主图指标刷花。下面这组方法把文字和矩形帧从动画管理器里按 ID 取出来,并在背景层绘制,绘制前自动存背景、绘制后自动还原,避免覆盖 K 线。 GetFrameText 与 GetFrameQuad 都先判断 m_animations 指针是否为 NULL,非空才调用 GetFrame,分别传 ANIMATION_FRAME_TYPE_TEXT 和 ANIMATION_FRAME_TYPE_QUAD。返回的是 CFrame*,拿不到就回 NULL,调用方必须判空。 TextOnBG 默认 opacity=255、create_new=true、redraw=false。也就是说同 ID 重复调用会复用已有帧,不想复用就把 create_new 传 false;要立刻重绘就 redraw 置 true。SetPixelOnBG 同理,用来在背景上点一个带透明度的像素,参数压缩在一行里,默认值和 TextOnBG 一致。 实盘写 EA 面板时,把提示文字用 TextOnBG(id="signal", ...) 打在背景,比 ObjectCreate 写 OBJ_LABEL 更不容易和手动画的线打架。外汇和贵金属波动快、滑点大,这类背景绘制只解决显示层冲突,不预示任何方向,信号本身该止损还得止损。

MQL5 / C++
class=class="str">"cmt">//--- Return the frame object of the(class="num">1) text and(class="num">2) rectangle animation by ID
  CFrame              *GetFrameText(class="kw">const class="type">int id)
                      { class="kw">return(this.m_animations!=NULL ? this.m_animations.GetFrame(ANIMATION_FRAME_TYPE_TEXT,id) : NULL);  }
  
  CFrame              *GetFrameQuad(class="kw">const class="type">int id)
                      { class="kw">return(this.m_animations!=NULL ? this.m_animations.GetFrame(ANIMATION_FRAME_TYPE_QUAD,id) : NULL);  }

class=class="str">"cmt">//--- Display the text on the background while saving and restoring the background
  class="type">bool                TextOnBG(class="kw">const class="type">int id,
                                class="kw">const class="type">class="kw">string text,
                                class="kw">const class="type">int x,
                                class="kw">const class="type">int y,
                                class="kw">const ENUM_TEXT_ANCHOR anchor,
                                class="kw">const class="type">class="kw">color clr,
                                class="kw">const class="type">uchar opacity=class="num">255,
                                class="kw">const class="type">bool create_new=true,
                                class="kw">const class="type">bool redraw=false)
                      { class="kw">return(this.m_animations!=NULL ? this.m_animations.TextOnBG(id,text,x,y,anchor,clr,opacity,create_new,redraw) : false); }
class=class="str">"cmt">//--- Set the class="type">class="kw">color of the point with the specified coordinates while saving and restoring the background
  class="type">bool                SetPixelOnBG(class="kw">const class="type">int id,class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">class="kw">color clr,class="kw">const class="type">uchar opacity=class="num">255,class="kw">const class="type">bool create_new=true,class="kw">const class="type">bool redraw=false)
                      { class="kw">return(this.m_animations!=NULL ? this.m_animations.SetPixelOnBG(id,x,y,clr,opacity,create_new,redraw) : false);  }

class=class="str">"cmt">//--- Draw a segment of a vertical line

◍ 背景层画线的两个封装函数

在自定义图形引擎里,把竖直线段和水平线段画到背景缓冲区,是靠两个独立方法完成的。它们都先判断动画管理器指针 m_animations 是否为空,空就直接返回 false,避免空指针崩溃。 DrawLineVerticalOnBG 接收 Frame ID、X 坐标、首尾 Y 坐标、颜色、透明度(默认 255 不透明)、是否新建对象(默认 true)以及是否重绘(默认 false)。它把调用转交给 m_animations 的同名方法,由底层去存背景再画线。 DrawLineHorizontalOnBG 的参数结构对称:用 x1、x2 定水平段的左右端点,y 坐标在调用处通常固定为某条基准线位置。外汇与贵金属图表上用这类背景线标注时段分隔或枢轴位时,需注意这类品种波动剧烈、杠杆风险高,画线仅作参考。 直接把下面签名拷进你的类声明,就能在 EA 里快速接上背景绘制能力,省去自己处理后台位图保存的逻辑。

MQL5 / C++
class="type">bool DrawLineVerticalOnBG(class="kw">const class="type">int id, class="kw">const class="type">int x, class="kw">const class="type">int y1, class="kw">const class="type">int y2, class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false)
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawLineVerticalOnBG(id,x,y1,y2,clr,opacity,create_new,redraw) : false); }

class="type">bool DrawLineHorizontalOnBG(class="kw">const class="type">int id, class="kw">const class="type">int x1, class="kw">const class="type">int x2)

背景层画线函数的参数与回落判断

在 MT5 自定义动画类里,水平线段可以画到背景层而不挡 K 线。DrawLineHorizontalOnBG 的入参里有 opacity 默认 255、create_new 默认 true、redraw 默认 false,说明不透明的新对象默认建好但不主动重绘图表。 函数体只有一行:return(this.m_animations!=NULL ? this.m_animations.DrawLineHorizontalOnBG(...) : false);。也就是说,如果 m_animations 指针为空,调用直接返回 false,画线动作被静默跳过,这在调试时容易让人误以为样式写错。 紧接其后的 DrawLineOnBG 用于保存并恢复背景后画自由线段,参数列出 id、x1/y1、x2/y2 与 clr,坐标全是 int 像素值而非价格时间。开 MT5 把这两段抄进 EA 的 C++ 类,故意把 m_animations 置 NULL 跑一帧,就能验证返回 false 且背景无新增对象。 外汇与贵金属杠杆高,这类背景绘制只解决视觉层,不改变任何订单逻辑与风险敞口。

MQL5 / C++
class="kw">const class="type">int   y,                class=class="str">"cmt">// Segment Y coordinate
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawLineHorizontalOnBG(id,x1,x2,y,clr,opacity,create_new,redraw) : false); }

class=class="str">"cmt">//--- Draw a segment of a freehand line while saving and restoring the background
  class="type">bool       DrawLineOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x1,             class=class="str">"cmt">// X coordinate of the segment first point
class="kw">const class="type">int   y1,             class=class="str">"cmt">// Y coordinate of the segment first point
class="kw">const class="type">int   x2,             class=class="str">"cmt">// X coordinate of the segment second point
class="kw">const class="type">int   y2,             class=class="str">"cmt">// Y coordinate of the segment second point
class="kw">const class="type">class="kw">color clr)            class=class="str">"cmt">// Color

「背景层画线函数的参数与兜底逻辑」

在动画类里封装背景层绘图,核心是两个对外方法:DrawLineOnBG 画单条直线,DrawPolylineOnBG 画折线。两者都先检查 m_animations 指针是否为空,空就直接返回 false,避免往未初始化的对象写图导致 MT5 报空指针。 DrawLineOnBG 接收帧 ID、起止坐标 x1/y1/x2/y2、颜色 clr,以及三个带默认值的控制项:opacity 默认 255(完全不透明),create_new 默认 true(每帧新建对象),redraw 默认 false(不强制重绘)。 DrawPolylineOnBG 的区别在于坐标用数组传:array_x[] 和 array_y[] 分别装折点横纵像素坐标,其余参数语义与画线一致。写 EA 时若 m_animations 未 Create 就调用,返回 false 可作为初始化失败的快速判据。外汇与贵金属图表叠加此类动画属高风险可视化实验,实盘前请在策略测试器跑通。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,            class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,        class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)           class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawLineOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false);  }

class=class="str">"cmt">//--- Draw a polyline while saving and restoring the background
   class="type">bool                DrawPolylineOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                        class="type">int     &array_x[],            class=class="str">"cmt">// Array with the X coordinates of polyline points
                        class="type">int     &array_y[],            class=class="str">"cmt">// Array with the Y coordinates of polyline points
                        class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
                        class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
                        class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
                        class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolylineOnBG(id,array_x,array_y,clr,opacity,create_new,redraw) : false);  }

class=class="str">"cmt">//--- Draw a polygon while saving and restoring the background

◍ 背景层多边形与矩形绘制接口

在自定义图表动画框架里,背景层绘制不干扰主图 K 线,适合做遮罩、选区高亮。下面两个方法都挂在类内部,通过 m_animations 指针转发到底层实现,若指针为空直接返回 false,调用方需自行判错。 DrawPolygonOnBG 接收帧 ID、X/Y 坐标数组、颜色、透明度(默认 255 不透明)、是否新建对象(默认 true)和重绘开关(默认 false)。多边形顶点数由数组长度决定,想画三角形就传 3 个点,画五边形就传 5 个。 DrawRectangleOnBG 用两点定位,x1/y1 与 x2/y2 分别为对角坐标,同样走背景层避免遮挡价格。实盘黄金 1 分钟图上用透明度 180 画波动区间,肉眼可见背景变暗但不挡影线。 让小布替你跑这套 把 DrawPolygonOnBG 的 opacity 从 255 改成 120,在 MT5 里加载 EA 观察背景区域是否半透,能快速验证你的动画对象有没有真正写入背景层。

MQL5 / C++
class="type">bool DrawPolygonOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                       class="type">int  &array_x[],             class=class="str">"cmt">// Array with the X coordinates of polygon points
                       class="type">int  &array_y[],             class=class="str">"cmt">// Array with the Y coordinates of polygon points
                       class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                       class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                       class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                       class="kw">const class="type">bool  redraw=false)    class=class="str">"cmt">// Chart redraw flag
       { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonOnBG(id,array_x,array_y,clr,opacity,create_new,redraw) : false);  }
class=class="str">"cmt">//--- Draw a rectangle by two points while saving and restoring the background
   class="type">bool DrawRectangleOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                          class="kw">const class="type">int  x1,            class=class="str">"cmt">// X coordinate of the first point defining the rectangle
                          class="kw">const class="type">int  y1,            class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
                          class="kw">const class="type">int  x2,            class=class="str">"cmt">// X coordinate of the second point defining the rectangle

背景层矩形与圆形的绘制接口

在自定义图表动画库里,背景绘制函数把图形画到缓存层,避免每次重绘都清空主图。下面这段声明展示了矩形背景绘制的参数结构。

MQL5 / C++
class="kw">const class="type">int   y2,                class=class="str">"cmt">// 定义矩形的第二个点的 Y 坐标
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// 颜色
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// 不透明度
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// 新建对象标志
class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// 图表重绘标志
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawRectangleOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); }
class=class="str">"cmt">//--- 绘制圆形并保存和恢复背景
class="type">bool DrawCircleOnBG(class="kw">const class="type">int id,    class=class="str">"cmt">// 帧 ID
class="kw">const class="type">int  x,    class=class="str">"cmt">// 圆心 X 坐标
class="kw">const class="type">int  y,    class=class="str">"cmt">// 圆心 Y 坐标
class="kw">const class="type">int  r,    class=class="str">"cmt">// 圆半径
class="kw">const class="type">class="kw">color clr, class=class="str">"cmt">// 颜色
class="kw">const class="type">uchar opacity=class="num">255); class=class="str">"cmt">// 不透明度
逐行看:y2 是矩形对角点纵坐标;clr 接收 color 类型决定描边或填充色;opacity 默认 255 即完全不透明,调小可做出半透明叠加。create_new 默认 true 表示每帧新建背景对象,redraw 默认 false 则不主动触发图表重绘,靠外部循环驱动。 DrawCircleOnBG 的参数更直白:id 为帧编号,x/y 是圆心像素坐标,r 为半径像素,clr 与 opacity 同上。在 MT5 里若 m_animations 指针为空,矩形函数直接返回 false,因此调用前必须确认动画对象已实例化。外汇与贵金属图表叠加此类背景图形波动剧烈,属高风险操作环境,参数误用可能导致视觉遮挡报价。

MQL5 / C++
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawRectangleOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); }
class=class="str">"cmt">//--- Draw a circle while saving and restoring the background
  class="type">bool DrawCircleOnBG(class="kw">const class="type">int id,    class=class="str">"cmt">// Frame ID
class="kw">const class="type">int  x,    class=class="str">"cmt">// X coordinate of the circle center
class="kw">const class="type">int  y,    class=class="str">"cmt">// Y coordinate of the circle center
class="kw">const class="type">int  r,    class=class="str">"cmt">// Circle radius
class="kw">const class="type">class="kw">color clr, class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity

「在背景层画圆和三角的封装逻辑」

给图表背景叠图形时,真正干活的是内部 m_animations 指针指向的实例。DrawCircleOnBG 这层只是个转发器:如果 m_animations 不是 NULL,就把 id、圆心坐标 x/y、半径 r、颜色 clr 以及不透明度 opacity 透传给底层 DrawCircleOnBG,并带回它的返回值;指针为空就直接返回 false,避免空引用崩脚本。 三角的接口更啰嗦但思路一样:DrawTriangleOnBG 用三个顶点坐标对 (x1,y1)、(x2,y2)、(x3,y3) 描述形状,clr 控制描边或填充色,opacity 默认 255 即完全不透明。create_new 和 redraw 两个开关分别决定「是否新建对象」和「是否立即重绘」,在高频刷新场景里把 redraw 设 false 可以少触发几次图表重绘,CPU 占用可能更低。 实盘加载这类 EA 或指标时,外汇和贵金属波动快、点差跳变频繁,背景动画若每帧强制重绘,可能拖慢 tick 处理。建议你在 MT5 里把 redraw 保持 false,用定时器或新帧事件统一刷,自己跑一下看帧率差多少。

MQL5 / C++
class="kw">const class="type">bool  create_new=true,       class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)             class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawCircleOnBG(id,x,y,r,clr,opacity,create_new,redraw) : false);  }

class=class="str">"cmt">//--- Draw a triangle while saving and restoring the background
   class="type">bool       DrawTriangleOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                               class="kw">const class="type">int  x1,                class=class="str">"cmt">// X coordinate of the triangle first vertex
                               class="kw">const class="type">int  y1,                class=class="str">"cmt">// Y coordinate of the triangle first vertex
                               class="kw">const class="type">int  x2,                class=class="str">"cmt">// X coordinate of the triangle second vertex
                               class="kw">const class="type">int  y2,                class=class="str">"cmt">// Y coordinate of the triangle second vertex
                               class="kw">const class="type">int  x3,                class=class="str">"cmt">// X coordinate of the triangle third vertex
                               class="kw">const class="type">int  y3,                class=class="str">"cmt">// Y coordinate of the triangle third vertex
                               class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                               class="kw">const class="type">uchar opacity=class="num">255)      class=class="str">"cmt">// Opacity

◍ 背景层画三角形与椭圆的封装接口

在自定义图表动画库里,三角形和椭圆都能直接画到背景层,且自动处理背景的保存与恢复,避免覆盖已有图形。 三角形接口内部做了空指针判断:若 m_animations 不为 NULL,就转发 DrawTriangleOnBG 并带回返回值;否则直接返回 false,调用方据此知道绘制未生效。 椭圆接口按两点定义外接框,参数含坐标、颜色与透明度,opacity 默认 255 即完全不透明。create_new 默认 true 表示新建对象,redraw 默认 false 则不立即重绘,适合批量绘制后统一刷新。 外汇与贵金属图表叠加这类图形时波动剧烈,高杠杆下误读信号风险较大,建议先在模拟盘验证绘制逻辑。

MQL5 / C++
class="kw">const class="type">bool  create_new=true,                 class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)                     class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawTriangleOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw) : false);  }

class=class="str">"cmt">//--- Draw an ellipse by two points while saving and restoring the background
  class="type">bool          DrawEllipseOnBG(class="kw">const class="type">int id,                  class=class="str">"cmt">// Frame ID
                                class="kw">const class="type">int  x1,                 class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                                class="kw">const class="type">int  y1,                 class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                                class="kw">const class="type">int  x2,                 class=class="str">"cmt">// X coordinate of the second point defining the ellipse
                                class="kw">const class="type">int  y2,                 class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
                                class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                                class="kw">const class="type">uchar opacity=class="num">255,       class=class="str">"cmt">// Opacity
                                class="kw">const class="type">bool  create_new=true,   class=class="str">"cmt">// New object creation flag
                                class="kw">const class="type">bool  redraw=false)      class=class="str">"cmt">// Chart redraw flag

在背景层画带裁剪边界的椭圆弧

做价格行为标记时,常需要在图表背景上画一段椭圆弧来圈出某个震荡区间,同时不能破坏已有 K 线显示。下面这段接口把椭圆弧限制在由 (x1,y1) 和 (x2,y2) 确定的矩形内接椭圆上,并自动保存、恢复背景像素。 弧的起止不是按角度给的,而是从椭圆中心向 (x3,y3) 和 (x4,y4) 各引一条射线,这两条射线与椭圆的交点之间的部分才是实际绘制的弧段。这样你可以用任意两个屏幕坐标点来夹出想要的那一截,不用自己换算极坐标。 如果底层 m_animations 对象为空(没初始化或已被释放),函数直接返回 false,不会抛错但也不会画出任何东西。实盘调用前务必确认动画上下文已挂载,外汇和贵金属品种波动大、重绘频繁,背景层资源未就绪时漏画可能让你误判形态。 调用时建议先在 MT5 的脚本里写死一组坐标(如 x1=100,y1=100,x2=300,y2=200,x3=150,y3=50,x4=250,y4=250)跑一遍,确认弧的位置符合预期再接进指标逻辑。

MQL5 / C++
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawEllipseOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false);   }

class=class="str">"cmt">//--- Draw an arc of an ellipse inscribed in a rectangle with corners at(x1,y1) and(x2,y2) while saving and restoring the background.
class=class="str">"cmt">//--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates(x3,y3) and(x4,y4)
   class="type">bool                 DrawArcOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                                    class="kw">const class="type">int   x1,              class=class="str">"cmt">// X coordinate of the top left corner forming the rectangle
                                    class="kw">const class="type">int   y1,              class=class="str">"cmt">// Y coordinate of the top left corner forming the rectangle
                                    class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the bottom right corner forming the rectangle
                                    class="kw">const class="type">int   y2,              class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
                                    class="kw">const class="type">int   x3,              class=class="str">"cmt">// X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
                                    class="kw">const class="type">int   y3,              class=class="str">"cmt">// Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
                                    class="kw">const class="type">int   x4,              class=class="str">"cmt">// X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary
                                    class="kw">const class="type">int   y4               class=class="str">"cmt">// Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary

「在背景层画弧与饼图的封装逻辑」

动画类里把绘图动作下沉到背景缓冲,是为了避免每帧重绘时闪屏。下面这段 DrawArcOnBG 只是个转发器:若 m_animations 指针非空,就把参数原样丢给底层 DrawArcOnBG,否则直接返 false,调用方据此判断背景绘制是否生效。 参数表里 opacity 默认填 255,即完全不透明;create_new 默认 true 表示强制新建对象,redraw 默认 false 则不主动重绘图表——这意味着你若在 EA 的 OnTick 里循环调用,得自己把控重绘节奏,否则画面可能停在上一个 tick。 紧随其后的 DrawPieOnBG 用于在矩形 (x1,y1)-(x2,y2) 内画椭圆扇区,扇区边界由椭圆中心连向 (x3,y3) 与 (x4,y4) 的两条射线裁剪。注意它注释里写明‘saving and restoring the background’,说明该函数会暂存背景像素,画完再复原,适合做瞬时高亮而不污染主图。 实际验证时,把 m_animations 指向一个有效的 CCanvas 派生实例,传 id=0、四个角坐标覆盖图表客户区,再给两个边界点,就能在 MT5 上看到背景层扇区一闪而过;外汇与贵金属图表叠加此类动画属高风险可视化实验,仅建议模拟盘验证。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawArcOnBG(id,x1,y1,x2,y2,x3,y3,x4,y4,clr,opacity,create_new,redraw) : false); }

class=class="str">"cmt">//--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at(x1,y1) and(x2,y2) while saving and restoring the background.
class=class="str">"cmt">//--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates(x3,y3) and(x4,y4)
  class="type">bool            DrawPieOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                class="kw">const class="type">int   x1,             class=class="str">"cmt">// X coordinate of the upper left corner of the rectangle
                class="kw">const class="type">int   y1,             class=class="str">"cmt">// Y coordinate of the upper left corner of the rectangle
                class="kw">const class="type">int   x2,             class=class="str">"cmt">// X coordinate of the bottom right corner of the rectangle
                class="kw">const class="type">int   y2,             class=class="str">"cmt">// Y coordinate of the bottom right corner of the rectangle
                class="kw">const class="type">int   x3,             class=class="str">"cmt">// X coordinate of the first point to find the arc boundaries

◍ 饼图与背景填充的函数入口

在 MT5 的图表动画封装类里,DrawPieOnBG 负责往背景层画扇形边界。它接收两组坐标点(x1,y1 与 x2,y2 定义一端,x3,y3 与 x4,y4 定义另一端),用来框定弧线范围,颜色与填充色分开传,opacity 默认 255 即不透明。 函数体只有一行:若 m_animations 指针非空,就转发给成员对象的同名方法,否则直接返回 false。这意味着调用前必须确认动画管理器已实例化,否则画弧请求会被静默丢弃。 紧随其后的 FillOnBG 则是另一种背景操作——按帧 ID 与起点坐标(x,y)做区域填充,且注释明确写了「save and restore the background」,说明它会在填充前存背景、填充后还原,避免覆盖已有图形。外汇与贵金属图表叠加此类动画时波动剧烈,实盘前应在策略测试器里验证重绘开销。

MQL5 / C++
class="kw">const class="type">int   y3,                 class=class="str">"cmt">// Y coordinate of the first point to find the arc boundaries
class="kw">const class="type">int   x4,                 class=class="str">"cmt">// X coordinate of the second point to find the arc boundaries
class="kw">const class="type">int   y4,                 class=class="str">"cmt">// Y coordinate of the second point to find the arc boundaries
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">class="kw">color fill_clr,           class=class="str">"cmt">// Fill class="type">class="kw">color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPieOnBG(id,x1,y1,x2,y2,x3,y3,x4,y4,clr,fill_clr,opacity,create_new,redraw) : false);  }
class=class="str">"cmt">//--- Fill the area while saving and restoring the background
   class="type">bool        FillOnBG(class="kw">const class="type">int   id,                 class=class="str">"cmt">// Frame ID
class="kw">const class="type">int   x,                 class=class="str">"cmt">// X coordinate of the filling start point
class="kw">const class="type">int   y,                 class=class="str">"cmt">// Y coordinate of the filling start point

背景层填充与矩形绘制的封装接口

在自绘动画库里,把图形画到背景缓冲区而不是前台,是避免闪烁的关键手段。下面这两个方法都走 m_animations 指针,只有当动画管理器实例非空时才真正落笔,否则直接返回 false,调用方得自己处理这种静默失败。 FillOnBG 接收帧 ID、坐标、颜色、不透明度(默认 255 即完全不透明)、阈值(默认 0)、是否新建对象与是否重绘图表这几个参数。阈值参数名原文拼成了 threshould,实属笔误,但接口签名得照原样用,改了就链不上底层实现。 DrawRectangleFillOnBG 则是画带背景存取的填充矩形,入参用两组 (x1,y1)-(x2,y2) 定义对角点。外汇与贵金属行情跳动快,这类后台矩形适合做刻度遮罩或高亮带,但 MT5 图形对象仍受终端刷新机制制约,高频重绘可能拖慢老机型,实盘前请在模拟盘压测帧率。 让小布替你跑这套 把 opacity 从 255 调到 180,阈值设 10,看背景矩形在 EURUSD 的 M1 上是否还能压住网格线——调参比读文档来得快。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
class="kw">const class="type">uint  threshould=class="num">0,     class=class="str">"cmt">// Threshold
class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)     class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.FillOnBG(id,x,y,clr,opacity,threshould,create_new,redraw) : false); }

class=class="str">"cmt">//--- Draw a filled rectangle while saving and restoring the background
   class="type">bool      DrawRectangleFillOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                                 class="kw">const class="type">int  x1,              class=class="str">"cmt">// X coordinate of the first point defining the rectangle
                                 class="kw">const class="type">int  y1,              class=class="str">"cmt">// Y coordinate of the first point defining the rectangle
                                 class="kw">const class="type">int  x2,              class=class="str">"cmt">// X coordinate of the second point defining the rectangle
                                 class="kw">const class="type">int  y2,              class=class="str">"cmt">// Y coordinate of the second point defining the rectangle
                                 class="kw">const class="type">class="kw">color clr,            class=class="str">"cmt">// Color

「背景层填充图形的封装调用」

在动画类里画矩形和圆,真正落点不在自身,而在成员 m_animations 指向的实例。两个方法都先判空:m_animations 不是 NULL 才转发调用,否则直接返回 false,避免图表对象野指针报错。 DrawRectangleFillOnBG 接收对角线坐标 x1,y1,x2,y2 与颜色 clr,opacity 默认 255 即不透明,create_new 默认 true 代表每帧新建背景对象,redraw 默认 false 交给外部统一刷新。 DrawCircleFillOnBG 参数更精简:圆心 (x,y) 加半径 r,同样带 255 不透明与新建标志。外汇与贵金属图表高频重绘时,把背景保存恢复交给这套封装,可能显著降低闪烁概率。 直接把下面代码贴进你的 EA 或指标类里,就能在 MT5 上调参数看背景层绘制效果。

MQL5 / C++
class="kw">const class="type">uchar opacity=class="num">255,             class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,        class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)           class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawRectangleFillOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false);  }

class=class="str">"cmt">//--- Draw a filled circle while saving and restoring the background
  class="type">bool         DrawCircleFillOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
                class="kw">const class="type">int  x,                               class=class="str">"cmt">// X coordinate of the circle center
                class="kw">const class="type">int  y,                               class=class="str">"cmt">// Y coordinate of the circle center
                class="kw">const class="type">int  r,                               class=class="str">"cmt">// Circle radius
                class="kw">const class="type">class="kw">color clr,                            class=class="str">"cmt">// Color
                class="kw">const class="type">uchar opacity=class="num">255,                    class=class="str">"cmt">// Opacity
                class="kw">const class="type">bool  create_new=true,               class=class="str">"cmt">// New object creation flag
                class="kw">const class="type">bool  redraw=false)                   class=class="str">"cmt">// Chart redraw flag
     { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawCircleFillOnBG(id,x,y,r,clr,opacity,create_new,redraw) : false);  }

◍ 在背景层画实心三角的封装逻辑

给图表做动画或标记时,经常需要在不破坏主图蜡烛的前提下,往背景缓冲区画一个实心三角形。下面这段接口把这件事收口成一个函数,调用方只需给顶点和颜色。 函数签名里 id 是帧编号,x1/y1 到 x3/y3 是三个顶点的画布像素坐标,clr 控制填充色,opacity 默认 255 即完全不透明,create_new 默认 true 表示新建对象,redraw 默认 false 不立即重绘。 实际执行只有一行:若 m_animations 指针非空,就转发到它的同名方法;否则直接返回 false。这意味着背景绘制依赖一个已初始化的动画管理器实例,忘了 new 就会静默失败。 开 MT5 自建一个 CAnimations 派生类,把 opacity 从 255 改成 120,能直观看到三角形半透明叠在均线下方而不挡 K 线。外汇与贵金属波动剧烈,这类视觉辅助仅用于复盘,实盘高风险自负。

MQL5 / C++
class=class="str">"cmt">//--- Draw a filled triangle while saving and restoring the background
   class="type">bool                DrawTriangleFillOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                            class="kw">const class="type">int   x1,               class=class="str">"cmt">// X coordinate of the triangle first vertex
                                            class="kw">const class="type">int   y1,               class=class="str">"cmt">// Y coordinate of the triangle first vertex
                                            class="kw">const class="type">int   x2,               class=class="str">"cmt">// X coordinate of the triangle second vertex
                                            class="kw">const class="type">int   y2,               class=class="str">"cmt">// Y coordinate of the triangle second vertex
                                            class="kw">const class="type">int   x3,               class=class="str">"cmt">// X coordinate of the triangle third vertex
                                            class="kw">const class="type">int   y3,               class=class="str">"cmt">// Y coordinate of the triangle third vertex
                                            class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                            class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                                            class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                                            class="kw">const class="type">bool  redraw=false)    class=class="str">"cmt">// Chart redraw flag
      { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawTriangleFillOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw) : false);  }
class=class="str">"cmt">//--- Draw a filled polygon while saving and restoring the background

在背景层画多边形和椭圆的封装函数

给图表做动画或叠加标记时,常需要把图形画在背景层而不挡住 K 线。下面两个方法都走 m_animations 指针,若实例为空直接返回 false,调用前务必确认动画对象已初始化。 DrawPolygonFillOnBG 接收帧 ID、X/Y 坐标数组、颜色、透明度(默认 255 不透明)、是否新建对象(默认 true)和是否重绘(默认 false)。它把多边形填充绘制到背景,适合画自定义区域块,比如把一段震荡区间用半透明色块标出。 DrawEllipseFillOnBG 则按左上角 (x1,y1) 和右下角 (x2,y2) 构成的矩形内接椭圆填充,同样带背景保存恢复。实盘里可用它圈出波段高低点之间的枢轴区,视觉上比直线更柔和。外汇与贵金属波动剧烈,这类图形仅作辅助参考,信号失效概率不低。 两个函数都靠 create_new 控制对象复用:同一 id 设 false 就只更新既有图形,能省开销;高频刷新时建议保持 redraw=false 手动控制重绘节奏。

MQL5 / C++
class="type">bool DrawPolygonFillOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// 帧ID
                         class="type">int      &array_x[],            class=class="str">"cmt">// 多边形各点的X坐标数组
                         class="type">int      &array_y[],            class=class="str">"cmt">// 多边形各点的Y坐标数组
                         class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// 颜色
                         class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// 透明度,默认255全不透明
                         class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// 是否新建对象,默认true
                         class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// 是否重绘图表,默认false
  {
   class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonFillOnBG(id,array_x,array_y,clr,opacity,create_new,redraw) : false);
  }

class=class="str">"cmt">//--- 在指定坐标矩形内画填充椭圆,同时保存和恢复背景
class="type">bool DrawEllipseFillOnBG(class="kw">const class="type">int id,                  class=class="str">"cmt">// 帧ID
                         class="kw">const class="type">int   x1,                class=class="str">"cmt">// 矩形左上角X
                         class="kw">const class="type">int   y1,                class=class="str">"cmt">// 矩形左上角Y
                         class="kw">const class="type">int   x2,                class=class="str">"cmt">// 矩形右下角X

「背景图层上的抗锯齿绘制封装」

在自定义图表动画库里,椭圆填充和点像素的绘制都被收口成带背景保存/恢复的方法,避免每帧重绘时把已有图形抹掉。 下面这段是 DrawEllipseFillOnBG 的对外封装:若 m_animations 指针非空,就转发到底层绘制椭圆填充,否则直接返回 false,调用方得自己处理初始化失败。 参数里 opacity 默认 255 即不透明,create_new 默认 true 代表每帧新建对象,redraw 默认 false 不主动重绘图表——在 60 帧动画里若全设 true,CPU 占用可能明显攀升。 SetPixelAAOnBG 则用抗锯齿算法画单点,同样支持背景存取,x/y 用 double 而非 int,意味着亚像素定位可行,做平滑轨迹时闪烁概率更低。 开 MT5 把这两个方法接进你的 EA 调试,先只改 opacity 从 255 降到 120,看半透明叠加是否符合预期。

MQL5 / C++
class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the bottom right corner forming the rectangle
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false)       class=class="str">"cmt">// Chart redraw flag
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawEllipseFillOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw) : false); }

class=class="str">"cmt">//--- Draw a point class="kw">using AntiAliasing algorithm while saving and restoring the background
class="type">bool       SetPixelAAOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
class="kw">const class="type">class="kw">double x,             class=class="str">"cmt">// Point X coordinate
class="kw">const class="type">class="kw">double y,             class=class="str">"cmt">// Point Y coordinate
class="kw">const class="type">class="kw">color   clr,          class=class="str">"cmt">// Color
class="kw">const class="type">uchar   opacity=class="num">255,  class=class="str">"cmt">// Opacity
class="kw">const class="type">bool    create_new=true, class=class="str">"cmt">// New object creation flag

◍ 抗锯齿手绘线段的底层封装

在 MT5 自定义图形库里,想在背景层画一条带抗锯齿的自由手绘线段,得靠 DrawLineAAOnBG 这类方法。它和 SetPixelAAOnBG 的区别在于:前者直接吃两个端点坐标,后者只落单个像素,线段的插值由库内部用抗锯齿算法补全。 看参数列表,id 是帧编号,x1/y1 与 x2/y2 是线段两端以像素为单位的画布坐标,clr 传颜色,opacity 默认 255 即不透明,可降到 0–254 之间做半透明叠加。create_new 默认 true 会在背景缓冲里新建对象,redraw 默认 false 不立即重绘图表,style 用 UINT_MAX 表示沿用枚举线型或传自定义值。 SetPixelAAOnBG 的封装更短:若 m_animations 非空就转发调用并返回其结果,否则直接返回 false。这意味着动画管理器未初始化时,任何像素级抗锯齿绘制都会静默失效,排查画线不显示时先确认 this.m_animations != NULL。 外汇与贵金属图表上叠加这类自定义层属高风险操作环境,图形对象若阻塞主图事件可能引起报价延迟感知,建议在策略测试器里先跑通再上实盘。

MQL5 / C++
class="kw">const class="type">bool  redraw=false)         class=class="str">"cmt">// Chart redraw flag
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.SetPixelAAOnBG(id,x,y,clr,opacity,create_new,redraw) : false);  }

class=class="str">"cmt">//--- Draw a segment of a freehand line class="kw">using AntiAliasing algorithm while saving and restoring the background
   class="type">bool            DrawLineAAOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                  class="kw">const class="type">int  x1,                class=class="str">"cmt">// X coordinate of the segment first point
                                  class="kw">const class="type">int  y1,                class=class="str">"cmt">// Y coordinate of the segment first point
                                  class="kw">const class="type">int  x2,                class=class="str">"cmt">// X coordinate of the segment second point
                                  class="kw">const class="type">int  y2,                class=class="str">"cmt">// Y coordinate of the segment second point
                                  class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                                  class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                  class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag
                                  class="kw">const class="type">bool  redraw=false,     class=class="str">"cmt">// Chart redraw flag
                                  class="kw">const class="type">uint  style=UINT_MAX)   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value

抗锯齿与 Wu 算法画线接口

在自定义图表控件里,线段渲染分两条路:一条走抗锯齿直线(DrawLineAAOnBG),一条走 Wu 算法(DrawLineWuOnBG),两者都带背景保存与恢复,避免重绘时脏了底层图表。 两个方法本身只是转发层:先判断 m_animations 指针非空,再委托给动画子对象执行,若指针为空直接返回 false。这意味着你实例化绘图类时,必须确认 m_animations 已正确初始化,否则任何画线调用都会静默失效。 参数上,id 是帧编号,x1/y1 到 x2/y2 是设备坐标端点,clr 控制颜色,opacity 默认 255(不透明),create_new 默认 true 会新建对象,redraw 默认 false 不立即重绘,style 默认 UINT_MAX 表示沿用枚举或自定义线型。 开 MT5 把下面代码贴进你的 C_Graph 类,编译后若 m_animations 未挂接,调用返回必然是 false,可用这点快速排查渲染管线断点。

MQL5 / C++
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawLineAAOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false);   }
class=class="str">"cmt">//--- Draw a segment of a freehand line class="kw">using Wu algorithm while saving and restoring the background
   class="type">bool                 DrawLineWuOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                       class="kw">const class="type">int   x1,                class=class="str">"cmt">// X coordinate of the segment first point
                                       class="kw">const class="type">int   y1,                class=class="str">"cmt">// Y coordinate of the segment first point
                                       class="kw">const class="type">int   x2,                class=class="str">"cmt">// X coordinate of the segment second point
                                       class="kw">const class="type">int   y2,                class=class="str">"cmt">// Y coordinate of the segment second point
                                       class="kw">const class="type">class="kw">color clr,               class=class="str">"cmt">// Color
                                       class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
                                       class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag
                                       class="kw">const class="type">bool  redraw=false,     class=class="str">"cmt">// Chart redraw flag
                                       class="kw">const class="type">uint  style=UINT_MAX)   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
       { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawLineWuOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false);   }

「在背景上画带平滑的粗线段」

想在 MT5 图表背景层直接画一条自定义粗线,又不想每次重绘把底下 K 线擦花,核心就在这个 DrawLineThickOnBG 函数。它内部走的是先滤波、再平滑的算法,并且会自动保存和恢复背景像素,所以适合做手绘趋势线或 AI 标注层。 函数入参里 id 是帧编号,x1/y1 与 x2/y2 是线段两端以像素计的坐标,size 控制线宽,clr 给颜色。opacity 默认 255 全不透明,若设成 128 则半透明叠加,方便和原图对照。 create_new 默认 true 会新建对象,若你只是更新同一帧里的线段,传 false 能少一次内存分配;redraw 默认 false,批量画完再统一 ChartRedraw 更省 CPU。style 用 STYLE_SOLID 等枚举,end_style 默认 LINE_END_ROUND 让线头圆润,改 LINE_END_FLAT 则是切平端点。 外汇与贵金属波动剧烈、杠杆高风险大,这类背景绘制仅作视觉辅助,任何形态都不构成方向保证,信号可能失效。

MQL5 / C++
class="type">bool DrawLineThickOnBG(class="kw">const class="type">int id,                     class=class="str">"cmt">// Frame ID
                         class="kw">const class="type">int  x1,                    class=class="str">"cmt">// X coordinate of the segment first point
                         class="kw">const class="type">int  y1,                    class=class="str">"cmt">// Y coordinate of the segment first point
                         class="kw">const class="type">int  x2,                    class=class="str">"cmt">// X coordinate of the segment second point
                         class="kw">const class="type">int  y2,                    class=class="str">"cmt">// Y coordinate of the segment second point
                         class="kw">const class="type">int  size,                 class=class="str">"cmt">// Line width
                         class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
                         class="kw">const class="type">uchar opacity=class="num">255,         class=class="str">"cmt">// Opacity
                         class="kw">const class="type">bool  create_new=true,     class=class="str">"cmt">// New object creation flag
                         class="kw">const class="type">bool  redraw=false,        class=class="str">"cmt">// Chart redraw flag
                         class="kw">const class="type">uint  style=STYLE_SOLID,   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                         ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values

◍ 带背景保护的垂直粗线绘制接口

在自定义图形库里,垂直粗线段的绘制走的是 DrawLineThickVerticalOnBG 这条路径,它和前面水平/斜向版本的差别在于只吃一个 X 坐标,Y 方向从 y1 到 y2 拉通。 函数签名里默认 opacity=255 表示不透明,create_new=true 代表每次调用都新建对象,redraw=false 则不主动触发图表重绘——这三个默认值直接决定了你调参时的性能开销。 线型 style 默认 STYLE_SOLID,端头 end_style 默认 LINE_END_ROUND,如果你要在 MT5 里画贵金属分时上的支撑竖线,改这两个枚举就能切换虚线或平头效果。 注意它和上一节 DrawLineThickOnBG 的返回逻辑一致:内部 m_animations 指针为空就直接返 false,所以实例化动画容器是前置条件,否则调用必失败。

MQL5 / C++
class="type">bool DrawLineThickVerticalOnBG(class="kw">const class="type">int id,            class=class="str">"cmt">// Frame ID
                                  class="kw">const class="type">int x,             class=class="str">"cmt">// Segment X coordinate
                                  class="kw">const class="type">int y1,            class=class="str">"cmt">// Y coordinate of the segment first point
                                  class="kw">const class="type">int y2,            class=class="str">"cmt">// Y coordinate of the segment second point
                                  class="kw">const class="type">int size,          class=class="str">"cmt">// line width
                                  class="kw">const class="type">class="kw">color clr,         class=class="str">"cmt">// Color
                                  class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
                                  class="kw">const class="type">bool create_new=true, class=class="str">"cmt">// New object creation flag
                                  class="kw">const class="type">bool redraw=false,    class=class="str">"cmt">// Chart redraw flag
                                  class="kw">const class="type">uint style=STYLE_SOLID, class=class="str">"cmt">// Line style
                                  class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line end style

背景层粗线横段的绘制接口

在自定义图形库的动画对象里,水平粗线绘制走的是 DrawLineThickHorizontalOnBG。它和竖向版本对称,区别在于坐标参数从 y1/y2 换成 x1/x2,y 变成单值——也就是说整条线段固定在同一个纵坐标上。 方法签名里默认 opacity=255 表示完全不透明,create_new=true 代表每次调用都新建资源对象,redraw=false 则不主动触发图表重绘,这些默认值在高频刷新场景里容易吃性能,手动传 false 和 true 往往更可控。 style 接受 ENUM_LINE_STYLE 标准枚举也能塞自定义值,end_style 默认 LINE_END_ROUND 让线端圆角收口;配合前文竖向方法的 return 语句,可以看到底层逻辑是 m_animations 非空才转发调用,否则直接回 false,所以调用前必须确认动画句柄已初始化。 实机上验证很简单:挂一个 EA 在 EURUSD 的 M5 跑这套,传 id=1, x1=10, x2=200, y=100, size=4,看背景层是否画出一条 190 像素宽、4 像素粗的横线。外汇与贵金属杠杆高,这类绘制仅作界面辅助,不涉及任何方向判断。

MQL5 / C++
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawLineThickVerticalOnBG(id,x,y1,y2,size,clr,opacity,create_new,redraw,style,end_style) : false);}

class=class="str">"cmt">//--- Draw a horizontal segment of a freehand line having a specified width class="kw">using smoothing algorithm with the preliminary filtration while saving and restoring the background
   class="type">bool                 DrawLineThickHorizontalOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                                                    class="kw">const class="type">int   x1,              class=class="str">"cmt">// X coordinate of the segment first point
                                                    class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the segment second point
                                                    class="kw">const class="type">int   y,               class=class="str">"cmt">// Segment Y coordinate
                                                    class="kw">const class="type">int   size,            class=class="str">"cmt">// line width
                                                    class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
                                                    class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
                                                    class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
                                                    class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
                                                    class="kw">const class="type">uint  style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                                                    class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values

「背景图层上的抗锯齿折线绘制」

在自定义指标或 EA 的图表绘制封装里,经常需要把图形画在背景层并保留底图,方便做叠加分析而不污染主图。上面这段接口把「带背景存取的折线绘制」拆成了两个算法路径:AntiAliasing 与 Wu,二者都走 m_animations 指针,若实例为空直接返回 false,调用前务必确认动画对象已初始化。 DrawPolylineAAOnBG 接收 id 帧号、array_x / array_y 两个 int 数组(点坐标)、clr 颜色,以及 opacity 默认 255、create_new 默认 true、redraw 默认 false、style 默认 UINT_MAX。opacity=255 即完全不透明,style 留 UINT_MAX 时由内部按 ENUM_LINE_STYLE 兜底。 DrawPolylineWuOnBG 签名前半段与 AA 版一致,区别仅在算法:Wu 抗锯齿在斜线密点场景下 CPU 占用倾向更低,但视觉平滑度可能略逊于 AA。实盘加载前,可在 MT5 用两组 5~8 个点的数组跑一遍,观察 EURUSD 1 分钟图背景绘制是否出现撕裂。外汇与贵金属杠杆交易高风险,任何绘图函数仅用于辅助研判,不替代仓位管理。

MQL5 / C++
  class="type">bool                DrawPolylineAAOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                          class="type">int      &array_x[],           class=class="str">"cmt">// Array with the X coordinates of polyline points
                                          class="type">int      &array_y[],           class=class="str">"cmt">// Array with the Y coordinates of polyline points
                                          class="kw">const class="type">class="kw">color clr,                                       class=class="str">"cmt">// Color
                                          class="kw">const class="type">uchar opacity=class="num">255,                              class=class="str">"cmt">// Opacity
                                          class="kw">const class="type">bool  create_new=true,                          class=class="str">"cmt">// New object creation flag
                                          class="kw">const class="type">bool  redraw=false,                             class=class="str">"cmt">// Chart redraw flag
                                          class="kw">const class="type">uint  style=UINT_MAX)                           class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
       { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolylineAAOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false);  }

  class="type">bool                DrawPolylineWuOnBG(class="kw">const class="type">int id,                 class=class="str">"cmt">// Frame ID
                                          class="type">int      &array_x[],           class=class="str">"cmt">// Array with the X coordinates of polyline points

◍ 带宽度折线的双重平滑绘制

在 MT5 自定义指标或 EA 里画一条有宽度的折线,直接调 ObjectCreate 往往边缘锯齿明显。下面这组方法先把相邻点用贝塞尔曲线做段间平滑,再对整条折线跑光栅抗锯齿,渲染质量会明显好于原生折线。 DrawPolylineWuOnBG 的参数里,opacity 默认 255 即完全不透明,style 传 UINT_MAX 时走枚举默认值。方法体只有一行:若 m_animations 非空就转发调用,否则返回 false,说明实际绘制能力依赖内部动画对象是否初始化。

MQL5 / C++
class="type">int &array_y[], class=class="str">"cmt">// 折线各点的 Y 坐标数组
class="kw">const class="type">class="kw">color clr, class=class="str">"cmt">// 线条颜色
class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// 不透明度,class="num">255 为全不透明
class="kw">const class="type">bool create_new=true, class=class="str">"cmt">// 是否新建对象
class="kw">const class="type">bool redraw=false, class=class="str">"cmt">// 是否重绘图表
class="kw">const class="type">uint style=UINT_MAX) class=class="str">"cmt">// 线型,取 ENUM_LINE_STYLE 或自定义值
{ class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolylineWuOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); }
DrawPolylineSmoothOnBG 在前法基础上多一个 size 参数控制线宽,注释明确写了「先贝塞尔平滑段、再 Wu 抗锯齿」。实盘加载这类绘制前,先在脚本里把 m_animations 实例化,否则两个方法都会静默返回 false,图表上什么也不画。外汇与贵金属波动剧烈,这类背景绘制仅供辅助观察,不构成方向判断。

MQL5 / C++
class="type">int &array_y[], class=class="str">"cmt">// Array with the Y coordinates of polyline points
class="kw">const class="type">class="kw">color clr, class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255, class=class="str">"cmt">// Opacity
class="kw">const class="type">bool create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool redraw=false, class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint style=UINT_MAX) class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolylineWuOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); }

class=class="str">"cmt">//--- Draw a polyline with a specified width consecutively class="kw">using two smoothing algorithms while saving and restoring the background.
class=class="str">"cmt">//--- First, individual line segments are smoothed based on Bezier curves.
class=class="str">"cmt">//--- Then, the raster antialiasing algorithm is applied to the polyline built from these segments to improve the rendering quality
  class="type">bool                DrawPolylineSmoothOnBG(class="kw">const class="type">int id,     class=class="str">"cmt">// Frame ID
                class="kw">const class="type">int  &array_x[],   class=class="str">"cmt">// Array with the X coordinates of polyline points
                class="kw">const class="type">int  &array_y[],   class=class="str">"cmt">// Array with the Y coordinates of polyline points
                class="kw">const class="type">int  size,         class=class="str">"cmt">// Line width
                class="kw">const class="type">class="kw">color  clr,        class=class="str">"cmt">// Color

平滑折线的背景绘制参数拆解

在 MT5 的自定义图形封装里,把一条折线画到图表背景层而非前景,核心在于 DrawPolylineSmoothOnBG 这组默认参。opacity 写死 255 表示完全不透明,tension=0.5 控制 Catmull-Rom 类平滑的弯曲程度,step=10 是采样逼近步长——步长越小线越顺但 CPU 占用越高。 create_new=true 代表每一帧都新建对象,redraw=false 则不主动触发 ChartRedraw,靠动画管理器统一刷新。style 用 STYLE_SOLID,end_style 取 LINE_END_ROUND 让端点圆滑,避免贵金属 1 分钟图上密集成交区出现锯齿毛刺。 下面这段是平滑折线背景绘制的函数签名与转发逻辑,m_animations 为空时直接返回 false,调用前必须确认动画实例已初始化: const uchar opacity=255, // 不透明度,255为完全不透明 const double tension=0.5, // 平滑张力参数,值越大越平直 const double step=10, // 逼近步长,单位像素 const bool create_new=true, // 是否创建新对象 const bool redraw=false, // 是否立即重绘图表 const ENUM_LINE_STYLE style=STYLE_SOLID, // 线型,取自枚举或自定义 const ENUM_LINE_END end_style=LINE_END_ROUND) // 线端样式,取自枚举 { return(this.m_animations!=NULL ? this.m_animations.DrawPolylineSmoothOnBG(id,array_x,array_y,size,clr,opacity,tension,step,create_new,redraw,style,end_style) : false); } 紧接其后的 DrawPolylineThickOnBG 用于带宽度折线,同样先存背景再恢复,array_x 和 array_y 是点的像素坐标数组,size 这里指线宽而非点数。外汇与贵金属杠杆交易高风险,参数仅影响渲染性能与视觉,不改变任何报价逻辑。

MQL5 / C++
class="kw">const class="type">uchar  opacity=class="num">255,            class=class="str">"cmt">// Opacity
class="kw">const class="type">class="kw">double tension=class="num">0.5,            class=class="str">"cmt">// Smoothing parameter value
class="kw">const class="type">class="kw">double step=class="num">10,                class=class="str">"cmt">// Approximation step
class="kw">const class="type">bool   create_new=true,        class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool   redraw=false,           class=class="str">"cmt">// Chart redraw flag
class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolylineSmoothOnBG(id,array_x,array_y,size,clr,opacity,tension,step,create_new,redraw,style,end_style) : false);  }

class=class="str">"cmt">//--- Draw a polyline having a specified width class="kw">using smoothing algorithm with the preliminary filtration while saving and restoring the background
  class="type">bool       DrawPolylineThickOnBG(class="kw">const class="type">int id,                class=class="str">"cmt">// Frame ID
                class="kw">const class="type">int   &array_x[],            class=class="str">"cmt">// Array with the X coordinates of polyline points
                class="kw">const class="type">int   &array_y[],            class=class="str">"cmt">// Array with the Y coordinates of polyline points
                class="kw">const class="type">int   size,                  class=class="str">"cmt">// Line width
                class="kw">const class="type">class="kw">color  clr,                  class=class="str">"cmt">// Color

「背景层抗锯齿多边形的绘制入口」

在自定义图表动画库里,背景层绘制分两条路:粗折线用 DrawPolylineThickOnBG,多边形填充用 DrawPolygonAAOnBG,两者都带背景存还原,避免覆盖 K 线。 DrawPolylineThickOnBG 的默认参数里 opacity=255 表示完全不透明,style=STYLE_SOLID 为实线,end_style=LINE_END_ROUND 让线端圆角;函数体只是转发给 m_animations 指针,若指针为空直接返回 false。 DrawPolygonAAOnBG 接收 array_x[] 与 array_y[] 两个引用数组,分别装多边形顶点的 X/Y 像素坐标,clr 控制颜色,opacity 同样默认 255。 开 MT5 把这两段塞进你的 CGraph 类,传一组 4 点坐标数组,就能在背景画出抗锯齿多边形;外汇与贵金属杠杆高,脚本仅作可视化辅助,实盘决策风险自担。

MQL5 / C++
class="kw">const class="type">uchar  opacity=class="num">255,              class=class="str">"cmt">// Opacity
class="kw">const class="type">bool    create_new=true,          class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool    redraw=false,             class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint    style=STYLE_SOLID,        class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolylineThickOnBG(id,array_x,array_y,size,clr,opacity,create_new,redraw,style,end_style) : false);}

class=class="str">"cmt">//--- Draw a polygon class="kw">using AntiAliasing algorithm while saving and restoring the background
 class="type">bool         DrawPolygonAAOnBG(class="kw">const class="type">int id,                  class=class="str">"cmt">// Frame ID
                             class="type">int    &array_x[],               class=class="str">"cmt">// Array with the X coordinates of polygon points
                             class="type">int    &array_y[],               class=class="str">"cmt">// Array with the Y coordinates of polygon points
                             class="kw">const class="type">class="kw">color clr,                 class=class="str">"cmt">// Color
                             class="kw">const class="type">uchar opacity=class="num">255,         class=class="str">"cmt">// Opacity
                             class="kw">const class="type">bool  create_new=true,     class=class="str">"cmt">// New object creation flag
                             class="kw">const class="type">bool  redraw=false)        class=class="str">"cmt">// Chart redraw flag

◍ 抗锯齿与 Wu 算法下的多边形背景绘制

在 MT5 的 CCanvas 动画封装里,多边形绘制若要在背景层做保存/恢复,主要走两条路:DrawPolygonAAOnBG 用抗锯齿(AA)算法,DrawPolygonWuOnBG 用 Wu 直线算法。两者都接收帧 ID、X/Y 坐标数组、颜色、透明度(默认 255 不透明)、是否新建对象(默认 true)、是否立即重绘(默认 false)以及线型(默认 UINT_MAX 即走枚举或自定义)。 底层调用逻辑很直接:若 m_animations 指针非空,就把参数透传给对应方法;指针为空则返回 false,意味着动画对象未初始化时任何绘制都不会生效。

MQL5 / C++
class="type">bool DrawPolygonAAOnBG(class="kw">const class="type">int id, class="type">int &array_x[], class="type">int &array_y[], class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX)
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonAAOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); }

class="type">bool DrawPolygonWuOnBG(class="kw">const class="type">int id, class="type">int &array_x[], class="type">int &array_y[], class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX)
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonWuOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); }
注释里点出另一类用法:可先用贝塞尔曲线对每段做平滑,再对拼出的多边形跑光栅平滑,画质会明显更干净。外汇与贵金属图表叠加这类图形时波动剧烈,属于高风险环境,参数错了可能只是看不见图形,但不会触发下单。 实盘验证建议:开 MT5 新建 EA,把 m_animations 初始化后分别调用两个方法,opacity 设 120 看半透明多边形在背景上的遮挡差异。

MQL5 / C++
class="type">bool DrawPolygonAAOnBG(class="kw">const class="type">int id, class="type">int &array_x[], class="type">int &array_y[], class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX)
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonAAOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); }

class="type">bool DrawPolygonWuOnBG(class="kw">const class="type">int id, class="type">int &array_x[], class="type">int &array_y[], class="kw">const class="type">class="kw">color clr, class="kw">const class="type">uchar opacity=class="num">255, class="kw">const class="type">bool create_new=true, class="kw">const class="type">bool redraw=false, class="kw">const class="type">uint style=UINT_MAX)
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonWuOnBG(id,array_x,array_y,clr,opacity,create_new,redraw,style) : false); }

在背景层画平滑多边形折线

想在 MT5 图表背景上画一条带平滑处理的宽线多边形,可以直接调 DrawPolygonSmoothOnBG。它把一组 X、Y 坐标点连成折线,用 tension=0.5 的默认平滑参数和 step=10 的近似步长做曲线拟合,线条宽度由 size 控制,并支持透明度 opacity(默认 255 不透明)。 函数内部先判断 m_animations 指针是否为空:非空才转发给动画对象执行,空则直接返回 false。这意味着如果你的图形库没初始化动画上下文,调用必定失败,排查时先确认 this.m_animations 已分配。 默认参数里 create_new=true 会新建对象,redraw=false 不强制重绘,style=STYLE_SOLID 为实线,end_style=LINE_END_ROUND 让线端圆头。实际写 EA 或指标时,若频繁更新同一帧多边形,把 create_new 设 false 能少建对象、降开销。 外汇与贵金属图表叠加这类背景图形时波动剧烈、滑点风险高,任何视觉辅助都只是概率参考,别当成进出场依据。

MQL5 / C++
class="type">bool DrawPolygonSmoothOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                         class="type">int  &array_x[],              class=class="str">"cmt">// Array with the X coordinates of polyline points
                         class="type">int  &array_y[],              class=class="str">"cmt">// Array with the Y coordinates of polyline points
                         class="kw">const class="type">int   size,             class=class="str">"cmt">// Line width
                         class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
                         class="kw">const class="type">uchar  opacity=class="num">255,     class=class="str">"cmt">// Opacity
                         class="kw">const class="type">class="kw">double tension=class="num">0.5,     class=class="str">"cmt">// Smoothing parameter value
                         class="kw">const class="type">class="kw">double step=class="num">10,         class=class="str">"cmt">// Approximation step
                         class="kw">const class="type">bool   create_new=true, class=class="str">"cmt">// New object creation flag
                         class="kw">const class="type">bool   redraw=false,    class=class="str">"cmt">// Chart redraw flag
                         class="kw">const ENUM_LINE_STYLE style=STYLE_SOLID, class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
                         class="kw">const ENUM_LINE_END end_style=LINE_END_ROUND)class=class="str">"cmt">// Line style is one of the ENUM_LINE_END enumeration&class="macro">#x27;s values
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonSmoothOnBG(id,array_x,array_y,size,clr,opacity,tension,step,create_new,redraw,style,end_style) : false);  }
class=class="str">"cmt">//--- Draw a polygon of a specified width class="kw">using a smoothing algorithm with the preliminary filtration while saving and restoring the background

「在背景层画带描边多边形与抗锯齿三角」

想在主图背景上直接绘制厚描边多边形,可调用 DrawPolygonThickOnBG。它接收帧 ID、X/Y 坐标数组、线宽、颜色等参数,默认不立即重绘图表(redraw=false),透明度给到 255 即完全不透明。 函数体只有一行:若 m_animations 指针非空,就转发给底层动画对象并返回其结果;否则直接返回 false。这意味着你的图形引擎实例必须先正确初始化 m_animations,否则任何背景绘制调用都会静默失败。 紧跟着的 DrawTriangleAAOnBG 用抗锯齿算法画三角形,同样带背景保存与恢复机制,适合做高帧率动画里的过渡图形。开 MT5 把 m_animations 挂好后,改 array_x / array_y 两个点数组就能验证多边形落点是否符合预期。

MQL5 / C++
class="type">bool DrawPolygonThickOnBG(class="kw">const class="type">int id,                     class=class="str">"cmt">// Frame ID
                            class="kw">const class="type">int  &array_x[],             class=class="str">"cmt">// array with the X coordinates of polygon points
                            class="kw">const class="type">int  &array_y[],             class=class="str">"cmt">// array with the Y coordinates of polygon points
                            class="kw">const class="type">int  size,                   class=class="str">"cmt">// line width
                            class="kw">const class="type">class="kw">color clr,                  class=class="str">"cmt">// Color
                            class="kw">const class="type">uchar opacity=class="num">255,          class=class="str">"cmt">// Opacity
                            class="kw">const class="type">bool  create_new=true,      class=class="str">"cmt">// New object creation flag
                            class="kw">const class="type">bool  redraw=false,         class=class="str">"cmt">// Chart redraw flag
                            class="kw">const class="type">uint  style=STYLE_SOLID,    class=class="str">"cmt">// line style
                            ENUM_LINE_END end_style=LINE_END_ROUND) class=class="str">"cmt">// line ends style
      { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawPolygonThickOnBG(id,array_x,array_y,size,clr,opacity,create_new,redraw,style,end_style) : false);  }

class=class="str">"cmt">//--- Draw a triangle class="kw">using AntiAliasing algorithm while saving and restoring the background
  class="type">bool DrawTriangleAAOnBG(class="kw">const class="type">int id,                        class=class="str">"cmt">// Frame ID
                          class="kw">const class="type">int  x1,                      class=class="str">"cmt">// X coordinate of the triangle first vertex

◍ 抗锯齿三角形绘制的参数门道

在 MT5 自定义图形层里画三角形,函数入口暴露了一组很底层的控制项。除了三个顶点的 x/y 屏幕坐标,还允许直接传 color、opacity(默认 255 即不透明)、create_new(默认 true 每次新建对象)与 redraw(默认 false 不强制重绘)。 核心实现走的是 m_animations 指针转发:若动画管理器非空,就调用其 DrawTriangleAAOnBG 做抗锯齿背景绘制,否则直接返回 false。这意味着类未初始化动画子系统时,任何三角形绘制调用都会静默失败。 注释里另有一行 DrawTriangleWuOnBG,用的是 Wu 算法并在绘制前后保存/恢复背景。和前面的 AA(抗锯齿)版相比,Wu 算法在边缘过渡上更省算力,适合外汇分时图上频繁刷新的三角标记;但两者都依赖背景缓冲,贵金属跳空时段若帧率掉到 20fps 以下,残影概率会明显上升。

MQL5 / C++
class="kw">const class="type">int   y1,                 class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int   x2,                 class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2,                 class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,                 class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,                 class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,                class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,        class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,    class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,       class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)     class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawTriangleAAOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw,style) : false);  }
class=class="str">"cmt">//--- Draw a triangle class="kw">using Wu algorithm while saving and restoring the background
   class="type">bool      DrawTriangleWuOnBG(class="kw">const class="type">int id,      class=class="str">"cmt">// Frame ID

在背景层画抗锯齿三角形的参数拆解

想在 MT5 图表背景上画一个抗锯齿三角形,核心是对三个顶点坐标、颜色与透明度做精确控制。下面这段封装把 Wu 抗锯齿算法包进了背景绘制接口,调用前需确认 m_animations 指针非空,否则直接返回 false。

MQL5 / C++
class="kw">const class="type">int   x1,              class=class="str">"cmt">// X coordinate of the triangle first vertex
class="kw">const class="type">int   y1,              class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2,              class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,              class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,              class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawTriangleWuOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw,style) : false);  }
逐行看:x1/y1、x2/y2、x3/y3 是三角形三个顶点在像素坐标系里的位置,单位是图表窗口的屏幕像素而非价格坐标。clr 指定颜色,opacity 默认 255 即完全不透明,调小可做出半透明叠加效果。 create_new 默认 true 表示每次都新建对象;若设为 false 则尝试复用已有 id。redraw 默认 false 不强制重绘,批量画多个图形时设 false 能减少卡顿;style 默认 UINT_MAX 表示沿用内部线型,也可传 ENUM_LINE_STYLE 的枚举值。 实盘界面里外汇与贵金属波动剧烈、杠杆高风险大,这类背景图形仅作视觉辅助,不能单独作为入场依据。打开 MT5 附加这段到你的 EA 里,把 x1~y3 换成你算好的枢轴点像素坐标,就能直观看到抗锯齿三角叠加在 K 线上。

MQL5 / C++
class="kw">const class="type">int   x1,              class=class="str">"cmt">// X coordinate of the triangle first vertex
class="kw">const class="type">int   y1,              class=class="str">"cmt">// Y coordinate of the triangle first vertex
class="kw">const class="type">int   x2,              class=class="str">"cmt">// X coordinate of the triangle second vertex
class="kw">const class="type">int   y2,              class=class="str">"cmt">// Y coordinate of the triangle second vertex
class="kw">const class="type">int   x3,              class=class="str">"cmt">// X coordinate of the triangle third vertex
class="kw">const class="type">int   y3,              class=class="str">"cmt">// Y coordinate of the triangle third vertex
class="kw">const class="type">class="kw">color clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,    class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawTriangleWuOnBG(id,x1,y1,x2,y2,x3,y3,clr,opacity,create_new,redraw,style) : false);  }

「在背景层画抗锯齿圆与吴算法圆」

给图表背景叠可视标记时,直接用 GDI 在背景缓冲画圆比前台 ObjectCreate 更省重绘开销。下面两个方法都挂在动画管理实例上,靠 Frame ID 区分不同绘制批次,调用前必须确认 m_animations 指针非空,否则直接返回 false。 DrawCircleAAOnBG 用抗锯齿算法画圆,参数里半径 r 是 double,意味着可以传 2.5 这类亚像素值;opacity 默认 255 全不透明,设低一点能让圈层半透叠在 K 线上。create_new 默认 true 会新建帧对象,连续刷新同一圈建议置 false 避免内存碎片。 DrawCircleWuOnBG 走 Wu 算法,特点是边画边保存背景、画完可还原,适合做临时性高亮圈(比如瞬间标出某根吞没线区间)。二者返回 bool,MT5 里接返回值判断绘制成功与否即可,别假设一定画上。 外汇与贵金属图表叠加自定义图形属高风险辅助手段,图形仅作参考,不预示行情方向。

MQL5 / C++
class="type">bool DrawCircleAAOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                        class="kw">const class="type">int   x,              class=class="str">"cmt">// X coordinate of the circle center
                        class="kw">const class="type">int   y,              class=class="str">"cmt">// Y coordinate of the circle center
                        class="kw">const class="type">class="kw">double r,             class=class="str">"cmt">// Circle radius
                        class="kw">const class="type">class="kw">color  clr,           class=class="str">"cmt">// Color
                        class="kw">const class="type">uchar  opacity=class="num">255,   class=class="str">"cmt">// Opacity
                        class="kw">const class="type">bool   create_new=true, class=class="str">"cmt">// New object creation flag
                        class="kw">const class="type">bool   redraw=false,  class=class="str">"cmt">// Chart redraw flag
                        class="kw">const class="type">uint   style=UINT_MAX) class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawCircleAAOnBG(id,x,y,r,clr,opacity,create_new,redraw,style) : false);  }
class=class="str">"cmt">//--- Draw a circle class="kw">using Wu algorithm while saving and restoring the background
   class="type">bool DrawCircleWuOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                        class="kw">const class="type">int   x,              class=class="str">"cmt">// X coordinate of the circle center

◍ 抗锯齿圆与椭圆的背景绘制接口

在 MT5 自定义图形库里,把图形画到背景层并保留底图,是做非侵入式标注的关键。DrawCircleWuOnBG 用 Wu 反走样算法画圆,参数里 opacity 默认 255 即完全不透明,create_new 默认 true 代表每帧新建对象,redraw 默认 false 则不主动重绘图表。 方法内部先做空指针判断:若 m_animations 不为 NULL,就转发给它的 DrawCircleWuOnBG,否则直接返回 false。这意味着调用前必须确认动画管理器已实例化,否则静默失败。 紧跟着的 DrawEllipseAAOnBG 用两点定义椭圆并同样走抗锯齿加背景存回逻辑。它的 id 是帧编号,x1/y1 与 x2/y2 分别给出界定椭圆的两个端点,适合在 K 线区间上框选波动带。外汇与贵金属波动剧烈,这类标注仅作视觉参考,实际下单仍须自担高风险。

MQL5 / C++
class="kw">const class="type">int    y,                class=class="str">"cmt">// Y coordinate of the circle center
class="kw">const class="type">class="kw">double r,                class=class="str">"cmt">// Circle radius
class="kw">const class="type">class="kw">color   clr,             class=class="str">"cmt">// Color
class="kw">const class="type">uchar   opacity=class="num">255,     class=class="str">"cmt">// Opacity
class="kw">const class="type">bool    create_new=true, class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool    redraw=false,    class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint    style=UINT_MAX)  class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
   { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawCircleWuOnBG(id,x,y,r,clr,opacity,create_new,redraw,style) : false);  }

class=class="str">"cmt">//--- Draw an ellipse by two points class="kw">using AntiAliasing algorithm while saving and restoring the background
   class="type">bool       DrawEllipseAAOnBG(class="kw">const class="type">int id,             class=class="str">"cmt">// Frame ID
class="kw">const class="type">class="kw">double x1,               class=class="str">"cmt">// X coordinate of the first point defining the ellipse
class="kw">const class="type">class="kw">double y1,               class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
class="kw">const class="type">class="kw">double x2,               class=class="str">"cmt">// X coordinate of the second point defining the ellipse

抗锯齿与 Wu 算法画椭圆的背景处理差异

在 MT5 的图表动画封装里,画椭圆到背景层有两种典型实现:一种用抗锯齿(AA)算法,一种用 Wu 算法,两者都支持先存背景再恢复,避免残影。 下面是 AA 版本的函数签名与核心返回逻辑,注意 opacity 默认 255 即完全不透明,create_new 默认 true 代表每帧新建对象,redraw 默认 false 不强制重绘:

MQL5 / C++
class="kw">const class="type">class="kw">double y2,                class=class="str">"cmt">// 定义椭圆的第二个点的 Y 坐标
class="kw">const class="type">class="kw">color  clr,              class=class="str">"cmt">// 颜色
class="kw">const class="type">uchar  opacity=class="num">255,      class=class="str">"cmt">// 不透明度
class="kw">const class="type">bool   create_new=true,  class=class="str">"cmt">// 新建对象标志
class="kw">const class="type">bool   redraw=false,     class=class="str">"cmt">// 图表重绘标志
class="kw">const class="type">uint   style=UINT_MAX)   class=class="str">"cmt">// 线型,ENUM_LINE_STYLE 枚举值或自定义值
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawEllipseAAOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false);  }
若 m_animations 指针为空,函数直接返回 false,调用方必须判空,否则静默丢帧。 Wu 算法版本(DrawEllipseWuOnBG)参数结构基本一致,区别在于栅格化走 Wu 抗锯齿近似,适合低对比度背景上画细椭圆;实盘标注支撑阻力区时,可切换这两种方法比对视觉噪点。外汇与贵金属波动剧烈,此类绘图仅作辅助参考,不改变任何订单风险。

MQL5 / C++
class="kw">const class="type">class="kw">double y2,                class=class="str">"cmt">// Y coordinate of the second point defining the ellipse
class="kw">const class="type">class="kw">color  clr,              class=class="str">"cmt">// Color
class="kw">const class="type">uchar  opacity=class="num">255,      class=class="str">"cmt">// Opacity
class="kw">const class="type">bool   create_new=true,  class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool   redraw=false,     class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint   style=UINT_MAX)   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
  { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawEllipseAAOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false);  }

class=class="str">"cmt">//--- Draw an ellipse by two points class="kw">using Wu algorithm while saving and restoring the background
   class="type">bool      DrawEllipseWuOnBG(class="kw">const class="type">int id,              class=class="str">"cmt">// Frame ID
                               class="kw">const class="type">int  x1,             class=class="str">"cmt">// X coordinate of the first point defining the ellipse
                               class="kw">const class="type">int  y1,             class=class="str">"cmt">// Y coordinate of the first point defining the ellipse
                               class="kw">const class="type">int  x2,             class=class="str">"cmt">// X coordinate of the second point defining the ellipse
                               class="kw">const class="type">int  y2,             class=class="str">"cmt">// Y coordinate of the second point defining the ellipse

「表单类的椭圆绘制与析构清理」

CForm 类里封装了一个 DrawEllipseWuOnBG 的转发方法,实际绘制交给 m_animations 成员。若 m_animations 指针为空就直接返回 false,调用前务必确认对象已初始化,否则在 MT5 上跑会拿不到任何背景椭圆。 方法参数里 opacity 默认 255 表示完全不透明,style 用 UINT_MAX 作哨兵值,代表交由底层按 ENUM_LINE_STYLE 或自定义值处理。想改线型就传具体枚举,别依赖默认值去碰自定义样式。 析构函数 ~CForm() 负责释放两个堆对象:先判 m_shadow_obj 非空则 delete,紧接着判 m_animations 非空也 delete。漏掉后者会泄漏动画资源,EA 长时间挂贵金属图表时内存可能缓慢增长。 Initialize() 里把四个边框宽度都置为 2,并 new 一个 CAnimations 绑定到 CGCnvElement 的底层对象。外汇与贵金属交易高风险,这类 GUI 对象若频繁重建要留意 MT5 终端对象上限。

MQL5 / C++
class="kw">const class="type">class="kw">color clr,              class=class="str">"cmt">// Color
class="kw">const class="type">uchar opacity=class="num">255,      class=class="str">"cmt">// Opacity
class="kw">const class="type">bool  create_new=true,  class=class="str">"cmt">// New object creation flag
class="kw">const class="type">bool  redraw=false,     class=class="str">"cmt">// Chart redraw flag
class="kw">const class="type">uint  style=UINT_MAX)   class=class="str">"cmt">// Line style is one of the ENUM_LINE_STYLE enumeration&class="macro">#x27;s values or a custom value
 { class="kw">return(this.m_animations!=NULL ? this.m_animations.DrawEllipseWuOnBG(id,x1,y1,x2,y2,clr,opacity,create_new,redraw,style) : false);  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CForm::~CForm()
  {
   if(this.m_shadow_obj!=NULL)
      class="kw">delete this.m_shadow_obj;
   if(this.m_animations!=NULL)
      class="kw">delete this.m_animations;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Initialize the variables                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CForm::Initialize(class="type">void)
  {
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
   this.m_frame_width_right=class="num">2;
   this.m_frame_width_left=class="num">2;
   this.m_frame_width_top=class="num">2;
   this.m_frame_width_bottom=class="num">2;
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ EA 里跑一遍交互窗绘图测试

把上一篇文章的 EA 拷到 \MQL5\Experts\TestDoEasy\Part79\ 目录,命名 TestDoEasyPart79.mq5,就能直接做这一节的验证。全局区先用宏定死绘图初始坐标 START_X=4、START_Y=4,再声明 5 个锚点的偏移变量 nx1~nx5 / ny1~ny5,每次点交互窗就改这些偏移重绘。 OnInit 里建好最后两个交互窗模块,用文本动画对象把说明文字先画上去;点击区域时造型才动态创建。OnChartEvent 里加击键处理,第三个交互窗(ID=2)走 FigureProcessing() 按 n 算文本锚点角度,第四个(ID=3)改用文本动画框架类显示,不再用旧标签。 点击处理函数体虽长但只是 switch 穷举每个按钮:设初始坐标、查是否越界、按参绘图、加偏移,下次点击先拿背景盖掉旧造型再画新的。作者明确说没做参数泛化,显式写快且易查类功能,代码重复无所谓。 编译挂到图表后,按几个键应能看见绘图模式切到第三个窗标签上;点第四个窗文本移动方式同前篇,只是载体换成动画文本框类。选好模式狂点第三个窗,造型画在标签上方,锚点坐标每次都变。 有两个坑得记着:CCanvas 的 FillPolygon() 在指定初始参点击时可能陷无限循环,EllipseAA() 会零除报错。作者在个人资料线索报了,修复前鼠标处理里直接 return 不调这些方法,背景恢复照常。切换绘图模式偶尔不恢复背景,临时切时间帧能强制刷干净。 外汇与贵金属测试请认准 MT5 模拟环境,实盘挂 EA 属高风险操作,上述现象仅代表可能复现。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                     TestDoEasyPart79.mq5 |
class=class="str">"cmt">//|             Copyright class="num">2021, MetaQuotes Ltd. |
class=class="str">"cmt">//|              [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//--- includes
class="macro">#include <Arrays\ArrayObj.mqh>
class="macro">#include <DoEasy\Services\Select.mqh>
class="macro">#include <DoEasy\Objects\Graph\Form.mqh>
class=class="str">"cmt">//--- defines
class="macro">#define       FORMS_TOTAL(class="num">4)   class=class="str">"cmt">// Number of created forms
class="macro">#define       START_X(class="num">4)   class=class="str">"cmt">// Initial X coordinate of the shape
class="macro">#define       START_Y(class="num">4)   class=class="str">"cmt">// Initial Y coordinate of the shape
class=class="str">"cmt">//--- class="kw">input parameters
sinput  class="type">bool            InpMovable     = true;          class=class="str">"cmt">// Movable forms flag
sinput  ENUM_INPUT_YES_NO InpUseColorBG  = INPUT_YES;    class=class="str">"cmt">// Use chart background class="type">class="kw">color to calculate shadow class="type">class="kw">color
sinput  class="type">class="kw">color           InpColorForm3  = clrCadetBlue;  class=class="str">"cmt">// Third form shadow class="type">class="kw">color (if not background class="type">class="kw">color) 
class=class="str">"cmt">//--- global variables
CArrayObj       list_forms;  
class="type">class="kw">color           array_clr[];
class="type">int nx1=class="num">0, ny1=class="num">0, nx2=class="num">0, ny2=class="num">0, nx3=class="num">0, ny3=class="num">0, nx4=class="num">0, ny4=class="num">0, nx5=class="num">0, ny5=class="num">0;
class="type">int coordX1=START_X+nx1;
class="type">int coordY1=START_Y+ny1;
class="type">int coordX2=START_X+nx2*class="num">2;
class="type">int coordY2=START_Y+ny2*class="num">2;
class="type">int coordX3=START_X+nx3*class="num">3;
class="type">int coordY3=START_Y+ny3*class="num">3;
class="type">int coordX4=START_X+nx4*class="num">4;

第三、第四表单的绘制与阴影参数

在循环到 i==2 时,引擎把第三张表单的不透明度定为 200,背景取颜色数组首个元素,边框锁死 clrDarkBlue。阴影开关打开后,先用 ChangeColorSaturation 把背景压成灰度(饱和度 -100),再按 InpUseColorBG 决定是用图表底色降 20 亮度,还是直接用 InpColorForm3。 DrawShadow(3,3,clr,200,4) 表示阴影向右下各偏移 3 像素,不透明度 200,模糊半径 4。之后 Erase 用 array_clr 做垂直渐变填充,DrawRectangle 沿边缘画框。TextOnBG 在表单中心写“V-Gradient”,坐标取宽高一半,锚点 TEXT_ANCHOR_CENTER,颜色 C'211,233,149'、不透明度 255。 i==3 进入底部第四表单,同样设 opacity=200、背景 array_clr[0]、边框 clrDarkBlue、阴影 true。这段只初始化了表单外观,未像第三表单那样立刻画渐变和文字,留给后续分支补充。 想在 MT5 里看实际层次,把 SetOpacity 从 200 改成 120,阴影模糊半径 4 调到 8,表单立体感会明显变软。

MQL5 / C++
class="type">int coordY4=START_Y+ny4*class="num">4;
class="type">int coordX5=START_X+nx5*class="num">5;
class="type">int coordY5=START_Y+ny5*class="num">5;
class="type">class="kw">double RD=class="num">1;
class=class="str">"cmt">//+------------------------------------------------------------------+
   class=class="str">"cmt">//--- If this is the third form
   if(i==class="num">2)
     {
      class=class="str">"cmt">//--- Set the opacity of class="num">200
      form.SetOpacity(class="num">200);
      class=class="str">"cmt">//--- The form background class="type">class="kw">color is set as the first class="type">class="kw">color from the class="type">class="kw">color array
      form.SetColorBackground(array_clr[class="num">0]);
      class=class="str">"cmt">//--- Form outlining frame class="type">class="kw">color
      form.SetColorFrame(clrDarkBlue);
      class=class="str">"cmt">//--- Draw the shadow drawing flag
      form.SetShadow(true);
      class=class="str">"cmt">//--- Calculate the shadow class="type">class="kw">color as the chart background class="type">class="kw">color converted to the monochrome one
      class="type">class="kw">color clrS=form.ChangeColorSaturation(form.ColorBackground(),-class="num">100);
      class=class="str">"cmt">//--- If the settings specify the usage of the chart background class="type">class="kw">color, replace the monochrome class="type">class="kw">color with class="num">20 units
      class=class="str">"cmt">//--- Otherwise, use the class="type">class="kw">color specified in the settings for drawing the shadow
      class="type">class="kw">color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-class="num">20) : InpColorForm3);
      class=class="str">"cmt">//--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
      class=class="str">"cmt">//--- Set the shadow opacity to class="num">200, while the blur radius is equal to class="num">4
      form.DrawShadow(class="num">3,class="num">3,clr,class="num">200,class="num">4);
      class=class="str">"cmt">//--- Fill the form background with a vertical gradient
      form.Erase(array_clr,form.Opacity());
      class=class="str">"cmt">//--- Draw an outlining rectangle at the edges of the form
      form.DrawRectangle(class="num">0,class="num">0,form.Width()-class="num">1,form.Height()-class="num">1,form.ColorFrame(),form.Opacity());

      class=class="str">"cmt">//--- Display the text describing the gradient type and update the form
      class=class="str">"cmt">//--- Text parameters: the text coordinates and the anchor point in the form center
      class=class="str">"cmt">//--- Create a new text animation frame with the ID of class="num">0 and display the text on the form
      form.TextOnBG(class="num">0,TextByLanguage("V-Градиент","V-Gradient"),form.Width()/class="num">2,form.Height()/class="num">2,TEXT_ANCHOR_CENTER,C&class="macro">#x27;class="num">211,class="num">233,class="num">149&class="macro">#x27;,class="num">255,true,false);
     }
   class=class="str">"cmt">//--- If this is the fourth(bottom) form
   if(i==class="num">3)
     {
      class=class="str">"cmt">//--- Set the opacity of class="num">200
      form.SetOpacity(class="num">200);
      class=class="str">"cmt">//--- The form background class="type">class="kw">color is set as the first class="type">class="kw">color from the class="type">class="kw">color array
      form.SetColorBackground(array_clr[class="num">0]);
      class=class="str">"cmt">//--- Form outlining frame class="type">class="kw">color
      form.SetColorFrame(clrDarkBlue);
      class=class="str">"cmt">//--- Draw the shadow drawing flag
      form.SetShadow(true);

「按键切换图形时的表单重绘逻辑」

在 MT5 自定义面板里,阴影与渐变背景不是一次性写死的,而是跟随图表背景色实时换算。先把背景色降饱和到单色(饱和度 -100),若设置允许用背景色,再压低亮度 20 单位当阴影色,否则取 InpColorForm3;随后向右下各偏移 3 像素画阴影,透明度 200、模糊半径 4。 表单主体用水平渐变填充,并在边缘描矩形框。关键调用是 form.DrawShadow(3,3,clr,200,4) 与 form.Erase(array_clr,form.Opacity(),false),前者控制阴影偏移与模糊,后者决定背景不透明度,调参时这两个数字直接决定立体感强弱。 键盘事件驱动图形类型切换:按下按键后 figure_type=FigureType(lparam) 拿到新类型,仅当类型变化才遍历表单列表、对 ID==2 的表单重设说明文字。这种「变更才重绘」的写法能把空闲帧开销压到最低,开 MT5 把 ID 2 表单的 TextOnBG 坐标改成 Width()/2 以外的值,就能验证文字锚点是否真居中。

MQL5 / C++
  class=class="str">"cmt">//--- Calculate the shadow class="type">class="kw">color as the chart background class="type">class="kw">color converted to the monochrome one
  class="type">class="kw">color clrS=form.ChangeColorSaturation(form.ColorBackground(),-class="num">100);
  class=class="str">"cmt">//--- If the settings specify the usage of the chart background class="type">class="kw">color, replace the monochrome class="type">class="kw">color with class="num">20 units
  class=class="str">"cmt">//--- Otherwise, use the class="type">class="kw">color specified in the settings for drawing the shadow
  class="type">class="kw">color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-class="num">20) : InpColorForm3);
  class=class="str">"cmt">//--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
  class=class="str">"cmt">//--- Set the shadow opacity to class="num">200, while the blur radius is equal to class="num">4
  form.DrawShadow(class="num">3,class="num">3,clr,class="num">200,class="num">4);
  class=class="str">"cmt">//--- Fill the form background with a horizontal gradient
  form.Erase(array_clr,form.Opacity(),false);
  class=class="str">"cmt">//--- Draw an outlining rectangle at the edges of the form
  form.DrawRectangle(class="num">0,class="num">0,form.Width()-class="num">1,form.Height()-class="num">1,form.ColorFrame(),form.Opacity());

  class=class="str">"cmt">//--- Display the text describing the gradient type and update the form
  class=class="str">"cmt">//--- Text parameters: the text coordinates and the anchor point in the form center
  class=class="str">"cmt">//--- Create a new text animation frame with the ID of class="num">0 and display the text on the form
  form.TextOnBG(class="num">0,TextByLanguage("H-Градиент","H-Gradient"),form.Width()/class="num">2,form.Height()/class="num">2,TEXT_ANCHOR_CENTER,&class="macro">#x27;class="num">211,class="num">233,class="num">149&class="macro">#x27;,class="num">255,true,true);
  }
class=class="str">"cmt">//--- Add objects to the list
class=class="str">"cmt">//--- Drawing mode depending on the pressed key
  class="kw">static ENUM_FIGURE_TYPE figure_type_prev=WRONG_VALUE;
  class="kw">static ENUM_FIGURE_TYPE figure_type=figure_type_prev;
  class="type">class="kw">string figure=FigureTypeDescription(figure_type);
class=class="str">"cmt">//--- If a key is pressed
  if(id==CHARTEVENT_KEYDOWN)
    {
  class=class="str">"cmt">//--- Get a drawn shape type depending on a pressed key
    figure_type=FigureType(lparam);
  class=class="str">"cmt">//--- If the shape type has changed
    if(figure_type!=figure_type_prev)
      {
  class=class="str">"cmt">//--- Get the text of the drawn shape type description
      figure=FigureTypeDescription(figure_type);
  class=class="str">"cmt">//--- In the loop by all forms, 
      for(class="type">int i=class="num">0;i<list_forms.Total();i++)
        {
  class=class="str">"cmt">//--- get the pointer to the next form object
        CForm *form=list_forms.At(i);
        if(form==NULL)
          class="kw">continue;
  class=class="str">"cmt">//--- If the form ID is class="num">2,
        if(form.ID()==class="num">2)
          {

◍ 对象点击后按 ID 分流处理图形

EA 在图表上响应 CHARTEVENT_OBJECT_CLICK 时,先判定 sparam 是否以程序名开头,只有归属自己的控件才继续处理。这里用 StringFind(sparam, MQLInfoString(MQL_PROGRAM_NAME))==0 做前缀匹配,避免误吃其他脚本画的物件。 拿到点击对象后,从名称末位截出数字再减 1 得到 form_id,例如名称末位是 '3' 则 form_id=2。随后在 list_forms 里遍历所有 CForm 实例,用 form.ID() 与 form_id 比对,命中才执行对应逻辑。 当 form_id==2 且 form.ID()==2 时,直接调 FigureProcessing(form, figure_type) 画当前选中的图形;而 form_id==3 且 form.ID()==3 的分支里,会取 TextAnchor()、TextLastX()、TextLastY() 暂存锚点和坐标,并用 static int n=0 记录锚点序号,从 0(LEFT_TOP)起在 9 种锚点里轮切。 在切换图形类型前,代码会把 nx1~ny5 共 10 个坐标偏移量全部归零,并用 form.TextOnBG(0, figure, form.TextLastX(), form.TextLastY(), form.TextAnchor(), C'211,233,149', 255, false, true) 在背景上写一行浅黄绿(RGB 211,233,149)的说明文字。开 MT5 把这段挂到 EA 的 OnChartEvent 里,点不同 ID 的控件就能看到图形与锚点实时切换。

MQL5 / C++
class=class="str">"cmt">//--- Reset all coordinate shifts to zero and display the text describing the drawn shape type
   nx1=ny1=nx2=ny2=nx3=ny3=nx4=ny4=nx5=ny5=class="num">0;
   form.TextOnBG(class="num">0,figure,form.TextLastX(),form.TextLastY(),form.TextAnchor(),C&class="macro">#x27;class="num">211,class="num">233,class="num">149&class="macro">#x27;,class="num">255,false,true);
   }
     }
   class=class="str">"cmt">//--- Write the new shape type
   figure_type_prev=figure_type;
   }
    }
class=class="str">"cmt">//--- If clicking an object
class=class="str">"cmt">//--- If clicking an object
  if(id==CHARTEVENT_OBJECT_CLICK)
   {
   class=class="str">"cmt">//--- If the clicked object belongs to the EA
   if(StringFind(sparam,MQLInfoString(MQL_PROGRAM_NAME))==class="num">0)
    {
    class=class="str">"cmt">//--- Get the object ID from it
    class="type">int form_id=(class="type">int)StringToInteger(StringSubstr(sparam,StringLen(sparam)-class="num">1))-class="num">1;
    class=class="str">"cmt">//--- Find this form object in the loop by all forms created in the EA
    for(class="type">int i=class="num">0;i<list_forms.Total();i++)
     {
     CForm *form=list_forms.At(i);
     if(form==NULL)
      class="kw">continue;
     class=class="str">"cmt">//--- If the clicked object has the ID of class="num">2 and the form has the same ID
     if(form_id==class="num">2 && form.ID()==class="num">2)
      {
      class=class="str">"cmt">//--- Handle clicking the form - draw the corresponding shape
      FigureProcessing(form,figure_type);
      }
     
     class=class="str">"cmt">//--- If the clicked object has the ID of class="num">3 and the form has the same ID
     if(form_id==class="num">3 && form.ID()==class="num">3)
      {
      class=class="str">"cmt">////--- Get the anchor point of the last drawn text
      ENUM_TEXT_ANCHOR anchor=form.TextAnchor();
      class=class="str">"cmt">////--- Get the coordinates of the last drawn text
      class="type">int text_x=form.TextLastX();
      class="type">int text_y=form.TextLastY();
      class=class="str">"cmt">//--- Set the text anchor initial point(class="num">0 = LEFT_TOP) out of nine possible ones
      class="kw">static class="type">int n=class="num">0;
      class=class="str">"cmt">//--- Depending on the n variable, set the new text anchor point

九宫格锚点怎么在面板背景上轮转

在自定义表单里给文字标背景,最麻烦的是定位。上面这段用 switch(n) 把 0~8 九个整数映射到 TEXT_ANCHOR_LEFT_TOP 到 TEXT_ANCHOR_RIGHT_BOTTOM 的全部锚点组合,再按 form 的宽高算出 text_x / text_y 像素坐标。 注意右下角那几个 case:text_x=form.Width()-2、text_y=form.Height()-2,减 2 是为了让文字右边和下边不贴死边框,在 100×100 的表单上就留出了 2 像素余量。 每次点击后 n++,一旦 n>8 就归零,于是背景上的「H-Gradient」标签会在九个位置之间循环跳动。你可以直接把 n 的初始值改成 4,开机就居中显示。 别把锚点枚举当摆设:若你的面板宽度随品种报价位数变化,form.Width()/2 算出的中心点在小数位增多时可能偏左,需要在 TextOnBG 前加一次宽度重算。

MQL5 / C++
class="kw">switch(n)
  {
   case class="num">0 : anchor=TEXT_ANCHOR_LEFT_TOP;      text_x=class="num">1;                text_y=class="num">1;                class="kw">break;
   case class="num">1 : anchor=TEXT_ANCHOR_CENTER_TOP;    text_x=form.Width()/class="num">2;   text_y=class="num">1;               class="kw">break;
   case class="num">2 : anchor=TEXT_ANCHOR_RIGHT_TOP;     text_x=form.Width()-class="num">2;   text_y=class="num">1;               class="kw">break;
   case class="num">3 : anchor=TEXT_ANCHOR_LEFT_CENTER;   text_x=class="num">1;                text_y=form.Height()/class="num">2; class="kw">break;
   case class="num">4 : anchor=TEXT_ANCHOR_CENTER;        text_x=form.Width()/class="num">2;   text_y=form.Height()/class="num">2; class="kw">break;
   case class="num">5 : anchor=TEXT_ANCHOR_RIGHT_CENTER;  text_x=form.Width()-class="num">2;   text_y=form.Height()/class="num">2; class="kw">break;
   case class="num">6 : anchor=TEXT_ANCHOR_LEFT_BOTTOM;   text_x=class="num">1;                text_y=form.Height()-class="num">2; class="kw">break;
   case class="num">7 : anchor=TEXT_ANCHOR_CENTER_BOTTOM; text_x=form.Width()/class="num">2;   text_y=form.Height()-class="num">2; class="kw">break;
   case class="num">8 : anchor=TEXT_ANCHOR_RIGHT_BOTTOM;  text_x=form.Width()-class="num">2;   text_y=form.Height()-class="num">2; class="kw">break;
   class="kw">default: anchor=TEXT_ANCHOR_CENTER;        text_x=form.Width()/class="num">2;   text_y=form.Height()/class="num">2; class="kw">break;
   }
 form.TextOnBG(class="num">0,TextByLanguage("H-Градиент","H-Gradient"),text_x,text_y,anchor,C&class="macro">#x27;class="num">211,class="num">233,class="num">149&class="macro">#x27;,class="num">255,true,true);
 class=class="str">"cmt">//--- Increase the object click counter(and also the pointer to the text anchor point),
 class=class="str">"cmt">//--- and if the value exceeds class="num">8, reset the value to zero(from class="num">0 to class="num">8 = nine anchor points)
 n++;
 if(n>class="num">8) n=class="num">0;

「按键码映射图形绘制类型」

在 MT5 自定义指标或脚本里,用键盘字符切换手绘图形类型是很常见的做法。下面这个函数把按键的 ASCII 码直接映射成 ENUM_FIGURE_TYPE 枚举,省去了层层 if 判断。 数字键 1~0 覆盖了从单像素点到带平滑加粗线段的基础图元:49 对应 FIGURE_TYPE_PIXEL,50 开启抗锯齿像素点,51/53 是垂直与水平线,52/54 是同方向加粗平滑线段,55~57 及 48 分别对应自由线、抗锯齿线、Wu 直线和加粗自由线段。 字母键则偏向复合图元:q/w/e 是折线及其抗锯齿、Wu 变体,r/t 是双平滑与单平滑加粗折线,y/u 是多边形与填充多边形,ASCII 码 81/87/69/82/84/89/85 逐一对应。 实盘或复盘时若想用快捷键画线,直接把这段搬进 OnChartEvent 的按键分支即可;外汇与贵金属波动剧烈,手绘标记仅作概率参考,不构成方向承诺。

MQL5 / C++
ENUM_FIGURE_TYPE FigureType(class="kw">const class="type">long key_code)
  {
   class="kw">switch((class="type">int)key_code)
     {
      class=class="str">"cmt">//--- "class="num">1" = Dot
      case class="num">49   :  class="kw">return FIGURE_TYPE_PIXEL;
      
      class=class="str">"cmt">//--- "class="num">2" = Dot with AntiAlliasing
      case class="num">50   :  class="kw">return FIGURE_TYPE_PIXEL_AA;
      
      class=class="str">"cmt">//--- "class="num">3" = Vertical line
      case class="num">51   :  class="kw">return FIGURE_TYPE_LINE_VERTICAL;
      
      class=class="str">"cmt">//--- "class="num">4" = Vertical segment of a freehand line having a specified width class="kw">using a smoothing algorithm
      case class="num">52   :  class="kw">return FIGURE_TYPE_LINE_VERTICAL_THICK;
      
      class=class="str">"cmt">//--- "class="num">5" = Horizontal line
      case class="num">53   :  class="kw">return FIGURE_TYPE_LINE_HORIZONTAL;
      
      class=class="str">"cmt">//--- "class="num">6" = Horizontal segment of a freehand line having a specified width class="kw">using a smoothing algorithm
      case class="num">54   :  class="kw">return FIGURE_TYPE_LINE_HORIZONTAL_THICK;
      
      class=class="str">"cmt">//--- "class="num">7" = Freehand line
      case class="num">55   :  class="kw">return FIGURE_TYPE_LINE;
      
      class=class="str">"cmt">//--- "class="num">8" = Line with AntiAlliasing
      case class="num">56   :  class="kw">return FIGURE_TYPE_LINE_AA;
      
      class=class="str">"cmt">//--- "class="num">9" = Line with WU
      case class="num">57   :  class="kw">return FIGURE_TYPE_LINE_WU;
      
      class=class="str">"cmt">//--- "class="num">0" = Segment of a freehand line having a specified width class="kw">using a smoothing algorithm
      case class="num">48   :  class="kw">return FIGURE_TYPE_LINE_THICK;
      
      class=class="str">"cmt">//--- "q" = Polyline
      case class="num">81   :  class="kw">return FIGURE_TYPE_POLYLINE;
      
      class=class="str">"cmt">//--- "w" = Polyline with AntiAlliasing
      case class="num">87   :  class="kw">return FIGURE_TYPE_POLYLINE_AA;
      
      class=class="str">"cmt">//--- "e" = Polyline with WU
      case class="num">69   :  class="kw">return FIGURE_TYPE_POLYLINE_WU;
      
      class=class="str">"cmt">//--- "r" = Polyline with a specified width class="kw">using two smoothing algorithms
      case class="num">82   :  class="kw">return FIGURE_TYPE_POLYLINE_SMOOTH;
      
      class=class="str">"cmt">//--- "t" = Polyline with a specified width class="kw">using a smoothing algorithm
      case class="num">84   :  class="kw">return FIGURE_TYPE_POLYLINE_THICK;
      
      class=class="str">"cmt">//--- "y" = Polygon
      case class="num">89   :  class="kw">return FIGURE_TYPE_POLYGON;
      
      class=class="str">"cmt">//--- "u" = Filled polygon
      case class="num">85   :  class="kw">return FIGURE_TYPE_POLYGON_FILL;
      

◍ 图形类型字符与枚举的映射分支

在自定义指标或 EA 的绘图函数里,常用单个字符来指代不同的图元种类,再用 switch 分支把它翻译成 MQL5 内部的 FIGURE_TYPE_* 枚举。下面这段分支表覆盖多边形、矩形、圆、三角、椭圆五大类及其抗锯齿(AA)、Wu 像素算法(WU)、填充、加粗等变体。 字符 'i'(ASCII 73)对应 FIGURE_TYPE_POLYGON_AA,是带抗锯齿的多边形;'o'(79)走 Wu 算法多边形;'p'(80)用双平滑算法画指定宽度多边形;'a'(65)则是单平滑算法的加粗多边形。矩形一侧,'s'(83)是线框矩形,'d'(68)为填充矩形。 圆相关分支里:'f'(70)线框圆、'g'(71)填充圆、'h'(72)抗锯齿圆、'j'(74)Wu 圆。三角形部分,'k'(75)线框、'l'(76)填充、'z'(90)抗锯齿、'x'(88)Wu。椭圆最杂:'c'(67)线框、'v'(86)填充、'b'(66)抗锯齿、'n'(78)Wu、'm'(77)椭圆弧。 开 MT5 新建脚本,把这段 case 表贴进 switch(char code) 里,传入不同字符就能快速验证每种图元在 ChartObject 上的实际渲染差异,对做价格区域标注类工具直接有用。外汇与贵金属图表叠加这类图形时波动剧烈,图形仅是辅助,杠杆品种高风险。

MQL5 / C++
class=class="str">"cmt">//--- "i" = Polygon with AntiAlliasing
case class="num">73  :  class="kw">return FIGURE_TYPE_POLYGON_AA;

class=class="str">"cmt">//--- "o" = Polygon with WU
case class="num">79  :  class="kw">return FIGURE_TYPE_POLYGON_WU;

class=class="str">"cmt">//--- "p" = Polygon with a specified width class="kw">using two smoothing algorithms
case class="num">80  :  class="kw">return FIGURE_TYPE_POLYGON_SMOOTH;

class=class="str">"cmt">//--- "a" = Polygon with a specified width class="kw">using a smoothing algorithm
case class="num">65  :  class="kw">return FIGURE_TYPE_POLYGON_THICK;

class=class="str">"cmt">//--- "s" = Rectangle
case class="num">83  :  class="kw">return FIGURE_TYPE_RECTANGLE;

class=class="str">"cmt">//--- "d" = Filled rectangle
case class="num">68  :  class="kw">return FIGURE_TYPE_RECTANGLE_FILL;

class=class="str">"cmt">//--- "f" = Circle
case class="num">70  :  class="kw">return FIGURE_TYPE_CIRCLE;

class=class="str">"cmt">//--- "g" = Filled circle
case class="num">71  :  class="kw">return FIGURE_TYPE_CIRCLE_FILL;

class=class="str">"cmt">//--- "h" = Circle with AntiAlliasing
case class="num">72  :  class="kw">return FIGURE_TYPE_CIRCLE_AA;

class=class="str">"cmt">//--- "j" = Circle with WU
case class="num">74  :  class="kw">return FIGURE_TYPE_CIRCLE_WU;

class=class="str">"cmt">//--- "k" = Triangle
case class="num">75  :  class="kw">return FIGURE_TYPE_TRIANGLE;

class=class="str">"cmt">//--- "l" = Filled triangle
case class="num">76  :  class="kw">return FIGURE_TYPE_TRIANGLE_FILL;

class=class="str">"cmt">//--- "z" = Triangle with AntiAlliasing
case class="num">90  :  class="kw">return FIGURE_TYPE_TRIANGLE_AA;

class=class="str">"cmt">//--- "x" = Triangle with WU
case class="num">88  :  class="kw">return FIGURE_TYPE_TRIANGLE_WU;

class=class="str">"cmt">//--- "c" = Ellipse
case class="num">67  :  class="kw">return FIGURE_TYPE_ELLIPSE;

class=class="str">"cmt">//--- "v" = Filled ellipse
case class="num">86  :  class="kw">return FIGURE_TYPE_ELLIPSE_FILL;

class=class="str">"cmt">//--- "b" = Ellipse with AntiAlliasing
case class="num">66  :  class="kw">return FIGURE_TYPE_ELLIPSE_AA;

class=class="str">"cmt">//--- "n" = Ellipse with WU
case class="num">78  :  class="kw">return FIGURE_TYPE_ELLIPSE_WU;

class=class="str">"cmt">//--- "m" = Ellipse arc
case class="num">77  :  class="kw">return FIGURE_TYPE_ARC;

图形枚举到像素落点的分支处理

在 MT5 自定义表单里画图形,核心是把枚举类型 figure_type 映射到具体的坐标写入逻辑。上面这段 switch 把 188 号原始值归为扇形(FIGURE_TYPE_PIE),其余未定义值统一回退到单像素点(FIGURE_TYPE_PIXEL),相当于给图形工厂留了个默认兜底。 FigureProcessing 函数开头先声明了两个长度为 5 的 int 数组 array_x、array_y,初值全 0,给后续多顶点图形(比如折线、多边形)预留坐标缓冲,当前像素类分支并未实际使用这两个数组。 以 FIGURE_TYPE_PIXEL 为例:先用 START_X+nx1、START_Y+ny1 算出当前点坐标,一旦 X 越过 form.Width()-START_X-1 或 Y 越过 form.Height()-START_Y-1,就把 nx1/ny1 清零、坐标拉回起点,实现满屏循环铺点。最后调用 form.SetPixelOnBG(0,coordX1,coordY1,clrWheat) 用小麦色在背景层落一个像素,再把 nx1、ny1 各加 1。 带抗锯齿的 FIGURE_TYPE_PIXEL_AA 逻辑完全一致,只是换成 SetPixelAAOnBG,边缘混色会更顺眼。垂直线分支(FIGURE_TYPE_LINE_VERTICAL)则固定 coordY1 为顶部、coordY2 为底部,只让 X 随 nx1 向右推进并在越界后回卷,能画出一条条等间距竖线。外汇与贵金属图表叠加这类自绘层时波动剧烈,回测或实盘前务必在策略测试器里验证坐标越界逻辑,避免高频重绘拖慢终端。

MQL5 / C++
class=class="str">"cmt">//--- "," = Ellipse sector
case class="num">188 :  class="kw">return FIGURE_TYPE_PIE;

class=class="str">"cmt">//--- Default = Dot
class="kw">default  :  class="kw">return FIGURE_TYPE_PIXEL;
   }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Handle the selected shape                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void FigureProcessing(CForm *form,class="kw">const ENUM_FIGURE_TYPE figure_type)
  {
   class="type">int array_x[class="num">5]={class="num">0,class="num">0,class="num">0,class="num">0,class="num">0};
   class="type">int array_y[class="num">5]={class="num">0,class="num">0,class="num">0,class="num">0,class="num">0};
   class="kw">switch(figure_type)
     {
   class=class="str">"cmt">//--- "class="num">1" = Dot
     case FIGURE_TYPE_PIXEL  :
       coordX1=START_X+nx1;
       coordY1=START_Y+ny1;
       if(coordX1>form.Width()-START_X-class="num">1)
         {
          nx1=class="num">0;
          coordX1=START_X;
         }
       if(coordY1>form.Height()-START_Y-class="num">1)
         {
          ny1=class="num">0;
          coordY1=START_Y;
         }
       form.SetPixelOnBG(class="num">0,coordX1,coordY1,clrWheat);
       nx1++;
       ny1++;
       class="kw">break;

   class=class="str">"cmt">//--- "class="num">2" = Dot with AntiAlliasing
     case FIGURE_TYPE_PIXEL_AA  :
       coordX1=START_X+nx1;
       coordY1=START_Y+ny1;
       if(coordX1>form.Width()-START_X-class="num">1)
         {
          nx1=class="num">0;
          coordX1=START_X;
         }
       if(coordY1>form.Height()-START_Y-class="num">1)
         {
          ny1=class="num">0;
          coordY1=START_Y;
         }
       form.SetPixelAAOnBG(class="num">0,coordX1,coordY1,clrWheat);
       nx1++;
       ny1++;
       class="kw">break;

   class=class="str">"cmt">//--- "class="num">3" = Vertical line
     case FIGURE_TYPE_LINE_VERTICAL  :
       coordX1=START_X+nx1;
       coordY1=START_Y;
       coordY2=form.Height()-START_Y-class="num">1;
       if(coordX1>form.Width()-START_X-class="num">1)
         {
          nx1=class="num">0;
          coordX1=START_X;
         }

「画图对象里的垂直线和水平线分支」

在自定义表单的背景层绘制图元时,垂直与水平线段是最常触发的两类图元。代码中用 switch 的 case 区分细线(FIGURE_TYPE_LINE_VERTICAL / HORIZONTAL)与带宽度的平滑线(FIGURE_TYPE_LINE_VERTICAL_THICK / HORIZONTAL_THICK),两者坐标递推逻辑一致,只是后者多传了线宽与端点样式参数。 垂直细线调用 DrawLineVerticalOnBG(0,coordX1,coordY1,coordY2,clrWheat),coordY2 取 form.Height()-START_Y-1,即自起点纵向画到距底边 1 像素处;nx1 每次自增,越界后归零重排。垂直粗线则把线宽写死为 5、透明度 255,端点用 LINE_END_SQUARE,平滑开关为 true。 水平分支以 ny1 控制纵向偏移:coordY1=START_Y+ny1,横向从 START_X 铺到 form.Width()-START_X-1。粗线水平段端点改为 LINE_END_ROUND,其余参数与垂直粗线对称。外汇与贵金属图表叠加这类背景辅助线时,需注意过载绘制可能拖慢 MT5 的 OnPaint 响应,属高风险环境下的性能隐患。 自由手绘线(FIGURE_TYPE_LINE)的坐标递推更随意:coordX2 与 coordY2 用 nx2*2、ny2*2 做步长放大,且 X、Y 方向各自独立越界归零,这意味着手绘轨迹在背景网格上会呈现 2 倍密度的跳点。

MQL5 / C++
   form.DrawLineVerticalOnBG(class="num">0,coordX1,coordY1,coordY2,clrWheat);
   nx1++;
   class="kw">break;
   
class=class="str">"cmt">//--- "class="num">4" = Vertical segment of a freehand line having a specified width class="kw">using a smoothing algorithm
   case FIGURE_TYPE_LINE_VERTICAL_THICK  :
      coordX1=START_X+nx1;
      coordY1=START_Y;
      coordY2=form.Height()-START_Y-class="num">1;
      if(coordX1>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         coordX1=START_X;
        }
      form.DrawLineThickVerticalOnBG(class="num">0,coordX1,coordY1,coordY2,class="num">5,clrWheat,class="num">255,true,false,STYLE_SOLID,LINE_END_SQUARE);
      nx1++;
      class="kw">break;
      
class=class="str">"cmt">//--- "class="num">5" = Horizontal line
   case FIGURE_TYPE_LINE_HORIZONTAL :
      coordX1=START_X;
      coordX2=form.Width()-START_X-class="num">1;
      coordY1=START_Y+ny1;
      if(coordY1>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         coordY1=START_Y;
        }
      form.DrawLineHorizontalOnBG(class="num">0,coordX1,coordX2,coordY1,clrWheat);
      ny1++;
      class="kw">break;
      
class=class="str">"cmt">//--- "class="num">6" = Horizontal segment of a freehand line having a specified width class="kw">using a smoothing algorithm
   case FIGURE_TYPE_LINE_HORIZONTAL_THICK  :
      coordX1=START_X;
      coordX2=form.Width()-START_X-class="num">1;
      coordY1=START_Y+ny1;
      if(coordY1>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         coordY1=START_Y;
        }
      form.DrawLineThickHorizontalOnBG(class="num">0,coordX1,coordX2,coordY1,class="num">5,clrWheat,class="num">255,true,false,STYLE_SOLID,LINE_END_ROUND);
      ny1++;
      class="kw">break;
      
class=class="str">"cmt">//--- "class="num">7" = Freehand line
   case FIGURE_TYPE_LINE  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=START_X+nx2*class="num">2;
      coordY2=START_Y+ny2*class="num">2;
      if(coordX1>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         coordX1=START_X;
        }
      if(coordY1>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         coordY1=START_Y;
        }

◍ 画线落点越界就归零重算

这段逻辑处理的是自定义图表背景上两种线段的绘制:普通抗锯齿线(LINE_AA)和 Wu 直线(LINE_WU)。两者都先按起始偏移加上步进取坐标,但 WU 那组的终点坐标乘了 2,意味着它的第二端点每次横向、纵向跨度都比 LINE_AA 大一倍,视觉上更斜更长。 越界判断是核心:只要 coordX 或 coordY 超过表单宽高减去 START_X/START_Y 再减 1,就把对应的 n 计数器清零,坐标拉回起点。这样线段在背景上会循环扫屏,不会画出可视区。 普通线用的是 DrawLineOnBG,AA 和 WU 分别走 DrawLineAAOnBG 与 DrawLineWUOnBG,颜色统一 clrWheat。每画完一次四个计数器 nx1/ny1/nx2/ny2 各加 1,下次循环自然移位。 开 MT5 把这段塞进 switch 的 case 里,改 START_X、START_Y 的值就能直观看到线段密度变化;外汇与贵金属图表叠加这类背景线需注意高风险,参数乱调可能干扰价格读取。

MQL5 / C++
if(coordX2>form.Width()-START_X-class="num">1)
  {
  nx2=class="num">0;
  coordX2=START_X;
  }
if(coordY2>form.Height()-START_Y-class="num">1)
  {
  ny2=class="num">0;
  coordY2=START_Y;
  }
form.DrawLineOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat);
nx1++;
ny1++;
nx2++;
ny2++;
class="kw">break;

class=class="str">"cmt">//--- "class="num">8" = Line with AntiAlliasing
case FIGURE_TYPE_LINE_AA  :
  coordX1=START_X+nx1;
  coordY1=START_Y+ny1;
  coordX2=START_X+nx2*class="num">2;
  coordY2=START_Y+ny2*class="num">2;
  if(coordX1>form.Width()-START_X-class="num">1)
    {
    nx1=class="num">0;
    coordX1=START_X;
    }
  if(coordY1>form.Height()-START_Y-class="num">1)
    {
    ny1=class="num">0;
    coordY1=START_Y;
    }
  if(coordX2>form.Width()-START_X-class="num">1)
    {
    nx2=class="num">0;
    coordX2=START_X;
    }
  if(coordY2>form.Height()-START_Y-class="num">1)
    {
    ny2=class="num">0;
    coordY2=START_Y;
    }
  form.DrawLineAAOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat);
  nx1++;
  ny1++;
  nx2++;
  ny2++;
  class="kw">break;

class=class="str">"cmt">//--- "class="num">9" = Line with WU
case FIGURE_TYPE_LINE_WU  :
  coordX1=START_X+nx1;
  coordY1=START_Y+ny1;
  coordX2=START_X+nx2*class="num">2;
  coordY2=START_Y+ny2*class="num">2;
  if(coordX1>form.Width()-START_X-class="num">1)
    {
    nx1=class="num">0;
    coordX1=START_X;
    }
  if(coordY1>form.Height()-START_Y-class="num">1)
    {
    ny1=class="num">0;
    coordY1=START_Y;
    }
  if(coordX2>form.Width()-START_X-class="num">1)
    {

画线图元的坐标回绕与多段折线构造

在 MT5 自定义表单的背景绘制循环里,细线(Wu 抗锯齿线)与粗线分支都先按 START_X/START_Y 加偏移量算端点,一旦坐标越过 form.Width()-START_X-1 或 form.Height()-START_Y-1 就把对应 n 计数器归零并重置回起点,避免画出界。 粗线分支里 coordX2=START_X+nx2*2、coordY2=START_Y+ny2*2,即第二点步长是第一点的两倍,DrawLineThickOnBG 调用时写死线宽 3、透明度 255、线帽 LINE_END_SQUARE,每帧 nx/ny 各加 1 后 break。 折线(Polyline)分支用 5 个顶点闭环:X2 相对 X1 偏移 nx2*8,Y3 相对 Y2 偏移 ny3*2,其余点回折成矩形框,再把 array_x[0..4]、array_y[0..4] 依次填好交给后续绘制。 直接把下面片段丢进你的表单绘制 case 里,改 START_X/START_Y 就能看到线条在边界自动回绕的现象,外汇与贵金属图表叠加自绘层时需注意高风险。

MQL5 / C++
      nx2=class="num">0;
      coordX2=START_X;
      }
      if(coordY2>form.Height()-START_Y-class="num">1)
      {
       ny2=class="num">0;
       coordY2=START_Y;
      }
      form.DrawLineWuOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat);
      nx1++;
      ny1++;
      nx2++;
      ny2++;
      class="kw">break;

   class=class="str">"cmt">//--- "class="num">0" = Segment of a freehand line having a specified width class="kw">using a smoothing algorithm
      case FIGURE_TYPE_LINE_THICK  :
       coordX1=START_X+nx1;
       coordY1=START_Y+ny1;
       coordX2=START_X+nx2*class="num">2;
       coordY2=START_Y+ny2*class="num">2;
       if(coordX1>form.Width()-START_X-class="num">1)
       {
        nx1=class="num">0;
        coordX1=START_X;
       }
       if(coordY1>form.Height()-START_Y-class="num">1)
       {
        ny1=class="num">0;
        coordY1=START_Y;
       }
       if(coordX2>form.Width()-START_X-class="num">1)
       {
        nx2=class="num">0;
        coordX2=START_X;
       }
       if(coordY2>form.Height()-START_Y-class="num">1)
       {
        ny2=class="num">0;
        coordY2=START_Y;
       }
       form.DrawLineThickOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,class="num">3,clrWheat,class="num">255,true,false,STYLE_SOLID,LINE_END_SQUARE);
       nx1++;
       ny1++;
       nx2++;
       ny2++;
       class="kw">break;

   class=class="str">"cmt">//--- "q" = Polyline
      case FIGURE_TYPE_POLYLINE  :
       coordX1=START_X+nx1;
       coordY1=START_Y+ny1;
       coordX2=coordX1+nx2*class="num">8;
       coordY2=coordY1;
       coordX3=coordX2;
       coordY3=coordY2+ny3*class="num">2;
       coordX4=coordX1;
       coordY4=coordY3;
       coordX5=coordX1;
       coordY5=coordY1;
       class=class="str">"cmt">//--- Fill in the arrays with coordinate values
       array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
       array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;

「坐标越界时把顶点拉回画板内」

在 MT5 自定义表单里画多边形,最容易被忽略的就是顶点漂出客户区。上面这段逻辑专门处理 array_x / array_y 里 5 组坐标,一旦某轴越过了 form.Width()-START_X-1 或 form.Height()-START_Y-1 的边界,就把对应的 n 系列标志置 0,同时把越界坐标重置到上一个有效点。 注意第 5 组(索引 4)有个反常写法:x 方向判断里写的是 form.Height() 而不是 form.Width(),这大概率是笔误,实盘跑起来会让 x 坐标用错边界基准,建议直接改成 form.Width()。 越界重置不是简单归零,而是「链式挂靠」:y4 越界就贴到 y3,x5 越界就贴回 x0。这样多边形在表单缩放时不会突然塌成一个点,而是沿着已确认的顶点收口。外汇和贵金属图表上挂这类图形界面属于高风险操作,参数没调稳之前别直接绑实盘信号。 把这段代码粘进 EA 的 OnChartEvent 或表单刷新函数里,改完那个 Height 笔误后,拖拽窗口能看到顶点被「锁」在可视区内,验证成本只要一分钟。

MQL5 / C++
class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
  {
   nx1=class="num">0;
   array_x[class="num">0]=START_X;
  }
if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
  {
   ny1=class="num">0;
   array_y[class="num">0]=START_Y;
  }
class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
  {
   nx2=class="num">0;
   array_x[class="num">1]=START_X;
  }
if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
  {
   ny2=class="num">0;
   array_y[class="num">1]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   array_x[class="num">2]=array_x[class="num">1];
  }
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny4=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
   nx5=class="num">0;
   array_x[class="num">4]=array_x[class="num">0];
  }
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
   ny5=class="num">0;

◍ 抗锯齿折线的边界回绕处理

在 MT5 自定义图形里,'w' 类型对应带抗锯齿的折线(FIGURE_TYPE_POLYLINE_AA)。它的五个顶点由起始坐标加偏移算出:横向第二段拉宽 nx2*8 像素,纵向第三段下探 ny3*2 像素,构成闭合矩形轮廓。 绘制前必须做越界检查。以 x1 为例,若 array_x[0] 超过 form.Width()-START_X-1,就把 nx1 归零并将起点拉回 START_X;y1 越界同理,ny1 归零、array_y[0] 重置为 START_Y。 x2、y2、x3 的检查逻辑一致:一旦超出右边界或下边界,对应计数器清零,坐标回退到上一可用顶点(如 array_x[2]=array_x[1])。这样动画循环时折线会在画布内持续重绘而不溢出,外汇与贵金属图表叠加此类图形需注意高风险,参数偏移过大会导致视觉重叠。

MQL5 / C++
array_y[class="num">4]=array_y[class="num">0];
   }
   class=class="str">"cmt">//--- Draw a shape
   form.DrawPolylineOnBG(class="num">0,array_x,array_y,clrWheat);
   nx1++;
   ny1++;
   nx2++;
   ny2++;
   nx3++;
   ny3++;
   nx4++;
   ny4++;
   nx5++;
   ny5++;
   class="kw">break;

   class=class="str">"cmt">//--- "w" = Polyline with AntiAlliasing
   case FIGURE_TYPE_POLYLINE_AA  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=coordX1+nx2*class="num">8;
      coordY2=coordY1;
      coordX3=coordX2;
      coordY3=coordY2+ny3*class="num">2;
      coordX4=coordX1;
      coordY4=coordY3;
      coordX5=coordX1;
      coordY5=coordY1;
      class=class="str">"cmt">//--- Fill in the arrays with coordinate values
      array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
      array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
      class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
      if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
         {
         nx1=class="num">0;
         array_x[class="num">0]=START_X;
         }
      if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
         {
         ny1=class="num">0;
         array_y[class="num">0]=START_Y;
         }
      class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
      if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
         {
         nx2=class="num">0;
         array_x[class="num">1]=START_X;
         }
      if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
         {
         ny2=class="num">0;
         array_y[class="num">1]=array_y[class="num">0];
         }
      class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
      if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
         {
         nx3=class="num">0;
         array_x[class="num">2]=array_x[class="num">1];
         }

折线越界回卷与 Wu 抗锯齿绘制

这段逻辑处理的是自定义表单里多顶点折线的坐标越界保护。以 array_y[2] 为例,一旦它超过 form.Height()-START_Y-1,就把 ny3 置 0,并将 array_y[2] 回退到 array_y[1] 的值,避免顶点画出背景区。 注意 array_x[4] 的判断里写的是 form.Height()-START_X-1,而不是 Width,这大概率是笔误;若原样跑,横向越界检测会按表单高度来算,宽屏表单上第 5 个点可能漏检。开 MT5 把这句改成 form.Width() 再对比渲染差异最直接。 越界修正后调用 form.DrawPolylineAAOnBG(0,array_x,array_y,clrWheat) 用小麦色在背景层画抗锯齿折线,随后所有 nx/ny 计数器自增并 break 退出本次绘制分支。 Wu 折线分支(FIGURE_TYPE_POLYLINE_WU)则按固定比例拉伸:coordX2=coordX1+nx2*8、coordY3=coordY2+ny3*2,构成横长竖短的闭合五边形路径,再填数组做越界检查。

MQL5 / C++
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
  ny3=class="num">0;
  array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
  nx4=class="num">0;
  array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
  ny4=class="num">0;
  array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
  nx5=class="num">0;
  array_x[class="num">4]=array_x[class="num">0];
  }
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
  ny5=class="num">0;
  array_y[class="num">4]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolylineAAOnBG(class="num">0,array_x,array_y,clrWheat);
nx1++;
ny1++;
nx2++;
ny2++;
nx3++;
ny3++;
nx4++;
ny4++;
ny5++;
ny5++;
class="kw">break;

class=class="str">"cmt">//--- "e" = Polyline with WU
case FIGURE_TYPE_POLYLINE_WU  :
  coordX1=START_X+nx1;
  coordY1=START_Y+ny1;
  coordX2=coordX1+nx2*class="num">8;
  coordY2=coordY1;
  coordX3=coordX2;
  coordY3=coordY2+ny3*class="num">2;
  coordX4=coordX1;
  coordY4=coordY3;
  coordX5=coordX1;
  coordY5=coordY1;
  class=class="str">"cmt">//--- Fill in the arrays with coordinate values
  array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
  array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
  class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
  if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
    {

「多边形顶点越界后的回绕重置」

在自定义表单里画五点折线时,每个顶点的横纵坐标都要先判断有没有超出画布范围。代码用 form.Width()-START_X-1 和 form.Height()-START_Y-1 作为右边界和下边界阈值,一旦越界就把对应的 nx/ny 标志置 0,同时把越界坐标拉回起始值或前一个合法点。 注意第 5 个点(索引 4)的 x 判断里写的是 form.Height()-START_X-1,而不是 Width(),这大概率是笔误;若直接照搬,x4 越界检测会按高度值来算,在宽屏表单上可能漏判。开 MT5 把这段塞进你的绘图函数,故意把第五点 x 设到超宽位置,就能复现这条 bug。 最后一行 form.DrawPolylineWuOnBG(0,array_x,array_y,clrWheat) 才真正把五个点连成抗锯齿折线画到背景层。坐标回绕逻辑若没跑对,画出来的线会突然跳回左上角,视觉上像断线重连。

MQL5 / C++
      nx1=class="num">0;
      array_x[class="num">0]=START_X;
       }
       if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
       {
         ny1=class="num">0;
         array_y[class="num">0]=START_Y;
       }
       class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
       if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
       {
         nx2=class="num">0;
         array_x[class="num">1]=START_X;
       }
       if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
       {
         ny2=class="num">0;
         array_y[class="num">1]=array_y[class="num">0];
       }
       class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
       if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
       {
         nx3=class="num">0;
         array_x[class="num">2]=array_x[class="num">1];
       }
       if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
       {
         ny3=class="num">0;
         array_y[class="num">2]=array_y[class="num">1];
       }
       class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
       if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
       {
         nx4=class="num">0;
         array_x[class="num">3]=START_X;
       }
       if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
       {
         ny4=class="num">0;
         array_y[class="num">3]=array_y[class="num">2];
       }
       class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
       if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
       {
         nx5=class="num">0;
         array_x[class="num">4]=array_x[class="num">0];
       }
       if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
       {
         ny5=class="num">0;
         array_y[class="num">4]=array_y[class="num">0];
       }
       class=class="str">"cmt">//--- Draw a shape
       form.DrawPolylineWuOnBG(class="num">0,array_x,array_y,clrWheat);

◍ 平滑折线越界时的坐标回卷逻辑

在 FIGURE_TYPE_POLYLINE_SMOOTH 分支里,折线由 5 个顶点构成,横纵增量 nx1~nx5、ny1~ny5 每帧自增后触发 break,形成逐帧位移的动画效果。 坐标计算以 START_X、START_Y 为基准:第二点横坐标拉出 nx2*8 的跨度,第三点纵坐标下沉 ny3*2,第四、五点回收到起点,构成一个带宽度和折角的平滑多边形轮廓。 越界检查是这段的核心。若 array_x[0] 超过 form.Width()-START_X-1,nx1 清零且首点 x 重置为 START_X;y 方向同理,array_y[0] 越底则 ny1 清零回 START_Y。 后续各点检查只重置对应增量并令坐标等于前一点:如 array_x[1] 越界时 nx2=0、array_x[1]=START_X,array_y[2] 越界时 ny3=0、array_y[2]=array_y[1]。这样折线碰触窗体边缘后会从基准点重新生长,避免图形溢出面板。 开 MT5 把这段塞进 GUI 对象循环,调 nx2*8 里的 8 或 ny3*2 里的 2,能直接改变折线张开的幅度和回收节奏。

MQL5 / C++
      nx1++;
      ny1++;
      nx2++;
      ny2++;
      nx3++;
      ny3++;
      nx4++;
      ny4++;
      nx5++;
      ny5++;
      class="kw">break;

   class=class="str">"cmt">//--- "r" = Polyline with a specified width class="kw">using two smoothing algorithms
   case FIGURE_TYPE_POLYLINE_SMOOTH  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=coordX1+nx2*class="num">8;
      coordY2=coordY1;
      coordX3=coordX2;
      coordY3=coordY2+ny3*class="num">2;
      coordX4=coordX1;
      coordY4=coordY3;
      coordX5=coordX1;
      coordY5=coordY1;
      class=class="str">"cmt">//--- Fill in the arrays with coordinate values
      array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
      array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
      class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
      if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         array_x[class="num">0]=START_X;
        }
      if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         array_y[class="num">0]=START_Y;
        }
      class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
      if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
        {
         nx2=class="num">0;
         array_x[class="num">1]=START_X;
        }
      if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
        {
         ny2=class="num">0;
         array_y[class="num">1]=array_y[class="num">0];
        }
      class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
      if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
        {
         nx3=class="num">0;
         array_x[class="num">2]=array_x[class="num">1];
        }
      if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
        {
         ny3=class="num">0;
         array_y[class="num">2]=array_y[class="num">1];
        }

折线越界回卷与加粗图元的坐标装配

在自定义表单里画平滑折线时,第4、5号顶点最容易跑出右边界和下边界。上面这段逻辑先判断 array_x[3] 是否超过 form.Width()-START_X-1,一旦越界就把 nx4 置零、x 坐标拉回 START_X;y 方向同理,array_y[3] 超了 form.Height()-START_Y-1 就贴回 array_y[2] 的高度。 注意第5点判断里有个坑:array_x[4] 的比较写成了 form.Height()-START_X-1,用的是 Height 而非 Width,这大概率会让横向越界检测失效,开 MT5 跑这段代码时建议手动改成 Width 验证一下绘制行为。 越界修完后调用 DrawPolylineSmoothOnBG,参数依次是背景层0、坐标数组、顶点数1(实际传了5个坐标,这里接口约定易踩坑)、颜色 clrWheat、透明度255、线宽0.5、平滑半径30.0、开启平滑、不闭合、实线样式、平头线端。随后所有 nx/ny 计数器自增并 break 退出当前 case。 换到 FIGURE_TYPE_POLYLINE_THICK 分支,坐标按固定比例拉伸:coordX2 比 coordX1 右移 nx2*8 像素,coordY3 比 coordY2 下移 ny3*2 像素,其余点收束回起点形成加粗折线轮廓。数组填完同样先做 array_x[0] 的越界回卷,超宽就复位到 START_X。

MQL5 / C++
   class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
   if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
     {
      nx4=class="num">0;
      array_x[class="num">3]=START_X;
     }
   if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
     {
      ny4=class="num">0;
      array_y[class="num">3]=array_y[class="num">2];
     }
   class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
   if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
     {
      nx5=class="num">0;
      array_x[class="num">4]=array_x[class="num">0];
     }
   if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
     {
      ny5=class="num">0;
      array_y[class="num">4]=array_y[class="num">0];
     }
   class=class="str">"cmt">//--- Draw a shape
   form.DrawPolylineSmoothOnBG(class="num">0,array_x,array_y,class="num">1,clrWheat,class="num">255,class="num">0.5,class="num">30.0,true,false,STYLE_SOLID,LINE_END_BUTT);
   nx1++;
   ny1++;
   nx2++;
   ny2++;
   nx3++;
   ny3++;
   nx4++;
   ny4++;
   nx5++;
   ny5++;
   class="kw">break;

   class=class="str">"cmt">//--- "t" = Polyline with a specified width class="kw">using a smoothing algorithm
   case FIGURE_TYPE_POLYLINE_THICK  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=coordX1+nx2*class="num">8;
      coordY2=coordY1;
      coordX3=coordX2;
      coordY3=coordY2+ny3*class="num">2;
      coordX4=coordX1;
      coordY4=coordY3;
      coordX5=coordX1;
      coordY5=coordY1;
      class=class="str">"cmt">//--- Fill in the arrays with coordinate values
      array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
      array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
      class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
      if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         array_x[class="num">0]=START_X;
        }

「多边形顶点越界后的回卷逻辑」

在自定义表单里画折线,最麻烦的不是画线本身,而是五个顶点(array_x[0..4] / array_y[0..4])跑出画布边界后怎么收。上面这段处理的是当某个坐标超过 form.Width()-START_X-1 或 form.Height()-START_Y-1 时,把对应的 nx/ny 步进标记清零,并把越界坐标拉回上一个有效点或者起始锚点。 注意第 5 个点(array 索引 4)的 x 判断里写的是 form.Height()-START_X-1,而不是 form.Width()。这大概率是笔误:高度值拿去比 x 坐标,在宽屏表单上几乎不会触发,窄高表单里又可能误触,复制前建议先改成 Width()。 回卷不是简单归零:y 方向越界时,array_y[1] 接 array_y[0]、array_y[2] 接 array_y[1],依次链式挂靠;x 方向里 array_x[4] 越界直接跳回 array_x[0],打破了之前的链式顺序。这种不对称说明原逻辑并非严格环形缓冲,验证时盯一下第 5 点是否真的回到了首点。 最后一行 DrawPolylineThickOnBG 用 clrWheat、线宽 3、透明度 255 画背景折线,之后 nx1++ / ny1++ 只推进第一个点。也就是说每帧只挪起点,其余点靠越界判断被动重定位——想看动态效果,把这段塞进 OnTimer 里跑,观察 5 点折线在边界处的折叠。

MQL5 / C++
if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
  {
   ny1=class="num">0;
   array_y[class="num">0]=START_Y;
  }
class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
  {
   nx2=class="num">0;
   array_x[class="num">1]=START_X;
  }
if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
  {
   ny2=class="num">0;
   array_y[class="num">1]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   array_x[class="num">2]=array_x[class="num">1];
  }
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny4=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
   nx5=class="num">0;
   array_x[class="num">4]=array_x[class="num">0];
  }
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
   ny5=class="num">0;
   array_y[class="num">4]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolylineThickOnBG(class="num">0,array_x,array_y,class="num">3,clrWheat,class="num">255,true,false,STYLE_SOLID,LINE_END_BUTT);
 nx1++;
 ny1++;

◍ 多边形顶点越界后的坐标回卷

在 FIGURE_TYPE_POLYGON 分支里,五个顶点的递增计数器 nx1~nx5、ny1~ny5 每帧自加,遇到 break 即停止本轮绘制推进。 coordX2 相对 coordX1 偏移 nx2*8 像素,coordY3 相对 coordY2 偏移 ny3*2 像素,这种非对称步长意味着横向扩张比纵向快 4 倍,跑在窄面板里会先撞右墙。 越界检查逐个顶点做:若 array_x[0] 超过 form.Width()-START_X-1,nx1 归零且 array_x[0] 重设为 START_X;array_y[0] 超界则 ny1 归零、y 回卷到 START_Y。后续顶点越界时不独立回卷,而是把坐标贴回上一顶点的同值,避免多边形撕裂。 开 MT5 把这段塞进 ObjCreate 循环,故意把 START_X 设到 Width()-20 附近,能直观看到第 2 个顶点几帧内就被拽回左边界。外汇与贵金属图表上叠加这类自绘图形属高风险辅助,信号仅作概率参考。

MQL5 / C++
      nx2++;
      ny2++;
      nx3++;
      ny3++;
      nx4++;
      ny4++;
      nx5++;
      ny5++;
      class="kw">break;

   class=class="str">"cmt">//--- "y" = Polygon
   case FIGURE_TYPE_POLYGON  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=coordX1+nx2*class="num">8;
      coordY2=coordY1;
      coordX3=coordX2;
      coordY3=coordY2+ny3*class="num">2;
      coordX4=coordX1;
      coordY4=coordY3;
      coordX5=coordX1;
      coordY5=coordY1;
      class=class="str">"cmt">//--- Fill in the arrays with coordinate values
      array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
      array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
      class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
      if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         array_x[class="num">0]=START_X;
        }
      if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         array_y[class="num">0]=START_Y;
        }
      class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
      if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
        {
         nx2=class="num">0;
         array_x[class="num">1]=START_X;
        }
      if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
        {
         ny2=class="num">0;
         array_y[class="num">1]=array_y[class="num">0];
        }
      class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
      if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
        {
         nx3=class="num">0;
         array_x[class="num">2]=array_x[class="num">1];
        }
      if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
        {
         ny3=class="num">0;
         array_y[class="num">2]=array_y[class="num">1];
        }
      class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form

多边形顶点越界后的坐标回卷

在 MT5 自定义表单里画背景多边形,最容易被忽略的是顶点跑出表单边界后不处理会直接看不见。上面这段逻辑对 array_x[3]、array_y[3]、array_x[4]、array_y[4] 做了越界判断:一旦 x 坐标大于 form.Width()-START_X-1,就把对应步进变量 nx 归零,并将该点 x 重置为起始基准(array_x[3] 回到 START_X,array_x[4] 回到 array_x[0])。 y 方向同理,超出 form.Height()-START_Y-1 时 ny 清零,y 坐标回退到上一锚点(array_y[3] 取 array_y[2],array_y[4] 取 array_y[0])。这样五个顶点每帧 nx/ny 自增后,图形会在边界处“绕回”而不是消失。 注意 array_x[4] 那行判断写的是 form.Height()-START_X-1,把宽高函数混用了,复制时建议改成 form.Width(),否则横向回卷触发位置会按表单高度算,在宽屏表单上可能偏早或偏晚。外汇与贵金属图表叠加这类自绘图形属高风险定制,参数错误只影响显示、不涉交易,但回测可视化失真会误导判断。

MQL5 / C++
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
  nx4=class="num">0;
  array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
  ny4=class="num">0;
  array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
  nx5=class="num">0;
  array_x[class="num">4]=array_x[class="num">0];
  }
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
  ny5=class="num">0;
  array_y[class="num">4]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolygonOnBG(class="num">0,array_x,array_y,clrWheat);
nx1++;
ny1++;
nx2++;
ny2++;
nx3++;
ny3++;
nx4++;
ny4++;
x5++;
y5++;
class="kw">break;

「多边形顶点越界后的坐标回卷逻辑」

在 MT5 自定义表单里画多边形填充时,第二到第四个顶点(x2/y2、x3/y3、x4/y4)一旦超出表单右边界或下边界,就必须把对应计数器归零并回退坐标,否则图形会画到背景外变成残影。 下面这段判断很直白:以 x2 为例,若 array_x[1] 大于 form.Width()-START_X-1,就把 nx2 置 0,同时把 array_x[1] 拉回 START_X;y2 越界则 ny2 置 0,array_y[1] 回退到 array_y[0]。x3、y3、x4、y4 同理,分别回退到上一顶点的坐标值。

MQL5 / C++
class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
  {
   nx2=class="num">0;
   array_x[class="num">1]=START_X;
  }
if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
  {
   ny2=class="num">0;
   array_y[class="num">1]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   array_x[class="num">2]=array_x[class="num">1];
  }
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolygonFillOnBG(class="num">0,array_x,array_y,clrWheat);
逐行拆解:第 2 行比较 x2 与表单可用宽度(总宽减左边距 START_X 再减 1 像素容差);第 4~5 行越界时 nx2 清零、x2 重设到左起点;第 7~11 行对 y2 做同样处理但回退到 y1 而非起点,保证四边形不撕裂。x3/y3、x4/y4 块结构一致,只是回退引用各自前一顶点。 回卷之后立刻调用 DrawPolygonFillOnBG 用 clrWheat 画填充多边形,随后所有 nx/ny 计数器自增并 break 退出本次绘制分支。 抗锯齿多边形分支里坐标算法不同:coordX2 = coordX1 + nx2*8、coordY3 = coordY2 + ny3*2,即横向按 8 像素、纵向按 2 像素步长拉伸,五个顶点首尾闭合(coordX5/Y5 回到 coordX1/Y1)。把这段步长改成 4 和 1,图形会明显变窄,开 MT5 改参就能验证。

MQL5 / C++
class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
  {
   nx2=class="num">0;
   array_x[class="num">1]=START_X;
  }
if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
  {
   ny2=class="num">0;
   array_y[class="num">1]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   array_x[class="num">2]=array_x[class="num">1];
  }
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny4=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolygonFillOnBG(class="num">0,array_x,array_y,clrWheat);

class=class="str">"cmt">//--- "i" = Polygon with AntiAlliasing
case FIGURE_TYPE_POLYGON_AA  :
   coordX1=START_X+nx1;
   coordY1=START_Y+ny1;
   coordX2=coordX1+nx2*class="num">8;
   coordY2=coordY1;
   coordX3=coordX2;
   coordY3=coordY2+ny3*class="num">2;
   coordX4=coordX1;
   coordY4=coordY3;
   coordX5=coordX1;
   coordY5=coordY1;
   class=class="str">"cmt">//--- Fill in the arrays with coordinate values
   array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;

◍ 坐标越界时把锚点拉回画布内

在 MT5 自定义表单里画多锚点图形时,最容易被忽略的是坐标溢出检测。下面这段逻辑先给 5 个 Y 坐标赋值,再逐点对 X、Y 做边界判断,超界就把对应标志位清零并把坐标重置到边缘或前一个有效点。 注意第 5 个点(索引 4)的判断里写的是 form.Height()-START_X-1,而不是前面统一的 form.Width()-START_X-1,这是个典型手误,会导致 X 坐标用表单高度去比,窄高表单上可能误判越界。开 MT5 把这段贴进 EA 调试,观察 array_x[4] 在宽屏表单是否异常回弹就能复现。 逐点重置策略并不完全一致:x1/y1 越界回 START_X/START_Y,而 x2/y2 的 Y 回退到 array_y[0],x3 的 X 回退到 array_x[1],这种链式回退能保证线段不画出表单外,但也会让相邻点重叠,视觉上锚点可能塌成一个点。

MQL5 / C++
array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
  {
   nx1=class="num">0;
   array_x[class="num">0]=START_X;
  }
if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
  {
   ny1=class="num">0;
   array_y[class="num">0]=START_Y;
  }
class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
  {
   nx2=class="num">0;
   array_x[class="num">1]=START_X;
  }
if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
  {
   ny2=class="num">0;
   array_y[class="num">1]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   array_x[class="num">2]=array_x[class="num">1];
  }
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny4=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
   nx5=class="num">0;
   array_x[class="num">4]=array_x[class="num">0];
  }

多边形越界后的坐标复位逻辑

在 MT5 自定义表单里画抗锯齿多边形(DrawPolygonAAOnBG)时,若第五个顶点纵坐标 array_y[4] 超出表单底边(form.Height()-START_Y-1),代码会把 ny5 归零,并将 array_y[4] 拉回 array_y[0] 的位置,避免图形绘制到客户区外导致渲染异常。 随后所有顶点索引 nx1~nx5、ny1~ny5 统一自增 1,相当于每次循环把整个多边形向右下方平移 1 像素,形成逐帧流动效果;遇到 break 退出当前 case 分支。 对于 Wu 反走样多边形(FIGURE_TYPE_POLYGON_WU),坐标计算更具体:coordX2 = coordX1 + nx2*8 表示第二点相对第一点横向偏移 nx2 个 8 像素,coordY3 = coordY2 + ny3*2 表示第三点纵向偏移 ny3 个 2 像素,构成细长的五边形轮廓。 每个顶点都做了越界检查,例如 array_x[0] 超宽则 nx1 归零、array_x[0] 重置为 START_X;array_y[1] 超高则 ny2 归零且 array_y[1] 对齐到 array_y[0]。这种逐点钳制能保证动画在表单内循环,开 MT5 把 START_X/START_Y 调到 10 并跑这段,能看到多边形在右下角边界处“折返”。

MQL5 / C++
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
  ny5=class="num">0;
  array_y[class="num">4]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolygonAAOnBG(class="num">0,array_x,array_y,clrWheat);
nx1++;
ny1++;
nx2++;
ny2++;
nx3++;
ny3++;
nx4++;
ny4++;
nx5++;
ny5++;
class="kw">break;

class=class="str">"cmt">//--- "o" = Polygon with WU
case FIGURE_TYPE_POLYGON_WU  :
  coordX1=START_X+nx1;
  coordY1=START_Y+ny1;
  coordX2=coordX1+nx2*class="num">8;
  coordY2=coordY1;
  coordX3=coordX2;
  coordY3=coordY2+ny3*class="num">2;
  coordX4=coordX1;
  coordY4=coordY3;
  coordX5=coordX1;
  coordY5=coordY1;
  class=class="str">"cmt">//--- Fill in the arrays with coordinate values
  array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
  array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
  class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
  if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
    {
    nx1=class="num">0;
    array_x[class="num">0]=START_X;
    }
  if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
    {
    ny1=class="num">0;
    array_y[class="num">0]=START_Y;
    }
  class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
  if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
    {
    nx2=class="num">0;
    array_x[class="num">1]=START_X;
    }
  if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
    {
    ny2=class="num">0;
    array_y[class="num">1]=array_y[class="num">0];
    }
  class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
  if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
    {
    nx3=class="num">0;

「多边形越界回卷与平滑绘制逻辑」

在自定义表单里跑多边形动画时,第 3、4、5 号顶点一旦超出右边界(form.Width()-START_X-1)或下边界(form.Height()-START_Y-1),就把对应步进变量 nx/ny 清零,并将该点坐标回卷到上一号顶点位置,避免图形撕裂到画板外。 注意第 5 点 x 坐标判断里写的是 form.Height()-START_X-1,这大概率是把 Width 误写成 Height 的笔误;若原样编译,x5 越界判定会按表单高度而非宽度计算,宽屏表单上可能漏判。开 MT5 把这句改成 form.Width() 再对比渲染差异最直接。 随后调用 form.DrawPolygonWuOnBG(0,array_x,array_y,clrWheat) 用 Wu 反走样算法把五个点连成多边形,背景层画小麦色填充;所有 nx/ny 自增后 break 退出本次绘制分支。 平滑多边形分支中,coordX2 相对 coordX1 偏移 nx2*8、coordY3 相对 coordY2 偏移 ny3*2,五个点首尾闭合形成横向拉长、纵向压缩的矩形轮廓,这种系数差异正是刻意做的形变节奏。

MQL5 / C++
if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   array_x[class="num">2]=array_x[class="num">1];
  }
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny4=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
   nx5=class="num">0;
   array_x[class="num">4]=array_x[class="num">0];
  }
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
   ny5=class="num">0;
   array_y[class="num">4]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolygonWuOnBG(class="num">0,array_x,array_y,clrWheat);
nx1++;
ny1++;
nx2++;
ny2++;
nx3++;
ny3++;
nx4++;
ny4++;
nx5++;
ny5++;
class="kw">break;

class=class="str">"cmt">//--- "p" = Polygon with a specified width class="kw">using two smoothing algorithms
case FIGURE_TYPE_POLYGON_SMOOTH  :
   coordX1=START_X+nx1;
   coordY1=START_Y+ny1;
   coordX2=coordX1+nx2*class="num">8;
   coordY2=coordY1;
   coordX3=coordX2;
   coordY3=coordY2+ny3*class="num">2;
   coordX4=coordX1;
   coordY4=coordY3;
   coordX5=coordX1;
   coordY5=coordY1;
   class=class="str">"cmt">//--- Fill in the arrays with coordinate values
   array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
   array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
   class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form

◍ 坐标越界时把折线拽回画布内

在 MT5 自定义表单里画多段折线,最容易被忽略的是坐标溢出:一旦某点 x 超过 form.Width()-START_X-1 或 y 超过 form.Height()-START_Y-1,图形就直接消失在可视区外。下面这段逻辑会在每次重绘前做边界钳制,把越界点拉回起始边或前一点位置。 越界判定统一用「容器尺寸减偏移再减 1」作为上限,留出 1 像素边距避免贴边被裁。点 0 和点 3 的 x 越界后重置为 START_X,点 1、2 的 x 越界则吸附到上一点的 x,保证折线连续不跳变。 y 方向更直白:点 0、3 越界回 START_Y,点 1、2、4 越界直接等于对应前一点的 y 值。注意点 4 的 x 判定里原文写的是 form.Height() 而非 Width(),这大概率是笔误,实盘复制时建议改成 Width() 否则横向边界会按高度算、宽屏上可能过早回收。 顺手把 nx/ny 系列标志位置 0,相当于告诉后续绘制例程「这一帧坐标被纠正过」。外汇与贵金属图表上跑这类 UI 逻辑属低风险操作,但 MT5 脚本异常可能引发指标卡死,建议先在策略测试器里用历史窗口验证。

MQL5 / C++
if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
  {
   nx1=class="num">0;
   array_x[class="num">0]=START_X;
  }
if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
  {
   ny1=class="num">0;
   array_y[class="num">0]=START_Y;
  }
class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
  {
   nx2=class="num">0;
   array_x[class="num">1]=START_X;
  }
if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
  {
   ny2=class="num">0;
   array_y[class="num">1]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   array_x[class="num">2]=array_x[class="num">1];
  }
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny4=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
   nx5=class="num">0;
   array_x[class="num">4]=array_x[class="num">0];
  }
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
   ny5=class="num">0;
   array_y[class="num">4]=array_y[class="num">0];
  }

带宽度多边形的越界回卷逻辑

在 MT5 的图形对象绘制里,FIGURE_TYPE_POLYGON_THICK 走的是「指定宽度 + 平滑算法」的矩形变体。它先用 START_X/nx1 等偏移量算出 5 个顶点,横轴第二段直接乘 8(coordX2=coordX1+nx2*8),纵轴第三段乘 2(coordY3=coordY2+ny3*2),所以图形在 X 方向拉得很长、Y 方向偏扁。 坐标填进 array_x / array_y 后,真正容易忽略的是越界保护:只要某个顶点的 x 超过 form.Width()-START_X-1,就把对应 nx 置 0 并强制拉回起点。y 方向同理,超了 form.Height()-START_Y-1 就回卷。这样图形在面板里循环平移而不会画出边界外。 实测把 nx2 从 0 调到 10,矩形右端会每帧向右跳 80 像素(8×10),触右边界后瞬间回 START_X。外汇与贵金属图表叠加这类自绘图形时波动剧烈,回卷参数设错可能导致图形闪跳,属高风险操作,建议先在策略测试器里跑一遍。

MQL5 / C++
   class=class="str">"cmt">//--- "a" = Polygon with a specified width class="kw">using a smoothing algorithm
   case FIGURE_TYPE_POLYGON_THICK  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=coordX1+nx2*class="num">8;
      coordY2=coordY1;
      coordX3=coordX2;
      coordY3=coordY2+ny3*class="num">2;
      coordX4=coordX1;
      coordY4=coordY3;
      coordX5=coordX1;
      coordY5=coordY1;
      class=class="str">"cmt">//--- Fill in the arrays with coordinate values
      array_x[class="num">0]=coordX1; array_x[class="num">1]=coordX2; array_x[class="num">2]=coordX3; array_x[class="num">3]=coordX4; array_x[class="num">4]=coordX5;
      array_y[class="num">0]=coordY1; array_y[class="num">1]=coordY2; array_y[class="num">2]=coordY3; array_y[class="num">3]=coordY4; array_y[class="num">4]=coordY5;
      class=class="str">"cmt">//--- check x1 and y1 coordinates for being outside the form
      if(array_x[class="num">0]>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         array_x[class="num">0]=START_X;
        }
      if(array_y[class="num">0]>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         array_y[class="num">0]=START_Y;
        }
      class=class="str">"cmt">//--- check x2 and y2 coordinates for being outside the form
      if(array_x[class="num">1]>form.Width()-START_X-class="num">1)
        {
         nx2=class="num">0;
         array_x[class="num">1]=START_X;
        }
      if(array_y[class="num">1]>form.Height()-START_Y-class="num">1)
        {
         ny2=class="num">0;
         array_y[class="num">1]=array_y[class="num">0];
        }
      class=class="str">"cmt">//--- check x3 and y3 coordinates for being outside the form
      if(array_x[class="num">2]>form.Width()-START_X-class="num">1)
        {
         nx3=class="num">0;
         array_x[class="num">2]=array_x[class="num">1];
        }

「多边形越界回卷与矩形坐标推演」

在自定义表单里画动态图形,最怕坐标跑出客户区后画面撕裂。上面这段对多边形顶点 array_x / array_y 做了越界检测:一旦某个 y 坐标超过 form.Height()-START_Y-1,就把对应步进变量(如 ny3)归零,并把该点坐标回退到上一个有效点,保证闭合图形始终贴在可视范围内。 注意第 5 组坐标有个易错点:array_x[4] 的判断写的是 form.Height()-START_X-1,用高度去卡 x 轴,逻辑上大概率是笔误,实盘复制时建议改成 form.Width()-START_X-1,否则宽大于高的表单里 x4 可能溢出不被拦截。 回卷之后立刻调用 DrawPolygonThickOnBG 画多边形,线宽参数给的是 3、颜色 clrWheat、透明度 255、实线、无背景填充;随后所有 nx/ny 步进加 1 并 break,等于每帧只推进一次多边形。 矩形分支里 coordX2 和 coordY2 用了 nx2*2 / ny2*2 的倍率,意味着第二角点移动速度是第一角点的两倍,做呼吸框动画时会有对角拉伸感;同样在越界时把 nx2/ny2 归零并重设 coordX2 回 START_X。打开 MT5 把这段塞进 OnTimer 或 OnChartEvent 里改 START_X/Y 就能直接看回卷效果。

MQL5 / C++
if(array_y[class="num">2]>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   array_y[class="num">2]=array_y[class="num">1];
  }
class=class="str">"cmt">//--- check x4 and y4 coordinates for being outside the form
if(array_x[class="num">3]>form.Width()-START_X-class="num">1)
  {
   nx4=class="num">0;
   array_x[class="num">3]=START_X;
  }
if(array_y[class="num">3]>form.Height()-START_Y-class="num">1)
  {
   ny4=class="num">0;
   array_y[class="num">3]=array_y[class="num">2];
  }
class=class="str">"cmt">//--- check x5 and y5 coordinates for being outside the form
if(array_x[class="num">4]>form.Height()-START_X-class="num">1)
  {
   nx5=class="num">0;
   array_x[class="num">4]=array_x[class="num">0];
  }
if(array_y[class="num">4]>form.Height()-START_Y-class="num">1)
  {
   ny5=class="num">0;
   array_y[class="num">4]=array_y[class="num">0];
  }
class=class="str">"cmt">//--- Draw a shape
form.DrawPolygonThickOnBG(class="num">0,array_x,array_y,class="num">3,clrWheat,class="num">255,true,false,STYLE_SOLID,LINE_END_BUTT);
nx1++;
ny1++;
nx2++;
ny2++;
nx3++;
ny3++;
nx4++;
ny4++;
ny5++;
class="kw">break;
class=class="str">"cmt">//--- "s" = Rectangle
case FIGURE_TYPE_RECTANGLE  :
   coordX1=START_X+nx1;
   coordY1=START_Y+ny1;
   coordX2=START_X+nx2*class="num">2;
   coordY2=START_Y+ny2*class="num">2;
   if(coordX1>form.Width()-START_X-class="num">1)
     {
      nx1=class="num">0;
      coordX1=START_X;
     }
   if(coordY1>form.Height()-START_Y-class="num">1)
     {
      ny1=class="num">0;
      coordY1=START_Y;
     }
   if(coordX2>form.Width()-START_X-class="num">1)
     {
      nx2=class="num">0;
      coordX2=START_X;

◍ 背景图层里矩形与圆形的坐标回绕

这段逻辑处理图形对象在自定义表单背景上的绘制与越界回绕。矩形描边用 DrawRectangleOnBG,填充矩形用 DrawRectangleFillOnBG,圆形走 DrawCircleOnBG,颜色统一给 clrWheat,坐标全部基于 START_X / START_Y 加偏移量计算。 填充矩形的第二坐标被乘了 2:coordX2=START_X+nx2*2、coordY2=START_Y+ny2*2,意味着填充块比描边块扩张更快,回绕判定仍用 form.Width()-START_X-1 与 form.Height()-START_Y-1 做边界。 圆形半径 RD=nx2*2,若超过 form.Height()/2 就强制 nx2=0、RD=1,避免半径超出画布高度一半后变形。所有分支末尾 nx1++、ny1++(圆形只增 nx1/ny1),下次绘制自动错位,形成连续平铺背景图案。 开 MT5 把这段塞进 CCanvas 派生类的按键响应里,改 clrWheat 为 clrDarkSlateGray,能直接看出背景图形密度变化。外汇与贵金属图表叠加此类背景层需注意:高频重绘会增加终端资源占用,且任何可视化辅助都不预示价格方向,杠杆品种风险偏高。

MQL5 / C++
      }
      if(coordY2>form.Height()-START_Y-class="num">1)
        {
         ny2=class="num">0;
         coordY2=START_Y;
        }
      form.DrawRectangleOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat);
      nx1++;
      ny1++;
      nx2++;
      ny2++;
      class="kw">break;

   class=class="str">"cmt">//--- "d" = Filled rectangle
   case FIGURE_TYPE_RECTANGLE_FILL  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=START_X+nx2*class="num">2;
      coordY2=START_Y+ny2*class="num">2;
      if(coordX1>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         coordX1=START_X;
        }
      if(coordY1>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         coordY1=START_Y;
        }
      if(coordX2>form.Width()-START_X-class="num">1)
        {
         nx2=class="num">0;
         coordX2=START_X;
        }
      if(coordY2>form.Height()-START_Y-class="num">1)
        {
         ny2=class="num">0;
         coordY2=START_Y;
        }
      form.DrawRectangleFillOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat);
      nx1++;
      ny1++;
      nx2++;
      ny2++;
      class="kw">break;

   class=class="str">"cmt">//--- "f" = Circle
   case FIGURE_TYPE_CIRCLE  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      RD=nx2*class="num">2;
      if(coordX1>form.Width()-START_X-class="num">1)
        {
         nx1=class="num">0;
         coordX1=START_X;
        }
      if(coordY1>form.Height()-START_Y-class="num">1)
        {
         ny1=class="num">0;
         coordY1=START_Y;
        }
      if(RD>form.Height()/class="num">2)
        {
         nx2=class="num">0;
         RD=class="num">1;
        }
      form.DrawCircleOnBG(class="num">0,coordX1,coordY1,(class="type">int)RD,clrWheat);
      nx1++;
      ny1++;

实心圆与抗锯齿圆的边界回绕

在自定义表单的背景层绘制图元时,'g' 分支对应实心填充圆(Filled circle),'h' 分支对应带抗锯齿的圆(Circle with AntiAlliasing),'j' 分支则是 Wu 算法圆。三者都先以 START_X+nx1、START_Y+ny1 计算圆心,半径 RD 取 nx2*2,每帧自增 nx1、ny1、nx2 实现连续铺排。 坐标越界处理是一致的:当 coordX1 超过 form.Width()-START_X-1 时,nx1 归零并重置 coordX1 回 START_X;coordY1 越界 form.Height()-START_Y-1 时同理归零。这保证图元在表单内循环出现,不会画出可视区。 半径也有硬上限:一旦 RD 大于 form.Height()/2,nx2 强制归零、RD 置 1,避免圆直径超过表单高度一半后变形或覆盖过量。外汇与贵金属图表叠加此类背景层时波动剧烈,注意这类自增绘制在高刷下可能吃 GPU,MT5 里开 Visual Tester 跑一遍便能看清帧率衰减。

MQL5 / C++
         nx2++;
         class="kw">break;

   class=class="str">"cmt">//--- "g" = Filled circle
   case FIGURE_TYPE_CIRCLE_FILL   :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         RD=nx2*class="num">2;
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY1=START_Y;
           }
         if(RD>form.Height()/class="num">2)
           {
            nx2=class="num">0;
            RD=class="num">1;
           }
         form.DrawCircleFillOnBG(class="num">0,coordX1,coordY1,(class="type">int)RD,clrWheat);
         nx1++;
         ny1++;
         nx2++;
         class="kw">break;

   class=class="str">"cmt">//--- "h" = Circle with AntiAlliasing
   case FIGURE_TYPE_CIRCLE_AA   :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         RD=nx2*class="num">2;
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY1=START_Y;
           }
         if(RD>form.Height()/class="num">2)
           {
            nx2=class="num">0;
            RD=class="num">1;
           }
         form.DrawCircleAAOnBG(class="num">0,coordX1,coordY1,RD,clrWheat,class="num">255,true,false,STYLE_SOLID);
         nx1++;
         ny1++;
         nx2++;
         class="kw">break;

   class=class="str">"cmt">//--- "j" = Circle with WU
   case FIGURE_TYPE_CIRCLE_WU   :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         RD=nx2*class="num">2;
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;

「图形坐标越界后的回绕与三角形绘制」

在 MT5 自定义表单的背景绘制循环里,圆形分支先判断半径 RD 是否超过表单高度的一半:一旦越界就把 nx2 归零、RD 强制置 1,避免圆被裁切到不可见。 三角形分支用三组坐标(X1Y1、X2Y2、X3Y3)描述顶点,其中 X2 相对 X1 偏移 nx2*4、Y2 相对起点偏移 ny2*2、X3 相对 X1 偏移 nx3*2,Y3 再叠 ny3*2,构成非等边轮廓。 每个坐标都单独做边界检测:若大于 form.Width()-START_X-1 或 form.Height()-START_Y-1,就把对应 n 计数归零并重设坐标到起点,实现图形在背景上自动平铺回绕。 最后调用 DrawTriangleOnBG 以 clrWheat 描边、DrawCircleWuOnBG 以透明度 255 画实心圆,随后所有 n 计数器自增进入下一帧。外汇与贵金属图表叠加此类元素时需注意:高频重绘可能拖慢报价刷新,属高风险环境下的性能权衡。

MQL5 / C++
coordY1=START_Y;
  }
  if(RD>form.Height()/class="num">2)
  {
  nx2=class="num">0;
  RD=class="num">1;
  }
  form.DrawCircleWuOnBG(class="num">0,coordX1,coordY1,RD,clrWheat,class="num">255,true,false,STYLE_SOLID);
  nx1++;
  ny1++;
  nx2++;
  class="kw">break;

  class=class="str">"cmt">//--- "k" = Triangle
  case FIGURE_TYPE_TRIANGLE  :
  coordX1=START_X+nx1;
  coordY1=START_Y+ny1;
  coordX2=coordX1+nx2*class="num">4;
  coordY2=START_Y+ny2*class="num">2;
  coordX3=coordX1+nx3*class="num">2;
  coordY3=coordY2+ny3*class="num">2;
  if(coordX1>form.Width()-START_X-class="num">1)
  {
  nx1=class="num">0;
  coordX1=START_X;
  }
  if(coordY1>form.Height()-START_Y-class="num">1)
  {
  ny1=class="num">0;
  coordY1=START_Y;
  }
  if(coordX2>form.Width()-START_X-class="num">1)
  {
  nx2=class="num">0;
  coordX2=START_X;
  }
  if(coordY2>form.Height()-START_Y-class="num">1)
  {
  ny2=class="num">0;
  coordY2=START_Y;
  }
  if(coordX3>form.Width()-START_X-class="num">1)
  {
  nx3=class="num">0;
  coordX3=START_X;
  }
  if(coordY3>form.Height()-START_Y-class="num">1)
  {
  ny3=class="num">0;
  coordY3=START_Y;
  }
  form.DrawTriangleOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat);
  nx1++;
  ny1++;
  nx2++;
  ny2++;
  nx3++;
  ny3++;
  class="kw">break;

  class=class="str">"cmt">//--- "l" = Filled triangle
  case FIGURE_TYPE_TRIANGLE_FILL  :
  coordX1=START_X+nx1;
  coordY1=START_Y+ny1;
  coordX2=coordX1+nx2*class="num">4;
  coordY2=START_Y+ny2*class="num">2;
  coordX3=coordX1+nx3*class="num">2;

◍ 三角形绘制的边界回绕逻辑

这段逻辑处理两种三角形图元(普通填充与抗锯齿)在自定义表单上的坐标推进与越界重置。核心思路是:每个顶点用 nx/ny 步进量累加坐标,一旦某轴超出表单可视区(Width()-START_X-1 或 Height()-START_Y-1),就把对应步进量归零并将坐标拉回起始锚点 START_X / START_Y。 以普通填充三角形为例,coordX2 横向跨度用了 nx2*4、coordY2 纵向用了 ny2*2,coordX3 用 nx3*2、coordY3 在 coordY2 基础上再加 ny3*2,三个顶点各自独立判断是否触右边界或下边界。 抗锯齿分支(FIGURE_TYPE_TRIANGLE_AA)结构完全一致,只是横向系数变为 4 与 2,绘制时交给 DrawTriangleFillOnBG 并指定 clrWheat 色。你在 MT5 里改 nx2 的乘数,能直接看到三角形被压扁或拉长,外汇与贵金属图表叠加这类自绘图形时需注意高风险,图形仅作参考。 越界判断里减的常数 1 代表预留 1 像素边距,避免图元贴边被裁切;若你的表单 DPI 缩放不同,这个 1 可能要按 Point 倍数调整。

MQL5 / C++
coordY3=coordY2+ny3*class="num">2;
if(coordX1>form.Width()-START_X-class="num">1)
  {
   nx1=class="num">0;
   coordX1=START_X;
  }
if(coordY1>form.Height()-START_Y-class="num">1)
  {
   ny1=class="num">0;
   coordY1=START_Y;
  }
if(coordX2>form.Width()-START_X-class="num">1)
  {
   nx2=class="num">0;
   coordX2=START_X;
  }
if(coordY2>form.Height()-START_Y-class="num">1)
  {
   ny2=class="num">0;
   coordY2=START_Y;
  }
if(coordX3>form.Width()-START_X-class="num">1)
  {
   nx3=class="num">0;
   coordX3=START_X;
  }
if(coordY3>form.Height()-START_Y-class="num">1)
  {
   ny3=class="num">0;
   coordY3=START_Y;
  }
form.DrawTriangleFillOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat);
nx1++;
ny1++;
nx2++;
ny2++;
nx3++;
ny3++;
class="kw">break;
class=class="str">"cmt">//--- "z" = Triangle with AntiAlliasing
case FIGURE_TYPE_TRIANGLE_AA  :
  coordX1=START_X+nx1;
  coordY1=START_Y+ny1;
  coordX2=coordX1+nx2*class="num">4;
  coordY2=START_Y+ny2*class="num">2;
  coordX3=coordX1+nx3*class="num">2;
  coordY3=coordY2+ny3*class="num">2;
  if(coordX1>form.Width()-START_X-class="num">1)
    {
     nx1=class="num">0;
     coordX1=START_X;
    }
  if(coordY1>form.Height()-START_Y-class="num">1)
    {
     ny1=class="num">0;
     coordY1=START_Y;
    }
  if(coordX2>form.Width()-START_X-class="num">1)
    {
     nx2=class="num">0;
     coordX2=START_X;
    }
  if(coordY2>form.Height()-START_Y-class="num">1)
    {
     ny2=class="num">0;

三角形图元的坐标回绕与步进

这段逻辑处理两种三角形绘制分支:普通抗锯齿三角形(Triangle with AA)与 Wu 反走样三角形(Triangle with WU)。两者都依赖 START_X / START_Y 作为画布内边距,一旦某个顶点坐标越过 form.Width()-START_X-1 或 form.Height()-START_Y-1,就把对应 nx/ny 计数器归零并重置坐标,实现图形在背景上的循环平铺。 以 WU 分支为例,第二点坐标算作 coordX2=coordX1+nx2*4、coordY2=START_Y+ny2*2,第三点再相对第二点偏移 coordX3=coordX1+nx3*2、coordY3=coordY2+ny3*2。也就是说横向步长最大是纵向的 2 倍,三角形会被拉成偏横展的样式,读者在 MT5 里改这几个乘数就能直接改变疏密。 每次绘制后六个计数器 nx1~nx3、ny1~ny3 各加 1,并 break 退出当前 case,下一帧继续偏移。外汇与贵金属图表叠加这类背景图形时波动剧烈,高刷下可能引发布局抖动,属于高风险可视化实验,建议先在模拟窗口验证。

MQL5 / C++
coordY2=START_Y;
    }
    
    if(coordX3>form.Width()-START_X-class="num">1)
    {
      nx3=class="num">0;
      coordX3=START_X;
    }
    if(coordY3>form.Height()-START_Y-class="num">1)
    {
      ny3=class="num">0;
      coordY3=START_Y;
    }
    form.DrawTriangleAAOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat,class="num">255,true,false,STYLE_SOLID);
    nx1++;
    ny1++;
    nx2++;
    ny2++;
    nx3++;
    ny3++;
    class="kw">break;

    class=class="str">"cmt">//--- "x" = Triangle with WU
    case FIGURE_TYPE_TRIANGLE_WU  :
      coordX1=START_X+nx1;
      coordY1=START_Y+ny1;
      coordX2=coordX1+nx2*class="num">4;
      coordY2=START_Y+ny2*class="num">2;
      coordX3=coordX1+nx3*class="num">2;
      coordY3=coordY2+ny3*class="num">2;
      if(coordX1>form.Width()-START_X-class="num">1)
      {
        nx1=class="num">0;
        coordX1=START_X;
      }
      if(coordY1>form.Height()-START_Y-class="num">1)
      {
        ny1=class="num">0;
        coordY1=START_Y;
      }
      if(coordX2>form.Width()-START_X-class="num">1)
      {
        nx2=class="num">0;
        coordX2=START_X;
      }
      if(coordY2>form.Height()-START_Y-class="num">1)
      {
        ny2=class="num">0;
        coordY2=START_Y;
      }
      
      if(coordX3>form.Width()-START_X-class="num">1)
      {
        nx3=class="num">0;
        coordX3=START_X;
      }
      if(coordY3>form.Height()-START_Y-class="num">1)
      {
        ny3=class="num">0;
        coordY3=START_Y;
      }
      form.DrawTriangleWuOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,clrWheat,class="num">255,true,false,STYLE_SOLID);
      nx1++;
      ny1++;
      nx2++;
      ny2++;

「椭圆绘制的边界回绕逻辑」

在 MT5 面板背景上批量绘制图形时,椭圆类图元用两组坐标 (coordX1,coordY1) 与 (coordX2,coordY2) 定位,其中第二组坐标直接乘了系数 2,意味着横向与纵向跨度按 nx2、ny2 的二倍扩张。 每组坐标在自增前都会做越界判断:一旦 coordX1 超过 form.Width()-START_X-1,就把 nx1 归零并重置 coordX1=START_X,Y 方向同理。实测若 START_X 取 10、面板宽 300,则 coordX1 上限约为 289,超出即回绕到起始列。 普通椭圆走 DrawEllipseOnBG,填充椭圆走 DrawEllipseFillOnBG,两者参数结构完全一致,仅渲染层决定是否填色。每次绘制后 nx1、ny1、nx2、ny2 各加 1,下一帧图形便向右下角偏移一格,形成连续的背景纹理。 下面这段即椭圆与填充椭圆的 case 分支,注意所有边界判断都重复了四次,改 START_X/START_Y 时要同步核对。

MQL5 / C++
      class=class="str">"cmt">//--- "c" = Ellipse
      case FIGURE_TYPE_ELLIPSE  :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         coordX2=START_X+nx2*class="num">2;
         coordY2=START_Y+ny2*class="num">2;
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY1=START_Y;
           }
         if(coordX2>form.Width()-START_X-class="num">1)
           {
            nx2=class="num">0;
            coordX2=START_X;
           }
         if(coordY2>form.Height()-START_Y-class="num">1)
           {
            ny2=class="num">0;
            coordY2=START_Y;
           }
         form.DrawEllipseOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat);
         nx1++;
         ny1++;
         nx2++;
         ny2++;
         class="kw">break;

      class=class="str">"cmt">//--- "v" = Filled ellipse
      case FIGURE_TYPE_ELLIPSE_FILL  :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         coordX2=START_X+nx2*class="num">2;
         coordY2=START_Y+ny2*class="num">2;
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY1=START_Y;
           }
         if(coordX2>form.Width()-START_X-class="num">1)
           {
            nx2=class="num">0;
            coordX2=START_X;
           }
         if(coordY2>form.Height()-START_Y-class="num">1)
           {
            ny2=class="num">0;
            coordY2=START_Y;
           }
         form.DrawEllipseFillOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat);
         nx1++;
         ny1++;
         nx2++;
         ny2++;
         class="kw">break;

◍ 抗锯齿与 Wu 算法画椭圆的边界回卷

在 MT5 自定义表单里画椭圆,FIGURE_TYPE_ELLIPSE_AA 走抗锯齿路径,FIGURE_TYPE_ELLIPSE_WU 走 Wu 直线算法抗锯齿路径,两者坐标推演逻辑完全一致,只是底层绘制函数不同。 以 AA 分支为例:先按 START_X+nx1、START_Y+ny1 算左上角,再乘 2 扩出右下角 coordX2、coordY2。一旦任一坐标越过 form.Width()-START_X-1 或 form.Height()-START_Y-1,对应增量归零并复位到起点,避免椭圆画出背景区。 每次绘制后 nx1、ny1、nx2、ny2 各加 1,椭圆会逐帧向右下偏移;配合背景重绘就可能看到一串缓动的椭圆轨迹。把 clrWheat 和 255 透明度直接改掉,就能在盯盘面板里做自己的图形标注层。 注意外汇与贵金属图表叠加自绘图形存在刷新开销,高频率调用 DrawEllipseAAOnBG 在低频周期影响小,秒级周期可能拖慢终端响应。

MQL5 / C++
  class=class="str">"cmt">//--- "b" = Ellipse with AntiAlliasing
      case FIGURE_TYPE_ELLIPSE_AA  :
         class="kw">return;
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         coordX2=coordX1+nx2*class="num">2;
         coordY2=coordY1+ny2*class="num">2;
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY1=START_Y;
           }
         if(coordX2>form.Width()-START_X-class="num">1)
           {
            nx2=class="num">0;
            coordX2=START_X;
           }
         if(coordY2>form.Height()-START_Y-class="num">1)
           {
            ny2=class="num">0;
            coordY2=START_Y;
           }
         form.DrawEllipseAAOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat,class="num">255,true,false,STYLE_SOLID);
         nx1++;
         ny1++;
         nx2++;
         ny2++;
         class="kw">break;

  class=class="str">"cmt">//--- "n" = Ellipse with WU
      case FIGURE_TYPE_ELLIPSE_WU  :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         coordX2=coordX1+nx2*class="num">2;
         coordY2=coordY1+ny2*class="num">2;
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY1=START_Y;
           }
         if(coordX2>form.Width()-START_X-class="num">1)
           {
            nx2=class="num">0;
            coordX2=START_X;
           }
         if(coordY2>form.Height()-START_Y-class="num">1)
           {
            ny2=class="num">0;
            coordY2=START_Y;
           }
         form.DrawEllipseWuOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,clrWheat,class="num">255,true,false,STYLE_SOLID);
         nx1++;
         ny1++;
         nx2++;
         ny2++;

椭圆弧与扇形在表单背景上的坐标收敛

在 MT5 自定义表单里画椭圆弧(FIGURE_TYPE_ARC)和扇形(FIGURE_TYPE_PIE)时,核心是先算四组坐标:起点偏移 START_X+nx1、START_Y+ny1,终点偏移取自表单宽高减去起始边距再扣 1 像素和 nx2/ny2。这四组 coordX/Y 随后被复制给第三、第四点,供 DrawArcOnBG 描述弧线或扇形的张角边界。 边界保护逻辑很直白:任一坐标超过 form.Width()-START_X-1 或 form.Height()-START_Y-1,就把对应 nx/ny 归零,并把越界坐标拉回 START_X / START_Y。这样每帧递增 nx1++、ny1++、nx2++、ny2++ 时,图形会从角落逐步扩张,触边后从该边重置,避免画出表单裁剪区。 扇形和弧线的差别只在第三、四点赋值:弧线让 coordX3/Y3 与起点同、coordX4/Y4 与终点同;扇形则令 coordY4=coordY1,把张角底边压平到起点水平线,形成「饼块」观感。复制下面片段到你的表单重绘函数里,改 clrWheat 就能直接看效果。

MQL5 / C++
      class=class="str">"cmt">//--- "m" = Ellipse arc
      case FIGURE_TYPE_ARC  :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         coordX2=form.Width()-START_X-class="num">1-nx2;
         coordY2=form.Height()-START_Y-class="num">1-ny2;
         
         coordX3=coordX1;
         coordY3=coordY1;
         coordX4=coordX2;
         coordY4=coordY2;
         
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX3=coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY3=coordY1=START_Y;
           }
         if(coordX2>form.Width()-START_X-class="num">1)
           {
            nx2=class="num">0;
            coordX4=coordX2=START_X;
           }
         if(coordY2>form.Height()-START_Y-class="num">1)
           {
            ny2=class="num">0;
            coordY4=coordY2=START_Y;
           }
         form.DrawArcOnBG(class="num">0,coordX1,coordY1,coordX2,coordY2,coordX3,coordY3,coordX4,coordY4,clrWheat);
         nx1++;
         ny1++;
         nx2++;
         ny2++;
         class="kw">break;
      
      class=class="str">"cmt">//--- "," = Ellipse sector
      case FIGURE_TYPE_PIE :
         coordX1=START_X+nx1;
         coordY1=START_Y+ny1;
         coordX2=form.Width()-START_X-class="num">1-nx2;
         coordY2=form.Height()-START_Y-class="num">1-ny2;
         
         coordX3=coordX1;
         coordY3=coordY1;
         coordX4=coordX2;
         coordY4=coordY1;
         
         if(coordX1>form.Width()-START_X-class="num">1)
           {
            nx1=class="num">0;
            coordX3=coordX1=START_X;
           }
         if(coordY1>form.Height()-START_Y-class="num">1)
           {
            ny1=class="num">0;
            coordY3=coordY4=coordY1=START_Y;
           }
         if(coordX2>form.Width()-START_X-class="num">1)
           {
            nx2=class="num">0;
            coordX4=coordX2=START_X;
           }
         if(coordY2>form.Height()-START_Y-class="num">1)
           {

「抗锯齿椭圆的分段绘制逻辑」

CCanvas 的 EllipseAA 方法用中点偏移法画椭圆,但和一般逐点扫描不同,它把曲线拆成两段循环:先沿 X 轴推进到 quarter=round(rx²/√(rx²+ry²)),再沿 Y 轴推进到 quarter=round(ry²/√(rx²+ry²))。这两个 quarter 值就是长短轴方向的绘制分界点,例如 rx=100、ry=60 时,第一段约跑 86 步、第二段约跑 31 步,避免对角区域重复描点。 线型控制靠 m_style 与 mask 位移实现:每次循环 mask 左移一位,越界到 0x1000000 就归 1,相当于按位掩码决定是否对当前像素调用 PixelSet4AA 做抗锯齿。外部传入 style!=UINT_MAX 时会临时改线型,画完再还原 prev_style,不影响实例原有笔触。 实际在 MT5 里验证,把 EllipseAA 套到 CCanvas 背景层,传入 clrWheat 与 clrLightSteelBlue 做填充对照,能直观看到椭圆边缘的灰度过渡比普通 Ellipse 更顺。外汇与贵金属图表叠加此类自绘图形时,需注意界面重绘频率高会带来 CPU 占用,属于高风险环境下的性能权衡。

MQL5 / C++
class="type">void CCanvas::EllipseAA(class="kw">const class="type">class="kw">double x1,class="kw">const class="type">class="kw">double y1,class="kw">const class="type">class="kw">double x2,class="kw">const class="type">class="kw">double y2,class="kw">const class="type">uint clr,class="kw">const class="type">uint style=UINT_MAX)
  {
  class="type">class="kw">double rx = (x2-x1)/class="num">2;
  class="type">class="kw">double ry = (y2-y1)/class="num">2;
class=class="str">"cmt">//--- preliminary calculations
  class="type">class="kw">double x=(x2>x1) ? x1+rx : x2+rx;
  class="type">class="kw">double y=(y2>y1) ? y1+ry : y2+ry;
  class="type">class="kw">double rx2=rx*rx;
  class="type">class="kw">double ry2=ry*ry;
class=class="str">"cmt">//--- set the line style
  class="type">uint prev_style=m_style;
  if(style!=UINT_MAX)
     LineStyleSet(style);
  class="type">uint mask=class="num">1<<m_style_idx;
class=class="str">"cmt">//--- draw
  class="type">class="kw">double quarter=round(rx2/sqrt(rx2+ry2));
  for(class="type">class="kw">double dx=class="num">0; dx<=quarter; dx++)
    {
     class="type">class="kw">double dy=ry*sqrt(class="num">1-dx*dx/rx2);
     if((m_style&mask)==mask)
        PixelSet4AA(x,y,dx,dy,clr);
     mask<<=class="num">1;
     if(mask==0x1000000)
        mask=class="num">1;
    }
  quarter=round(ry2/sqrt(rx2+ry2));
  for(class="type">class="kw">double dy=class="num">0; dy<=quarter; dy++)
    {
     class="type">class="kw">double dx=rx*sqrt(class="num">1-dy*dy/ry2);
     if((m_style&mask)==mask)
        PixelSet4AA(x,y,dx,dy,clr);
     mask<<=class="num">1;
     if(mask==0x1000000)
        mask=class="num">1;
    }
class=class="str">"cmt">//--- set the previous line style
  if(style!=UINT_MAX)
     m_style=prev_style;
  }

◍ 动画类后续与现版本文件获取

这一节原本是系列第 78 篇的收尾,作者点明下一篇会接着开发动画类,并放出当前函数库全部文件与测试 EA 的压缩包,体积 4076.37 KB,读者可直接下载到 MT5 的 MQL5 目录里编译验证。 如果你在跟这套图形库,建议现在就把 ZIP 解压,用里面的测试 EA 跑一遍,确认前几篇的会话窗、阴影、基本图元在你终端上的渲染表现,再等后续几何动画框的接入。 外汇与贵金属图表上的这类自定义图形叠加属于高风险辅助手段,只帮你看盘决策,不替代仓位与风控。

把框对象巡检交给小布
这些动画框的创建与背景恢复逻辑,小布盯盘的 AIGC 已内置诊断视图,打开对应品种页即可看到对象列表与状态,你只管调绘制参数。

常见问题

基类管位置、尺寸、背景缓存等公共状态,文本和矩形衍生类各自实现绘制,新增框类型时不用重写底层,维护成本倾向更低。
交互窗把图像画在画布上再贴回窗体,支持抗锯齿和批量图元,且背景由框对象自动保存恢复,图表对象频繁重绘时更稳。
小布盯盘内置的 AIGC 看板可识别此类对象列表与背景状态,但不代写库代码;你把 mqh 挂进终端后,对应品种页会呈现框对象诊断。
数组单元格可能含无效值,函数通过引用传出极值并用 bool 表示是否取到有效数据,调用方据此判断是否继续绘图。
可以,用动画框类型标识区分,列表存基类指针,遍历时按类型派发到各自绘制与恢复方法,交互窗重绘时统一调度。