/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                  COPYRIGHT 2001-2012 NUANCE COMMUNICATIONS                **
;**                                                                           **
;**               NUANCE COMMUNICATIONS PROPRIETARY INFORMATION               **
;**                                                                           **
;**     This software is supplied under the terms of a license agreement      **
;**     or non-disclosure agreement with Nuance Communications and may not    **
;**     be copied or disclosed except in accordance with the terms of that    **
;**     agreement.                                                            **
;**                                                                           **
;*******************************************************************************
;**                                                                           **
;**     FileName: et9ageometry.c                                              **
;**                                                                           **
;**  Description: Point and vector utilities.                                 **
;**                                                                           **
;*******************************************************************************
;******************************************************************************/


#include "et9ageometry.h"

/*================================================================== */
/* SWPoint Class Methods */
/*================================================================== */

/* Addition */
_SWPoint * ET9FARCALL _SWPoint_AddAssign(_SWPoint *pThis, const _SWPoint *p)        /* +sum points */
{
    pThis->x = (SBYTE2)(pThis->x + p->x);
    pThis->y = (SBYTE2)(pThis->y + p->y);
    return pThis;
}

_SWPoint ET9FARCALL _SWPoint_Add_P(const _SWPoint *p1, const _SWPoint *p2)
{
    _SWPoint pRet;
    pRet.x = p1->x + p2->x;
    pRet.y = p1->y + p2->y;
    return pRet;
}

/* Subtraction */
_SWPoint * ET9FARCALL _SWPoint_SubAssign(_SWPoint *pThis, const _SWPoint *p)        /* -dec points */
{
    pThis->x = (SBYTE2)(pThis->x - p->x);
    pThis->y = (SBYTE2)(pThis->y - p->y);
    return pThis;
}

_SWPoint ET9FARCALL _SWPoint_Sub_P(const _SWPoint *p1, const _SWPoint *p2)
{
    _SWPoint pRet;
    pRet.x = p1->x - p2->x;
    pRet.y = p1->y - p2->y;
    return pRet;
}

/* Multiplication */
_SWPoint * ET9FARCALL _SWPoint_MultAssign_SB2(_SWPoint *pThis,  SBYTE2 c ) {
    pThis->x  = (SBYTE2)(pThis->x * c);
    pThis->y  = (SBYTE2)(pThis->y * c);
    return pThis;
}

_SWPoint * ET9FARCALL _SWPoint_MultAssign_D(_SWPoint *pThis,  double c ) {
    pThis->x  = (SBYTE2)(pThis->x * c);
    pThis->y  = (SBYTE2)(pThis->y * c);
    return pThis;
}

_SWPoint ET9FARCALL _SWPoint_Mult_SB2P(SBYTE2 c, const _SWPoint *p1) {
    _SWPoint pRet;
    pRet.x = (SBYTE2)(c * p1->x);
    pRet.y = (SBYTE2)(c * p1->y);
    return pRet;
}

_SWPoint ET9FARCALL _SWPoint_Mult_DP(double c, const _SWPoint *p1) {
    _SWPoint pRet;
    pRet.x = (SBYTE2)(c * p1->x);
    pRet.y = (SBYTE2)(c * p1->y);
    return pRet;
}

_SWPoint ET9FARCALL _SWPoint_Mult_SB2(const _SWPoint *p1, SBYTE2 c) {
    _SWPoint pRet;
    pRet.x = (SBYTE2)(c * p1->x);
    pRet.y = (SBYTE2)(c * p1->y);
    return pRet;
}

_SWPoint ET9FARCALL _SWPoint_Mult_D(const _SWPoint *p1, double c) {
    _SWPoint pRet;
    pRet.x = (SBYTE2)(c * p1->x);
    pRet.y = (SBYTE2)(c * p1->y);
    return pRet;
}

/* Division */
_SWPoint * ET9FARCALL _SWPoint_DivAssign(_SWPoint *pThis, SBYTE2 c)
{
    pThis->x = (SBYTE2)(pThis->x / c);
    pThis->y = (SBYTE2)(pThis->y / c);
    return pThis;
}

_SWPoint ET9FARCALL _SWPoint_Div_SB2(const _SWPoint *p1, SBYTE2 c) {
    _SWPoint pRet;
    pRet.x = p1->x / c;
    pRet.y = p1->y / c;
    return pRet;
}

