chenluhua1980
2026-01-08 2be286ac19bf2bb00e27f556e8b2cc292a58bd09
SourceCode/Bond/Servo/EqsGraphWnd.cpp
@@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "EqsGraphWnd.h"
#include "ColorTransfer.h"
#include "MapPosWnd.h"
@@ -35,13 +35,7 @@
   m_hWnd = NULL;
   m_crFrame = GetSysColor(COLOR_WINDOWFRAME);
   m_crBkgnd = RGB(255, 255, 255);
   m_listener.onConnectPin = nullptr;
   m_listener.onCheckConnectPin = nullptr;
   m_listener.onDisconnectPin = nullptr;
   m_listener.onDeleteEqItem = nullptr;
   m_listener.onEqItemPosChanged = nullptr;
   m_listener.onDblckEqItem = nullptr;
   m_listener.onRclickEqItem = nullptr;
   m_listener = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
   m_crItemBackground[0] = RGB(218, 218, 218);
   m_crItemBackground[1] = RGB(193, 208, 227);
   m_crItemFrame[0] = RGB(128, 128, 128);
@@ -70,6 +64,8 @@
   m_nMagneticLinHoz = 0;
   m_nMagneticLinVer = 0;
   m_hFontTitle = nullptr;
   m_nIndicatorSize = 10;
   m_nIndicatorMargin = 3;
}
@@ -92,7 +88,7 @@
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   // 注册窗口类
   // 注册窗口类
   return (::RegisterClass(&wc) != 0);
}
@@ -163,6 +159,20 @@
   m_crItemIdText[0] = CColorTransfer::ApproximateColor(m_crItemNameText[0], -0.3f);
   m_crItemIdText[1] = CColorTransfer::ApproximateColor(m_crItemNameText[1], -0.3f);
}
void CEqsGraphWnd::SetIndicatorSize(int nSize)
{
   if (nSize > 0) {
      m_nIndicatorSize = nSize;
   }
}
void CEqsGraphWnd::SetIndicatorMargin(int nMargin)
{
   if (nMargin >= 0) {
      m_nIndicatorMargin = nMargin;
   }
}
void CEqsGraphWnd::EnableScroll(BOOL bEnable)
@@ -241,7 +251,7 @@
}
/*
 * 计算磁力线位置
 * 计算磁力线位置
 */
void CEqsGraphWnd::CalculateMagneticLine(EQITEM* pItem, LPRECT lprcItemRect, int &hoz, int &ver)
{
@@ -249,7 +259,7 @@
   ver = 0;
#define MAGNETIC_DIS      10
   // 检测是否接近或对齐
   // 检测是否接近或对齐
   for (int i = 0; i < m_arItem.GetSize(); i++) {
      EQITEM *pTemp = (EQITEM*)m_arItem.GetAt(i);
      if (pTemp != pItem) {
@@ -293,10 +303,10 @@
}
/*
 * 取得In Pin的区域
 * 取得In Pin的区域
 * pItem -- EQITEM
 * lpRect -- 得到的Rect
 * 返回是否成功
 * lpRect -- 得到的Rect
 * 返回是否成功
 */
BOOL CEqsGraphWnd::GetItemRect(EQITEM* pItem, LPRECT lpRect)
{
@@ -327,11 +337,11 @@
}
/*
 * 取得In Pin的区域
 * 取得In Pin的区域
 * pItem -- EQITEM
 * nPinIndex -- in pin索引
 * lpRect -- 得到的Rect
 * 返回是否成功
 * nPinIndex -- in pin索引
 * lpRect -- 得到的Rect
 * 返回是否成功
 */
BOOL CEqsGraphWnd::GetInPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect)
{
@@ -351,11 +361,11 @@
}
/*
 * 取得Out Pin的区域
 * 取得Out Pin的区域
 * pItem -- EQITEM
 * nPinIndex -- in pin索引
 * lpRect -- 得到的Rect
 * 返回是否成功
 * nPinIndex -- in pin索引
 * lpRect -- 得到的Rect
 * 返回是否成功
 */
BOOL CEqsGraphWnd::GetOutPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect)
{
@@ -374,11 +384,11 @@
}
/*
 * 取得Pin的Point
 * 取得Pin的Point
 * pItem -- EQITEM
 * nPinIndex -- in pin索引
 * lpRect -- 得到的Rect
 * 返回是否成功
 * nPinIndex -- in pin索引
 * lpRect -- 得到的Rect
 * 返回是否成功
 */
