/************************************************************
**
** Application: ChartDraw
**
** Title:       c.draw
**
*************************************************************/

/* Include files */
/* from standard clib */
//#include <stdlib.h>
//#include <stdio.h>
#include <math.h>
#include <string.h>

/* from oslib */
#include "oslib/colourtrans.h"
#include "oslib/draw.h"
#include "oslib/drawfile.h"
#include "oslib/os.h"
#include "oslib/font.h"
#include "oslib/messagetrans.h"


/* from CJLib */
#include "etc.h"
#include "flex.h"
#include "alloc.h"
#include "message.h"

/* from application */
#include "draw.h"
#include "fonts.h"



#define CIRC_CONST (double)0.551805   /* for making bezier curves for circles */


/******************   structs   *****************************/

/************************************************************/

/* exported variables */
/* These buffers are used if the app is not doing its own memory management */
#ifdef NEVER
char   *DrawL_vdu_buffer = NULL;
int     DrawL_vdu_buffer_length = 0;
int     DrawL_vdu_buffer_chunk = 1024;
int     DrawL_vdu_buffer_increase = 512;
char   *DrawL_draw_buffer = NULL;
int     DrawL_draw_buffer_chunk = 1024;
#endif


DrawL_buffer_details DrawL_buffers = { NULL, 0, 1024, 256, NULL, 0, 2048, 512 };


/* Transformation matrices to perform pure rotation */
/* rotation x in deg */
/* a=cos x, b = sin x, c = -sin x, d = cos x, all times &10000 (16 bit fixed point) */
/* e and f are zero - no translation */
/* identity matrix */
////os_trfm trfmID = { 0x10000, 0, 0, 0x10000, 0, 0 };
os_trfm trfmID = { 65536, 0, 0, 65536, 0, 0 };
/* 30 deg anticlockwise */
os_trfm trfmrot30 = { 56756, 32768, -32768, 56756, 0, 0 };
/* 45 deg anticlockwise */
////os_trfm trfmrot45 = { 0xb504, 0xb504, 0xffff4afc, 0xb504, 0, 0 };
os_trfm trfmrot45 = { 46340, 46340, -46340, 46340, 0, 0 };
/* 60 deg anticlockwise */
os_trfm trfmrot60 = { 32768, 56756, -56756, 32768, 0, 0 };
/* 90 deg anticlockwise */
os_trfm trfmrot90 = { 0, 65536, -65536, 0, 0, 0 };
/* 90 deg clockwise */
os_trfm trfmrot270 = { 0, 65536, 65536, 0, 0, 0 };
/* 60 deg clockwise */
os_trfm trfmrot300 = { 32768, -56756, 56756, 32768, 0, 0 };
/* 45 deg clockwise */
os_trfm trfmrot315 = { 46340, -46340, 46340, 46340, 0, 0 };
/* 30 deg clockwise */
os_trfm trfmrot330 = { 56756, -32768, 32768, 56756, 0, 0 };

/* global variables */
static dash_def dash;
/* ptr to external mem alloc function */
static allocfn memalloc;


static drawfile_path_style pathstyle = { 2 };
static draw_line_style linestyle = { 2, 0, 0, 0, 0, 0, 0, 0, 0 };

//////draw_settings drawset;





/* forward function declarations */



/************************************************************/

static int path_object_wrapper_size ( draw_settings *dpars )

{
  int size;

  if (dpars->device == DEVICE_DRAWFILE)
  {
    size = 36;   /* size of basic header (no dash) + path close word */
    if (dpars->dash_pattern != NULL)
    {
      size += (( 2 + dpars->dash_pattern->element_count ) << 2);
    }
  }
  else
  {
    size = 4;
  }

  return (size);
}




/************************************************************/

/*
  Check the remaining buffer for sufficient space to accommodate the
  next object. If not enough then the current memory block is extended.
  If include_header is TRUE, then the length of the path object header,
  and path close word, is added to the memory needed before making the
  extend call.
*/


static osbool check_buffer_size ( draw_settings *dpars,
                                  int needed,
                                  osbool include_header )

{
  int new_size;

  needed += 4;
  if (include_header) needed += path_object_wrapper_size (dpars);

  if ((dpars->buf_offset + needed) >= dpars->buf_length)
  {
    if (memalloc)
    {
      /* App is doing its own memory allocation and management */
      return ( memalloc (dpars, needed) );
    }
    else
    {
      if (dpars->device == DEVICE_DRAWFILE)
      {
        /* extend by a block >= needed (in case a large block is needed */
        new_size = dpars->buf_length + MAX (DrawL_buffers.draw_extend, needed);
        if ( schunk ((flex_ptr)&DrawL_buffers.draw, new_size, DrawL_buffers.draw_chunk) )
        {
          dpars->buffer = &DrawL_buffers.draw;
          DrawL_buffers.draw_length = new_size;
          dpars->buf_length = new_size;
        }
        else
        {
          /* this is bad - cannot claim more memory */
          CJL_MsgWarnF ( "OutOfMem",
                              "Out of memory while making drawfile" );
          return FALSE;
        }
      }
      else
      {
        /* need to extend vdu buffer */
        new_size = DrawL_buffers.vdu_length + MAX (DrawL_buffers.vdu_extend, needed);
        if ( schunk ((flex_ptr)&DrawL_buffers.vdu, new_size, DrawL_buffers.vdu_chunk))
        {
          dpars->buffer = &DrawL_buffers.vdu;
          DrawL_buffers.vdu_length = new_size;
          dpars->buf_length = new_size;
        }
        else
        {
          /* this is bad - cannot claim more memory */
          CJL_MsgWarnF ( "OutOfMem",
                              "Out of memory while updating screen" );
          return FALSE;
        }
      }
    }
  }
  return TRUE;
}