_SWPoint ET9FARCALL _SWPoint_Div_D(const _SWPoint *p1, double c) {
    _SWPoint pRet;
    pRet.x = (SBYTE2)(p1->x / c);
    pRet.y = (SBYTE2)(p1->y / c);
    return pRet;
}

/* Vector difference of _SWPoint's */
_SWVector ET9FARCALL _SWPoint_VSub_P(const _SWPoint *pThis, const _SWPoint *p)
{
    _SWVector v;
    v.sPoint.x = pThis->x - p->x;
    v.sPoint.y = pThis->y - p->y;
    return v;
}

/* Translation by a vector */
_SWPoint ET9FARCALL _SWPoint_Add_V(const _SWPoint *pThis, const _SWVector *v)
{
    _SWPoint pRet;
    pRet.x = pThis->x + v->sPoint.x;
    pRet.y = pThis->y + v->sPoint.y;
    return pRet;
}

_SWPoint ET9FARCALL _SWPoint_Sub_V(const _SWPoint *pThis, const _SWVector *v)
{
    _SWPoint pRet;
    pRet.x = pThis->x - v->sPoint.x;
    pRet.y = pThis->y - v->sPoint.y;
    return pRet;
}

_SWPoint * ET9FARCALL _SWPoint_AddAssign_V(_SWPoint *pThis, const _SWVector *v)
{
    pThis->x = (SBYTE2)(pThis->x + v->sPoint.x);
    pThis->y = (SBYTE2)(pThis->y + v->sPoint.y);
    return pThis;
}

_SWPoint * ET9FARCALL _SWPoint_SubAssign_V(_SWPoint *pThis, const _SWVector *v)
{
    pThis->x = (SBYTE2)(pThis->x - v->sPoint.x);
    pThis->y = (SBYTE2)(pThis->y - v->sPoint.y);
    return pThis;
}


/*------------------------------------------------------------------ */
/* Distance between Points */
/*------------------------------------------------------------------ */

/* The distance calculation algorithm is documented in the Z1 design document. */
BYTE2 ET9FARCALL _SWPoint_distance(const _SWPoint *pThis, const _SWPoint *p1) {       /* Euclidean distance approximation */
    BYTE4 S, L, L2, A, D, T;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);              /* calculate differences */
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);
    if (S > L) {T = L; L = S; S = T;}       /* Set S to short side, L to long */
    if (S == 0) return (BYTE2) L;
    if (L == 0) return 0;
    L2 = L * L;
    A = ((PRECISION_ADJ_L_MULT * L) - (PRECISION_ADJ_S_MULT * S)) * S * S;
    D = (A + (L2 >> 1)) / L2;               /* adjust by L2 / 2 for rounding */
    D = (D + PRECISION_ADJ_VAL/2) >> PRECISION_ADJ_EXP;    /* adjust by 2^X / 2 for rounding */
    return (BYTE2)(D + L);
}
BYTE2 ET9FARCALL _SWPoint_roughDistance(const _SWPoint *pThis, const _SWPoint *p1)
{       /* Euclidean distance rough approximation */
    BYTE4 S, L, D, T;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);              /* calculate differences */
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);
    if (S > L) {T = L; L = S; S = T;}       /* Set S to short side, L to long */
    if (L == 0) return 0;
    D = (S * 3) >> 3;
    return (BYTE2)(D + L);                  /* Approximate distance as L + (3/8 * S) */
}
/* The distance calculation algorithm is documented in the Z1 design document. */
/* preciseDistance() returns the distance times 2^11 (2048), retaining */
/* near-floating-point precision. */
/* Note this used to return distance times 2^12 (4096). */
BYTE4 ET9FARCALL _SWPoint_preciseDistance(const _SWPoint *pThis, const _SWPoint *p1) {        /* Euclidean distance approximation */
    BYTE4 S, L, L2, A, D, T;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);                      /* calculate differences */
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);
    if (S > L) {T = L; L = S; S = T;}       /* Set S to short side, L to long */
    if (S == 0) return (L << PRECISION_ADJ_EXP);
    if (L == 0) return 0;
    L2 = L * L;
    A = ((PRECISION_ADJ_L_MULT * L) - (PRECISION_ADJ_S_MULT * S)) * S * S;
    D = (A + (L2 >> 1)) / L2;               /* adjust by L2 / 2 for rounding */
    D += (L << PRECISION_ADJ_EXP);
    return(D);
}

