/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
#pragma  pagesize(55)

/**************************************************************************
 *
 * SOURCE FILE NAME = OUTPARC.C
 *
 * DESCRIPTIVE NAME = Output Arc Functions
 *
 *
 * VERSION = V2.0
 *
 * DATE      09/18/88
 *
 * DESCRIPTION This file contains all the drawing routines used to create
 *             arc primitives for the Plotter.
 *
 * FUNCTIONS
 *             construct_chord_tolerance
 *             draw_fullarc
 *             get_arc_center
 *             get_angle
 *             get_distance
 *             get_line_intersection
 *             get_perpendicular_bisect
 *             set_arc_scale
 *             Arc
 *             FullArcBoth
 *             FullArcBoundary
 *             FullArcInterior
 *             PartialArc
 *             PolySpline
 *
 *
 * NOTES
 *
 *
 * STRUCTURES ChordTable
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "plotters.h"
#include "bounds.h"
#include "clip.h"
#include "color.h"
#include "dispatch.h"
#include "dosio.h"
#include "error.h"
#include "glib.h"
#include "outparc.h"
#include "outpline.h"
#include "output.h"
#include "utils.h"
#include "xforms.h"
#include "lockddc.h"
#define  SLOPE_CONSTANT 1
#define  SLOPE_ZERO    0
#define  SLOPE_INFINITE -1
typedef struct
{
  LONG MaxRadius;
  USHORT Tolerance;
} CHORDTABLE;


LOCAL CHORDTABLE ChordTable[] =
{
  {
    0L,             9000
  } ,                                  /* 90 degrees                        */
  {
    1L,             9000
  } ,                                  /* 90 degrees                        */
  {
    2L,             9000
  } ,                                  /* 90 degrees                        */
  {
    3L,             4500
  } ,                                  /* 45 degrees                        */
  {
    4L,             3500
  } ,                                  /* 35 degrees                        */
  {
    8L,             3000
  } ,                                  /* 30 degrees                        */
  {
   12L,             2500
  } ,                                  /* 25 degrees                        */
  {
   16L,             2000
  } ,                                  /* 20 degrees                        */
  {
   17L,             1500
  } ,                                  /* 15 degrees                        */
  {
   30L,             1000
  } ,                                  /* 10 degrees                         */
  {
   50L,              500
  } ,                                  /* 5 degrees                         */
  {
   100L,             300
  } ,                                  /* 2 degrees                         */
  {
   200L,             100
  } ,                                  /* 1 degrees                         */
  {
   0x7fffffffL,      50
  }                                    /* .5 degrees                        */
} ;
#define CHORDTABLESIZE (sizeof( ChordTable ) / sizeof( CHORDTABLE ) )



/*
** Local functions
*/
LOCAL VOID   construct_chord_tolerance(PDDC,LONG);
LOCAL VOID   draw_fullarc(HDC,PDDC,PPOINTL,LONG,LONG,BOOL,LONG);
LOCAL BOOL   get_arc_center(PPOINTL,PPOINTL,PPOINTL,PPOINTL);
LOCAL SHORT  get_angle(PPOINTL,PPOINTL);
LOCAL LONG   get_distance(PPOINTL,PPOINTL);
LOCAL BOOL   get_line_intersection(PPOINTL,PPOINTL,PPOINTL);
LOCAL VOID   get_perpendicular_bisect(PPOINTL,PPOINTL,PPOINTL,
                                                 PPOINTL);
LOCAL VOID   set_arc_scale(PDDC,PARCPARAMS);
LOCAL BOOL   is_elipse(HDC, LONG, PDDC, LONG);
LOCAL BOOL   ConvertArcParams(HDC, PDDC, PARCPARAMS, LONG);

/***************************************************************************
 *
 * FUNCTION NAME = function: ConvertArcParams
 *
 * DESCRIPTION   = Converts the lQ and lP arcparameter values using
 *                 the fixed multiplier and the current transform.
 * NOTE!!!!!!!!!
 *
 *
 * INPUT         = hDC        - DC handle
 *                 pDDC       - Divice info block
 *                 pArcParams - Pointer to the arc paramaters
 *                 lMultiplier- Radius/multiplier
 *
 * OUTPUT        = UPDATES the arcparams structure with the
 *                 converted lQ (Y) and lP (X)
 *
 * RETURN-NORMAL = TRUE = SUCCESS
 * RETURN-ERROR  = FALSE = FAIL
 *
 **************************************************************************/
BOOL ConvertArcParams (HDC hDC, PDDC pDDC, PARCPARAMS pArcParams,
                       LONG fxMultiplier)

{
    LONG    lXLen, lYLen;
    LONG    lMultiplierX100;

    /*
    ** get the fixed radius into a long keeping 2 decimal places
    */
    lMultiplierX100  = fxMultiplier / 65536L * 100L;
    lMultiplierX100 += (LONG)(LOUSHORT(fxMultiplier)) * 100L / 32768L;

    lXLen = pArcParams->lP * lMultiplierX100 / 100L;
    lYLen = pArcParams->lQ * lMultiplierX100 / 100L;

    ConvertWithRotation (hDC, pDDC, TRUE, &lXLen, &lYLen);

    // update arcparams structure with the Device lQ (Y) and lP (X)
    pArcParams->lP = lXLen;
    pArcParams->lQ = lYLen;

    return ( TRUE );

}

/***************************************************************************
 *
 * FUNCTION NAME = is_elipse
 *
 * DESCRIPTION   = This function sees if we are about to draw an ellipse
 *                 instead of a circle.
 *
 * NOTE!!!!!!!!!   This code is copied from draw_fullarc.  Any changes to
 *                 it might have to be duplicated here.
 *
 * INPUT         = hDC        - DC handle
 *                 Radius     - radius of Arc
 *                 pDDC       - pointer to device driver cookie
 *                 usComFlags - COM FLAGS
 *
 * OUTPUT        = TRUE if elipse
 *                 FALSE if circle
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/
LOCAL BOOL is_elipse(HDC hDC, LONG fxRadius, PDDC pDDC, LONG lComFlags)
{
   if (lComFlags & COM_TRANSFORM)
   {
      ARCPARAMS DevArcParams;

      /*
      ** Get a copy of the current ArcParams
      */
      DevArcParams = pDDC->DCState.ArcParams;

      /*
      ** Convert the lQ (y) and lP (x) arcparams to Device
      */
      ConvertArcParams (hDC, pDDC, &DevArcParams, fxRadius);

      /*
      ** if the converted lQ (y) is not equal to lP (x) it's an elipse
      */
      return (ABS(DevArcParams.lQ) != ABS(DevArcParams.lP));
   }
   else
   {
      /*
      ** if the lQ (y) is not equal to lP (x) it's and elipse
      */
      return (ABS(pDDC->DCState.ArcParams.lQ) != ABS(pDDC->DCState.ArcParams.lP));
    }
}

