2

在我的一个项目(VC++2010,MFC)中,我想使用 CDC::Ellipse 画一个圆。我设置了两个点:第一个是圆心,第二个是我希望它在圆周上的点。

我将左上角和右下角的坐标传递给 CDC::Ellipse(int x1, int y1, int x2, int y2)。

简而言之:使用 Pitagora Theorem 我计算两点之间的距离(半径),然后我从中心坐标中减去该值以获得左上角并添加以获得右下角。

当我绘制圆和点并放大时,我看到第二个不像预期的那样在圆周上,它稍微在里面,除非你将它设置为 0°、45°、90° 等等相对于坐标的绝对系统。

然后我尝试使用 CDC::Polyline 绘制相同的圆,我将获得的点提供给该方法,该点围绕中心旋转另一个点,距离等于半径。在这种情况下,该点位于我设置它的每个位置的圆周上。

这两个圆的重叠表明它们在0°、45°、90°等处完全重叠,但在22.5°、67.5°等处差距最大。

有没有人注意到类似的行为?

感谢所有可以帮助我的人!

代码片段

这就是我如何计算给定 2 点的半径:

centerPX = vvFPoint( 1380, 845 );
secondPointPX = vvFPoint( 654,654 );
double radiusPX = (sqrt( (secondPointPX.x - centerPX.x) * (secondPointPX.x - centerPX.x) + (secondPointPX.y - centerPX.y) * (secondPointPX.y - centerPX.y) ));

vvFPoint是从 CPoint 派生的自定义类型)

这就是我用 CDC::Ellipse 画“圆”的方式:

int up = (int)(((double)(m_p1.y-(double)originY - m_radius) / zoom) + 0.5) + offY;
int left = (int)(((double)(m_p1.x-(double)originX - m_radius) / zoom) + 0.5) + offX;
int down = (int)(((double)(m_p1.y-(double)originY + m_radius) / zoom) + 0.5) + offY;
int right = (int)(((double)(m_p1.x-(double)originX + m_radius) / zoom) + 0.5) + offX;
pDC->Ellipse( left, up, right, down);

m_p1是圆心,originX/Y是图像的原点,m_radius是圆的半径,zoom是比例因子,offX/Y是我的 SW 客户区的偏移量)

这就是我使用自定义折线类“手动”(并且非常简单的方法)绘制圆的方式:

1)创建点数组:

point.x = centerPX.x + radiusPX;
point.y = centerPX.y;
for ( i=0; i < 3600; i++ )
{
    pt1.RotateDeg ( centerPX, (double)0.1 );
    poly->AddPoint( pt1 );
}

RotateDeg是一种自定义方法,使用第一个参数作为枢轴,第二个参数作为角度值(以度为单位)旋转点,AddPoint是创建点数组的自定义方法,poly是我的自定义折线对象)。

2)绘制它:

当我调用 Draw( CDC* pDC ) 时,我使用前面的数组来绘制折线:

pDC->MoveTo(p);

我希望这可以帮助您重现我的奇怪观察!

代码片段2

void vvPoint<Tipo>::RotateDeg(const vvPoint<Tipo> &center, double angle)
{
vvPoint<Tipo> ptB;

angle *= -(M_PI / 180);

*this -= center;
ptB.x = ((this->x * cos(angle)) - (this->y * sin(angle)));
ptB.y = ((this->x * sin(angle)) + (this->y * cos(angle)));
*this = ptB + center;
}

但是为了让您更好地理解我的观察,我想添加一些图像,以便您可以看到我的整个问题从哪里开始......问题是:我无法添加图像,因为我需要有 10 名声望。我在 Dropbox 上上传了一个 .zip 文件,如果你愿意,我可以把这个文件的 URL 发给你。让我知道这是否是绕过此问题的正确(且安全..)方法。

谢谢!

4

1 回答 1

2

这或许是一种可能的解释。正如MSDN 所说CDC::Ellipse我强调):

椭圆的中心是由x1y1x2y2lpRect指定的边界矩形的中心。椭圆是用当前画笔绘制的,它的内部是用当前画笔填充的。

此函数绘制的图形延伸到但不包括右下坐标。这意味着图形的高度为y2 – y1,图形的宽度为x2 – x1

您描述如何计算边界矩形的方式并不完全清楚(一些源代码会有所帮助)但是,鉴于上面引用的第二段,您可能需要将 1 添加到您的x2y2值,以确保您有具有所需半径的圆。

还值得注意的是,您的两种绘图方法之间可能存在细微的舍入差异,其中您有一个奇数大小的边界框(即中心点在逻辑上落在半像素上)。

更新

使用您的代码片段(谢谢),并假设没有缩放和零偏移等,我得到 750.704 像素的半径和椭圆的以下参数:

pDC->Ellipse(629, 94, 2131, 1596);

根据 MSDN,这意味着椭圆将绘制成以下尺寸的图形:

width  = (2131 - 629) = 1502
height = (1596 -  94) = 1502

据我所知,这应该产生一个圆而不是椭圆。

接下来要做的是找出你是如何绘制多边形的——为此我们需要看看它的实现RotateDeg——你能发布那个代码吗?我怀疑这里有一些简单的舍入误差,当你放大时可能会被放大。

更新 2

只看这段代码:

for ( i=0; i < 3600; i++ )
{
    pt1.RotateDeg ( centerPX, (double)0.1 );
    poly->AddPoint( pt1 );
}

您每次将多边形点旋转 0.1 度。这可能会累积一些错误,因此可能值得这样做:

for ( i=0; i < 3600; i++ )
{
    vvFPoint ptNew = pt1;
    ptNew.RotateDeg ( centerPX, (double)i * 0.1 );
    poly->AddPoint( ptNew );
}

也许这意味着你必须改变你的RotateDeg函数来处理正确的象限。

还有一点,您提到放大图像时会看到问题。如果这意味着您正在使用您的zoom变量,那么值得检查这一行...:

pDC->Ellipse( left, up, right, down);

...参数仍然形成一个正方形,所以(right - left) == (down - up)

更新 3

我刚刚RotateDeg以当前形式运行了您的函数,以查看错误如何累积(通过将先前的结果提供给下一次迭代)。在每一步,我都会计算新点与中心之间的距离,并将其与所需的半径进行比较。

下图显示了结果,您可以在计算点时看到 4 个像素的误差。

错误

我认为这至少解释了部分差异(即您的多边形绘图有缺陷)并且 - 取决于zoom- 您可能会在椭圆参数中引入不对称性,您可以通过将宽度与高度进行比较来调试,如上所述。

于 2013-11-01T09:20:55.447 回答