我真的很接近使粗略的椭圆算法起作用,但是我有点麻烦。我采用了here的中点粗圆算法和here的中点椭圆算法,并且试图将它们组合在一起以得到中点粗椭圆算法。我这样做是因为谷歌搜索“中点粗椭圆算法”没有显示我在寻找什么。我的尝试输出类似于一个粗圆圈(图像在帖子的底部)。
这是图像代码(只是一个占位符):
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}
的粗椭圆:
我已经接近了,但还没到那儿。谁能比我更了解这些东西?
答案 0 :(得分:8)
我必须承认,我坚信圆形比椭圆形对称得多。如果圆可以在通过中心的任何轴上镜像,对于椭圆形,通常只有在x和y轴上才有可能。因此,我认为midPointCircleThick()
无法适应椭圆。
因此,我从OP提供的midpointEllipse()
开始实施。
这些是我的基本想法:
恕我直言,Bresenham Line algorithm是Midpoint Circle algorithm以及中点椭圆算法的起源。这可能有助于理解所使用的错误/增量魔术。对于一行来说,它要简单得多,但遵循的相同思想适用于x²/a²+y²/b²= 1(the ellipse equation)。
midpointEllipse()
的原点位于椭圆的中心,同时渲染了所有四个象限(利用对称性)。因此,仅必须有效地计算一个象限中的曲线。曲线在该区域是单调的。
midpointEllipse()
有两个区域:
我的想法是采用这样的方式来适应midpointEllipse()
:将代码“复制”以管理具有相同y坐标的两个点(一个用于内部边界,一个用于外部)以绘制水平线(跨度线)
我的第一个观察结果是,新算法必须管理最后一个阶段(对于innerRadius.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 外部-x 内部时,显然会出现间隙。
要解决此问题,在生成跨度线时也必须考虑∆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();
}
}
结果看起来还不错:
间隙被消除。
我意识到,还有一个抱怨过的错误:
椭圆的顶部和底部的厚度似乎太小了一个像素。
嗯……这是一个定义问题。每当必须给出范围时,就必须说出开始和结束是(每个)包容性还是排他性。 (例如,与标准容器中的迭代器范围进行比较-开始→包含,结束→排除。)
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()
的开头进行参数的预先调整。
答案 1 :(得分:0)