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_task->win_pixel_left>*left)
      *left=base->win_task->win_pixel_left;
    if (base->win_task->win_pixel_top>*top)
      *top=base->win_task->win_pixel_top;
    if (base->win_task->win_pixel_right<*right)
      *right=base->win_task->win_pixel_right;
    if (base->win_task->win_pixel_bottom<*bottom)
      *bottom=base->win_task->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_task->win_pixel_left;
      *y1+=base->win_task->win_pixel_top;
      *x2+=base->win_task->win_pixel_left;
      *y2+=base->win_task->win_pixel_top;
    }
    return ClipLine(x1,y1,x2,y2,left,top,right,bottom);
  } else
    return FALSE;
}

public BoolI8 GrPlot(GrBitMap *base,I8 x,I8 y)
{  //Clipping but No transformation or pen width
  I4 *db=base->depth_buffer;
  base->depth_buffer=NULL;
  if (base->brush)
    GrBlot(base,x,y,base->brush);
  else if (base->flags & BMF_SCREEN_BITMAP) {
    if (x>=0 && y>=0) {
      x+=base->win_task->win_pixel_left;
      y+=base->win_task->win_pixel_top;
      if (x<=base->win_task->win_pixel_right &&
          y<=base->win_task->win_pixel_bottom &&
          0<=x<base->width && 0<=y<base->height &&
          (base->win_task->next_task==sys_winmgr_task ||
           base->flags&BMF_ON_TOP ||
           !IsPixelCovered(base->win_task,x,y)))
        GrPlot0(base,x,y);
    }
  } else
    if (0<=x<base->width && 0<=y<base->height)
      GrPlot0(base,x,y);
  base->depth_buffer=db;
  return TRUE;
}

BoolI8 GrPlot1(GrBitMap *base,I8 x,I8 y)
{  //Clipping but No transformation or pen width, called with db_z set
  if (base->brush) {
    if (base->depth_buffer)
      GrBlot3(base,x,y,base->db_z,base->brush);
    else
      GrBlot(base,x,y,base->brush);
  } else if (base->flags & BMF_SCREEN_BITMAP) {
    if (x>=0 && y>=0) {
      x+=base->win_task->win_pixel_left;
      y+=base->win_task->win_pixel_top;
      if (x<=base->win_task->win_pixel_right &&
          y<=base->win_task->win_pixel_bottom &&
          0<=x<base->width && 0<=y<base->height &&
          (base->win_task->next_task==sys_winmgr_task ||
           base->flags&BMF_ON_TOP ||
           !IsPixelCovered(base->win_task,x,y)))
        GrPlot0(base,x,y);
    }
  } else
    if (0<=x<base->width && 0<=y<base->height)
      GrPlot0(base,x,y);
  return TRUE;
}

public I8 GrPeek(GrBitMap *base,I8 x,I8 y)
//Returns pixel color or -1 if off-screen or covered.
{  //Clipping but No transformation
  if (base->flags & BMF_SCREEN_BITMAP) {
    if (x<0 || y<0) return -1;
    x+=base->win_task->win_pixel_left;
    y+=base->win_task->win_pixel_top;
    if (x<0 || y<0 ||
        x>base->win_task->win_pixel_right ||
        y>base->win_task->win_pixel_bottom ||
        x>=base->width || y>=base->height ||
        base->win_task->next_task!=sys_winmgr_task &&
        !(base->flags&BMF_ON_TOP) &&
        IsPixelCovered(base->win_task,x,y))
      return -1;
  } else
    if (!(0<=x<base->width) || !(0<=y<base->height))
    return -1;
  return GrPeek0(base,x,y);
}

/*

This is an easier to understand
version of the nonrecursive routine below.
I8 GrFloodFillRay(GrBitMap *base,I8 x,I8 y,I8 z,I4 *db)
{
  I8 cnt,j,x1,ray_len,ray_len2;

  if (GetStkPtr-Fs->stack_base<0x80)
    StkOverflow(Fs);

  cnt=ray_len=GrRayLen(base,&x,y,z,db);
  y--;
  j=ray_len;
  x1=x;
  while (j>0) {
    if (ray_len2=GrRayLenMinus(base,x1,y))
      cnt+=GrFloodFillRay(base,x1,y,z,db);
    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,z,db);
    j-=ray_len2+1;
    x1-=ray_len2+1;
  }
  return cnt;
}
*/

class FFRay
{
  I8 state,x,y,j,x1,ray_len,ray_len2;
};

