工業控制系統中,管道控件是模擬現場環境的關鍵元件。市面組態軟體擁有豐富圖形庫可繪製 3D 場景,但自行開發常缺乏此類支援,介面顯得單調。現有開源管道控件多僅支援橫向/縱向,拐角與交叉處理粗糙。本文實作支援 11 種形狀 (4 種拐角、橫向、縱向、5 種交叉) 的管道控件,提升專業度。
管道外觀為圓柱體,視覺上呈現中間亮、兩側暗的漸層效果。參考 Windows 畫圖工具:用筆刷繪製不同顏色直線,顏色漸變可產生「凹凸」視覺效果。管道多為黑白,採用黑白漸層色填充矩形區段即可模擬立體感。

typedef enum _PipeFace
{
PF_CORNER_LEFT_TOP = 0, // 左上拐角
PF_CORNER_RIGHT_TOP = 2, // 右上拐角
PF_CORNER_RIGHT_BOTTOM = 3, // 右下拐角
PF_CORNER_LEFT_BOTTOM = 4, // 左下拐角
PF_VERTICAL = 5, // 縱向
PF_HORIZONTAL = 6, // 橫向
PF_CROSS_CENTER = 7, // 中央交叉
PF_CROSS_LEFT = 8, // 左側交叉
PF_CROSS_TOP = 9, // 頂部交叉
PF_CROSS_RIGHT = 10, // 右側交叉
PF_CROSS_BOTTOM = 11 // 底部交叉
} PIPEFACE;
在 OnPaint() 依 m_enumFace 分派繪製:
void CPipeCtrl::OnPaint()
{
CPaintDC dc(this);
RECT rcClient;
GetClientRect(&rcClient);
switch (m_enumFace)
{
case PF_HORIZONTAL: DrawHorizontal(&dc, rcClient); break;
case PF_VERTICAL: DrawVertical(&dc, rcClient); break;
case PF_CORNER_LEFT_TOP: DrawCornerLeftTop(&dc, rcClient); break;
case PF_CORNER_RIGHT_TOP: DrawCornerRightTop(&dc, rcClient); break;
case PF_CORNER_RIGHT_BOTTOM: DrawCornerRightBottom(&dc, rcClient); break;
case PF_CORNER_LEFT_BOTTOM: DrawCornerLeftBottom(&dc, rcClient); break;
case PF_CROSS_CENTER: DrawCrossCenter(&dc, rcClient); break;
case PF_CROSS_TOP: DrawCrossTop(&dc, rcClient); break;
case PF_CROSS_RIGHT: DrawCrossRight(&dc, rcClient); break;
case PF_CROSS_BOTTOM: DrawCrossBottom(&dc, rcClient); break;
case PF_CROSS_LEFT: DrawCrossLeft(&dc, rcClient); break;
}
}
以 SECTCOUNT 區段數將管道寬度切割,每區段填入對應漸層色 (GetSectColor(i)):
void CPipeCtrl::DrawHorizontal(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush brush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i = 0; i < SECTCOUNT; i++)
{
brush.CreateSolidBrush(GetSectColor(i));
pOldBrush = pDC->SelectObject(&brush);
SetRect(&rcSect, rectClient.left, rectClient.top + i * nSectWidth,
rectClient.right, rectClient.top + (i + 1) * nSectWidth);
pDC->FillRect(&rcSect, &brush);
pDC->SelectObject(pOldBrush);
brush.DeleteObject();
}
}
同時繪製水平與垂直兩段,共用交會區域:
void CPipeCtrl::DrawCornerLeftTop(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush brush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i = 0; i < SECTCOUNT; i++)
{
brush.CreateSolidBrush(GetSectColor(i));
pOldBrush = pDC->SelectObject(&brush);
// 水平段
SetRect(&rcSect, rectClient.left + i * nSectWidth, rectClient.top + i * nSectWidth,
rectClient.right, rectClient.top + (i + 1) * nSectWidth);
pDC->FillRect(&rcSect, &brush);
// 垂直段
SetRect(&rcSect, rectClient.left + i * nSectWidth, rectClient.top + i * nSectWidth,
rectClient.left + (i + 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &brush);
pDC->SelectObject(pOldBrush);
brush.DeleteObject();
}
}
計算中心點,繪製水平貫穿條帶與垂直四象限區塊:
void CPipeCtrl::DrawCrossCenter(CDC *pDC, RECT &rectClient)
{
// ... 計算中心座標 xCenter, yCenter, xLeft, yTop
// 水平貫穿
for (int i = 0; i < SECTCOUNT; i++) { /* 填滿整列 */ }
// 垂直四象限
for (int i = 0; i <= SECTCOUNT / 2; i++) {
// 左上、右上、左下、右下四個矩形
}
}

目前固定黑白模式,未提供動態顏色調整。交叉形狀涵蓋基礎需求,歡迎各位改進分享。
Email: successq_g@163.com
QQ: 5516853