From 4216f66ca4e1f37ac40f14690bd6996b74a64b71 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 28 Jun 2014 14:16:00 +1200 Subject: [PATCH] Add Exts.ISqrt to avoid fp sqrt calculations. --- OpenRA.Game/CVec.cs | 2 +- OpenRA.Game/Exts.cs | 85 ++++++++++++++++++++++++++++ OpenRA.Game/Graphics/LineRenderer.cs | 5 +- OpenRA.Game/Map/Map.cs | 2 +- OpenRA.Game/Primitives/int2.cs | 2 +- OpenRA.Game/WVec.cs | 4 +- 6 files changed, 93 insertions(+), 7 deletions(-) diff --git a/OpenRA.Game/CVec.cs b/OpenRA.Game/CVec.cs index 206cfc3b29..b2008970f1 100644 --- a/OpenRA.Game/CVec.cs +++ b/OpenRA.Game/CVec.cs @@ -47,7 +47,7 @@ namespace OpenRA public CVec Sign() { return new CVec(Math.Sign(X), Math.Sign(Y)); } public CVec Abs() { return new CVec(Math.Abs(X), Math.Abs(Y)); } public int LengthSquared { get { return X * X + Y * Y; } } - public int Length { get { return (int)Math.Sqrt(LengthSquared); } } + public int Length { get { return Exts.ISqrt(LengthSquared); } } public float2 ToFloat2() { return new float2(X, Y); } public int2 ToInt2() { return new int2(X, Y); } diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs index 242b75d7f0..527bb0e88f 100644 --- a/OpenRA.Game/Exts.cs +++ b/OpenRA.Game/Exts.cs @@ -202,6 +202,91 @@ namespace OpenRA public static Size NextPowerOf2(this Size s) { return new Size(NextPowerOf2(s.Width), NextPowerOf2(s.Height)); } + public enum ISqrtRoundMode { Floor, Nearest, Ceiling } + public static int ISqrt(int number, ISqrtRoundMode round = ISqrtRoundMode.Floor) + { + if (number < 0) + throw new InvalidOperationException("Attempted to calculate the square root of a negative integer: {0}".F(number)); + + return (int)ISqrt((uint)number, round); + } + + public static uint ISqrt(uint number, ISqrtRoundMode round = ISqrtRoundMode.Floor) + { + var divisor = 1U << 30; + + var root = 0U; + var remainder = number; + + // Find the highest term in the divisor + while (divisor > number) + divisor >>= 2; + + // Evaluate the root, two bits at a time + while (divisor != 0) + { + if (root + divisor <= remainder) + { + remainder -= root + divisor; + root += 2 * divisor; + } + + root >>= 1; + divisor >>= 2; + } + + // Adjust for other rounding modes + if (round == ISqrtRoundMode.Nearest && remainder > root) + root += 1; + + else if (round == ISqrtRoundMode.Ceiling && root * root < number) + root += 1; + + return root; + } + + public static long ISqrt(long number, ISqrtRoundMode round = ISqrtRoundMode.Floor) + { + if (number < 0) + throw new InvalidOperationException("Attempted to calculate the square root of a negative integer: {0}".F(number)); + + return (long)ISqrt((ulong)number, round); + } + + public static ulong ISqrt(ulong number, ISqrtRoundMode round = ISqrtRoundMode.Floor) + { + var divisor = 1UL << 62; + + var root = 0UL; + var remainder = number; + + // Find the highest term in the divisor + while (divisor > number) + divisor >>= 2; + + // Evaluate the root, two bits at a time + while (divisor != 0) + { + if (root + divisor <= remainder) + { + remainder -= root + divisor; + root += 2 * divisor; + } + + root >>= 1; + divisor >>= 2; + } + + // Adjust for other rounding modes + if (round == ISqrtRoundMode.Nearest && remainder > root) + root += 1; + + else if (round == ISqrtRoundMode.Ceiling && root * root < number) + root += 1; + + return root; + } + public static string JoinWith(this IEnumerable ts, string j) { return string.Join(j, ts); diff --git a/OpenRA.Game/Graphics/LineRenderer.cs b/OpenRA.Game/Graphics/LineRenderer.cs index 0d74b9c6ac..2b16179a97 100644 --- a/OpenRA.Game/Graphics/LineRenderer.cs +++ b/OpenRA.Game/Graphics/LineRenderer.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -8,6 +8,7 @@ */ #endregion +using System; using System.Drawing; namespace OpenRA.Graphics @@ -98,7 +99,7 @@ namespace OpenRA.Graphics var yc = (r.Bottom + r.Top) / 2; for (var y = r.Top; y <= r.Bottom; y++) { - var dx = a * System.Convert.ToSingle(System.Math.Sqrt(1 - (y - yc) * (y - yc) / b / b)); + var dx = a * (float)(Math.Sqrt(1 - (y - yc) * (y - yc) / b / b)); DrawLine(new float2(xc - dx, y), new float2(xc + dx, y), color, color); } } diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 2cb72c682e..21083da355 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -623,7 +623,7 @@ namespace OpenRA for (var j = -max; j <= max; j++) for (var i = -max; i <= max; i++) if (max * max >= i * i + j * j) - ts [(int)Math.Ceiling(Math.Sqrt(i * i + j * j))].Add(new CVec(i, j)); + ts [Exts.ISqrt(i * i + j * j, Exts.ISqrtRoundMode.Ceiling)].Add(new CVec(i, j)); return ts; } diff --git a/OpenRA.Game/Primitives/int2.cs b/OpenRA.Game/Primitives/int2.cs index 6997e9ef6f..80f2360c8d 100644 --- a/OpenRA.Game/Primitives/int2.cs +++ b/OpenRA.Game/Primitives/int2.cs @@ -35,7 +35,7 @@ namespace OpenRA public int2 Sign() { return new int2(Math.Sign(X), Math.Sign(Y)); } public int2 Abs() { return new int2(Math.Abs(X), Math.Abs(Y)); } public int LengthSquared { get { return X * X + Y * Y; } } - public int Length { get { return (int)Math.Sqrt(LengthSquared); } } + public int Length { get { return Exts.ISqrt(LengthSquared); } } public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); } public static int2 Max(int2 a, int2 b) { return new int2(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); } diff --git a/OpenRA.Game/WVec.cs b/OpenRA.Game/WVec.cs index cc11c81722..53077558bc 100644 --- a/OpenRA.Game/WVec.cs +++ b/OpenRA.Game/WVec.cs @@ -37,9 +37,9 @@ namespace OpenRA public static int Dot(WVec a, WVec b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } public long LengthSquared { get { return (long)X * X + (long)Y * Y + (long)Z * Z; } } - public int Length { get { return (int)Math.Sqrt(LengthSquared); } } + public int Length { get { return (int)Exts.ISqrt(LengthSquared); } } public long HorizontalLengthSquared { get { return (long)X * X + (long)Y * Y; } } - public int HorizontalLength { get { return (int)Math.Sqrt(HorizontalLengthSquared); } } + public int HorizontalLength { get { return (int)Exts.ISqrt(HorizontalLengthSquared); } } public WVec Rotate(WRot rot) {