BOOL CEqsGraphWnd::GetPinPoint(PIN *pPin, LPPOINT lpPoint)
{
@@ -459,7 +469,7 @@
}
/*
 * 清空PIN连接线缓存点,以便重新计算和绘制
 * 清空PIN连接线缓存点,以便重新计算和绘制
 */
void CEqsGraphWnd::ClearConnectedLinePoint(EQITEM*& pItem)
{
@@ -492,6 +502,7 @@
   m_listener.onEqItemPosChanged = listener.onEqItemPosChanged;
   m_listener.onDblckEqItem = listener.onDblckEqItem;
   m_listener.onRclickEqItem = listener.onRclickEqItem;
   m_listener.onSelectEqItem = listener.onSelectEqItem;
}
BOOL CEqsGraphWnd::SetCurSel(int nSel)
@@ -560,7 +571,7 @@
 */
EQITEM* CEqsGraphWnd::AddItem(int id, CString strText, DWORD_PTR dwData, int nType/* = ITEM_NORMAL*/)
{
   // 需要计算一个新位置,不然全部重叠在一起
   // 需要计算一个新位置,不然全部重叠在一起
   int x, y;
   x = (m_arItem.GetCount() % 4) * 218;
   y = (m_arItem.GetCount() / 4) * 168;
@@ -760,7 +771,7 @@
   return 0;
}
// 删除Item, 如果pin有连接,注意先断开
// 删除Item, 如果pin有连接,注意先断开
int CEqsGraphWnd::DeleteItem(EQITEM* pItem)
{
   for (int i = 0; i < m_arItem.GetSize(); i++) {
@@ -800,7 +811,7 @@
}
/*
 * 设置子项的选中状态
 * 设置子项的选中状态
 */
void CEqsGraphWnd::SetItemSelectState(int nIndex, BOOL bSelect)
{
@@ -833,14 +844,14 @@
}
/*
 * 检测坐标点所在的项
 * 返回项类型, 如HT_ITEM, HT_PIN, HT_LINE
 * pItem - 所在的EQITEM
 * pPin --所在的pin, 如果在连线上,表示所属pin, out pin;
 * 检测坐标点所在的项
 * 返回项类型, 如HT_ITEM, HT_PIN, HT_LINE
 * pItem - 所在的EQITEM
 * pPin --所在的pin, 如果在连线上,表示所属pin, out pin;
 */
int CEqsGraphWnd::HighTest(POINT pt, OUT EQITEM*& pItem, OUT PIN *& pPin)
{
   // 检测是否在某个子项
   // 检测是否在某个子项
   int nRet = HT_NOWHERE;
   pItem = NULL;
   pPin = NULL;
@@ -849,7 +860,7 @@
      EQITEM *pTempItem = (EQITEM*)m_arItem.GetAt(i);
      GetItemRect(pTempItem, &rcItem);
      if (::PtInRect(&rcItem, pt)) {
         // 在Item
         // 在Item
         pItem = pTempItem;
         nRet = HT_ITEM;
         break;
@@ -859,7 +870,7 @@
         CPtrArray * pPins = (CPtrArray *)pTempItem->pInPins;
         for (int j = 0; j < pPins->GetSize(); j++) {
            if (GetInPinRect(pTempItem, j, &rcPin) && ::PtInRect(&rcPin, pt)) {
               // 在in pin上
               // 在in pin上
               pPin = (PIN *)pPins->GetAt(j);
               pItem = pTempItem;
               nRet = HT_PIN;
@@ -871,15 +882,15 @@
            pPins = (CPtrArray *)pTempItem->pOutPins;
            for (int j = 0; j < pPins->GetSize(); j++) {
               if (GetOutPinRect(pTempItem, j, &rcPin) && ::PtInRect(&rcPin, pt)) {
                  // 在out pin
                  // 在out pin
                  pPin = (PIN *)pPins->GetAt(j);
                  pItem = pTempItem;
                  nRet = HT_PIN;
                  break;
               }
               else {
                  // 是否在pin连接线上,即判断点是否在线上
                  // 点到直线的距离公式(先通过p1,p2用两点式求出直线的表达式,再套距离公式);abs()为取绝对值函数,sqrt()为开根号函数
                  // 是否在pin连接线上,即判断点是否在线上
                  // 点到直线的距离公式(先通过p1,p2用两点式求出直线的表达式,再套距离公式);abs()为取绝对值函数,sqrt()为开根号函数
                  PIN *pTempPin = (PIN *)pPins->GetAt(j);
                  if (pTempPin->pConnectedPin != NULL && pTempPin->nLinePtCount > 1) {
                     for (int i = 0; i < pTempPin->nLinePtCount - 1; i++) {
@@ -912,7 +923,7 @@
}
/*
 * 绘制虚线框,代表正在拖动的item
 * 绘制虚线框,代表正在拖动的item
 */
void CEqsGraphWnd::DrawDropItemRectangle(LPRECT lpRect1, LPRECT lpRect2)
{
@@ -940,7 +951,7 @@
}
/*
 * 绘制磁吸线
 * 绘制磁吸线
 */
void CEqsGraphWnd::DrawMagneticLine(LPRECT lprcClient, int nHozLine1, int nHozLine2, int nVerLine1, int nVerLine2)
{
@@ -974,16 +985,16 @@
}
/*
 * 缓制Pin连接线
 * pBrush -- 画刷
 * pPen - 画笔
 * lpPt1, lpPt2 -- Pin脚的位置
 * lpRect1, lpRect2 -- 两个Item的Rect
 * 缓制Pin连接线
 * pBrush -- 画刷
 * pPen - 画笔
 * lpPt1, lpPt2 -- Pin脚的位置
 * lpRect1, lpRect2 -- 两个Item的Rect
 */
void CEqsGraphWnd::DrawPinConnectedLine(Gdiplus::Graphics *pGraphics, Gdiplus::Brush *pBrush, Gdiplus::Pen *pPen, LPPOINT lpPt1, LPPOINT lpPt2,
   LPRECT lpRect1, LPRECT lpRect2, PIN *pOwnerPin)
{
   // 如果没有缓存线条的POINT,则先计算并缓存
   // 如果没有缓存线条的POINT,则先计算并缓存
   ASSERT(pOwnerPin);
   int nPinCount = ((CPtrArray*)pOwnerPin->pItem->pOutPins)->GetSize();
@@ -992,10 +1003,10 @@
   int nMargin = 12;
   int x1, x2, y1;
   if (pOwnerPin->nLinePtCount == 0) {                  // 第一个点的最小折线长
   if (pOwnerPin->nLinePtCount == 0) {                  // 第一个点的最小折线长
      ::OffsetRect(lpRect1, +m_nOffsetX, +m_nOffsetY);
      ::OffsetRect(lpRect2, +m_nOffsetX, +m_nOffsetY);
      lpPt1->x += m_nOffsetX;            // 消除偏移
      lpPt1->x += m_nOffsetX;            // 消除偏移
      lpPt1->y += m_nOffsetY;
      lpPt2->x += m_nOffsetX;
      lpPt2->y += m_nOffsetY;
@@ -1104,7 +1115,7 @@
}
/*
 * WindowProc,窗口过程
 * WindowProc,窗口过程
 */
LRESULT CALLBACK CEqsGraphWnd::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
@@ -1115,7 +1126,7 @@
   }
   // 处理窗口消息
   // 处理窗口消息
   ASSERT(hWnd);
   switch (uMsg)
   {
@@ -1179,7 +1190,7 @@
/*
 * WM_NCCREATE
 * 窗口创建
 * 窗口创建
 */
LRESULT CEqsGraphWnd::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
@@ -1192,7 +1203,7 @@
/*
 * WM_DESTROY
 * 窗口销毁
 * 窗口销毁
 */
LRESULT CEqsGraphWnd::OnDestroy(WPARAM wParam, LPARAM lParam)
{
@@ -1255,7 +1266,7 @@
/*
 * WM_MOUSEMOVE
 * 鼠标滚动
 * 鼠标滚动
 */
LRESULT CEqsGraphWnd::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
@@ -1264,7 +1275,7 @@
/*
 * WM_LBUTTONDOWN
 * 鼠标左键按下
 * 鼠标左键按下
 */
LRESULT CEqsGraphWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
@@ -1281,11 +1292,12 @@
   int nLastVerLine = 0;
   // 检测点击坐标是否在某一子项上,如是,则高亮显示
   // 检测点击坐标是否在某一子项上,如是,则高亮显示
   EQITEM* pLastItem = m_pCurItem;
   PIN *pLastPin = m_pCurPin;
   PIN *pLastSelLineOutPin = m_pSelLineOutPin;
   BOOL bChanged = FALSE;
   BOOL bSelectChanged = FALSE;
   EQITEM* pHitItem = NULL;
   PIN *pHitPin = NULL;
   PIN *pPin2 = NULL;
@@ -1309,8 +1321,11 @@
   }
   if (nRet == HT_ITEM) {
      m_pCurItem = pHitItem;
      m_pCurItem->bHighlight = TRUE;
      if (m_pCurItem != pHitItem) {
         m_pCurItem = pHitItem;
         m_pCurItem->bHighlight = TRUE;
         bSelectChanged = TRUE;
      }
   }
   else if (nRet == HT_PIN) {
      m_pCurPin = pHitPin;
@@ -1324,14 +1339,14 @@
   bChanged = pLastItem != m_pCurItem || pLastPin != m_pCurPin || pLastSelLineOutPin != m_pSelLineOutPin;
   // 刷新
   // 刷新
   SetFocus(m_hWnd);
   if (bChanged) {
      ::InvalidateRect(m_hWnd, &rcClient, TRUE);
   }
   // 捕捉鼠标消息,检测是否拖动
   // 捕捉鼠标消息,检测是否拖动
   if (nRet == HT_ITEM && m_pCurItem != NULL) {
      GetItemRect(m_pCurItem, &rcItem);
@@ -1394,12 +1409,14 @@
         ReleaseCapture();
      ExitLoop:
         m_nMagneticLinHoz = 0;
         m_nMagneticLinVer = 0;
         AfxUnlockTempMaps(FALSE);
      }
   }
   // 捕捉鼠标消息,检测是否连接引脚
   // 捕捉鼠标消息,检测是否连接引脚
   else if (nRet == HT_PIN && m_pCurPin != NULL) {
      if (::GetCapture() == NULL) {
         BOOL bLast = FALSE;
@@ -1426,12 +1443,12 @@
               ptNew = msg.pt;
               ::ScreenToClient(m_hWnd, &ptNew);
               // 擦除上一次
               // 擦除上一次
               if (bLast) {
                  DrawPinWillConnectLine(lineColor, &ptPin, &ptLast);
               }
               // 检测是否可以连接
               // 检测是否可以连接
               bCanConnect = false;
               nRet = HighTest(ptNew, pHitItem, pHitPin);
               if (nRet == HT_PIN) {
@@ -1457,12 +1474,12 @@
               ptNew = msg.pt;
               ::ScreenToClient(m_hWnd, &ptNew);
               // 擦除上一次
               // 擦除上一次
               if (bLast) {
                  DrawPinWillConnectLine(lineColor, &ptPin, &ptLast);
               }
               // 检测是否可以连接
               // 检测是否可以连接
               bCanConnect = false;
               nRet = HighTest(ptNew, pHitItem, pHitPin);
               if (nRet == HT_PIN) {
@@ -1497,7 +1514,7 @@
   }
   
   // 检测鼠标消息,检测是否移动画布
   // 检测鼠标消息,检测是否移动画布
   else if (nRet == HT_NOWHERE) {
      if (::GetCapture() == NULL) {
         int nLastOffsetX = m_nOffsetX;
@@ -1556,12 +1573,19 @@
   }
   if (bSelectChanged) {
      if (m_listener.onSelectEqItem != nullptr) {
         m_listener.onSelectEqItem(m_pCurItem);
      }
   }
   return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
}
/*
 * WM_LBUTTONDBLCLK
 * 鼠标左键双击
 * 鼠标左键双击
 */
LRESULT CEqsGraphWnd::OnLButtonDblclk(WPARAM wParam, LPARAM lParam)
{
@@ -1573,7 +1597,7 @@
   GetClientRect(m_hWnd, &rcClient);
   rcLast = { 0, 0, 0, 0 };
   // 检测点击坐标是否在某一子项上,如是,则高亮显示
   // 检测点击坐标是否在某一子项上,如是,则高亮显示
   EQITEM* pLastItem = m_pCurItem;
   BOOL bChanged = FALSE;
   EQITEM* pHitItem = NULL;
@@ -1593,7 +1617,7 @@
/*
 * WM_MOUSEWHEEL
 * 鼠标滚动
 * 鼠标滚动
 */
LRESULT CEqsGraphWnd::OnMouseWheel(WPARAM wParam, LPARAM lParam)
{
@@ -1620,7 +1644,7 @@
/*
* WM_MOUSEHWHEEL
* 鼠标滚动
* 鼠标滚动
*/
LRESULT CEqsGraphWnd::OnMouseHWheel(WPARAM wParam, LPARAM lParam)
{
@@ -1647,7 +1671,7 @@
/*
 * WM_RBUTTONDOWN
 * 鼠标左键按下
 * 鼠标左键按下
 */
LRESULT CEqsGraphWnd::OnRButtonDown(WPARAM wParam, LPARAM lParam)
{
@@ -1659,7 +1683,7 @@
   GetClientRect(m_hWnd, &rcClient);
   rcLast = { 0, 0, 0, 0 };
   // 检测点击坐标是否在某一子项上,如是,则高亮显示
   // 检测点击坐标是否在某一子项上,如是,则高亮显示
   EQITEM* pLastItem = m_pCurItem;
   PIN *pLastPin = m_pCurPin;
   PIN *pLastSelLineOutPin = m_pSelLineOutPin;
@@ -1702,14 +1726,14 @@
   bChanged = pLastItem != m_pCurItem || pLastPin != m_pCurPin || pLastSelLineOutPin != m_pSelLineOutPin;
   // 刷新
   // 刷新
   SetFocus(m_hWnd);
   if (bChanged) {
      ::InvalidateRect(m_hWnd, &rcClient, TRUE);
   }
   // 捕捉鼠标消息,检测是否拖动
   // 捕捉鼠标消息,检测是否拖动
   if (nRet == HT_ITEM && m_pCurItem != NULL) {
      CopyRect(&rcItem, &m_pCurItem->rect);
@@ -1762,13 +1786,13 @@
/*
 * WM_KEYDOWN
 * 键盘消息,按下按键
 * 键盘消息,按下按键
 */
LRESULT CEqsGraphWnd::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
   BOOL bChanged = FALSE;
   if (wParam == VK_DELETE) {
      // 如果当前选择为线,则断开连接
      // 如果当前选择为线,则断开连接
      if (m_pSelLineOutPin != NULL) {
         if (m_listener.onDisconnectPin != nullptr) {
            if (m_listener.onDisconnectPin(m_pSelLineOutPin)) {
@@ -1858,7 +1882,6 @@
   CString strText;
   HBRUSH hBrushBK;
   // BeginPaint
   PAINTSTRUCT ps;
   hDC = BeginPaint(m_hWnd, &ps);
@@ -1869,14 +1892,12 @@
      rcClient.bottom - rcClient.top);
   ::SelectObject(hMemDC, hBitmap);
   // 背景颜色
   // 背景颜色
   hBrushBK = CreateSolidBrush(m_crBkgnd);
   ::FillRect(hMemDC, &rcClient, hBrushBK);
   DeleteObject(hBrushBK);
   // 标题
   // 标题字体
   if (m_hFontTitle == nullptr) {
      LOGFONT lf;
      HFONT hFontDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
@@ -1886,8 +1907,9 @@
      m_hFontTitle = CreateFontIndirect(&lf);
   }
   // 绘制标题文本
   {
      char szTitle[256];
      char szTitle[256] = { 0 };
      GetWindowText(m_hWnd, szTitle, 256);
      RECT rcTitle;
      rcTitle.left = rcClient.left + 5;
@@ -1898,19 +1920,23 @@
      ::DrawText(hMemDC, szTitle, (int)strlen(szTitle), &rcTitle, DT_LEFT | DT_TOP);
   }
   // 绘制子项
   // 绘制子项
   HBRUSH hbrItemBackground[2];
   HBRUSH hbrItemFrame[2];
   HBRUSH hbrPinBackground[3];
   HBRUSH hbrIndicator;
   HBRUSH hbrIndicatorGray;
   hbrItemBackground[0] = CreateSolidBrush(m_crItemBackground[0]);
   hbrItemBackground[1] = CreateSolidBrush(m_crItemBackground[1]);
   hbrItemFrame[0] = CreateSolidBrush(m_crItemFrame[0]);
   hbrItemFrame[1] = CreateSolidBrush(m_crItemFrame[1]);
   hbrIndicator = CreateSolidBrush(RGB(34, 177, 76));    // 绿色
   hbrIndicatorGray = CreateSolidBrush(RGB(192, 192, 192));  // 灰色
   for (int i = 0; i < 3; i++) {
      hbrPinBackground[i] = CreateSolidBrush(m_crPinBkgnd[i]);
   }
   // gdi+
   Gdiplus::Graphics graphics(hMemDC);
@@ -1921,21 +1947,22 @@
      graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
   }
   SetBkMode(hMemDC, TRANSPARENT);
   {
      RECT rcItem;
      int nPinState;
      int nItemCount = (int)m_arItem.GetCount();
      // 先画 item、文本、pin 和指示灯
      for (int i = 0; i < nItemCount; i++) {
         EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
         if (pItem->nFlashFlag == 1) {
            continue;
         }
         GetItemRect(pItem, &rcItem);
         // 子项背景和边框
         // 子项背景和边框
         if (m_nItemRound == 0) {
            ::FillRect(hMemDC, &rcItem, pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]);
            ::FrameRect(hMemDC, &rcItem, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
@@ -1947,12 +1974,37 @@
            ::DeleteObject(hRgn);
         }
         // name和id
         // name
         HFONT hFontOld = (HFONT)::SelectObject(hMemDC, m_hFontName);
         ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemNameText[1] : m_crItemNameText[0]);
         ::DrawText(hMemDC, pItem->text, (int)strlen(pItem->text), &rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
         ::DrawText(hMemDC, pItem->text, (int)strlen(pItem->text), &rcItem,
            DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
         // indicators vertical column layout
         const int indicatorSize = m_nIndicatorSize;
         const int indicatorMargin = m_nIndicatorMargin;
         const int indicatorX = rcItem.left + 5;
         for (int k = 0; k < EQITEM_INDICATOR_COUNT; ++k) {
            BYTE indicatorState = pItem->nIndicatorState[k];
            if (indicatorState == INDICATOR_STATE_HIDDEN) {
               continue;
            }
            RECT rcIndicator;
            rcIndicator.left = indicatorX;
            rcIndicator.top = rcItem.top + 5 + k * (indicatorSize + indicatorMargin);
            rcIndicator.right = rcIndicator.left + indicatorSize;
            rcIndicator.bottom = rcIndicator.top + indicatorSize;
            RECT rcInner = rcIndicator;
            ::InflateRect(&rcInner, -1, -1);
            ::FillRect(hMemDC, &rcInner, indicatorState == INDICATOR_STATE_HIGHLIGHT
               ? hbrIndicator : hbrIndicatorGray);
            ::FrameRect(hMemDC, &rcIndicator, hbrItemFrame[0]);
         }
         // ID 文本(非小号 item)
         if (pItem->nShowType != ITEM_SMALL) {
            RECT rcId = rcItem;
            rcId.left += 5;
@@ -1961,29 +2013,28 @@
            strId.Format(_T("ID:%d"), pItem->id);
            ::SelectObject(hMemDC, m_hFontId);
            ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
            ::DrawText(hMemDC, strId, (int)strId.GetLength(), &rcId, DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS);
            ::DrawText(hMemDC, strId, (int)strId.GetLength(), &rcId,
               DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS);
         }
         // 动画效果不绘pin
         // 动画效果时不绘制 pin
         if (m_pAnimationItem == pItem) {
            ::SelectObject(hMemDC, hFontOld);
            continue;
         }
         // 绘制pin
         // 绘制 pin
         RECT rcPin, rcPin2, rcPinText;
         CPtrArray *pPins;
         CPtrArray* pPins;
         rcPinText.left = rcItem.left + 8;
         rcPinText.right = rcItem.right - 8;
         // in pins
         PIN *pPin = NULL;
         pPins = (CPtrArray *)pItem->pInPins;
         PIN* pPin = NULL;
         pPins = (CPtrArray*)pItem->pInPins;
         for (int j = 0; j < pPins->GetSize(); j++) {
            if (GetInPinRect(pItem, j, &rcPin)) {
               pPin = (PIN *)pPins->GetAt(j);
               pPin = (PIN*)pPins->GetAt(j);
               ::FrameRect(hMemDC, &rcPin, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
               rcPin2.left = rcPin.left + 1;
@@ -1991,22 +2042,25 @@
               rcPin2.top = rcPin.top + 1;
               rcPin2.bottom = rcPin.bottom - 1;
               nPinState = GetPinState(pPin);
               ::FillRect(hMemDC, &rcPin2, nPinState == 0 ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]) : hbrPinBackground[nPinState]);
               ::FillRect(hMemDC, &rcPin2,
                  nPinState == 0
                  ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0])
                  : hbrPinBackground[nPinState]);
               if (pItem->nShowType != ITEM_SMALL) {
                  rcPinText.top = rcPin.top - 12;
                  rcPinText.bottom = rcPin.bottom + 12;
                  ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
                  ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
                  ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText,
                     DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
               }
            }
         }
         // out pins
         pPins = (CPtrArray *)pItem->pOutPins;
         pPins = (CPtrArray*)pItem->pOutPins;
         for (int j = 0; j < pPins->GetSize(); j++) {
            pPin = (PIN *)pPins->GetAt(j);
            pPin = (PIN*)pPins->GetAt(j);
            if (GetOutPinRect(pItem, j, &rcPin)) {
               ::FrameRect(hMemDC, &rcPin, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
@@ -2015,50 +2069,53 @@
               rcPin2.top = rcPin.top + 1;
               rcPin2.bottom = rcPin.bottom - 1;
               nPinState = GetPinState(pPin);
               ::FillRect(hMemDC, &rcPin2, nPinState == 0 ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]) : hbrPinBackground[nPinState]);
               ::FillRect(hMemDC, &rcPin2,
                  nPinState == 0
                  ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0])
                  : hbrPinBackground[nPinState]);
               if (pItem->nShowType != ITEM_SMALL) {
                  rcPinText.top = rcPin.top - 12;
                  rcPinText.bottom = rcPin.bottom + 12;
                  ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
                  ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText, DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
                  ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText,
                     DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
               }
            }
         }
         ::DeleteObject(hbrItemFrame);
         ::SelectObject(hMemDC, hFontOld);
      }
      // 绘制连接线,保存线条在最后绘制
      // 再绘制连接线
      for (int i = 0; i < nItemCount; i++) {
         EQITEM *pItem = (EQITEM*)m_arItem.GetAt(i);
         EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
         if (pItem->nFlashFlag == 1) {
            continue;
         }
         PIN *pPin = NULL;
         CPtrArray *pPins;
         // out pins边线
         PIN* pPin = NULL;
         CPtrArray* pPins;
         RECT rcItem1, rcItem2;
         pPins = (CPtrArray *)pItem->pOutPins;
         // out pins 边线
         pPins = (CPtrArray*)pItem->pOutPins;
         for (int j = 0; j < pPins->GetSize(); j++) {
            pPin = (PIN *)pPins->GetAt(j);
            pPin = (PIN*)pPins->GetAt(j);
            if (pPin->pConnectedPin != NULL) {
               POINT pt1, pt2;
               if (GetPinPoint(pPin, &pt1) && GetPinPoint(pPin->pConnectedPin, &pt2)) {
                  GetItemRect(pItem, &rcItem1);
                  GetItemRect(pPin->pConnectedPin->pItem, &rcItem2);
                  DrawPinConnectedLine(&graphics, &brush1, pPin == m_pSelLineOutPin ? &pen2 : &pen1,
                  DrawPinConnectedLine(&graphics, &brush1,
                     pPin == m_pSelLineOutPin ? &pen2 : &pen1,
                     &pt1, &pt2, &rcItem1, &rcItem2, pPin);
               }
            }
         }
      }
      // 删除 brush
      for (int i = 0; i < 3; i++) {
         ::DeleteObject(hbrPinBackground[i]);
      }
@@ -2066,10 +2123,9 @@
      ::DeleteObject(hbrItemBackground[1]);
      ::DeleteObject(hbrItemFrame[0]);
      ::DeleteObject(hbrItemFrame[1]);
      ::DeleteObject(hbrIndicator);
      ::DeleteObject(hbrIndicatorGray);
   }
   // EndPaint
   ::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
@@ -2078,9 +2134,9 @@
   ::DeleteObject(hBitmap);
   ::DeleteDC(hMemDC);
   return 1;
}
/*
 * WM_SIZE
@@ -2328,8 +2384,8 @@
/*
 * 设置背景颜色
 * color -- 背景色
 * 设置背景颜色
 * color -- 背景色
 */
void CEqsGraphWnd::SetBkgndColor(COLORREF color)
{
@@ -2337,8 +2393,8 @@
}
/*
 * 边框颜色
 * color -- 边框色
 * 边框颜色
 * color -- 边框色
 */
void CEqsGraphWnd::SetFrameColor(COLORREF color)
{
@@ -2382,6 +2438,19 @@
   SetTimer(m_hWnd, TIMER_ANIMATION_RECT, uElpase, NULL);
}
void CEqsGraphWnd::ShowItemIndicator(DWORD_PTR dwItemData, int state, int nIndex)
{
   if (nIndex < 0 || nIndex >= EQITEM_INDICATOR_COUNT) {
      return;
   }
   EQITEM* pItem = GetItem(dwItemData);
   if (pItem != nullptr) {
      pItem->nIndicatorState[nIndex] = (BYTE)state;
      ::InvalidateRect(m_hWnd, nullptr, TRUE);
   }
}
double CEqsGraphWnd::PointToSegDist(double x, double y, double x1, double y1, double x2, double y2)
{
   double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);