I8 GrFloodFillRay(GrBitMap *base,I8 x,I8 y,I8 z,I4 *db)
{//See the above commented-out routine for an easier to understand version.
  I8 cnt=0;
//We don't dynamically calculate the size to avoid 
//fragmentation of memory.
  FFRay *f_base=MAlloc(sizeof(FFRay)*0x80000),*f=f_base;
  f->x=x;
  f->y=y;
  f->state=0;
  do {
    nobound_switch (f->state) {
      case 0:
        f->state++;
        cnt+=f->ray_len=GrRayLen(base,&f->x,f->y,z,db);
        f->y--;
        f->j=f->ray_len;
        f->x1=f->x;
        break;
      case 1:
        if (f->j>0) {
          f->state++;
          if (f->ray_len2=GrRayLenMinus(base,f->x1,f->y)) {
            f[1].x=f->x1;
            f[1].y=f->y;
            f[1].state=0;
            f++;
          }
        } else
          f->state+=2;
        break;
      case 2:
        f->state--;
        f->j-=f->ray_len2+1;
        f->x1-=f->ray_len2+1;
        break;
      case 3:
        f->state++;
        f->y+=2;
        f->j=f->ray_len;
        f->x1=f->x;
        break;
      case 4:
        if (f->j>0) {
          f->state++;
          if (f->ray_len2=GrRayLenMinus(base,f->x1,f->y)) {
            f[1].x=f->x1;
            f[1].y=f->y;
            f[1].state=0;
            f++;
          }
        } else
          f->state+=2;
        break;
      case 5:
        f->state--;
        f->j-=f->ray_len2+1;
        f->x1-=f->ray_len2+1;
        break;
      case 6:
        f--;
        break;
    }
  } while (f>=f_base);
  Free(f_base);
  return cnt;
}


public I8 GrFloodFill(GrBitMap *base,I8 x,I8 y,BoolI1 not_color=FALSE,I8 z=0,I4 *db=NULL)
{
//Ignore z and db.
//not_color=TRUE means fill up to everything which is not the current color.
//not_color=FALSE means fill all parts equal to the color under the point.
  I8 cnt=0,j,c,c2,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) {
    nobound_switch (base->type) {
      case BMT_COLOR4:
        if (not_color) {
          base->color2=base->color&0xFFFFFF;
          base->flags|=BMF_FILL_NOT_COLOR;
        } else {
          base->color2=j;
          if (base->color&ROP_DITHER) {
            if (base->color2&0xFFFFFF==base->color&0xFFFFFF &&
                base->color&0xFFFFFF==base->color>>32&0xFFFFFF)
              break;
          } else if (base->color2&0xFFFFFF==base->color&0xFFFFFF)
            break;
          base->flags&=~BMF_FILL_NOT_COLOR;
        }
        if (not_color && j!=base->color2 ||
           !not_color)
          cnt=GrFloodFillRay(base,x,y,z,db);
        break;
      case BMT_MONO:
        c=ToBool(base->color&0xFFFFFF);
        c2=ToBool(base->color>>32&0xFFFFFF);
        if (not_color) {
          base->color2=c;
          base->flags|=BMF_FILL_NOT_COLOR;
        } else {
          base->color2=j;
          if (base->color&ROP_DITHER) {
            if (base->color2==c && c==c2)
              break;
          } else if (base->color2==c)
            break;
          base->flags&=~BMF_FILL_NOT_COLOR;
        }
        if (not_color && j!=c ||
           !not_color)
          cnt=GrFloodFillRay(base,x,y,z,db);
        break;
    }
  }
  base->brush=old_brush;
  base->flags=old_flags|base->flags&BMF_DIRTY;
  base->color2=old_color2;
  return cnt;
}

public BoolI8 GrPlot3b(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) {
    base->transform(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;
    base->db_z=_z;
    GrPlot1(base,_x,_y);
    was_symmetry=TRUE;
    if (base->flags&BMF_JUST_MIRROR)
      goto gr_done;
  }
  base->db_z=z;
  GrPlot1(base,x,y);
gr_done:
  if (was_transform)
    base->flags|=BMF_TRANSFORMATION;
  if (was_symmetry)
    base->flags|=BMF_SYMMETRY;
  return TRUE;
}

