// An arc in Direct2D is defined by the chord between the endpoints.
// There are four possible arcs with the same two endpoints that you can draw this way.
// See https://www.youtube.com/watch?v=ATS0ANW1UxQ for a demonstration.
// There is a property rotationAngle which deals with the rotation /of the entire ellipse that forms an ellpitical arc/ - it's effectively a transformation on the arc.
// That is to say, it's NOT THE SWEEP.
// The sweep is defined by the start and end points and whether the arc is "large".
// As a result, this design does not allow for full circles or ellipses with a single arc; they have to be simulated with two.
// as above, we can't do a full circle with one arc
// simulate it with two half-circles
// of course, we have a dragon: equality on floating-point values!
// I've chosen to do the AlmostEqualRelative() technique in https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
fullCircle=FALSE;
// use the absolute value to tackle both ≥2π and ≤-2π at the same time
absSweep=fabs(a->sweep);
if(absSweep>(2*M_PI))// this part is easy
fullCircle=TRUE;
else{
doubleaerDiff;
aerDiff=fabs(absSweep-(2*M_PI));
// if we got here then we know a->sweep is larger (or the same!)
fullCircle=aerDiff<=absSweep*aerMax;
}
// TODO make sure this works right for the negative direction
if(fullCircle){
a->sweep=M_PI;
drawArc(p,a,startFunction);
a->startAngle+=M_PI;
drawArc(p,a,NULL);
return;
}
// first, figure out the arc's endpoints
// unfortunately D2D1SinCos() is only defined on Windows 8 and newer
// the MSDN page doesn't say this, but says it requires d2d1_1.h, which is listed as only supported on Windows 8 and newer elsewhere on MSDN
// so we must use sin() and cos() and hope it's right...
sinx=sin(a->startAngle);
cosx=cos(a->startAngle);
startX=a->xCenter+a->radius*cosx;
startY=a->yCenter+a->radius*sinx;
sinx=sin(a->startAngle+a->sweep);
cosx=cos(a->startAngle+a->sweep);
endX=a->xCenter+a->radius*cosx;
endY=a->yCenter+a->radius*sinx;
// now do the initial step to get the current point to be the start point
// this is either creating a new figure, drawing a line, or (in the case of our full circle code above) doing nothing
if(startFunction!=NULL)
(*startFunction)(p,startX,startY);
// now we can draw the arc
as.point.x=endX;
as.point.y=endY;
as.size.width=a->radius;
as.size.height=a->radius;
as.rotationAngle=0;// as above, not relevant for circles