/***************************************************************************
 *
 * FUNCTION NAME = construct_chord_tolerance
 *
 * DESCRIPTION   = This function outputs the chord tolerance for the
 *                 Circle and Arc commands to the plotter, based on
 *                 the Radius received.
 *
 * INPUT         = pDDC   - pointer to device driver cookie
 *                 Radius - radius of arc
 *
 * OUTPUT        = chord tolerance sent to Plotter
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL VOID  construct_chord_tolerance(PDDC pDDC,LONG Radius)
{
  short Index;
  LONG Tolerance;

  Radius = ABS(Radius);

//if (pDDC->pPDevice->pSetup->bDraft)
//  Radius /= 2L;

  for (Index = 0; Index < CHORDTABLESIZE; Index++)
    if (Radius <= ChordTable[Index].MaxRadius)
      break;

  if (Radius < ChordTable[Index].MaxRadius)
  {
    --Index;
  }

  if (Index < 0)
    Index = 0;

  Tolerance = (long)ChordTable[Index].Tolerance;

  if (Tolerance != 500L)
  {
    output_comma(pDDC);
    construct_float(pDDC, Tolerance *10L, 1000L);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = draw_fullarc
 *
 * DESCRIPTION   = This function draws a fullarc (circle or ellipse),
 *                 given its center point and a radius.  If Fill is
 *                 TRUE, then the fullarc is filled, otherwise it is
 *                 outlined.  Upon completion, the current physical
 *                 pen position is at the center of the circle.
 *
 * NOTE!!!!!!!!!   Code is also duplicated in is_ellipse.  Check that
 *                 function for more details.
 *                 A problem was found for ellipses when an input window
 *                 was imposed on a section of the page. They would dissapear.
 *                 An attempt was made to reset the input window after the
 *                 scale command, but this failed.  So, in all functions that
 *                 call this, an extra test was added.  This function should
 *                 only be called for circles and not for ellipses.
 *
 * INPUT         = hDC     - DC handle
 *                 pDDC    - pointer to device driver cookie
 *                 pptlCenter - center of Arc
 *                 Radius  - radius of Arc
 *                 lColor   - color of Arc
 *                 bFill   - fill flag
 *                 lComFlags - long COM FLAGS will also have fun#
 *
 * OUTPUT        = Arc is drawn
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL VOID  draw_fullarc(HDC hDC,PDDC pDDC,
                                     PPOINTL pptlCenter,LONG fxRadius,LONG
                                     lColor,BOOL bFill, LONG lComFlags)
{

  BOOL bScale;
  ARCPARAMS DevArcParams;
  LONG lRadius;
  LONG lRadiusX100;

  /*
  ** get the fixed radius into a long keeping 2 decimal places
  */
  lRadiusX100  = fxRadius / 65536L * 100L;
  lRadiusX100 += (LONG)(LOUSHORT(fxRadius)) *100L / 32768L;

  /*
  ** copy the current ArcParams
  */
  DevArcParams = pDDC->DCState.ArcParams;

  if (lComFlags & COM_TRANSFORM)
  {
    /*
    ** Convert the lQ (y) and lP (x) arcparams to Device
    */
    ConvertArcParams (hDC, pDDC, &DevArcParams, fxRadius);
  }
  else
  {
    DevArcParams.lQ  = DevArcParams.lQ * lRadiusX100 / 100L;
    DevArcParams.lP  = DevArcParams.lP * lRadiusX100 / 100L;
  }

  move_pen(pDDC, pptlCenter, FALSE);


  /*
  ** Check if we need to set ellipse scalling
  ** if Q != P than we must scale the circle into an ellipse
  */
  bScale = ABS(DevArcParams.lQ) != ABS(DevArcParams.lP);
  if (bScale)
  {
     set_arc_scale(pDDC,&DevArcParams);
     lRadius = 1;
  }
  else
  {
    /*
    ** it's a circle
    ** just scale the radius
    */
    lRadius = ABS(DevArcParams.lQ);
  }

  if (bFill)
  {
    USHORT usPatSym = pDDC->DCState.abnd.usSymbol;
    POINTL ptlEnd;
    /*
    ** handle special case- setting area to white
    */
    if (usPatSym == PATSYM_NOSHADE || usPatSym == PATSYM_BLANK)
    {
      if (!(PlotterClass[pDDC->pPDevice->Plotter].usHPGLCaps & HPGL2))
      {
        /*
        ** Not an HPGL2 device
        ** Can't erase, area so just return.
        */
        return;
      }
      else
      {
        /*
        ** Force color to White
        */
        if (pDDC->DCState.usColorFormat != LCOLF_RGB)
          lColor = CLR_WHITE;
        else
          lColor = COLOR_WHITE;
      }
    }
    select_fill_pen(pDDC, lColor);
    /*
    ** Note: must select fill type after selecting fill pen
    */
    /*
    ** Set up fill type to fill in the most efficient direction
    */
    ptlEnd.x = DevArcParams.lP;
    ptlEnd.y = DevArcParams.lQ;

    /*
    ** If it is a user defined pattern.   Kran.
    */
    if (pDDC->DCState.abnd.usSet)
    {
     /*
     ** check_area_back_color is called in setup_raster_fill_index
     */
     setup_raster_fill_index(hDC,pDDC,NULL,NULL);

     if (pDDC->DCState.abnd.usMixMode == FM_LEAVEALONE)
     {
       usPatSym = PATSYM_SOLID;
       select_fill_pen(pDDC,pDDC->DCState.abnd.lBackColor);
     }
     else
       usPatSym = PATSYM_USERDEFINED;
    }

    select_fill_type( pDDC, usPatSym, pptlCenter, &ptlEnd );
    set_line_type(pDDC, LINETYPE_SOLID);
    output_bytes(pDDC, "PM0");
  }
  else
  {
    select_line_pen(pDDC, lColor);
    set_line_type(pDDC, pDDC->DCState.lbnd.usType);
  }

  output_bytes(pDDC, "CI");
  output_number(pDDC, lRadius);

  lRadius = (DevArcParams.lP < DevArcParams.lQ)?
              DevArcParams.lQ : DevArcParams.lP;
  construct_chord_tolerance(pDDC, lRadius);

  if (bFill)
    output_bytes(pDDC, "PM2FP");

  /*
  ** if we changed the scale set it back
  */
  if (bScale)
  {
     set_default_scale(pDDC);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = get_angle
 *
 * DESCRIPTION   = This function returns the angle of a line in tenths
 *                 of a degree.
 *
 * INPUT         = pPtA - pointer to Point A
 *                 pPtB - pointer to Point B
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Angle
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL SHORT  get_angle(PPOINTL pPtA,PPOINTL pPtB)
{
  SHORT Angle = Arctan((SHORT)(pPtB->x-pPtA->x),
                       (SHORT)(pPtB->y-pPtA->y), 1, 1);

  return (Angle);
}

/***************************************************************************
 *
 * FUNCTION NAME = get_arc_center
 *
 * DESCRIPTION   = This function sets the center point of a circle
 *                 defined by three points on the circumference of
 *                 that circle.
 *
 * INPUT         = pPtA - first point on circle
 *                 pPtB - second point on circle
 *                 pPtC - third point on circle
 *
 * OUTPUT        = pptlCenter - center point of circle
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL BOOL  get_arc_center(PPOINTL pptlCenter,PPOINTL pPtA,PPOINTL
                                       pPtB,PPOINTL pPtC)
{
  POINTL PtsAB[2],PtsBC[2];


  get_perpendicular_bisect(pPtA, pPtB, &PtsAB[0], &PtsAB[1]);
  get_perpendicular_bisect(pPtB, pPtC, &PtsBC[0], &PtsBC[1]);

  return (get_line_intersection(pptlCenter, PtsAB, PtsBC));
}

/***************************************************************************
 *
 * FUNCTION NAME = get_distance
 *
 * DESCRIPTION   = This function returns the distance, in specified
 *                 coordinates, between the two given points.
 *
 * INPUT         = pPtA
 *                 pPtB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL LONG  get_distance(PPOINTL pPtA,PPOINTL pPtB)
{

  LONG Height   = pPtB->y-pPtA->y,
       Width    = pPtB->x-pPtA->x,
       SaveSine = (long)Sine(Arctan((short)Width, (short)Height, 1, 1)),
       Length   = (Height == 0L || SaveSine == 0L) ? Width : Height
                                                   * TRIG_SCALE/SaveSine;

  return (ABS(Length));
}

/***************************************************************************
 *
 * FUNCTION NAME = get_line_intersection
 *
 * DESCRIPTION   = This function returns the intersection point of two
 *                 lines defined by pPtsAB[0], pPtsAB[1] and
 *                 pPtsCD[0], pPtsCD[1].  Returns TRUE if successful,
 *                 otherwise FALSE for lines are parallel.
 *
 * INPUT         = pPtsAB
 *                 pPtsCD
 *
 * OUTPUT        = pIntersect
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *
 **************************************************************************/

LOCAL BOOL  get_line_intersection(PPOINTL pIntersect,PPOINTL pPtsAB
                                              ,PPOINTL pPtsCD)
{
  BOOL bResult = TRUE;

  LONG Rise1  = pPtsAB[0].y-pPtsAB[1].y,
       Rise2  = pPtsCD[0].y-pPtsCD[1].y,
       Run1   = pPtsAB[0].x-pPtsAB[1].x,
       Run2   = pPtsCD[0].x-pPtsCD[1].x,
       Slope1 = 0,
       Slope2 = 0,
      Line1Slope = SLOPE_ZERO,         /* default 0 slope                   */
      Line2Slope = SLOPE_ZERO;         /* default 0 slope                   */

  /*
  ** Calculate slopes for each line, and mark their appropiate flags
  */
  if (Run1)
  {
    if (Rise1)
    {
      Slope1 = (Rise1 *TRIG_SCALE)/Run1;
      Line1Slope = SLOPE_CONSTANT;     /* slope is okay                     */
    }
    else
      Line1Slope = SLOPE_INFINITE;     /* slope is infinite                 */
  }

  if (Run2)
  {
    if (Rise2)
    {
      Slope2 = (Rise2 *TRIG_SCALE)/Run2;
      Line2Slope = SLOPE_CONSTANT;     /* slope is okay                     */
    }
    else
      Line2Slope = SLOPE_INFINITE;     /* slope is infinite                 */
  }

  /*
  ** Now check for parallel lines.
  */
  if ((Line1Slope == SLOPE_ZERO && Line2Slope == SLOPE_ZERO) ||
      (Line1Slope == SLOPE_INFINITE &&
       Line2Slope == SLOPE_INFINITE) ||
       ((Line1Slope ==
     SLOPE_CONSTANT && Line2Slope == SLOPE_CONSTANT) && (Slope1 == Slope2)))
    bResult = FALSE;
  else
  {
    LONG B1 = pPtsAB[0].y * TRIG_SCALE - (Slope1 *pPtsAB[0].x),
         B2 = pPtsCD[0].y * TRIG_SCALE - (Slope2 *pPtsCD[0].x);

    if (Line1Slope == SLOPE_CONSTANT)
    {
      if (Line2Slope == SLOPE_CONSTANT)
      {
        pIntersect->x = (B2-B1)/(Slope1-Slope2);
        pIntersect->y = ((Slope1 *pIntersect->x)/TRIG_SCALE)+(B1/TRIG_SCALE);
      }
      else
        if (Line2Slope == SLOPE_ZERO)
        {
          pIntersect->x = pPtsCD[0].x;
          pIntersect->y = ((Slope1 *pIntersect->x)/TRIG_SCALE)+(B1/TRIG_SCALE) ;
        }
        else                           /* Line2Slope == SLOPE_INFINITE      */
        {
          pIntersect->y = pPtsCD[0].y;
          pIntersect->x = ((pIntersect->y *TRIG_SCALE)-B1)/Slope1;
        }
    }
    else
      if (Line1Slope == SLOPE_ZERO)
      {
        pIntersect->x = pPtsAB[0].x;

        if (Line2Slope == SLOPE_CONSTANT)
          pIntersect->y = ((Slope2 *pIntersect->x)/TRIG_SCALE)+(B2/TRIG_SCALE);
        else
          pIntersect->y = pPtsCD[0].y;
      }
      else                             /* Line1Slope == SLOPE_INFINITE      */
      {
        pIntersect->y = pPtsAB[0].y;

        if (Line2Slope == SLOPE_CONSTANT)
          pIntersect->x = ((pIntersect->y *TRIG_SCALE)-B2)/Slope2;
        else
          pIntersect->x = pPtsCD[0].x;
      }
  }

  return (bResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = get_perpendicular_bisect
 *
 * DESCRIPTION   = This function returns line CD which runs
 *                 perpendicular to and bisects line AB.
 *
 * INPUT         = pPtA
 *                 pPtB
 *
 * OUTPUT        = pPtC
 *                 pPtD
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

LOCAL VOID  get_perpendicular_bisect(PPOINTL pPtA,PPOINTL pPtB,
                                                 PPOINTL pPtC,PPOINTL pPtD)
{
  POINTL PtMidAB;
  LONG DistX,DistY;

  PtMidAB.x = (pPtA->x+pPtB->x+1)/2L;
  PtMidAB.y = (pPtA->y+pPtB->y+1)/2L;
  DistX = ABS(pPtA->x-PtMidAB.x);
  DistY = ABS(pPtA->y-PtMidAB.y);

  if (pPtA->x >= PtMidAB.x && pPtA->y >= PtMidAB.y)/* quadrant 1            */
  {
    pPtC->x = PtMidAB.x-DistY;
    pPtC->y = PtMidAB.y+DistX;
    pPtD->x = PtMidAB.x+DistY;
    pPtD->y = PtMidAB.y-DistX;
  }
  else
    if (pPtA->x <= PtMidAB.x && pPtA->y >= PtMidAB.y)/* quadrant 2          */
    {
      pPtC->x = PtMidAB.x-DistY;
      pPtC->y = PtMidAB.y-DistX;
      pPtD->x = PtMidAB.x+DistY;
      pPtD->y = PtMidAB.y+DistX;
    }
    else
      if (pPtA->x <= PtMidAB.x && pPtA->y <= PtMidAB.y)/* quadrant 3        */
      {
        pPtC->x = PtMidAB.x+DistY;
        pPtC->y = PtMidAB.y-DistX;
        pPtD->x = PtMidAB.x-DistY;
        pPtD->y = PtMidAB.y+DistX;
      }
      else                             /* quadrant 4                        */
      {
        pPtC->x = PtMidAB.x+DistY;
        pPtC->y = PtMidAB.y+DistX;
        pPtD->x = PtMidAB.x-DistY;
        pPtD->y = PtMidAB.y-DistX;
      }
}

/***************************************************************************
 *
 * FUNCTION NAME = set_arc_scale
 *
 * DESCRIPTION   = This sets up the scaling for the arc parameters
 *
 * INPUT         = pDDC - pointer to device driver cookie
 *               = pDevArcParams - Pointer to device arc params
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL VOID  set_arc_scale(PDDC pDDC, PARCPARAMS pDevArcParams)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  RECTL Scale;

  Scale.xLeft   = 0;
  Scale.yBottom = 0;
  /*
  ** Use current scaling factor times Q and P
  */
  Scale.xRight  = pPDevice->lIWScale;
  Scale.yTop    = Scale.xRight;

  Scale.xRight  = Scale.xRight * ((pPDevice->fsTransform & DXF_PORTRAIT) ?
                  pDevArcParams->lQ : pDevArcParams->lP);
  Scale.yTop    = Scale.yTop * ((pPDevice->fsTransform & DXF_PORTRAIT) ?
                  pDevArcParams->lP : pDevArcParams->lQ);
  if (pPDevice->usHPGLType & HPGL2)
  {
    set_scale(pDDC, &Scale, SCALE_POINTFACTOR);
  }
  /*
  ** On some HPGL1 devices Point-Factor scaling does not work
  ** so we will use regular Anisotorpic scaling.
  **
  ** divide P1P2 by the point factor(with 3 decimal places) to
  ** calculate the correct ratio of user units.
  */
  else
  {
    Scale.xRight  = (pPDevice->DefaultP1P2.xRight -
                     pPDevice->DefaultP1P2.xLeft) *
                     pPDevice->lIWScaleMultiplier / Scale.xRight;
    Scale.yTop    = (pPDevice->DefaultP1P2.yTop -
                     pPDevice->DefaultP1P2.yBottom) *
                     pPDevice->lIWScaleMultiplier / Scale.yTop;

    /*
    ** if P1P2 origin is at the center of the paper
    */
    if (pPDevice->fsTransform & DXF_CENTERED)
    {
      Scale.xLeft   = -(Scale.xRight / 2);
      Scale.xRight  = Scale.xRight / 2;
      Scale.yBottom = -(Scale.yTop / 2);
      Scale.yTop    = Scale.yTop / 2;
    }
    set_scale(pDDC, &Scale, SCALE_ANISOTROPIC);
  }
}

/*
** Exported Routines
*/

/***************************************************************************
 *
 * FUNCTION NAME = Arc (Exported)
 *
 * DESCRIPTION   = Creates an Arc using the current arc parameters and
 *                 a pointer to 2 x,y coordinate pairs.  The Arc is
 *                 drawn from the current position (a) thru the first
 *                 coordinate pair (b) to the second coordinate pair
 *                 (c).  Upon completion the current position is
 *                 updated to the end of the arc (c).
 *
 * INPUT         = hDC      - device context handle
 *                 pWrldPts - world coordinate pairs
 *                 pDDC     - pointer to device driver cookie
 *                 FunN     - function number
 *
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

LONG  Arc(HDC hDC,PPOINTL pWrldPts,PDDC pDDC,ULONG FunN)
{
  LONG Result = 1L;
  PPDEVICE pPDevice = pDDC->pPDevice;


  if(!(EnterDriver(pDDC)))
      return(GPI_ERROR);
  if (!(FunN & COM_DRAW) ||
       (FunN & (COM_AREA | COM_PATH | COM_BOUND)) ||
      pDDC->DCState.ClipComplexity > 1L ||
      // p Scales in the x-direction
      // q scales in the y-driection
      // r and s are the shear components
      // if p*r + s*q = 0 it is not an ellipse
      pDDC->DCState.ArcParams.lR ||
      pDDC->DCState.ArcParams.lS ||
      (pDDC->DCState.ArcParams.lP != pDDC->DCState.ArcParams.lQ) ||
      (pPDevice->Plotter == CLASS_COLORPRO && !pPDevice->pSetup->bGECOption) ||
      !(PlotterClass[pPDevice->Plotter].usHPGLCaps & HPGL_ARCS))
    Result = (*daArc)(hDC, pWrldPts, pDDC, FunN);
  else
  {
    POINTL Center,DevPts[3];
    DevPts[0] = pDDC->DCState.LogPosition;
    DevPts[1] = pWrldPts[0];
    DevPts[2] = pWrldPts[1];

    if (FunN & COM_TRANSFORM)
      convert_world_to_device(hDC, &DevPts[1], 2L, pDDC);

    if (check_line_color(pDDC) && (FunN & COM_DRAW))
    {
      if (DevPts[0].x == DevPts[2].x && DevPts[0].y == DevPts[2].y)
        Result = PolyLine(hDC, &pWrldPts[0], 1L, pDDC,
                          MAKEULONG(NGrePolyLine, HIUSHORT(FunN)));
      else
        if ((DevPts[0].x == DevPts[1].x && DevPts[0].y == DevPts[1].y) ||
            (DevPts[1].x == DevPts[2].x && DevPts[1].y == DevPts[2].y))
          Result = PolyLine(hDC, &pWrldPts[1], 1L, pDDC,
                            MAKEULONG(NGrePolyLine, HIUSHORT(FunN)));
        else
        {
          PPDEVICE pPDevice = pDDC->pPDevice;


          pPDevice->bLineInConstruction = FALSE;
          move_pen(pDDC, &DevPts[0], FALSE);
          select_line_pen(pDDC, pDDC->DCState.lbnd.lColor);
          set_line_type(pDDC, pDDC->DCState.lbnd.usType);

          if (pPDevice->bPenIsUp)
          {
            pPDevice->bPenIsUp = FALSE;
            output_bytes(pDDC, "PD");
          }

          /*
          ** if we are on an HPGL/2 device
          ** use the three point arc function "AT"
          */
          if (pPDevice->usHPGLType & HPGL2)
          {
            output_bytes(pDDC, "AT");
            construct_point(pDDC, &DevPts[1], FALSE);
            output_comma(pDDC);
            construct_point(pDDC, &DevPts[2], FALSE);

            pPDevice->PhysPosition = DevPts[2];
          }
          /*
          ** else are on an HPGL1 device
          ** do the necessary calulations uand
          ** use the Arc Absolute function "AA"
          */
          else
          {
            DevPts[0].x *= 4L;
            DevPts[0].y *= 4L;
            DevPts[1].x *= 4L;
            DevPts[1].y *= 4L;
            DevPts[2].x *= 4L;
            DevPts[2].y *= 4L;

            if (!get_arc_center(&Center, &DevPts[0], &DevPts[1], &DevPts[2]))
              Result = PolyLine(hDC, &pWrldPts[1], 1L, pDDC,
                                MAKEULONG(NGrePolyLine, HIUSHORT(FunN)));
            else
            {
              LONG Angle1 = get_angle(&Center,
                                      &DevPts[0]),
                   Angle2 = get_angle(&Center, &DevPts[1]),
                   Angle3 = get_angle(&Center, &DevPts[2]),
                   Sweep  = ((Angle3-Angle1)-3600)% 3600;

              DevPts[0].x /= 4L;
              DevPts[0].y /= 4L;
              DevPts[1].x /= 4L;
              DevPts[1].y /= 4L;
              DevPts[2].x /= 4L;
              DevPts[2].y /= 4L;
              Center.x /= 4L;
              Center.y /= 4L;

              if ((((Angle2-Angle1)+3600)%3600) < (((Angle3-Angle1)+3600)%3600))
                Sweep += 3600;

              output_bytes(pDDC, "AA");
              construct_point(pDDC, &Center, FALSE);
              output_comma(pDDC);
              construct_fixed(pDDC, (Sweep *65536L)/10L);
              construct_chord_tolerance(pDDC,
                                        (LONG)get_distance(&Center,
                                                           &DevPts[0]));
              pPDevice->PhysPosition = DevPts[2];
            }
          }
        }
    }
    SetCurrentPosition(hDC, &pWrldPts[1], pDDC,
                       MAKEULONG(NGreSetCurrentPosition, HIUSHORT(FunN)));
  }
  LeaveDriver(pDDC);
  return (Result);
}

