c++ - CDC::Ellipse 无法正确绘制圆圈

标签 c++ mfc

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

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

简单地说:通过 Pitagora 定理,我计算两点之间的距离(半径),然后从中心坐标中减去该值以获得左上角,并添加以获得右下角。

当我绘制圆和点并放大时,我发现第二个圆并不像预期的那样位于圆周上,而是稍微靠内,除非您将其设置为 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是我的软件客户区域的偏移量)

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

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。让我知道这是否是绕过此问题的正确(且安全)方法。

谢谢!

最佳答案

这可能是一个可能的解释。如MSDN says关于CDC::Ellipse(我强调):

The center of the ellipse is the center of the bounding rectangle specified by x1, y1, x2, and y2, or lpRect. The ellipse is drawn with the current pen, and its interior is filled with the current brush.

The figure drawn by this function extends up to, but does not include, the right and bottom coordinates. This means that the height of the figure is y2 – y1 and the width of the figure is 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 个像素的误差。

errors

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

关于c++ - CDC::Ellipse 无法正确绘制圆圈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19703907/

相关文章:

c++ - 在 Windows 上与 USB HID 设备进行通信的最佳 USB 库是什么?

c++ - 由 4 个十六进制字符组成的数字

c++ - 使用 VS2015 CLR 在 CDialog 派生类上创建返回 0,在 VC++ 6 中运行良好

c++ - CArchive 当前在文件中的位置

c++ - 使用 CFileDialog 打开文件失败时如何捕获异常

c++ - std::runtime_error 中的显式构造函数

C++ 指向使用随机指针归档的其他类对象的指针 vector

c++ - 为什么 c++ 不支持 for 循环中的多个初始化程序?

c++ - 定义的改变

c++ - 如何在CCombobox中添加背景文字