#help_index "Graphics/Math"

I8 GrXOffsets[8]={-1,0,1,-1,1,-1,0,1},
GrYOffsets[8]={-1,-1,-1,0,0,1,1,1};

public void Line(void *aux_data,
                            I8 x1,I8 y1,I8 z1,
                            I8 x2,I8 y2,I8 z2,
                            void plot_cb(void *aux,I8 x,I8 y,I8 z),
                            I8 step=1,I8 start=0)
{
I8 i,j,d,dx=x2-x1,dy=y2-y1,dz=z2-z1,_x,_y,_z;
I8 adx=AbsI8(dx),ady=AbsI8(dy),adz=AbsI8(dz);
BoolI1 first=TRUE;
if (adx>=ady) {
  if (adx>=adz) {
      if (d=adx) {
          if (dx>=0)
              dx=0x100000000;
          else
              dx=-0x100000000;
          dy=dy<<32/d;
          dz=dz<<32/d;
      }
  } else {
      if (d=adz) {
          dx=dx<<32/d;
          dy=dy<<32/d;
          if (dz>=0)
              dz=0x100000000;
          else
              dz=-0x100000000;
      }
  }
} else {
  if (ady>=adz) {
      if (d=ady) {
          dx=dx<<32/d;
          if (dy>=0)
              dy=0x100000000;
          else
              dy=-0x100000000;
          dz=dz<<32/d;
      }
  } else {
      if (d=adz) {
          dx=dx<<32/d;
          dy=dy<<32/d;
          if (dz>=0)
              dz=0x100000000;
          else
              dz=-0x100000000;
      }
  }
}
x1<<=32; y1<<=32; z1<<=32;
for (j=0;j<start;j++) {
      x1+=dx; y1+=dy; z1+=dz;
}
if (step!=1 && step!=0) {
  dx*=step;
  dy*=step;
  dz*=step;
  d/=step;
}
for (i=start;i<=d;i++) {
  if (_x!=x1.i4[1] || _y!=y1.i4[1] || _z!=z1.i4[1] || first)
      plot_cb(aux_data,x1.i4[1],y1.i4[1],z1.i4[1]);
  first=FALSE;
  _x=x1.i4[1]; _y=y1.i4[1]; _z=z1.i4[1];
  x1+=dx; y1+=dy; z1+=dz;
}
if (step==1 && (_x!=x2||_y!=y2||_z!=z2))
  plot_cb(aux_data,x2,y2,z2);
}

public void Box(void *aux_data,
                          I8 x1,I8 y1,I8 z1,
                          I8 x2,I8 y2,I8 z2,
                          I8 x3,I8 y3,I8 z3,
                          void plot_cb(void *aux,I8 x,I8 y,I8 z))
{
I8 i,j,
    d1,dx1=x2-x1,dy1=y2-y1,dz1=z2-z1,
    d2,dx2=x3-x1,dy2=y3-y1,dz2=z3-z1,
    adx1=AbsI8(dx1),ady1=AbsI8(dy1),adz1=AbsI8(dz1),
    adx2=AbsI8(dx2),ady2=AbsI8(dy2),adz2=AbsI8(dz2),
    last_x,last_y,last_z;
BoolI1 first=TRUE;

if (adx1>=ady1)
  d1=adx1>=adz1 ? adx1:adz1;
else
  d1=ady1>=adz1 ? ady1:adz1;
if (adx2>=ady2)
  d2=adx2>=adz2 ? adx2:adz2;
else
  d2=ady2>=adz2 ? ady2:adz2;
d1<<=1;
d2<<=1;
if (d1) {
  dx1=dx1<<32/d1;
  dy1=dy1<<32/d1;
  dz1=dz1<<32/d1;
}

if (d2) {
  dx2=dx2<<32/d2;
  dy2=dy2<<32/d2;
  dz2=dz2<<32/d2;
}

x1<<=32; y1<<=32; z1<<=32;
for (j=0;j<=d1;j++) {
  x2=x1; y2=y1; z2=z1;
  for (i=0;i<=d2;i++) {
      if (x2.i4[1]!=last_x || y2.i4[1]!=last_y || z2.i4[1]!=last_z ||first)
          plot_cb(aux_data,x2.i4[1],y2.i4[1],z2.i4[1]);
      first=FALSE;
      last_x=x2.i4[1]; last_y=y2.i4[1]; last_z=z2.i4[1];
      x2+=dx2; y2+=dy2; z2+=dz2;
  }
  x1+=dx1; y1+=dy1; z1+=dz1;
}
}

#help_index "Graphics/Math/3D Transformation"
#help_file "::/LT/Doc/Transform.TXZ"
I8 gr_scale;
gr_scale.u4[0]=0;
gr_scale.i4[1]=1;

public void GrRotate(I8 *r,I8 *x,I8 *y,I8 *z)
{
I8 x1,y1,z1,xx=*x,yy=*y,zz=*z;
x1=(r[0]*xx+r[1]*yy+r[2]*zz+r[3])>>32;
y1=(r[4]*xx+r[5]*yy+r[6]*zz+r[7])>>32;
z1=(r[8]*xx+r[9]*yy+r[10]*zz+r[11])>>32;
*x=x1;*y=y1;*z=z1;
}

public void GrTransform(GrBitMap *base,I8 *x,I8 *y,I8 *z)
{
GrRotate(base->r,x,y,z);
*x+=base->x;
*y+=base->y;
*z+=base->z;
}

public I8 *GrRotX(double phi,TssStruct *tss=NULL)
{
double my_cos,my_sin;
I8 *r=MAllocZ(sizeof(I8)*16,tss);

my_cos=Cos(phi)*gr_scale;
my_sin=Sin(phi)*gr_scale;
r[5]=my_cos;
r[10]=my_cos;
r[9]=my_sin;
r[6]=-my_sin;
r[0]=gr_scale;
r[15]=gr_scale;
return r;
}

public I8 *GrRotY(double zeta,TssStruct *tss=NULL)
{
double my_cos,my_sin;
I8 *r=MAllocZ(sizeof(I8)*16,tss);

my_cos=Cos(zeta)*gr_scale;
my_sin=Sin(zeta)*gr_scale;
r[0]=my_cos;
r[10]=my_cos;
r[8]=-my_sin;
r[2]=my_sin;
r[5]=gr_scale;
r[15]=gr_scale;
return r;
}

public I8 *GrRotZ(double theta,TssStruct *tss=NULL)
{
double my_cos,my_sin;
I8 *r=MAllocZ(sizeof(I8)*16,tss);

my_cos=Cos(theta)*gr_scale;
my_sin=Sin(theta)*gr_scale;
r[0]=my_cos;
r[5]=my_cos;
r[4]=my_sin;
r[1]=-my_sin;
r[10]=gr_scale;
r[15]=gr_scale;
return r;
}

public I8 *GrScaleMat(I8 *m1,double s,TssStruct *tss=NULL)
{
I8 i,*r=MAllocZ(sizeof(I8)*16,tss);
for (i=0;i<16;i++)
  r[i]=m1[i]*s;
return r;
}

public I8 *GrMulMat(I8 *m1,I8 *m2,TssStruct *tss=NULL)
{
I8 *r=MAllocZ(sizeof(I8)*16,tss);
I8 i,j,k;
double d;
for (i=0;i<4;i++) {
  for (j=0;j<4;j++) {
      for (k=0;k<4;k++) {
          d=m1[k+4*j];
          d*=m2[i+4*k];
          d/=gr_scale;
          r[i+4*j]+=d;
      }
  }
}
return r;
}

public I8 GrDetMat(I8 *m1)
{ //determinant
I8 i;
double m[16],d;
for (i=0;i<16;i++)
  m[i]=m1[i]/ToDouble(0x100000000);
d=m[0]*(m[5]*m[10]-m[6]*m[9])-
  m[1]*(m[4]*m[10]-m[6]*m[8])+
  m[2]*(m[4]*m[9]-m[5]*m[8]);
return d*0x100000000;
}

public void GrScalePenWidth(GrBitMap *base)
{
I8 d;
if (base->flags&BMF_TRANSFORMATION) {
  if (base->pen_width) {
      d=base->pen_width*GrDetMat(base->r)+0x80000000;
      base->pen_width=d.u4[1];
      if (base->pen_width<1)
          base->pen_width=1;
  }
}
}