/* The distance calculation algorithm is documented in the Z1 design document. */
/* distance8() returns the distance times 2^3 (8), retaining additional */
/* precision for scoring, while accomodating a returned distance value of up */
/* to 511 that can still be multiplied by a maximum potential weighting of 16 */
/* without overflow.  Distances greater than 2^13 (DISTANCE8_MAX) */
/* are detected, and the value DISTANCE8_MAX is returned.  Distances of 0 are */
/* returned as 1/8 (the actual value 0x0001) to distinguish them from */
/* uninitialized values). */
BYTE2 ET9FARCALL _SWPoint_distance8(const _SWPoint *pThis, const _SWPoint *p1) {      /* Euclidean distance approximation */
    BYTE4 S, L, L2, A, D, T;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);                      /* calculate differences */
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);
    if (S > L) {T = L; L = S; S = T;}   /* Set S to short side, L to long */
    if (L == 0)
    {
        return 1;
    }
    if (S == 0)
    {
        L = L << 3;
        if (L == 0)
            return 1;
        if (L > DISTANCE8_MAX)
            return (BYTE2) DISTANCE8_MAX;
        return (BYTE2) L;
    }
    L2 = L * L;
    A = ((PRECISION_ADJ_L_MULT * L) - (PRECISION_ADJ_S_MULT * S)) * S * S;
    D = (A + (L2 >> 1)) / L2;           /* adjust by L2 / 2 for rounding */
    D = (D >> (PRECISION_ADJ_EXP - 3)) + (L << 3);
    if (D > DISTANCE8_MAX)
        return (BYTE2) DISTANCE8_MAX;
    return (BYTE2) D;
}

/* The distance calculation algorithm is documented in the Z1 design document. */
/* distance8() returns the distance times 2^7 (128), retaining additional */
/* precision for scoring, while accomodating a returned distance value of up */
/* to 511 that can still be multiplied by a maximum potential weighting of 16 */
/* without overflow.  Distances greater than 2^9 (DISTANCE128_MAX) */
/* are detected, and the value DISTANCE128_MAX is returned.  Distances of 0 are */
/* returned as 1/128 (the actual value 0x0001) to distinguish them from */
/* uninitialized values). */
BYTE2 ET9FARCALL _SWPoint_distance128(const _SWPoint *pThis, const _SWPoint *p1) {      /* Euclidean distance approximation */
    BYTE4 S, L, L2, A, D, T;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);                      /* calculate differences */
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);
    if (S > L) {T = L; L = S; S = T;}   /* Set S to short side, L to long */
    if (L == 0)
    {
        return 1;
    }
    if (S == 0)
    {
        L = L << 7;
        if (L == 0)
            return 1;
        if (L > DISTANCE128_MAX)
            return (BYTE2) DISTANCE128_MAX;
        return (BYTE2) L;
    }
    L2 = L * L;
    A = ((PRECISION_ADJ_L_MULT * L) - (PRECISION_ADJ_S_MULT * S)) * S * S;
    D = (A + (L2 >> 1)) / L2;           /* adjust by L2 / 2 for rounding */
    D = (D >> (PRECISION_ADJ_EXP - 7)) + (L << 7);
    if (D > DISTANCE128_MAX)
        return (BYTE2) DISTANCE128_MAX;
    return (BYTE2) D;
}