public BoolI8 GrPlot3(GrBitMap *base,I8 x,I8 y,I8 z)
{  //clipping and transformation and pen width
 
  I8 _x,_y,_z,i,j,w,old_color=base->color,dist;
  BoolI1 record,was_transform=FALSE,was_symmetry=FALSE;
  if (base->flags & BMF_TRANSFORMATION) {
    base->transform(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 gr_done;
  }
  w=base->pen_width>>1;
  base->db_z=z;
  if (w<=0)
    GrPlot1(base,x,y);
  else if (base->pen_width<GR_NUM_PEN_BRUSHES) {
    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 (base->depth_buffer) {
      if (base->color&ROP_DITHER) {
        base->color.u1[7]=base->color.u1[3];
        if (((x-w)^(y-w))&1) {
          record=GrBlot3(base,x-w,y-w,z,gr_odd_pen_brushes[base->pen_width]);
          base->color.u4[0]=base->color.u4[1];
          record=GrBlot3(base,x-w,y-w,z,gr_even_pen_brushes[base->pen_width]);
        } else {
          record=GrBlot3(base,x-w,y-w,z,gr_even_pen_brushes[base->pen_width]);
          base->color.u4[0]=base->color.u4[1];
          record=GrBlot3(base,x-w,y-w,z,gr_odd_pen_brushes[base->pen_width]);
        }
      } else
        record=GrBlot3(base,x-w,y-w,z,gr_pen_brushes[base->pen_width]);
    } else {
      if (base->color&ROP_DITHER) {
        base->color.u1[7]=base->color.u1[3];
        if (((x-w)^(y-w))&1) {
          record=GrBlot(base,x-w,y-w,gr_odd_pen_brushes[base->pen_width]);
          base->color.u4[0]=base->color.u4[1];
          record=GrBlot(base,x-w,y-w,gr_even_pen_brushes[base->pen_width]);
        } else {
          record=GrBlot(base,x-w,y-w,gr_even_pen_brushes[base->pen_width]);
          base->color.u4[0]=base->color.u4[1];
          record=GrBlot(base,x-w,y-w,gr_odd_pen_brushes[base->pen_width]);
        }
      } else
        record=GrBlot(base,x-w,y-w,gr_pen_brushes[base->pen_width]);
    }
    if (record) {
      if (base->flags & BMF_SCREEN_BITMAP) {
        x+=base->win_task->win_pixel_left;
        y+=base->win_task->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;
        }
      }
    }
  } else if (base->pen_width & 1) { //TODO Dither
    for (i=-w;i<=w;i++)
      for (j=-w;j<=w;j++)
        GrPlot1(base,x+i,y+j);
  } else {
    for (i=-w;i<w;i++)
      for (j=-w;j<w;j++)
        GrPlot1(base,x+i,y+j);
  }
gr_done:
  base->color=old_color;
  if (was_transform)
    base->flags|=BMF_TRANSFORMATION;
  if (was_symmetry)
    base->flags|=BMF_SYMMETRY;
  return TRUE;
}

BoolI8 GrLinePlot0(GrBitMap *base,I8 x,I8 y,I8 z)
{
  if (base->win_task->next_task==sys_winmgr_task ||
      !(base->flags & BMF_SCREEN_BITMAP) ||
      base->flags&BMF_ON_TOP ||
      !IsPixelCovered(base->win_task,x,y)) {
    base->db_z=z;
    GrPlot0(base,x,y);
  }
  return TRUE;
}

BoolI8 GrLinePlot(GrBitMap *base,I8 x,I8 y,I8 z)
{
  base->db_z=z;
  GrPlot1(base,x,y);
  return TRUE;
}

public U0 GrLine(GrBitMap *base,I8 x1,I8 y1,I8 x2,I8 y2,I8 step=1,I8 start=0)
{
  I4 *db=base->depth_buffer;
  base->depth_buffer=NULL;
  if (step==1 && !start && !base->brush && !base->depth_buffer) {
    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);
  base->depth_buffer=db;
}

public U0 GrCircle(GrBitMap *base,I8 cx,I8 cy,I8 radius,
  I8 step=1,F8 start_radians=0,F8 len_radians=2*pi)
{
  I4 *db=base->depth_buffer;
  base->depth_buffer=NULL;
  Circle(base,cx,cy,0,radius,&GrLinePlot,step,start_radians,len_radians);
  base->depth_buffer=db;
}

public U0 GrEllipse(GrBitMap *base,
                I8 cx,I8 cy,
                I8 x_radius,I8 y_radius,
                F8 rot_angle=0,
                I8 step=1,
                F8 start_radians=0,
                F8 len_radians=2*pi)
{
  I4 *db=base->depth_buffer;
  base->depth_buffer=NULL;
  Ellipse(base,cx,cy,0,x_radius,y_radius,&GrLinePlot,rot_angle,step,start_radians,len_radians);
  base->depth_buffer=db;
}

