Fix QSettings problems:
- Reopen file in ReadOnly mode if it was open in ReadWrite mode
and is being read. This is needed for stupid QSettings which
opens file in ReadWrite mode just to call readAll() on it.
- Implement setSize(0)
(*
* Hedgewars, a free turn based strategy game
* Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*)
{$INCLUDE "options.inc"}
unit uFloat;
(*
* This unit provides a custom data type, hwFloat.
*
* hwFloat represents a floating point number - the value and operations
* of this numbers are independent from the hardware architecture
* the game runs on.
*
* This is important for calculations that affect the course of the game
* and would lead to different results if based on a hardware dependent
* data type.
*
* Note: Not all comparisons are implemented.
*
* Note: Below you'll find a list of hwFloat constants:
* E.g. _1 is an hwFloat with value 1.0, and -_0_9 is -0.9
* Use and extend the list if needed, rather than using int2hwFloat()
* with integer constants.
*)
interface
{$IFDEF FPC}
{$IFDEF ENDIAN_LITTLE}
type hwFloat = record
isNegative: boolean;
case byte of
0: (Frac, Round: Longword);
1: (QWordValue : QWord);
end;
{$ELSE}
type hwFloat = record
isNegative: boolean;
case byte of
0: (Round, Frac: Longword);
1: (QWordValue : QWord);
end;
{$ENDIF}
// Returns an hwFloat that represents the value of integer parameter i
function int2hwFloat (const i: LongInt) : hwFloat; inline;
function hwFloat2Float (const i: hwFloat) : extended; inline;
// The implemented operators
operator = (const z1, z2: hwFloat) z : boolean; inline;
{$IFDEF PAS2C}
operator <> (const z1, z2: hwFloat) z : boolean; inline;
{$ENDIF}
operator + (const z1, z2: hwFloat) z : hwFloat; inline;
operator - (const z1, z2: hwFloat) z : hwFloat; inline;
operator - (const z1: hwFloat) z : hwFloat; inline;
operator * (const z1, z2: hwFloat) z : hwFloat; inline;
operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline;
operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
operator < (const z1, z2: hwFloat) b : boolean; inline;
operator > (const z1, z2: hwFloat) b : boolean; inline;
// Various functions for hwFloat (some are inlined in the resulting code for better performance)
function cstr(const z: hwFloat): shortstring; // Returns a shortstring representations of the hwFloat.
function hwRound(const t: hwFloat): LongInt; inline; // Does NOT really round but returns the integer representation of the hwFloat without fractional digits. (-_0_9 -> -0, _1_5 -> _1)
function hwAbs(const t: hwFloat): hwFloat; inline; // Returns the value of t with positive sign.
function hwSqr(const t: hwFloat): hwFloat; inline; // Returns the square value of parameter t.
function hwPow(const t: hwFloat; p: LongWord): hwFloat; inline; // Returns the power of the value
function hwSqrt(const t: hwFloat): hwFloat; inline; // Returns the the positive square root of parameter t.
function Distance(const dx, dy: hwFloat): hwFloat; // Returns the distance between two points in 2-dimensional space, of which the parameters are the horizontal and vertical distance.
function DistanceI(const dx, dy: LongInt): hwFloat; // Same as above for integer parameters.
function AngleSin(const Angle: Longword): hwFloat;
function AngleCos(const Angle: Longword): hwFloat;
function vector2Angle(const x, y: hwFloat): LongInt;
function SignAs(const num, signum: hwFloat): hwFloat; inline; // Returns an hwFloat with the value of parameter num and the sign of signum.
function hwSign(r: hwFloat): LongInt; inline; // Returns an integer with value 1 and sign of parameter r.
function hwSignf(r: real): LongInt; inline; // Returns an integer with value 1 and sign of parameter r.
function isZero(const z: hwFloat): boolean; inline;
{$IFDEF FPC}
{$J-}
{$ENDIF}
{$WARNINGS OFF}
// some hwFloat constants
const _1div1024: hwFloat = (isNegative: false; QWordValue: 4194304);
_1div10000: hwFloat = (isNegative: false; QWordValue: 429496);
_1div50000: hwFloat = (isNegative: false; QWordValue: 85899);
_1div100000: hwFloat = (isNegative: false; QWordValue: 42950);
_1div3: hwFloat = (isNegative: false; QWordValue: 1431655766);
hwPi: hwFloat = (isNegative: false; QWordValue: 13493037704);
_0_000004: hwFloat = (isNegative: false; QWordValue: 17179);
_0_000064: hwFloat = (isNegative: false; QWordValue: 274878);
_0_0002: hwFloat = (isNegative: false; QWordValue: 858993);
_0_0005: hwFloat = (isNegative: false; QWordValue: 2147484);
_0_001: hwFloat = (isNegative: false; QWordValue: 4294967);
_0_003: hwFloat = (isNegative: false; QWordValue: 12884902);
_0_0032: hwFloat = (isNegative: false; QWordValue: 13743895);
_0_004: hwFloat = (isNegative: false; QWordValue: 17179869);
_0_005: hwFloat = (isNegative: false; QWordValue: 21474836);
_0_008: hwFloat = (isNegative: false; QWordValue: 34359738);
_0_01: hwFloat = (isNegative: false; QWordValue: 42949673);
_0_0128: hwFloat = (isNegative: false; QWordValue: 54975581);
_0_02: hwFloat = (isNegative: false; QWordValue: 85899345);
_0_03: hwFloat = (isNegative: false; QWordValue: 128849018);
_0_07: hwFloat = (isNegative: false; QWordValue: 300647710);
_0_08: hwFloat = (isNegative: false; QWordValue: 343597383);
_0_1: hwFloat = (isNegative: false; QWordValue: 429496730);
_0_15: hwFloat = (isNegative: false; QWordValue: 644245094);
_0_2: hwFloat = (isNegative: false; QWordValue: 858993459);
_0_25: hwFloat = (isNegative: false; QWordValue: 1073741824);
_0_3: hwFloat = (isNegative: false; QWordValue: 1288490189);
_0_35: hwFloat = (isNegative: false; QWordValue: 1503238553);
_0_375: hwFloat = (isNegative: false; QWordValue: 4294967296 * 3 div 8);
_0_39: hwFloat = (isNegative: false; QWordValue: 1675037245);
_0_4: hwFloat = (isNegative: false; QWordValue: 1717986918);
_0_45: hwFloat = (isNegative: false; QWordValue: 1932735283);
_0_5: hwFloat = (isNegative: false; QWordValue: 2147483648);
_0_55: hwFloat = (isNegative: false; QWordValue: 2362232012);
_0_6: hwFloat = (isNegative: false; QWordValue: 2576980377);
_0_64: hwFloat = (isNegative: false; QWordValue: 2748779064);
_0_7: hwFloat = (isNegative: false; QWordValue: 3006477107);
_0_8: hwFloat = (isNegative: false; QWordValue: 3435973837);
_0_84: hwFloat = (isNegative: false; QWordValue: 3607772528);
_0_87: hwFloat = (isNegative: false; QWordValue: 3736621547);
_0_9: hwFloat = (isNegative: false; QWordValue: 3865470566);
_0_93: hwFloat = (isNegative: false; QWordValue: 3994319585);
_0_96: hwFloat = (isNegative: false; QWordValue: 4123168604);
_0_995: hwFloat = (isNegative: false; QWordValue: 4273492459);
_0_999: hwFloat = (isNegative: false; QWordValue: 4290672328);
_0: hwFloat = (isNegative: false; QWordValue: 0);
_1: hwFloat = (isNegative: false; QWordValue: 4294967296);
_1_2: hwFloat = (isNegative: false; QWordValue: 1288490189*4);
_1_5: hwFloat = (isNegative: false; QWordValue: 4294967296 * 3 div 2);
_1_6: hwFloat = (isNegative: false; QWordValue: 4294967296 * 8 div 5);
_1_9: hwFloat = (isNegative: false; QWordValue: 8160437862);
_2: hwFloat = (isNegative: false; QWordValue: 4294967296 * 2);
_2_4: hwFloat = (isNegative: false; QWordValue: 4294967296 * 12 div 5);
_3: hwFloat = (isNegative: false; QWordValue: 4294967296 * 3);
_3_2: hwFloat = (isNegative: false; QWordValue: 3435973837*4);
_PI: hwFloat = (isNegative: false; QWordValue: 13493037704);
_4: hwFloat = (isNegative: false; QWordValue: 4294967296 * 4);
_4_5: hwFloat = (isNegative: false; QWordValue: 4294967296 * 9 div 2);
_5: hwFloat = (isNegative: false; QWordValue: 4294967296 * 5);
_6: hwFloat = (isNegative: false; QWordValue: 4294967296 * 6);
_6_4: hwFloat = (isNegative: false; QWordValue: 3435973837 * 8);
_7: hwFloat = (isNegative: false; QWordValue: 4294967296 * 7);
_10: hwFloat = (isNegative: false; QWordValue: 4294967296 * 10);
_12: hwFloat = (isNegative: false; QWordValue: 4294967296 * 12);
_16: hwFloat = (isNegative: false; QWordValue: 4294967296 * 16);
_19: hwFloat = (isNegative: false; QWordValue: 4294967296 * 19);
_20: hwFloat = (isNegative: false; QWordValue: 4294967296 * 20);
_25: hwFloat = (isNegative: false; QWordValue: 4294967296 * 25);
_30: hwFloat = (isNegative: false; QWordValue: 4294967296 * 30);
_40: hwFloat = (isNegative: false; QWordValue: 4294967296 * 40);
_41: hwFloat = (isNegative: false; QWordValue: 4294967296 * 41);
_49: hwFloat = (isNegative: false; QWordValue: 4294967296 * 49);
_50: hwFloat = (isNegative: false; QWordValue: 4294967296 * 50);
_70: hwFloat = (isNegative: false; QWordValue: 4294967296 * 70);
_90: hwFloat = (isNegative: false; QWordValue: 4294967296 * 90);
_128: hwFloat = (isNegative: false; QWordValue: 4294967296 * 128);
_180: hwFloat = (isNegative: false; QWordValue: 4294967296 * 180);
_250: hwFloat = (isNegative: false; QWordValue: 4294967296 * 250);
_256: hwFloat = (isNegative: false; QWordValue: 4294967296 * 256);
_300: hwFloat = (isNegative: false; QWordValue: 4294967296 * 300);
_360: hwFloat = (isNegative: false; QWordValue: 4294967296 * 360);
_450: hwFloat = (isNegative: false; QWordValue: 4294967296 * 450);
_1000: hwFloat = (isNegative: false; QWordValue: 4294967296 * 1000);
_1024: hwFloat = (isNegative: false; QWordValue: 4294967296 * 1024);
_2048: hwFloat = (isNegative: false; QWordValue: 4294967296 * 2048);
_4096: hwFloat = (isNegative: false; QWordValue: 4294967296 * 4096);
_10000: hwFloat = (isNegative: false; QWordValue: 4294967296 * 10000);
cLittle: hwFloat = (isNegative: false; QWordValue: 1);
cHHKick: hwFloat = (isNegative: false; QWordValue: 42949673); // _0_01
{$WARNINGS ON}
{$ENDIF}
{$IFNDEF FPC}
type hwFloat = Extended;
{$ENDIF}
implementation
uses uSinTable;
{$IFDEF FPC}
function int2hwFloat (const i: LongInt) : hwFloat; inline;
begin
int2hwFloat.isNegative:= i < 0;
int2hwFloat.Round:= abs(i);
int2hwFloat.Frac:= 0
end;
function hwFloat2Float (const i: hwFloat) : extended; inline;
begin
hwFloat2Float:= i.Frac / $100000000 + i.Round;
if i.isNegative then
hwFloat2Float:= -hwFloat2Float;
end;
{$IFNDEF WEB}
operator = (const z1, z2: hwFloat) z : boolean; inline;
begin
z:= (z1.isNegative = z2.isNegative) and (z1.QWordValue = z2.QWordValue);
end;
{$IFDEF PAS2C}
operator <> (const z1, z2: hwFloat) z : boolean; inline;
begin
z:= (z1.isNegative <> z2.isNegative) or (z1.QWordValue <> z2.QWordValue);
end;
{$ENDIF}
operator + (const z1, z2: hwFloat) z : hwFloat; inline;
begin
if z1.isNegative = z2.isNegative then
begin
z.isNegative:= z1.isNegative;
z.QWordValue:= z1.QWordValue + z2.QWordValue
end
else
if z1.QWordValue > z2.QWordValue then
begin
z.isNegative:= z1.isNegative;
z.QWordValue:= z1.QWordValue - z2.QWordValue
end
else
begin
z.isNegative:= z2.isNegative;
z.QWordValue:= z2.QWordValue - z1.QWordValue
end
end;
operator - (const z1, z2: hwFloat) z : hwFloat; inline;
begin
if z1.isNegative = z2.isNegative then
if z1.QWordValue > z2.QWordValue then
begin
z.isNegative:= z1.isNegative;
z.QWordValue:= z1.QWordValue - z2.QWordValue
end
else
begin
z.isNegative:= not z2.isNegative;
z.QWordValue:= z2.QWordValue - z1.QWordValue
end
else
begin
z.isNegative:= z1.isNegative;
z.QWordValue:= z1.QWordValue + z2.QWordValue
end
end;
function isZero(const z: hwFloat): boolean; inline;
begin
isZero := z.QWordValue = 0;
end;
operator < (const z1, z2: hwFloat) b : boolean; inline;
begin
if z1.isNegative xor z2.isNegative then
b:= z1.isNegative
else
if z1.QWordValue = z2.QWordValue then
b:= false
else
b:= not((z1.QWordValue = z2.QWordValue) or ((z2.QWordValue < z1.QWordValue) <> z1.isNegative))
end;
operator > (const z1, z2: hwFloat) b : boolean; inline;
begin
if z1.isNegative xor z2.isNegative then
b:= z2.isNegative
else
if z1.QWordValue = z2.QWordValue then
b:= false
else
b:= (z1.QWordValue > z2.QWordValue) <> z2.isNegative
end;
{$ENDIF}
{$IFDEF WEB}
(*
Mostly to be kind to JS as of 2012-08-27 where there is no int64/uint64. This may change though.
*)
operator = (const z1, z2: hwFloat) z : boolean; inline;
begin
z:= (z1.isNegative = z2.isNegative) and (z1.Frac = z2.Frac) and (z1.Round = z2.Round);
end;
operator <> (const z1, z2: hwFloat) z : boolean; inline;
begin
z:= (z1.isNegative <> z2.isNegative) or (z1.Frac <> z2.Frac) or (z1.Round <> z2.Round);
end;
operator + (const z1, z2: hwFloat) z : hwFloat; inline;
begin
if z1.isNegative = z2.isNegative then
begin
z:= z1;
z.Frac:= z.Frac + z2.Frac;
z.Round:= z.Round + z2.Round;
if z.Frac<z1.Frac then inc(z.Round)
end
else
if (z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac)) then
begin
z.isNegative:= z1.isNegative;
z.Round:= z1.Round - z2.Round;
z.Frac:= z1.Frac - z2.Frac;
if z2.Frac > z1.Frac then dec(z.Round)
end
else
begin
z.isNegative:= z2.isNegative;
z.Round:= z2.Round - z1.Round;
z.Frac:= z2.Frac-z1.Frac;
if z2.Frac < z1.Frac then dec(z.Round)
end
end;
operator - (const z1, z2: hwFloat) z : hwFloat; inline;
begin
if z1.isNegative = z2.isNegative then
if (z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac)) then
begin
z.isNegative:= z1.isNegative;
z.Round:= z1.Round - z2.Round;
z.Frac:= z1.Frac-z2.Frac;
if z2.Frac > z1.Frac then dec(z.Round)
end
else
begin
z.isNegative:= not z2.isNegative;
z.Round:= z2.Round - z1.Round;
z.Frac:= z2.Frac-z1.Frac;
if z2.Frac < z1.Frac then dec(z.Round)
end
else
begin
z:= z1;
z.Frac:= z.Frac + z2.Frac;
z.Round:= z.Round + z2.Round;
if z.Frac<z1.Frac then inc(z.Round)
end
end;
operator < (const z1, z2: hwFloat) b : boolean; inline;
begin
if z1.isNegative xor z2.isNegative then
b:= z1.isNegative
else
(* Not so sure this specialcase is a win w/ Round/Frac. have to do more tests anyway.
if (z1.Round = z2.Round and (z1.Frac = z2.Frac)) then
b:= false
else *)
b:= ((z1.Round < z2.Round) or ((z1.Round = z2.Round) and (z1.Frac < z2.Frac))) <> z1.isNegative
end;
operator > (const z1, z2: hwFloat) b : boolean; inline;
begin
if z1.isNegative xor z2.isNegative then
b:= z2.isNegative
else
(*
if z1.QWordValue = z2.QWordValue then
b:= false
else*)
b:= ((z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac))) <> z1.isNegative
end;
function isZero(const z: hwFloat): boolean; inline;
begin
isZero := (z.Round = 0) and (z.Frac = 0);
end;
{$ENDIF}
operator - (const z1: hwFloat) z : hwFloat; inline;
begin
z:= z1;
z.isNegative:= not z.isNegative
end;
operator * (const z1, z2: hwFloat) z : hwFloat; inline;
begin
z.isNegative:= z1.isNegative xor z2.isNegative;
z.QWordValue:= QWord(z1.Round) * z2.Frac + QWord(z1.Frac) * z2.Round + ((QWord(z1.Frac) * z2.Frac) shr 32);
z.Round:= z.Round + QWord(z1.Round) * z2.Round;
end;
operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
begin
z.isNegative:= z1.isNegative xor (z2 < 0);
z.QWordValue:= z1.QWordValue * abs(z2)
end;
operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline;
var t: hwFloat;
begin
z.isNegative:= z1.isNegative xor z2.isNegative;
z.Round:= z1.QWordValue div z2.QWordValue;
t:= z1 - z2 * z.Round;
if t.QWordValue = 0 then
z.Frac:= 0
else
begin
while ((t.QWordValue and $8000000000000000) = 0) and ((z2.QWordValue and $8000000000000000) = 0) do
begin
t.QWordValue:= t.QWordValue shl 1;
z2.QWordValue:= z2.QWordValue shl 1
end;
if z2.Round > 0 then
z.Frac:= (t.QWordValue) div (z2.Round)
else
z.Frac:= 0
end
end;
operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
begin
z.isNegative:= z1.isNegative xor (z2 < 0);
z.QWordValue:= z1.QWordValue div abs(z2)
end;
function cstr(const z: hwFloat): shortstring;
var tmpstr: shortstring;
begin
str(z.Round, cstr);
if z.Frac <> 0 then
begin
str(z.Frac / $100000000, tmpstr);
delete(tmpstr, 1, 2);
cstr:= cstr + '.' + copy(tmpstr, 1, 10)
end;
if z.isNegative then
cstr:= '-' + cstr
end;
function hwRound(const t: hwFloat): LongInt;
begin
if t.isNegative then
hwRound:= -(t.Round and $7FFFFFFF)
else
hwRound:= t.Round and $7FFFFFFF
end;
function hwAbs(const t: hwFloat): hwFloat;
begin
hwAbs:= t;
hwAbs.isNegative:= false
end;
function hwSqr(const t: hwFloat): hwFloat; inline;
begin
hwSqr.isNegative:= false;
hwSqr.QWordValue:= ((QWord(t.Round) * t.Round) shl 32) + QWord(t.Round) * t.Frac * 2 + ((QWord(t.Frac) * t.Frac) shr 32);
end;
function hwPow(const t: hwFloat;p: LongWord): hwFloat;
begin
hwPow:= t;
if p mod 2 = 0 then hwPow.isNegative:= false;
while p > 0 do
begin
hwPow.QWordValue:= QWord(hwPow.Round) * t.Frac + QWord(hwPow.Frac) * t.Round + ((QWord(hwPow.Frac) * t.Frac) shr 32);
dec(p)
end
end;
function hwSqrt(const t: hwFloat): hwFloat;
const pwr = 8; // even value, feel free to adjust
rThreshold = 1 shl (pwr + 32);
lThreshold = 1 shl (pwr div 2 + 32);
var l, r: QWord;
c: hwFloat;
begin
hwSqrt.isNegative:= false;
if t.Round = 0 then
begin
l:= t.QWordValue;
r:= $100000000
end
else
begin
if t.QWordValue > $FFFFFFFFFFFF then // t.Round > 65535.9999
begin
l:= $10000000000; // 256
r:= $FFFFFFFFFFFF; // 65535.9999
end else
if t.QWordValue >= rThreshold then
begin
l:= lThreshold;
r:= $10000000000; // 256
end else
begin
l:= $100000000;
r:= lThreshold;
end;
end;
repeat
c.QWordValue:= (l + r) shr 1;
if hwSqr(c).QWordValue > t.QWordValue then
r:= c.QWordValue
else
l:= c.QWordValue
until r - l <= 1;
hwSqrt.QWordValue:= l
end;
function Distance(const dx, dy: hwFloat): hwFloat;
begin
Distance:= hwSqrt(hwSqr(dx) + hwSqr(dy))
end;
function DistanceI(const dx, dy: LongInt): hwFloat;
begin
DistanceI:= hwSqrt(int2hwFloat(sqr(dx) + sqr(dy)))
end;
function SignAs(const num, signum: hwFloat): hwFloat;
begin
SignAs.QWordValue:= num.QWordValue;
SignAs.isNegative:= signum.isNegative
end;
function hwSign(r: hwFloat): LongInt;
begin
// yes, we have negative zero for a reason
if r.isNegative then
hwSign:= -1
else
hwSign:= 1
end;
function hwSignf(r: real): LongInt;
begin
if r < 0 then
hwSignf:= -1
else
hwSignf:= 1
end;
function AngleSin(const Angle: Longword): hwFloat;
begin
//TryDo((Angle >= 0) and (Angle <= 2048), 'Sin param exceeds limits', true);
AngleSin.isNegative:= false;
if Angle < 1024 then
AngleSin.QWordValue:= SinTable[Angle]
else
AngleSin.QWordValue:= SinTable[2048 - Angle]
end;
function AngleCos(const Angle: Longword): hwFloat;
begin
//TryDo((Angle >= 0) and (Angle <= 2048), 'Cos param exceeds limits', true);
AngleCos.isNegative:= Angle > 1024;
if Angle < 1024 then
AngleCos.QWordValue:= SinTable[1024 - Angle]
else
AngleCos.QWordValue:= SinTable[Angle - 1024]
end;
function vector2Angle(const x, y: hwFloat): LongInt;
var d, nf: hwFloat;
l, r, c, oc: Longword;
n: QWord;
begin
d:= _1 / Distance(x, y);
nf:= y * d;
n:= nf.QWordValue;
l:= 0;
r:= 1024;
c:= 0;
repeat
oc:= c;
c:= (l + r) shr 1;
if n >= SinTable[c] then
l:= c
else
r:= c;
until (oc = c);
if x.isNegative then c:= 2048 - c;
if y.isNegative then c:= - c;
vector2Angle:= c
end;
{$ENDIF}
end.