/* The distance calculation algorithm is documented in the Z1 design document. */
/* distance2y8() doubles the magnitude of the Y component before computing */
/* the distance to differentially weight that component. */
/* distance2y8() returns the distance times 2^3 (8), retaining additional */
/* precision for scoring, while accomodating a returned distance value of up */
/* to 511 that can still be multiplied by a maximum potential weighting of 16 */
/* without overflow.  Distances greater than 2^13 (DISTANCE8_MAX) */
/* are detected, and the value DISTANCE8_MAX is returned.  Distances of 0 are */
/* returned as 1/8 (the actual value 0x0001) to distinguish them from */
/* uninitialized values). */
/* If if both points are ABOVE the line of key centers in the top row, or */
/* BELOW the line of key centers in the bottom row, then do NOT double the Y */
/* coordinate of the distance.  This allows overshoot above and below the top and */
/* bottom alphabetic rows to be un-penalized */
BYTE2 ET9FARCALL _SWPoint_distance2y8(const _SWScreenGeometry *pScreenGeometry, const _SWPoint *pThis, const _SWPoint *p1) {        /* Euclidean distance approximation */
    BYTE4 S, L;
    ET9BOOL bottomRow;
    ET9BOOL topRow;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);                  /* calculate differences */
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);                  /* Multiply the Y component by 2.0 */
    bottomRow = (ET9BOOL)((p1->y >= pScreenGeometry->bottomRowMidY) && (pThis->y >= pScreenGeometry->bottomRowMidY));
    topRow = (ET9BOOL)((p1->y <= pScreenGeometry->topRowMidY) && (pThis->y <= pScreenGeometry->topRowMidY));
    if (!bottomRow && !topRow)
    {
        L = L << 1;         /* Multiply the Y component by 2.0 */
    }
    else if (bottomRow)     /* Eliminate y-coordinate overshoot from the bottom row */
    {
        if (p1->y > pScreenGeometry->keyboardHeight)
            L -= (p1->y - pScreenGeometry->keyboardHeight);
        else if (pThis->y > pScreenGeometry->keyboardHeight)
            L -= (pThis->y - pScreenGeometry->keyboardHeight);
    }
    return (_SWPoint_distanceFromSL(S, L));
}
/* */
BYTE2 ET9FARCALL _SWPoint_distanceHalfx2y8(const _SWScreenGeometry *pScreenGeometry, const _SWPoint *pThis, const _SWPoint *p1) {       /* Euclidean distance approximation */
    BYTE4 S, L;
    ET9BOOL bottomRow;
    ET9BOOL topRow;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);                  /* calculate differences */
    S = S >> 1;
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);                  /* Multiply the Y component by 2.0 */
    bottomRow = (ET9BOOL)((p1->y >= pScreenGeometry->bottomRowMidY) && (pThis->y >= pScreenGeometry->bottomRowMidY));
    topRow = (ET9BOOL)((p1->y <= pScreenGeometry->topRowMidY) && (pThis->y <= pScreenGeometry->topRowMidY));
    if (!bottomRow && !topRow)
    {
        L = L << 1;         /* Multiply the Y component by 2.0 */
    }
    else if (bottomRow)     /* Eliminate y-coordinate overshoot from the bottom row */
    {
        if (p1->y > pScreenGeometry->keyboardHeight)
            L -= (p1->y - pScreenGeometry->keyboardHeight);
        else if (pThis->y > pScreenGeometry->keyboardHeight)
            L -= (pThis->y - pScreenGeometry->keyboardHeight);
    }
    return (_SWPoint_distanceFromSL(S, L));
}

BYTE2 ET9FARCALL _SWPoint_distanceHalfx8(const _SWPoint *pThis, const _SWPoint *p1) {     /* Euclidean distance approximation */
    BYTE4 S, L;

    S = (BYTE4)(BYTE2)absGeo(p1->x - pThis->x);                  /* calculate differences */
    S = S >> 1;                                         /* Divide the X component by 2.0 */
    L = (BYTE4)(BYTE2)absGeo(p1->y - pThis->y);
    return (_SWPoint_distanceFromSL(S, L));
}

BYTE2 ET9FARCALL _SWPoint_secondDif(const _SWPoint *pThis, const _SWPoint *prev, const _SWPoint *follow)
{       /* Second difference approximation */
    BYTE4 S, L, L2, A, D, T;

    _SWPoint difVector;
    difVector.x = (2 * pThis->x) - prev->x - follow->x;
    difVector.y = (2 * pThis->y) - prev->y - follow->y;

    S = (BYTE4)(BYTE2)absGeo((2 * pThis->y) - prev->y - follow->y);
    L = (BYTE4)(BYTE2)absGeo((2 * pThis->x) - prev->x - follow->x);              /* calculate differences */
    if (S > L) {T = L; L = S; S = T;}       /* Set S to short side, L to long */
    if (S == 0) return (BYTE2) L;
    if (L == 0) return 0;
    L2 = L * L;
    A = ((PRECISION_ADJ_L_MULT * L) - (PRECISION_ADJ_S_MULT * S)) * S * S;
    D = (A + (L2 >> 1)) / L2;               /* adjust by L2 / 2 for rounding */
    D = (D + PRECISION_ADJ_VAL/2) >> PRECISION_ADJ_EXP;    /* adjust by 2^X / 2 for rounding */
    return (BYTE2)(D + L);
}