public void GrIdent(TssStruct *tss=NULL)
{
I8 *r=MAllocZ(sizeof(I8)*16,tss);
r[0].i4[1]=1;
r[5].i4[1]=1;
r[10].i4[1]=1;
r[15].i4[1]=1;
}

public void GrSetIdent(I8 *r)
{
MemSet(r,0,16*sizeof(I8));
r[0].i4[1]=1;
r[5].i4[1]=1;
r[10].i4[1]=1;
r[15].i4[1]=1;
}

public void GrSetTranslation(I8 *r,I8 x,I8 y,I8 z)
{
r[3]=x<<32;
r[7]=y<<32;
r[11]=z<<32;
r[15]=gr_scale;
}

public BoolI8 GrSetSymmetry(GrBitMap *base,I8 x1,I8 y1,I8 x2,I8 y2)
{
double d;
if (y1==y2 && x1==x2)
  return FALSE;
base->sym.snx=y2-y1;
base->sym.sny=x1-x2;
base->sym.snz=0;
if (d=Sqrt(SqrI8(base->sym.snx)+
                    SqrI8(base->sym.sny)+
                    SqrI8(base->sym.snz))) {
  d=gr_scale/d;
  base->sym.snx *= d;
  base->sym.sny *= d;
  base->sym.snz *= d;
}
base->sym.sx=x1;
base->sym.sy=y1;
base->sym.sz=0;
return TRUE;
}

public BoolI8 GrSetSymmetry3(GrBitMap *base,I8 x1,I8 y1,I8 z1,I8 x2,I8 y2,I8 z2,I8 x3,I8 y3,I8 z3)
{
double d,x,y,z,xx,yy,zz;
I8 xx1,yy1,zz1,xx2,yy2,zz2,*r;
xx1=x1-x2; yy1=y1-y2; zz1=z1-z2;
xx2=x3-x2; yy2=y3-y2; zz2=z3-z2;
if (!xx1 && !yy1 && !zz1 ||
      !xx2 && !yy2 && !zz2 ||
      xx1==xx2 && yy1==yy2 && zz1==zz2)
  return FALSE;

x=yy1*zz2-zz1*yy2;
y=-xx1*zz2+zz1*xx2;
z=xx1*yy2-yy1*xx2;
if (base->flags & BMF_TRANSFORMATION) {
  r=base->r;
  xx=x*r[0]+y*r[1]+z*r[2];
  yy=x*r[4]+y*r[5]+z*r[6];
  zz=x*r[8]+y*r[9]+z*r[10];
  x=xx; y=yy; z=zz;
}
if (d=Sqrt(Sqr(x)+Sqr(y)+Sqr(z))) {
  d=gr_scale/d;
  base->sym.snx = d*x;
  base->sym.sny = d*y;
  base->sym.snz = d*z;
}
if (base->flags & BMF_TRANSFORMATION)
  GrTransform(base,&x1,&y1,&z1);
base->sym.sx=x1;
base->sym.sy=y1;
base->sym.sz=z1;
return TRUE;
}

public void GrReflect(GrBitMap *base,I8 *_x,I8 *_y,I8 *_z)
{
I8 x=*_x<<32,y=*_y<<32,z=*_z<<32,
  xx=*_x-base->sym.sx,yy=*_y-base->sym.sy,zz=*_z-base->sym.sz,
  d=(xx*base->sym.snx+yy*base->sym.sny+zz*base->sym.snz)>>16,
  xn,yn,zn,xx2,yy2,zz2;
xn=d*base->sym.snx>>15;
yn=d*base->sym.sny>>15;
zn=d*base->sym.snz>>15;
xx=x-xn;
yy=y-yn;
zz=z-zn;
xx2=x+xn;
yy2=y+yn;
zz2=z+zn;
if (SqrI8(xx>>16 -base->sym.sx<<16)+
      SqrI8(yy>>16 -base->sym.sy<<16)+
      SqrI8(zz>>16 -base->sym.sz<<16)<
      SqrI8(xx2>>16-base->sym.sx<<16)+
      SqrI8(yy2>>16-base->sym.sy<<16)+
      SqrI8(zz2>>16-base->sym.sz<<16)) {
  *_x=xx.i4[1]; *_y=yy.i4[1]; *_z=zz.i4[1];
} else {
  *_x=xx2.i4[1]; *_y=yy2.i4[1]; *_z=zz2.i4[1];
}
}

#help_index "Graphics/Math"
public void Circle(void *aux_data,
                          I8 cx,I8 cy,I8 cz,
                          I8 radius,
                          void plot_cb(void *aux,I8 x,I8 y,I8 z),
                          I8 step=1,
                          double start_radians=0,
                          double len_radians=2*pi)
{
I8 i,j,len=len_radians*radius;
I8 x,y,x1,y1,s1,s2,c;
double t;
if (radius<=0) return;
t=1.0/radius;
s1=1<<24*Sin(t);
s2=-s1;
c=1<<24*Cos(t);
if (start_radians) {
  x=radius*Cos(start_radians);
  y=-radius*Sin(start_radians);
} else {
  x=radius;
  y=0;
}
x<<=8;
y<<=8;
for (i=0;i<=len;i+=step) {
  plot_cb(aux_data,cx+x>>8,cy+y>>8,cz);
  for (j=0;j<step;j++) {
      x1=(c*x+s1*y)>>24;
      y1=(s2*x+c*y)>>24;
      x=x1; y=y1;
  }
}
}

public void Ellipse(void *aux_data,
                          I8 cx,I8 cy,I8 cz,
                          I8 x_radius,I8 y_radius,
                          void plot_cb(void *aux,I8 x,I8 y,I8 z),
                          double rot_angle=0,
                          I8 step=1,
                          double start_radians=0,
                          double len_radians=2*pi)
{
I8 i,j,len;
I8 x,y,_x,_y,x1,y1,x2,y2, s1,s2,c, s12,s22,c2;
double t;
BoolI1 first=TRUE;
if (x_radius<=0 || y_radius<=0 || step<1)
  return;
if (x_radius>=y_radius) {
  t=1.0/x_radius;
  len=len_radians*x_radius;
} else {
  t=1.0/y_radius;
  len=len_radians*y_radius;
}

s1=1<<24*Sin(t);
s2=-s1;
c=1<<24*Cos(t);

s12=1<<24*Sin(rot_angle);
s22=-s12;
c2=1<<24*Cos(rot_angle);

if (start_radians) {
  x=x_radius*Cos(start_radians);
  y=-x_radius*Sin(start_radians);
} else {
  x=x_radius;
  y=0;
}
x<<=8;
y<<=8;
x2=x;
y2=y;

y1=y2*y_radius/x_radius;
x=(c2*x2+s12*y1)>>24;
y=(s22*x2+c2*y1)>>24;

for (i=0;i<=len;i+=step) {
  if (x>>8!=_x || y>>8!=_y || first)
      plot_cb(aux_data,cx+x>>8,cy+y>>8,cz);
  _x=x>>8; _y=y>>8; first=FALSE;
  for (j=0;j<step;j++) {
      x1=(c*x2+s1*y2)>>24;
      y1=(s2*x2+c*y2)>>24;
      x2=x1;
      y2=y1;
      y1=y1*y_radius/x_radius;
      x=(c2*x1+s12*y1)>>24;
      y=(s22*x1+c2*y1)>>24;
  }
}
}

