-1

I have a python program which draws some ellipses into a window. The following python code is used 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()

As you can see my input parameters are center, rad_x, rad_y and angle. These parameters are read in from a text file.

I want to use the very same parameter file in a Matlab program. For that I need to know the implementation of drawEllipse, so that I can implement the very same functionality in Matlab.

Unfortunately I don't seem to find the source code for drawEllipse. I found this link with the following code:

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 }

Which leads me to this code (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 }

So let's go into qstroker_8cpp and look at 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 }

This is quite a lot of code for drawing a simple ellipse! It doesn't feel right to rewrite all of this in Matlab, if a simple ellipse can be plotted like this:

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, '.-'),

Question:
given the four parameters listed above (center, rad_x, rad_y and angle), what's the easiest way for me to plot the ellipse correctly in matlab? With my matlab code above plotting currently only works for small angles and particular rad_x & rad_y combinations.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
memyself
  • 11,907
  • 14
  • 61
  • 102
  • Did you check out this [site](http://matlab.wikia.com/wiki/FAQ#How_do_I_create_an_ellipse.3F)? – Nemesis Jan 09 '15 at 11:15
  • @Nemesis it doesn't address the problem. I know how to plot an ellipse, see my code. What I'm missing is a translation of the four parameters into a generic form so I can plot with matlab. – memyself Jan 09 '15 at 11:18
  • Sorry, then I didn't understand you question right? What do you mean with generic form? A function? Why does is only work for specific cases? – Nemesis Jan 09 '15 at 11:20
  • look at the last python code snippet (qt_curves_for_arc) - you can see how different cases are implemented. – memyself Jan 09 '15 at 11:22
  • There are surely many different ways to draw an ellipse. I don't understand why you directly want to convert the code. Matlab can draw ellipses, Python can draw ellipses. All you have to do is converting the parameters. – NoDataDumpNoContribution Jan 28 '15 at 10:43

1 Answers1

1

How about something like this?

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')

Using matrices rather than complex numbers:

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')
Buck Thorn
  • 5,024
  • 2
  • 17
  • 27
  • can you please edit your code and use the parameter names `center`, `rad_x`, `rad_y` and `angle` as indicated in my question? that would make it easier to understand your code. – memyself Jan 09 '15 at 13:39
  • Please also notice that `center` is a tuple indicating the x & y coordinate of the ellipse center. – memyself Jan 09 '15 at 13:45
  • @memyself You don't use `angle`. All other variables are in there, just substitute the equalities with your own definitions. One simple way to plot multiple ellipses is to loop through the parameters, otherwise you may be able to use one of MATLAB's tricks to get a more concise expression. – Buck Thorn Jan 09 '15 at 17:02
  • This is great! could you explain your code a bit? I'm interesting in understanding a) what you are doing and b) how you figured out how to translate from python to matlab. Thanks! – memyself Jan 09 '15 at 17:12
  • 1
    The python code you posted refers to libraries that plot ellipses for you. I don't see the point in trying to recreate those in matlab. Rather I continue along the path you lay out at the end of your post, just write a short routine that implements the 2D geometric problem of creating an ellipse with major axes along x and y, rotating it into a new coord system, and shifting the origin. For conciseness I used complex numbers but I could have (maybe should have even) used matrices. I will add on how to use matrices explicitly rather than complex numbers. – Buck Thorn Jan 09 '15 at 17:17