/*
 *
 * Functionality for 2D rendering
 *
 * Copyright (c) 1998 Criterion Software Ltd.
 */

/****************************************************************************
 *                                                                          *
 *  Module  :   path.c                                                      *
 *                                                                          *
 *  Purpose :   Build paths                                                 *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 Includes
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>

#include <rwcore.h>
#include <rpdbgerr.h>

#include "gstate.h"
#include "path.h"
#include "rt2d.h"

static const char   rcsid[] __RWUNUSED__ =
    "@@(#)$Id: path.c,v 1.65 2001/02/05 12:07:56 johns Exp $";

#if defined (__MWERKS__)
#if (defined(RWVERBOSE))
#pragma message (__FILE__ "/" _SKY_EXPAND(__LINE__) ": __MWERKS__ == " _SKY_EXPAND(__MWERKS__))
#endif /* (defined (__MWERKS__)) */
#if (__option (global_optimizer))
#pragma always_inline off
#endif /* (__option (global_optimizer)) */
#endif /*  defined (__MWERKS__) */

/****************************************************************************
 Local Types
 */

typedef RwV2d       RtCubicBezier[4];

/****************************************************************************
 Local (Static) Prototypes
 */

/****************************************************************************
 Local Defines
 */

/****************************************************************************
 Globals (across program)
 */

/****************************************************************************
 Local (static) Globals
 */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

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

/* Remove the 'x' enable SubPathPrint2d */
#define RW2Dx

#if (defined(RW2D) && defined(RWDEBUG))

static Rt2dPath    *
SubPathPrint2d(Rt2dPath * path)
{
    RWFUNCTION(RWSTRING("SubPathPrint2d"));

    if (path)
    {
        rt2dPathNode       *pnode;
        RwInt32             vcount, i;

        pnode = (rt2dPathNode *) rwSListGetArray(path->segments);
        vcount = rwSListGetNumEntries(path->segments);

        /* copy prims */
        for (i = 0; i < vcount; i++)
        {
            switch (pnode[i].type)
            {
                case rt2dMOVE:
                    RWMESSAGE(("[%g %g] rt2dMOVE",
                               pnode[i].pos.x, pnode[i].pos.y));
                    break;
                case rt2dLINE:
                    RWMESSAGE(("[%g %g] rt2dLINE",
                               pnode[i].pos.x, pnode[i].pos.y));
                    break;
                case rt2dCURVE:
                    RWMESSAGE(("[%g %g] [%g %g] [%g %g] rt2dCURVE",
                               pnode[i].pos.x,
                               pnode[i].pos.y,
                               pnode[i + 1].pos.x,
                               pnode[i + 1].pos.y,
                               pnode[i + 2].pos.x, pnode[i + 2].pos.y));
                    i += 2;
                    break;
                default:
                    RWMESSAGE(("Unrecognized type"));
                    break;
            }
        }
        RWMESSAGE(("%s\n", path->closed ? "Closed" : "Open"));
    }

    RWRETURN(path);
}

#define RT2DSUBPATHPRINT(path) SubPathPrint2d(path)

#endif /* ( defined(RW2D) && defined(RWDEBUG) ) */

#if (!defined(RT2DSUBPATHPRINT))
#define RT2DSUBPATHPRINT(path) /* No op */
#endif /* (!defined(RT2DSUBPATHPRINT)) */

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

#define Rt2dIsFlatMacro(result, _control)                               \
MACRO_START                                                             \
{                                                                       \
    RwV2d               delta, dir;                                     \
    RwReal              Sqdist, dot;                                    \
                                                                        \
    RwV2dSub(&delta, &(_control)[3], &(_control)[0]);                   \
    Sqdist = RwV2dDotProduct(&delta, &delta);                           \
                                                                        \
    RwV2dSub(&dir, &(_control)[1], &(_control)[0]);                     \
    dot = RwV2dDotProduct(&dir, &delta);                                \
                                                                        \
    result =                                                            \
        (RwV2dDotProduct(&dir, &dir) - Rt2dGlobals.sqTolerance) *       \
        Sqdist < (dot * dot);                                           \
                                                                        \
    if (result)                                                         \
    {                                                                   \
                                                                        \
        RwV2dSub(&dir, &(_control)[2], &(_control)[0]);                 \
        dot = RwV2dDotProduct(&dir, &delta);                            \
                                                                        \
        result =                                                        \
            (RwV2dDotProduct(&dir, &dir) - Rt2dGlobals.sqTolerance) *   \
            Sqdist < (dot * dot);                                       \
                                                                        \
    }                                                                   \
}                                                                       \
MACRO_STOP

#define SubPathMoveto2dMacro(_path, _pt)                     \
MACRO_START                                                     \
{                                                               \
    rt2dPathNode         *_pnode = (rt2dPathNode *)             \
        rwSListGetNewEntry(((_path))->segments);                \
                                                                \
    _pnode->type = rt2dMOVE;                                    \
    _pnode->pos.x = ((_pt))->x;                                 \
    _pnode->pos.y = ((_pt))->y;                                 \
    /* normal unknowable */                                     \
    _pnode->dist = 0.0f;                                        \
                                                                \
}                                                               \
MACRO_STOP

#if (0)
#define SubPathLineto2dMacro(_path, _pt)                     \
MACRO_START                                                     \
{                                                               \
    RwV2d                dir;                                   \
    RwReal               length;                                \
    rt2dPathNode         *_pnode = (rt2dPathNode *)             \
        rwSListGetNewEntry(((_path))->segments);                \
                                                                \
    _pnode->type = rt2dLINE;                                    \
    _pnode->pos.x = ((_pt))->x;                                 \
    _pnode->pos.y = ((_pt))->y;                                 \
    dir.x = ((_pt))->y - (_pnode-1)->pos.y;                     \
    dir.y = (_pnode-1)->pos.x - ((_pt))->x;                     \
    RwV2dNormalizeMacro(length, &_pnode->normal, &dir);         \
    _pnode->dist = (_pnode-1)->dist + length;                   \
    _pnode->pos.z = _pnode->dist;                               \
}                                                               \
MACRO_STOP
#endif /* (0) */

#define SubPathLineto2dMacro(_path, _pt)                     \
MACRO_START                                                     \
{                                                               \
    RwV2d                dir;                                   \
    RwReal               l;                                     \
    rt2dPathNode         *_pnode = (rt2dPathNode *)             \
        rwSListGetNewEntry(((_path))->segments);                \
    RwInt32              vcount =                               \
        rwSListGetNumEntries((_path)->segments);                \
                                                                \
                                                                \
    _pnode->type = rt2dLINE;                                    \
    _pnode->pos.x = ((_pt))->x;                                 \
    _pnode->pos.y = ((_pt))->y;                                 \
    dir.x = ((_pt))->y - (_pnode-1)->pos.y;                     \
    dir.y = (_pnode-1)->pos.x - ((_pt))->x;                     \
    RwV2dNormalizeMacro(l, &(_pnode)->normal, &dir);            \
    _pnode->dist = (_pnode-1)->dist + l;                        \
                                                                \
    if (vcount > 2)                                             \
    {                                                           \
        RwV2dAdd(&dir, &(_pnode-1)->normal, &(_pnode)->normal); \
        l = 1.0f / RwV2dDotProduct(&(_pnode-1)->normal, &dir);  \
        RwV2dScale(&(_pnode-1)->normal, &dir, l);               \
    }                                                           \
    else                                                        \
    {                                                           \
        (_pnode-1)->normal = (_pnode)->normal;                  \
    }                                                           \
}                                                               \
MACRO_STOP