/************************************************************/

/*
  Set up all the details for the start of a new path object. The
  remaining size in the buffer is checked only if check_buffer is TRUE.
  This prevent the check being done twice when the call is part of a
  self contained object for which the total buffer requirement has
  already been checked.
*/

void DrawL_BeginPathObject (draw_settings *dpars, osbool check_buffer)

{
  char *ptr;
  char *obj_startp;
  drawfile_object *draw_obj;
  drawfile_path_with_pattern *drawpathpatt;
  int i;
  drawfile_path_style pstyle;



  if (dpars->device == DEVICE_DRAWFILE)
  {
    if (check_buffer)
    {
      if (!check_buffer_size (dpars, path_object_wrapper_size (dpars), NO_HEADER))
        return;
    }
    ptr = *dpars->buffer + dpars->buf_offset;
    obj_startp = ptr;
    dpars->obj_start = ptr;

    draw_obj = (drawfile_object *)ptr;
    draw_obj->type = drawfile_TYPE_PATH;
    /* size to be filled in later */
    ptr += 8;
    drawpathpatt = (drawfile_path_with_pattern *)ptr;
    drawpathpatt->fill = dpars->fill_colour;
    drawpathpatt->outline = dpars->line_colour;
    drawpathpatt->width = dpars->line_width;

    /* we are assuming a basic line style of zero */
    if (dpars->dash_pattern == NULL)
    {
      drawpathpatt->style = pathstyle;
      ptr += 32;
    }
    else
    {
      pstyle = pathstyle;
      pstyle.flags = pstyle.flags | drawfile_PATH_DASHED;
      drawpathpatt->style = pstyle;
      drawpathpatt->pattern.start = 0;
      drawpathpatt->pattern.element_count = dpars->dash_pattern->element_count;
      for (i = 0; i < dpars->dash_pattern->element_count; i++)
      {
        drawpathpatt->pattern.elements[i] = dpars->dash_pattern->elements[i];
      }
      ptr += 32 + (( 2 + dpars->dash_pattern->element_count ) << 2);
    }
    dpars->buf_offset += ptr - obj_startp;

  }
  else
  {
    /* for vdu or printer restart obj from start of buffer */
    dpars->buf_offset = 0;
  }
  return;
}








/************************************************************/

void DrawL_EndPathObject (draw_settings *dpars, osbool check_buffer)

{
  drawfile_diagram *diag;
  drawfile_object *draw_obj;
  draw_path   *dpath;
  draw_output_path outpath;
  char *p, *p1;
  int element_count;
  int *ip;

  os_gcol gcol;
  int dummy;
  char *ptr;
  path_comp_no_arg *path;

  os_error *err;
  byte *end;

    if (check_buffer)
    {
      if (!check_buffer_size (dpars, 4, NO_HEADER))
        return;
    }
    /* Include path terminator */
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_comp_no_arg *)ptr;
    path->comp_id = SUBPATH_END;
    dpars->buf_offset += 4;
    ptr += 4;

    if (dpars->device == DEVICE_DRAWFILE)
    {
      draw_obj = (drawfile_object *)dpars->obj_start;
      /* fill in the size */
      draw_obj->size = ptr - dpars->obj_start;

      /* now find the bounding box */
      if (dpars->dash_pattern == NULL)
      {
        dpath = &draw_obj->data.path.path;
        p = (char *)(&draw_obj->data.path.bbox) + 0x80000000;
      }
      else
      {
        /* We cannot simply pickup the start of the actual draw path
        ** from the drawfile_path_with_pattern struct because the number
        ** of elements in the dash pattern at any time is unknown - thus
        ** we have to read the number of elements and use that value to
        ** find the start of the draw path */
        p1 = dpars->obj_start + 44;
        /* p1 should be pointing to element count in dash pattern */
        ip = (int *)p1;
        element_count = *ip;
        p1 += (element_count + 1)<<2;
        dpath = (draw_path *)p1;
////        dpath = &draw_obj->data.path_with_pattern.path;
        p = (char *)(&draw_obj->data.path_with_pattern.bbox) + 0x80000000;
      }
      outpath = (draw_output_path)p;
      err = xdraw_process_path ( dpath,
                          draw_FILL_NONZERO,
                          NULL,
                          0,
                          dpars->line_width,
                          dpars->line_style,
                          dpars->dash_pattern,
                          outpath,
                          &end);
      if (err) CJL_MsgDBF("Draw err %s", err->errmess);
      /* now update the diagram bbox */
      diag = (drawfile_diagram *)*dpars->buffer;
      if (diag->bbox.x0 > draw_obj->data.path.bbox.x0)
        diag->bbox.x0 = draw_obj->data.path.bbox.x0;
      if (diag->bbox.x1 < draw_obj->data.path.bbox.x1)
        diag->bbox.x1 = draw_obj->data.path.bbox.x1;
      if (diag->bbox.y0 > draw_obj->data.path.bbox.y0)
        diag->bbox.y0 = draw_obj->data.path.bbox.y0;
      if (diag->bbox.y1 < draw_obj->data.path.bbox.y1)
        diag->bbox.y1 = draw_obj->data.path.bbox.y1;

    }
    else
    {
      {
        /* is there a fill? */
        if (dpars->fill_colour != os_COLOUR_TRANSPARENT)
        {
          gcol = colourtrans_set_gcol ( dpars->fill_colour,
                                        colourtrans_SET_FG_GCOL,
                                        os_ACTION_OVERWRITE,
                                        &dummy );
          draw_fill ( (draw_path *)*dpars->buffer, draw_FILL_NONZERO, NULL, 0);
        }
        /* Is there a line colour? */
        if (dpars->line_colour != os_COLOUR_TRANSPARENT)
        {
          gcol = colourtrans_set_gcol ( dpars->line_colour,
                                        colourtrans_SET_FG_GCOL,
                                        os_ACTION_OVERWRITE,
                                        &dummy );

          draw_stroke ( (draw_path *)*dpars->buffer,
                        draw_FILL_NONZERO,
                        NULL,
                        0,
                        dpars->line_width,
                        dpars->line_style,
                        dpars->dash_pattern );
        }
      }
    }
  return;
}



