Thanks for this! It looks like everything I might need to
dig further. But, I'm kinda burnt-out on xpost and taking
a few days on other projects. I've spent the last few weeks
debugging memory problems raised by a change to the memory
tables. The good news is it's almost 50% faster and suddenly
collecting credible quantities of garbage (for a long time,
the gc results have been suspiciously low). The bad news is
there's still (at least) one bug that's still proving
difficult to diagnose. If the gc runs at certain unfortuitous
moments, it hits a bunch of errors. As a result of this week's
efforts, this is now a regular postscript VMerror and not a
crash. But it's still bad, but can be avoided by compiling
with -DXPOST_NO_GC or by tweaking the
XPOST_GARBAGE_COLLECTION_THRESHOLD so it doesn't happen in
just that wrong spot. Even weirder, running on different
OSes produce slightly different errors. It's always
"can't find table for ent XXXXXX" with some out-of-bounds
number. But mys2, Cygwin, and Linux all report a different
ent number.
I discovered that part of the problem in the numbers
revealed by Scott's program is that xpost is erroneously
producing 2 beziers for the 90-degree arc, for some reason.
Re-reading the code (quoted below), I still don't see why it's
doing that.
One apparent difference is that you're taking a tangent to
find the z value, whereas the idea I've been following
was to solve that equation for y1. I'm not sure I see where
the calculation for z comes from, but perhaps that will be
more clear when my brain is less mushy.
And I could always peek at gs or even ralpage for further
reference (and stealing).
For reference, my current implementation (with un-updated
postscript original in comments). This version does not (yet)
incorporate the suggested calculations in the above pdf.
https://github.com/luser-dr00g/xpost/blob/19ace0b18a946ddd95a9e489a2b3759dd14f1221/src/lib/xpost_op_path.c#L511
/*
% packs the center-point, radius and center-angle in a matrix
% then performs the simpler task of calculating a bezier
% for the arc that is symmetrical about the x-axis
% formula derived from
http://www.tinaja.com/glib/bezarc1.pdf
/arcbez { % draw single bezier % x y r angle1 angle2 . x1 y1 x2 y2 x3 y3 x0 y0
DICT
%5 dict
begin
%/mat matrix def
5 3 roll mat translate pop % r angle1 angle2
3 2 roll dup mat1 scale mat mat concatmatrix pop % angle1 angle2
2 copy exch sub /da exch def % da=a2-a1
add 2 div mat1 rotate mat mat concatmatrix pop
/da_2 da 2 div def
/sin_a da_2 sin def
/cos_a da_2 cos def
4 cos_a sub 3 div % x1
1 cos_a sub cos_a 3 sub mul
3 sin_a mul div % x1 y1
neg
1 index % x1 y1 x2(==x1)
1 index neg % x1 y1 x2 y2(==-y1)
cos_a sin_a neg % x1 y1 x2 y2 x3 y3
cos_a sin_a % ... x0 y0
4 { 8 2 roll mat transform } repeat
%pstack()=
end
}
dup 0 10 dict
dup /mat matrix put
dup /mat1 matrix put
put
bind
def
*/
static
void _transform(Xpost_Matrix mat, real x, real y, real *xres, real *yres)
{
*xres = mat.xx * x + mat.xy * y + mat.xz;
*yres = mat.yx * x + mat.yy * y + mat.yz;
}
static
Xpost_Object _arc_start_proc;
static
int _arcbez(Xpost_Context *ctx,
Xpost_Object x, Xpost_Object y, Xpost_Object r,
Xpost_Object angle1, Xpost_Object angle2)
{
Xpost_Matrix mat1, mat2, mat3;
real da_2, sin_a, cos_a;
real x0, y0, x1, y1, x2, y2, x3, y3;
xpost_matrix_scale(&mat1, r.real_.val, r.real_.val);
xpost_matrix_translate(&mat2, x.real_.val, y.real_.val);
xpost_matrix_mult(&mat2, &mat1, &mat3);
xpost_matrix_rotate(&mat2, (real)(((angle1.real_.val + angle2.real_.val) / 2.0) * RAD_PER_DEG));
xpost_matrix_mult(&mat3, &mat2, &mat1);
da_2 = (real)(((angle2.real_.val - angle1.real_.val) / 2.0) * RAD_PER_DEG);
sin_a = (real)sin(da_2);
cos_a = (real)cos(da_2);
x0 = cos_a;
y0 = sin_a;
x1 = (real)((4 - cos_a) / 3.0);
//y1 = - (((1 - cos_a) * (cos_a - 3)) / (3 * sin_a));
y1 = (1 - x1*cos_a) / sin_a;
x2 = x1;
y2 = -y1;
x3 = cos_a;
y3 = -sin_a;
_transform(mat1, x0, y0, &x0, &y0);
_transform(mat1, x1, y1, &x1, &y1);
_transform(mat1, x2, y2, &x2, &y2);
_transform(mat1, x3, y3, &x3, &y3);
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(x1));
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(y1));
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(x2));
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(y2));
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(x3));
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(y3));
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(x0));
xpost_stack_push(ctx->lo, ctx->os, xpost_real_cons(y0));
return 0;
}
static
int _arc(Xpost_Context *ctx,
Xpost_Object x, Xpost_Object y, Xpost_Object r,
Xpost_Object angle1, Xpost_Object angle2)
{
double a1 = angle1.real_.val;
double a2 = angle2.real_.val;
while (a2 < a1)
{
double t;
t = a2 + 360;
a2 = t;
}
if ((a2 - a1) > 90)
{
_arc(ctx, x, y, r, xpost_real_cons(a1), xpost_real_cons((real)(a2 - ((a2 - a1)/2.0))));
_arc(ctx, x, y, r, xpost_real_cons((real)(a1 + ((a2 - a1)/2.0))), xpost_real_cons(a2));
}
else
{
//Xpost_Object path = _cpath(ctx);
//int pathlen = xpost_dict_length_memory(xpost_context_select_memory(ctx, path), path);
_arcbez(ctx, x, y, r, xpost_real_cons(a1), xpost_real_cons(a2));
xpost_stack_push(ctx->lo, ctx->es, xpost_operator_cons_opcode(_curveto_opcode));
xpost_stack_push(ctx->lo, ctx->es, _arc_start_proc);
/*
if (pathlen)
xpost_stack_push(ctx->lo, ctx->es, xpost_operator_cons_opcode(_lineto_opcode));
else
xpost_stack_push(ctx->lo, ctx->es, xpost_operator_cons_opcode(_moveto_opcode));
*/
}
return 0;
}
static
int _arcn(Xpost_Context *ctx,
Xpost_Object x, Xpost_Object y, Xpost_Object r,
Xpost_Object angle1, Xpost_Object angle2)
{
real a1 = angle1.real_.val;
real a2 = angle2.real_.val;
while (a2 > a1)
{
double t;
t = a2 - 360;
a2 = t;
}
if ((a1 - a2) > 90)
{
_arcn(ctx, x, y, r, xpost_real_cons(a1), xpost_real_cons(a2 + (real)((a1 - a2)/2.0)));
_arcn(ctx, x, y, r, xpost_real_cons(a1 - (real)((a1 - a2)/2.0)), xpost_real_cons(a2));
}
else
{
//Xpost_Object path = _cpath(ctx);
//int pathlen = xpost_dict_length_memory(xpost_context_select_memory(ctx, path), path);
_arcbez(ctx, x, y, r, xpost_real_cons(a1), xpost_real_cons(a2));
xpost_stack_push(ctx->lo, ctx->es, xpost_operator_cons_opcode(_curveto_opcode));
xpost_stack_push(ctx->lo, ctx->es, _arc_start_proc);
/*
if (pathlen)
xpost_stack_push(ctx->lo, ctx->es, xpost_operator_cons_opcode(_lineto_opcode));
else
xpost_stack_push(ctx->lo, ctx->es, xpost_operator_cons_opcode(_moveto_opcode));
*/
}
return 0;
}