/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = ARC.C
 *
 * DESCRIPTIVE NAME = Contains arc drawing routines.
 *
 *
 * VERSION = V2.0
 *
 * DATE        09/16/88
 *
 * DESCRIPTION This module contains PartialArc, Arc, FullArcBoundary,
 *             FullArcInterior, FullArcBoth, and support routines.  Several
 *             ASM routines are called externally, and are located in ARC.ASM
 *             and CIRCCTR.ASM.
 *
 *
 *
 *
 *
 *
 *
 * FUNCTIONS   prdl_PartialArc
 *             find_arc_direction
 *             prdl_FullArcBoundary
 *             prdl_FullArcInterior
 *             prdl_FullArcBoth
 *             prdl_FullArcCommon
 *             prdl_FullArcPath
 *             prdl_Arc
 *             CheckLinearPoints
 *             prdl_GetArcParameters
 *             prdl_SetArcParameters
 *
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define  INCL_GPIERRORS
#include "inc\prdinclt.h"
#include "inc\utl.h"
#include "inc\prdlextf.h"
#include "inc\prdmath.h"
#include "inc\pspagtun.h"              /* V2.174057   Page Tuning */
#define  INCL_GENPLIB_ERROR
#include <genplib.h>

#if      0
typedef struct _tagPOINTLABC           /* room for the three Arc points  */
{
  POINTL ptl_a;
  POINTL ptl_b;
  POINTL ptl_c;
} POINTLABC;

  #define  MATRIX_SINGULAR 0x40
  #define  FIXED_255     (LONG)255 << 16
  #define  FIXED_360     (LONG)360 << 16

/*
** array of sharpness values
*/

static FIXED afx[5] =
{
  FX_ROOTHALF,    FX_ROOTHALF,    FX_ROOTHALF,    FX_ROOTHALF,    FX_ROOTHALF
} ;

extern BOOL   CopyNormalizeAndShift(XFORM  *,POINTL  *);
void   find_arc_direction(HDC,PDDC);
#endif
ULONG   prdl_FullArcPath(HDC,FIXED,PDDC);
ULONG   prdl_FullArcCommon(HDC,FIXED,LONG,PDDC,ULONG);

#if      0
ULONG   CheckLinearPoints(PPOINTL,PSHORT);

extern PDDC  EnterDriver(PDDC);
extern VOID  ExitDriver(PDDC);
extern void   _System CopyAndNormalize(XFORM  *,MATRIX  *);
extern BOOL   FindInverseArcMatrix(MATRIX  *,MATRIX  *);
extern BOOL   LocateCircleCenter(POINTL  *,POINTL  *);
extern BOOL   TransformPoints(MATRIX  *,POINTL  *,SHORT);
extern ULONG   find_vector_length(POINTL  *);
extern SHORT   WalkTheCircle(POINTL  *,ULONG,SHORT);
extern BOOL   FindAllPoints(POINTL  *,FIXED  *,ULONG,SHORT);
extern void   FindNewCP(FIXED,FIXED,FIXED,POINTFX  *);
extern void   MakeMMatrix(FIXED,SHORT  *);
extern ULONG   UnNormalize(ULONG,SHORT);
extern ULONG   prdl_PolyFilletSharp(HDC,PPOINTL,LONG,FIXED  *,PDDC
                                              ,ULONG);

/***************************************************************************
 *
 * FUNCTION NAME = prdl_PartialArc
 *
 * DESCRIPTION   = Draws two figures:
 *
 *                 1. A straight line, from current position to
 *                    the starting point of a partial arc, and
 *                 2. The arc itself, with its center at the
 *                    specified point.
 *
 *                 The full arc, of which the arc is a part, is
 *                 identical to that defined by GpiFullArc.  The
 *                 part of the arc drawn by this primitive is
 *                 defined by the parameters start and sweep,
 *                 which are the start and sweep angles,
 *                 respectively, subtended from the center, if
 *                 the current arc parameters specify a circular
 *                 form.  If they do not, these angles are
 *                 skewed to the same degree that the ellipse is
 *                 a skewed circle.  Start is measured
 *                 anticlockwise from the x-axis of the circle
 *                 prior to the application of the arc
 *                 parameters.
 *
 * INPUT         = (hdc, pptl, fxMult, fxStart, fxSweep, pddc, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = ps_status(pddc)
 *
 * RETURN-ERROR  = FAILURE
 *
 **************************************************************************/