////// | draw_FILL_INTERIOR_BOUNDARY | draw_FILL_EXTERIOR_BOUNDARY

/************************************************************/

#define ZERO_ARG_SUBPATH_LENGTH 4

void DrawL_ClosePath (draw_settings *dpars)

{
  char *ptr;
  path_comp_no_arg *path;

  if (check_buffer_size (dpars, ZERO_ARG_SUBPATH_LENGTH, NO_HEADER))
  {
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_comp_no_arg *)ptr;
    path->comp_id = SUBPATH_CLOSE;
    dpars->buf_offset += ZERO_ARG_SUBPATH_LENGTH;
  }
  return;
}






/************************************************************/

#define ONE_ARG_SUBPATH_LENGTH 12

void DrawL_Move (draw_settings *dpars, int x, int y)

{
  char *ptr;
  subpath_onearg *path;

  if (check_buffer_size (dpars, ONE_ARG_SUBPATH_LENGTH, NO_HEADER))
  {
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (subpath_onearg *)ptr;
    path->id = SUBPATH_MOVE;
    path->x = x;
    path->y = y;
    dpars->buf_offset += ONE_ARG_SUBPATH_LENGTH;
  }
  return;
}




/************************************************************/

void DrawL_Draw (draw_settings *dpars, int x, int y)

{
  char *ptr;
  subpath_onearg *path;

  if (check_buffer_size (dpars, ONE_ARG_SUBPATH_LENGTH, NO_HEADER))
  {
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (subpath_onearg *)ptr;
    path->id = SUBPATH_DRAW;
    path->x = x;
    path->y = y;
    dpars->buf_offset += ONE_ARG_SUBPATH_LENGTH;
  }
  return;
}




/************************************************************/

#define TWO_ARG_SUBPATH_LENGTH 24
void DrawL_Line (draw_settings *dpars, int x1, int y1, int x2, int y2)

{
  char *ptr;
  path_comp_line *path;
  int len;

  len = TWO_ARG_SUBPATH_LENGTH;
  if (check_buffer_size (dpars, len, NO_HEADER))
  {
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_comp_line *)ptr;
    path->mv.id = SUBPATH_MOVE;
    path->mv.x = x1;
    path->mv.y = y1;
    path->dr.id = SUBPATH_DRAW;
    path->dr.x = x2;
    path->dr.y = y2;
    dpars->buf_offset += len;
  }
  return;
}



/************************************************************/

void DrawL_Square_C (draw_settings *dpars, int x, int y, int size, osbool os_units)

{
  char *ptr;
  path_rectangle_closed *path;
  int len;
  int x1, y1, x2, y2;

  if ( os_units )
  {
    /* need to convert os units to draw units */
    x = x << 8;
    y = y << 8;
    size = size << 8;
  }

  /* Calculate the corner coordinates */
  x1 = x - size;
  x2 = x + size;
  y1 = y - size;
  y2 = y + size;


  len = sizeof(path_rectangle_closed);
  if (check_buffer_size (dpars, len, ADD_HEADER))
  {
    DrawL_BeginPathObject (dpars, NO_CHECK);
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_rectangle_closed *)ptr;

    path->mv.id = SUBPATH_MOVE;
    path->mv.x = x1;
    path->mv.y = y1;

    path->d1.id = SUBPATH_DRAW;
    path->d1.x = x2;
    path->d1.y = y1;

    path->d2.id = SUBPATH_DRAW;
    path->d2.x = x2;
    path->d2.y = y2;

    path->d3.id = SUBPATH_DRAW;
    path->d3.x = x1;
    path->d3.y = y2;

    path->d4.id = SUBPATH_DRAW;
    path->d4.x = x1;
    path->d4.y = y1;

    path->close = SUBPATH_CLOSE;
    dpars->buf_offset += len;
    DrawL_EndPathObject (dpars, NO_CHECK);
  }

  return;
}





/************************************************************/

void DrawL_Triangle_C (draw_settings *dpars, int x, int y, int size,
                      osbool up, osbool os_units)

{
  char *ptr;
  path_four_comp_closed *path;
  int len;
  int x1, y1, x2, y2;

  if ( os_units )
  {
    /* need to convert os units to draw units */
    x = x << 8;
    y = y << 8;
    size = size << 8;
  }

  /* Calculate the corner coordinates */
  x1 = x - size;
  x2 = x + size;
  if (!up) size = - size;
  y1 = y - size;
  y2 = y + size;


  len = sizeof (path_four_comp_closed);

  if (check_buffer_size (dpars, len, ADD_HEADER))
  {
    DrawL_BeginPathObject (dpars, NO_CHECK);
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_four_comp_closed *)ptr;

    path->c1.id = SUBPATH_MOVE;
    path->c1.x = x;
    path->c1.y = y2;

    path->c2.id = SUBPATH_DRAW;
    path->c2.x = x2;
    path->c2.y = y1;

    path->c3.id = SUBPATH_DRAW;
    path->c3.x = x1;
    path->c3.y = y1;

    path->c4.id = SUBPATH_DRAW;
    path->c4.x = x;
    path->c4.y = y2;

    path->close = SUBPATH_CLOSE;

    dpars->buf_offset += len;
    DrawL_EndPathObject (dpars, NO_CHECK);
  }

  return;
}