/***************************************************************************
 *
 * FUNCTION NAME = FullArcBoth
 *
 * DESCRIPTION   = This function creates a FILLED and OUTLINED full
 *                 arc with its center at the current position.  A
 *                 multiplier may be specified to apply to the current
 *                 arc parameters.  Upon completion, the current x,y
 *                 position is NOT changed.
 *
 * INPUT         = hDC        - handle to device context
 *                 lMultiplier - arc multiplier
 *                 pDDC       - pointer to device driver cookie
 *                 FunN       - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG  FullArcBoth(HDC hDC,LONG lMultiplier,PDDC pDDC,ULONG FunN)
{
  LONG Result = 1L;
  PPDEVICE pPDevice = pDDC->pPDevice;
  USHORT  usPlotter = pPDevice->Plotter;


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);
  if (!(FunN & COM_DRAW) ||
  (FunN & (COM_AREA | COM_PATH | COM_BOUND)) ||
   pDDC->DCState.ClipComplexity > 1L ||
   /* r and s are the shear components */
   pDDC->DCState.ArcParams.lR ||
   pDDC->DCState.ArcParams.lS ||
   // draw_fullarc can only handle circles.  If an input window is set on a
   // portion of the page, ellipses will disappear.  Call back to the engine
   // to make them show up.
   is_elipse (hDC, lMultiplier, pDDC, FunN) ||
   (usPlotter == CLASS_COLORPRO && !pPDevice->pSetup->bGECOption) ||
   //(check_fill_type(pDDC) &&
   // !(PlotterClass[usPlotter].fsOdd & OD_FILLTYPE)) ||
   !(PlotterClass[usPlotter].usHPGLCaps & HPGL_CIRCLE) ||
   !(PlotterClass[usPlotter].usHPGLCaps & HPGL_POLYGON))
    Result = (*daFullArcBoth)(hDC, lMultiplier, pDDC, FunN);
  else
    if (FunN & COM_DRAW)
    {
      set_clip_rectangle(pDDC);

      if (pDDC->DCState.abnd.usSet || check_area_color(pDDC))
        draw_fullarc(hDC, pDDC, &pDDC->DCState.LogPosition, lMultiplier,
                     pDDC->DCState.abnd.lColor, TRUE, FunN);
      if (check_line_color(pDDC))
        draw_fullarc(hDC, pDDC, &pDDC->DCState.LogPosition, lMultiplier,
                     pDDC->DCState.lbnd.lColor, FALSE, FunN);
    }
    else
    {
      check_line_color(pDDC);
      check_area_color(pDDC);
    }

  LeaveDriver(pDDC);
  return (Result);
}

