Bug 471281. Reimplement arcTo() to match Canvas spec. r=jrmuizel

This is based on Philip Taylor's emulation written in JavasScript.
This commit is contained in:
Dirk Schulze 2009-06-17 13:57:50 -04:00
Родитель 2b712738f6
Коммит b8ea13fb7a
9 изменённых файлов: 49 добавлений и 79 удалений

Просмотреть файл

@ -1751,83 +1751,53 @@ nsCanvasRenderingContext2D::ArcTo(float x1, float y1, float x2, float y2, float
if (!FloatValidate(x1,y1,x2,y2,radius)) if (!FloatValidate(x1,y1,x2,y2,radius))
return NS_ERROR_DOM_SYNTAX_ERR; return NS_ERROR_DOM_SYNTAX_ERR;
if (radius <= 0) if (radius < 0)
return NS_ERROR_DOM_INDEX_SIZE_ERR; return NS_ERROR_DOM_INDEX_SIZE_ERR;
/* This is an adaptation of the cairo_arc_to patch from Behdad
* Esfahbod; once that patch is accepted, we should remove this
* and just call cairo_arc_to() directly.
*/
double angle0, angle1, angle2, angled;
double d0, d2;
double sin_, cos_;
double dc;
int forward;
gfxPoint p0 = mThebes->CurrentPoint(); gfxPoint p0 = mThebes->CurrentPoint();
angle0 = atan2 (p0.y - y1, p0.x - x1); /* angle from (x1,y1) to (p0.x,p0.y) */ double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
angle2 = atan2 (y2 - y1, x2 - x1); /* angle from (x1,y1) to (x2,y2) */ bool anticlockwise;
angle1 = (angle0 + angle2) / 2; /* angle from (x1,y1) to (xc,yc) */
angled = angle2 - angle0; /* the angle (p0.x,p0.y)--(x1,y1)--(x2,y2) */ if ((x1 == p0.x && y1 == p0.y) || (x1 == x2 && y1 == y2) || radius == 0) {
mThebes->LineTo(gfxPoint(x1, y1));
/* Shall we go forward or backward? */ return NS_OK;
if (angled > M_PI || (angled < 0 && angled > -M_PI)) {
angle1 += M_PI;
angled = 2 * M_PI - angled;
forward = 1;
} else {
double tmp;
tmp = angle0;
angle0 = angle2;
angle2 = tmp;
forward = 0;
} }
angle0 += M_PI_2; /* angle from (xc,yc) to (p0.x,p0.y) */ dir = (x2-x1)*(p0.y-y1) + (y2-y1)*(x1-p0.x);
angle2 -= M_PI_2; /* angle from (xc,yc) to (x2,y2) */ if (dir == 0) {
angled /= 2; /* the angle (p0.x,p0.y)--(x1,y1)--(xc,yc) */ mThebes->LineTo(gfxPoint(x1, y1));
return NS_OK;
/* distance from (x1,y1) to (p0.x,p0.y) */
d0 = sqrt ((p0.x-x1)*(p0.x-x1)+(p0.y-y1)*(p0.y-y1));
/* distance from (x2,y2) to (p0.x,p0.y) */
d2 = sqrt ((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
dc = -1;
sin_ = sin(angled);
cos_ = cos(angled);
if (fabs(cos_) >= 1e-5) { /* the arc may not fit */
/* min distance of end-points from corner */
double min_d = d0 < d2 ? d0 : d2;
/* max radius of an arc that fits */
double max_r = min_d * sin_ / cos_;
if (radius > max_r) {
/* arc with requested radius doesn't fit */
radius = (float) max_r;
dc = min_d / cos_; /* distance of (xc,yc) from (x1,y1) */
}
} }
if (dc < 0) a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
dc = radius / sin_; /* distance of (xc,yc) from (x1,y1) */ b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
sinx = sqrt(1 - cosx*cosx);
d = radius / ((1 - cosx) / sinx);
/* find (cx,cy), the center of the arc */ anx = (x1-p0.x) / sqrt(a2);
gfxPoint c(x1 + sin(angle1) * dc, y1 + cos(angle1) * dc); any = (y1-p0.y) / sqrt(a2);
bnx = (x1-x2) / sqrt(b2);
bny = (y1-y2) / sqrt(b2);
x3 = x1 - anx*d;
y3 = y1 - any*d;
x4 = x1 - bnx*d;
y4 = y1 - bny*d;
anticlockwise = (dir < 0);
cx = x3 + any*radius*(anticlockwise ? 1 : -1);
cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
angle0 = atan2((y3-cy), (x3-cx));
angle1 = atan2((y4-cy), (x4-cx));
/* the arc operation draws the line from current point (p0.x,p0.y) mThebes->LineTo(gfxPoint(x3, y3));
* to arc center too. */
if (forward) if (anticlockwise)
mThebes->Arc(c, radius, angle0, angle2); mThebes->NegativeArc(gfxPoint(cx, cy), radius, angle0, angle1);
else else
mThebes->NegativeArc(c, radius, angle2, angle0); mThebes->Arc(gfxPoint(cx, cy), radius, angle0, angle1);
mThebes->LineTo(gfxPoint(x2, y2));
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -55,9 +55,9 @@ ctx.moveTo(50, 25);
ctx.arcTo(50, 25, 100, 25, 1); ctx.arcTo(50, 25, 100, 25, 1);
ctx.stroke(); ctx.stroke();
todo_isPixel(ctx, 50,1, 0,255,0,255, "50,1", "0,255,0,255", 0); isPixel(ctx, 50,1, 0,255,0,255, "50,1", "0,255,0,255", 0);
todo_isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0); isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0);
todo_isPixel(ctx, 50,48, 0,255,0,255, "50,48", "0,255,0,255", 0); isPixel(ctx, 50,48, 0,255,0,255, "50,48", "0,255,0,255", 0);
SimpleTest.finish(); SimpleTest.finish();

Просмотреть файл

@ -54,7 +54,7 @@ ctx.moveTo(-100, 25);
ctx.arcTo(0, 25, 100, 25, 1); ctx.arcTo(0, 25, 100, 25, 1);
ctx.stroke(); ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0); isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0);
SimpleTest.finish(); SimpleTest.finish();