/************************************************************/

void DrawL_Diamond_C (draw_settings *dpars, int x, int y, int size, osbool os_units)

{
  char *ptr;
  path_rectangle_closed *path;
  int len;
  int x1, y1, x2, y2;

  if ( os_units )
  {
    /* need to convert os units to draw units */
    x = x << 8;
    y = y << 8;
    size = size << 8;
  }

  /* Calculate the corner coordinates */
  x1 = x - size;
  x2 = x + size;
  y1 = y - size;
  y2 = y + size;


  len = sizeof(path_rectangle_closed);
  if (check_buffer_size (dpars, len, ADD_HEADER))
  {
    DrawL_BeginPathObject (dpars, NO_CHECK);
    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_rectangle_closed *)ptr;

    path->mv.id = SUBPATH_MOVE;
    path->mv.x = x1;
    path->mv.y = y;

    path->d1.id = SUBPATH_DRAW;
    path->d1.x = x;
    path->d1.y = y1;

    path->d2.id = SUBPATH_DRAW;
    path->d2.x = x2;
    path->d2.y = y;

    path->d3.id = SUBPATH_DRAW;
    path->d3.x = x;
    path->d3.y = y2;

    path->d4.id = SUBPATH_DRAW;
    path->d4.x = x1;
    path->d4.y = y;

    path->close = SUBPATH_CLOSE;

    dpars->buf_offset += len;
    DrawL_EndPathObject (dpars, NO_CHECK);
  }

  return;
}





/************************************************************/

void DrawL_PlusC (draw_settings *dpars, int x, int y, int size, osbool os_units)

{
  char *ptr;
  path_four_comp_closed *path;
  int len;
  int x1, y1, x2, y2;

  if ( os_units )
  {
    /* need to convert os units to draw units */
    x = x << 8;
    y = y << 8;
    size = size << 8;
  }

  /* Calculate the end of arm coordinates */
  x1 = x - size;
  x2 = x + size;
  y1 = y - size;
  y2 = y + size;

  len = sizeof (path_four_comp_closed);

  if (check_buffer_size (dpars, len, ADD_HEADER))
  {
    DrawL_BeginPathObject (dpars, NO_CHECK);

    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_four_comp_closed *)ptr;

    path->c1.id = SUBPATH_MOVE;
    path->c1.x = x;
    path->c1.y = y2;

    path->c2.id = SUBPATH_DRAW;
    path->c2.x = x;
    path->c2.y = y1;

    path->c3.id = SUBPATH_MOVE;
    path->c3.x = x1;
    path->c3.y = y;

    path->c4.id = SUBPATH_DRAW;
    path->c4.x = x2;
    path->c4.y = y;

    path->close = SUBPATH_CLOSE;

    dpars->buf_offset += len;
    DrawL_EndPathObject (dpars, NO_CHECK);
  }

  return;
}







/************************************************************/

void DrawL_CrossC (draw_settings *dpars, int x, int y, int size, osbool os_units)

{
  char *ptr;
  path_four_comp_closed *path;
  int len;
  int x1, y1, x2, y2;

  if ( os_units )
  {
    /* need to convert os units to draw units */
    x = x << 8;
    y = y << 8;
    size = size << 8;
  }

  /* Calculate the end of arm coordinates */
  x1 = x - size;
  x2 = x + size;
  y1 = y - size;
  y2 = y + size;

  len = sizeof (path_four_comp_closed);

  if (check_buffer_size (dpars, len, ADD_HEADER))
  {
    DrawL_BeginPathObject (dpars, NO_CHECK);

    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_four_comp_closed *)ptr;

    path->c1.id = SUBPATH_MOVE;
    path->c1.x = x1;
    path->c1.y = y2;

    path->c2.id = SUBPATH_DRAW;
    path->c2.x = x2;
    path->c2.y = y1;

    path->c3.id = SUBPATH_MOVE;
    path->c3.x = x1;
    path->c3.y = y1;

    path->c4.id = SUBPATH_DRAW;
    path->c4.x = x2;
    path->c4.y = y2;

    path->close = SUBPATH_CLOSE;

    dpars->buf_offset += len;
    DrawL_EndPathObject (dpars, NO_CHECK);
  }

  return;
}






/************************************************************/

#define CIRC_STR_LEN 128

void DrawL_Circle_C (draw_settings *dpars, int xc, int yc, int rad, osbool os_units)