#define SubPathCurveto2dMacro(_path, _pt1,  _pt2, _pt3)      \
MACRO_START                                                     \
{                                                               \
    RwV2d                dir;                                   \
    RwReal               length;                                \
    rt2dPathNode         *_pnode = (rt2dPathNode *)             \
        rwSListGetNewEntries(((_path))->segments, 3);           \
                                                                \
    _pnode->type = rt2dCURVE;                                   \
    _pnode->pos.x = ((_pt1))->x;                                \
    _pnode->pos.y = ((_pt1))->y;                                \
    dir.x = ((_pt1))->y - (_pnode-1)->pos.y;                    \
    dir.y = (_pnode-1)->pos.x - ((_pt1))->x;                    \
    RwV2dNormalizeMacro(length, &_pnode->normal, &dir);           \
    _pnode->dist = (_pnode-1)->dist + length;                   \
                                                                \
    _pnode++;                                                   \
    _pnode->type = rt2dCURVE;                                   \
    _pnode->pos.x = ((_pt2))->x;                                \
    _pnode->pos.y = ((_pt2))->y;                                \
    dir.x = ((_pt2))->y - (_pnode-1)->pos.y;                    \
    dir.y = (_pnode-1)->pos.x - ((_pt2))->x;                    \
    RwV2dNormalizeMacro(length, &_pnode->normal, &dir);           \
    _pnode->dist = (_pnode-1)->dist + length;                   \
                                                                \
    _pnode++;                                                   \
    _pnode->type = rt2dCURVE;                                   \
    _pnode->pos.x = ((_pt3))->x;                                \
    _pnode->pos.y = ((_pt3))->y;                                \
    dir.x = ((_pt3))->y - (_pnode-1)->pos.y;                    \
    dir.y = (_pnode-1)->pos.x - ((_pt3))->x;                    \
    RwV2dNormalizeMacro(length, &_pnode->normal, &dir);         \
    _pnode->dist = (_pnode-1)->dist + length;                   \
                                                                \
    (_path)->flat = FALSE;                                      \
}                                                               \
MACRO_STOP

#define SubPathClose2dMacro(_path)           \
MACRO_START                                     \
{                                               \
    (_path)->closed = TRUE;                     \
}                                               \
MACRO_STOP

#if (! ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) ))

#define SubPathMoveto2d(_path, _pt)                          \
  SubPathMoveto2dMacro(_path, _pt)

#define SubPathLineto2d(_path, _pt)                          \
  SubPathLineto2dMacro(_path, _pt)

#define SubPathCurveto2d(_path, _pt1, _pt2, _pt3)            \
  SubPathCurveto2dMacro(_path, _pt1, _pt2, _pt3)

#define SubPathClose2d(_path)                                \
  SubPathClose2dMacro(_path)

#else /*  (! ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) )) */

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

static void
SubPathMoveto2d(Rt2dPath * const path, const RwV2d * const pt)
{
    RWFUNCTION(RWSTRING("SubPathMoveto2d"));

    SubPathMoveto2dMacro(path, pt);

    RWRETURNVOID();
}

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

static void
SubPathLineto2d(Rt2dPath * const path, const RwV2d * const pt)
{
    RWFUNCTION(RWSTRING("SubPathLineto2d"));

    SubPathLineto2dMacro(path, pt);

    RWRETURNVOID();
}

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

static void
SubPathCurveto2d(Rt2dPath * const path,
                 const RwV2d * const pt1,
                 const RwV2d * const pt2, const RwV2d * const pt3)
{
    RWFUNCTION(RWSTRING("SubPathCurveto2d"));

    SubPathCurveto2dMacro(path, pt1, pt2, pt3);

    RWRETURNVOID();
}

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

static void
SubPathClose2d(Rt2dPath * path)
{
    RWFUNCTION(RWSTRING("SubPathClose2d"));

    SubPathClose2dMacro(path);

    RWRETURNVOID();
}

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

#if (0)
static              RwBool
IsFlat2d(const RtCubicBezier control)
{
    RwBool              result;

    RWFUNCTION(RWSTRING("IsFlat2d"));

    Rt2dIsFlatMacro(result, control);

    RWRETURN(result);

}
#endif /* (0) */

#endif /*  (! ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) )) */

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

static Rt2dPath    *
SubPathDestroy2d(Rt2dPath * path)
{
    Rt2dPath           *next;

    RWFUNCTION(RWSTRING("SubPathDestroy2d"));

    RWASSERT(path);

    next = path->next;

    rwSListDestroy(path->segments);
    RwFreeListFree(Rt2dGlobals.pathFreeList, path);
    path = next;

    /* tail of path */
    RWRETURN(path);
}

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