public U0 GrRegPolygon(GrBitMap *base,
                I8 cx,I8 cy,
                I8 x_radius,I8 y_radius,I8 sides,
                F8 rot_angle=0,
                I8 step=1,
                F8 start_radians=0,
                F8 len_radians=2*pi)
{
  I4 *db=base->depth_buffer;
  base->depth_buffer=NULL;
  RegPolygon(base,cx,cy,0,x_radius,y_radius,sides,
    &GrLinePlot,rot_angle,step,start_radians,len_radians);
  base->depth_buffer=db;
}

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

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

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

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

public U0 GrLine3b(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) {
    base->transform(base,&x1,&y1,&z1);
    base->transform(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 && !base->depth_buffer) { //TODO: clip z for depth buffer
      if (GrClipLine(base,&_x1,&_y1,&_x2,&_y2))
        Line(base,_x1,_y1,0,_x2,_y2,0,&GrLinePlot0,step,start);
    } else
      Line(base,_x1,_y1,_z1,_x2,_y2,_z2,&GrLinePlot,step,start);
    was_symmetry=TRUE;
    if (base->flags&BMF_JUST_MIRROR)
      goto gr_done;
  }
  if (step==1 && !start && !base->brush && !base->depth_buffer) { //TODO: clip z for depth buffer
    if (GrClipLine(base,&x1,&y1,&x2,&y2))
      Line(base,x1,y1,0,x2,y2,0,&GrLinePlot0,step,start);
  } else
    Line(base,x1,y1,z1,x2,y2,z2,&GrLinePlot,step,start);
gr_done:
  if (was_transform)
    base->flags|=BMF_TRANSFORMATION;
  if (was_symmetry)
    base->flags|=BMF_SYMMETRY;
}

public U0 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) {
    base->transform(base,&x1,&y1,&z1);
    base->transform(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,_z1,_x2,_y2,_z2,&GrPlot3,step,start);
    was_symmetry=TRUE;
    if (base->flags&BMF_JUST_MIRROR)
      goto gr_done;
  }
  Line(base,x1,y1,z1,x2,y2,z2,&GrPlot3,step,start);
gr_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,U1 *s)
{  //transformation
//BMF_SYMMETRY is silly.
  if (base->flags & BMF_TRANSFORMATION)
    base->transform(base,&x1,&y1,&z1);
  return GrPutS(base,x1,y1,s);
}

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

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

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


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

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

    x_radius<<=16;
    y_radius<<=16;

    xx=0;
    yy=0;
    zz=0;
    base->transform(base,&xx,&yy,&zz);

    x=x_radius*c;
    y=x_radius*s;
    z=0;
    base->transform(base,&x,&y,&z);
    x-=xx;
    y-=yy;
    z-=zz;
    R2P(&m1,&a1,x,y);

    x=-y_radius*s;
    y=y_radius*c;
    z=0;
    base->transform(base,&x,&y,&z);
    x-=xx;
    y-=yy;
    z-=zz;
    R2P(&m2,&a2,x,y);
    m2*=Abs(Sin(a2-a1));

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

public U0 GrCircle3(GrBitMap *base,I8 cx,I8 cy,I8 cz,I8 radius,
  I8 step=1,F8 start_radians=0,F8 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,&GrPlot3,step,start_radians,len_radians);
}

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

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

    x_radius<<=16;
    y_radius<<=16;

    xx=0;
    yy=0;
    zz=0;
    base->transform(base,&xx,&yy,&zz);

    x=x_radius*c;
    y=x_radius*s;
    z=0;
    base->transform(base,&x,&y,&z);
    x-=xx;
    y-=yy;
    z-=zz;
    R2P(&m1,&a1,x,y);

    x=-y_radius*s;
    y=y_radius*c;
    z=0;
    base->transform(base,&x,&y,&z);
    x-=xx;
    y-=yy;
    z-=zz;
    R2P(&m2,&a2,x,y);
    m2*=Abs(Sin(a2-a1));

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

public I8 GrFloodFill3(GrBitMap *base,I8 x1,I8 y1,I8 z1,BoolI1 not_color=FALSE)
{  //transformation
//not_color=TRUE means fill up to everything which is not the current color.
//not_color=FALSE 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) {
    base->transform(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,_z,base->depth_buffer);
    if (base->flags&BMF_JUST_MIRROR)
      goto gr_done;
  }
  cnt=GrFloodFill(base,x1,y1,not_color,z1,base->depth_buffer);