BYTE2 ET9FARCALL _SWPoint_distanceFromSL(BYTE4 S, BYTE4 L) {     /* Euclidean distance approximation */
    BYTE4 L2, A, D, T;

    if (S > L) {T = L; L = S; S = T;}   /* Set S to short side, L to long */
    if (L == 0)
        return 1;
    if (S == 0)
    {
        L = L << 3;
        return (BYTE2) ((L) ? L : 1);
    }
    L2 = L * L;
    A = ((PRECISION_ADJ_L_MULT * L) - (PRECISION_ADJ_S_MULT * S)) * S * S;
    D = (A + (L2 >> 1)) / L2;           /* adjust by L2 / 2 for rounding */
    D = (D >> (PRECISION_ADJ_EXP - 3)) + (L << 3);
    if (D > DISTANCE8_MAX)
        return (BYTE2) DISTANCE8_MAX;
    return (BYTE2) D;
}

/* The distance calculation algorithm is documented in the Z1 design document. */
double ET9FARCALL _SWPoint_distanceF(const _SWPoint *pThis, const _SWPoint *p1) { /* Euclidean distance approximation */
    double R, A, D;
    double S = absGeo(p1->x - pThis->x);                 /* calculate differences */
    double L = absGeo(p1->y - pThis->y);
    if (S > L) {double T = L; L = S; S = T;}    /* Set S to short side, L to long */
    if (S == 0) return L;
    if (L == 0) return 0.0;
    R = S / L;
    A = PRECISION_ADJ_L_MULT_F - (PRECISION_ADJ_S_MULT_F * R);
    D = L + (A * R * S);
    return (D);
}


/*============================================================================= */
/* Function:    _SWPoint::intersectionPoint() */
/* Parameters:  _SWPoint p1, p2  - x and y coordinates of points determining first segment */
/*              _SWPoint p3, p4  - x and y coordinates of points determining second segment */
/* Description: Calculate the intersection of the lines defined by p1 & p2 and */
/*              by p3 & p4. */
/* Return:      Return false if the segments do not intersect */
/*              or true if they do. */
/*              Point coordinates are set to the coordinates of the Intersection point, if any. */
/*============================================================================= */