{
  char *ptr;
  path_comp_one_arg *p1;
  path_comp_bez     *pb;
  path_comp_no_arg  *pn;
  int rc;
  int xcrp, xcrm;
  int ycrp, ycrm;
  int xcfp, xcfm;
  int ycfp, ycfm;

  if (os_units)
  {
    xc = xc << 8;
    yc = yc << 8;
    rad = rad << 8;
  }

  if (check_buffer_size (dpars, CIRC_STR_LEN, ADD_HEADER))
  {
    /* calculate the four quadrant points, and the corresponding
    ** bezier control points */
    rc = (int)((double)rad * CIRC_CONST);
    xcrp = xc + rad;
    xcrm = xc - rad;
    ycrp = yc + rad;
    ycrm = yc - rad;
    xcfp = xc + rc;
    xcfm = xc - rc;
    ycfp = yc + rc;
    ycfm = yc - rc;

    DrawL_BeginPathObject (dpars, NO_CHECK);
    /* now fill the drawfile structure */
    ptr = *dpars->buffer + dpars->buf_offset;
    p1 = (path_comp_one_arg *)ptr;
    p1->comp_id = SUBPATH_MOVE;
    p1->x = xcrp;
    p1->y = yc;

    ptr += 12;
    pb = (path_comp_bez *)ptr;
    pb->comp_id = SUBPATH_BEZIER;
    pb->x1 = xcrp;
    pb->y1 = ycfp;
    pb->cx1 = xcfp;
    pb->cy1 = ycrp;
    pb->cx2 = xc;
    pb->cy2 = ycrp;

    ptr += 28;
    pb = (path_comp_bez *)ptr;
    pb->comp_id = SUBPATH_BEZIER;
    pb->x1 = xcfm;
    pb->y1 = ycrp;
    pb->cx1 = xcrm;
    pb->cy1 = ycfp;
    pb->cx2 = xcrm;
    pb->cy2 = yc;

    ptr += 28;
    pb = (path_comp_bez *)ptr;
    pb->comp_id = SUBPATH_BEZIER;
    pb->x1 = xcrm;
    pb->y1 = ycfm;
    pb->cx1 = xcfm;
    pb->cy1 = ycrm;
    pb->cx2 = xc;
    pb->cy2 = ycrm;

    ptr += 28;
    pb = (path_comp_bez *)ptr;
    pb->comp_id = SUBPATH_BEZIER;
    pb->x1 = xcfp;
    pb->y1 = ycrm;
    pb->cx1 = xcrp;
    pb->cy1 = ycfm;
    pb->cx2 = xcrp;
    pb->cy2 = yc;

    ptr += 28;
    pn = (path_comp_no_arg *)ptr;
    pn->comp_id = SUBPATH_CLOSE;

    dpars->buf_offset += CIRC_STR_LEN;
    DrawL_EndPathObject (dpars, NO_CHECK);
}

  return;
}









#ifdef NEVER


/************************************************************/

void DrawL_Star4 (draw_settings *dpars)

{
  return;
}




#endif

/************************************************************/

#define LENGTH_DOT 0x600
#define LENGTH_DASH 0x1200
#define LENGTH_SMALL_GAP 0x800
#define LENGTH_LARGE_GAP 0x1600

draw_dash_pattern * DrawL_MakeDash (enum dashpattern pattern)

{
  if (pattern == DASHP_SOLID)
  {
    return (NULL);
  }

  /* always start at first component */
  dash.start = 0;

  switch (pattern)
  {
    case DASHP_DOT:
      dash.components = 2;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_SMALL_GAP;
      break;

    case DASHP_DASH:
      dash.components = 2;
      dash.element[0] = LENGTH_DASH;
      dash.element[1] = LENGTH_SMALL_GAP;
      break;

    case DASHP_DOTDASH:
      dash.components = 4;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_SMALL_GAP;
      dash.element[2] = LENGTH_DASH;
      dash.element[3] = LENGTH_SMALL_GAP;
      break;

    case DASHP_OPENDOT:
      dash.components = 2;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_LARGE_GAP;
      break;

    case DASHP_WIDEOPENDOT:
      dash.components = 2;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_LARGE_GAP;
      break;

    case DASHP_LONGDASH:
      dash.components = 2;
      dash.element[0] = LENGTH_DASH * 2;
      dash.element[1] = LENGTH_LARGE_GAP;
      break;

    case DASHP_OPENDOTDASH:
      dash.components = 4;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_LARGE_GAP;
      dash.element[2] = LENGTH_DASH;
      dash.element[3] = LENGTH_LARGE_GAP;
      break;

    case DASHP_LONGDOTDASH:
      dash.components = 4;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_SMALL_GAP;
      dash.element[2] = LENGTH_DASH * 2;
      dash.element[3] = LENGTH_LARGE_GAP;
      break;

    case DASHP_SHORTDOTDASH:
      dash.components = 4;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_SMALL_GAP;
      dash.element[2] = LENGTH_DASH;
      dash.element[3] = LENGTH_SMALL_GAP;
      break;

    case DASHP_OPENDASH:
      dash.components = 2;
      dash.element[0] = LENGTH_DASH;
      dash.element[1] = LENGTH_LARGE_GAP;
      break;

    case DASHP_SHORTDOT:
      dash.components = 2;
      dash.element[0] = 0x400;
      dash.element[1] = 0x400;
      break;

    case DASHP_DOTDOTDASH:
      dash.components = 6;
      dash.element[0] = LENGTH_DOT;
      dash.element[1] = LENGTH_SMALL_GAP;
      dash.element[2] = LENGTH_DOT;
      dash.element[3] = LENGTH_SMALL_GAP;
      dash.element[4] = LENGTH_DASH;
      dash.element[5] = LENGTH_SMALL_GAP;
      break;

  }
  return ((draw_dash_pattern *)&dash);
}




/* Functions that draw higher level objects */

/************************************************************/

void DrawL_Rectangle ( draw_settings *dpars,
                      int x1, int y1, int x2, int y2, osbool os_units)

{
  char *ptr;
  path_rectangle_closed *path;
  int len;

  if ( os_units )
  {
    /* need to convert os units to draw units */
    x1 = x1 << 8;
    y1 = y1 << 8;
    x2 = x2 << 8;
    y2 = y2 << 8;
  }

  len = sizeof(path_rectangle_closed);
  if (check_buffer_size (dpars, len, ADD_HEADER))
  {
    DrawL_BeginPathObject (dpars, NO_CHECK);

    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_rectangle_closed *)ptr;

    path->mv.id = SUBPATH_MOVE;
    path->mv.x = x1;
    path->mv.y = y1;

    path->d1.id = SUBPATH_DRAW;
    path->d1.x = x2;
    path->d1.y = y1;

    path->d2.id = SUBPATH_DRAW;
    path->d2.x = x2;
    path->d2.y = y2;

    path->d3.id = SUBPATH_DRAW;
    path->d3.x = x1;
    path->d3.y = y2;

    path->d4.id = SUBPATH_DRAW;
    path->d4.x = x1;
    path->d4.y = y1;

    path->close = SUBPATH_CLOSE;

    dpars->buf_offset += len;

    DrawL_EndPathObject (dpars, NO_CHECK);
  }
  return;
}

