将 drawEllipse 的 PyQt 实现转换为 Matlab
Conversion of PyQt implementation of drawEllipse into Matlab
我有一个 python 程序可以将一些椭圆绘制成 window。下面的python代码用到了w
from PyQt4.QtGui import *
def draw_ellipse(self, center, rad_x, rad_y, angle, color):
qp = QtGui.QPainter()
qp.begin(self)
qp.translate(center)
qp.rotate(math.degrees(angle))
qp.setPen(QtGui.QColor(color))
qp.drawEllipse(QPoint(0, 0), rad_x, rad_y)
qp.end()
如您所见,我的输入参数是 center
、rad_x
、rad_y
和 angle
。这些参数是从文本文件中读取的。
我想在 Matlab 程序中使用完全相同的参数文件。为此,我需要了解 drawEllipse
的实现,以便我可以在 Matlab 中实现完全相同的功能。
不幸的是,我似乎没有找到 drawEllipse
的源代码。我发现 this link 具有以下代码:
03114 {
03115 #ifdef QT_DEBUG_DRAW
03116 if (qt_show_painter_debug_output)
03117 printf("QPainter::drawEllipse(), [%.2f,%.2f,%.2f,%.2f]\n", r.x(), r.y(), r.width(), r.height());
03118 #endif
03119
03120 if (!isActive())
03121 return;
03122 Q_D(QPainter);
03123 d->updateState(d->state);
03124
03125 QRectF rect(r.normalized());
03126
03127 if (rect.isEmpty())
03128 return;
03129
03130 if (d->state->emulationSpecifier) {
03131 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
03132 && d->state->txop == QPainterPrivate::TxTranslate) {
03133 rect.translate(QPointF(d->state->matrix.dx(), d->state->matrix.dy()));
03134 } else {
03135 QPainterPath path;
03136 path.addEllipse(rect);
03137 d->draw_helper(path, QPainterPrivate::StrokeAndFillDraw);
03138 return;
03139 }
03140 }
03141
03142 d->engine->drawEllipse(rect);
03143 }
这导致我 this 代码 (QPainterPath.addEllipse
):
01052 void QPainterPath::addEllipse(const QRectF &boundingRect)
01053 {
01054 #ifndef QT_NO_DEBUG
01055 if (qIsNan(boundingRect.x()) || qIsNan(boundingRect.y())
01056 || qIsNan(boundingRect.width()) || qIsNan(boundingRect.height()))
01057 qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN, results are undefined");
01058 #endif
01059 if (boundingRect.isNull())
01060 return;
01061
01062 ensureData();
01063 detach();
01064
01065 Q_D(QPainterPath);
01066 d->elements.reserve(d->elements.size() + 13);
01067
01068 QPointF pts[12];
01069 int point_count;
01070 QPointF start = qt_curves_for_arc(boundingRect, 0, 360, pts, &point_count);
01071
01072 moveTo(start);
01073 cubicTo(pts[0], pts[1], pts[2]); // 0 -> 270
01074 cubicTo(pts[3], pts[4], pts[5]); // 270 -> 180
01075 cubicTo(pts[6], pts[7], pts[8]); // 180 -> 90
01076 cubicTo(pts[9], pts[10], pts[11]); // 90 - >0
01077 d_func()->require_moveTo = true;
01078 }
所以让我们进入 qstroker_8cpp 看看 qt_curves_for_arc
:
00722 QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
00723 QPointF *curves, int *point_count)
00724 {
00725 Q_ASSERT(point_count);
00726 Q_ASSERT(curves);
00727
00728 #ifndef QT_NO_DEBUG
00729 if (qIsNan(rect.x()) || qIsNan(rect.y()) || qIsNan(rect.width()) || qIsNan(rect.height())
00730 || qIsNan(startAngle) || qIsNan(sweepLength))
00731 qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
00732 #endif
00733 *point_count = 0;
00734
00735 if (rect.isNull()) {
00736 return QPointF();
00737 }
00738
00739 if (sweepLength > 360) sweepLength = 360;
00740 else if (sweepLength < -360) sweepLength = -360;
00741
00742 // Special case fast path
00743 if (startAngle == 0.0 && sweepLength == 360.0) {
00744 qreal x = rect.x();
00745 qreal y = rect.y();
00746
00747 qreal w = rect.width();
00748 qreal w2 = rect.width() / 2;
00749 qreal w2k = w2 * QT_PATH_KAPPA;
00750
00751 qreal h = rect.height();
00752 qreal h2 = rect.height() / 2;
00753 qreal h2k = h2 * QT_PATH_KAPPA;
00754
00755 // 0 -> 270 degrees
00756 curves[(*point_count)++] = QPointF(x + w, y + h2 + h2k);
00757 curves[(*point_count)++] = QPointF(x + w2 + w2k, y + h);
00758 curves[(*point_count)++] = QPointF(x + w2, y + h);
00759
00760 // 270 -> 180 degrees
00761 curves[(*point_count)++] = QPointF(x + w2 - w2k, y + h);
00762 curves[(*point_count)++] = QPointF(x, y + h2 + h2k);
00763 curves[(*point_count)++] = QPointF(x, y + h2);
00764
00765 // 180 -> 90 degrees
00766 curves[(*point_count)++] = QPointF(x, y + h2 - h2k);
00767 curves[(*point_count)++] = QPointF(x + w2 - w2k, y);
00768 curves[(*point_count)++] = QPointF(x + w2, y);
00769
00770 // 90 -> 0 degrees
00771 curves[(*point_count)++] = QPointF(x + w2 + w2k, y);
00772 curves[(*point_count)++] = QPointF(x + w, y + h2 - h2k);
00773 curves[(*point_count)++] = QPointF(x + w, y + h2);
00774
00775 return QPointF(x + w, y + h2);
00776 }
00777
00778 #define ANGLE(t) ((t) * 2 * Q_PI / 360.0)
00779 #define SIGN(t) (t > 0 ? 1 : -1)
00780 qreal a = rect.width() / 2.0;
00781 qreal b = rect.height() / 2.0;
00782
00783 qreal absSweepLength = (sweepLength < 0 ? -sweepLength : sweepLength);
00784 int iterations = (int)ceil((absSweepLength) / 90.0);
00785
00786 QPointF first_point;
00787
00788 if (iterations == 0) {
00789 first_point = rect.center() + QPointF(a * qCos(ANGLE(startAngle)),
00790 -b * qSin(ANGLE(startAngle)));
00791 } else {
00792 qreal clength = sweepLength / iterations;
00793 qreal cosangle1, sinangle1, cosangle2, sinangle2;
00794
00795 for (int i=0; i<iterations; ++i) {
00796 qreal cangle = startAngle + i * clength;
00797
00798 cosangle1 = qCos(ANGLE(cangle));
00799 sinangle1 = qSin(ANGLE(cangle));
00800 cosangle2 = qCos(ANGLE(cangle + clength));
00801 sinangle2 = qSin(ANGLE(cangle + clength));
00802
00803 // Find the start and end point of the curve.
00804 QPointF startPoint = rect.center() + QPointF(a * cosangle1, -b * sinangle1);
00805 QPointF endPoint = rect.center() + QPointF(a * cosangle2, -b * sinangle2);
00806
00807 // The derived at the start and end point.
00808 qreal sdx = -a * sinangle1;
00809 qreal sdy = -b * cosangle1;
00810 qreal edx = -a * sinangle2;
00811 qreal edy = -b * cosangle2;
00812
00813 // Creating the tangent lines. We need to reverse their direction if the
00814 // sweep is negative (clockwise)
00815 QLineF controlLine1(startPoint, startPoint + SIGN(sweepLength) * QPointF(sdx, sdy));
00816 QLineF controlLine2(endPoint, endPoint - SIGN(sweepLength) * QPointF(edx, edy));
00817
00818 // We need to scale down the control lines to match that of the current sweeplength.
00819 // qAbs because we only want to scale, not change direction.
00820 qreal kappa = QT_PATH_KAPPA * qAbs(clength) / 90.0;
00821 // Adjust their length to fit the magic KAPPA length.
00822 controlLine1.setLength(controlLine1.length() * kappa);
00823 controlLine2.setLength(controlLine2.length() * kappa);
00824
00825 curves[(*point_count)++] = controlLine1.p2();
00826 curves[(*point_count)++] = controlLine2.p2();
00827 curves[(*point_count)++] = endPoint;
00828
00829 if (i == 0)
00830 first_point = startPoint;
00831 }
00832 }
00833
00834 return first_point;
00835 }
绘制一个简单的椭圆,代码量相当大!在 Matlab 中重写所有这些感觉不对,如果一个简单的椭圆可以像这样绘制:
a = rad_x; % horizontal radius
b = rad_y; % vertical radius
x0 = center_x; % x0, y0 ellipse centre coordinates
y0 = center_y;
steps = 50;
t = linspace(0, 2*pi, steps);
theta0 = angle;
x = x0 + (a * sin(t - theta0));
y = y0 + (b * cos(t));
plot(x, y, '.-'),
问题:
鉴于上面列出的四个参数(center
、rad_x
、rad_y
和 angle
),我在 matlab 中正确绘制椭圆的最简单方法是什么?使用我上面的 matlab 代码绘图目前仅适用于小角度和特定的 rad_x
& rad_y
组合。
这样的怎么样?
a = rad_x;
b = rad_y;
r0 = center_x + i*center_y; % <-- origin of ellipse
theta0=angle; % <-- angle of rotation of ellipse
steps = 200;
t = linspace(0, 2*pi, steps);
r = a*sin(t) + i*b*cos(t);
R = exp(i*theta0);
r = R*r;
r = r0 + r;
figure(1), hold on
plot(real(r), imag(r), '-r')
使用矩阵而不是复数:
a = rad_x;
b = rad_y;
r0 = [center_x ; center_y]; % <-- origin of ellipse
theta0=angle; % <-- angle of rotation of ellipse
steps = 200;
t = linspace(0, 2*pi, steps);
r = [a*sin(t) ; b*cos(t)];
R = [cos(theta0) -sin(theta0) ; sin(theta0) cos(theta0)]; % <-- note neg signs: define direction of rotation!
r = R*r;
r = r0*ones(1,length(t)) + r;
figure(1)
plot(r(1,:), r(2,:), '-r')
我有一个 python 程序可以将一些椭圆绘制成 window。下面的python代码用到了w
from PyQt4.QtGui import *
def draw_ellipse(self, center, rad_x, rad_y, angle, color):
qp = QtGui.QPainter()
qp.begin(self)
qp.translate(center)
qp.rotate(math.degrees(angle))
qp.setPen(QtGui.QColor(color))
qp.drawEllipse(QPoint(0, 0), rad_x, rad_y)
qp.end()
如您所见,我的输入参数是 center
、rad_x
、rad_y
和 angle
。这些参数是从文本文件中读取的。
我想在 Matlab 程序中使用完全相同的参数文件。为此,我需要了解 drawEllipse
的实现,以便我可以在 Matlab 中实现完全相同的功能。
不幸的是,我似乎没有找到 drawEllipse
的源代码。我发现 this link 具有以下代码:
03114 {
03115 #ifdef QT_DEBUG_DRAW
03116 if (qt_show_painter_debug_output)
03117 printf("QPainter::drawEllipse(), [%.2f,%.2f,%.2f,%.2f]\n", r.x(), r.y(), r.width(), r.height());
03118 #endif
03119
03120 if (!isActive())
03121 return;
03122 Q_D(QPainter);
03123 d->updateState(d->state);
03124
03125 QRectF rect(r.normalized());
03126
03127 if (rect.isEmpty())
03128 return;
03129
03130 if (d->state->emulationSpecifier) {
03131 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
03132 && d->state->txop == QPainterPrivate::TxTranslate) {
03133 rect.translate(QPointF(d->state->matrix.dx(), d->state->matrix.dy()));
03134 } else {
03135 QPainterPath path;
03136 path.addEllipse(rect);
03137 d->draw_helper(path, QPainterPrivate::StrokeAndFillDraw);
03138 return;
03139 }
03140 }
03141
03142 d->engine->drawEllipse(rect);
03143 }
这导致我 this 代码 (QPainterPath.addEllipse
):
01052 void QPainterPath::addEllipse(const QRectF &boundingRect)
01053 {
01054 #ifndef QT_NO_DEBUG
01055 if (qIsNan(boundingRect.x()) || qIsNan(boundingRect.y())
01056 || qIsNan(boundingRect.width()) || qIsNan(boundingRect.height()))
01057 qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN, results are undefined");
01058 #endif
01059 if (boundingRect.isNull())
01060 return;
01061
01062 ensureData();
01063 detach();
01064
01065 Q_D(QPainterPath);
01066 d->elements.reserve(d->elements.size() + 13);
01067
01068 QPointF pts[12];
01069 int point_count;
01070 QPointF start = qt_curves_for_arc(boundingRect, 0, 360, pts, &point_count);
01071
01072 moveTo(start);
01073 cubicTo(pts[0], pts[1], pts[2]); // 0 -> 270
01074 cubicTo(pts[3], pts[4], pts[5]); // 270 -> 180
01075 cubicTo(pts[6], pts[7], pts[8]); // 180 -> 90
01076 cubicTo(pts[9], pts[10], pts[11]); // 90 - >0
01077 d_func()->require_moveTo = true;
01078 }
所以让我们进入 qstroker_8cpp 看看 qt_curves_for_arc
:
00722 QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
00723 QPointF *curves, int *point_count)
00724 {
00725 Q_ASSERT(point_count);
00726 Q_ASSERT(curves);
00727
00728 #ifndef QT_NO_DEBUG
00729 if (qIsNan(rect.x()) || qIsNan(rect.y()) || qIsNan(rect.width()) || qIsNan(rect.height())
00730 || qIsNan(startAngle) || qIsNan(sweepLength))
00731 qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
00732 #endif
00733 *point_count = 0;
00734
00735 if (rect.isNull()) {
00736 return QPointF();
00737 }
00738
00739 if (sweepLength > 360) sweepLength = 360;
00740 else if (sweepLength < -360) sweepLength = -360;
00741
00742 // Special case fast path
00743 if (startAngle == 0.0 && sweepLength == 360.0) {
00744 qreal x = rect.x();
00745 qreal y = rect.y();
00746
00747 qreal w = rect.width();
00748 qreal w2 = rect.width() / 2;
00749 qreal w2k = w2 * QT_PATH_KAPPA;
00750
00751 qreal h = rect.height();
00752 qreal h2 = rect.height() / 2;
00753 qreal h2k = h2 * QT_PATH_KAPPA;
00754
00755 // 0 -> 270 degrees
00756 curves[(*point_count)++] = QPointF(x + w, y + h2 + h2k);
00757 curves[(*point_count)++] = QPointF(x + w2 + w2k, y + h);
00758 curves[(*point_count)++] = QPointF(x + w2, y + h);
00759
00760 // 270 -> 180 degrees
00761 curves[(*point_count)++] = QPointF(x + w2 - w2k, y + h);
00762 curves[(*point_count)++] = QPointF(x, y + h2 + h2k);
00763 curves[(*point_count)++] = QPointF(x, y + h2);
00764
00765 // 180 -> 90 degrees
00766 curves[(*point_count)++] = QPointF(x, y + h2 - h2k);
00767 curves[(*point_count)++] = QPointF(x + w2 - w2k, y);
00768 curves[(*point_count)++] = QPointF(x + w2, y);
00769
00770 // 90 -> 0 degrees
00771 curves[(*point_count)++] = QPointF(x + w2 + w2k, y);
00772 curves[(*point_count)++] = QPointF(x + w, y + h2 - h2k);
00773 curves[(*point_count)++] = QPointF(x + w, y + h2);
00774
00775 return QPointF(x + w, y + h2);
00776 }
00777
00778 #define ANGLE(t) ((t) * 2 * Q_PI / 360.0)
00779 #define SIGN(t) (t > 0 ? 1 : -1)
00780 qreal a = rect.width() / 2.0;
00781 qreal b = rect.height() / 2.0;
00782
00783 qreal absSweepLength = (sweepLength < 0 ? -sweepLength : sweepLength);
00784 int iterations = (int)ceil((absSweepLength) / 90.0);
00785
00786 QPointF first_point;
00787
00788 if (iterations == 0) {
00789 first_point = rect.center() + QPointF(a * qCos(ANGLE(startAngle)),
00790 -b * qSin(ANGLE(startAngle)));
00791 } else {
00792 qreal clength = sweepLength / iterations;
00793 qreal cosangle1, sinangle1, cosangle2, sinangle2;
00794
00795 for (int i=0; i<iterations; ++i) {
00796 qreal cangle = startAngle + i * clength;
00797
00798 cosangle1 = qCos(ANGLE(cangle));
00799 sinangle1 = qSin(ANGLE(cangle));
00800 cosangle2 = qCos(ANGLE(cangle + clength));
00801 sinangle2 = qSin(ANGLE(cangle + clength));
00802
00803 // Find the start and end point of the curve.
00804 QPointF startPoint = rect.center() + QPointF(a * cosangle1, -b * sinangle1);
00805 QPointF endPoint = rect.center() + QPointF(a * cosangle2, -b * sinangle2);
00806
00807 // The derived at the start and end point.
00808 qreal sdx = -a * sinangle1;
00809 qreal sdy = -b * cosangle1;
00810 qreal edx = -a * sinangle2;
00811 qreal edy = -b * cosangle2;
00812
00813 // Creating the tangent lines. We need to reverse their direction if the
00814 // sweep is negative (clockwise)
00815 QLineF controlLine1(startPoint, startPoint + SIGN(sweepLength) * QPointF(sdx, sdy));
00816 QLineF controlLine2(endPoint, endPoint - SIGN(sweepLength) * QPointF(edx, edy));
00817
00818 // We need to scale down the control lines to match that of the current sweeplength.
00819 // qAbs because we only want to scale, not change direction.
00820 qreal kappa = QT_PATH_KAPPA * qAbs(clength) / 90.0;
00821 // Adjust their length to fit the magic KAPPA length.
00822 controlLine1.setLength(controlLine1.length() * kappa);
00823 controlLine2.setLength(controlLine2.length() * kappa);
00824
00825 curves[(*point_count)++] = controlLine1.p2();
00826 curves[(*point_count)++] = controlLine2.p2();
00827 curves[(*point_count)++] = endPoint;
00828
00829 if (i == 0)
00830 first_point = startPoint;
00831 }
00832 }
00833
00834 return first_point;
00835 }
绘制一个简单的椭圆,代码量相当大!在 Matlab 中重写所有这些感觉不对,如果一个简单的椭圆可以像这样绘制:
a = rad_x; % horizontal radius
b = rad_y; % vertical radius
x0 = center_x; % x0, y0 ellipse centre coordinates
y0 = center_y;
steps = 50;
t = linspace(0, 2*pi, steps);
theta0 = angle;
x = x0 + (a * sin(t - theta0));
y = y0 + (b * cos(t));
plot(x, y, '.-'),
问题:
鉴于上面列出的四个参数(center
、rad_x
、rad_y
和 angle
),我在 matlab 中正确绘制椭圆的最简单方法是什么?使用我上面的 matlab 代码绘图目前仅适用于小角度和特定的 rad_x
& rad_y
组合。
这样的怎么样?
a = rad_x;
b = rad_y;
r0 = center_x + i*center_y; % <-- origin of ellipse
theta0=angle; % <-- angle of rotation of ellipse
steps = 200;
t = linspace(0, 2*pi, steps);
r = a*sin(t) + i*b*cos(t);
R = exp(i*theta0);
r = R*r;
r = r0 + r;
figure(1), hold on
plot(real(r), imag(r), '-r')
使用矩阵而不是复数:
a = rad_x;
b = rad_y;
r0 = [center_x ; center_y]; % <-- origin of ellipse
theta0=angle; % <-- angle of rotation of ellipse
steps = 200;
t = linspace(0, 2*pi, steps);
r = [a*sin(t) ; b*cos(t)];
R = [cos(theta0) -sin(theta0) ; sin(theta0) cos(theta0)]; % <-- note neg signs: define direction of rotation!
r = R*r;
r = r0*ones(1,length(t)) + r;
figure(1)
plot(r(1,:), r(2,:), '-r')