#help_index "Graphics/Math"

public I8 gr_x_offsets[8]={-1,0,1,-1,1,-1,0,1},
          gr_y_offsets[8]={-1,-1,-1,0,0,1,1,1},
          gr_x_offsets2[4]={0,-1,1,0},
          gr_y_offsets2[4]={-1,0,0,1};

public BoolI8 Line(U0 *aux_data,
                 I8 x1,I8 y1,I8 z1,
                 I8 x2,I8 y2,I8 z2,
                 BoolI8 (*fp_plot)(U0 *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) {
      if (!fp_plot(aux_data,x1.i4[1],y1.i4[1],z1.i4[1]))
        return FALSE;
    }
    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)) {
    if (!fp_plot(aux_data,x2,y2,z2))
      return FALSE;
  }
  return TRUE;
}

public BoolI8 Rect(U0 *aux_data,
                I8 x1,I8 y1,I8 z1,
                I8 x2,I8 y2,I8 z2,
                I8 x3,I8 y3,I8 z3,
                BoolI8 (*fp_plot)(U0 *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) {
        if (!fp_plot(aux_data,x2.i4[1],y2.i4[1],z2.i4[1]))
          return FALSE;
      }
      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;
  }
  return TRUE;
}

#help_index "Graphics/Math/3D Transformation"
#help_file "::/LT/Doc/Transform"
I8 gr_scale=MAX_U4+1;

public U0 GrRot(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 U0 GrTransform(GrBitMap *base,I8 *x,I8 *y,I8 *z)
{
  GrRot(base->r,x,y,z);
  *x+=base->x;
  *y+=base->y;
  *z+=base->z;
}

public I8 *GrRotX(F8 phi,TaskStruct *mem_task=NULL)
{
  F8 my_cos=Cos(phi)*gr_scale,
     my_sin=Sin(phi)*gr_scale;
  I8 *r=CAlloc(sizeof(I8)*16,mem_task);

  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(F8 zeta,TaskStruct *mem_task=NULL)
{
  F8 my_cos=Cos(zeta)*gr_scale,
     my_sin=Sin(zeta)*gr_scale;
  I8 *r=CAlloc(sizeof(I8)*16,mem_task);

  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(F8 theta,TaskStruct *mem_task=NULL)
{
  F8 my_cos=Cos(theta)*gr_scale,
     my_sin=Sin(theta)*gr_scale;
  I8 *r=CAlloc(sizeof(I8)*16,mem_task);

  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,F8 s,TaskStruct *mem_task=NULL)
{
  I8 i,*r=CAlloc(sizeof(I8)*16,mem_task);
  for (i=0;i<16;i++)
    r[i]=m1[i]*s;
  return r;
}

public I8 *GrMulMat(I8 *m1,I8 *m2,TaskStruct *mem_task=NULL)
{ //Conceptually, the transform m1 is applied after m2
  I8 *r=CAlloc(sizeof(I8)*16,mem_task);
  I8 i,j,k;
  F8 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]; //TODO!  Possible overflow if norm > one.
        d/=gr_scale;
        r[i+4*j]+=d;
      }
    }
  }
  return r;
}

public I8 *GrRotXEqu(I8 *m1,F8 phi)
{
  F8 my_cos=Cos(phi)*gr_scale,
     my_sin=Sin(phi)*gr_scale;
  I8 r[16],*r2;
  MemSet(r,0,sizeof(r));
  r[5]=my_cos;
  r[10]=my_cos;
  r[9]=my_sin;
  r[6]=-my_sin;
  r[0]=gr_scale;
  r[15]=gr_scale;

  r2=GrMulMat(r,m1);
  MemCpy(m1,r2,sizeof(I8)*16);
  Free(r2);
  return m1;
}

public I8 *GrRotYEqu(I8 *m1,F8 zeta)
{
  F8 my_cos=Cos(zeta)*gr_scale,
     my_sin=Sin(zeta)*gr_scale;
  I8 r[16],*r2;
  MemSet(r,0,sizeof(r));
  r[0]=my_cos;
  r[10]=my_cos;
  r[8]=-my_sin;
  r[2]=my_sin;
  r[5]=gr_scale;
  r[15]=gr_scale;

  r2=GrMulMat(r,m1);
  MemCpy(m1,r2,sizeof(I8)*16);
  Free(r2);
  return m1;
}

public I8 *GrRotZEqu(I8 *m1,F8 theta)
{
  F8 my_cos=Cos(theta)*gr_scale,
     my_sin=Sin(theta)*gr_scale;
  I8 r[16],*r2;
  MemSet(r,0,sizeof(r));
  r[0]=my_cos;
  r[5]=my_cos;
  r[4]=my_sin;
  r[1]=-my_sin;
  r[10]=gr_scale;
  r[15]=gr_scale;

  r2=GrMulMat(r,m1);
  MemCpy(m1,r2,sizeof(I8)*16);
  Free(r2);
  return m1;
}