/***************************************************************************
 *
 * FUNCTION NAME = FullArcBoundary
 *
 * DESCRIPTION   = This function creates an OUTLINED full arc with its
 *                 center at the current position.  A multiplier may
 *                 be specified to apply to the current arc
 *                 parameters.  Upon completion, the current x,y
 *                 position is NOT changed.
 *
 * INPUT         = hDC        - handle to device context
 *                 lMultiplier - arc multiplier
 *                 pDDC       - pointer to device driver cookie
 *                 FunN       - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG  FullArcBoundary(HDC hDC,LONG lMultiplier,PDDC pDDC,ULONG FunN)
{
  LONG Result = 1L;
  PPDEVICE pPDevice = pDDC->pPDevice;
  USHORT usPlotter = pPDevice->Plotter;


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);
  if (!(FunN & COM_DRAW) ||
      (FunN & (COM_AREA | COM_PATH | COM_BOUND)) ||
      pDDC->DCState.ClipComplexity > 1L ||
      /* r and s are the shear components */
      pDDC->DCState.ArcParams.lR ||
      pDDC->DCState.ArcParams.lS ||
      // draw_fullarc can only handle circles.  If an input window is set on a
      // portion of the page, ellipses will disappear.  Call back to the engine
      // to make them show up.
      is_elipse (hDC, lMultiplier, pDDC, FunN) ||
      (usPlotter == CLASS_COLORPRO && !pPDevice->pSetup->bGECOption) ||
      !(PlotterClass[usPlotter].usHPGLCaps & HPGL_CIRCLE))
    Result = (*daFullArcBoundary)(hDC, lMultiplier, pDDC, FunN);
  else
    if (check_line_color(pDDC) && (FunN & COM_DRAW))
    {
      set_clip_rectangle(pDDC);
      draw_fullarc(hDC, pDDC, &pDDC->DCState.LogPosition, lMultiplier,
                   pDDC->DCState.lbnd.lColor, FALSE, FunN);
    }

  LeaveDriver(pDDC);
  return (Result);
}

