V10/cmd/picasso/arcgen.c
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/* @(#)picasso:arcgen.c 1.0 */
#include "picasso.h"
#include "y.tab.h"
obj *arcgen(type) /* handles circular and (eventually) elliptical arcs */
{
static double prevwid = HT10;
static double prevht = HT5;
static double prevrad = HT2;
static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR,
R_DIR, U_DIR, L_DIR };
struct objattr obat;
double dx2, dy2, phi, r, d, fromx, fromy, tox, toy;
int i, head, to, at, cw;
obj *p, *ppos;
Attr *ap;
obat.a_ht = getfval("arrowht");
obat.a_wid = getfval("arrowwid");
obat.a_rad = getfval("arcrad");
obat.a_layer = (int)getfval("curlayer");
obat.a_flags = EDGED;
obat.a_weight = obat.a_lcolor = obat.a_pcolor = obat.a_tcolor = -1;
obat.a_dashpat.a = (float *)0;
set_text();
fromx = curx;
fromy = cury;
head = to = at = cw = 0;
for (i = 0; i < nattr; i++) {
ap = &attr[i];
switch (ap->a_type) {
default:
miscattrs(ap, &obat);
break;
case HEAD:
head += ap->a_val.i;
break;
case CW:
cw = 1;
break;
case FROM: /* start point of arc */
ppos = ap->a_val.o;
fromx = Xformx(ppos, 1, ppos->o_x, ppos->o_y);
fromy = Xformy(ppos, 0, ppos->o_x, ppos->o_y);
break;
case TO: /* end point of arc */
ppos = ap->a_val.o;
tox = Xformx(ppos, 1, ppos->o_x, ppos->o_y);
toy = Xformy(ppos, 0, ppos->o_x, ppos->o_y);
to++;
break;
case AT: /* center of arc */
ppos = ap->a_val.o;
curx = Xformx(ppos, 1, ppos->o_x, ppos->o_y);
cury = Xformy(ppos, 0, ppos->o_x, ppos->o_y);
at = 1;
break;
case UP:
hvmode = U_DIR;
break;
case DOWN:
hvmode = D_DIR;
break;
case RIGHT:
hvmode = R_DIR;
break;
case LEFT:
hvmode = L_DIR;
break;
case SAME:
obat.a_ht = prevht;
obat.a_wid = prevwid;
obat.a_rad = prevrad;
break;
}
}
if (!at && !to) { /* the defaults are mostly OK */
curx = fromx + obat.a_rad * dctrx[cw][hvmode];
cury = fromy + obat.a_rad * dctry[cw][hvmode];
tox = fromx + obat.a_rad * dtox[cw][hvmode];
toy = fromy + obat.a_rad * dtoy[cw][hvmode];
hvmode = nexthv[cw][hvmode];
}
else if (!at) {
dx2 = (tox - fromx) / 2;
dy2 = (toy - fromy) / 2;
phi = atan2(dy2, dx2) + (cw ? -M_PI_2 : M_PI_2);
if (obat.a_rad <= 0.0)
obat.a_rad = sqrt(dx2*dx2+dy2*dy2);
for (r=obat.a_rad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
; /* this kludge gets around too-small radii */
obat.a_rad = r;
d = sqrt(d);
curx = fromx + dx2 + d * cos(phi);
cury = fromy + dy2 + d * sin(phi);
}
else if (at && !to) { /* do we have all the cases??? */
tox = fromx + obat.a_rad * dtox[cw][hvmode];
toy = fromy + obat.a_rad * dtoy[cw][hvmode];
hvmode = nexthv[cw][hvmode];
}
p = makenode(type, N_VAL + 10, obat.a_layer);
prevrad = p->o_val[N_VAL+0].f = p->o_val[N_VAL+1].f = obat.a_rad;
prevwid = p->o_val[N_VAL+8].f = obat.a_wid;
prevht = p->o_val[N_VAL+9].f = obat.a_ht;
if (cw) { /* interchange roles of from-to and heads */
double temp;
temp = fromx; curx = fromx = tox; tox = temp;
temp = fromy; cury = fromy = toy; toy = temp;
if (head == HEAD1)
head = HEAD2;
else if (head == HEAD2)
head = HEAD1;
p->o_attr |= CW_ARC;
}
else {
curx = tox;
cury = toy;
}
p->o_val[N_VAL+2].f = fromx;
p->o_val[N_VAL+3].f = fromy;
p->o_val[N_VAL+4].f = tox;
p->o_val[N_VAL+5].f = toy;
if (head)
p->o_attr |= head | arrowfill();
primattrs(p, &obat);
text_bounds(p);
arc_extreme(p);
return(p);
}
quadrant(x,y)
double x, y;
{
if ( x>=0.0 && y> 0.0) return(1);
else if( x< 0.0 && y>=0.0) return(2);
else if( x<=0.0 && y< 0.0) return(3);
else if( x> 0.0 && y<=0.0) return(4);
else
{ fprintf(stderr,"can't happen: x,y=%g,%g",x,y); exit(1);}
}
/***************************************************************************
bounding box of a circular arc Eric Grosse 24 May 84
Conceptually, this routine generates a list consisting of the start,
end, and whichever north, east, south, and west points lie on the arc.
The bounding box is then the range of this list.
list = {start,end}
j = quadrant(start)
k = quadrant(end)
if( j==k && long way 'round ) append north,west,south,east
else
while( j != k )
append center+radius*[j-th of north,west,south,east unit vectors]
j += 1 (mod 4)
set bounds (return object with wid/ht and offset from arc center)
The following code implements this, with simple optimizations.
***********************************************************************/
arc_extreme(p)
obj *p;
{
/* assumes center isn't too far out */
double x0, y0, x1, y1, xc, yc; /* start, end, center */
double r, xmin, ymin, xmax, ymax, wgt;
int j, k;
x0 = p->o_val[N_VAL+2].f - (xc = p->o_x); /* translate to center */
y0 = p->o_val[N_VAL+3].f - (yc = p->o_y);
x1 = p->o_val[N_VAL+4].f - xc;
y1 = p->o_val[N_VAL+5].f - yc;
xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
r = p->o_val[N_VAL].f;
if (r > 0.0) {
j = quadrant(x0,y0);
k = quadrant(x1,y1);
if (j == k && y1*x0 < x1*y0) {
/* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
if( xmax < r) xmax = r; if( ymax < r) ymax = r;
} else {
while (j != k) {
switch (j) {
case 1: if( ymax < r) ymax = r; break; /* north */
case 2: if( xmin > -r) xmin = -r; break; /* west */
case 3: if( ymin > -r) ymin = -r; break; /* south */
case 4: if( xmax < r) xmax = r; break; /* east */
}
j = j%4 + 1;
}
}
}
if (p->o_type == SECTOR) { /* include center */
if (xmin * xmax > 0)
if (xmin > 0) xmin = 0; else xmax = 0;
if (ymin * ymax > 0)
if (ymin > 0) ymin = 0; else ymax = 0;
}
p->o_wid = xmax - xmin;
p->o_ht = ymax - ymin;
p->o_val[N_VAL+6].f = -0.5 * (xmin+xmax); /* record offset to center */
p->o_val[N_VAL+7].f = -0.5 * (ymin+ymax);
wgt = p->o_weight/2;
track_bounds(xmin+xc-wgt, ymin+yc-wgt, xmax+xc+wgt, ymax+yc+wgt);
}