SBYTE2 ET9FARCALL _SWPoint_intersectionPoint(const _SWScreenGeometry *pScreenGeometry, _SWPoint *pThis, const _SWPoint *p1, const _SWPoint *p2, const _SWPoint *p3, const _SWPoint *p4 )
{
    _SWVector Segment1;
    _SWVector Segment12;
    _SWVector Segment2;
    SBYTE2 slopeChange;
    double denom, ua, xVal, yVal;
    ET9BOOL inP1P2, inP3P4;
    BYTE2   XYdistP1, XYdistP2, XYdistP3, XYdistP4;

    _SWVector_Construct_PP(&Segment1, p1, p2);
    _SWVector_Construct_PP(&Segment12, p2, p3);
    _SWVector_Construct_PP(&Segment2, p3, p4);

    slopeChange = _SWVector_slopeDifference(&Segment1, &Segment12) + _SWVector_slopeDifference(&Segment12, &Segment2);
    pThis->x = (SBYTE2)0;
    pThis->y = (SBYTE2)0;
    if (slopeChange < MIN_INTERSECTING_SLOPE_CHANGE)
        return(NON_INTERSECTING);
    denom = ((p4->y - p3->y) * (p2->x - p1->x)) - ((p4->x - p3->x) * (p2->y - p1->y));
    if (denom == 0.0)
    {
        return(NON_INTERSECTING);
    }
    ua = ((p4->x - p3->x) * (p1->y - p3->y)) - ((p4->y - p3->y) * (p1->x - p3->x));
    ua = ua / denom;
    xVal = p1->x + (ua * (p2->x - p1->x)) + 0.5;   /* Add 0.5 for integer truncation */
    yVal = p1->y + (ua * (p2->y - p1->y)) + 0.5;   /* Add 0.5 for integer truncation */
    pThis->x = (SBYTE2)xVal;
    pThis->y = (SBYTE2)yVal;
    /* (x, y) are the coordinates of an intersection point assuming two infinite lines */
    /* Determine whether actual segments intersect */
    inP1P2 = (ET9BOOL)(((pThis->x >= sw_min(p1->x, p2->x)) && (pThis->x <= sw_max(p1->x, p2->x))) && ((pThis->y >= sw_min(p1->y, p2->y)) && (pThis->y <= sw_max(p1->y, p2->y))));
    inP3P4 = (ET9BOOL)(((pThis->x >= sw_min(p3->x, p4->x)) && (pThis->x <= sw_max(p3->x, p4->x))) && ((pThis->y >= sw_min(p3->y, p4->y)) && (pThis->y <= sw_max(p3->y, p4->y))));
    /* Calculate distance of intersection point from each endpoint of segments */
    XYdistP1 = _SWPoint_distance8(pThis, p1);
    XYdistP2 = _SWPoint_distance8(pThis, p2);
    XYdistP3 = _SWPoint_distance8(pThis, p3);
    XYdistP4 = _SWPoint_distance8(pThis, p4);
    /* If intersection is within threshold distance of endpoint of segment, treat it as an intersection */
    if (!inP1P2)
        inP1P2 = (ET9BOOL)(sw_min(XYdistP1, XYdistP2) <= pScreenGeometry->keyWidth8);
    if (!inP3P4)
        inP3P4 = (ET9BOOL)(sw_max(XYdistP3, XYdistP4) <= pScreenGeometry->keyWidth8);
    /* Deal with simple cases first */
    if (inP1P2 && inP3P4)
        return(INTERSECTING);
    /* If intersection is in only one of the segments - i.e. some kind of "T" formation - then */
    /*   determine which end of non-containing segment should be checked to see if it is close enough to */
    /*   containing segment that the path segments should be separated */
    else if (inP1P2 && !inP3P4)
    {
        if (XYdistP3 < XYdistP4)
            return(CHECK_P3);
        else
            return(CHECK_P4);
    }
    else if (!inP1P2 && inP3P4)
    {
        if (XYdistP1 < XYdistP2)
            return(CHECK_P1);
        else
            return(CHECK_P2);
    }

    return(NON_INTERSECTING);
}

/*================================================================== */
/* SWVector Class Methods */
/*================================================================== */

/* Unary minus */
_SWVector ET9FARCALL _SWVector_Sub(const _SWVector *pThis) {
    _SWVector v;
    v.sPoint.x = -pThis->sPoint.x; v.sPoint.y = -pThis->sPoint.y;
    return v;
}

/* Multiplication */
_SWVector ET9FARCALL _SWVector_Mult_SB2V(SBYTE2 c, const _SWVector *v) {
    _SWVector vMult;
    vMult.sPoint.x = (SBYTE2)(c * v->sPoint.x);
    vMult.sPoint.y = (SBYTE2)(c * v->sPoint.y);
    return vMult;
}

_SWVector ET9FARCALL _SWVector_Mult_DV(double c, const _SWVector *v) {
    _SWVector vMult;
    vMult.sPoint.x = (SBYTE2)(c * v->sPoint.x);
    vMult.sPoint.y = (SBYTE2)(c * v->sPoint.y);
    return vMult;
}

_SWVector ET9FARCALL _SWVector_Mult_SB2(const _SWVector *v, SBYTE2 c) {
    _SWVector vMult;
    vMult.sPoint.x = (SBYTE2)(c * v->sPoint.x);
    vMult.sPoint.y = (SBYTE2)(c * v->sPoint.y);
    return vMult;
}

_SWVector ET9FARCALL _SWVector_Mult_D(const _SWVector *v, double c) {
    _SWVector vMult;
    vMult.sPoint.x = (SBYTE2)(c * v->sPoint.x);
    vMult.sPoint.y = (SBYTE2)(c * v->sPoint.y);
    return vMult;
}

/* Division */
_SWVector ET9FARCALL _SWVector_Div_SB2(const _SWVector *v, SBYTE2 c) {
    _SWVector vDiv;
    vDiv.sPoint.x = v->sPoint.x / c;
    vDiv.sPoint.y = v->sPoint.y / c;
    return vDiv;
}

_SWVector ET9FARCALL _SWVector_Div_D(const _SWVector *v, double c) {
    _SWVector vDiv;
    vDiv.sPoint.x = (SBYTE2)(v->sPoint.x / c);
    vDiv.sPoint.y = (SBYTE2)(v->sPoint.y / c);
    return vDiv;
}