/***************************************************************************
 *
 * FUNCTION NAME = FullArcInterior
 *
 * DESCRIPTION   = This function creates a FILLED full arc with its
 *                 center at the current x,y position.  A multiplier
 *                 may be specified to apply to the current arc
 *                 parameters.  Upon completion, the current x,y
 *                 position is NOT changed.
 *
 * INPUT         = hDC        - handle to device context
 *                 lMultiplier - arc multiplier
 *                 pDDC       - pointer to device driver cookie
 *                 FunN       - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG  FullArcInterior(HDC hDC,LONG lMultiplier,PDDC pDDC,ULONG FunN)
{
  LONG Result = 1L;
  PPDEVICE pPDevice = pDDC->pPDevice;
  USHORT usPlotter = pPDevice->Plotter;


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);
  if (!(FunN & COM_DRAW) ||
      (FunN & (COM_AREA | COM_PATH | COM_BOUND)) ||
      pDDC->DCState.ClipComplexity > 1L ||
      /* r and s are the shear components */
      pDDC->DCState.ArcParams.lR ||
      pDDC->DCState.ArcParams.lS ||
      // draw_fullarc can only handle circles.  If an input window is set on a
      // portion of the page, ellipses will disappear.  Call back to the engine
      // to make them show up.
      is_elipse (hDC, lMultiplier, pDDC, FunN) ||
      (usPlotter == CLASS_COLORPRO && !pPDevice->pSetup->bGECOption) ||
      //(check_fill_type(pDDC) &&
      // !(PlotterClass[usPlotter].fsOdd & OD_FILLTYPE)) ||
      !(PlotterClass[usPlotter].usHPGLCaps & HPGL_CIRCLE) ||
      !(PlotterClass[usPlotter].usHPGLCaps & HPGL_POLYGON))
    Result = (*daFullArcInterior)(hDC, lMultiplier, pDDC, FunN);
  else
    if ((pDDC->DCState.abnd.usSet || check_area_color(pDDC)) &&
        (FunN & COM_DRAW))
    {
      set_clip_rectangle(pDDC);
      draw_fullarc(hDC, pDDC, &pDDC->DCState.LogPosition,
                   lMultiplier, pDDC->DCState.abnd.lColor,
                   TRUE, FunN);
    }

  LeaveDriver(pDDC);
  return (Result);
}

