1225 lines
31 KiB
C++
1225 lines
31 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Integer, rectangle and point utilities.
|
|
WARNING: Must be in a fixed (pre-loaded) seg on Mac.
|
|
|
|
***************************************************************************/
|
|
#include "util.h"
|
|
ASSERTNAME
|
|
|
|
|
|
/***************************************************************************
|
|
Calculates the GCD of two longs.
|
|
***************************************************************************/
|
|
long LwGcd(long lw1, long lw2)
|
|
{
|
|
return LuGcd(LwAbs(lw1), LwAbs(lw2));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Calculates the GCD of two unsigned longs.
|
|
***************************************************************************/
|
|
ulong LuGcd(ulong lu1, ulong lu2)
|
|
{
|
|
//Euclidean algorithm - keep mod'ing until we hit zero
|
|
if (lu1 == 0)
|
|
{
|
|
// if both are zero, return 1.
|
|
return lu2 == 0 ? 1 : lu2;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
lu2 %= lu1;
|
|
if (lu2 == 0)
|
|
return lu1 == 0 ? 1 : lu1;
|
|
lu1 %= lu2;
|
|
if (lu1 == 0)
|
|
return lu2;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Sort the two longs so the smaller is in *plw1.
|
|
***************************************************************************/
|
|
void SortLw(long *plw1, long *plw2)
|
|
{
|
|
if (*plw1 > *plw2)
|
|
{
|
|
long lwT = *plw1;
|
|
*plw1 = *plw2;
|
|
*plw2 = lwT;
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef MC_68020 //68020 version in utilmc.asm
|
|
#ifndef IN_80386 //80386 version inline in utilint.h
|
|
/***************************************************************************
|
|
Multiply lw by lwMul and divide by lwDiv without losing precision.
|
|
***************************************************************************/
|
|
long LwMulDiv(long lw, long lwMul, long lwDiv)
|
|
{
|
|
Assert(lwDiv != 0, "divide by zero error");
|
|
double dou;
|
|
|
|
dou = (double)lw * lwMul / lwDiv;
|
|
Assert(dou <= klwMax && dou >= klwMin, "overflow in LwMulDiv");
|
|
return (long)dou;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the quotient and set *plwRem to the remainder when (lw * lwMul)
|
|
is divided by lwDiv.
|
|
***************************************************************************/
|
|
long LwMulDivMod(long lw, long lwMul, long lwDiv, long *plwRem)
|
|
{
|
|
Assert(lwDiv != 0, "moding by 0");
|
|
AssertVarMem(plwRem);
|
|
double dou;
|
|
|
|
dou = (double)lw * lwMul;
|
|
*plwRem = (long)(dou % lwDiv);
|
|
dou /= lwDiv;
|
|
Assert(dou <= klwMax && dou >= klwMin, "overflow in LwMulDiv");
|
|
return (long)dou;
|
|
}
|
|
#endif //IN_80386
|
|
|
|
|
|
/***************************************************************************
|
|
Multiply two longs to get a 64 bit (signed) result.
|
|
***************************************************************************/
|
|
void MulLw(long lw1, long lw2, long *plwHigh, ulong *pluLow)
|
|
{
|
|
#ifdef IN_80386
|
|
__asm
|
|
{
|
|
mov eax,lw1
|
|
imul lw2
|
|
mov ebx,plwHigh
|
|
mov [ebx],edx
|
|
mov ebx,pluLow
|
|
mov [ebx],eax
|
|
}
|
|
#else //!IN_80386
|
|
double dou;
|
|
bool fNeg;
|
|
|
|
fNeg = 0 > (dou = (double)lw1 * lw2);
|
|
if (fNeg)
|
|
dou = -dou;
|
|
*plwHigh = (long)(dou / ((double)0x10000 * 0x10000));
|
|
*pluLow = dou - *plwHigh * ((double)0x10000 * 0x10000);
|
|
if (fNeg)
|
|
{
|
|
if (*pluLow == 0)
|
|
*plwHigh = -*plwHigh;
|
|
else
|
|
{
|
|
*pluLow = -*pluLow;
|
|
*plwHigh = ~*plwHigh;
|
|
}
|
|
}
|
|
#endif //!IN_80386
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Multiply lu by luMul and divide by luDiv without losing precision.
|
|
***************************************************************************/
|
|
ulong LuMulDiv(ulong lu, ulong luMul, ulong luDiv)
|
|
{
|
|
Assert(luDiv != 0, "divide by zero error");
|
|
|
|
#ifdef IN_80386
|
|
//REVIEW shonk: this will fault on overflow!
|
|
__asm
|
|
{
|
|
mov eax,lu
|
|
mul luMul
|
|
div luDiv
|
|
mov lu,eax
|
|
}
|
|
return lu;
|
|
#else //!IN_80386
|
|
double dou;
|
|
|
|
dou = (double)lu * luMul / luDiv;
|
|
Assert(dou <= kluMax && dou >= 0, "overflow in LuMulDiv");
|
|
return (ulong)dou;
|
|
#endif //!IN_80386
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Multiply two unsigned longs to get a 64 bit (unsigned) result.
|
|
***************************************************************************/
|
|
void MulLu(ulong lu1, ulong lu2, ulong *pluHigh, ulong *pluLow)
|
|
{
|
|
#ifdef IN_80386
|
|
__asm
|
|
{
|
|
mov eax,lu1
|
|
mul lu2
|
|
mov ebx,pluHigh
|
|
mov [ebx],edx
|
|
mov ebx,pluLow
|
|
mov [ebx],eax
|
|
}
|
|
#else //!IN_80386
|
|
double dou;
|
|
|
|
dou = (double)lu1 * lu2;
|
|
*pluHigh = (ulong)(dou / ((double)0x10000 * 0x10000));
|
|
*pluLow = dou - *pluHigh * ((double)0x10000 * 0x10000);
|
|
#endif //!IN_80386
|
|
}
|
|
#endif //!MC_68020
|
|
|
|
|
|
/***************************************************************************
|
|
Does a multiply and divide without losing precision, rounding away from
|
|
zero during the divide.
|
|
***************************************************************************/
|
|
long LwMulDivAway(long lw, long lwMul, long lwDiv)
|
|
{
|
|
Assert(lwDiv != 0, "divide by zero error");
|
|
long lwT, lwRem;
|
|
|
|
lwT = LwMulDivMod(lw, lwMul, lwDiv, &lwRem);
|
|
if (lwRem != 0)
|
|
{
|
|
//divide wasn't exact
|
|
if (lwT < 0)
|
|
lwT--;
|
|
else
|
|
lwT++;
|
|
}
|
|
|
|
return lwT;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Does a multiply and divide without losing precision, rounding away from
|
|
zero during the divide.
|
|
***************************************************************************/
|
|
ulong LuMulDivAway(ulong lu, ulong luMul, ulong luDiv)
|
|
{
|
|
Assert(luDiv != 0, "divide by zero error");
|
|
ulong luT;
|
|
|
|
//get rid of common factors
|
|
if (1 < (luT = LuGcd(lu, luDiv)))
|
|
{
|
|
lu /= luT;
|
|
luDiv /= luT;
|
|
}
|
|
if (1 < (luT = LuGcd(luMul, luDiv)))
|
|
{
|
|
luMul /= luT;
|
|
luDiv /= luT;
|
|
}
|
|
|
|
return LuMulDiv(lu, luMul, luDiv) + (luDiv > 1);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns lwNum divided by lwDen rounded away from zero.
|
|
***************************************************************************/
|
|
long LwDivAway(long lwNum, long lwDen)
|
|
{
|
|
Assert(lwDen != 0, "divide by zero");
|
|
|
|
//make sure lwDen is greater than zero
|
|
if (lwDen < 0)
|
|
{
|
|
lwDen = -lwDen;
|
|
lwNum = -lwNum;
|
|
}
|
|
if (lwNum < 0)
|
|
lwNum -= (lwDen - 1);
|
|
else
|
|
lwNum += (lwDen - 1);
|
|
return lwNum / lwDen;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns lwNum divided by lwDen rounded toward the closest integer.
|
|
***************************************************************************/
|
|
long LwDivClosest(long lwNum, long lwDen)
|
|
{
|
|
Assert(lwDen != 0, "divide by zero");
|
|
|
|
//make sure lwDen is greater than zero
|
|
if (lwDen < 0)
|
|
{
|
|
lwDen = -lwDen;
|
|
lwNum = -lwNum;
|
|
}
|
|
if (lwNum < 0)
|
|
lwNum -= lwDen / 2;
|
|
else
|
|
lwNum += lwDen / 2;
|
|
return lwNum / lwDen;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Rounds lwSrc to a multiple of lwBase. The rounding is done away from
|
|
zero. Equivalent to LwDivAway(lwSrc, lwBase) * lwBase.
|
|
***************************************************************************/
|
|
long LwRoundAway(long lwSrc, long lwBase)
|
|
{
|
|
Assert(lwBase != 0, "divide by zero");
|
|
|
|
//make sure lwBase is greater than zero
|
|
if (lwBase < 0)
|
|
lwBase = -lwBase;
|
|
if (lwSrc < 0)
|
|
lwSrc -= (lwBase - 1);
|
|
else
|
|
lwSrc += (lwBase - 1);
|
|
return lwSrc - (lwSrc % lwBase);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Rounds lwSrc to a multiple of lwBase. The rounding is done toward zero.
|
|
Equivalent to (lwSrc / lwBase) * lwBase.
|
|
***************************************************************************/
|
|
long LwRoundToward(long lwSrc, long lwBase)
|
|
{
|
|
Assert(lwBase != 0, "divide by zero");
|
|
|
|
//make sure lwBase is greater than zero
|
|
if (lwBase < 0)
|
|
lwBase = -lwBase;
|
|
return lwSrc - (lwSrc % lwBase);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Rounds lwSrc to the closest multiple of lwBase.
|
|
Equivalent to LwDivClosest(lwSrc, lwBase) * lwBase.
|
|
***************************************************************************/
|
|
long LwRoundClosest(long lwSrc, long lwBase)
|
|
{
|
|
Assert(lwBase != 0, "divide by zero");
|
|
|
|
//make sure lwBase is greater than zero
|
|
if (lwBase < 0)
|
|
lwBase = -lwBase;
|
|
if (lwSrc < 0)
|
|
lwSrc -= lwBase / 2;
|
|
else
|
|
lwSrc += lwBase / 2;
|
|
return lwSrc - (lwSrc % lwBase);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns fcmpGt, fcmpEq or fcmpLt according to whether (lwNum1 / lwDen2)
|
|
is greater than, equal to or less than (lwNum2 / lwDen2).
|
|
***************************************************************************/
|
|
ulong FcmpCompareFracs(long lwNum1, long lwDen1, long lwNum2, long lwDen2)
|
|
{
|
|
long lwHigh1, lwHigh2; //must be signed
|
|
ulong luLow1, luLow2; //must be unsigned
|
|
|
|
MulLw(lwNum1, lwDen2, &lwHigh1, &luLow1);
|
|
MulLw(lwNum2, lwDen1, &lwHigh2, &luLow2);
|
|
|
|
if (lwHigh1 > lwHigh2)
|
|
return fcmpGt;
|
|
if (lwHigh1 < lwHigh2)
|
|
return fcmpLt;
|
|
|
|
// the high 32 bits are the same, so just compare the low 32 bits
|
|
// (as unsigned longs)
|
|
Assert(lwHigh1 == lwHigh2, 0);
|
|
if (luLow1 > luLow2)
|
|
return fcmpGt;
|
|
if (luLow1 < luLow2)
|
|
return fcmpLt;
|
|
return fcmpEq;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Adjusts an index after an edit. *piv is the index to adjust, iv is
|
|
the index where the edit occurred, cvIns is the number of things inserted
|
|
and cvDel is the number deleted. Returns true iff *piv is not in
|
|
(iv, iv + cvDel). If *piv is in this interval, mins *piv with iv + cvIns
|
|
and returns false.
|
|
***************************************************************************/
|
|
bool FAdjustIv(long *piv, long iv, long cvIns, long cvDel)
|
|
{
|
|
AssertVarMem(piv);
|
|
AssertIn(iv, 0, kcbMax);
|
|
AssertIn(cvIns, 0, kcbMax);
|
|
AssertIn(cvDel, 0, kcbMax);
|
|
|
|
if (*piv <= iv)
|
|
return fTrue;
|
|
if (*piv < iv + cvDel)
|
|
{
|
|
*piv = LwMin(*piv, iv + cvIns);
|
|
return fFalse;
|
|
}
|
|
*piv += cvIns - cvDel;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Multiplies two longs. Asserts on overflow.
|
|
***************************************************************************/
|
|
long LwMul(long lw1, long lw2)
|
|
{
|
|
if (lw1 == 0)
|
|
return 0;
|
|
Assert((lw1 * lw2) / lw1 == lw2, "overflow");
|
|
return lw1 * lw2;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Asserts that the lw is >= lwMin and < lwLim.
|
|
***************************************************************************/
|
|
void AssertIn(long lw, long lwMin, long lwLim)
|
|
{
|
|
Assert(lw >= lwMin, "long too small");
|
|
Assert(lw < lwLim, "long too big");
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Swap bytes in pv according to bom. bom consists of up to 16
|
|
2-bit opcodes (packed from hi bit to low bit). The high bit of
|
|
each opcode indicates a long field (1) or short field (0). The low
|
|
bit of each opcode indicates whether the bytes are to be swapped
|
|
in the field (1) or left alone (0).
|
|
***************************************************************************/
|
|
void SwapBytesBom(void *pv, BOM bom)
|
|
{
|
|
byte b;
|
|
byte *pb = (byte *)pv;
|
|
|
|
Assert(size(short) == 2 && size(long) == 4, "code broken");
|
|
while (bom != 0)
|
|
{
|
|
if (bom & 0x80000000L)
|
|
{
|
|
// long field
|
|
AssertPvCb(pb, 4);
|
|
if (bom & 0x40000000L)
|
|
{
|
|
b = pb[3];
|
|
pb[3] = pb[0];
|
|
pb[0] = b;
|
|
b = pb[2];
|
|
pb[2] = pb[1];
|
|
pb[1] = b;
|
|
}
|
|
pb += 4;
|
|
}
|
|
else
|
|
{
|
|
// short field
|
|
AssertPvCb(pb, 2);
|
|
if (bom & 0x40000000L)
|
|
{
|
|
b = pb[1];
|
|
pb[1] = pb[0];
|
|
pb[0] = b;
|
|
}
|
|
pb += 2;
|
|
}
|
|
bom <<= 2;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Swap bytes within an array of short words.
|
|
***************************************************************************/
|
|
void SwapBytesRgsw(void *psw, long csw)
|
|
{
|
|
AssertIn(csw, 0, kcbMax);
|
|
AssertPvCb(psw, LwMul(csw, size(short)));
|
|
|
|
byte b;
|
|
byte *pb = (byte *)psw;
|
|
|
|
Assert(size(short) == 2, "code broken");
|
|
for ( ; csw > 0; csw--, pb += 2)
|
|
{
|
|
b = pb[1];
|
|
pb[1] = pb[0];
|
|
pb[0] = b;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Swap bytes within an array of long words.
|
|
***************************************************************************/
|
|
void SwapBytesRglw(void *plw, long clw)
|
|
{
|
|
AssertIn(clw, 0, kcbMax);
|
|
AssertPvCb(plw, LwMul(clw, size(long)));
|
|
|
|
byte b;
|
|
byte *pb = (byte *)plw;
|
|
|
|
Assert(size(long) == 4, "code broken");
|
|
for ( ; clw > 0; clw--, pb += 4)
|
|
{
|
|
b = pb[3];
|
|
pb[3] = pb[0];
|
|
pb[0] = b;
|
|
b = pb[2];
|
|
pb[2] = pb[1];
|
|
pb[1] = b;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Asserts that the given BOM indicates a struct having cb/size(long) longs
|
|
to be swapped (so SwapBytesRglw can legally be used on an array of
|
|
these).
|
|
***************************************************************************/
|
|
void AssertBomRglw(BOM bom, long cb)
|
|
{
|
|
BOM bomT;
|
|
long clw;
|
|
|
|
clw = cb / size(long);
|
|
Assert(cb == clw * size(long), "cb is not a multiple of size(long)");
|
|
AssertIn(clw, 1, 17);
|
|
bomT = -1L << 2 * (16 - clw);
|
|
Assert(bomT == bom, "wrong bom");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Asserts that the given BOM indicates a struct having cb/size(short) shorts
|
|
to be swapped (so SwapBytesRgsw can legally be used on an array of
|
|
these).
|
|
***************************************************************************/
|
|
void AssertBomRgsw(BOM bom, long cb)
|
|
{
|
|
BOM bomT;
|
|
long csw;
|
|
|
|
csw = cb / size(short);
|
|
Assert(cb == csw * size(short), "cb is not a multiple of size(short)");
|
|
AssertIn(csw, 1, 17);
|
|
bomT = 0x55555555 << 2 * (16 - csw);
|
|
Assert(bomT == bom, "wrong bom");
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Truncates a util point to a system point.
|
|
REVIEW shonk: should we assert on truncation? Should we truncate
|
|
on windows?
|
|
***************************************************************************/
|
|
PT::operator PTS(void)
|
|
{
|
|
AssertThisMem();
|
|
PTS pts;
|
|
|
|
MacWin(pts.h, pts.x) = SwTruncLw(xp);
|
|
MacWin(pts.v, pts.y) = SwTruncLw(yp);
|
|
return pts;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copies a system point to a util point.
|
|
***************************************************************************/
|
|
PT & PT::operator = (PTS &pts)
|
|
{
|
|
AssertThisMem();
|
|
xp = MacWin(pts.h, pts.x);
|
|
yp = MacWin(pts.v, pts.y);
|
|
return *this;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map a point from prcSrc coordinates to prcDst coordinates.
|
|
***************************************************************************/
|
|
void PT::Map(RC *prcSrc, RC *prcDst)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
long dzpSrc, dzpDst;
|
|
|
|
if ((dzpDst = prcDst->Dxp()) == (dzpSrc = prcSrc->Dxp()))
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
xp += prcDst->xpLeft - prcSrc->xpLeft;
|
|
}
|
|
else
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
xp = prcDst->xpLeft + LwMulDiv(xp - prcSrc->xpLeft,
|
|
dzpDst, dzpSrc);
|
|
}
|
|
if ((dzpDst = prcDst->Dyp()) == (dzpSrc = prcSrc->Dyp()))
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
yp += prcDst->ypTop - prcSrc->ypTop;
|
|
}
|
|
else
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
yp = prcDst->ypTop + LwMulDiv(yp - prcSrc->ypTop,
|
|
dzpDst, dzpSrc);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map a point from prcSrc coordinates to prcDst coordinates.
|
|
***************************************************************************/
|
|
PT PT::PtMap(RC *prcSrc, RC *prcDst)
|
|
{
|
|
AssertThisMem();
|
|
PT pt = *this;
|
|
pt.Map(prcSrc, prcDst);
|
|
return pt;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Transform the xp and yp values according to grfpt. Negating comes
|
|
before transposition.
|
|
***************************************************************************/
|
|
void PT::Transform(ulong grfpt)
|
|
{
|
|
AssertThisMem();
|
|
long zp;
|
|
|
|
if (grfpt & fptNegateXp)
|
|
xp = -xp;
|
|
if (grfpt & fptNegateYp)
|
|
yp = -yp;
|
|
if (grfpt & fptTranspose)
|
|
{
|
|
zp = xp;
|
|
xp = yp;
|
|
yp = zp;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Check for equality, special casing empty.
|
|
***************************************************************************/
|
|
bool RC::operator==(RC &rc)
|
|
{
|
|
AssertThisMem();
|
|
|
|
if (FEmpty())
|
|
return rc.FEmpty();
|
|
|
|
return xpLeft == rc.xpLeft && ypTop == rc.ypTop &&
|
|
xpRight == rc.xpRight && ypBottom == rc.ypBottom;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Check for non-equality, special casing empty.
|
|
***************************************************************************/
|
|
bool RC::operator!=(RC &rc)
|
|
{
|
|
AssertThisMem();
|
|
|
|
if (FEmpty())
|
|
return !rc.FEmpty();
|
|
|
|
return xpLeft != rc.xpLeft || ypTop != rc.ypTop ||
|
|
xpRight != rc.xpRight || ypBottom != rc.ypBottom;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Unionize the rects.
|
|
***************************************************************************/
|
|
void RC::Union(RC *prc1, RC *prc2)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc1);
|
|
AssertVarMem(prc2);
|
|
|
|
*this = *prc1;
|
|
Union(prc2);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Unionize the rects.
|
|
***************************************************************************/
|
|
void RC::Union(RC *prc)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc);
|
|
|
|
// if a rect is empty, it shouldn't contribute to the union
|
|
if (!prc->FEmpty())
|
|
{
|
|
if (FEmpty())
|
|
*this = *prc;
|
|
else
|
|
{
|
|
xpLeft = LwMin(xpLeft, prc->xpLeft);
|
|
xpRight = LwMax(xpRight, prc->xpRight);
|
|
ypTop = LwMin(ypTop, prc->ypTop);
|
|
ypBottom = LwMax(ypBottom, prc->ypBottom);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Intersect the rects and return whether the result is non-empty.
|
|
***************************************************************************/
|
|
bool RC::FIntersect(RC *prc1, RC *prc2)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc1);
|
|
AssertVarMem(prc2);
|
|
|
|
xpLeft = LwMax(prc1->xpLeft, prc2->xpLeft);
|
|
xpRight = LwMin(prc1->xpRight, prc2->xpRight);
|
|
ypTop = LwMax(prc1->ypTop, prc2->ypTop);
|
|
ypBottom = LwMin(prc1->ypBottom, prc2->ypBottom);
|
|
|
|
if (FEmpty())
|
|
{
|
|
Zero();
|
|
return fFalse;
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Intersect this rect with the given rect and return whether the result
|
|
is non-empty.
|
|
***************************************************************************/
|
|
bool RC::FIntersect(RC *prc)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc);
|
|
|
|
xpLeft = LwMax(xpLeft, prc->xpLeft);
|
|
xpRight = LwMin(xpRight, prc->xpRight);
|
|
ypTop = LwMax(ypTop, prc->ypTop);
|
|
ypBottom = LwMin(ypBottom, prc->ypBottom);
|
|
|
|
if (FEmpty())
|
|
{
|
|
Zero();
|
|
return fFalse;
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Inset a rectangle (into another)
|
|
***************************************************************************/
|
|
void RC::InsetCopy(RC *prc, long dxp, long dyp)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc);
|
|
|
|
xpLeft = prc->xpLeft + dxp;
|
|
xpRight = prc->xpRight - dxp;
|
|
ypTop = prc->ypTop + dyp;
|
|
ypBottom = prc->ypBottom - dyp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Inset a rectangle (in place)
|
|
***************************************************************************/
|
|
void RC::Inset(long dxp, long dyp)
|
|
{
|
|
AssertThisMem();
|
|
|
|
xpLeft += dxp;
|
|
xpRight -= dxp;
|
|
ypTop += dyp;
|
|
ypBottom -= dyp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map this rectangle through the two rectangles (from prcSrc
|
|
coordinates to prcDst coordinates). This cannot be either prcSrc
|
|
or prcDst.
|
|
***************************************************************************/
|
|
void RC::Map(RC *prcSrc, RC *prcDst)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
long dzpSrc, dzpDst;
|
|
|
|
if ((dzpDst = prcDst->Dxp()) == (dzpSrc = prcSrc->Dxp()))
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
xpLeft += prcDst->xpLeft - prcSrc->xpLeft;
|
|
xpRight += prcDst->xpLeft - prcSrc->xpLeft;
|
|
}
|
|
else
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
xpLeft = prcDst->xpLeft + LwMulDiv(xpLeft - prcSrc->xpLeft,
|
|
dzpDst, dzpSrc);
|
|
xpRight = prcDst->xpLeft + LwMulDiv(xpRight - prcSrc->xpLeft,
|
|
dzpDst, dzpSrc);
|
|
}
|
|
if ((dzpDst = prcDst->Dyp()) == (dzpSrc = prcSrc->Dyp()))
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
ypTop += prcDst->ypTop - prcSrc->ypTop;
|
|
ypBottom += prcDst->ypTop - prcSrc->ypTop;
|
|
}
|
|
else
|
|
{
|
|
AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
|
|
ypTop = prcDst->ypTop + LwMulDiv(ypTop - prcSrc->ypTop,
|
|
dzpDst, dzpSrc);
|
|
ypBottom = prcDst->ypTop + LwMulDiv(ypBottom - prcSrc->ypTop,
|
|
dzpDst, dzpSrc);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Transform the xp and yp values according to grfpt. Negating comes
|
|
before transposition.
|
|
***************************************************************************/
|
|
void RC::Transform(ulong grfpt)
|
|
{
|
|
AssertThisMem();
|
|
long zp;
|
|
|
|
if (grfpt & fptNegateXp)
|
|
{
|
|
zp = xpLeft;
|
|
xpLeft = -xpRight;
|
|
xpRight = -zp;
|
|
}
|
|
if (grfpt & fptNegateYp)
|
|
{
|
|
zp = ypTop;
|
|
ypTop = -ypBottom;
|
|
ypBottom = -zp;
|
|
}
|
|
if (grfpt & fptTranspose)
|
|
{
|
|
zp = xpLeft;
|
|
xpLeft = ypTop;
|
|
ypTop = zp;
|
|
zp = xpRight;
|
|
xpRight = ypBottom;
|
|
ypBottom = zp;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Move a rectangle (into another)
|
|
***************************************************************************/
|
|
void RC::OffsetCopy(RC *prc, long dxp, long dyp)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc);
|
|
|
|
xpLeft = prc->xpLeft + dxp;
|
|
xpRight = prc->xpRight + dxp;
|
|
ypTop = prc->ypTop + dyp;
|
|
ypBottom = prc->ypBottom + dyp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Move a rectangle (in place)
|
|
***************************************************************************/
|
|
void RC::Offset(long dxp, long dyp)
|
|
{
|
|
AssertThisMem();
|
|
|
|
xpLeft += dxp;
|
|
xpRight += dxp;
|
|
ypTop += dyp;
|
|
ypBottom += dyp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Move the rectangle so the top left is (0, 0).
|
|
***************************************************************************/
|
|
void RC::OffsetToOrigin(void)
|
|
{
|
|
AssertThisMem();
|
|
|
|
xpRight -= xpLeft;
|
|
ypBottom -= ypTop;
|
|
xpLeft = ypTop = 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Move this rectangle so it is centered over *prcBase.
|
|
***************************************************************************/
|
|
void RC::CenterOnRc(RC *prcBase)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prcBase);
|
|
long dxp = Dxp();
|
|
long dyp = Dyp();
|
|
|
|
xpLeft = (prcBase->xpLeft + prcBase->xpRight - dxp) / 2;
|
|
xpRight = xpLeft + dxp;
|
|
ypTop = (prcBase->ypTop + prcBase->ypBottom - dyp) / 2;
|
|
ypBottom = ypTop + dyp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Centers this rectangle on (xp, yp).
|
|
***************************************************************************/
|
|
void RC::CenterOnPt(long xp, long yp)
|
|
{
|
|
AssertThisMem();
|
|
long dxp = Dxp();
|
|
long dyp = Dyp();
|
|
|
|
xpLeft = xp - dxp / 2;
|
|
xpRight = xpLeft + dxp;
|
|
ypTop = yp - dyp / 2;
|
|
ypBottom = ypTop + dyp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Center this rectangle over *prcBase. If it doesn't fit inside *prcBase,
|
|
scale it down so it does.
|
|
***************************************************************************/
|
|
void RC::SqueezeIntoRc(RC *prcBase)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prcBase);
|
|
|
|
if (Dxp() <= prcBase->Dxp() && Dyp() <= prcBase->Dyp())
|
|
CenterOnRc(prcBase);
|
|
else
|
|
StretchToRc(prcBase);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Scale this rectangle proportionally (and translate it) so it is centered
|
|
on *prcBase and as large as possible but still inside *prcBase.
|
|
***************************************************************************/
|
|
void RC::StretchToRc(RC *prcBase)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prcBase);
|
|
long dxp = Dxp();
|
|
long dyp = Dyp();
|
|
long dxpBase = prcBase->Dxp();
|
|
long dypBase = prcBase->Dyp();
|
|
|
|
if (dxp <= 0 || dyp <= 0)
|
|
{
|
|
Bug("empty rc to stretch");
|
|
Zero();
|
|
return;
|
|
}
|
|
|
|
if (FcmpCompareFracs(dxp, dyp, dxpBase, dypBase) & fcmpLt)
|
|
{
|
|
// height dominated
|
|
dxp = LwMulDiv(dxp, dypBase, dyp);
|
|
dyp = dypBase;
|
|
}
|
|
else
|
|
{
|
|
// width dominated
|
|
dyp = LwMulDiv(dyp, dxpBase, dxp);
|
|
dxp = dxpBase;
|
|
}
|
|
xpRight = xpLeft + dxp;
|
|
ypBottom = ypTop + dyp;
|
|
CenterOnRc(prcBase);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determine if the given point is in the rectangle
|
|
***************************************************************************/
|
|
bool RC::FPtIn(long xp, long yp)
|
|
{
|
|
AssertThisMem();
|
|
|
|
return xp >= xpLeft && xp < xpRight &&
|
|
yp >= ypTop && yp < ypBottom;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Pin the point to the rectangle.
|
|
***************************************************************************/
|
|
void RC::PinPt(PT *ppt)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(ppt);
|
|
|
|
ppt->xp = LwBound(ppt->xp, xpLeft, xpRight);
|
|
ppt->yp = LwBound(ppt->yp, ypTop, ypBottom);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Pin this rectangle to the given one.
|
|
***************************************************************************/
|
|
void RC::PinToRc(RC *prc)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc);
|
|
long dxp, dyp;
|
|
|
|
dxp = LwMax(LwMin(0, prc->xpRight - xpRight), prc->xpLeft - xpLeft);
|
|
dyp = LwMax(LwMin(0, prc->ypBottom - ypBottom), prc->ypTop - ypTop);
|
|
if (dxp != 0 || dyp != 0)
|
|
Offset(dxp, dyp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copies a system rectangle to a util rectangle.
|
|
***************************************************************************/
|
|
RC & RC::operator = (RCS &rcs)
|
|
{
|
|
AssertThisMem();
|
|
|
|
xpLeft = (long)rcs.left;
|
|
xpRight = (long)rcs.right;
|
|
ypTop = (long)rcs.top;
|
|
ypBottom = (long)rcs.bottom;
|
|
return *this;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Truncates util rectangle to a system rectangle.
|
|
REVIEW shonk: should we assert on truncation?
|
|
***************************************************************************/
|
|
RC::operator RCS(void)
|
|
{
|
|
AssertThisMem();
|
|
RCS rcs;
|
|
|
|
rcs.left = SwTruncLw(xpLeft);
|
|
rcs.right = SwTruncLw(xpRight);
|
|
rcs.top = SwTruncLw(ypTop);
|
|
rcs.bottom = SwTruncLw(ypBottom);
|
|
return rcs;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the area of the rectangle.
|
|
***************************************************************************/
|
|
long RC::LwArea(void)
|
|
{
|
|
AssertThisMem();
|
|
|
|
if (FEmpty())
|
|
return 0;
|
|
return LwMul(xpRight - xpLeft, ypBottom - ypTop);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return whether this rectangle fully contains *prc.
|
|
***************************************************************************/
|
|
bool RC::FContains(RC *prc)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prc);
|
|
|
|
return prc->FEmpty() ||
|
|
prc->xpLeft >= xpLeft && prc->xpRight <= xpRight &&
|
|
prc->ypTop >= ypTop && prc->ypBottom <= ypBottom;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Imagine *prcSrc divided into a crcWidth by crcHeight grid. This sets
|
|
this rc to the (ircWidth, ircHeight) cell of the grid. prcSrc cannot
|
|
be equal to this.
|
|
***************************************************************************/
|
|
void RC::SetToCell(RC *prcSrc, long crcWidth, long crcHeight,
|
|
long ircWidth, long ircHeight)
|
|
{
|
|
AssertThisMem();
|
|
AssertVarMem(prcSrc);
|
|
AssertIn(crcWidth, 1, kcbMax);
|
|
AssertIn(crcHeight, 1, kcbMax);
|
|
AssertIn(ircWidth, 0, crcWidth);
|
|
AssertIn(ircHeight, 0, crcHeight);
|
|
Assert(this != prcSrc, "this can't be prcSrc");
|
|
|
|
xpLeft = prcSrc->xpLeft + LwMulDiv(prcSrc->Dxp(), ircWidth, crcWidth);
|
|
xpRight = prcSrc->xpLeft + LwMulDiv(prcSrc->Dxp(), ircWidth + 1, crcWidth);
|
|
ypTop = prcSrc->ypTop + LwMulDiv(prcSrc->Dyp(), ircHeight, crcHeight);
|
|
ypBottom = prcSrc->ypTop + LwMulDiv(prcSrc->Dyp(), ircHeight + 1, crcHeight);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determines which cell the given (xp, yp) is in. This is essentially
|
|
the inverse of SetToCell.
|
|
***************************************************************************/
|
|
bool RC::FMapToCell(long xp, long yp, long crcWidth, long crcHeight,
|
|
long *pircWidth, long *pircHeight)
|
|
{
|
|
AssertThisMem();
|
|
AssertIn(crcWidth, 1, kcbMax);
|
|
AssertIn(crcHeight, 1, kcbMax);
|
|
AssertVarMem(pircWidth);
|
|
AssertVarMem(pircHeight);
|
|
|
|
if (!FPtIn(xp, yp))
|
|
return fFalse;
|
|
*pircWidth = LwMulDiv(xp - xpLeft, crcWidth, Dxp());
|
|
*pircHeight = LwMulDiv(yp - ypTop, crcHeight, Dyp());
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Asserts the validity of a fraction.
|
|
***************************************************************************/
|
|
void RAT::AssertValid(ulong grf)
|
|
{
|
|
AssertIn(_lwDen, 1, klwMax);
|
|
long lwGcd = LwGcd(_lwNum, _lwDen);
|
|
Assert(lwGcd == 1, "fraction not in lowest terms");
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the master clock.
|
|
***************************************************************************/
|
|
USAC::USAC(void)
|
|
{
|
|
AssertThisMem();
|
|
_tsBaseSys = MacWin(TickCount(), timeGetTime());
|
|
_tsBaseApp = 0;
|
|
_luScale = kluTimeScaleNormal;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the current application time.
|
|
***************************************************************************/
|
|
ulong USAC::TsCur(void)
|
|
{
|
|
AssertThisMem();
|
|
ulong dtsSys = TsCurrentSystem() - _tsBaseSys;
|
|
|
|
if (_luScale != kluTimeScaleNormal)
|
|
{
|
|
ulong luHigh;
|
|
MulLu(dtsSys, _luScale, &luHigh, &dtsSys);
|
|
dtsSys = LwHighLow(SuLow(luHigh), SuHigh(dtsSys));
|
|
}
|
|
|
|
return _tsBaseApp + dtsSys;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Scale the time.
|
|
***************************************************************************/
|
|
void USAC::Scale(ulong luScale)
|
|
{
|
|
AssertThisMem();
|
|
ulong tsSys, dts;
|
|
|
|
if (luScale == _luScale)
|
|
return;
|
|
|
|
// set the _tsBaseSys and _tsBaseApp to now and set _luScale to luScale.
|
|
tsSys = TsCurrentSystem();
|
|
dts = tsSys - _tsBaseSys;
|
|
if (_luScale != kluTimeScaleNormal)
|
|
{
|
|
ulong luHigh;
|
|
MulLu(dts, _luScale, &luHigh, &dts);
|
|
dts = LwHighLow(SuLow(luHigh), SuHigh(dts));
|
|
}
|
|
_tsBaseApp += dts;
|
|
_tsBaseSys = tsSys;
|
|
_luScale = luScale;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the DVER structure.
|
|
***************************************************************************/
|
|
void DVER::Set(short swCur, short swBack)
|
|
{
|
|
_swBack = swBack;
|
|
_swCur = swCur;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determines if the DVER structure is compatible with (swCur and swMin).
|
|
Asserts that 0 <= swMin <= swCur.
|
|
***************************************************************************/
|
|
bool DVER::FReadable(short swCur, short swMin)
|
|
{
|
|
AssertIn(_swBack, 0, _swCur + 1);
|
|
AssertIn(_swCur, 0, kswMax);
|
|
AssertIn(swMin, 0, swCur + 1);
|
|
AssertIn(swCur, 0, kswMax);
|
|
return _swCur >= swMin && _swBack <= swCur;
|
|
}
|
|
|
|
|