_SWVector * ET9FARCALL _SWVector_DivAssign(_SWVector *pThis, double c) {        /* Vector scalar div */
    pThis->sPoint.x  = (SBYTE2)(pThis->sPoint.x / c);
    pThis->sPoint.y  = (SBYTE2)(pThis->sPoint.y / c);
    return pThis;
}

/*------------------------------------------------------------------ */
/*  Special Operations */
/*------------------------------------------------------------------ */

void ET9FARCALL _SWVector_normalize(_SWVector *pThis) {              /* convert to unit length */
    _SWPoint origin;
    _SWPoint_Construct_Z(&origin);
    pThis->length = _SWPoint_distance(&pThis->sPoint, &origin);
    if (pThis->length == 0) return;            /* do nothing for nothing */
    pThis->sPoint.x = (SBYTE2)(pThis->sPoint.x * NORM_VECTOR_LEN / pThis->length); /* normalize to 115 (360/pi) */
    pThis->sPoint.y = (SBYTE2)(pThis->sPoint.y * NORM_VECTOR_LEN / pThis->length);
}

/*============================================================================== */
/* Function:    calcOctant() */
/* Description: Determine slope octant of the vector. */
/*              Result is a number 0 - 7. */
/*============================================================================== */
void ET9FARCALL _SWVector_calcOctant(_SWVector *pThis) {

    /*  To represent the octant the slope of a vector, we define the following 8 */
    /*  octants, based on the following values: */
    /*  dX = (Xb >= Xa) */
    /*  dY = (Yb >= Ya)      // dX and dY are signed values */
    /*  |dX| and |dY|        // absolute magnitudes */
    /*  Octant:     dX >= 0     dY >= 0   |dX| >= |dY| */
    /*     0           1           1           1         //  1 => TRUE,   0 => FALSE  w.r.t. column header */
    /*     1           1           1           0 */
    /*     2           0           1           0 */
    /*     3           0           1           1 */
    /*     4           0           0           1 */
    /*     5           0           0           0 */
    /*     6           1           0           0 */
    /*     7           1           0           1 */

    BYTE1 index;
    static const BYTE1 which[] = { 5, 4, 2, 3, 6, 7, 1, 0 };    /* map results to octant */

    index = (pThis->sPoint.x >= 0) ? 4 : 0;
    index += (pThis->sPoint.y >= 0) ? 2 : 0;
    index += (absGeo(pThis->sPoint.x) >= absGeo(pThis->sPoint.y)) ? 1 : 0;
    pThis->octant = which[index];

} /* calcOctant() */

/*============================================================================== */
/* Function:    Distance() */
/* Description: Calculate the distance between the vectors and return it */
/*============================================================================== */
BYTE2 ET9FARCALL _SWVector_Distance(const _SWVector *pThis, const _SWVector *w)
{
    return (_SWPoint_distance(&pThis->sPoint, &w->sPoint));
} /* Distance() */

/*============================================================================== */
/* Function:    similarSlope() */
/* Description: First compare slope octants of vectors and return BYTE2_MAX if */
/*              the slopes are not in the same or adjacent octants.  If they are, */
/*              then return the distance between the vectors. */
/*============================================================================== */
BYTE2 ET9FARCALL _SWVector_similarSlope(const _SWVector *pThis, const _SWVector *w) {

    BYTE1 result = (pThis->octant > w->octant) ? pThis->octant - w->octant : w->octant - pThis->octant;

    return ( result == 0  ||  result == 1  ||  result == 7 ) ? (BYTE2)_SWPoint_distance(&pThis->sPoint, &w->sPoint)
                                                             : BYTE2_MAX;

} /* similarSlope() */

/*============================================================================== */
/* Function:    similarSlope() */
/* Description: Compare the distance between the vectors and return true if the */
/*              distance is less than slopeThreshold. */
/*============================================================================== */
ET9BOOL ET9FARCALL _SWVector_similarSlope_B2(const _SWVector *pThis, const _SWVector *w, BYTE2 slopeThreshold)
{
    return (ET9BOOL)(_SWPoint_distance(&pThis->sPoint, &w->sPoint) < slopeThreshold);
} /* similarSlope() */