/***************************************************************************
 *
 * FUNCTION NAME = PartialArc
 *
 * DESCRIPTION   = This draws 2 figures:
 *                   1) A straight line from current position to the
 *                      starting point of the partial arc, and
 *                   2) The arc itself, with its center at the
 *                      specified point.
 *
 *                 A sweep angle of > 360 degrees is valid.  This
 *                 means that after the line is drawn, a fullarc is
 *                 drawn followed by a partial arc with a sweep angle
 *                 of (sweep MOD 360) degrees.  The current position
 *                 is updated to the final point of the arc.
 *
 * INPUT         = hDC        - handle to device context
 *                 pWrldPts   - center of arc
 *                 lMult       - arc multiplier
 *                 StartAngle - arc start angle in degrees
 *                 SweepAngle - arc sweep angle in degrees
 *                 pDDC       - pointer to device driver cookie
 *                 FunN       - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG  PartialArc(HDC hDC,PPOINTL pWrldPts,LONG lMult,LONG StartAngle,
                            LONG SweepAngle,PDDC pDDC,ULONG FunN)
{
  LONG Result = 1L;
  PPDEVICE pPDevice;
  USHORT usPlotter;

  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  pPDevice = pDDC->pPDevice;
  usPlotter = pPDevice->Plotter;

  if (!(FunN & COM_DRAW) ||
      (FunN & (COM_AREA | COM_PATH | COM_BOUND)) ||
      pDDC->DCState.ClipComplexity > 1L ||
      pDDC->DCState.ArcParams.lR ||
      pDDC->DCState.ArcParams.lS ||
      (pDDC->DCState.ArcParams.lP != 1L) ||
      (pDDC->DCState.ArcParams.lQ != 1L) ||
      (usPlotter == CLASS_COLORPRO && !pPDevice->pSetup->bGECOption) ||
      !(PlotterClass[usPlotter].usHPGLCaps & HPGL_ARCS))
    Result = (*daPartialArc)(hDC, pWrldPts, lMult, StartAngle,
                             SweepAngle, pDDC, FunN);
  else
  {
    PPDEVICE pPDevice = pDDC->pPDevice;
    BOOL bDrawIt = FALSE;
    ARCPARAMS DevArcParams;

    SHORT Angle = (SHORT)((StartAngle *10L)/65536L),
          Sweep = (SHORT)((SweepAngle *10L)/65536L);
    // un int var??
    //LONG Radius = (LONG)HIUSHORT(Radius)+((LONG)LOUSHORT(Radius)/32768L);
    LONG Radius = (LONG)HIUSHORT(lMult)+((LONG)LOUSHORT(lMult)/32768L);

    POINTL DevPts,Pts[3],TmpPts;
    DevPts = pWrldPts[0];


    if (FunN & COM_TRANSFORM &&
        pDDC->DCState.ArcParams.lP != 1 &&
        pDDC->DCState.ArcParams.lQ != 1)
    {
      POINTL aptlArcParams[3];


      aptlArcParams[0].x = 0L;
      aptlArcParams[0].y = 0L;
      aptlArcParams[1].x = pDDC->DCState.ArcParams.lR;
      aptlArcParams[1].y = pDDC->DCState.ArcParams.lQ;
      aptlArcParams[2].x = pDDC->DCState.ArcParams.lP;
      aptlArcParams[2].y = pDDC->DCState.ArcParams.lS;

      convert_world_to_device(hDC, &aptlArcParams[0], 3L, pDDC);

      DevArcParams.lR = aptlArcParams[1].x - aptlArcParams[0].x;
      DevArcParams.lQ = aptlArcParams[1].y - aptlArcParams[0].y;
      DevArcParams.lP = aptlArcParams[2].x - aptlArcParams[0].x;
      DevArcParams.lS = aptlArcParams[2].y - aptlArcParams[0].y;
    }
    else
    {
      DevArcParams = pDDC->DCState.ArcParams;
    }

    Pts[0]   = pDDC->DCState.LogPosition;
    TmpPts.x = (long)Cosine(Angle) * Radius;
    TmpPts.y = (long)Sine(Angle) * Radius;
    Pts[1].x = DevPts.x +
       ((TmpPts.x+(TmpPts.x < 0?-(TRIG_SCALE/2):(TRIG_SCALE/2 )))/TRIG_SCALE);
    Pts[1].y = DevPts.y +
       ((TmpPts.y+(TmpPts.y < 0?-(TRIG_SCALE/2):(TRIG_SCALE/2 )))/TRIG_SCALE);
    TmpPts.x = (long)Cosine(Angle+Sweep)*Radius;
    TmpPts.y = (long)Sine(Angle+Sweep)*Radius;
    Pts[2].x = DevPts.x +
       ((TmpPts.x+(TmpPts.x < 0?-(TRIG_SCALE/2):(TRIG_SCALE/2 )))/TRIG_SCALE);
    Pts[2].y = DevPts.y +
       ((TmpPts.y+(TmpPts.y < 0?-(TRIG_SCALE/2):(TRIG_SCALE/2 )))/TRIG_SCALE);

    if (bDrawIt = (check_line_color(pDDC)&(FunN & COM_DRAW)))
    {
      set_clip_rectangle(pDDC);
      select_line_pen(pDDC, pDDC->DCState.lbnd.lColor);
      set_line_type(pDDC, pDDC->DCState.lbnd.usType);
      draw_line(pDDC, &Pts[0], &Pts[1]);
    }
    TmpPts = Pts[1];

    if (Sweep < 0)
    {
      if (Sweep < -3600)
        Sweep = -3600+(Sweep%3600);
    }
    else
    {
      if (Sweep > 3600)
        Sweep = 3600+(Sweep%3600);
    }

    if (Sweep)
    {
      if (bDrawIt)
      {
        if (pPDevice->bPenIsUp)
        {
          pPDevice->bPenIsUp = FALSE;
          output_bytes(pDDC, "PD");
        }
        output_bytes(pDDC, "AA");
        construct_point(pDDC, &DevPts, FALSE);
        output_comma(pDDC);
        construct_float(pDDC, (long)Sweep *100L, 1000L);
        Radius /= 2L;
        Radius *= (DevArcParams.lP < DevArcParams.lQ) ?
                      DevArcParams.lQ : DevArcParams.lP;
        construct_chord_tolerance(pDDC, Radius);
        pDDC->pPDevice->PhysPosition = Pts[2];
      }
      TmpPts = Pts[2];
    }
    convert_device_to_world(hDC, &TmpPts, 1L, pDDC);
    SetCurrentPosition(hDC, &TmpPts, pDDC,
                       MAKEULONG(NGreSetCurrentPosition, HIUSHORT(FunN)));
  }

  LeaveDriver(pDDC);
  return (Result);
}

/***************************************************************************
 *
 * FUNCTION NAME = SetArcParameters
 *
 * DESCRIPTION   = This sets the arc parameters for subsequent arc,
 *                 fullarc, partialarc functions.  Note:  this
 *                 function must call the engine because it also needs
 *                 to know the arc parameters as of v4.16.
 *
 * INPUT         = hDC        - handle to device context
 *                 pParams    - arc parameters
 *                 pDDC       - pointer to device driver cookie
 *                 FunN       - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************/