public void RegPolygon(void *aux_data,
                          I8 cx,I8 cy,I8 cz,
                          I8 x_radius,I8 y_radius,I8 sides,
                          void plot_cb(void *aux,I8 x,I8 y,I8 z),
                          double rot_angle=0,
                          I8 step=1,
                          double start_radians=0,
                          double len_radians=2*pi)
{
I8 i,n,x,y,x1,y1,x2,y2,
  xx1,yy1,xx2,yy2,
  s1,s2,c, s12,s22,c2;
double angle_step;

if (sides<=0 || x_radius<=0 || y_radius<=0)
  return;

angle_step=2*pi/sides;
n=len_radians/angle_step;

s1=1<<24*Sin(angle_step);
s2=-s1;
c=1<<24*Cos(angle_step);

s12=1<<24*Sin(rot_angle);
s22=-s12;
c2=1<<24*Cos(rot_angle);

if (start_radians) {
  x=x_radius*Cos(start_radians);
  y=-x_radius*Sin(start_radians);
} else {
  x=x_radius;
  y=0;
}
x<<=8;
y<<=8;
x2=x;
y2=y;

y1=y2*y_radius/x_radius;
x=(c2*x2+s12*y1)>>24;
y=(s22*x2+c2*y1)>>24;

xx1=cx+x>>8;
yy1=cy+y>>8;
for (i=0;i<n;i++) {
  x1=(c*x2+s1*y2)>>24;
  y1=(s2*x2+c*y2)>>24;
  x2=x1;
  y2=y1;
  y1=y1*y_radius/x_radius;
  x=(c2*x1+s12*y1)>>24;
  y=(s22*x1+c2*y1)>>24;
  xx2=cx+x>>8;
  yy2=cy+y>>8;
  Line(aux_data,xx1,yy1,cz,xx2,yy2,cz,plot_cb,step);
  xx1=xx2; yy1=yy2;
}
}

public I8 DistP3I4(P3I4 *p1,P3I4 *p2)
{
return Sqrt(SqrI8(p1->x-p2->x)+SqrI8(p1->y-p2->y)+SqrI8(p1->z-p2->z));
}

public void Bezier2(void *aux_data,P3I4 *ctrl,void plot_cb(void *aux,I8 x,I8 y,I8 z),BoolI1
first=TRUE)
{//2nd order
I8 x,y,z,xx,yy,zz,dx,dy,dz,d_max;
double x0=ctrl[0].x,y0=ctrl[0].y,z0=ctrl[0].z;
double x1=ctrl[1].x-x0,y1=ctrl[1].y-y0,z1=ctrl[1].z-z0;
double x2=ctrl[2].x-x0,y2=ctrl[2].y-y0,z2=ctrl[2].z-z0;
double t,d=DistP3I4(&ctrl[0],&ctrl[1])+
                    DistP3I4(&ctrl[1],&ctrl[2])+
                    DistP3I4(&ctrl[2],&ctrl[0]),
            s=0.5/d,t1,t2;
xx=x0; yy=y0; zz=z0;
if (first)
  plot_cb(aux_data,xx,yy,zz);
for (t=0.0;t<=1.0;t+=s) {
  t1=t*(1.0-t);
  t2=t*t;
  x=x0+x1*t1+x2*t2;
  y=y0+y1*t1+y2*t2;
  z=z0+z1*t1+z2*t2;
  dx=AbsI8(x-xx);
  dy=AbsI8(y-yy);
  dz=AbsI8(z-zz);
  if (dx>dy)
      d_max=dx;
  else
      d_max=dy;
  if (dz>d_max)
      d_max=dz;
  if (!d_max)
      s*=1.1;
  else {
      s*=0.9;
      plot_cb(aux_data,x,y,z);
      xx=x;yy=y;zz=z;
  }
}
x=ctrl[2].x; y=ctrl[2].y; z=ctrl[2].z;
if (xx!=x || yy!=y || zz!=z)
  plot_cb(aux_data,x,y,z);
}

public void Bezier3(void *aux_data,P3I4 *ctrl,void plot_cb(void *aux,I8 x,I8 y,I8 z),BoolI1
first=TRUE)
{ //3rd order
I8 x,y,z,xx,yy,zz,dx,dy,dz,d_max;
double x0=ctrl[0].x,y0=ctrl[0].y,z0=ctrl[0].z;
double x1=ctrl[1].x-x0,y1=ctrl[1].y-y0,z1=ctrl[1].z-z0;
double x2=ctrl[2].x-x0,y2=ctrl[2].y-y0,z2=ctrl[2].z-z0;
double x3=ctrl[3].x-x0,y3=ctrl[3].y-y0,z3=ctrl[3].z-z0;
double t,d=DistP3I4(&ctrl[0],&ctrl[1])+
                    DistP3I4(&ctrl[1],&ctrl[2])+
                    DistP3I4(&ctrl[2],&ctrl[3])+
                    DistP3I4(&ctrl[3],&ctrl[0]),
            s=0.5/d,nt,t1,t2,t3;
xx=x0; yy=y0; zz=z0;
if (first)
  plot_cb(aux_data,xx,yy,zz);
for (t=0.0;t<=1.0;t+=s) {
  nt=1.0-t;
  t1=t*nt*nt;
  t2=t*t*nt;
  t3=t*t*t;
  x=x0+x1*t1+x2*t2+x3*t3;
  y=y0+y1*t1+y2*t2+y3*t3;
  z=z0+z1*t1+z2*t2+z3*t3;
  dx=AbsI8(x-xx);
  dy=AbsI8(y-yy);
  dz=AbsI8(z-zz);
  if (dx>dy)
      d_max=dx;
  else
      d_max=dy;
  if (dz>d_max)
      d_max=dz;
  if (!d_max)
      s*=1.1;
  else {
      s*=0.9;
      plot_cb(aux_data,x,y,z);
      xx=x;yy=y;zz=z;
  }
}
x=ctrl[3].x; y=ctrl[3].y; z=ctrl[3].z;
if (xx!=x || yy!=y || zz!=z)
  plot_cb(aux_data,x,y,z);
}

public void BSpline2(void *aux_data,P3I4 *ctrl,I8 cnt,void plot_cb(void *aux,I8 x,I8 y,I8 z),BoolI1
closed=FALSE)
{ //2nd order
I8 i,j;
P3I4 *c;
BoolI1 first=TRUE;
if (cnt<3) return;
if (closed) {
  cnt++;
  c=MAlloc(sizeof(P3I4)*(cnt*2-1));
  j=1;
  for (i=0;i<cnt-2;i++) {
      c[j].x=(ctrl[i].x+ctrl[i+1].x)/2.0;
      c[j].y=(ctrl[i].y+ctrl[i+1].y)/2.0;
      c[j].z=(ctrl[i].z+ctrl[i+1].z)/2.0;
      j+=2;
  }
  c[j].x=(ctrl[0].x+ctrl[cnt-2].x)/2.0;
  c[j].y=(ctrl[0].y+ctrl[cnt-2].y)/2.0;
  c[j].z=(ctrl[0].z+ctrl[cnt-2].z)/2.0;

  c[0].x=(c[1].x+c[j].x)/2.0;
  c[0].y=(c[1].y+c[j].y)/2.0;
  c[0].z=(c[1].z+c[j].z)/2.0;
  j=2;
  for (i=0;i<cnt-2;i++) {
      c[j].x=(c[j-1].x+c[j+1].x)/2.0;
      c[j].y=(c[j-1].y+c[j+1].y)/2.0;
      c[j].z=(c[j-1].z+c[j+1].z)/2.0;
      j+=2;
  }
  c[j].x=c[0].x;
  c[j].y=c[0].y;
  c[j].z=c[0].z;
} else {
  c=MAlloc(sizeof(P3I4)*(cnt*2-1));
  c[0].x=ctrl[0].x;
  c[0].y=ctrl[0].y;
  c[0].z=ctrl[0].z;
  c[cnt*2-2].x=ctrl[cnt-1].x;
  c[cnt*2-2].y=ctrl[cnt-1].y;
  c[cnt*2-2].z=ctrl[cnt-1].z;
  j=1;
  for (i=0;i<cnt-1;i++) {
      c[j].x=(ctrl[i].x+ctrl[i+1].x)/2.0;
      c[j].y=(ctrl[i].y+ctrl[i+1].y)/2.0;
      c[j].z=(ctrl[i].z+ctrl[i+1].z)/2.0;
      j+=2;
  }
  j=2;
  for (i=0;i<cnt-2;i++) {
      c[j].x=(c[j-1].x+c[j+1].x)/2.0;
      c[j].y=(c[j-1].y+c[j+1].y)/2.0;
      c[j].z=(c[j-1].z+c[j+1].z)/2.0;
      j+=2;
  }
}
for (i=0;i<cnt*2-2;i+=2) {
  Bezier2(aux_data,&c[i],plot_cb,first);
  first=FALSE;
}
Free(c);
}