/*============================================================================== */
/* Function:    slopeDifference() */
/* Description: Calculate the distance between the vectors and return it */
/*============================================================================== */
SBYTE2 ET9FARCALL _SWVector_slopeDifference(const _SWVector *pThis, const _SWVector *w)
{
    return (_SWPoint_distance(&pThis->sPoint, &w->sPoint));
} /* slopeDifference() */

/*============================================================================== */
/* Function:    signedSlopeDifference() */
/* Description: Calculate the signed distance between the vectors and return it */
/*============================================================================== */
SBYTE2 ET9FARCALL _SWVector_signedSlopeDifference(const _SWVector *pThis, const _SWVector *w)
{
    SBYTE2 slopeDif = _SWPoint_distance(&pThis->sPoint, &w->sPoint);
    ET9BOOL clockwise;   /* Determine whether clockwise (positive) vector relationship */
    if (pThis->octant == w->octant)
    {
        if (pThis->octant <= 3)        /* */
            clockwise = (ET9BOOL)(w->sPoint.x > pThis->sPoint.x);
        else
            clockwise = (ET9BOOL)(w->sPoint.x < pThis->sPoint.x);
    }
    else
    {
        BYTE1 oppositeOctant = pThis->octant + 4;
        if (oppositeOctant > 7)
            oppositeOctant -= 8;
        if (oppositeOctant == w->octant)
        {
            if (pThis->octant <= 3)        /* */
                clockwise = (ET9BOOL)(w->sPoint.x > -pThis->sPoint.x);
            else
                clockwise = (ET9BOOL)(w->sPoint.x < -pThis->sPoint.x);
        }
        else
        {
            if (pThis->octant <= 3)
                clockwise = (ET9BOOL)((w->octant < pThis->octant) || (w->octant > oppositeOctant));
            else
                clockwise = (ET9BOOL)((w->octant < pThis->octant) && (w->octant > oppositeOctant));
        }
    }
    if (clockwise)
        return (slopeDif);
    else
        return (-slopeDif);
} /* signedSlopeDifference() */

/*============================================================================== */
/* Function:    oppositeSlope() */
/* Description: Invert vector being compared, and return result from similarSlope() */
/*              for inverted vector. */
/*============================================================================== */
BYTE2 ET9FARCALL _SWVector_oppositeSlope(const _SWVector *pThis, const _SWVector *w)
{
    _SWVector wOpp;
    _SWVector_Construct_SB2SB2(&wOpp, (SBYTE2)-w->sPoint.x, (SBYTE2)-w->sPoint.y);
    return(_SWVector_similarSlope(pThis, &wOpp));

} /* oppositeSlope() */

/*============================================================================== */
/* Function:    verticalSlopeDifference() */
/* Description: Return the distance between the vector and a vector that is */
/*              straight down. */
/*============================================================================== */
SBYTE2 ET9FARCALL _SWVector_verticalSlopeDifference(const _SWVector *pThis)
{
    _SWVector vert;
    _SWVector_Construct_SB2SB2(&vert, (SBYTE2)0, (SBYTE2)NORM_VECTOR_LEN);
    return (_SWPoint_distance(&pThis->sPoint, &vert.sPoint));

} /* verticalSlopeDifference() */


/*/ <summary> */
/*/     Sets the screen geometry. */
/*/ </summary> */
/*/ <returns>void</returns> */
void ET9FARCALL _SWScreenGeometry_setScreenGeometry(_SWScreenGeometry *pThis, ET9INT keyHeight8, ET9INT keyWidth8, ET9INT keyboardHeight, ET9INT keyboardWidth, ET9INT topRowMidY, ET9INT bottomRowMidY) {
    pThis->keyHeight8 = keyHeight8;
    pThis->keyHeight = (keyHeight8 + 3) / 8;
    pThis->keyWidth8 = keyWidth8;
    pThis->keyWidth = (keyWidth8 + 3) / 8;
    pThis->keyRadius = (keyWidth8 + 7) / 16;
    pThis->keyboardHeight = keyboardHeight;
    pThis->keyboardWidth = keyboardWidth;
    pThis->topRowMidY = topRowMidY;
    pThis->bottomRowMidY = bottomRowMidY;
}

/*############################################################################# */
/* */
/*  The preceding software is   Copyright 2003  ForWord Input, Inc. */
/* */
/*############################################################################# */

/* SavedCodeSegments/SearchDB.cpp/44 */


/* eof */