public I8 *GrScaleMatEqu(I8 *m1,F8 s)
{
  I8 i;
  for (i=0;i<16;i++)
    m1[i]*=s;
  return m1;
}

public U0 GrSetRotMat(GrBitMap *base,I8 *r)
{
// (1.0/Sqrt(3))*65536=37837.22
  I8 xx=37838,yy=37837,zz=37837;
  base->r=r;
  GrRot(r,&xx,&yy,&zz);
  //scaled 32 bits
  base->r_norm=Sqrt(xx*xx+yy*yy+zz*zz)*65536;
}

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

public I8 *GrIdent(TaskStruct *mem_task=NULL)
{
  I8 *r=CAlloc(sizeof(I8)*16,mem_task);
  r[0].i4[1]=1;
  r[5].i4[1]=1;
  r[10].i4[1]=1;
  r[15].i4[1]=1;
  return r;
}

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

public I8 *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;
  return r;
}

public BoolI8 GrSetSymmetry(GrBitMap *base,I8 x1,I8 y1,I8 x2,I8 y2)
{
  F8 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)
{
  F8 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)
    base->transform(base,&x1,&y1,&z1);
  base->sym.sx=x1;
  base->sym.sy=y1;
  base->sym.sz=z1;
  return TRUE;
}

public U0 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/Mesh"
public U0 GrLighting(GrBitMap *base,P3I4 *p1,P3I4 *p2,P3I4 *p3,I8 color)
{
  P3I4 v1,v2;
  I8 i,vn_x,vn_y,vn_z;
  F8 d;

  v1.x=p1->x-p2->x;
  v1.y=p1->y-p2->y;
  v1.z=p1->z-p2->z;

  v2.x=p3->x-p2->x;
  v2.y=p3->y-p2->y;
  v2.z=p3->z-p2->z;

//V1 and V2 are vectors along two sides
//of the triangle joined at p2.

  vn_x=v1.y*v2.z-v1.z*v2.y;
  vn_y=v1.z*v2.x-v1.x*v2.z;
  vn_z=v1.x*v2.y-v1.y*v2.x;
  if (d=Sqrt(SqrI8(vn_x)+SqrI8(vn_y)+SqrI8(vn_z)))
    d=1<<16/d;
  vn_x*=d;
  vn_y*=d;
  vn_z*=d;
//Vn is the cross product of V1 and V3
//which means it is perpendicular.It
//is the normal vector to the surface.
//It has been scaled to length 65536.

//Light source has been scaled to length 65536.
  i=(vn_x*base->ls.x+vn_y*base->ls.y+vn_z*base->ls.z)>>16;
//The dot product of the light source
//vector and the surface normal
//gives an illumination number.
//65536*65536>>16=65536

//LoseThos will generate a random U2
//and compare to dither_probability and
//will pick from two colors.
//Probability dithering does not work with pen_width>1 at this time.
  if (color&ROP_TWO_SIDED) {
    color&=~ROP_TWO_SIDED;
    i=AbsI8(i)<<1;
  } else
    i+=65536;
  if (color&ROP_HALF_RANGE_COLOR) {
    color&=~ROP_HALF_RANGE_COLOR;
    i>>=1;
    if (color>=8) {
      color-=8;
      i+=65536;
    }
  }
  if (i<65536) {
    base->color=ROP_PROBABILITY_DITHER+color<<32+BLACK;
    base->dither_probability=i;
  } else {
    base->color=ROP_PROBABILITY_DITHER+(color^8)<<32+color;
    base->dither_probability=i-65536;
  }
}

#help_index "Graphics/Math"
public BoolI8 Circle(U0 *aux_data,
                I8 cx,I8 cy,I8 cz,
                I8 radius,
                BoolI8 (*fp_plot)(U0 *aux,I8 x,I8 y,I8 z),
                I8 step=1,
                F8 start_radians=0,
                F8 len_radians=2*pi)
{
  I8 i,j,len=Ceiling(len_radians*radius);
  I8 x,y,x1,y1,s1,s2,c;
  F8 t;
  if (radius<=0||step<1) return TRUE;
  t=1.0/radius;
  s1=1<<16*Sin(t);
  s2=-s1;
  c=1<<16*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) {
    if (!fp_plot(aux_data,cx+x>>8,cy+y>>8,cz))
      return FALSE;
    for (j=0;j<step;j++) {
      x1=(c*x+s1*y)>>16;
      y1=(s2*x+c*y)>>16;
      x=x1; y=y1;
    }
  }
  return TRUE;
}

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

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

  s12=1<<16*Sin(rot_angle);
  s22=-s12;
  c2=1<<16*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)>>16;
  y=(s22*x2+c2*y1)>>16;

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

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

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

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

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

  s12=1<<16*Sin(rot_angle);
  s22=-s12;
  c2=1<<16*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)>>16;
  y=(s22*x2+c2*y1)>>16;

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