/************************************************************/

void DrawL_Trapezium (draw_settings *dpars,
                      int x1, int y1, int x2, int y2,
                      int x3, int y3, int x4, int y4,
                      osbool os_units)

{
  char *ptr;
  path_rectangle *path;
  int len;

  if ( os_units )
  {
    /* need to convert os units to draw units */
    x1 = x1 << 8;
    y1 = y1 << 8;
    x2 = x2 << 8;
    y2 = y2 << 8;
    x3 = x3 << 8;
    y3 = y3 << 8;
    x4 = x4 << 8;
    y4 = y4 << 8;
  }
  len = 5 * ONE_ARG_SUBPATH_LENGTH;
  if (check_buffer_size (dpars, len, ADD_HEADER))
  {
    DrawL_BeginPathObject (dpars, NO_CHECK);

    ptr = *dpars->buffer + dpars->buf_offset;
    path = (path_rectangle *)ptr;

    path->mv.id = SUBPATH_MOVE;
    path->mv.x = x1;
    path->mv.y = y1;

    path->d1.id = SUBPATH_DRAW;
    path->d1.x = x2;
    path->d1.y = y2;

    path->d2.id = SUBPATH_DRAW;
    path->d2.x = x3;
    path->d2.y = y3;

    path->d3.id = SUBPATH_DRAW;
    path->d3.x = x4;
    path->d3.y = y4;

    path->d4.id = SUBPATH_DRAW;
    path->d4.x = x1;
    path->d4.y = y1;

    dpars->buf_offset += len;

    DrawL_EndPathObject (dpars, NO_CHECK);
  }
  return;
}



/************************************************************/

#define ARC_LENGTH 28

/*
   Draws a simple arc of a circle. This is constructed only as a subpath.
   On entry:
     xo and yo are the coordinates of the centre of the circle
     radius is the radius of the circle
     startangle, endangle define where the arc is on the circle
     if os_units is true then xo, yo and radius are converted to draw
     units first.
     All angles are in radians
*/

void DrawL_Arc ( draw_settings *dpars, int xo, int yo, int radius,
                double startangle, double endangle, osbool os_units )

{
  char *ptr;
  path_comp_bez  *pb;

  double meanangle;
  double ri, xs, ys, xe, ye, xi, yi;
  double rf;

  if (check_buffer_size (dpars, ARC_LENGTH, NO_HEADER))
  {
    if (os_units)
    {
      xo = xo << 8;
      yo = yo << 8;
      radius = radius << 8;
    }
    meanangle = (startangle + endangle) / 2;
    ri = (double)radius / cos (meanangle - startangle);
    rf = (double)radius;
    xs = (double)xo + rf * cos (startangle);
    ys = (double)yo + rf * sin (startangle);
    xe = (double)xo + rf * cos (endangle);
    ye = (double)yo + rf * sin (endangle);
    xi = (double)xo + ri * cos (meanangle);
    yi = (double)yo + ri * sin (meanangle);

    ptr = *dpars->buffer + dpars->buf_offset;
    pb = (path_comp_bez *)ptr;

    pb->comp_id = SUBPATH_BEZIER;
    pb->x1 = (int)(xs - CIRC_CONST * (xs - xi));
    pb->y1 = (int)(ys + CIRC_CONST * (yi - ys));
    pb->cx1 = (int)(xe + CIRC_CONST * (xi - xe));
    pb->cy1 = (int)(ye - CIRC_CONST * (ye - yi));
    pb->cx2 = (int)xe;
    pb->cy2 = (int)ye;

    dpars->buf_offset += ARC_LENGTH;
  }

  return;
}




/************************************************************/

/*
   Draws a slice of a circle.
   On entry:
     xo and yo are the coordinates of the centre of the circle
     radius is the radius of the circle
     startangle, endangle define where the slice is on the circle
     if os_units is true then xo, yo and radius are converted to draw
     units first and the draw units are passed to the arc subroutine.
     All angles are in radians
*/

void DrawL_CircleSlice ( draw_settings *dpars, int xo, int yo, int radius,
                        double startangle, double endangle, osbool os_units )