gr_done:
  base->flags=old_flags|base->flags&BMF_DIRTY;
  return cnt;
}

public U0 GrRect3(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;
    base->transform(base,&x,&y,&z);
    base->transform(base,&x2,&y2,&z2);
    base->transform(base,&x3,&y3,&z3);
    if (!(base->flags&BMF_JUST_MIRROR) ||
        !(base->flags&BMF_SYMMETRY))
      Rect(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);
      Rect(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)) {
      if (base->depth_buffer)
        Rect(base,x,y,z,x+w,y,z,x,y+h,z,&GrLinePlot);
      else
        GrRect(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);
      Rect(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,result=FALSE;
  GrBitMap *old_brush=base->brush;

  if (base->depth_buffer || 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) {
      base->transform(base,&x1,&y1,&z1);
      base->transform(base,&x2,&y2,&z2);
      base->transform(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)) {
              if (img->type==BMT_MONO)
                base->color=old_color;
              else
                base->color=old_color&0xFF000000+color;
              if (base->depth_buffer) {
                base->db_z=_z2.i4[1];
                GrPlot1(base,_x2.i4[1],_y2.i4[1]);
              } else
                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;
      }
      result=TRUE;
normal_image:
      if (base->flags&BMF_JUST_MIRROR)
        goto gr_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
      goto gr_done;
    if (d2) {
      dx2=dx2<<32/d2;
      dy2=dy2<<32/d2;
      dz2=dz2<<32/d2;
    } else
      goto gr_done;
    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)) {
            if (img->type==BMT_MONO)
              base->color=old_color;
            else
              base->color=old_color&0xFF000000+color;
            if (base->depth_buffer) {
              base->db_z=z2.i4[1];
              GrPlot1(base,x2.i4[1],y2.i4[1]);
            } else
              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;
    }
    result=TRUE;  //TODO: check off screen
  } else
    result=GrBlot(base,x1,y1,img);
gr_done:
  base->color=old_color;
  base->brush=old_brush;
  return result;
}


public U0 Gr2Bezier3(GrBitMap *base,P3I4 *ctrl)
{
  U8 old_flags=base->flags;
  I8 i,x,y,z;
  P3I4 *ctrl2=NULL,*ctrl3=NULL;
  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;
      base->transform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
    }
    base->flags&=~BMF_TRANSFORMATION;
    ctrl=ctrl2;
  }
  if (base->flags & BMF_SYMMETRY) {
    ctrl3=MAlloc(sizeof(P3I4)*3);
    for (i=0;i<3;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrReflect(base,&x,&y,&z);
      ctrl3[i].x=x;
      ctrl3[i].y=y;
      ctrl3[i].z=z;
    }
    base->flags&=~BMF_SYMMETRY;
    Bezier2(base,ctrl3,&GrPlot3);
    if (base->flags & BMF_JUST_MIRROR)
      goto gr_done;
  }

  Bezier2(base,ctrl,&GrPlot3);
gr_done:
  Free(ctrl2);
  Free(ctrl3);
  base->flags=old_flags|base->flags&BMF_DIRTY;
}

public U0 Gr3Bezier3(GrBitMap *base,P3I4 *ctrl)
{
  U8 old_flags=base->flags;
  I8 i,x,y,z;
  P3I4 *ctrl2=NULL,*ctrl3=NULL;
  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;
      base->transform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
    }
    base->flags&=~BMF_TRANSFORMATION;
    ctrl=ctrl2;
  }
  if (base->flags & BMF_SYMMETRY) {
    ctrl3=MAlloc(sizeof(P3I4)*4);
    for (i=0;i<4;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrReflect(base,&x,&y,&z);
      ctrl3[i].x=x;
      ctrl3[i].y=y;
      ctrl3[i].z=z;
    }
    base->flags&=~BMF_SYMMETRY;
    Bezier3(base,ctrl3,&GrPlot3);
    if (base->flags & BMF_JUST_MIRROR)
      goto gr_done;
  }

  Bezier3(base,ctrl,&GrPlot3);
gr_done:
  Free(ctrl2);
  Free(ctrl3);
  base->flags=old_flags|base->flags&BMF_DIRTY;
}