public void BSpline3(void *aux_data,P3I4 *ctrl,I8 cnt,void plot_cb(void *aux,I8 x,I8 y,I8 z),BoolI1
closed=FALSE)
{ //3rd order
I8 i,j;
double x,y,z;
P3I4 *c;
BoolI1 first=TRUE;
if (cnt<3) return;
if (closed) {
  cnt++;
  c=MAlloc(sizeof(P3I4)*(cnt*3-2));
  j=1;
  for (i=0;i<cnt-2;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      c[j].x=(ctrl[i+1].x-x)/3.0+x;
      c[j].y=(ctrl[i+1].y-y)/3.0+y;
      c[j].z=(ctrl[i+1].z-z)/3.0+z;
      j++;
      c[j].x=2.0*(ctrl[i+1].x-x)/3.0+x;
      c[j].y=2.0*(ctrl[i+1].y-y)/3.0+y;
      c[j].z=2.0*(ctrl[i+1].z-z)/3.0+z;
      j+=2;
  }
  x=ctrl[cnt-2].x;
  y=ctrl[cnt-2].y;
  z=ctrl[cnt-2].z;
  c[j].x=(ctrl[0].x-x)/3.0+x;
  c[j].y=(ctrl[0].y-y)/3.0+y;
  c[j].z=(ctrl[0].z-z)/3.0+z;
  j++;
  c[j].x=2.0*(ctrl[0].x-x)/3.0+x;
  c[j].y=2.0*(ctrl[0].y-y)/3.0+y;
  c[j].z=2.0*(ctrl[0].z-z)/3.0+z;

  c[0].x=(c[1].x+c[j].x)/2.0;
  c[0].y=(c[1].y+c[j].y)/2.0;
  c[0].z=(c[1].z+c[j].z)/2.0;

  j=3;
  for (i=0;i<cnt-2;i++) {
      c[j].x=(c[j-1].x+c[j+1].x)/2.0;
      c[j].y=(c[j-1].y+c[j+1].y)/2.0;
      c[j].z=(c[j-1].z+c[j+1].z)/2.0;
      j+=3;
  }
  c[j].x=c[0].x;
  c[j].y=c[0].y;
  c[j].z=c[0].z;
} else {
  c=MAlloc(sizeof(P3I4)*(cnt*3-2));
  c[0].x=ctrl[0].x;
  c[0].y=ctrl[0].y;
  c[0].z=ctrl[0].z;
  c[cnt*3-3].x=ctrl[cnt-1].x;
  c[cnt*3-3].y=ctrl[cnt-1].y;
  c[cnt*3-3].z=ctrl[cnt-1].z;
  j=1;
  for (i=0;i<cnt-1;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      c[j].x=(ctrl[i+1].x-x)/3.0+x;
      c[j].y=(ctrl[i+1].y-y)/3.0+y;
      c[j].z=(ctrl[i+1].z-z)/3.0+z;
      j++;
      c[j].x=2.0*(ctrl[i+1].x-x)/3.0+x;
      c[j].y=2.0*(ctrl[i+1].y-y)/3.0+y;
      c[j].z=2.0*(ctrl[i+1].z-z)/3.0+z;
      j+=2;
  }
  j=3;
  for (i=0;i<cnt-2;i++) {
      c[j].x=(c[j-1].x+c[j+1].x)/2.0;
      c[j].y=(c[j-1].y+c[j+1].y)/2.0;
      c[j].z=(c[j-1].z+c[j+1].z)/2.0;
      j+=3;
  }
}
for (i=0;i<cnt*3-3;i+=3) {
  Bezier3(aux_data,&c[i],plot_cb,first);
  first=FALSE;
}
Free(c);
}

#define CC_LEFT                1
#define CC_RIGHT        2
#define CC_TOP                4
#define CC_BOTTOM        8

public BoolI8 ClipLine(I8 *_x1,I8 *_y1,I8 *_x2,I8 *_y2,I8 left,I8 top,I8 right,I8 bottom)
{
I8 x,y,x1=*_x1,y1=*_y1,x2=*_x2,y2=*_y2;
U8 cc,cc1,cc2;

if (y1>bottom)
  cc1=CC_BOTTOM;
else if (y1<top)
  cc1=CC_TOP;
else
  cc1=0;
if (x1>right)
  cc1|=CC_RIGHT;
else if (x1<left)
  cc1|=CC_LEFT;

if (y2>bottom)
  cc2=CC_BOTTOM;
else if (y2<top)
  cc2=CC_TOP;
else
  cc2=0;
if (x2>right)
  cc2|=CC_RIGHT;
else if (x2<left)
  cc2|=CC_LEFT;

do {
  if (!(cc1|cc2))
      return TRUE;
  if (cc1&cc2)
      return FALSE;

  if (cc1)
      cc=cc1;
  else
      cc=cc2;

  if (cc&CC_BOTTOM) {
      x=x1+(x2-x1)*(bottom-y1)/(y2-y1);
      y=bottom;
  } else if (cc&CC_TOP) {
      x=x1+(x2-x1)*(top-y1)/(y2-y1);
      y=top;
  } else if (cc&CC_RIGHT) {
      y=y1+(y2-y1)*(right-x1)/(x2-x1);
      x=right;
  } else {
      y=y1+(y2-y1)*(left-x1)/(x2-x1);
      x=left;
  }

  if (cc==cc1) {
      *_x1=x1=x;
      *_y1=y1=y;
      if (y1>bottom)
          cc1=CC_BOTTOM;
      else if (y1<top)
          cc1=CC_TOP;
      else
          cc1=0;
      if (x1>right)
          cc1|=CC_RIGHT;
      else if (x1<left)
          cc1|=CC_LEFT;
  } else {
      *_x2=x2=x;
      *_y2=y2=y;
      if (y2>bottom)
          cc2=CC_BOTTOM;
      else if (y2<top)
          cc2=CC_TOP;
      else
          cc2=0;
      if (x2>right)
          cc2|=CC_RIGHT;
      else if (x2<left)
          cc2|=CC_LEFT;
  }
} while (TRUE);
}

#help_index "Graphics"

public BoolI8 GrLimits(GrBitMap *base,I8 *left,I8 *top,I8 *right,I8 *bottom,I8 width=0,I8 height=0)
{ //returns screen, not window coordinates
*left=0;
*top=0;
*right=base->width-1;
*bottom=base->height-1;
if (base->flags & BMF_SCREEN_BITMAP) {
  if (GR_WIDTH-1<*right)
      *right=GR_WIDTH-1;
  if (GR_HEIGHT-1<*bottom)
      *bottom=GR_HEIGHT-1;
  if (base->win_tss->win_pixel_left>*left)
      *left=base->win_tss->win_pixel_left;
  if (base->win_tss->win_pixel_top>*top)
      *top=base->win_tss->win_pixel_top;
  if (base->win_tss->win_pixel_right<*right)
      *right=base->win_tss->win_pixel_right;
  if (base->win_tss->win_pixel_bottom<*bottom)
      *bottom=base->win_tss->win_pixel_bottom;
}
*left-=width;
*right+=width;
*top-=height;
*bottom+=height;
return *left<=*right && *top<=*bottom;
}

BoolI8 GrClipLine(GrBitMap *base,I8 *x1,I8 *y1,I8 *x2,I8 *y2,I8 width=0,I8 height=0)
{ //also converts window to screen coordinates
I8 left,top,right,bottom;
if (GrLimits(base,&left,&top,&right,&bottom,width,height)) {
  if (base->flags & BMF_SCREEN_BITMAP) {
      *x1+=base->win_tss->win_pixel_left;
      *y1+=base->win_tss->win_pixel_top;
      *x2+=base->win_tss->win_pixel_left;
      *y2+=base->win_tss->win_pixel_top;
  }
  return ClipLine(x1,y1,x2,y2,left,top,right,bottom);
} else
  return FALSE;
}