static void
SubPathEmpty2d(Rt2dPath * path)
{
    RWFUNCTION(RWSTRING("SubPathEmpty2d"));

    rwSListEmpty(path->segments);
    path->closed = FALSE;
    path->flat = TRUE;

    RWRETURNVOID();
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathEmpty
 * is used to delete all data describing the specified path
 * without actually destroying it. This leaves the path empty and ready
 * for a new path description to be constructed.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathCreate
 * \see Rt2dPathDestroy
 */
Rt2dPath           *
Rt2dPathEmpty(Rt2dPath * path)
{
    RWAPIFUNCTION(RWSTRING("Rt2dPathEmpty"));

    RWASSERT(NULL != path);

    Rt2dPathDestroy(path->next);
    path->next = (Rt2dPath *)NULL;

    SubPathEmpty2d(path);
    path->curr = path;

    RWRETURN(path);
}

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

static Rt2dPath    *
SubPathCopy2d(Rt2dPath * dst, const Rt2dPath * src)
{
    rt2dPathNode       *pnode, *qnode;
    RwInt32             vcount;

    RWFUNCTION(RWSTRING("SubPathCopy2d"));

    RWASSERT(src);
    RWASSERT(dst);

    pnode = (rt2dPathNode *) rwSListGetArray(src->segments);
    vcount = rwSListGetNumEntries(src->segments);

    qnode =
        (rt2dPathNode *) rwSListGetNewEntries(dst->segments, vcount);
    memcpy(qnode, pnode, sizeof(rt2dPathNode) * vcount);

    if (src->closed)
    {
        SubPathClose2d(dst);
    }

    RWRETURN(dst);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathCopy
 * is used to duplicate the definition of the source path
 * and store the result in the destination path.
 * Note that the destination path is emptied before the copy is
 * performed, therefore, the source and destination path arguments must
 * not point to the same path.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param dst  Pointer to the path that will receive the copy.
 * \param src  Pointer to the path that is to be copied.
 * \return a pointer to the destination path if successful or NULL if
 * there is an error.
 * \see Rt2dPathCreate
 * \see Rt2dPathDestroy
 */
Rt2dPath           *
Rt2dPathCopy(Rt2dPath * dst, const Rt2dPath * src)
{
    Rt2dPath           *srctarg, *dsttarg;

    RWAPIFUNCTION(RWSTRING("Rt2dPathCopy"));

    RWASSERT(src);
    RWASSERT(dst);

    Rt2dPathEmpty(dst);

    SubPathCopy2d(dst, src);
    dsttarg = dst;
    for (srctarg = src->next; srctarg; srctarg = srctarg->next)
    {
        dsttarg->next = Rt2dPathCreate();
        dsttarg = SubPathCopy2d(dsttarg->next, srctarg);
    }

    RWRETURN(dst);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathMoveto
 * is used to define the current point for the specified
 * path using the given coordinates.
 * Note that positions are defined in absolute coordinates and are
 * subject to the current transformation matrix (CTM).
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x  A RwReal value equal to the x-coordinate of the point.
 * \param y  A RwReal value equal to the y-coordinate of the point.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathLineto
 * \see Rt2dPathCurveto
 * \see Rt2dPathRLineto
 * \see Rt2dPathRCurveto
 */
Rt2dPath           *
Rt2dPathMoveto(Rt2dPath * path, RwReal x, RwReal y)
{

    RWAPIFUNCTION(RWSTRING("Rt2dPathMoveto"));

    RWASSERT(NULL != path);

    if (rwSListGetNumEntries(path->curr->segments) == 0)
    {
        RwV2d               pt;

        pt.x = x;
        pt.y = y;
        SubPathMoveto2d(path->curr, &pt);
    }
    else
    {
        /* start a new subpath */
        path->curr->next = Rt2dPathCreate();
        path->curr = path->curr->next;
        Rt2dPathMoveto(path, x, y);
    }

    RWRETURN(path);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathLineto
 * is used to add a straight line segment to the specified
 * path that extends from the current point to a point with the given
 * coordinates. After this function has executed the current point is
 * equal to the point with the specified position.
 * Note that positions are defined in absolute coordinates. Use the
 * function Rt2dPathRLineto to specify coordinates relative to the
 * current point.
 * Also note that the positions are subject to the current transformation
 * matrix (CTM).
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x  A RwReal value equal to the x-coordinate of the line's end-point
 * \param y  A RwReal value equal to the y-coordinate of the line's end-point
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathRLineto
 * \see Rt2dPathCurveto
 * \see Rt2dPathRCurveto
 * \see Rt2dPathMoveto
 */
Rt2dPath           *
Rt2dPathLineto(Rt2dPath * path, RwReal x, RwReal y)
{

    RwV2d               pt;

    RWAPIFUNCTION(RWSTRING("Rt2dPathLineto"));

    RWASSERT(NULL != path);

    pt.x = x;
    pt.y = y;
    SubPathLineto2d(path->curr, &pt);

    RWRETURN(path);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathRLineto
 * is used to add a straight line segment to the
 * specified path that extends from the current point to a point with the
 * given coordinates. After this function has executed the current point
 * is equal to the point with the specified position.
 * Note that positions are defined using coordinates relative to the
 * currrent point. Use the function Rt2dPathLineto to specify absolute
 * coordinates.
 * Also note that the positions are subject to the current transformation
 * matrix (CTM).
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x  A RwReal value equal to the x-coordinate of the line's end-point.
 * \param y  A RwReal value equal to the y-coordinate of the line's  end-point.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathLineto
 * \see Rt2dPathRCurveto
 * \see Rt2dPathCurveto
 * \see Rt2dPathMoveto
 */
Rt2dPath           *
Rt2dPathRLineto(Rt2dPath * path, RwReal x, RwReal y)
{
    rt2dPathNode       *pnode;
    RwInt32             vcount;

    RWAPIFUNCTION(RWSTRING("Rt2dPathRLineto"));

    RWASSERT(NULL != path);

    vcount = rwSListGetNumEntries(path->curr->segments);
    pnode = (rt2dPathNode *)rwSListGetEntry(path->curr->segments, vcount - 1);

    Rt2dPathLineto(path, pnode->pos.x + x, pnode->pos.y + y);

    RWRETURN(path);

}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathCurveto
 * is used to add a curved line segment to the specified
 * path. The curve is defined by the four control points of a cubic
 * Bezier whose positions are given by the current point and the points
 * passed to this function (in that order). After this function has
 * executed, the current point is equal to the last position specified
 * here.
 *
 * Note that positions are defined in absolute coordinates. Use the
 * function Rt2dPathRCurveto to specify coordinates relative to the
 * current point.
 *
 * Also note that the positions are subject to the current transformation
 * matrix (CTM).
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x1  A RwReal value equal to the x-coordinate of the second control
 * \param y1  A RwReal value equal to the y-coordinate of the second control
 * \param x2  A RwReal value equal to the x-coordinate of the third control
 * \param y2  A RwReal value equal to the y-coordinate of the third control
 * \param x3  A RwReal value equal to the x-coordinate of the fourth control
 * \param y3  A RwReal value equal to the y-coordinate of the fourth control
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathRCurveto
 * \see Rt2dPathLineto
 * \see Rt2dPathRLineto
 * \see Rt2dPathMoveto
 */
Rt2dPath           *
Rt2dPathCurveto(Rt2dPath * path, RwReal x1, RwReal y1, RwReal x2,
                RwReal y2, RwReal x3, RwReal y3)
{
    RwV2d               pt1, pt2, pt3;

    RWAPIFUNCTION(RWSTRING("Rt2dPathCurveto"));

    RWASSERT(NULL != path);

    pt1.x = x1;
    pt1.y = y1;

    pt2.x = x2;
    pt2.y = y2;

    pt3.x = x3;
    pt3.y = y3;

    SubPathCurveto2d(path->curr, &pt1, &pt2, &pt3);

    RWRETURN(path);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathRCurveto
 * is used to add a curved line segment to the specified
 * path. The curve is defined by the four control points of a cubic
 * Bezier whose positions are given by the current point and the points
 * passed to this function (in that order). After this function has
 * executed, the current point is equal to the last position specified
 * here.
 *
 * Note that positions are defined using coordinates relative to the
 * currrent point. Use the function Rt2dPathCurveto to specify absolute
 * coordinates.
 *
 * Also note that the positions are subject to the current transformation
 * matrix (CTM).
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x1  A RwReal value equal to the x-coordinate of the second control
 * \param y1  A RwReal value equal to the y-coordinate of the second control
 * \param x2  A RwReal value equal to the x-coordinate of the third control
 * \param y2  A RwReal value equal to the y-coordinate of the third control
 * \param x3  A RwReal value equal to the x-coordinate of the fourth control
 * \param y3  A RwReal value equal to the y-coordinate of the fourth control
 * rdesc a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathCurveto
 * \see Rt2dPathRLineto
 * \see Rt2dPathLineto
 * \see Rt2dPathMoveto
 */
Rt2dPath           *
Rt2dPathRCurveto(Rt2dPath * path, RwReal x1, RwReal y1, RwReal x2,
                 RwReal y2, RwReal x3, RwReal y3)
{
    rt2dPathNode       *pnode;
    RwInt32             vcount;

    RWAPIFUNCTION(RWSTRING("Rt2dPathRCurveto"));

    RWASSERT(NULL != path);

    vcount = rwSListGetNumEntries(path->curr->segments);
    pnode = (rt2dPathNode *)rwSListGetEntry(path->curr->segments, vcount - 1);

    Rt2dPathCurveto(path, pnode->pos.x + x1, pnode->pos.y + y1,
                    pnode->pos.x + x2, pnode->pos.y + y2,
                    pnode->pos.x + x3, pnode->pos.y + y3);

    RWRETURN(path);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathClose
 * is used to add a line segment to the specified path that
 * joins the current point with the first point defined for this
 * path. This operation thus forces the path to define a closed 2D
 * region.
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 *
 * \param path  Pointer to the path.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathMoveto
 * \see Rt2dPathLineto
 * \see Rt2dPathCurveto
 * \see Rt2dPathRLineto
 * \see Rt2dPathRCurveto
 * \see Rt2dPathCreate
 * \see Rt2dPathDestroy
 * \see Rt2dPathEmpty
 */
Rt2dPath           *
Rt2dPathClose(Rt2dPath * path)
{
    Rt2dPath           *curr;
    RwInt32             vcount;
    rt2dPathNode       *firstnode, *lastnode;
    RwV2d               first, normal;
    RwReal              length;

    RWAPIFUNCTION(RWSTRING("Rt2dPathClose"));

    RWASSERT(path);

    curr = path->curr;
    RWASSERT(curr);

    vcount = rwSListGetNumEntries(curr->segments);

    firstnode = (rt2dPathNode *) rwSListGetArray(curr->segments);
    lastnode = (rt2dPathNode *)
        rwSListGetEntry(curr->segments, vcount - 1);

    first = firstnode->pos;

    normal.x = first.y - lastnode->pos.y;
    normal.y = lastnode->pos.x - first.x;

#if (0)

    if ((normal.x != 0.0f) || (normal.y != 0.0f))
    {
        RwReal              length;
        rt2dPathNode       *pnode = (rt2dPathNode *)
            rwSListGetNewEntry(curr->segments);

        pnode->type = rt2dLINE;
        pnode->pos = first;

        RwV2dNormalizeMacro(length, &pnode->normal, &normal);
        pnode->dist = (pnode - 1)->dist + length;

        /* because we may have reallocated... */
        firstnode = rwSListGetArray(curr->segments);
        lastnode = pnode;
    }

#endif /* (0) */

    if ((normal.x != 0.0f) || (normal.y != 0.0f))
    {
        SubPathLineto2d(path->curr, &first);

        firstnode = (rt2dPathNode *) rwSListGetArray(curr->segments);
        lastnode = (rt2dPathNode *)
            rwSListGetEntry(curr->segments, vcount);

    }

    RwV2dAdd(&normal, &firstnode->normal, &lastnode->normal);

    length = 1.0f / RwV2dDotProduct(&firstnode->normal, &normal);
    RwV2dScale(&lastnode->normal, &normal, length);

    /* rt2dMOVE normal now knowable */
    firstnode->normal = lastnode->normal;

    curr->closed = TRUE;

    RWRETURN(path);

}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathRect
 * is used to add an outline rectangle to the specified path
 * that has the given position, width and height.
 *
 * Note that positions are defined in absolute coordinates and are
 * subject to the current transformation matrix (CTM).
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x  A RwReal value equal to the x-coordinate of the lower-left corner
 *       the rectangle.
 * \param y  A RwReal value equal to the y-coordinate of the lower-left corner
 *       the rectangle.
 * \param w  A RwReal value equal to the width of the rectangle.
 * \param h  A RwReal value equal to the height of the rectangle.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dPathRoundRect
 * \see Rt2dPathOval
 */
Rt2dPath           *
Rt2dPathRect(Rt2dPath * path, RwReal x, RwReal y, RwReal w, RwReal h)
{
    RWAPIFUNCTION(RWSTRING("Rt2dPathRect"));

    RWASSERT(NULL != path);

    Rt2dPathMoveto(path, x, y);

    Rt2dPathLineto(path, x + w, y);
    Rt2dPathLineto(path, x + w, y + h);
    Rt2dPathLineto(path, x, y + h);

#if (0)

    /* special case rectangle */
    pnode = (rt2dPathNode *) rwSListGetNewEntries(path->segments, 3);

    pnode->type = rt2dLINE;
    pnode->pos.x = x + w;
    pnode->pos.y = y;
    pnode->normal.x = 0.0f;
    pnode->normal.y = -1.0f;
    pnode->dist = w;

    pnode++;
    pnode->type = rt2dLINE;
    pnode->pos.x = x + w;
    pnode->pos.y = y + h;
    pnode->normal.x = 1.0f;
    pnode->normal.y = 0.0f;
    pnode->dist = w + h;

    pnode++;
    pnode->type = rt2dLINE;
    pnode->pos.x = x;
    pnode->pos.y = y + h;
    pnode->normal.x = 0.0f;
    pnode->normal.y = 1.0f;
    pnode->dist = 2 * w + h;

#endif /* (0) */

    Rt2dPathClose(path);

    RWRETURN(path);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathRoundRect
 * is used to add an outline rectangle with rounded
 * corners to the specified path that has the given position, width,
 * height and radius of curvature of the corners. The radius must be
 * greater than zero.
 *
 * Note that positions are defined in absolute coordinates and are
 * subject to the current transformation matrix (CTM).
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x  A RwReal value equal to the x-coordinate of the lower-left corner
 *       the rectangle.
 * \param y  A RwReal value equal to the y-coordinate of the lower-left corner
 *       the rectangle.
 * \param w  A RwReal value equal to the width of the rectangle.
 * \param h  A RwReal value equal to the height of the rectangle.
 * \param radius  A RwReal value equal to the radius of the corners.
 * \return a pointer to the path if successful or NULL if there is an
 *        error.
 * \see Rt2dPathRect
 * \see Rt2dPathOval
 */
Rt2dPath           *
Rt2dPathRoundRect(Rt2dPath * path, RwReal x, RwReal y, RwReal w,
                  RwReal h, RwReal radius)
{
    RwReal              ctrlPos = (RwReal) (0.5522847498);

    RWAPIFUNCTION(RWSTRING("Rt2dPathRoundRect"));

    if (radius <= (RwReal) (0.0))
    {
        Rt2dPathRect(path, x, y, w, h);
    }
    else
    {
        RwBool              hstrut = radius * (RwReal) (2.0) < w;
        RwBool              vstrut = radius * (RwReal) (2.0) < h;

        Rt2dPathMoveto(path, x + radius, y);

        if (hstrut)
        {
            Rt2dPathLineto(path, x + w - radius, y);
        }

        Rt2dPathCurveto(path,
                        x + w - radius * ((RwReal) (1.0) - ctrlPos), y,
                        x + w, y + radius * ((RwReal) (1.0) - ctrlPos),
                        x + w, y + radius);

        if (vstrut)
        {
            Rt2dPathLineto(path, x + w, y + h - radius);
        }

        Rt2dPathCurveto(path, x + w,
                        y + h - radius * ((RwReal) (1.0) - ctrlPos),
                        x + w - radius * ((RwReal) (1.0) - ctrlPos),
                        y + h, x + w - radius, y + h);

        if (hstrut)
        {
            Rt2dPathLineto(path, x + radius, y + h);
        }

        Rt2dPathCurveto(path, x + radius * ((RwReal) (1.0) - ctrlPos),
                        y + h, x,
                        y + h - radius * ((RwReal) (1.0) - ctrlPos), x,
                        y + h - radius);

        if (vstrut)
        {
            Rt2dPathLineto(path, x, y + radius);
        }

        Rt2dPathCurveto(path, x,
                        y + radius * ((RwReal) (1.0) - ctrlPos),
                        x + radius * ((RwReal) (1.0) - ctrlPos), y,
                        x + radius, y);

        Rt2dPathClose(path);

    }

    RWRETURN(path);

}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathOval
 * is used to add an outline oval to the specified path that
 * has the given position, width and height.
 *
 * Note that positions are defined in absolute coordinates and are
 * subject to the current transformation matrix (CTM).
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param x  A RwReal value equal to the x-coordinate of the lower-left corner
 *       the oval.
 * \param y  A RwReal value equal to the y-coordinate of the lower-left corner
 *       the oval.
 * \param w  A RwReal value equal to the width of the oval.
 * \param h  A RwReal value equal to the height of the oval.
 * \return a pointer to the path if successful or NULL if there is an
 *       error.
 * \see Rt2dPathRect
 * \see Rt2dPathRoundRect
 */
Rt2dPath           *
Rt2dPathOval(Rt2dPath * path, RwReal x, RwReal y, RwReal w, RwReal h)
{
    const RwReal        ctrlPos = (RwReal) (0.5522847498);

    RWAPIFUNCTION(RWSTRING("Rt2dPathOval"));

    RWASSERT(NULL != path);

    Rt2dPathMoveto(path, x + w * (RwReal) (0.5), y);

    Rt2dPathCurveto(path,
                    x + w * (RwReal) (0.5) +
                    w * (RwReal) (0.5) * ctrlPos, y, x + w,
                    y + h * (RwReal) (0.5) -
                    h * (RwReal) (0.5) * ctrlPos, x + w,
                    y + h * (RwReal) (0.5));

    Rt2dPathCurveto(path, x + w,
                    y + h * (RwReal) (0.5) +
                    h * (RwReal) (0.5) * ctrlPos,
                    x + w * (RwReal) (0.5) +
                    w * (RwReal) (0.5) * ctrlPos, y + h,
                    x + w * (RwReal) (0.5), y + h);

    Rt2dPathCurveto(path,
                    x + w * (RwReal) (0.5) -
                    w * (RwReal) (0.5) * ctrlPos, y + h, x,
                    y + h * (RwReal) (0.5) +
                    h * (RwReal) (0.5) * ctrlPos, x,
                    y + h * (RwReal) (0.5));

    Rt2dPathCurveto(path, x,
                    y + h * (RwReal) (0.5) -
                    h * (RwReal) (0.5) * ctrlPos,
                    x + w * (RwReal) (0.5) -
                    w * (RwReal) (0.5) * ctrlPos, y,
                    x + w * (RwReal) (0.5), y);

    Rt2dPathClose(path);

    RWRETURN(path);

}

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

Rt2dPath           *
_rt2dScratchPath(void)
{
    RWFUNCTION(RWSTRING("_rt2dScratchPath"));

    Rt2dPathEmpty(Rt2dGlobals.scratch);

    RWRETURN(Rt2dGlobals.scratch);
}

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

static void
Bisect2d(const RtCubicBezier control,
         RtCubicBezier left, RtCubicBezier right)
{
    RwV2d               vTemp, vTemp2;

    RWFUNCTION(RWSTRING("Bisect2d"));

    RwV2dAdd(&vTemp2, &control[1], &control[2]);
    RwV2dScale(&vTemp2, &vTemp2, (RwReal) (0.5));

    RwV2dAdd(&vTemp, &control[0], &control[1]);
    RwV2dScale(&left[1], &vTemp, (RwReal) (0.5));

    RwV2dAdd(&vTemp, &control[3], &control[2]);
    RwV2dScale(&right[2], &vTemp, (RwReal) (0.5));

    RwV2dAdd(&vTemp, &left[1], &vTemp2);
    RwV2dScale(&left[2], &vTemp, (RwReal) (0.5));

    RwV2dAdd(&vTemp, &right[2], &vTemp2);
    RwV2dScale(&right[1], &vTemp, (RwReal) (0.5));

    RwV2dAdd(&vTemp, &left[2], &right[1]);
    RwV2dScale(&left[3], &vTemp, (RwReal) (0.5));

    RwV2dAssign(&left[0], &control[0]);
    RwV2dAssign(&right[0], &left[3]);
    RwV2dAssign(&right[3], &control[3]);

    RWRETURNVOID();
}

/****************************************************************************/
static void
Flatten2d(Rt2dPath * const flat, const RtCubicBezier control, int depth)
{
    RwBool              terminate;

    RWFUNCTION(RWSTRING("Flatten2d"));

    terminate = (--depth < 0);
    if (terminate)
    {
        SubPathLineto2d(flat, &control[3]);
    }
    else
    {
        Rt2dIsFlatMacro(terminate, control);
        if (terminate)
        {
            SubPathLineto2d(flat, &control[3]);
        }
        else
        {
            RtCubicBezier       left, right;

            Bisect2d(control, left, right);

            Flatten2d(flat, left, depth);
            Flatten2d(flat, right, depth);
        }
    }

    RWRETURNVOID();
}

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

Rt2dPath           *
_rt2dSubPathFlatten(Rt2dPath * flat, const Rt2dPath * path)
{
    rt2dPathNode       *pnode, *qnode;
    RwInt32             vcount, i;

    RWFUNCTION(RWSTRING("_rt2dSubPathFlatten"));

    RWASSERT(NULL != path);

    /* get tolerance in object space */
    Rt2dGlobals.sqTolerance = _rt2dGetTolerance();

    pnode = (rt2dPathNode *) rwSListGetArray(path->segments);
    vcount = rwSListGetNumEntries(path->segments);

    for (i = 0; i < vcount; i++, pnode++)
    {
        if (pnode->type == rt2dCURVE)
        {
            RwV2d               control[4];

            control[0] = (pnode - 1)->pos;
            control[1] = (pnode + 0)->pos;
            control[2] = (pnode + 1)->pos;
            control[3] = (pnode + 2)->pos;
            Flatten2d(flat, control, 7);
            i += 2;
            pnode += 2;
        }
        else
        {
            qnode = (rt2dPathNode *) rwSListGetNewEntry(flat->segments);
            *qnode = *pnode;
        }
    }

    if (path->closed)
    {
        Rt2dPathClose(flat);
    }

    RWRETURN(flat);
}

/****************************************************************************/
Rt2dBBox           *
_rt2dFlatSubPathExtendBBox(const Rt2dPath * path, Rt2dBBox * bbox)
{
    rt2dPathNode       *pnode;
    RwInt32             vcount, i;

    RWFUNCTION(RWSTRING("_rt2dFlatSubPathExtendBBox"));

    RWASSERT(NULL != path);

    pnode = (rt2dPathNode *) rwSListGetArray(path->segments);
    vcount = rwSListGetNumEntries(path->segments);

    /* determine bbox of subpath */
    for (i = 0; i < vcount; i++, pnode++)
    {
        if (pnode->pos.x < bbox->x)
        {
            bbox->x = pnode->pos.x;
        }
        else if (pnode->pos.x > bbox->w)
        {
            bbox->w = pnode->pos.x;
        }

        if (pnode->pos.y < bbox->y)
        {
            bbox->y = pnode->pos.y;
        }
        else if (pnode->pos.y > bbox->h)
        {
            bbox->h = pnode->pos.y;
        }
    }

    RWRETURN(bbox);
}

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

static Rt2dBBox    *
PathExtendBBox2d(const Rt2dPath * path, Rt2dBBox * bbox)
{
    const Rt2dPath     *target;

    RWFUNCTION(RWSTRING("PathExtendBBox2d"));

    RWASSERT(NULL != path);

    for (target = path; NULL != target; target = target->next)
    {
        if (!target->flat)
        {
            Rt2dPath           *const flat = _rt2dScratchPath();

            _rt2dSubPathFlatten(flat, target);
            _rt2dFlatSubPathExtendBBox(flat, bbox);
        }
        else
        {
            _rt2dFlatSubPathExtendBBox(target, bbox);
        }
    }

    RWRETURN(bbox);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathGetBBox
 * is used to determine the parameters of an axis-aligned
 * bounding-box that completely encloses the specified path. The box
 * determined by this function is the box before the CTM has transformed
 * the path.
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param bbox  Pointer to a Rt2dBBox value that will receive the path's
 *       box.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dVisible
 */
const Rt2dPath     *
Rt2dPathGetBBox(const Rt2dPath * path, Rt2dBBox * bbox)
{
    rt2dPathNode       *pnode;

    RWAPIFUNCTION(RWSTRING("Rt2dPathGetBBox"));

    RWASSERT(path);
    RWASSERT(bbox);

    pnode = (rt2dPathNode *)rwSListGetArray(path->segments);
    bbox->x = bbox->w = pnode[0].pos.x;
    bbox->y = bbox->h = pnode[0].pos.y;

    PathExtendBBox2d(path, bbox);

    bbox->w -= bbox->x;
    bbox->h -= bbox->y;

    RWRETURN(path);
}

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

Rt2dPath           *
_rt2dPathOptimize(Rt2dPath * path)
{
    RWFUNCTION(RWSTRING("_rt2dPathOptimize"));

    /* NULL path is valid */
    if (NULL != path)
    {
        path->next = _rt2dPathOptimize(path->next);

        /* delete degenerate subpaths */
        if (rwSListGetNumEntries(path->segments) == 1)
        {
            path = SubPathDestroy2d(path);
        }
    }

    RWRETURN(path);
}

#if (defined __ICL)            /* Intel compiler optimises this bit badly */
#pragma optimize ("", off)
#endif

/****************************************************************************/
static Rt2dPath    *
SubPathInset2d(Rt2dPath * path, RwReal inset)
{
    rt2dPathNode       *pnode, *nextcorner, *firstnode;
    RwInt32             vcount, i;
    RwV2d               avgnormal;
    RwV3d               pos;

    RWFUNCTION(RWSTRING("SubPathInset2d"));
    RWASSERT(NULL != path);

    RT2DSUBPATHPRINT(path);

    firstnode = pnode =
        (rt2dPathNode *) rwSListGetArray(path->segments);
    vcount = rwSListGetNumEntries(path->segments);

    /* segment normal */
    nextcorner = pnode;
    if (!path->closed)
    {
        /* NB first path node has no normal iff path is not closed */
        nextcorner++;
    }

    for (i = 1; i < vcount; i++, pnode++)
    {
        rt2dPathNode       *corner;
        RwReal              dot;

        /* segment normal */
        corner = nextcorner;
        nextcorner = pnode + 1;

        /* corner normal */
        RwV2dAdd(&avgnormal, &corner->normal, &nextcorner->normal);
        dot = RwV2dDotProduct(&nextcorner->normal, &avgnormal);
        dot = inset / dot;
        RwV2dScale(&avgnormal, &avgnormal, dot);

        RwV2dSub((RwV2d *) & pos, &pnode->pos, &avgnormal);
    }

/*
    if (path->closed)
    {
        pnode->pos = firstnode->pos;
    }
    else
    {
        RwV2dScale(&avgnormal, &nextcorner->normal, inset);
        RwV2dSub(&pos, &pnode->pos, &avgnormal);
    }
 */
    RWRETURN(path);
}

#if (defined __ICL)            /* Intel compiler optimises this bit badly */
#pragma optimize ("", on)
#endif

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathInset
 * is used to scale the specified path such that the new
 * path is parallel to the original at every point. The perpendicular
 * distance between the new and old paths is given by the inset; positive
 * values result in a new path contained inside the original and vice
 * versa.
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param inset  A RwReal value equal to the inset distance.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 */
Rt2dPath           *
Rt2dPathInset(Rt2dPath * path, RwReal inset)
{
    Rt2dPath           *target;

    RWAPIFUNCTION(RWSTRING("Rt2dPathInset"));

    RWASSERT(path);

    for (target = path; NULL != target; target = target->next)
    {
        target->inset = inset;

        target = SubPathInset2d(target, inset);
    }

    RWRETURN(path);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathFlatten
 * is used to convert the specified path to use only
 * straight line segments. For the curved portions of the path, the
 * degree of flattening may be controlled with a factor specified using
 * the function Rt2dDeviceSetFlat, which has a default value of 0.5. This
 * factor specifies the maximum pixel error that is tolerated as the
 * curves are tessellated.
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dDeviceSetFlat
 */
Rt2dPath           *
Rt2dPathFlatten(Rt2dPath * path)
{
    Rt2dPath           *target;

    RWAPIFUNCTION(RWSTRING("Rt2dPathFlatten"));

    RWASSERT(path);

    for (target = path; NULL != target; target = target->next)
    {
        if (!target->flat)
        {
            Rt2dPath           *flat;

            flat = _rt2dScratchPath();
            _rt2dSubPathFlatten(flat, target);
            SubPathEmpty2d(target);
            SubPathCopy2d(target, flat);
        }
    }

    RWRETURN(path);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathDestroy
 * is used to destroy the specified path. All paths
 * created by an application must be destroyed before the application
 * closes down.
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \return TRUE if successful or FALSE if there is an error.
 * \see Rt2dPathCreate
 * \see Rt2dOpen
 * \see Rt2dClose
 */
RwBool
Rt2dPathDestroy(Rt2dPath * path)
{
    RwBool              result;

    RWAPIFUNCTION(RWSTRING("Rt2dPathDestroy"));

    /* NULL path is valid */
    result = (NULL != path);
    if (result)
    {
        Rt2dPathDestroy(SubPathDestroy2d(path));
    }

    RWRETURN(result);
}

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

/**
 * \ingroup rt2d
 * \ref Rt2dPathCreate
 * is used to create a new path. The path is initially
 * empty but may be constructed using any combination of the available
 * primitive line drawing procedures. A path may be closed or open,
 * connected or disjoint (that is, have multiple boundaries), depending
 * on how it has been constructed.
 *
 * Note that the current point for a newly created path is undefined. To
 * establish an initial current point use the function Rt2dPathMoveto.
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \return a pointer to the new path if successful or NULL if there is an
 * error.
 * \see Rt2dPathDestroy
 * \see Rt2dPathMoveto
 * \see Rt2dPathLineto
 * \see Rt2dPathCurveto
 * \see Rt2dPathRLineto
 * \see Rt2dPathRCurveto
 * \see Rt2dPathRect
 * \see Rt2dPathRoundRect
 * \see Rt2dPathOval
 */
Rt2dPath           *
Rt2dPathCreate(void)
{
    Rt2dPath           *path;

    RWAPIFUNCTION(RWSTRING("Rt2dPathCreate"));

    path = (Rt2dPath *) RwFreeListAlloc(Rt2dGlobals.pathFreeList);
    if ((Rt2dPath *)NULL != path)
    {
        path->segments = rwSListCreate(sizeof(rt2dPathNode));
        path->closed = FALSE;
        path->flat = TRUE;
        path->next = (Rt2dPath *)NULL;
        path->curr = path;
        path->inset = 0;
    }

    RWRETURN(path);
}

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

void
_rt2dPathClose(void)
{
    RWFUNCTION(RWSTRING("_rt2dPathClose"));

    Rt2dPathDestroy(Rt2dGlobals.scratch);

    RwFreeListDestroy(Rt2dGlobals.pathFreeList);

    Rt2dGlobals.pathFreeList = (RwFreeList *)NULL;

    RWRETURNVOID();
}

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

RwBool
_rt2dPathOpen(void)
{
    RWFUNCTION(RWSTRING("_rt2dPathOpen"));

    Rt2dGlobals.pathFreeList =
        RwFreeListCreate(sizeof(Rt2dPath), 20, 0);

    Rt2dGlobals.scratch = Rt2dPathCreate();

    RWRETURN(TRUE);
}

/* ongoing work.. */

/****************************************************************************/
#if (0)

static void
LineInset2d(RtCubicBezier target, const RwV2d source[2], const RwReal R)
{
    RwV2d               normal;
    RwV2d               delta;

    RWFUNCTION(RWSTRING("LineInset2d"));

    RwV2dLineNormal(&normal, &source[1], &source[2]);
    RwV2dScale(&delta, &normal, R);

    RwV2dAdd(&target[0], &source[0], &delta);
    RwV2dAdd(&target[3], &source[1], &delta);

    target[1].x = target[0].x - normal.y;
    target[1].y = target[0].y + normal.x;

    target[2].x = target[3].x + normal.y;
    target[2].y = target[3].y - normal.x;

    RWRETURNVOID();
}

/****************************************************************************/
static void
BezierInset2d(RtCubicBezier target,
              const RtCubicBezier source, const RwReal R)
{

    RwV2d               P0, D1, D2, P3;
    RwV2d               Q0, E1, E2, Q3;

    RWFUNCTION(RWSTRING("BezierInset2d"));

    P0.x = source[0].x;
    P0.y = source[0].y;
    P3.x = source[3].x;
    P3.y = source[3].y;
    D1.x = source[1].x - P0.x;
    D1.y = source[1].y - P0.y;
    D2.x = source[2].x - P3.x;
    D2.y = source[2].y - P3.y;

    Q0.x =
        P0.x + 3 * (D1.y * R) / sqrt(9 * RwPow(D1.y, 2) +
                                     9 * RwPow(D1.x, 2));
    Q0.y =
        P0.y - 3 * (D1.x * R) / sqrt(9 * RwPow(D1.y, 2) +
                                     9 * RwPow(D1.x, 2));
    Q3.x =
        2 * P0.x - 2 * P3.x + 3 * D1.x - 3 * D2.x + 3 * (P3.x - P0.x -
                                                         2 * D1.x +
                                                         D2.x) +
        3 * D1.x + P0.x +
        (3 * (2 * P0.y - 2 * P3.y + 3 * D1.y - 3 * D2.y) +
         6 * (P3.y - P0.y - 2 * D1.y + D2.y) +
         3 * D1.y) * R / sqrt(RwPow(3 *
                                    (2 * P0.y - 2 * P3.y + 3 * D1.y -
                                     3 * D2.y) + 6 * (P3.y - P0.y -
                                                      2 * D1.y + D2.y) +
                                    3 * D1.y,
                                    2) + RwPow(3 * (2 * P3.x -
                                                    2 * P0.x -
                                                    3 * D1.x +
                                                    3 * D2.x) +
                                               6 * (P0.x - P3.x +
                                                    2 * D1.x - D2.x) -
                                               3 * D1.x, 2));
    Q3.y =
        2 * P0.y - 2 * P3.y + 3 * D1.y - 3 * D2.y + 3 * (P3.y - P0.y -
                                                         2 * D1.y +
                                                         D2.y) +
        3 * D1.y + P0.y +
        (3 * (2 * P3.x - 2 * P0.x - 3 * D1.x + 3 * D2.x) +
         6 * (P0.x - P3.x + 2 * D1.x - D2.x) -
         3 * D1.x) * R / sqrt(RwPow(3 *
                                    (2 * P0.y - 2 * P3.y + 3 * D1.y -
                                     3 * D2.y) + 6 * (P3.y - P0.y -
                                                      2 * D1.y + D2.y) +
                                    3 * D1.y,
                                    2) + RwPow(3 * (2 * P3.x -
                                                    2 * P0.x -
                                                    3 * D1.x +
                                                    3 * D2.x) +
                                               6 * (P0.x - P3.x +
                                                    2 * D1.x - D2.x) -
                                               3 * D1.x, 2));
    E1.x =
        3 * D1.x +
        6 * (R * (P3.y - P0.y - 2 * D1.y + D2.y)) / sqrt(9 *
                                                         RwPow(D1.y,
                                                               2) +
                                                         9 * RwPow(D1.x,
                                                                   2)) -
        1.5 * (D1.y *
               (R *
                (36 * (D1.y * (P3.y - P0.y - 2 * D1.y + D2.y)) -
                 36 * (D1.x * (P0.x - P3.x + 2 * D1.x - D2.x))))) /
        RwPow(9 * RwPow(D1.y, 2) + 9 * RwPow(D1.x, 2), (3.0 / 2.0));
    E1.y =
        3 * D1.y +
        6 * (R * (P0.x - P3.x + 2 * D1.x - D2.x)) / sqrt(9 *
                                                         RwPow(D1.y,
                                                               2) +
                                                         9 * RwPow(D1.x,
                                                                   2)) +
        1.5 * (D1.x *
               (R *
                (36 * (D1.y * (P3.y - P0.y - 2 * D1.y + D2.y)) -
                 36 * (D1.x * (P0.x - P3.x + 2 * D1.x - D2.x))))) /
        RwPow(9 * RwPow(D1.y, 2) + 9 * RwPow(D1.x, 2), (3.0 / 2.0));
    E2.x =
        3 * (2 * P0.x - 2 * P3.x + 3 * D1.x - 3 * D2.x) + 6 * (P3.x -
                                                               P0.x -
                                                               2 *
                                                               D1.x +
                                                               D2.x) +
        3 * D1.x +
        R * (6 * (2 * P0.y - 2 * P3.y + 3 * D1.y - 3 * D2.y) +
             6 * (P3.y - P0.y - 2 * D1.y +
                  D2.y)) / sqrt(RwPow(3 * (2 * P0.y - 2 * P3.y +
                                           3 * D1.y - 3 * D2.y) +
                                      6 * (P3.y - P0.y - 2 * D1.y +
                                           D2.y) + 3 * D1.y,
                                      2) + RwPow(3 * (2 * P3.x -
                                                      2 * P0.x -
                                                      3 * D1.x +
                                                      3 * D2.x) +
                                                 6 * (P0.x - P3.x +
                                                      2 * D1.x - D2.x) -
                                                 3 * D1.x,
                                                 2)) -
        0.5 *
        ((3
          * (2 * P0.y - 2 * P3.y + 3 * D1.y - 3 * D2.y) + 6 * (P3.y -
                                                               P0.y -
                                                               2 *
                                                               D1.y +
                                                               D2.y) +
          3 * D1.y) * (R * (2 *
                            ((3
                              * (2 * P0.y - 2 * P3.y + 3 * D1.y -
                                 3 * D2.y) + 6 * (P3.y - P0.y -
                                                  2 * D1.y + D2.y) +
                              3 * D1.y) * (6 * (2 * P0.y - 2 * P3.y +
                                                3 * D1.y - 3 * D2.y) +
                                           6 * (P3.y - P0.y - 2 * D1.y +
                                                D2.y))) +
                            2 *
                            ((3
                              * (2 * P3.x - 2 * P0.x - 3 * D1.x +
                                 3 * D2.x) + 6 * (P0.x - P3.x +
                                                  2 * D1.x - D2.x) -
                              3 * D1.x) * (6 * (2 * P3.x - 2 * P0.x -
                                                3 * D1.x + 3 * D2.x) +
                                           6 * (P0.x - P3.x + 2 * D1.x -
                                                D2.x)))))) /
        RwPow(RwPow
              (3 * (2 * P0.y - 2 * P3.y + 3 * D1.y - 3 * D2.y) +
               6 * (P3.y - P0.y - 2 * D1.y + D2.y) + 3 * D1.y,
               2) + RwPow(3 * (2 * P3.x - 2 * P0.x - 3 * D1.x +
                               3 * D2.x) + 6 * (P0.x - P3.x + 2 * D1.x -
                                                D2.x) - 3 * D1.x, 2),
              (3.0 / 2.0));
    E2.y =
        3 * (2 * P0.y - 2 * P3.y + 3 * D1.y - 3 * D2.y) + 6 * (P3.y -
                                                               P0.y -
                                                               2 *
                                                               D1.y +
                                                               D2.y) +
        3 * D1.y +
        R * (6 * (2 * P3.x - 2 * P0.x - 3 * D1.x + 3 * D2.x) +
             6 * (P0.x - P3.x + 2 * D1.x -
                  D2.x)) / sqrt(RwPow(3 * (2 * P0.y - 2 * P3.y +
                                           3 * D1.y - 3 * D2.y) +
                                      6 * (P3.y - P0.y - 2 * D1.y +
                                           D2.y) + 3 * D1.y,
                                      2) + RwPow(3 * (2 * P3.x -
                                                      2 * P0.x -
                                                      3 * D1.x +
                                                      3 * D2.x) +
                                                 6 * (P0.x - P3.x +
                                                      2 * D1.x - D2.x) -
                                                 3 * D1.x,
                                                 2)) -
        0.5 *
        ((3
          * (2 * P3.x - 2 * P0.x - 3 * D1.x + 3 * D2.x) + 6 * (P0.x -
                                                               P3.x +
                                                               2 *
                                                               D1.x -
                                                               D2.x) -
          3 * D1.x) * (R * (2 *
                            ((3
                              * (2 * P0.y - 2 * P3.y + 3 * D1.y -
                                 3 * D2.y) + 6 * (P3.y - P0.y -
                                                  2 * D1.y + D2.y) +
                              3 * D1.y) * (6 * (2 * P0.y - 2 * P3.y +
                                                3 * D1.y - 3 * D2.y) +
                                           6 * (P3.y - P0.y - 2 * D1.y +
                                                D2.y))) +
                            2 *
                            ((3
                              * (2 * P3.x - 2 * P0.x - 3 * D1.x +
                                 3 * D2.x) + 6 * (P0.x - P3.x +
                                                  2 * D1.x - D2.x) -
                              3 * D1.x) * (6 * (2 * P3.x - 2 * P0.x -
                                                3 * D1.x + 3 * D2.x) +
                                           6 * (P0.x - P3.x + 2 * D1.x -
                                                D2.x)))))) /
        RwPow(RwPow
              (3 * (2 * P0.y - 2 * P3.y + 3 * D1.y - 3 * D2.y) +
               6 * (P3.y - P0.y - 2 * D1.y + D2.y) + 3 * D1.y,
               2) + RwPow(3 * (2 * P3.x - 2 * P0.x - 3 * D1.x +
                               3 * D2.x) + 6 * (P0.x - P3.x + 2 * D1.x -
                                                D2.x) - 3 * D1.x, 2),
              (3.0 / 2.0));

    target[0].x = Q0.x;
    target[0].y = Q0.y;

    target[3].x = Q3.x;
    target[3].y = Q3.y;

    target[1].x = target[0].x + E1.x * (1.0 / 3.0);
    target[1].y = target[0].y + E1.y * (1.0 / 3.0);

    target[2].x = target[3].x - E2.x * (1.0 / 3.0);
    target[2].y = target[3].y - E2.y * (1.0 / 3.0);

    RWRETURNVOID();
}
#endif /* (0) */

#if defined (__MWERKS__)
#if (defined(RWVERBOSE))
#pragma message (__FILE__ "/" _SKY_EXPAND(__LINE__) ": __MWERKS__ == " _SKY_EXPAND(__MWERKS__))
#endif /* (defined (__MWERKS__)) */
#if (__option (global_optimizer))
#pragma always_inline on
#endif /* (__option (global_optimizer)) */
#endif /*  defined (__MWERKS__) */