{
  double midangle1, midangle2, midangle3;
  double sweep, abssweep;
  int xstart, ystart;

  sweep = endangle - startangle;
  abssweep = fabs (sweep);
  if (abssweep < 1.0e-5) return;

  if (os_units)
  {
    xo = xo << 8;
    yo = yo << 8;
    radius = radius << 8;
    os_units = FALSE;
  }
  xstart = (int)((double)xo + (double)radius * cos(startangle));
  ystart = (int)((double)yo + (double)radius * sin(startangle));


  if (abssweep > 2.0 * PI)
  {
    DrawL_Circle_C ( dpars, xo, yo, radius, os_units );
  }
  else
  {
    DrawL_BeginPathObject (dpars, CHECK_BUFFER);
    DrawL_Line ( dpars, xo, yo, xstart, ystart);
    if (abssweep > (3.0 * PI / 2.0))
    {
      midangle1 = startangle + sweep / 4.0;
      midangle2 = startangle + sweep / 2.0;
      midangle3 = startangle + 3.0 * sweep / 4.0;
      DrawL_Arc ( dpars, xo, yo, radius, startangle, midangle1, os_units );
      DrawL_Arc ( dpars, xo, yo, radius, midangle1, midangle2, os_units );
      DrawL_Arc ( dpars, xo, yo, radius, midangle2, midangle3, os_units );
      DrawL_Arc ( dpars, xo, yo, radius, midangle3, endangle, os_units );
    }
    else
    {
      if (abssweep > PI)
      {
        midangle1 = startangle + sweep / 3.0;
        midangle2 = startangle + 2.0 * sweep / 3.0;
        DrawL_Arc ( dpars, xo, yo, radius, startangle, midangle1, os_units );
        DrawL_Arc ( dpars, xo, yo, radius, midangle1, midangle2, os_units );
        DrawL_Arc ( dpars, xo, yo, radius, midangle2, endangle, os_units );
      }
      else
      {
        if (abssweep > (PI / 2.0))
        {
          midangle1 = startangle + sweep / 2.0;
          DrawL_Arc ( dpars, xo, yo, radius, startangle, midangle1, os_units );
          DrawL_Arc ( dpars, xo, yo, radius, midangle1, endangle, os_units );
        }
        else
        {
          DrawL_Arc ( dpars, xo, yo, radius, startangle, endangle, os_units );
        }
      }
    }
    DrawL_Draw ( dpars, xo, yo );
    DrawL_ClosePath (dpars);

    DrawL_EndPathObject (dpars, CHECK_BUFFER);
  }

  return;
}




/*******************************************************************/

/*  Text object creation
 */

/*******************************************************************/

/* size of object excluding the final text string */
#define TRFM_TEXT_OBJ_BASE_SIZE 80
#define TEXT_OBJ_BASE_SIZE      52


/*******************************************************************/

/* x and y coords for start and the fontsize must be in draw units */

void DrawL_TextObjectDU ( draw_settings *dpars,
                          char *text,
                          int x,
                          int y,
                          int fontsize,
                          int font_index,
                          font_f fhandle )

{
  drawfile_diagram_base *diag;
  drawfile_object *draw_obj;
  int fsize;
  int size=0;
  int j;
  int align;
  int len;
  os_box bbox;
  int tx, ty;

  if (*text == 0) return;  /* no text to include */

  len = strlen ( text ) + 1; /* include the terminator */
  size = ( TEXT_OBJ_BASE_SIZE + len ) ;

  if (check_buffer_size (dpars, size + 8, NO_HEADER))  /* room for align bytes + a bit */
  {

    draw_obj = (drawfile_object *)(*dpars->buffer + dpars->buf_offset);
    draw_obj->type = drawfile_TYPE_TEXT;

    draw_obj->data.text.fill = dpars->text_fgnd ;
    draw_obj->data.text.bg_hint = dpars->text_bkgnd ;
    draw_obj->data.text.style.font_index = font_index & 0xff ;
    draw_obj->data.text.style.reserved[0] = 0x00;
    draw_obj->data.text.style.reserved[1] = 0x00;
    draw_obj->data.text.style.reserved[2] = 0x00;
    draw_obj->data.text.xsize = fontsize ;
    draw_obj->data.text.ysize = fontsize ;
    draw_obj->data.text.base.x = x ;
    draw_obj->data.text.base.y = y ;
    /* now copy in the text */
    strcpy (draw_obj->data.text.text, text );
    /* do we need any padding to a word boundary? */
    align = (len) & 3;
    if (align != 0)
    {
      align = 4 - align;
      for ( j = 1; j <= align; j++)
      {
        draw_obj->data.text.text[len + j] = 0x00;
      }
    }
    size += align ;
    /* insert size into object */
    draw_obj->size = size;
    /* update length of file */
    dpars->buf_offset += size;
    /* now calculate the bounding box of this object */
    FontL_string_bbox ( fhandle, text, &trfmID, &bbox );

    font_convertto_os ( bbox.x0 << 8, bbox.y0 << 8, &tx, &ty );

    draw_obj->data.text.bbox.x0 = x + tx;
    draw_obj->data.text.bbox.y0 = y + ty;

    font_convertto_os ( bbox.x1 << 8, bbox.y1 << 8, &tx, &ty );
    draw_obj->data.text.bbox.x1 = x + tx;
    draw_obj->data.text.bbox.y1 = y + ty;

    /* now update the overall bounding box */
    diag = (drawfile_diagram_base *)*dpars->buffer;
    if ( draw_obj->data.text.bbox.x0 < diag->bbox.x0 )
    {
      diag->bbox.x0 = draw_obj->data.text.bbox.x0;
    }
    if ( draw_obj->data.text.bbox.x1 > diag->bbox.x1 )
    {
      diag->bbox.x1 = draw_obj->data.text.bbox.x1;
    }
    if ( draw_obj->data.text.bbox.y0 < diag->bbox.y0 )
    {
      diag->bbox.y0 = draw_obj->data.text.bbox.y0;
    }
    if ( draw_obj->data.text.bbox.y1 > diag->bbox.y1 )
    {
      diag->bbox.y1 = draw_obj->data.text.bbox.y1;
    }

  }

  return;
}


/************************************************************/

/*  x and y coords for start may be in os or draw units,
 *  specified by the os_units flag, and the fontsize must be
 *  in points.
 */

void DrawL_TextObject ( draw_settings *dpars,
                        char *text,
                        int x,
                        int y,
                        double fontsize,
                        int font_index,
                        font_f fhandle,
                        osbool os_units )

{
  int fsize;

  if (*text == 0) return;  /* no text to include */


  if ( os_units )
  {
    /* need to convert os units to draw units */
    x = x << 8;
    y = y << 8;
  }

  fsize = (int)(fontsize * 640);

  DrawL_TextObjectDU ( dpars, text, x, y, fsize, font_index, fhandle );

  return;
}