public void GrPlot(GrBitMap *base,I8 x,I8 y)
{  //Clipping but No transformation or pen width
if (base->brush)
  GrBlot(base,x,y,base->brush);
else if (base->flags & BMF_SCREEN_BITMAP) {
  if (x>=0 && y>=0) {
      x+=base->win_tss->win_pixel_left;
      y+=base->win_tss->win_pixel_top;
      if (x<=base->win_tss->win_pixel_right &&
              y<=base->win_tss->win_pixel_bottom &&
              0<=x<base->right_margin-base->left_margin &&
              0<=y<base->bottom_margin-base->top_margin &&
              (base->flags&BMF_ON_TOP ||
                !IsPixelCovered(base->win_tss,x,y)))
          GrPlot0(base,x,y);
  }
} else
  if (0<=x<base->right_margin-base->left_margin &&
          0<=y<base->bottom_margin-base->top_margin)
      GrPlot0(base,x,y);
}

public I8 GrPeek(GrBitMap *base,I8 x,I8 y)
//Returns pixel color or -1 if off-screen or covered.
{  //Clipping but No transformation
BoolI1 peek=TRUE;
if (base->flags & BMF_SCREEN_BITMAP) {
  if (x<0) peek=FALSE;
  x+=base->win_tss->win_pixel_left;
  if (x>base->win_tss->win_pixel_right) peek=FALSE;

  if (y<0) peek=FALSE;
  y+=base->win_tss->win_pixel_top;
  if (y>base->win_tss->win_pixel_bottom) peek=FALSE;
  if (x>=base->right_margin-base->left_margin) peek=FALSE;
  if (y>=base->bottom_margin-base->top_margin) peek=FALSE;
  if (!(base->flags&BMF_ON_TOP) &&
          IsPixelCovered(base->win_tss,x,y)) peek=FALSE;
} else {
  if (x<0) peek=FALSE;
  if (x>=base->right_margin-base->left_margin) peek=FALSE;
  if (y<0) peek=FALSE;
  if (y>=base->bottom_margin-base->top_margin) peek=FALSE;
}
if (peek)
  return GrPeek0(base,x,y);
else
  return -1;
}

I8 GrFloodFillRay(GrBitMap *base,I8 x,I8 y)
{
I8 cnt,j,x1,ray_len,ray_len2;

cnt=ray_len=GrRayLen(base,&x,y);
y--;
j=ray_len;
x1=x;
while (j>0) {
  if (ray_len2=GrRayLenMinus(base,x1,y))
      cnt+=GrFloodFillRay(base,x1,y);
  j-=ray_len2+1;
  x1-=ray_len2+1;
}

y+=2;
j=ray_len;
x1=x;
while (j>0) {
  if (ray_len2=GrRayLenMinus(base,x1,y))
      cnt+=GrFloodFillRay(base,x1,y);
  j-=ray_len2+1;
  x1-=ray_len2+1;
}
return cnt;
}


public I8 GrFloodFill(GrBitMap *base,I8 x,I8 y,BoolI1 not_color=FALSE)
{
//$FG,2$not_color=TRUE$FG$ means fill up to everything which is not the current color.
//$FG,2$not_color=FALSE$FG$ means fill all parts equal to the color under the point.
I8 cnt=0,j,c,old_color2=base->color2,old_flags=base->flags;
GrBitMap *old_brush;
if (base->flags & BMF_DONT_DRAW) //TODO
  return 0;
old_brush=base->brush;
base->brush=NULL;
if ((j=GrPeek(base,x,y))>=0) {
  switch (base->type) {
      case BMT_COLOR4:
      case BMT_COLOR4_U1:
          if (not_color) {
              base->color2=base->color&0xFFFFFF;
              base->flags|=BMF_FILL_NOT_COLOR;
          } else {
              base->color2=j;
              if (base->color2==base->color&0xFFFFFF)
                  break;
              base->flags&=~BMF_FILL_NOT_COLOR;
          }
          if (not_color && j!=base->color2 ||
                !not_color)
              cnt=GrFloodFillRay(base,x,y);
          break;
      case BMT_MONO:
          c=base->color&0xFFFFFF ?1:0;
          if (not_color) {
              base->color2=c;
              base->flags|=BMF_FILL_NOT_COLOR;
          } else {
              base->color2=j;
              if (base->color2==c)
                  break;
              base->flags&=~BMF_FILL_NOT_COLOR;
          }
          if (not_color && j!=c ||
                !not_color)
              cnt=GrFloodFillRay(base,x,y);
          break;
  }
}
base->brush=old_brush;
base->flags=old_flags;
base->color2=old_color2;
return cnt;
}

public void GrPlot2(GrBitMap *base,I8 x,I8 y,I8 z)
{  //Clipping and transformation but no pen_width
I8 _x,_y,_z;
BoolI1 was_transform=FALSE,was_symmetry=FALSE;
if (base->flags & BMF_TRANSFORMATION) {
  GrTransform(base,&x,&y,&z);
  base->flags&=~BMF_TRANSFORMATION;
  was_transform=TRUE;
}
if (base->flags & BMF_SYMMETRY) {
  _x=x; _y=y; _z=z;
  GrReflect(base,&_x,&_y,&_z);
  base->flags&=~BMF_SYMMETRY;
  GrPlot(base,_x,_y);
  was_symmetry=TRUE;
  if (base->flags&BMF_JUST_MIRROR)
      goto done;
}
GrPlot(base,x,y);
done:
if (was_transform)
  base->flags|=BMF_TRANSFORMATION;
if (was_symmetry)
  base->flags|=BMF_SYMMETRY;
}

public void GrPlot3(GrBitMap *base,I8 x,I8 y,I8 z)
{  //clipping and transformation and pen width

I8 _x,_y,_z,i,j,w,old_color,dist;
BoolI1 was_transform=FALSE,was_symmetry=FALSE;
if (base->flags & BMF_TRANSFORMATION) {
  GrTransform(base,&x,&y,&z);
  base->flags&=~BMF_TRANSFORMATION;
  was_transform=TRUE;
}
if (base->flags & BMF_SYMMETRY) {
  _x=x; _y=y; _z=z;
  GrReflect(base,&_x,&_y,&_z);
  base->flags&=~BMF_SYMMETRY;
  GrPlot3(base,_x,_y,_z);
  was_symmetry=TRUE;
  if (base->flags&BMF_JUST_MIRROR)
      goto done;
}
w=base->pen_width>>1;
if (w<=0)
  GrPlot(base,x,y);
else if (base->pen_width<GR_NUM_PEN_BRUSHES) {
  old_color=base->color;
  if (base->color.u1[3]==ROPB_EQU)
      base->color.u1[3]=ROPB_TRANSPARENT;
  if (base->color.u1[3]==ROPB_CLEAR_MASK_EQU)
      base->color.u1[3]=ROPB_CLEAR_MASK_TRANSPARENT;
  if (GrBlot(base,x-w,y-w,gr_pen_brushes[base->pen_width])) {
      if (base->flags & BMF_SCREEN_BITMAP) {
          x+=base->win_tss->win_pixel_left;
          y+=base->win_tss->win_pixel_top;
      }
      if (base->flags & BMF_LOCATE_NEAREST) {
          dist=DistI8(x,y,base->cur_x,base->cur_y);
          if (dist<base->nearest_dist) {
              base->nearest_grelem_num=base->cur_grelem_num;
              base->nearest_dist=dist;
          }
      }
      if (base->flags & BMF_RECORD_EXTENTS) {
          if (x-w<base->min_x) base->min_x=x-w;
          if (y-w<base->min_y) base->min_y=y-w;
          if (base->pen_width & 1) {
              if (x+w>base->max_x) base->max_x=x+w;
              if (y+w>base->max_y) base->max_y=y+w;
          } else {
              if (x+w-1>base->max_x) base->max_x=x+w-1;
              if (y+w-1>base->max_y) base->max_y=y+w-1;
          }
      }
  }
  base->color=old_color;
} else if (base->pen_width & 1) {
  for (i=-w;i<=w;i++)
      for (j=-w;j<=w;j++)
          GrPlot(base,x+i,y+j);
} else {
  for (i=-w;i<w;i++)
      for (j=-w;j<w;j++)
          GrPlot(base,x+i,y+j);
}
done:
if (was_transform)
  base->flags|=BMF_TRANSFORMATION;
if (was_symmetry)
  base->flags|=BMF_SYMMETRY;
}