#help_index "Graphics/P3I4;Math/P3I4"
public F8 P3I4Dist(P3I4 *p1,P3I4 *p2)
{
  return Sqrt(SqrI8(p1->x-p2->x)+SqrI8(p1->y-p2->y)+SqrI8(p1->z-p2->z));
}

public F8 P3I4Norm(P3I4 *p)
{
  return Sqrt(SqrI8(p->x)+SqrI8(p->y)+SqrI8(p->z));
}

public I8 P3I4SqrNorm(P3I4 *p)
{
  return SqrI8(p->x)+SqrI8(p->y)+SqrI8(p->z);
}

#help_index "Graphics/Math"
public BoolI8 Bezier2(U0 *aux_data,P3I4 *ctrl,BoolI8 (*fp_plot)(U0 *aux,I8 x,I8 y,I8 z),BoolI1 first=TRUE)
{//2nd order
  I8 x,y,z,xx,yy,zz,dx,dy,dz,d_max;
  F8 x0=ctrl[0].x,y0=ctrl[0].y,z0=ctrl[0].z;
  F8 x1=ctrl[1].x-x0,y1=ctrl[1].y-y0,z1=ctrl[1].z-z0;
  F8 x2=ctrl[2].x-x0,y2=ctrl[2].y-y0,z2=ctrl[2].z-z0;
  F8 t,d=P3I4Dist(&ctrl[0],&ctrl[1])+
             P3I4Dist(&ctrl[1],&ctrl[2])+
             P3I4Dist(&ctrl[2],&ctrl[0]),
         s=0.5/d,t1,t2;
  xx=x0; yy=y0; zz=z0;
  if (first) {
    if (!fp_plot(aux_data,xx,yy,zz))
      return FALSE;
  }
  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;
      if (!fp_plot(aux_data,x,y,z))
        return FALSE;
      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) {
    if (!fp_plot(aux_data,x,y,z))
      return FALSE;
  }
  return TRUE;
}

public BoolI8 Bezier3(U0 *aux_data,P3I4 *ctrl,BoolI8 (*fp_plot)(U0 *aux,I8 x,I8 y,I8 z),BoolI1 first=TRUE)
{ //3rd order
  I8 x,y,z,xx,yy,zz,dx,dy,dz,d_max;
  F8 x0=ctrl[0].x,y0=ctrl[0].y,z0=ctrl[0].z;
  F8 x1=ctrl[1].x-x0,y1=ctrl[1].y-y0,z1=ctrl[1].z-z0;
  F8 x2=ctrl[2].x-x0,y2=ctrl[2].y-y0,z2=ctrl[2].z-z0;
  F8 x3=ctrl[3].x-x0,y3=ctrl[3].y-y0,z3=ctrl[3].z-z0;
  F8 t,d=P3I4Dist(&ctrl[0],&ctrl[1])+
             P3I4Dist(&ctrl[1],&ctrl[2])+
             P3I4Dist(&ctrl[2],&ctrl[3])+
             P3I4Dist(&ctrl[3],&ctrl[0]),
         s=0.5/d,nt,t1,t2,t3;
  xx=x0; yy=y0; zz=z0;
  if (first) {
    if (!fp_plot(aux_data,xx,yy,zz))
      return FALSE;
  }
  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;
      if (!fp_plot(aux_data,x,y,z))
        return FALSE;
      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) {
    if (!fp_plot(aux_data,x,y,z))
      return FALSE;
  }
  return TRUE;
}

public BoolI8 BSpline2(U0 *aux_data,P3I4 *ctrl,I8 cnt,BoolI8 (*fp_plot)(U0 *aux,I8 x,I8 y,I8 z),BoolI1 closed=FALSE)
{ //2nd order
  I8 i,j;
  P3I4 *c;
  BoolI1 first=TRUE;
  if (cnt<3) return TRUE;
  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) {
    if (!Bezier2(aux_data,&c[i],fp_plot,first)) {
      Free(c);
      return FALSE;
    }
    first=FALSE;
  }
  Free(c);
  return TRUE;
}

public BoolI8 BSpline3(U0 *aux_data,P3I4 *ctrl,I8 cnt,BoolI8 (*fp_plot)(U0 *aux,I8 x,I8 y,I8 z),BoolI1 closed=FALSE)
{ //3rd order
  I8 i,j;
  F8 x,y,z;
  P3I4 *c;
  BoolI1 first=TRUE;
  if (cnt<3) return TRUE;
  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) {
    if (!Bezier3(aux_data,&c[i],fp_plot,first)) {
      Free(c);
      return FALSE;
    }
    first=FALSE;
  }
  Free(c);
  return TRUE;
}

#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;

  while (TRUE) {
    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;
    }
  }
}

#help_index "Graphics"
Historical capture: 2010-01-31. Home | Power on LoseThos | Sources and changes