ULONG prdl_PartialArc( HDC hdc, PPOINTL pptl, FIXED fxMult, FIXED fxStart, FIXED fxSweep, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle  */
 /* PPOINTL pptl;                         Ptr to center point of the arc  */
 /* FIXED fxMult;                         The arc radius  */
 /* FIXED fxStart;                        The starting angle  */
 /* FIXED fxSweep;                        The sweep angle  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{

/*
** !!!CR add bounds
*/
  FIXED   fxStopAng;
  XFORM   xformMatrix;
  MATRIX  MMatrix;
  POINTL  ptlCP;                     /* place to store new current position  */
  POINTL  ptlCP2;                   /* place to store temp current position  */
  POINTFX ptlfxCP;
  POINTFX ptlfxCP2;

  #if      DEBUG
  LogCall((PSZ)
     "prdl_PartialArc(%08lx, %lp, %08lx, %08lx, %08lx, %lp, %08lx)\n", ((PB)
     &hdc)+sizeof(hdc));
  #endif

  PrintLog( (PSZ) "prdl_PartialArc: arc center {%ld, %ld}\n", pptl->x, pptl->y );
  PrintLog( (PSZ) "    radius %f  start %f  sweep %f\n", fxMult, fxStart, fxSweep );

  /*
  ** check for invalid multiplier.  the spec currently states that
  ** the multiplier has a limit of 255, and a negative multiplier
  ** makes no sense.
  */
  if ((fxMult < 0L) || (fxMult > FIXED_255))
  {
    GplErrSetError(  PMERR_INV_MULTIPLIER );
    return (FAILURE);
  }

  if ((fxStart > ((LONG)360 << 16)) || (fxStart < ((LONG) -360 << 16)))
  {
    GplErrSetError(  PMERR_INV_ANGLE_PARM);
    return (FAILURE);
  }

  /*
  ** the current position will have to be updated to point to the point
  ** on the arc corresponding to the ending angle.  find that point and
  ** store it in ptlCP.
  */
  FindNewCP( fxStart, fxSweep, fxMult, &ptlfxCP );

  /*
  ** find the position of the starting point of the angle.  we need this
  ** point as a kludge to get around a postscript problem.  if we do an
  ** arc call with the current point defined as the same point as the arc
  ** starts at, the postscript has problems when drawing wide lines.
  */
  FindNewCP( fxStart, 0L, fxMult, &ptlfxCP2 );

  /*
  ** due to the fact that our transform code can only transform
  ** POINTLS.  In order to keep accuracy, we will perform the
  ** transformation twice.  Once with the integer part and once
  ** with the fraction part.  Once we have transformed the fract
  ** part, shift it right by 16 bits and add it to the transformed
  ** integer part.  We cannot simply transform the whole fixed
  ** point number and shift it right by 16 bits.  This is because
  ** the transform of the integer part could be greater than 32K
  ** and we would lose that part in the shift.
  */

  ptlCP.x    = (ptlfxCP.x >> 16);
  ptlCP.y    = (ptlfxCP.y >> 16);
  ptlfxCP.x  = (ptlfxCP.x & 0xFFFF);      /* clear the hi word  */
  ptlfxCP.y  = (ptlfxCP.y & 0xFFFF);
  ptlCP2.x   = (ptlfxCP2.x >> 16);
  ptlCP2.y   = (ptlfxCP2.y >> 16);
  ptlfxCP2.x = (ptlfxCP2.x & 0xFFFF);    /* clear the hi word  */
  ptlfxCP2.y = (ptlfxCP2.y & 0xFFFF);

  /*
  ** set up to transform the final current position by the
  ** arc parameters.  this transformation will stretch the
  ** arc from a circle into whatever shape it is defined as
  */
  xformMatrix.fxM11 = LongToFx( pddc->pddcb->arc.arcparams.lP );
  xformMatrix.fxM12 = LongToFx( pddc->pddcb->arc.arcparams.lS );
  xformMatrix.fxM21 = LongToFx( pddc->pddcb->arc.arcparams.lR );
  xformMatrix.fxM22 = LongToFx( pddc->pddcb->arc.arcparams.lQ );
  xformMatrix.lM41 = pptl->x;
  xformMatrix.lM42 = pptl->y;

  /*
  ** CopyAndNormalize copies the transform information from an
  ** XFORM structure into a MATRIX structure, and fills in some
  ** extra accelerator type information. Then transform the new
  ** current position.
  */
  CopyAndNormalize( &xformMatrix, &MMatrix );

  if (!TransformPoints( &MMatrix, &ptlCP, 1 ))
  {
    GplErrSetError(  PMERR_COORDINATE_OVERFLOW );
    return (FAILURE);
  }

  /*
  ** transform the fractional part of the new current position
  ** then shift it right by 16 bits and add it to the integer
  ** part of the current position.
  */
  if (!TransformPoints( &MMatrix, (PPOINTL) &ptlfxCP, 1 ))
  {
    GplErrSetError(  PMERR_COORDINATE_OVERFLOW);
    return (FAILURE);
  }
  ptlCP.x += ptlfxCP.x >> 16;
  ptlCP.y += ptlfxCP.y >> 16;

  if (!TransformPoints( &MMatrix, &ptlCP2, 1 ))
  {
    GplErrSetError(  PMERR_COORDINATE_OVERFLOW);
    return (FAILURE);
  }

  /*
  ** transform the fractional part of the new current position
  ** then shift it right by 16 bits and add it to the integer
  ** part of the current position.
  */
  if (!TransformPoints( &MMatrix, (PPOINTL) &ptlfxCP2, 1 ))
  {
    GplErrSetError(  PMERR_COORDINATE_OVERFLOW);
    return (FAILURE);
  }
  ptlCP2.x += ptlfxCP2.x >> 16;
  ptlCP2.y += ptlfxCP2.y >> 16;

  /*
  ** if the draw bit is not on, then all we want to do is
  ** update the current position, without drawing anything
  */
  #if      DEBUG

    if (pddc->pddcb->pen.usFgMix == FM_LEAVEALONE)
    {
      PrintLog((PSZ)"PartialArc: FgMix = FM_LEAVEALONE\n");
    }
  #endif

  if (!(FunN&COM_DRAW))
  {
    goto partarc_newcp;
  }

  /*
  ** we are passed in a sweep angle.  we need to find the corresponding
  ** ending angle to pass to PostScript.
  */
  fxStopAng = fxSweep + fxStart;
  ps_movetoCP( pddc );
  ps_savematrix( pddc );                 /* save current matrix  */

  /*
  ** transform the arc by the arc parameters
  */
  PrintChannel( pddc, (PSZ)"[%ld %ld %ld %ld %ld %ld] concat\n",
                pddc->pddcb->arc.arcparams.lP, pddc->pddcb->arc.arcparams.lS,
                pddc->pddcb->arc.arcparams.lR, pddc->pddcb->arc.arcparams.lQ, pptl->x,
                pptl->y );

  if (fxSweep < 0)
  {
    PrintChannel( pddc, (PSZ)"0 0 %f %f %f arcn\n", fxMult, fxStart, fxStopAng );
  }
  else
  {
    PrintChannel( pddc, (PSZ)"0 0 %f %f %f arc\n", fxMult, fxStart, fxStopAng );
  }

  /*
  ** restore the transform matrix
  */
  ps_restorematrix( pddc );

  /*
  ** indicate the current path contains curves.
  */
  pddc->pddcb->cgs.fCurvesInPath = TRUE;

  /*
  ** Check to see if the path should be stroked immediatly.  If not
  ** then the path is left intact for COM_PATH or COM_AREA.
  */
  if (!pddc->pddcb->path.fPathOrArea)
  {
    ps_strokecurrentpath( pddc );
  }

  /*
  ** set the current position to that of ptlCP
  */
partarc_newcp:

  ps_sync_cp( pddc, &ptlCP );
  return (ps_status( pddc ));
}