void GrLinePlot0(GrBitMap *base,I8 x,I8 y,I8 z)
{
nounusedwarn z;
if (!(base->flags & BMF_SCREEN_BITMAP) ||
      base->flags&BMF_ON_TOP ||
      !IsPixelCovered(base->win_tss,x,y))
  GrPlot0(base,x,y);
}

void GrLinePlot(GrBitMap *base,I8 x,I8 y,I8 z)
{
nounusedwarn z;
GrPlot(base,x,y);
}

public void GrLine(GrBitMap *base,I8 x1,I8 y1,I8 x2,I8 y2,I8 step=1,I8 start=0)
{
if (step==1 && !start && !base->brush) {
  if (GrClipLine(base,&x1,&y1,&x2,&y2))
      Line(base,x1,y1,0,x2,y2,0,&GrLinePlot0,step,start);
} else
  Line(base,x1,y1,0,x2,y2,0,&GrLinePlot,step,start);
}

public void GrCircle(GrBitMap *base,I8 cx,I8 cy,I8 radius,
I8 step=1,double start_radians=0,double len_radians=2*pi)
{
Circle(base,cx,cy,0,radius,&GrLinePlot,step,start_radians,len_radians);
}

public void GrEllipse(GrBitMap *base,
                          I8 cx,I8 cy,
                          I8 x_radius,I8 y_radius,
                          double rot_angle=0,
                          I8 step=1,
                          double start_radians=0,
                          double len_radians=2*pi)
{
Ellipse(base,cx,cy,0,x_radius,y_radius,&GrLinePlot,rot_angle,step,start_radians,len_radians);
}

public void GrRegPolygon(GrBitMap *base,
                          I8 cx,I8 cy,
                          I8 x_radius,I8 y_radius,I8 sides,
                          double rot_angle=0,
                          I8 step=1,
                          double start_radians=0,
                          double len_radians=2*pi)
{
RegPolygon(base,cx,cy,0,x_radius,y_radius,sides,
  &GrLinePlot,rot_angle,step,start_radians,len_radians);
}

public void Gr2Bezier(GrBitMap *base,P3I4 *ctrl)
{
Bezier2(base,ctrl,&GrLinePlot);
}

public void Gr3Bezier(GrBitMap *base,P3I4 *ctrl)
{
Bezier3(base,ctrl,&GrLinePlot);
}

public void Gr2BSpline(GrBitMap *base,P3I4 *ctrl,I8 cnt,BoolI1 closed=FALSE)
{
BSpline2(base,ctrl,cnt,&GrLinePlot,closed);
}

public void Gr3BSpline(GrBitMap *base,P3I4 *ctrl,I8 cnt,BoolI1 closed=FALSE)
{
BSpline3(base,ctrl,cnt,&GrLinePlot,closed);
}

public void GrLine2(GrBitMap *base,I8 x1,I8 y1,I8 z1,I8 x2,I8 y2,I8 z2,I8 step=1,I8 start=0)
{  //transformation but not pen width
I8 _x1,_y1,_z1,_x2,_y2,_z2;
BoolI1 was_transform=FALSE,was_symmetry=FALSE;
if (base->flags & BMF_TRANSFORMATION) {
  GrTransform(base,&x1,&y1,&z1);
  GrTransform(base,&x2,&y2,&z2);
  base->flags&=~BMF_TRANSFORMATION;
  was_transform=TRUE;
}
if (base->flags & BMF_SYMMETRY) {
  _x1=x1; _y1=y1; _z1=z1;
  GrReflect(base,&_x1,&_y1,&_z1);
  _x2=x2; _y2=y2; _z2=z2;
  GrReflect(base,&_x2,&_y2,&_z2);
  base->flags&=~BMF_SYMMETRY;
  if (step==1 && !start && !base->brush) {
      if (GrClipLine(base,&_x1,&_y1,&_x2,&_y2))
          Line(base,_x1,_y1,0,_x2,_y2,0,&GrLinePlot0,step,start);
  } else
      Line(base,_x1,_y1,0,_x2,_y2,0,&GrLinePlot,step,start);
  was_symmetry=TRUE;
  if (base->flags&BMF_JUST_MIRROR)
      goto done;
}
if (step==1 && !start && !base->brush) {
  if (GrClipLine(base,&x1,&y1,&x2,&y2))
      Line(base,x1,y1,0,x2,y2,0,&GrLinePlot0,step,start);
} else
  Line(base,x1,y1,0,x2,y2,0,&GrLinePlot,step,start);
done:
if (was_transform)
  base->flags|=BMF_TRANSFORMATION;
if (was_symmetry)
  base->flags|=BMF_SYMMETRY;
}

void GrLine3Plot(GrBitMap *base,I8 x,I8 y,I8 z)
{
GrPlot3(base,x,y,z);
}

public void GrLine3(GrBitMap *base,I8 x1,I8 y1,I8 z1,I8 x2,I8 y2,I8 z2,I8 step=1,I8 start=0)
{  //transformation with pen width
I8 _x1,_y1,_z1,_x2,_y2,_z2;
BoolI1 was_transform=FALSE,was_symmetry=FALSE;
if (base->flags & BMF_TRANSFORMATION) {
  GrTransform(base,&x1,&y1,&z1);
  GrTransform(base,&x2,&y2,&z2);
  base->flags&=~BMF_TRANSFORMATION;
  was_transform=TRUE;
}
if (base->flags & BMF_SYMMETRY) {
  _x1=x1; _y1=y1; _z1=z1;
  GrReflect(base,&_x1,&_y1,&_z1);
  _x2=x2; _y2=y2; _z2=z2;
  GrReflect(base,&_x2,&_y2,&_z2);
  base->flags&=~BMF_SYMMETRY;
  Line(base,_x1,_y1,0,_x2,_y2,0,&GrLine3Plot,step,start);
  was_symmetry=TRUE;
  if (base->flags&BMF_JUST_MIRROR)
      goto done;
}
Line(base,x1,y1,0,x2,y2,0,&GrLine3Plot,step,start);
done:
if (was_transform)
  base->flags|=BMF_TRANSFORMATION;
if (was_symmetry)
  base->flags|=BMF_SYMMETRY;
}

public BoolI8 GrPutS3(GrBitMap *base,I8 x1,I8 y1,I8 z1,I1 *s)
{  //transformation
//BMF_SYMMETRY is silly.
if (base->flags & BMF_TRANSFORMATION)
  GrTransform(base,&x1,&y1,&z1);
return GrPutS(base,x1,y1,s);
}

public BoolI8 GrVPutS3(GrBitMap *base,I8 x1,I8 y1,I8 z1,I1 *s)
{ //Vertical text
//transformation
//BMF_SYMMETRY is silly.
if (base->flags & BMF_TRANSFORMATION)
  GrTransform(base,&x1,&y1,&z1);
return GrVPutS(base,x1,y1,s);
}

public void GrPrintF3(GrBitMap *base,I8 x,I8 y,I8 z,I1 *fmt,...)
{
I1 *buf=SPrintFJoin(NULL,fmt,argc,argv);
GrPutS3(base,x,y,z,buf);
Free(buf);
}

public void GrVPrintF3(GrBitMap *base,I8 x,I8 y,I8 z,I1 *fmt,...)
{ //Vertical text
I1 *buf=SPrintFJoin(NULL,fmt,argc,argv);
GrVPutS3(base,x,y,z,buf);
Free(buf);
}