/*******************************************************************/

/* x and y coords for start and the fontsize must be in draw units */

void DrawL_TransformedTextObjectDU ( draw_settings *dpars,
                                     char *text,
                                     int x,
                                     int y,
                                     int fontsize,
                                     int font_index,
                                     font_f fhandle,
                                     os_trfm *trfm )

{
  drawfile_diagram_base *diag;
  drawfile_object *draw_obj;
  int fsize;
  int size=0;
  int j;
  int align;
  int len;
  os_box bbox;
  int tx, ty;

  if (*text == 0) return;  /* no text to include */

  len = strlen ( text ) + 1; /* include the terminator */
  size = ( TRFM_TEXT_OBJ_BASE_SIZE + len ) ;

  if (check_buffer_size (dpars, size + 8, NO_HEADER))  /* room for align bytes + a bit */
  {

    draw_obj = (drawfile_object *)(*dpars->buffer + dpars->buf_offset);
    draw_obj->type = drawfile_TYPE_TRFM_TEXT;
    /* copy the transformation matrix */
    draw_obj->data.trfm_text.trfm = *trfm;
    /* always kern */
    draw_obj->data.trfm_text.flags = drawfile_TEXT_KERN;
    draw_obj->data.trfm_text.fill = dpars->text_fgnd ;
    draw_obj->data.trfm_text.bg_hint = dpars->text_bkgnd ;
    draw_obj->data.trfm_text.style.font_index = font_index & 0xff ;
    draw_obj->data.trfm_text.style.reserved[0] = 0x00;
    draw_obj->data.trfm_text.style.reserved[1] = 0x00;
    draw_obj->data.trfm_text.style.reserved[2] = 0x00;
    draw_obj->data.trfm_text.xsize = fontsize ;
    draw_obj->data.trfm_text.ysize = fontsize ;
    draw_obj->data.trfm_text.base.x = x ;
    draw_obj->data.trfm_text.base.y = y ;
    /* now copy in the text */
    strcpy (draw_obj->data.trfm_text.text, text );
    /* do we need any padding to a word boundary? */
    align = (len) & 3;
    if (align != 0)
    {
      align = 4 - align;
      for ( j = 1; j <= align; j++)
      {
        draw_obj->data.trfm_text.text[len + j] = 0x00;
      }
    }
    size += align ;
    /* insert size into object */
    draw_obj->size = size;
    /* update length of file */
    dpars->buf_offset += size;
    /* now calculate the bounding box of this object */
    FontL_string_bbox ( fhandle, text, trfm, &bbox );

    font_convertto_os ( bbox.x0 << 8, bbox.y0 << 8, &tx, &ty );

    draw_obj->data.trfm_text.bbox.x0 = x + tx;
    draw_obj->data.trfm_text.bbox.y0 = y + ty;

    font_convertto_os ( bbox.x1 << 8, bbox.y1 << 8, &tx, &ty );
    draw_obj->data.trfm_text.bbox.x1 = x + tx;
    draw_obj->data.trfm_text.bbox.y1 = y + ty;

    /* now update the overall bounding box */
    diag = (drawfile_diagram_base *)*dpars->buffer;
    if ( draw_obj->data.trfm_text.bbox.x0 < diag->bbox.x0 )
    {
      diag->bbox.x0 = draw_obj->data.trfm_text.bbox.x0;
    }
    if ( draw_obj->data.trfm_text.bbox.x1 > diag->bbox.x1 )
    {
      diag->bbox.x1 = draw_obj->data.trfm_text.bbox.x1;
    }
    if ( draw_obj->data.trfm_text.bbox.y0 < diag->bbox.y0 )
    {
      diag->bbox.y0 = draw_obj->data.trfm_text.bbox.y0;
    }
    if ( draw_obj->data.trfm_text.bbox.y1 > diag->bbox.y1 )
    {
      diag->bbox.y1 = draw_obj->data.trfm_text.bbox.y1;
    }

  }

  return;
}


/************************************************************/

/*  x and y coords for start may be in os or draw units,
 *  specified by the os_units flag, and the fontsize must be
 *  in points.
 */

void DrawL_TransformedTextObject ( draw_settings *dpars,
                                   char *text,
                                   int x,
                                   int y,
                                   double fontsize,
                                   int font_index,
                                   font_f fhandle,
                                   os_trfm *trfm,
                                   osbool os_units )

{
  int fsize;

  if (*text == 0) return;  /* no text to include */


  if ( os_units )
  {
    /* need to convert os units to draw units */
    x = x << 8;
    y = y << 8;
  }

  fsize = (int)(fontsize * 640);

  DrawL_TransformedTextObjectDU ( dpars, text, x, y, fsize, font_index, fhandle, trfm );

  return;
}


/************************************************************/

void DrawL_MakeHeader ( draw_settings *dpars, char *source_app )

{
  drawfile_diagram_base *diag;
  int len;
  char *p;

  diag = (drawfile_diagram_base *)*dpars->buffer;
  /* don't need to check buffer size - initial allocation more than enough */
  strncpy ( diag->tag, "Draw", 4 );
  diag->major_version = 201 ;
  diag->minor_version = 0 ;
  len = strlen ( source_app );
  memcpy ( diag->source, source_app, len );
  p = diag->source + len;
  memset ( p, ' ', 12 - len );
  diag->bbox.x0 = 0x7FFFFFFF;
  diag->bbox.y0 = 0x7FFFFFFF;
  diag->bbox.x1 = 0;
  diag->bbox.y1 = 0;
  dpars->buf_offset = sizeof (drawfile_diagram_base);
  return;
}






/************************************************************/

void DrawL_Init (allocfn fn)

{
  memalloc = fn;
  return;
}