LONG  SetArcParameters(HDC hDC,PARCPARAMS pParams,PDDC pDDC,ULONG
                                  FunN)
{
  ULONG ulRet;

  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  pDDC->DCState.ArcParams = *pParams;
  ulRet = (*daSetArcParameters)(hDC, pParams, pDDC, FunN);
  LeaveDriver(pDDC);
  return(ulRet);
}

/***************************************************************************
 *
 * FUNCTION NAME = PolySpline
 *
 * DESCRIPTION   = Draws a sequence of one or more Bezier splines
 *                 starting at the Current Polition.  As each spline
 *                 is drawn, the specified end point of the spline
 *                 becomes the start point for the next spline.
 *                 Upon completion, the current position is the
 *                 end point of the last spline.
 *
 *
 * INPUT         = hDC           - handle to Device Context
 *                 paptlWorldPts - points to draw lines through (in world)
 *                 lcPoints      - number of points
 *                 pDDC          - pointer to device driver cookie
 *                 ulFunN        - function number
 *
 * NOTE          = Function will not be called for memory DC because
 *                 it is unhooked in dispatch.c hook_unhook
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 **************************************************************************/
#define STK_ARRAY_SZ  30           /* Size of ptl array on stack.*/
                                   /* We use the stack array for */
                                   /* converting small arrays    */
                                   /* for speed.                 */