#endif

/***************************************************************************
 *
 * FUNCTION NAME = find_arc_direction
 *
 * DESCRIPTION   = finds the direction the arc should be drawn.  this is
 *                 dependent on the arc parameters.  if p * q > r * s the
 *                 arc is drawn counter clockwise, otherwise it is drawn
 *                 clockwise.
 *
 *                  Sets pddc->pddcb->arc.usfCW to FALSE if counterclockwise
 *                  TRUE otherwise
 *
 * INPUT         = hdc, pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void find_arc_direction( HDC hdc, PDDC pddc )
{
  ULONG     ulProduct1;
  ULONG     ulProduct2;
  ARCPARAMS ArcParams;

  /*
  ** get the arc parameters.
  */
  GreGetArcParameters( hdc, (ARCPARAMS *) &ArcParams.lP );

  /*
  ** if p * q > r * s, the arc is drawn counterclockwise,
  ** otherwise it is drawn clockwise
  */
  ulProduct1 = (ArcParams.lP * ArcParams.lQ);
  ulProduct2 = (ArcParams.lR * ArcParams.lS);

  if (ulProduct1 > ulProduct2)
  {
    /*
    ** Indicate counterclockwise
    */
    pddc->pddcb->arc.usfCW = FALSE;
  }
  else
  {
    /*
    ** Indicate clockwise.
    */
    pddc->pddcb->arc.usfCW = TRUE;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_FullArcBoundary
 *
 * DESCRIPTION   = Draws a full arc centered at the current position.
 *                 The current position is left unchanged.  Only the
 *                 boundary of the arc is drawn
 *
 * INPUT         = (hdc, M, pddc, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS , ps_status(pddc)
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdl_FullArcBoundary( HDC hdc, FIXED M, PDDC pddc, ULONG FunN )

 /* HDC hdc;                              The display context handle  */
 /* FIXED M;                              the multipler  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{
  POINTL ptlCP;                   /* a place to store the current position  */
  ULONG  tl;

EnterDriver( pddc );

#if      DEBUG
  if (pddc->pddcb->pen.usFgMix == FM_LEAVEALONE)
  {
    PrintLog( (PSZ) "FullArcBoundary: FgMix = FM_LEAVEALONE\n" );
  }
#endif

  /*
  ** check to see if we should do anything
  */
  if (!(FunN &COM_DRAW))
  {
    ExitDriver( pddc );
    return (SUCCESS);                  /* do nothing if DRAW bit not set  */
  }

  /*            */
  /*
  ** This will make the current path empty and create a newpath
  ** in all cases except where a path or area already exists,
  ** and at least one fullarc has already been put into the path or area.
  ** This is O.K. as long as there is no subpath other than one full arc
  ** has been put into the existing path, but if there is another subpath
  ** for example a box is in the current path, then we will lose the box.
  ** Hence, we cannot start a new path.
  **
  ** fullarcs cannot have a current point set in the printer
  ** this is a special case to handle the postscript arc
  ** command.  this seems kindda kludgey, but what we are
  ** doing here is outputting a newpath in all cases except
  ** where a path or area already exists, and at least one
  ** fullarc has already been put into the path or area.
  **
  **if ((pddc->pddcb->path.fPathOrArea == FALSE) ||
  **    (pddc->pddcb->cgs.fFullArcInPath == FALSE))
  **    ps_newpath(pddc);
  **
  ** The arc in Postscript includes a straight line segment
  ** from the current point to the fisrt point of the of the arc
  ** unless the current path is empty.
  ** Since we cannot make the current path empty, we have
  ** to move the current point to the start of the arc before
  ** drawing the arc.
  ** This moving is done in the function
  ** "prdl_FullArcPath" to take care of the scaling and skewing.
  ** a fullarc call must maintain the current position, so save
  ** it now so we can restore it later.
  */

  ptlCP.x = pddc->pddcb->pen.ptlCur.x;
  ptlCP.y = pddc->pddcb->pen.ptlCur.y;

  /*
  ** add the fullarc to the path
  */
  if (!prdl_FullArcPath( hdc, M, pddc ))
  {
    ExitDriver( pddc );
    return (FAILURE);
  }

  /*
  ** draw the fullarc, unless we are part of a larger path
  */
  if (!pddc->pddcb->path.fPathOrArea)
  {
    ps_strokecurrentpath( pddc );
  }
  else
  {
    pddc->pddcb->cgs.fFullArcInPath = TRUE;
  }
  ps_moveto( pddc, &ptlCP );
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return (tl);
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_FullArcInterior
 *
 * DESCRIPTION   = Draws a full arc centered at the current position.
 *                 The current position is left unchanged.  Only the
 *                 interior of the arc is drawn
 *
 * INPUT         = hdc, M, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = prdl_FullArcCommon(hdc, M, BA_NOBOUNDARY, pddc, FunN)
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdl_FullArcInterior( HDC hdc, FIXED M, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle  */
 /* FIXED M;                              the multipler  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{
  ULONG tl;

  EnterDriver( pddc );

  tl = prdl_FullArcCommon( hdc, M, BA_NOBOUNDARY, pddc, FunN );

  ExitDriver( pddc );

  return (tl);
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_FullArcBoth
 *
 * DESCRIPTION   = Draws a full arc centered at the current position.
 *                 The current position is left unchanged.  The
 *                 interior and boundary are drawn
 *
 * INPUT         = hdc, M, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = prdl_FullArcCommon(hdc, M, BA_BOUNDARY, pddc, FunN)
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdl_FullArcBoth( HDC hdc, FIXED M, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle  */
 /* FIXED M;                              the multipler  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{
  ULONG tl;

  EnterDriver( pddc );

  tl = prdl_FullArcCommon( hdc, M, BA_BOUNDARY, pddc, FunN );

  ExitDriver( pddc );

  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_FullArcCommon
 *
 * DESCRIPTION   = Draws a full arc centered at the current position.
 *                 The current position is left unchanged.  Only the
 *                 interior of the arc is drawn
 *
 * INPUT         = (hdc, M, lBoundary, pddc, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = ulCancel
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdl_FullArcCommon( HDC hdc, FIXED M, LONG lBoundary, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle  */
 /* FIXED M;                              the multipler  */
 /* LONG lBoundary;                       boundary drawing flag  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{
  POINTL ptlCP;                       /* a place to store current position  */
  ULONG  ulCancel;

  /*
  ** this call is illegal if a path or area is already defined
  */
  if (pddc->pddcb->path.fPathOrArea)
  {
    GplErrSetError(  PMERR_INV_NESTED_FIGURES);
    return (FAILURE);
  }

  #if      DEBUG
    if (pddc->pddcb->pen.usFgMix == FM_LEAVEALONE)
    {
      PrintLog( (PSZ)"FullArcCommon: FgMix = FM_LEAVEALONE\n" );
    }
  #endif

  /*
  ** check to see if we should do anything
  */
  if (!(FunN & COM_DRAW))
  {
    return (SUCCESS);                  /* do nothing if DRAW bit not set  */
  }

  /*
  ** a fullarc call must maintain the current position, so save
  ** it now so we can restore it later.
  */
  ptlCP.x = pddc->pddcb->pen.ptlCur.x;
  ptlCP.y = pddc->pddcb->pen.ptlCur.y;

  /*
  ** begin area definition, with filling only
  */
  if (!prdl_BeginArea(hdc, lBoundary, pddc, FunN))
  {
    return (FAILURE);
  }

  /*            */

  /*
  ** The arc in Postscript includes a straight line segment
  ** from the current point to the fisrt point of the of the arc
  ** unless the current path is empty.
  ** Since the function "prdl_FullArcPath" is modified
  ** to move to the start of the arc before drawing,
  ** we need not start a new path here.
  **
  ** output a newpath.  this will ensure we do not have a current
  ** position.  this is to make sure we do not get extraneous lines
  ** from the current position to the arc.
  **PrintChannel(pddc, (PSZ)"n ");
  ** add the fullarc to the path.  save the return code
  ** in case we have an error, so we can cancel the area
  ** fullarccommon returns 0 for error, 1 for ok.  by
  ** XORing ulCancel with EA_CANCEL, we set the correct
  ** value.
  */
  ulCancel = prdl_FullArcPath( hdc, M, pddc );

  /*
  ** end the path definition and draw the interior of the arc
  */
  if (!prdl_EndArea(hdc, (ULONG) (ulCancel ^ EA_CANCEL), pddc, FunN))
  {
    return (FAILURE);
  }

  /*
  ** restore the current position.  We want to do a SetCurrentPosition
  ** here rather than an UpdateCurrentPosition, because this routine
  ** has to preserve the current position.  Therefore, we force the
  ** current position in the printer back to where it was.
  */
  ps_moveto( pddc, &ptlCP );
  return (ulCancel);
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_FullArcPath
 *
 * DESCRIPTION   = The guts of the FullArc routines.  All
 *                 the work which sends the arc drawing
 *                 commands to postscript are in this
 *                 routine.
 *
 * INPUT         = (hdc, M, pddc)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (pddc)
 *
 * RETURN-ERROR  = FAILURE
 *
 **************************************************************************/

ULONG prdl_FullArcPath( HDC hdc, FIXED M, PDDC pddc )
 /* HDC hdc;                              The display context handle  */
 /* FIXED M;                              the multipler  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{
  ARCPARAMS ArcParams;

  /*
  ** get the arc parameters.
  */
  if (!GreGetArcParameters( hdc, (ARCPARAMS *) &ArcParams.lP ))
  {
    return (FAILURE);
  }
  PrintLog( (PSZ)"prdl_FullArcPath: multiplier = %f\n", M );

  /*
  ** make sure we have a positive multiplier
  */
  if (M < 0L)
  {
    GplErrSetError(  PMERR_INV_MULTIPLIER);
    return (FAILURE);
  }

  /*
  ** the arc parameters define which direction we draw the arc
  ** clockwise or counter-clockwise.
  */
  find_arc_direction( hdc, pddc );

  /*
  ** let everyone know we have a path in progress.
  */
  pddc->pddcb->cgs.fPathExists = TRUE;

  /*
  ** save current xform matrix
  */
  ps_savematrix( pddc );

  /*
  ** transform the arc by the arc parameters.  translate arc by the
  ** the current position, which is the center of the arc.
  */
  PrintChannel( pddc, (PSZ)"[%ld %ld %ld %ld %ld %ld] concat\n", ArcParams.lP,
                ArcParams.lS, ArcParams.lR, ArcParams.lQ, pddc->pddcb->pen.ptlCur.x,
                pddc->pddcb->pen.ptlCur.y );

  /*            */
  /*
  ** The arc in Postscript includes a straight line segment
  ** from the current point to the fisrt point of the of the arc
  ** unless the current path is empty.
  ** Since we cannot make the current path empty, we have
  ** to move the current point to the start of the arc before
  ** drawing the arc.
  **
  ** Move to the start of the arc.
  ** Do not use "m", because m will do "_snap moveto"
  ** _snap will transform and invert transform. itransform
  ** is not valid if the determenent of the transform matrix
  ** is equal to zero (example: The arc degenerates to a line).
  */
  PrintChannel( pddc, (PSZ)"%f 0 moveto ", M );

  /*
  ** add the fullarc definition to the path or area
  */
  PrintChannel( pddc, (PSZ)"0 0 %f 0 360 arc\n", M );

  /*
  ** restore the transform matrix
  */
  ps_restorematrix( pddc );

  /*
  ** indicate the current path contains curves.
  */
  pddc->pddcb->cgs.fCurvesInPath = TRUE;
  PrintLog( (PSZ) "Leaving prdl_FullArcPath\n" );
  return (ps_status(pddc));
}

#if      0

 /*
 ** !!!CR give a more general description of algorithm used
 */
/***************************************************************************
 *
 * FUNCTION NAME = prdl_Arc
 *
 * DESCRIPTION   = Give a current position A, and two points passed
 *                 in, B, C, this function creates an arc defined
 *                 by these points and the current arc parameters.
 *                 Points A, B and C sit on the arc.  Current
 *                 position is updated to the third of the three
 *                 points.
 *
 * INPUT         = (hdc, pPoints, pddc, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = prdl_PolyLine(hdc, &ArcPoints.ptl_b, 2L, pddc, FunN)
 *
 * RETURN-ERROR  = FAILURE
 *
 **************************************************************************/

ULONG prdl_Arc( HDC hdc, POINTL  *pPoints, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle  */
 /* POINTL  *pPoints;                  the arc points  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{
  POINTLABC ArcPoints;
  POINTL    aptl[11];
  FIXED     afxSharpness[5];
  POINTL    CenterPoint;
  ULONG     ulRadius1;
  SHORT     usPointCount, direction, i;
  XFORM     xformMatrix;
  MATRIX    MMatrix;

  #if      DEBUG
    LogCall( (PSZ) "prdl_Arc(%08lx, %lp, %lp, %lp)", ((PB)&hdc)+sizeof(hdc) );
  #endif

  /*
  ** make sure we have non-singular arc parameters
  */
  if (pddc->pddcb->arc.invarcpmatrix.mx_flags & MATRIX_SINGULAR)
  {
    GplErrSetError(  PMERR_INV_MATRIX_ELEMENT);
    return (FAILURE);
  }

  /*
  ** get the three arc points together in ArcPoints
  ** Error should be saved by GetCP
  */
  if (!prdl_GetCurrentPosition( hdc, &ArcPoints.ptl_a, pddc, FunN) )
  {
    return (FAILURE);
  }
  ArcPoints.ptl_b = pPoints[0];
  ArcPoints.ptl_c = pPoints[1];
  direction = 0;

  /*
  ** check to see if we have three different, non_linear points
  ** if not, we will just draw a polyline.
  ** CheckLinearPoints set pddc->pddcb->arc.usfCW if we have non-linear
  ** points.
  */
  if (!CheckLinearPoints( (POINTL *)&ArcPoints, (PSHORT) &direction))
  {
    return (prdl_PolyLine( hdc, &ArcPoints.ptl_b, 2L, pddc, FunN ));
  }
  pddc->pddcb->arc.usfCW = direction;

  /*
  ** transform the points to the circle. save a copy of the points first
  */
  aptl[0] = ArcPoints.ptl_a;
  aptl[1] = ArcPoints.ptl_b;
  aptl[2] = ArcPoints.ptl_c;

  if (!TransformPoints( &pddc->pddcb->arc.invarcpmatrix, &aptl[0], 3) )
  {
    goto arc_overflow;
  }

  /*
  ** find the center of the circle
  */
  if (!LocateCircleCenter( &aptl[0], &CenterPoint) )
  {
    goto arc_overflow;
  }

  /*
  ** translate circle to be centered at the origin.  we no longer
  ** need point B, so it will not be translated.
  */
  aptl[0].x -= CenterPoint.x;
  aptl[0].y -= CenterPoint.y;
  aptl[2].x -= CenterPoint.x;
  aptl[2].y -= CenterPoint.y;

  /*
  ** find the radius of the circle.  the radius is needed when so
  ** we can walk around the circle looking for control points.
  ** since the circle is centered at the origin, the radius is
  ** SQRT(ptl_x * ptl_x + ptl_y * ptl_y).  because of inaccuracies
  ** in the FindCenterOfCircle routine, the radius is calculated
  ** for both points A and C and the greater is used.
  */
  ulRadius1 = max( find_vector_length( &aptl[0] ), find_vector_length( &aptl[2] ) );

  /*
  ** now walk around the circle looking for control points which
  ** lie on the circle.  start at point A and walk to point C.
  ** see WalkCircle in ARC.ASM for details.
  */
  usPointCount = WalkTheCircle( &aptl[0], ulRadius1, direction );

  /*
  ** WalkTheCircle returns -1 if there was an overflow error
  */
  if (usPointCount == -1)
  {
    goto arc_overflow;
  }

  /*
  ** we have now walked the circle and found all control points
  ** on the circle.  now we will find all control points which
  ** lie off of the circle.
  */
  if (!FindAllPoints(&aptl[0], &afxSharpness[0], ulRadius1, usPointCount))
  {
    goto arc_overflow;
  }

  /*
  ** translate points back to previous origin
  */
  for (i = 0 ; i < usPointCount ; i++)
  {
    aptl[i].x += CenterPoint.x;
    aptl[i].y += CenterPoint.y;
  }

  /*
  ** transform the center point back to the arc.
  */
  if (!TransformPoints( &pddc->pddcb->arc.arcparmmatrix, &aptl[0], usPointCount ))
  {
    goto arc_overflow;
  }

  /*
  ** shove the original points back in, the transforms may have errors;
  */
  aptl[0] = ArcPoints.ptl_a;
  aptl[usPointCount - 1] = ArcPoints.ptl_c;

  /*
  ** output each of the arc sections
  ** call PolyFilletSharp with the points.
  */
  if (!prdl_PolyFilletSharp( hdc, &aptl[1], (LONG) (usPointCount - 1),
                             &afxSharpness[0], pddc, FunN ))
  {
    return (FAILURE);
  }

  /*
  ** indicate the current path contains curves.
  */
  pddc->pddcb->cgs.fCurvesInPath = TRUE;

  /*
  ** set the current position to that of point C
  */
  ps_sync_cp( pddc, &ArcPoints.ptl_c );
  return( ps_status( pddc ));

arc_overflow:

  GplErrSetError(  PMERR_COORDINATE_OVERFLOW );
  return (FAILURE);
}

/***************************************************************************
 *
 * FUNCTION NAME = CheckLinearPoints
 *
 *                 Given three points, A, B, C, which lie on an
 * DESCRIPTION   = arc, we can determine the direction the arc is
 *                 to be drawn in the following manner:
 *
 *                 If the z component of (B - A) X (C - B) > 0 then the arc is drawn
 *                 counterclockwise.  If (B - A) X (C - B) < 0 then the arc is drawn
 *                 clockwise.  Otherwise the points are coincident, or linear.
 *
 *                 We will define U = (B - A) and V = (C - B).  (U X V) then becomes
 *                 (U2V3 -U3V2, U3V1 - U1V3, U1V2 - U2V1).  We only care about the z
 *                 component.  So we must calculate (U1V2 - U2V1).
 *
 *                 If the points are coincident or collinear, we will return FALSE.
 *                 Otherwise we will return TRUE, and we will set pddc->pddcb->arc.usfCW.
 *
 * INPUT         = (pptl, pddc)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = FALSE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG CheckLinearPoints( PPOINTL pptl, PSHORT pusDirection )
{
  POINTL ptlU;
  POINTL ptlV;
  QUAD   qCrossProd;
  QUAD   qCrossProd2;

  /*
  ** calculate U = (B - A) and V = (C - B).
  */
  ptlU.x = pptl[1].x - pptl[0].x;
  ptlU.y = pptl[1].y - pptl[0].y;
  ptlV.x = pptl[2].x - pptl[1].x;
  ptlV.y = pptl[2].y - pptl[1].y;

  /*
  ** calculate the z component of (U X V) = U1V2 - U2V1.
  */
  BigMul( ptlU.x, ptlV.y, (QUAD *) &qCrossProd );
  BigMul( ptlU.y, ptlV.x, (QUAD *) &qCrossProd2 );
  QuadSubtract( qCrossProd, qCrossProd2, (QUAD *) &qCrossProd );

  /*
  ** return FALSE if cross product == 0.
  */
  if ((qCrossProd.qlo == 0) && (qCrossProd.qhi == 0))
  {
    return (FALSE);
  }

  /*
  ** set pddc->pddcb->arc.usfCW properly.
  */
  if (qCrossProd.qhi & 0x1000)           /* negative if hi bit set.  */
  {
    *pusDirection = TRUE;
  }
  else
  {
    *pusDirection = FALSE;
  }
  return (TRUE);
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_GetArcParameters
 *
 * DESCRIPTION   = Get the current arc parameters from the
 *                 pddc and return them in pArcParms.
 *
 * INPUT         = (hdc,pArcParms,pddc,FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdl_GetArcParameters( HDC hdc, PARCPARAMS pArcParms, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle  */
 /* PARCPARAMS pArcParms;                 dest for the arc parameters  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{

  /*
  ** copy the arcparameters from the ddc to the destination buffer
  */
  utl_memcopy( (PSZ) pArcParms, (PSZ)&pddc->pddcb->arc.arcparams,
                sizeof( ARCPARAMS ) );
  return (SUCCESS);
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_SetArcParameters
 *
 * DESCRIPTION   = Sets the arc parameters in the pddc as
 *                 passed in pArcParams.  Also checks for
 *                 the validity of the arc parameters.
 *
 * INPUT         = (hdc,pArcParms,pddc,FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdl_SetArcParameters( HDC hdc, PARCPARAMS pArcParms, PDDC pddc, ULONG FunN )
 /* HDC hdc;                              The display context handle  */
 /* PARCPARAMS pArcParms;                 dest for the arc parameters  */
 /* PDDC pddc;                            Ptr to the DC instance data  */
{
  XFORM xformSource;

  #if      DEBUG
    LogCall((PSZ)"prdl_SetArcParameters(%08lx, %lp, %lp, %lp)", ((PB)&hdc)+
            sizeof(hdc));
  #endif

  /*
  ** handle the special case of parameters = <1, 1, 0, 0>
  */
  if ((pArcParms->lP == 1L) && (pArcParms->lQ == 1L) && (pArcParms->lR == 0L)
      && (pArcParms->lS == 0L))
  {
    pddc->pddcb->arc.arcparams.lP = 1L; /* set arc params in pddc  */
    pddc->pddcb->arc.arcparams.lQ = 1L;
    pddc->pddcb->arc.arcparams.lR = 0L;
    pddc->pddcb->arc.arcparams.lS = 0L;
    pddc->pddcb->arc.arcparmmatrix.mx_fxM11 = 0x10000;
    pddc->pddcb->arc.arcparmmatrix.mx_fxM12 = 0L;
    pddc->pddcb->arc.arcparmmatrix.mx_fxM21 = 0L;
    pddc->pddcb->arc.arcparmmatrix.mx_fxM22 = 0x10000;
    pddc->pddcb->arc.arcparmmatrix.mx_lM41 = 0L;
    pddc->pddcb->arc.arcparmmatrix.mx_lM42 = 0L;
    pddc->pddcb->arc.arcparmmatrix.mx_usfracM41 = 0x8000; /* one half  */
    pddc->pddcb->arc.arcparmmatrix.mx_usfracM42 = 0x8000;
    pddc->pddcb->arc.arcparmmatrix.mx_flags = MATRIX_SIMPLE+MATRIX_UNITS;
    pddc->pddcb->arc.arcparmmatrix.mx_unused = 0;

    /*
    ** copy the arc parameter matrix into the inverse arc
    ** parameter matrix.
    */
    utl_memcopy( (PSZ) &pddc->pddcb->arc.invarcpmatrix,
                 (PSZ) &pddc->pddcb->arc.arcparmmatrix, sizeof( MATRIX ) );
    {
      return (SUCCESS);
    }
  }

  /*
  ** make a transform with the arc parameters.  copy them into
  ** an XFORM structure.  CopyAndNormalize will convert them into
  ** a MATRIX structure.  Then create a MATRIX which is an inverse
  ** of the arc parameter matrix.
  */
  xformSource.fxM11 = LongToFx(pArcParms->lP);
  xformSource.fxM12 = LongToFx(pArcParms->lR);
  xformSource.fxM21 = LongToFx(pArcParms->lS);
  xformSource.fxM22 = LongToFx(pArcParms->lQ);
  xformSource.lM41 = 0L;
  xformSource.lM42 = 0L;
  pddc->pddcb->arc.usfCW = CopyNormalizeAndShift( (XFORM *)&xformSource,
                           (POINTL *) pArcParms);
  CopyAndNormalize( &xformSource, &pddc->pddcb->arc.arcparmmatrix );
  FindInverseArcMatrix( &pddc->pddcb->arc.arcparmmatrix,
                        &pddc->pddcb->arc.invarcpmatrix);
  PrintLog( (PSZ) "matrix = %lx %lx %lx %lx %lx %lx\n\n",
            pddc->pddcb->arc.arcparmmatrix.mx_fxM11,
            pddc->pddcb->arc.arcparmmatrix.mx_fxM12,
            pddc->pddcb->arc.arcparmmatrix.mx_fxM21,
            pddc->pddcb->arc.arcparmmatrix.mx_fxM22,
            pddc->pddcb->arc.arcparmmatrix.mx_lM41,
            pddc->pddcb->arc.arcparmmatrix.mx_lM42);
  PrintLog( (PSZ) "Inv matrix = %lx %lx %lx %lx %lx %lx\n\n",
            pddc->pddcb->arc.invarcpmatrix.mx_fxM11,
            pddc->pddcb->arc.invarcpmatrix.mx_fxM12,
            pddc->pddcb->arc.invarcpmatrix.mx_fxM21,
            pddc->pddcb->arc.invarcpmatrix.mx_fxM22,
            pddc->pddcb->arc.invarcpmatrix.mx_lM41,
            pddc->pddcb->arc.invarcpmatrix.mx_lM42);

  /*
  ** store parameters in the ddc
  */
  utl_memcopy( (PSZ) &pddc->pddcb->arc.arcparams, (PSZ) pArcParms,
               sizeof( ARCPARAMS ) );
  return (SUCCESS);
}

#endif
