c++ - 中点粗椭圆绘制算法
问题描述
我真的很接近让厚椭圆算法工作,但我遇到了一些麻烦。我从这里采用了中点粗圆算法,从这里采用了中点椭圆算法,我正在尝试将它们组合在一起以获得中点粗椭圆算法。我这样做是因为谷歌搜索“中点粗椭圆算法”没有显示我在寻找什么。我尝试的输出类似于一个粗圆圈(图片在帖子的底部)。
这是图像代码(只是一个占位符):
struct Point {
int x, y;
};
struct Image {};
using Color = int;
void setPixel(Image &, Color, Point) {
// ...
}
void horiLine(Image &image, Color color, Point first, int last) {
while (first.x <= last) {
setPixel(image, color, first);
first.x++;
}
}
void vertLine(Image &image, Color color, Point first, int last) {
while (first.y <= last) {
setPixel(image, color, first);
first.y++;
}
}
这是中点粗圆算法:
void midpointCircleThick(
Image &image,
Color color,
Point center,
int innerRadius,
int outerRadius
) {
int innerX = innerRadius;
int outerX = outerRadius;
int posY = 0;
int innerErr = 1 - innerRadius;
int outerErr = 1 - outerRadius;
while (outerX >= posY) {
horiLine(image, color, {center.x + innerX, center.y + posY}, center.x + outerX);
vertLine(image, color, {center.x + posY, center.y + innerX}, center.y + outerX);
horiLine(image, color, {center.x - outerX, center.y + posY}, center.x - innerX);
vertLine(image, color, {center.x - posY, center.y + innerX}, center.y + outerX);
horiLine(image, color, {center.x - outerX, center.y - posY}, center.x - innerX);
vertLine(image, color, {center.x - posY, center.y - outerX}, center.y - innerX);
horiLine(image, color, {center.x + innerX, center.y - posY}, center.x + outerX);
vertLine(image, color, {center.x + posY, center.y - outerX}, center.y - innerX);
posY++;
if (outerErr < 0) {
outerErr += 2 * posY + 1;
} else {
outerX--;
outerErr += 2 * (posY - outerX) + 1;
}
if (posY > innerRadius) {
innerX = posY;
} else {
if (innerErr < 0) {
innerErr += 2 * posY + 1;
} else {
innerX--;
innerErr += 2 * (posY - innerX) + 1;
}
}
}
}
这是中点椭圆算法:
void midpointEllipse(
Image &image,
Color color,
Point center,
Point radius
) {
Point pos = {radius.x, 0};
Point delta = {
2 * radius.y * radius.y * pos.x,
2 * radius.x * radius.x * pos.y
};
int err = radius.x * radius.x
- radius.y * radius.y * radius.x
+ (radius.y * radius.y) / 4;
while (delta.y < delta.x) {
setPixel(image, color, {center.x + pos.x, center.y + pos.y});
setPixel(image, color, {center.x + pos.x, center.y - pos.y});
setPixel(image, color, {center.x - pos.x, center.y + pos.y});
setPixel(image, color, {center.x - pos.x, center.y - pos.y});
pos.y++;
if (err < 0) {
delta.y += 2 * radius.x * radius.x;
err += delta.y + radius.x * radius.x;
} else {
pos.x--;
delta.y += 2 * radius.x * radius.x;
delta.x -= 2 * radius.y * radius.y;
err += delta.y - delta.x + radius.x * radius.x;
}
}
err = radius.x * radius.x * (pos.y * pos.y + pos.y)
+ radius.y * radius.y * (pos.x - 1) * (pos.x - 1)
- radius.y * radius.y * radius.x * radius.x;
while (pos.x >= 0) {
setPixel(image, color, {center.x + pos.x, center.y + pos.y});
setPixel(image, color, {center.x + pos.x, center.y - pos.y});
setPixel(image, color, {center.x - pos.x, center.y + pos.y});
setPixel(image, color, {center.x - pos.x, center.y - pos.y});
pos.x--;
if (err > 0) {
delta.x -= 2 * radius.y * radius.y;
err += radius.y * radius.y - delta.x;
} else {
pos.y++;
delta.y += 2 * radius.x * radius.x;
delta.x -= 2 * radius.y * radius.y;
err += delta.y - delta.x + radius.y * radius.y;
}
}
}
我试图将这两种算法结合起来,这就是我目前所拥有的。我留下了一些?
我不确定代码的地方。我很清楚这里的混乱和重复。我只想在担心代码的样子之前让它工作。
void midpointEllipseThick(
Image &image,
Color color,
Point center,
Point innerRadius,
Point outerRadius
) {
int innerX = innerRadius.x;
int outerX = outerRadius.x;
int posY = 0;
Point innerDelta = {
2 * innerRadius.y * innerRadius.y * innerX,
2 * innerRadius.x * innerRadius.x * posY
};
Point outerDelta = {
2 * outerRadius.y * outerRadius.y * outerX,
2 * outerRadius.x * outerRadius.x * posY
};
int innerErr = innerRadius.x * innerRadius.x
- innerRadius.y * innerRadius.y * innerRadius.x
+ (innerRadius.y * innerRadius.y) / 4;
int outerErr = outerRadius.x * outerRadius.x
- outerRadius.y * outerRadius.y * outerRadius.x
+ (outerRadius.y * outerRadius.y) / 4;
while (outerDelta.y < outerDelta.x) { // ?
horiLine(image, color, {center.x + innerX, center.y + posY}, center.x + outerX);
vertLine(image, color, {center.x + posY, center.y + innerX}, center.y + outerX);
horiLine(image, color, {center.x - outerX, center.y + posY}, center.x - innerX);
vertLine(image, color, {center.x - posY, center.y + innerX}, center.y + outerX);
horiLine(image, color, {center.x - outerX, center.y - posY}, center.x - innerX);
vertLine(image, color, {center.x - posY, center.y - outerX}, center.y - innerX);
horiLine(image, color, {center.x + innerX, center.y - posY}, center.x + outerX);
vertLine(image, color, {center.x + posY, center.y - outerX}, center.y - innerX);
posY++;
if (outerErr < 0) {
outerDelta.y += 2 * outerRadius.x * outerRadius.x;
outerErr += outerDelta.y + outerRadius.x * outerRadius.x;
} else {
outerX--;
outerDelta.y += 2 * outerRadius.x * outerRadius.x;
outerDelta.x -= 2 * outerRadius.y * outerRadius.y;
outerErr += outerDelta.y - outerDelta.x + outerRadius.x * outerRadius.x;
}
// ?
// if (posY > innerRadius.y) {
// innerX = posY;
// } else {
if (innerErr < 0) {
innerDelta.y += 2 * innerRadius.x * innerRadius.x;
innerErr += innerDelta.y + innerRadius.x * innerRadius.x;
} else {
innerX--;
innerDelta.y += 2 * innerRadius.x * innerRadius.x;
innerDelta.x -= 2 * innerRadius.y * innerRadius.y;
innerErr += innerDelta.y - innerDelta.x + innerRadius.x * innerRadius.x;
}
// }
}
innerErr = innerRadius.x * innerRadius.x * (posY * posY + posY)
+ innerRadius.y * innerRadius.y * (innerX - 1) * (innerX - 1)
- innerRadius.y * innerRadius.y * innerRadius.x * innerRadius.x;
outerErr = outerRadius.x * outerRadius.x * (posY * posY + posY)
+ outerRadius.y * outerRadius.y * (outerX - 1) * (outerX - 1)
- outerRadius.y * outerRadius.y * outerRadius.x * outerRadius.x;
while (outerX >= 0) { // ?
horiLine(image, color, {center.x + innerX, center.y + posY}, center.x + outerX);
vertLine(image, color, {center.x + posY, center.y + innerX}, center.y + outerX);
horiLine(image, color, {center.x - outerX, center.y + posY}, center.x - innerX);
vertLine(image, color, {center.x - posY, center.y + innerX}, center.y + outerX);
horiLine(image, color, {center.x - outerX, center.y - posY}, center.x - innerX);
vertLine(image, color, {center.x - posY, center.y - outerX}, center.y - innerX);
horiLine(image, color, {center.x + innerX, center.y - posY}, center.x + outerX);
vertLine(image, color, {center.x + posY, center.y - outerX}, center.y - innerX);
outerX--; // ?
innerX--;
if (outerErr > 0) {
outerDelta.x -= 2 * outerRadius.y * outerRadius.y;
outerErr += outerRadius.y * outerRadius.y - outerDelta.x;
} else {
posY++;
outerDelta.y += 2 * outerRadius.x * outerRadius.x;
outerDelta.x -= 2 * outerRadius.y * outerRadius.y;
outerErr += outerDelta.y - outerDelta.x + outerRadius.y * outerRadius.y;
}
// ?
// if (innerX < -innerRadius.x) {
// } else {
if (outerErr > 0) {
innerDelta.x -= 2 * innerRadius.y * innerRadius.y;
innerErr += innerRadius.y * innerRadius.y - innerDelta.x;
} else {
posY++;
innerDelta.y += 2 * innerRadius.x * innerRadius.x;
innerDelta.x -= 2 * innerRadius.y * innerRadius.y;
outerErr += innerDelta.y - innerDelta.x + innerRadius.y * innerRadius.y;
}
// }
}
}
这是一个粗圆圈innerRadius = 22; outerRadius = 24
:
这是一个带有 的椭圆radius = {32, 24}
:
这是(应该是什么)一个带有 的厚椭圆innerRadius = {30, 22}; outerRadius = {32, 24}
:
我很近,但不完全在那里。比我更了解这些东西的人能否让我越过终点线?
解决方案
我必须承认,我坚信圆形比椭圆更对称。如果一个圆可以通过中心在任何轴上镜像,对于一个椭圆,这通常只有 x 和 y 轴才有可能。因此,我认为midPointCircleThick()
不能适用于椭圆。
midpointEllipse()
所以,我从OP 提供的开始我的实现。
这些是我的基本想法:
恕我直言,布雷森汉姆线算法是中点圆算法和中点椭圆算法的起源。这可能有助于理解所使用的错误/增量魔法。一条线要简单得多,但遵循适用于 x²/a² + y²/b² = 1 (椭圆方程)的相同想法。
以椭圆中心为原点,
midpointEllipse()
同时渲染所有 4 个象限(利用对称性)。因此,只需有效计算一个象限中的曲线。该区域的曲线是单调的。有
midpointEllipse()
两个区域:- 从 x 轴上的点开始,Δy > Δx 直到交叉偶数。
- 之后,Δx > Δy。
我的概念是采用midpointEllipse()
这种方式,即“复制”代码以管理具有相同 y 坐标的两个点(一个用于内边框,一个用于外边框)以绘制水平线(跨线)。
我的第一个观察结果是新算法必须管理最后一个阶段(对于innerRadius.y
< y ≤outerRadius.y
仅需要考虑外边界上的点。
记住原始算法有两个区域,现在有两个区域用于外边界,两个区域用于内边界,以及上面提到的两个阶段。这允许多种组合。(管理这个是我实施的主要工作。)
示例实现(基于Qt有一个简单的可视化):
#include <functional>
#include <QtWidgets>
class View: public QLabel {
public:
View(QWidget *pQParent = nullptr):
QLabel(pQParent)
{ }
virtual ~View() = default;
View(const View&) = delete;
View& operator=(const View&) = delete;
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override;
};
struct Point { int x, y; };
using Color = QColor;
void midpointEllipse(
Point center,
Point radius,
std::function<void(const Color&, const Point&)> setPixel)
{
Point pos = { radius.x, 0 };
Point delta = {
2 * radius.y * radius.y * pos.x,
2 * radius.x * radius.x * pos.y
};
int err = radius.x * radius.x
- radius.y * radius.y * radius.x
+ (radius.y * radius.y) / 4;
while (delta.y < delta.x) {
setPixel(Qt::blue, { center.x + pos.x, center.y + pos.y });
setPixel(Qt::blue, { center.x + pos.x, center.y - pos.y });
setPixel(Qt::blue, { center.x - pos.x, center.y + pos.y });
setPixel(Qt::blue, { center.x - pos.x, center.y - pos.y });
pos.y++;
if (err < 0) {
delta.y += 2 * radius.x * radius.x;
err += delta.y + radius.x * radius.x;
} else {
pos.x--;
delta.y += 2 * radius.x * radius.x;
delta.x -= 2 * radius.y * radius.y;
err += delta.y - delta.x + radius.x * radius.x;
}
}
err = radius.x * radius.x * (pos.y * pos.y + pos.y)
+ radius.y * radius.y * (pos.x - 1) * (pos.x - 1)
- radius.y * radius.y * radius.x * radius.x;
while (pos.x >= 0) {
setPixel(Qt::yellow, { center.x + pos.x, center.y + pos.y });
setPixel(Qt::yellow, { center.x + pos.x, center.y - pos.y });
setPixel(Qt::yellow, { center.x - pos.x, center.y + pos.y });
setPixel(Qt::yellow, { center.x - pos.x, center.y - pos.y });
pos.x--;
if (err > 0) {
delta.x -= 2 * radius.y * radius.y;
err += radius.y * radius.y - delta.x;
} else {
pos.y++;
delta.y += 2 * radius.x * radius.x;
delta.x -= 2 * radius.y * radius.y;
err += delta.y - delta.x + radius.y * radius.y;
}
}
}
void midpointEllipseThick(
Point center,
Point innerRadius,
Point outerRadius,
std::function<void(const Color&, const Point&, int)> horiLine)
{
/// @todo validate/correct innerRadius and outerRadius
Point pos = { outerRadius.x, 0 };
Point deltaOuter = {
2 * outerRadius.y * outerRadius.y * pos.x,
2 * outerRadius.x * outerRadius.x * pos.y
};
auto errOuterYX
= [&]() {
return outerRadius.x * outerRadius.x
- outerRadius.y * outerRadius.y * outerRadius.x
+ (outerRadius.y * outerRadius.y) / 4;
};
auto errOuterXY
= [&]() {
return outerRadius.x * outerRadius.x * (pos.y * pos.y + pos.y)
+ outerRadius.y * outerRadius.y * (pos.x - 1) * (pos.x - 1)
- outerRadius.y * outerRadius.y * outerRadius.x * outerRadius.x;
};
int errOuter = errOuterYX();
int xInner = innerRadius.x;
Point deltaInner = {
2 * innerRadius.y * innerRadius.y * xInner,
2 * innerRadius.x * innerRadius.x * pos.y
};
auto errInnerYX
= [&]() {
return innerRadius.x * innerRadius.x
- innerRadius.y * innerRadius.y * innerRadius.x
+ (innerRadius.y * innerRadius.y) / 4;
};
auto errInnerXY
= [&]() {
return innerRadius.x * innerRadius.x * (pos.y * pos.y + pos.y)
+ innerRadius.y * innerRadius.y * (xInner - 1) * (xInner - 1)
- innerRadius.y * innerRadius.y * innerRadius.x * innerRadius.x;
};
int errInner = errInnerYX();
// helpers (to reduce code duplication)
auto stepOuterYX
= [&]() {
++pos.y;
if (errOuter < 0) {
deltaOuter.y += 2 * outerRadius.x * outerRadius.x;
errOuter += deltaOuter.y + outerRadius.x * outerRadius.x;
} else {
--pos.x;
deltaOuter.y += 2 * outerRadius.x * outerRadius.x;
deltaOuter.x -= 2 * outerRadius.y * outerRadius.y;
errOuter += deltaOuter.y - deltaOuter.x + outerRadius.x * outerRadius.x;
}
};
auto stepOuterXY
= [&]() {
while (--pos.x > 0) {
if (errOuter > 0) {
deltaOuter.x -= 2 * outerRadius.y * outerRadius.y;
errOuter += outerRadius.y * outerRadius.y - deltaOuter.x;
} else {
++pos.y;
deltaOuter.y += 2 * outerRadius.x * outerRadius.x;
deltaOuter.x -= 2 * outerRadius.y * outerRadius.y;
errOuter += deltaOuter.y - deltaOuter.x + outerRadius.y * outerRadius.y;
break;
}
}
};
auto stepInnerYX
= [&]() {
if (errInner < 0) {
deltaInner.y += 2 * innerRadius.x * innerRadius.x;
errInner += deltaInner.y + innerRadius.x * innerRadius.x;
} else {
--xInner;
deltaInner.y += 2 * innerRadius.x * innerRadius.x;
deltaInner.x -= 2 * innerRadius.y * innerRadius.y;
errInner += deltaInner.y - deltaInner.x + innerRadius.x * innerRadius.x;
}
};
auto stepInnerXY
= [&]() {
while (--xInner >= 0) {
if (errInner > 0) {
deltaInner.x -= 2 * innerRadius.y * innerRadius.y;
errInner += innerRadius.y * innerRadius.y - deltaInner.x;
} else {
deltaInner.y += 2 * innerRadius.x * innerRadius.x;
deltaInner.x -= 2 * innerRadius.y * innerRadius.y;
errInner += deltaInner.y - deltaInner.x + innerRadius.y * innerRadius.y;
break;
}
}
};
// 1st phase
while (deltaOuter.y < deltaOuter.x && deltaInner.y < deltaInner.x) {
horiLine(Qt::blue, { center.x - pos.x, center.y + pos.y }, center.x - xInner);
horiLine(Qt::blue, { center.x + pos.x, center.y + pos.y }, center.x + xInner);
horiLine(Qt::blue, { center.x - pos.x, center.y - pos.y }, center.x - xInner);
horiLine(Qt::blue, { center.x + pos.x, center.y - pos.y }, center.x + xInner);
stepOuterYX();
stepInnerYX();
}
// 2nd phase
if (deltaOuter.y < deltaOuter.x) { // inner flipped
//errOuter = errOuterYX();
errInner = errInnerXY();
while (deltaOuter.y < deltaOuter.x && xInner >= 0) {
horiLine(Qt::green, { center.x - pos.x, center.y + pos.y }, center.x - xInner);
horiLine(Qt::green, { center.x + pos.x, center.y + pos.y }, center.x + xInner);
horiLine(Qt::green, { center.x - pos.x, center.y - pos.y }, center.x - xInner);
horiLine(Qt::green, { center.x + pos.x, center.y - pos.y }, center.x + xInner);
stepOuterYX();
stepInnerXY();
}
//errOuter = errOuterYX();
while (deltaOuter.y < deltaOuter.x) {
horiLine(Qt::red, { center.x - pos.x, center.y + pos.y }, center.x + pos.x);
horiLine(Qt::red, { center.x - pos.x, center.y - pos.y }, center.x + pos.x);
stepOuterYX();
}
} else { // outer flipped
errOuter = errOuterXY();
//errInner = errInnerYX();
while (deltaInner.y < deltaInner.x) {
horiLine(Qt::cyan, { center.x - pos.x, center.y + pos.y }, center.x - xInner);
horiLine(Qt::cyan, { center.x + pos.x, center.y + pos.y }, center.x + xInner);
horiLine(Qt::cyan, { center.x - pos.x, center.y - pos.y }, center.x - xInner);
horiLine(Qt::cyan, { center.x + pos.x, center.y - pos.y }, center.x + xInner);
stepOuterXY();
stepInnerYX();
}
//errOuter = errOuterXY();
}
// 3rd phase
errOuter = errOuterXY();
errInner = errInnerXY();
while (xInner >= 0) {
horiLine(Qt::yellow, { center.x - pos.x, center.y + pos.y }, center.x - xInner);
horiLine(Qt::yellow, { center.x + pos.x, center.y + pos.y }, center.x + xInner);
horiLine(Qt::yellow, { center.x - pos.x, center.y - pos.y }, center.x - xInner);
horiLine(Qt::yellow, { center.x + pos.x, center.y - pos.y }, center.x + xInner);
stepOuterXY();
stepInnerXY();
}
// 4th phase
//errOuter = errOuterXY();
while (pos.x >= 0) {
horiLine(Qt::magenta, { center.x - pos.x, center.y + pos.y }, center.x + pos.x);
horiLine(Qt::magenta, { center.x - pos.x, center.y - pos.y }, center.x + pos.x);
stepOuterXY();
}
}
void View::paintEvent(QPaintEvent*)
{
QPainter qPainter(this);
#if 0 // warm up
auto setPixel
= [&](const Color &color, const Point &point)
{
qPainter.setPen(color);
qPainter.drawPoint(point.x, point.y);
};
Point center = { 0.5 * width(), 0.5 * height() };
midpointEllipse(center, center, setPixel);
#else // my attempt to adapt it to thick ellipses
auto horiLine
= [&](const Color &color, const Point &pos0, int x1)
{
qPainter.setPen(color);
qPainter.drawLine(pos0.x, pos0.y, x1, pos0.y);
};
Point center = { 0.5 * width(), 0.5 * height() };
Point innerRadius = { 0.5 * center.x, 0.5 * center.y };
Point outerRadius = { 0.9 * center.x, 0.9 * center.y };
midpointEllipseThick(center, innerRadius, outerRadius, horiLine);
#endif // 0
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup UI
View qWin;
qWin.setWindowTitle(QString::fromUtf8("Draw Thick Ellipse"));
qWin.resize(320, 240);
qWin.show();
// runtime loop
return app.exec();
}
在 VS2017 (Qt 5.11.2) 中编译了一个测试:
我使用颜色来可视化区域和阶段的不同组合。这旨在简单地说明代码的哪一部分负责渲染椭圆的哪一部分。
我有点不确定else
. // 2nd phase
我用
Point center = { 0.5 * width(), 0.5 * height() };
Point innerRadius = { 0.3 * center.x, 0.8 * center.y };
Point outerRadius = { 0.9 * center.x, 0.9 * center.y };
midpointEllipseThick(center, innerRadius, outerRadius, horiLine);
得到了这个:
现在,// 1st phase
由于失败而停止deltaOuter.y < deltaOuter.x
(并出现青色区域)。
OP 抱怨对边缘情况的处理不善,例如innerRadius = outerRadius;
。我使用以下测试集对其进行了检查:
Point center = { 0.5 * width(), 0.5 * height() };
// test edge cases
{ Point outerRadius = { 0.9 * center.x, 0.9 * center.y };
Point innerRadius = { outerRadius.x, outerRadius.y };
Old::midpointEllipseThick(center, innerRadius, outerRadius, horiLine);
}
{ Point outerRadius = { 0.8 * center.x, 0.8 * center.y };
Point innerRadius = { outerRadius.x - 1, outerRadius.y };
Old::midpointEllipseThick(center, innerRadius, outerRadius, horiLine);
}
{ Point outerRadius = { 0.7 * center.x, 0.7 * center.y };
Point innerRadius = { outerRadius.x, outerRadius.y - 1 };
Old::midpointEllipseThick(center, innerRadius, outerRadius, horiLine);
}
{ Point outerRadius = { 0.6 * center.x, 0.6 * center.y };
Point innerRadius = { outerRadius.x - 1, outerRadius.y - 1 };
Old::midpointEllipseThick(center, innerRadius, outerRadius, horiLine);
}
{ Point outerRadius = { 0.5 * center.x, 0.5 * center.y };
Point innerRadius = { outerRadius.x - 2, outerRadius.y - 2 };
Old::midpointEllipseThick(center, innerRadius, outerRadius, horiLine);
}
更改Qt::yellow
为Qt::darkgray
(以获得更好的对比度)并得到这个:
很明显,当 Δx y →y+1 > x Outer - x Inner时出现间隙。
要解决此问题,还必须考虑 ∆x y →y+1来生成跨度线。为了实现这一点,我修改了 ∆x ≥ ∆y 的迭代(在函数的底部):
void midpointEllipseThick(
Point center,
Point innerRadius,
Point outerRadius,
std::function<void(const Color&, const Point&, int)> horiLine)
{
/// @todo validate/correct innerRadius and outerRadius
Point pos = { outerRadius.x, 0 };
Point deltaOuter = {
2 * outerRadius.y * outerRadius.y * pos.x,
2 * outerRadius.x * outerRadius.x * pos.y
};
auto errOuterYX
= [&]() {
return outerRadius.x * outerRadius.x
- outerRadius.y * outerRadius.y * outerRadius.x
+ (outerRadius.y * outerRadius.y) / 4;
};
auto errOuterXY
= [&]() {
return outerRadius.x * outerRadius.x * (pos.y * pos.y + pos.y)
+ outerRadius.y * outerRadius.y * (pos.x - 1) * (pos.x - 1)
- outerRadius.y * outerRadius.y * outerRadius.x * outerRadius.x;
};
int errOuter;
int xInner = innerRadius.x;
Point deltaInner = {
2 * innerRadius.y * innerRadius.y * xInner,
2 * innerRadius.x * innerRadius.x * pos.y
};
auto errInnerYX
= [&]() {
return innerRadius.x * innerRadius.x
- innerRadius.y * innerRadius.y * innerRadius.x
+ (innerRadius.y * innerRadius.y) / 4;
};
auto errInnerXY
= [&]() {
return innerRadius.x * innerRadius.x * (pos.y * pos.y + pos.y)
+ innerRadius.y * innerRadius.y * (xInner - 1) * (xInner - 1)
- innerRadius.y * innerRadius.y * innerRadius.x * innerRadius.x;
};
int errInner;
// helpers (to reduce code duplication)
auto stepOuterYX
= [&]() {
++pos.y;
if (errOuter < 0) {
deltaOuter.y += 2 * outerRadius.x * outerRadius.x;
errOuter += deltaOuter.y + outerRadius.x * outerRadius.x;
} else {
--pos.x;
deltaOuter.y += 2 * outerRadius.x * outerRadius.x;
deltaOuter.x -= 2 * outerRadius.y * outerRadius.y;
errOuter += deltaOuter.y - deltaOuter.x + outerRadius.x * outerRadius.x;
}
};
auto stepInnerYX
= [&]() {
if (errInner < 0) {
deltaInner.y += 2 * innerRadius.x * innerRadius.x;
errInner += deltaInner.y + innerRadius.x * innerRadius.x;
} else {
--xInner;
deltaInner.y += 2 * innerRadius.x * innerRadius.x;
deltaInner.x -= 2 * innerRadius.y * innerRadius.y;
errInner += deltaInner.y - deltaInner.x + innerRadius.x * innerRadius.x;
}
};
auto stepOuterXY
= [&]() {
while (--pos.x >= 0) {
if (errOuter > 0) {
deltaOuter.x -= 2 * outerRadius.y * outerRadius.y;
errOuter += outerRadius.y * outerRadius.y - deltaOuter.x;
} else {
++pos.y;
deltaOuter.y += 2 * outerRadius.x * outerRadius.x;
deltaOuter.x -= 2 * outerRadius.y * outerRadius.y;
errOuter += deltaOuter.y - deltaOuter.x + outerRadius.y * outerRadius.y;
break;
}
}
};
auto stepInnerXY
= [&]() {
while (--xInner >= 0) {
if (errInner > 0) {
deltaInner.x -= 2 * innerRadius.y * innerRadius.y;
errInner += innerRadius.y * innerRadius.y - deltaInner.x;
} else {
deltaInner.y += 2 * innerRadius.x * innerRadius.x;
deltaInner.x -= 2 * innerRadius.y * innerRadius.y;
errInner += deltaInner.y - deltaInner.x + innerRadius.y * innerRadius.y;
break;
}
}
};
auto min
= [](int x1, int x2, int x3) {
return std::min(std::min(x1, x2), x3);
};
// 1st phase
errOuter = errOuterYX(); // init error for delta y < delta x
errInner = errInnerYX(); // init error for delta y < delta x
while (deltaOuter.y < deltaOuter.x && deltaInner.y < deltaInner.x) {
horiLine(Qt::blue, { center.x - pos.x, center.y + pos.y }, center.x - xInner);
horiLine(Qt::blue, { center.x + pos.x, center.y + pos.y }, center.x + xInner);
horiLine(Qt::blue, { center.x - pos.x, center.y - pos.y }, center.x - xInner);
horiLine(Qt::blue, { center.x + pos.x, center.y - pos.y }, center.x + xInner);
stepOuterYX();
stepInnerYX();
}
// 2nd phase
if (deltaOuter.y < deltaOuter.x) { // inner flipped
//errOuter = errOuterYX(); // still delta y < delta x
errInner = errInnerXY(); // init error for delta x < delta y
while (deltaOuter.y < deltaOuter.x && xInner >= 0) {
horiLine(Qt::green, { center.x - pos.x, center.y + pos.y }, center.x - xInner);
horiLine(Qt::green, { center.x + pos.x, center.y + pos.y }, center.x + xInner);
horiLine(Qt::green, { center.x - pos.x, center.y - pos.y }, center.x - xInner);
horiLine(Qt::green, { center.x + pos.x, center.y - pos.y }, center.x + xInner);
stepOuterYX();
stepInnerXY();
}
//errOuter = errOuterYX(); // still delta y < delta x
while (deltaOuter.y < deltaOuter.x) {
horiLine(Qt::red, { center.x - pos.x, center.y + pos.y }, center.x + pos.x);
horiLine(Qt::red, { center.x - pos.x, center.y - pos.y }, center.x + pos.x);
stepOuterYX();
}
} else { // outer flipped
errOuter = errOuterXY(); // init error for delta x < delta y
//errInner = errInnerYX(); // still delta y < delta x
while (deltaInner.y < deltaInner.x) {
Point pos_ = pos;
stepOuterXY();
stepInnerYX();
int xInner_ = std::min(pos.x, xInner);
horiLine(Qt::cyan, { center.x - pos_.x, center.y + pos_.y }, center.x - xInner_);
horiLine(Qt::cyan, { center.x + pos_.x, center.y + pos_.y }, center.x + xInner_);
horiLine(Qt::cyan, { center.x - pos_.x, center.y - pos_.y }, center.x - xInner_);
horiLine(Qt::cyan, { center.x + pos_.x, center.y - pos_.y }, center.x + xInner_);
}
}
// 3rd phase
errOuter = errOuterXY(); // init error for delta x < delta y
errInner = errInnerXY(); // init error for delta x < delta y
while (xInner >= 0) {
Point pos_ = pos;
stepOuterXY();
int xInner_ = std::min(pos.x, xInner);
horiLine(Qt::darkGray, { center.x - pos_.x, center.y + pos_.y }, center.x - xInner_);
horiLine(Qt::darkGray, { center.x + pos_.x, center.y + pos_.y }, center.x + xInner_);
horiLine(Qt::darkGray, { center.x - pos_.x, center.y - pos_.y }, center.x - xInner_);
horiLine(Qt::darkGray, { center.x + pos_.x, center.y - pos_.y }, center.x + xInner_);
stepInnerXY();
}
// 4th phase
//errOuter = errOuterXY(); // still delta x < delta y
while (pos.x >= 0) {
horiLine(Qt::magenta, { center.x - pos.x, center.y + pos.y }, center.x + pos.x + 1);
horiLine(Qt::magenta, { center.x - pos.x, center.y - pos.y }, center.x + pos.x + 1);
stepOuterXY();
}
}
结果看起来还不错:
间隙被移除。
我意识到还有另一个关于一个错误的抱怨问题:
椭圆顶部和底部的厚度似乎太小了一个像素。
嗯……这是一个定义问题。无论何时必须给出一个范围,都必须说明 start 和 end 是(每个)包含还是不包含。(例如,与标准容器中的迭代器范围进行比较——开始→包含,结束→排除。)
Qt 文档。为这个主题Coordinate System专门增加了一章。
我必须承认:我当前的算法处理水平和垂直方向的这种不同,我认为这是“丑陋”。恕我直言,最简单的解决方法是使其水平和垂直一致。之后是医生。可能会分别调整。
员工:“老板!我们最近生产的水桶有一个洞并且会失水。”
老板:“很高兴知道。我们应该在手册中提到这一点。”
因此,我通过调整horiLine
辅助 lambda 来固定水平边框大小:
auto horiLine
= [&](const Color &color, const Point &pos0, int x1)
{
qPainter.setPen(color);
if (x1 != pos0.x) x1 += x1 < pos0.x ? +1 : -1;
qPainter.drawLine(pos0.x, pos0.y, x1, pos0.y);
};
现在,我认为结果至少是一致的(如果不令人满意的话):
现在innerRadius
显示为独家。如果这不是故意的,请分别。可以在开始时对参数进行预调整midpointEllipseThick()
。
推荐阅读
- javascript - 如何使用 Puppeteer 获取元素数组并单击随机元素
- spring-mvc - springfox-swagger-3-0- 不适用于非启动 spring4 mvc 项目
- heroku - 如何知道谁在使用应用程序名称?
- angular - Angular 应用的下拉值未预先填充
- mysql - Sequelize - 无法为 1:N 关系添加外键约束
- javascript - 如何使用网络上的身份验证规则从 Firebase 实时数据库中读取数据?
- javascript - 将标头发送到客户端后无法设置标头 - 错误
- r - R找到忽略NA的元组组
- apache-flink - 指定 TimeCharacteristic 为 EventTime,但不指定水印策略
- c++ - 为什么“CryptCATAdminEnumCatalogFromHash”会为非交互式用户返回 ERROR_NOT_FOUND (1062?)?