LONG  PolySpline(HDC hDC, PPOINTL paptlWorldPts, LONG lcPoints, PDDC pDDC,
               ULONG ulFunN)
{
  LONG   lIndex;
  LONG   lResult = GPI_OK;
  BOOL   bMemAlloc = FALSE;
  PPDEVICE pPDevice = pDDC->pPDevice;
  POINTL aptlOnStack[STK_ARRAY_SZ];


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  ** if the device does not have Bezier support or
  ** we are in a area, path or accumulation alt bounds or
  ** the clipping is complex ---
  ** Call the engine
  */
  if ( !(pPDevice->usHPGLType & HPGL2_BEZIER) ||
      (ulFunN & (COM_AREA | COM_PATH | COM_ALT_BOUND)) ||
       pDDC->DCState.ClipComplexity > 1)
  {
    /*
    **  Engine can do this one
    **  - if in a path the engine calls  DrawLinesInPath
    **    with clipped lines
    */
    lResult = (*daPolySpline)(hDC, paptlWorldPts, lcPoints, pDDC, ulFunN);
  }
  else
  {
    /*
    ** Make sure the lcPoints is a multiple of 3. (3 points for each
    ** Bezier spline) Starting point is the current position
    ** or the last position of the previous spline.
    **
    ** This check is important because the plotter will ignore odd points
    ** which will get our current position out of sync.
    */
    lcPoints = lcPoints / 3 * 3;

    if (lcPoints)
    {
      PPOINTL paPtl = paptlWorldPts;


      if (ulFunN & COM_TRANSFORM)
      {
        /*
        ** if the the points will fit on our stack array
        ** use the stack space for converting the points
        */
        if (lcPoints < STK_ARRAY_SZ)
        {
          paPtl = &aptlOnStack[0];
        }
        /*
        ** else alloc the memory
        */
        else
        {
          if (paPtl = (PPOINTL)GplMemoryAlloc(pDDC->pPDevice->hmcbHeap,
                                                sizeof(POINTL)*lcPoints))
             bMemAlloc = TRUE;
          else
            lResult = GPI_ERROR;        /* Failure                           */
        }

        if (paPtl)
        {
          /*
          **    The data is supplied in world coordinates,  so transform
          **  to device.  Copy data to local buffer,  then transform the
          **  copy.
          */
          // use CopyMem after fixing CopyMem to handle ULONG copy
          // also fix up PolyLine

          CopyMem(paPtl, paptlWorldPts, (lcPoints * sizeof(POINTL)));

          //LONG lLoop;
          //
          //
          //for (lLoop = 0; lLoop < lcPoints; lLoop++)
          //  paPtl[lLoop] = paptlWorldPts[lLoop];

          convert_world_to_device(hDC, paPtl, lcPoints, pDDC);
        }
      }

      if (paPtl)
      {
        POINTL ptlTemp;
        /*
        **  Have some data - either user supplied or locally transformed
        */
        if (ulFunN & COM_BOUND)
        {

          ptlTemp = pDDC->DCState.LogPosition;

          for (lIndex = 0; lIndex < lcPoints; lIndex++)
          {
            accumulate_line_bounds(hDC, pDDC, &ptlTemp, &paPtl[lIndex]);
            ptlTemp = paPtl[lIndex];
          }
        }

        if ( (ulFunN & COM_DRAW) &&
             (pDDC->DCState.bFilling ? check_area_color(pDDC) :
                                       check_line_color(pDDC)) )
        {
          /*
          **  Drawing,  and the desired pen color is available
          */
          if (pDDC->DCState.bFilling)
            select_fill_pen(pDDC, pDDC->DCState.abnd.lColor);
          else
            select_line_pen(pDDC, pDDC->DCState.lbnd.lColor);

          set_line_type(pDDC, pDDC->DCState.lbnd.usType);

          /*
          ** draw splines in order received, starting at current position
          */
          move_pen(pDDC, &pDDC->DCState.LogPosition, FALSE);
          if (pPDevice->bPenIsUp)
          {
            output_bytes(pDDC, "PD");
            pPDevice->bPenIsUp = FALSE;
          }
          output_bytes(pDDC, "BZ");   // Bezier Absolute
          pPDevice->bPlotRelative = FALSE;

          for (lIndex = 0; lIndex < lcPoints; lIndex++)
          {
            /*
            ** output a comma if this is not the first point
            */
            if (lIndex)
              output_comma(pDDC);

            /*
            ** output each point
            ** We move the point to a temp buffer because
            ** construct_point may modify the point
            */
            ptlTemp = paPtl[lIndex];
            construct_point( pDDC, &ptlTemp, FALSE );
          }
        }

        /*
        **  If we allocated memory,  free it now
        */
        if (bMemAlloc)
        {
          if (GplMemoryFree ((PVOID)paPtl))
            paPtl = (PPOINTL)NULL;
        }

        SetCurrentPosition(hDC, &paptlWorldPts[lcPoints-1], pDDC,
                           MAKEULONG(NGreSetCurrentPosition, HIUSHORT(ulFunN)));
      }
    }
  }

  LeaveDriver(pDDC);
  return  lResult;
}
#undef STK_ARRAY_SZ