public U0 Gr2BSpline3(GrBitMap *base,P3I4 *ctrl,I8 cnt,BoolI1 closed=FALSE)
{
  U8 old_flags=base->flags;
  I8 i,x,y,z;
  P3I4 *ctrl2=NULL,*ctrl3=NULL;
  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;
      base->transform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
    }
    base->flags&=~BMF_TRANSFORMATION;
    ctrl=ctrl2;
  }
  if (base->flags & BMF_SYMMETRY) {
    ctrl3=MAlloc(sizeof(P3I4)*cnt);
    for (i=0;i<cnt;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrReflect(base,&x,&y,&z);
      ctrl3[i].x=x;
      ctrl3[i].y=y;
      ctrl3[i].z=z;
    }
    base->flags&=~BMF_SYMMETRY;
    BSpline2(base,ctrl3,cnt,&GrPlot3,closed);
    if (base->flags & BMF_JUST_MIRROR)
      goto gr_done;
  }

  BSpline2(base,ctrl,cnt,&GrPlot3,closed);
gr_done:
  Free(ctrl2);
  Free(ctrl3);
  base->flags=old_flags|base->flags&BMF_DIRTY;
}

public U0 Gr3BSpline3(GrBitMap *base,P3I4 *ctrl,I8 cnt,BoolI1 closed=FALSE)
{
  U8 old_flags=base->flags;
  I8 i,x,y,z;
  P3I4 *ctrl2=NULL,*ctrl3=NULL;
  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;
      base->transform(base,&x,&y,&z);
      ctrl2[i].x=x;
      ctrl2[i].y=y;
      ctrl2[i].z=z;
    }
    base->flags&=~BMF_TRANSFORMATION;
    ctrl=ctrl2;
  }
  if (base->flags & BMF_SYMMETRY) {
    ctrl3=MAlloc(sizeof(P3I4)*cnt);
    for (i=0;i<cnt;i++) {
      x=ctrl[i].x;
      y=ctrl[i].y;
      z=ctrl[i].z;
      GrReflect(base,&x,&y,&z);
      ctrl3[i].x=x;
      ctrl3[i].y=y;
      ctrl3[i].z=z;
    }
    base->flags&=~BMF_SYMMETRY;
    BSpline3(base,ctrl3,cnt,&GrPlot3,closed);
    if (base->flags & BMF_JUST_MIRROR)
      goto gr_done;
  }

  BSpline3(base,ctrl,cnt,&GrPlot3,closed);
gr_done:
  Free(ctrl2);
  Free(ctrl3);
  base->flags=old_flags|base->flags&BMF_DIRTY;
}

public U0 GrSpeedLine(GrBitMap *base,I8 x1,I8 y1,I8 x2,I8 y2,F8 speed)
{
  I8 old_pen_width=base->pen_width;
  I4 *db=base->depth_buffer;
  U8 old_flags=base->flags;
  base->depth_buffer=NULL;
  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->flags&BMF_DIRTY;
  base->pen_width=old_pen_width;
  base->depth_buffer=db;
}

