2010-11-12 170 views

回答

2

需要一些小小的幾何圖形才能找出形成箭頭的三角形的拐角。

假設線條從P0到P1,並且我們希望P1的箭頭尖端。爲了找到箭頭的「后角」,我們希望沿着線從尖端向後移動,然後左右轉動以使箭頭有一定的寬度。

如果線條與x或y軸對齊,這將很簡單。爲了以任何角度處理線條,我們可以構建一個座標系,其座標軸與原始線平行並垂直。我們將這些軸稱爲u和v,u指向線的方向,v垂直於它。

現在我們可以從P0開始,通過加入由u和v確定的方向移動到角上,按照我們想要的任何箭頭長度和寬度進行縮放。

在代碼:

constexpr int Round(float x) { return static_cast<int>(x + 0.5f); } 

// Draws a line from p0 to p1 with an arrowhead at p1. Arrowhead is outlined 
// with the current pen and filled with the current brush. 
void DrawArrow(HDC hdc, POINT p0, POINT p1, int head_length, int head_width) { 
    ::MoveToEx(hdc, p0.x, p0.y, nullptr); 
    ::LineTo(hdc, p1.x, p1.y); 

    const float dx = static_cast<float>(p1.x - p0.x); 
    const float dy = static_cast<float>(p1.y - p0.y); 
    const auto length = std::sqrt(dx*dx + dy*dy); 
    if (head_length < 1 || length < head_length) return; 

    // ux,uy is a unit vector parallel to the line. 
    const auto ux = dx/length; 
    const auto uy = dy/length; 

    // vx,vy is a unit vector perpendicular to ux,uy 
    const auto vx = -uy; 
    const auto vy = ux; 

    const auto half_width = 0.5f * head_width; 

    const POINT arrow[3] = 
     { p1, 
      POINT{ Round(p1.x - head_length*ux + half_width*vx), 
       Round(p1.y - head_length*uy + half_width*vy) }, 
      POINT{ Round(p1.x - head_length*ux - half_width*vx), 
       Round(p1.y - head_length*uy - half_width*vy) } 
     }; 
    ::Polygon(hdc, arrow, 3); 
} 

和(使用WTL)演示:

LRESULT OnPaint(UINT, WPARAM, LPARAM, BOOL &) { 
    PAINTSTRUCT ps; 
    BeginPaint(&ps); 

    RECT rc; 
    GetClientRect(&rc); 

    const auto origin = POINT{rc.left + (rc.right - rc.left)/2, 
           rc.top + (rc.bottom - rc.top)/2 }; 
    const auto pi = 3.1415926f; 
    const auto tau = 2.0f*pi; 
    const auto cxInch = ::GetDeviceCaps(ps.hdc, LOGPIXELSX); 
    const auto radius = 2.0f * cxInch; 
    const auto size = Round(0.333f * cxInch); 
    for (float theta = 0.0f; theta < tau; theta += tau/12.0f) { 
     const auto p1 = 
      POINT{Round(origin.x + radius * std::cos(theta)), 
        Round(origin.y + radius * std::sin(theta))}; 
     DrawArrow(ps.hdc, origin, p1, size, size/3); 
    } 
    EndPaint(&ps); 
    return 0; 
}