public void GrEllipse3(GrBitMap *base,
                          I8 cx,I8 cy,I8 cz,
                          I8 x_radius,I8 y_radius,
                          double rot_angle=0,
                          I8 step=1,
                          double start_radians=0,
                          double len_radians=2*pi)
{
I8 x,y,z;
double m1,a1,m2,a2,s,c;
if (base->flags & BMF_TRANSFORMATION) {
  base->flags&=~BMF_TRANSFORMATION;
  GrTransform(base,&cx,&cy,&cz);

  c=Cos(rot_angle);
  s=Sin(rot_angle);

  x_radius<<=24;
  y_radius<<=24;

  x=x_radius*c;
  y=x_radius*s;
  z=0;
  GrRotate(base->r,&x,&y,&z);
  R2P(&m1,&a1,x,y);

  x=-y_radius*s;
  y=y_radius*c;
  z=0;
  GrRotate(base->r,&x,&y,&z);
  R2P(&m2,&a2,x,y);
  m2=m2*Abs(Sin(a2-a1));

  Ellipse(base,cx,cy,cz,
      m1/0x1000000,m2/0x1000000,&GrLine3Plot,-a1,step,start_radians,len_radians);
  base->flags|=BMF_TRANSFORMATION;
} else
  Ellipse(base,cx,cy,cz,x_radius,y_radius,&GrLine3Plot,rot_angle,step,start_radians,len_radians);
}

public void GrCircle3(GrBitMap *base,I8 cx,I8 cy,I8 cz,I8 radius,
I8 step=1,double start_radians=0,double len_radians=2*pi)
{ //transformation with pen width
if (base->flags & BMF_TRANSFORMATION)
  GrEllipse3(base,cx,cy,cz,radius,radius,0,step,start_radians,len_radians);
else
  Circle(base,cx,cy,cz,radius,&GrLine3Plot,step,start_radians,len_radians);
}

public void GrRegPolygon3(GrBitMap *base,
                          I8 cx,I8 cy,I8 cz,
                          I8 x_radius,I8 y_radius,I8 sides,
                          double rot_angle=0,
                          I8 step=1,
                          double start_radians=0,
                          double len_radians=2*pi)
{
I8 x,y,z;
double m1,a1,m2,a2,s,c;
if (base->flags & BMF_TRANSFORMATION) {
  base->flags&=~BMF_TRANSFORMATION;
  GrTransform(base,&cx,&cy,&cz);

  c=Cos(rot_angle);
  s=Sin(rot_angle);

  x_radius<<=24;
  y_radius<<=24;

  x=x_radius*c;
  y=x_radius*s;
  z=0;
  GrRotate(base->r,&x,&y,&z);
  R2P(&m1,&a1,x,y);

  x=-y_radius*s;
  y=y_radius*c;
  z=0;
  GrRotate(base->r,&x,&y,&z);
  R2P(&m2,&a2,x,y);
  m2=m2*Abs(Sin(a2-a1));

  RegPolygon(base,cx,cy,cz,
      m1/0x1000000,m2/0x1000000,sides,&GrLine3Plot,-a1,step,start_radians,len_radians);
  base->flags|=BMF_TRANSFORMATION;
} else
  
RegPolygon(base,cx,cy,cz,x_radius,y_radius,sides,&GrLine3Plot,rot_angle,step,start_radians,len_radians
);
}

public I8 GrFloodFill3(GrBitMap *base,I8 x1,I8 y1,I8 z1,BoolI1 not_color=FALSE)
{  //transformation
//$FG,2$not_color=TRUE$FG$ means fill up to everything which is not the current color.
//$FG,2$not_color=FALSE$FG$ means fill all parts equal to the color under the point.
I8 cnt,old_flags=base->flags;

I8 _x,_y,_z;
if (base->flags & BMF_TRANSFORMATION) {
  GrTransform(base,&x1,&y1,&z1);
  base->flags&=~BMF_TRANSFORMATION;
}
if (base->flags & BMF_SYMMETRY) {
  _x=x1; _y=y1; _z=z1;
  GrReflect(base,&_x,&_y,&_z);
  base->flags&=~BMF_SYMMETRY;
  cnt=GrFloodFill(base,_x,_y,not_color);
  if (base->flags&BMF_JUST_MIRROR)
      goto done;
}
cnt=GrFloodFill(base,x1,y1,not_color);
done:
base->flags=old_flags;
return cnt;
}

public void GrBox3(GrBitMap *base,I8 x,I8 y,I8 z,I8 w,I8 h)
{  //Clipping and transformation
I8 x2,y2,z2,x3,y3,z3;
GrBitMap *old_brush=base->brush;
if (base->flags & BMF_TRANSFORMATION) {
  base->brush=NULL;
  x2=x+w; y2=y; z2=z;
  x3=x; y3=y+h; z3=z;
  GrTransform(base,&x,&y,&z);
  GrTransform(base,&x2,&y2,&z2);
  GrTransform(base,&x3,&y3,&z3);
  if (!(base->flags&BMF_JUST_MIRROR) ||
          !(base->flags&BMF_SYMMETRY))
      Box(base,x,y,z,x2,y2,z2,x3,y3,z3,&GrLinePlot);
  if (base->flags & BMF_SYMMETRY) {
      GrReflect(base,&x,&y,&z);
      GrReflect(base,&x2,&y2,&z2);
      GrReflect(base,&x3,&y3,&z3);
      Box(base,x,y,z,x2,y2,z2,x3,y3,z3,&GrLinePlot);
  }
  base->brush=old_brush;
} else {
  if (!(base->flags&BMF_JUST_MIRROR)||
          !(base->flags&BMF_SYMMETRY))
      GrBox(base,x,y,w,h);
  if (base->flags & BMF_SYMMETRY) {
      base->brush=NULL;
      x2=x+w; y2=y; z2=z;
      x3=x; y3=y+h; z3=z;
      GrReflect(base,&x,&y,&z);
      GrReflect(base,&x2,&y2,&z2);
      GrReflect(base,&x3,&y3,&z3);
      Box(base,x,y,z,x2,y2,z2,x3,y3,z3,&GrLinePlot);
      base->brush=old_brush;
  }
}
}