public U0 GrSpeedLine3(GrBitMap *base,I8 x1,I8 y1,I8 z1,I8 x2,I8 y2,I8 z2,F8 speed)
{  //transformation
  U8 old_flags=base->flags;
  I8 _x1,_y1,_z1,_x2,_y2,_z2;
  I8 old_pen_width=base->pen_width;
  base->pen_width=base->speedline_scale*speed;
  if (base->flags & BMF_TRANSFORMATION) {
    base->transform(base,&x1,&y1,&z1);
    base->transform(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;

    GrLine3(base,_x1,_y1,_z1,_x2,_y2,_z2);

    if (base->flags & BMF_JUST_MIRROR)
      goto gr_done;
  }

  GrLine3(base,x1,y1,z1,x2,y2,z2);

gr_done:
  base->pen_width=old_pen_width;
  base->flags=old_flags|base->flags&BMF_DIRTY;
}

public I8 GrFillTriangle0(GrBitMap *base,P3I4 *p1,P3I4 *p2,P3I4 *p3)
{
  I8 x1,x2,y,z1,z2,dx1,dy1,dz1,dx2,dy2,dz2,cnt=0;
  //p1 is min x
  if (p3->x<p2->x)
    SwapU8(&p3,&p2);
  if (p2->x<p1->x)
    SwapU8(&p2,&p1);

  //p2 is min y of remainder
  if (p3->y<p2->y)
    SwapU8(&p3,&p2);

  if ((dy2=p3->y-p1->y)<0) {
    if (dy1=p2->y-p1->y) {
      dx1=(p1->x-p2->x)<<32/dy1;
      dz1=(p1->z-p2->z)<<32/dy1;
    } else {
      dx1=0;
      dz1=0;
    }
    dx2=(p1->x-p3->x)<<32/dy2;
    dz2=(p1->z-p3->z)<<32/dy2;
    x1=x2=p1->x<<32; y=p1->y; z1=z2=p1->z<<32;
    while (dy2++) {
      cnt+=GrHLine(base,x1.i4[1],x2.i4[1],y,z1.i4[1],z2.i4[1]);
      y--;
      x1+=dx1;
      x2+=dx2;
      z1+=dz1;
      z2+=dz2;
    }
    if (dy2=p2->y-p3->y) {
      dx2=(p3->x-p2->x)<<32/dy2;
      dz2=(p3->z-p2->z)<<32/dy2;
      while (dy2++) {
        cnt+=GrHLine(base,x1.i4[1],x2.i4[1],y,z1.i4[1],z2.i4[1]);
        y--;
        x1+=dx1;
        x2+=dx2;
        z1+=dz1;
        z2+=dz2;
      }
    }
  } else if ((dy2=p2->y-p1->y)>0) {
    if (dy1=p3->y-p1->y) {
      dx1=(p3->x-p1->x)<<32/dy1;
      dz1=(p3->z-p1->z)<<32/dy1;
    } else {
      dx1=0;
      dz1=0;
    }
    dx2=(p2->x-p1->x)<<32/dy2;
    dz2=(p2->z-p1->z)<<32/dy2;
    x1=x2=p1->x<<32; y=p1->y; z1=z2=p1->z<<32;
    while (dy2--) {
      cnt+=GrHLine(base,x1.i4[1],x2.i4[1],y,z1.i4[1],z2.i4[1]);
      y++;
      x1+=dx1;
      x2+=dx2;
      z1+=dz1;
      z2+=dz2;
    }
    if (dy2=p3->y-p2->y) {
      dx2=(p3->x-p2->x)<<32/dy2;
      dz2=(p3->z-p2->z)<<32/dy2;
      while (dy2--) {
        cnt+=GrHLine(base,x1.i4[1],x2.i4[1],y,z1.i4[1],z2.i4[1]);
        y++;
        x1+=dx1;
        x2+=dx2;
        z1+=dz1;
        z2+=dz2;
      }
    }
  } else {
    if (dy1=p2->y-p1->y) {
      dx1=(p2->x-p1->x)<<32/dy1;
      dz1=(p2->z-p1->z)<<32/dy1;
      if (dy2=p2->y-p3->y) {
        dx2=(p2->x-p3->x)<<32/dy2;
        dz2=(p2->z-p3->z)<<32/dy2;
      } else {
        dx2=0;
        dz2=0;
      }
      x1=x2=p2->x<<32; y=p2->y; z1=z2=p2->z<<32;
      while (dy1++<=0) {
        cnt+=GrHLine(base,x1.i4[1],x2.i4[1],y,z1.i4[1],z2.i4[1]);
        y++;
        x1+=dx1;
        x2+=dx2;
        z1+=dz1;
        z2+=dz2;
      }
    }
    if (dy1=p3->y-p1->y) {
      dx1=(p1->x-p3->x)<<32/dy1;
      dz1=(p1->z-p3->z)<<32/dy1;
      if (dy2=p3->y-p2->y) {
        dx2=(p2->x-p3->x)<<32/dy2;
        dz2=(p2->z-p3->z)<<32/dy2;
      } else {
        dx2=0;
        dz2=0;
      }
      x1=x2=p3->x<<32; y=p3->y; z1=z2=p3->z<<32;
      while (dy1--) {
        cnt+=GrHLine(base,x1.i4[1],x2.i4[1],y,z1.i4[1],z2.i4[1]);
        y--;
        x1+=dx1;
        x2+=dx2;
        z1+=dz1;
        z2+=dz2;
      }
    }
  }
  return cnt;
}

public I8 GrFillPolygon3(GrBitMap *base,I8 n,P3I4 *poly)
{ //Must be convex.
  P3I4 tri[3];
  I8 i,j,x0,y0,z0,x1,y1,z1,x2,y2,z2,cnt=0;
  if (n<3) return 0;
  if (base->flags & BMF_SYMMETRY) {
    for (i=1;i<n-1;i++) {
      j=i-1;
      if (i==1) {
        x0=poly[j].x; y0=poly[j].y; z0=poly[j].z;
        if (base->flags&BMF_TRANSFORMATION)
          base->transform(base,&x0,&y0,&z0);
        GrReflect(base,&x0,&y0,&z0);
        tri[0].x=x0; tri[0].y=y0; tri[0].z=z0;
      }

      j++;
      if (i==1) {
        x1=poly[j].x; y1=poly[j].y; z1=poly[j].z;
        if (base->flags&BMF_TRANSFORMATION)
          base->transform(base,&x1,&y1,&z1);
        GrReflect(base,&x1,&y1,&z1);
      } else {
        x1=x2; y1=y2; z1=z2;
      }
      tri[1].x=x1; tri[1].y=y1; tri[1].z=z1;

      j++;
      x2=poly[j].x; y2=poly[j].y; z2=poly[j].z;
      if (base->flags&BMF_TRANSFORMATION)
        base->transform(base,&x2,&y2,&z2);
      GrReflect(base,&x2,&y2,&z2);
      tri[2].x=x2; tri[2].y=y2; tri[2].z=z2;

      cnt+=GrFillTriangle0(base,&tri[0],&tri[1],&tri[2]);
    }
  }
  if (base->flags&BMF_JUST_MIRROR)
    return cnt;
  for (i=1;i<n-1;i++) {
    j=i-1;
    if (i==1) {
      x0=poly[j].x; y0=poly[j].y; z0=poly[j].z;
      if (base->flags&BMF_TRANSFORMATION)
        base->transform(base,&x0,&y0,&z0);
      tri[0].x=x0; tri[0].y=y0; tri[0].z=z0;
    }

    j++;
    if (i==1) {
      x1=poly[j].x; y1=poly[j].y; z1=poly[j].z;
      if (base->flags&BMF_TRANSFORMATION)
        base->transform(base,&x1,&y1,&z1);
    } else {
      x1=x2; y1=y2; z1=z2;
    }
    tri[1].x=x1; tri[1].y=y1; tri[1].z=z1;

    j++;
    x2=poly[j].x; y2=poly[j].y; z2=poly[j].z;
    if (base->flags&BMF_TRANSFORMATION)
      base->transform(base,&x2,&y2,&z2);
    tri[2].x=x2; tri[2].y=y2; tri[2].z=z2;

    cnt+=GrFillTriangle0(base,&tri[0],&tri[1],&tri[2]);
  }
  return cnt;
}

#help_index "Graphics/Mesh"

public I8 Gr3Mesh(GrBitMap *base,I8 vertex_cnt,P3I4 *p,I8 triangle_cnt,GrMeshTri *tri)
{
  I8 i,x,y,z,old_color=base->color,cnt=0;
  P3I4 *pt,*pt_sym,*p_sym,*dst;
  GrMeshTri *tri_sym=tri;
  if (base->flags&BMF_TRANSFORMATION) {
    dst=pt=MAlloc(sizeof(P3I4)*vertex_cnt);
    for (i=0;i<vertex_cnt;i++,p++,dst++) {
      x=p->x; y=p->y; z=p->z;
      base->transform(base,&x,&y,&z);
      dst->x=x; dst->y=y; dst->z=z;
    }
    p=pt;
  } else
    pt=NULL;

  if (base->flags & BMF_SYMMETRY) {
    dst=pt_sym=MAlloc(sizeof(P3I4)*vertex_cnt);
    p_sym=p;
    for (i=0;i<vertex_cnt;i++,p_sym++,dst++) {
      x=p_sym->x; y=p_sym->y; z=p_sym->z;
      GrReflect(base,&x,&y,&z);
      dst->x=x; dst->y=y; dst->z=z;
    }
    p_sym=pt_sym;
    for (i=0;i<triangle_cnt;i++,tri_sym++) {
      base->lighting(base,&p_sym[tri_sym->nums[0]],&p_sym[tri_sym->nums[2]],&p_sym[tri_sym->nums[1]],tri_sym->color);
      cnt+=GrFillTriangle0(base,&p_sym[tri_sym->nums[0]],&p_sym[tri_sym->nums[2]],&p_sym[tri_sym->nums[1]]);
    }
    Free(pt_sym);
    if (base->flags&BMF_JUST_MIRROR)
      goto mesh_done;
  }
  for (i=0;i<triangle_cnt;i++,tri++) {
    base->lighting(base,&p[tri->nums[0]],&p[tri->nums[1]],&p[tri->nums[2]],tri->color);
    cnt+=GrFillTriangle0(base,&p[tri->nums[0]],&p[tri->nums[1]],&p[tri->nums[2]]);
  }
mesh_done:
  base->color=old_color;
  Free(pt);
  return cnt;
}

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