Просмотреть файл

@ -69,8 +69,8 @@ isPixel(ctx, 73,27, 0,255,0,255, "73,27", "0,255,0,255", 0);
isPixel(ctx, 78,36, 0,255,0,255, "78,36", "0,255,0,255", 0); isPixel(ctx, 78,36, 0,255,0,255, "78,36", "0,255,0,255", 0);
isPixel(ctx, 79,35, 0,255,0,255, "79,35", "0,255,0,255", 0); isPixel(ctx, 79,35, 0,255,0,255, "79,35", "0,255,0,255", 0);
isPixel(ctx, 80,44, 0,255,0,255, "80,44", "0,255,0,255", 0); isPixel(ctx, 80,44, 0,255,0,255, "80,44", "0,255,0,255", 0);
todo_isPixel(ctx, 80,45, 0,255,0,255, "80,45", "0,255,0,255", 0); isPixel(ctx, 80,45, 0,255,0,255, "80,45", "0,255,0,255", 0);
todo_isPixel(ctx, 80,46, 0,255,0,255, "80,46", "0,255,0,255", 0); isPixel(ctx, 80,46, 0,255,0,255, "80,46", "0,255,0,255", 0);
SimpleTest.finish(); SimpleTest.finish();

Просмотреть файл

@ -58,13 +58,13 @@ ctx.moveTo(10, 25);
ctx.arcTo(75, 25, 75, 60, 20); ctx.arcTo(75, 25, 75, 60, 20);
ctx.stroke(); ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0); isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0);
isPixel(ctx, 55,19, 0,255,0,255, "55,19", "0,255,0,255", 0); isPixel(ctx, 55,19, 0,255,0,255, "55,19", "0,255,0,255", 0);
isPixel(ctx, 55,20, 0,255,0,255, "55,20", "0,255,0,255", 0); isPixel(ctx, 55,20, 0,255,0,255, "55,20", "0,255,0,255", 0);
todo_isPixel(ctx, 55,21, 0,255,0,255, "55,21", "0,255,0,255", 0); isPixel(ctx, 55,21, 0,255,0,255, "55,21", "0,255,0,255", 0);
isPixel(ctx, 64,22, 0,255,0,255, "64,22", "0,255,0,255", 0); isPixel(ctx, 64,22, 0,255,0,255, "64,22", "0,255,0,255", 0);
isPixel(ctx, 65,21, 0,255,0,255, "65,21", "0,255,0,255", 0); isPixel(ctx, 65,21, 0,255,0,255, "65,21", "0,255,0,255", 0);
todo_isPixel(ctx, 72,28, 0,255,0,255, "72,28", "0,255,0,255", 0); isPixel(ctx, 72,28, 0,255,0,255, "72,28", "0,255,0,255", 0);
isPixel(ctx, 73,27, 0,255,0,255, "73,27", "0,255,0,255", 0); isPixel(ctx, 73,27, 0,255,0,255, "73,27", "0,255,0,255", 0);
isPixel(ctx, 78,36, 0,255,0,255, "78,36", "0,255,0,255", 0); isPixel(ctx, 78,36, 0,255,0,255, "78,36", "0,255,0,255", 0);
isPixel(ctx, 79,35, 0,255,0,255, "79,35", "0,255,0,255", 0); isPixel(ctx, 79,35, 0,255,0,255, "79,35", "0,255,0,255", 0);

Просмотреть файл

@ -48,10 +48,10 @@ ctx.arcTo(-100, 25, 200, 25, 10);
ctx.stroke(); ctx.stroke();
isPixel(ctx, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 0); isPixel(ctx, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 0);
todo_isPixel(ctx, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 0); isPixel(ctx, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 0);
todo_isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0); isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0);
isPixel(ctx, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 0); isPixel(ctx, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 0);
todo_isPixel(ctx, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 0); isPixel(ctx, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 0);
SimpleTest.finish(); SimpleTest.finish();

Просмотреть файл

@ -48,10 +48,10 @@ ctx.arcTo(200, 25, 200, 50, 10);
ctx.stroke(); ctx.stroke();
isPixel(ctx, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 0); isPixel(ctx, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 0);
todo_isPixel(ctx, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 0); isPixel(ctx, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 0);
isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0); isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0);
isPixel(ctx, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 0); isPixel(ctx, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 0);
todo_isPixel(ctx, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 0); isPixel(ctx, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 0);
SimpleTest.finish(); SimpleTest.finish();

Просмотреть файл

@ -50,7 +50,7 @@ isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0);
} catch (e) { } catch (e) {
_thrown_outer = true; _thrown_outer = true;
} }
todo(!_thrown_outer, 'should not throw exception'); ok(!_thrown_outer, 'should not throw exception');
SimpleTest.finish(); SimpleTest.finish();

Просмотреть файл

@ -50,7 +50,7 @@ isPixel(ctx, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 0);
} catch (e) { } catch (e) {
_thrown_outer = true; _thrown_outer = true;
} }
todo(!_thrown_outer, 'should not throw exception'); ok(!_thrown_outer, 'should not throw exception');
SimpleTest.finish(); SimpleTest.finish();