public BoolI8 GrBlot3(GrBitMap *base,I8 x1,I8 y1,I8 z1,GrBitMap *img)
{  //Clipping and transformation
I8 reg i,j,w=img->width,h=img->height,old_color=base->color,reg color,
      d1,dx1,dy1,dz1,
      reg d2,dx2,dy2,dz2,
      adx1,ady1,adz1,
      adx2,ady2,adz2,
      x2,y2,z2,x3,y3,z3,
      dw,reg dh,x,y,_x1,_y1,_z1,_x2,_y2,_z2,_x3,_y3,_z3,
      last_x,last_y;
BoolI1 first;
GrBitMap *old_brush=base->brush;

if (base->flags & (BMF_TRANSFORMATION | BMF_SYMMETRY)) {
  x2=x1+w; y2=y1; z2=z1;
  x3=x1; y3=y1+h; z3=z1;
  if (base->flags & BMF_TRANSFORMATION) {
      GrTransform(base,&x1,&y1,&z1);
      GrTransform(base,&x2,&y2,&z2);
      GrTransform(base,&x3,&y3,&z3);
  }
  if (base->flags & BMF_SYMMETRY) {
      _x1=x1; _y1=y1; _z1=z1;
      GrReflect(base,&_x1,&_y1,&_z1);
      _x2=x2; _y2=y2; _z2=z2;
      GrReflect(base,&_x2,&_y2,&_z2);
      _x3=x3; _y3=y3; _z3=z3;
      GrReflect(base,&_x3,&_y3,&_z3);
      dx1=_x2-_x1; dy1=_y2-_y1; dz1=_z2-_z1;
      dx2=_x3-_x1; dy2=_y3-_y1; dz2=_z3-_z1;
      adx1=AbsI8(dx1); ady1=AbsI8(dy1); adz1=AbsI8(dz1);
      adx2=AbsI8(dx2); ady2=AbsI8(dy2); adz2=AbsI8(dz2);

      if (adx1>=ady1)
          d1=adx1>=adz1 ? adx1:adz1;
      else
          d1=ady1>=adz1 ? ady1:adz1;
      if (adx2>=ady2)
          d2=adx2>=adz2 ? adx2:adz2;
      else
          d2=ady2>=adz2 ? ady2:adz2;

      if (AbsI8(d1)!=w ||AbsI8(d2)!=h) {
          d1<<=1;
          d2<<=1;
      }
      if (d1) {
          dx1=dx1<<32/d1;
          dy1=dy1<<32/d1;
          dz1=dz1<<32/d1;
      } else
          goto normal_image;
      if (d2) {
          dx2=dx2<<32/d2;
          dy2=dy2<<32/d2;
          dz2=dz2<<32/d2;
      } else
          goto normal_image;
      base->brush=NULL;
      x=0;y=0;
      dw=w<<32/d1;
      dh=h<<32/d2;

      first=TRUE;
      _x1<<=32; _y1<<=32; _z1<<=32;
      for (j=0;j<=d1;j++) {
          _x2=_x1; _y2=_y1; _z2=_z1;
          y=0;
          for (i=0;i<=d2;i++) {
              if (_x2.i4[1]!=last_x || _y2.i4[1]!=last_y ||first) {
                  color=GrPeek(img,x.i4[1],y.i4[1]);
                  if (color>=0 &&
                          (old_color.u1[3]!=ROPB_TRANSPARENT ||
                          color&0xFFFFFF!=img->bkcolor&0xFFFFFF)) {
                      base->color=old_color&0xFF000000+color;
                      GrPlot(base,_x2.i4[1],_y2.i4[1]);
                  }
              }
              first=FALSE;
              last_x=_x2.i4[1]; last_y=_y2.i4[1];
              _x2+=dx2; _y2+=dy2; _z2+=dz2;
              y+=dh;
          }
          _x1+=dx1; _y1+=dy1; _z1+=dz1;
          x+=dw;
      }
normal_image:
      if (base->flags&BMF_JUST_MIRROR)
          goto done;
  }
  dx1=x2-x1; dy1=y2-y1; dz1=z2-z1;
  dx2=x3-x1; dy2=y3-y1; dz2=z3-z1;
  adx1=AbsI8(dx1); ady1=AbsI8(dy1); adz1=AbsI8(dz1);
  adx2=AbsI8(dx2); ady2=AbsI8(dy2); adz2=AbsI8(dz2);

  if (adx1>=ady1)
      d1=adx1>=adz1 ? adx1:adz1;
  else
      d1=ady1>=adz1 ? ady1:adz1;
  if (adx2>=ady2)
      d2=adx2>=adz2 ? adx2:adz2;
  else
      d2=ady2>=adz2 ? ady2:adz2;
  if (AbsI8(d1)!=w ||AbsI8(d2)!=h) {
      d1<<=1;
      d2<<=1;
  }
  if (d1) {
      dx1=dx1<<32/d1;
      dy1=dy1<<32/d1;
      dz1=dz1<<32/d1;
  } else
      return FALSE;
  if (d2) {
      dx2=dx2<<32/d2;
      dy2=dy2<<32/d2;
      dz2=dz2<<32/d2;
  } else
      return FALSE;
  base->brush=NULL;
  x=0;y=0;
  dw=w<<32/d1;
  dh=h<<32/d2;

  first=TRUE;
  x1<<=32; y1<<=32; z1<<=32;
  for (j=0;j<=d1;j++) {
      x2=x1; y2=y1; z2=z1;
      y=0;
      for (i=0;i<=d2;i++) {
          if (x2.i4[1]!=last_x || y2.i4[1]!=last_y || first) {
              color=GrPeek(img,x.i4[1],y.i4[1]);
              if (color>=0 &&
                      (old_color.u1[3]!=ROPB_TRANSPARENT ||
                      color&0xFFFFFF!=img->bkcolor&0xFFFFFF)) {
                  base->color=old_color&0xFF000000+color;
                  GrPlot(base,x2.i4[1],y2.i4[1]);
              }
          }
          first=FALSE;
          last_x=x2.i4[1]; last_y=y2.i4[1];
          x2+=dx2; y2+=dy2; z2+=dz2;
          y+=dh;
      }
      x1+=dx1; y1+=dy1; z1+=dz1;
      x+=dw;
  }
  base->color=old_color;
  base->brush=old_brush;
  return TRUE;  //TODO: check off screen
} else
  return GrBlot(base,x1,y1,img);
done:
}

public void Gr2Bezier3(GrBitMap *base,P3I4 *ctrl)
{
I8 i,x,y,z;
P3I4 *ctrl2;
if (base->flags & BMF_TRANSFORMATION) {
  ctrl2=MAlloc(sizeof(P3I4)*3);
  for (i=0;i<3;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrTransform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
  }
  base->flags&=~BMF_TRANSFORMATION;
  Bezier2(base,ctrl2,&GrLine3Plot);
  base->flags|=BMF_TRANSFORMATION;
  Free(ctrl2);
} else
  Bezier2(base,ctrl,&GrLine3Plot);
}

public void Gr3Bezier3(GrBitMap *base,P3I4 *ctrl)
{
I8 i,x,y,z;
P3I4 *ctrl2;
if (base->flags & BMF_TRANSFORMATION) {
  ctrl2=MAlloc(sizeof(P3I4)*4);
  for (i=0;i<4;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrTransform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
  }
  base->flags&=~BMF_TRANSFORMATION;
  Bezier3(base,ctrl2,&GrLine3Plot);
  base->flags|=BMF_TRANSFORMATION;
  Free(ctrl2);
} else
  Bezier3(base,ctrl,&GrLine3Plot);
}

public void Gr2BSpline3(GrBitMap *base,P3I4 *ctrl,I8 cnt,BoolI1 closed=FALSE)
{
I8 i,x,y,z;
P3I4 *ctrl2;
if (base->flags & BMF_TRANSFORMATION) {
  ctrl2=MAlloc(sizeof(P3I4)*cnt);
  for (i=0;i<cnt;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrTransform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
  }
  base->flags&=~BMF_TRANSFORMATION;
  BSpline2(base,ctrl2,cnt,&GrLine3Plot,closed);
  base->flags|=BMF_TRANSFORMATION;
  Free(ctrl2);
} else
  BSpline2(base,ctrl,cnt,&GrLine3Plot,closed);
}

public void Gr3BSpline3(GrBitMap *base,P3I4 *ctrl,I8 cnt,BoolI1 closed=FALSE)
{
I8 i,x,y,z;
P3I4 *ctrl2;
if (base->flags & BMF_TRANSFORMATION) {
  ctrl2=MAlloc(sizeof(P3I4)*cnt);
  for (i=0;i<cnt;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrTransform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
  }
  base->flags&=~BMF_TRANSFORMATION;
  BSpline3(base,ctrl2,cnt,&GrLine3Plot,closed);
  base->flags|=BMF_TRANSFORMATION;
  Free(ctrl2);
} else
  BSpline3(base,ctrl,cnt,&GrLine3Plot,closed);
}

public void GrSpeedLine(GrBitMap *base,I8 x1,I8 y1,I8 x2,I8 y2,double speed)
{
U8 old_flags=base->flags;
I8 old_pen_width=base->pen_width;
base->pen_width=base->speedline_scale*speed;
base->flags&=~(BMF_TRANSFORMATION|BMF_SYMMETRY|BMF_JUST_MIRROR);
GrLine3(base,x1,y1,0,x2,y2,0);
base->flags=old_flags;
base->pen_width=old_pen_width;
}

public void GrSpeedLine2(GrBitMap *base,I8 x1,I8 y1,I8 z1,I8 x2,I8 y2,I8 z2,double speed)
{  //transformation but not pen width
U8 old_flags=base->flags;
I8 _x1,_y1,_z1,_x2,_y2,_z2;
if (base->flags & BMF_TRANSFORMATION) {
  GrTransform(base,&x1,&y1,&z1);
  GrTransform(base,&x2,&y2,&z2);
  base->flags&=~BMF_TRANSFORMATION;
}
if (base->flags & BMF_SYMMETRY) {
  _x1=x1; _y1=y1; _z1=z1;
  GrReflect(base,&_x1,&_y1,&_z1);
  _x2=x2; _y2=y2; _z2=z2;
  GrReflect(base,&_x2,&_y2,&_z2);
  base->flags&=~BMF_SYMMETRY;
  GrSpeedLine(base,_x1,_y1,_x2,_y2,speed);
  if (base->flags & BMF_JUST_MIRROR)
      goto done;
}
GrSpeedLine(base,x1,y1,x2,y2,speed);
done:
base->flags=old_flags;
}
Graphic Plotting
Home

Kernel
StartUp
Scheduler
Memory
MultiCore
Interrupts
BitMaps
Screen
Plot
Compiler Frontend
Compiler Mid
Compiler Backend