【发布时间】:2011-05-09 03:06:41
【问题描述】:
有人可以帮我提供一些想法(最好是代码)关于如何在最后画一条带实心箭头的线吗?
箭头的方向必须与线的方向正确对齐。我想在 C++ 中做到这一点。
【问题讨论】:
-
Windows GDI?认为您应该在此处标记窗口以获取帮助
-
你试过 Pen::SetEndCap() 吗?
有人可以帮我提供一些想法(最好是代码)关于如何在最后画一条带实心箭头的线吗?
箭头的方向必须与线的方向正确对齐。我想在 C++ 中做到这一点。
【问题讨论】:
需要一点几何图形才能找出构成箭头的三角形的角。
假设这条线从 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;
}
【讨论】:
我知道这是一个旧的 Q,但我不久前找到了一个简单的方法,所以想我会分享...
这仅适用于左、右、上和下,但非常快速和简单。 根据您的需要进行调整并添加到行中:)
void DrawArrow(CDC* pDC, CPoint ArrowTip)
{
CPoint ptDest;
LOGPEN logPen; // to get the color
pDC->GetCurrentPen()->GetLogPen(&logPen);
pDC->SetPixel(ArrowTip, logPen.lopnColor); //....x....
ArrowTip -= CPoint(1, 1); // move up one line (or down to suit need)
pDC->MoveTo(ArrowTip);
ptDest = ArrowTip;
ptDest += CPoint(3, 0);
pDC->LineTo(ptDest); //...xxx...
ArrowTip -= CPoint(1, 1);
pDC->MoveTo(ArrowTip);
ptDest = ArrowTip;
ptDest += CPoint(5, 0);
pDC->LineTo(ptDest); //..xxxxx..
ArrowTip -= CPoint(1, 1);
pDC->MoveTo(ArrowTip);
ptDest = ArrowTip;
ptDest += CPoint(7, 0);
pDC->LineTo(ptDest); //.xxxxxxx.
}
【讨论】: