Ejemplo n.º 1
0
        /// <summary>
        /// Encodes a coordinate on the sphere to the corresponding icosahedral face and
        /// containing 2D hex coordinates relative to that face center.
        /// </summary>
        /// <param name="g">The spherical coordinates to encode.</param>
        /// <param name="res">The desired H3 resolution for the encoding.</param>
        /// <returns>
        /// Tuple
        /// Item1: The resulting face
        /// Item2: The 2D hex coordinates of the cell containing the point.
        /// </returns>
        /// <!--
        /// faceijk.c
        /// void _geoToHex2d
        /// -->
        public static (int, Vec2d) ToHex2d(this GeoCoord g, int res)
        {
            var v3d = g.ToVec3d();

            var newFace = 0;

            // determine the icosahedron face
            decimal sqd = v3d.PointSquareDistance(Constants.FaceIjk.FaceCenterPoint[0]);

            for (var f = 1; f < Constants.H3.NUM_ICOSA_FACES; f++)
            {
                decimal sqdT = v3d.PointSquareDistance(Constants.FaceIjk.FaceCenterPoint[f]);
                if (!(sqdT < sqd))
                {
                    continue;
                }
                newFace = f;
                sqd     = sqdT;
            }

            // cos(r) = 1 - 2 * sin^2(r/2) = 1 - 2 * (sqd / 4) = 1 - sqd/2
            decimal r = DecimalEx.ACos(1 - sqd / 2.0m);

            if (r < Constants.H3.EPSILON)
            {
                return(newFace, new Vec2d());
            }
            // now have face and r, now find CCW theta from CII i-axis
            decimal theta =
                (
                    Constants.FaceIjk.FaceAxesAzRadsCii[newFace, 0] -
                    Constants.FaceIjk.FaceCenterGeo[newFace].AzimuthRadiansTo(g)
                    .NormalizeRadians()
                ).NormalizeRadians();

            // adjust theta for Class III (odd resolutions)
            if (res.IsResClassIii())
            {
                theta = (theta - Constants.H3.M_AP7_ROT_RADS).NormalizeRadians();
            }

            // perform gnomonic scaling of r
            r = DecimalEx.Tan(r);

            // scale for current resolution length u
            r /= Constants.H3.RES0_U_GNOMONIC;
            for (var i = 0; i < res; i++)
            {
                r *= Constants.FaceIjk.MSqrt7;
            }

            // we now have (r, theta) in hex2d with theta ccw from x-axes
            // convert to local x,y
            return(newFace,
                   new Vec2d
                   (
                       r * DecimalEx.Cos(theta),
                       r * DecimalEx.Sin(theta)
                   ));
        }
Ejemplo n.º 2
0
        public void PercentOfTest2DivideByZero()
        {
            // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
            Action test = () => DecimalEx.PercentOf(0, (Int64)100);

            test.ShouldThrow <DivideByZeroException>();
        }
Ejemplo n.º 3
0
 public Term Pow(decimal power)
 {
     var fundamental = ConvertUnitToFundamental();
     var resMagnitude = DecimalEx.Pow(fundamental.Magnitude, power);
     var resQuantity = fundamental.Quantity.Pow(power);
     return new(resMagnitude, resQuantity.FundamentalUnit);
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Mirrors across line through line segment. Returns a new matrix with the result.
        /// </summary>
        /// <param name="l">The across which to mirror.</param>
        public Transform2D Mirror(LineSeg2D l)
        {
            // See here: http://planetmath.org/encyclopedia/DerivationOf2DReflectionMatrix.html


            // Mirror around origin
            //
            //         0           1       2
            // 0   x^2 - y^2      2xy      0
            // 1      2xy      y^2 - x^2   0
            // 2       0           0       1

            var v      = l.GetVectorP1toP2().Normalize();
            var mirror = new Transform2D(
                new[, ]
            {
                { DecimalEx.Pow(v.X, 2) - DecimalEx.Pow(v.Y, 2), 2 * v.X * v.Y, 0 },
                { 2 * v.X * v.Y, DecimalEx.Pow(v.Y, 2) - DecimalEx.Pow(v.X, 2), 0 },
                { 0, 0, 1 }
            });

            // Translate to origin because mirroring around a vector is relative to the origin.
            var r = Translate(-l.Pt1.X, -l.Pt1.Y);

            // Left multiply this transformation matrix
            r = mirror.Multiply(r);

            // Translate back where we came from
            r = r.Translate(l.Pt1.X, l.Pt1.Y);

            return(r);
        }
Ejemplo n.º 5
0
        public List <CbRateViewModel> GetCbRates()
        {
            try
            {
                var reader = XmlReader.Create(BuildCbUrl(Currencies.USD, Enums.Days.Week));
                var list   = new List <CbRateViewModel>();
                var items  = ((CbRateListModel) new XmlSerializer(typeof(CbRateListModel)).Deserialize(reader)).Items;
                items.RemoveRange(0, items.Count - 2);
                var todayValue = DecimalEx.Parse(items.Last().Value);
                list.Add(new CbRateViewModel
                {
                    Currency = "$",
                    Value    = todayValue,
                    Diff     = todayValue - DecimalEx.Parse(items.First().Value)
                });
                reader = XmlReader.Create(BuildCbUrl(Currencies.EUR, Enums.Days.Week));
                items  = ((CbRateListModel) new XmlSerializer(typeof(CbRateListModel)).Deserialize(reader)).Items;
                items.RemoveRange(0, items.Count - 2);
                todayValue = DecimalEx.Parse(items.Last().Value);
                list.Add(new CbRateViewModel
                {
                    Currency = "€",
                    Value    = todayValue,
                    Diff     = todayValue - DecimalEx.Parse(items.First().Value)
                });

                return(list);
            }
            catch (Exception ex)
            {
                _logger.Trace("[RateService.GetCbRates] " + ex.Message);
                return(new List <CbRateViewModel>());
            }
        }
Ejemplo n.º 6
0
        public void TestRange()
        {
            var reset = new AutoResetEvent(false);
            var step  = 1m;
            var i     = 0m;

            while (true)
            {
                Task.Factory.StartNew(() =>
                {
                    Debug.WriteLine("Sqrt({0})={1}", i, DecimalEx.Sqrt(i));
                    reset.Set();
                });
                Assert.IsTrue(reset.WaitOne(30000));

                step *= 1.01m;
                try { i += step; }
                catch (OverflowException)
                {
                    if (i == Decimal.MaxValue)
                    {
                        break;
                    }
                    i = decimal.MaxValue;
                }
            }
        }
Ejemplo n.º 7
0
        public void PercentOfTestDivideByZero()
        {
            // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
            Action test = () => DecimalEx.PercentOf(0, 100);

            Assert.Throws <DivideByZeroException>(test);
        }
Ejemplo n.º 8
0
        static Prefixes()
        {
            Yotta = new("yotta", 1e24m, "Y");
            Zetta = new("zetta", 1e21m, "Z");
            Exa   = new("exa", 1e18m, "E");
            Peta  = new("peta", 1e15m, "P");
            Tera  = new("tera", 1e12m, "T");
            Giga  = new("giga", 1e9m, "G");
            Mega  = new("mega", 1e6m, "M");
            Kilo  = new("kilo", 1e3m, "k");
            Hecto = new("hecto", 1e2m, "h");
            Deca  = new("deca", 1e1m, "da");

            Deci  = new("deci", 1e-1m, "d");
            Centi = new("centi", 1e-2m, "c");
            Milli = new("milli", 1e-3m, "m");
            Micro = new("micro", 1e-6m, "μ");
            Nano  = new("nano", 1e-9m, "n");
            Pico  = new("pico", 1e-12m, "p");
            Femto = new("femto", 1e-15m, "f");
            Atto  = new("atto", 1e-18m, "a");
            Zepto = new("zepto", 1e-21m, "z");
            Yocto = new("yocto", 1e-24m, "y");

            Kibi = new("kibi", DecimalEx.Pow(2m, 10m), "Ki");
            Mebi = new("mebi", DecimalEx.Pow(2m, 20m), "Mi");
            Gibi = new("gibi", DecimalEx.Pow(2m, 30m), "Gi");
            Tebi = new("tebi", DecimalEx.Pow(2m, 40m), "Ti");
            Pebi = new("pebi", DecimalEx.Pow(2m, 50m), "Pi");
            Exbi = new("exbi", DecimalEx.Pow(2m, 60m), "Ei");
        }
Ejemplo n.º 9
0
        public async Task <decimal> CalcularMontanteAsync(decimal valorInicial, int tempo)
        {
            await BuscarTaxa();

            decimal calc1      = DecimalEx.Pow((1 + juros), tempo) * valorInicial;
            decimal valorFinal = Truncar(calc1);

            return(valorFinal);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// The great circle distance in radians between two spherical coordinates.
        /// This function uses the Haversine formula.
        /// For math details, see:
        ///     https://en.wikipedia.org/wiki/Haversine_formula
        ///     https://www.movable-type.co.uk/scripts/latlong.html
        /// </summary>
        /// <param name="a">the first lat/lng pair (in radians)</param>
        /// <param name="b">the second lat/lng pair (in radians)</param>
        /// <returns>
        /// the great circle distance in radians between a and b
        /// </returns>
        /// <!--
        /// geoCoord.c
        /// double H3_EXPORT(pointDistRads)
        /// -->
        public static decimal DistanceToRadians(this GeoCoord a, GeoCoord b)
        {
            decimal sinLat = DecimalEx.Sin((b.Latitude - a.Latitude) / 2.0m);
            decimal sinLng = DecimalEx.Sin((b.Longitude - a.Longitude) / 2.0m);
            decimal p      = sinLat * sinLat + DecimalEx.Cos(a.Latitude) *
                             DecimalEx.Cos(b.Latitude) * sinLng * sinLng;

            return(2 * DecimalEx.ATan2(DecimalEx.Sqrt(p), DecimalEx.Sqrt(1 - p)));
        }
Ejemplo n.º 11
0
        public static decimal Pow(this decimal value, decimal other)
        {
            if (value.IsNaN() || other.IsNaN())
            {
                return(Decimals.NaN);
            }

            return(DecimalEx.Pow(value, other));
        }
Ejemplo n.º 12
0
        public static decimal Sqrt(this decimal value)
        {
            if (value.IsNaN())
            {
                return(Decimals.NaN);
            }

            return(DecimalEx.Sqrt(value));
        }
Ejemplo n.º 13
0
 public void LogarithmCalculation()
 {
     Console.WriteLine("Enter your number.");
     LogNumber = decimal.Parse(Console.ReadLine());
     Console.WriteLine("Enter your base number.");
     BaseNumber    = decimal.Parse(Console.ReadLine());
     CalculatedLog = DecimalEx.Log(LogNumber) / DecimalEx.Log(BaseNumber);
     Console.WriteLine($"Your answer is {CalculatedLog}.");
 }
Ejemplo n.º 14
0
        private void CalculateWindChill()
        {
            var temperatureInF = Temperature;
            var windSpeedInMph = WindSpeed;

            if (temperatureInF.IsBetween(-45, 45) && windSpeedInMph.IsBetween(3, 60))
            {
                WindChill = 35.74m + 0.6215m * temperatureInF - 35.75m * DecimalEx.Pow(windSpeedInMph, 0.16m) + 0.4275m * temperatureInF * DecimalEx.Pow(windSpeedInMph, 0.16m);
            }
        }
Ejemplo n.º 15
0
        public decimal Calcular()
        {
            if (!IsValid())
            {
                return(0);
            }

            var jurosElevadoAoTempo = DecimalEx.Pow(1 + TaxaJuros, Tempo);

            return((ValorInicial * jurosElevadoAoTempo).Truncate(2));
        }
Ejemplo n.º 16
0
        public void pythaGorTheorem()
        {
            Console.Write("Enter A:\n");
            decimal ASquared = decimal.Parse(Console.ReadLine());

            Console.Write("Enter B:\n");
            decimal BSquared = decimal.Parse(Console.ReadLine());

            Answer = DecimalEx.Sqrt((ASquared * ASquared) + (BSquared * BSquared));
            Console.Write($"C is {Answer}.\n");
        }
Ejemplo n.º 17
0
 /// <summary>
 /// Determines the azimuth to p2 from p1 in radians
 /// </summary>
 /// <param name="p1">The first spherical coordinates</param>
 /// <param name="p2">The second spherical coordinates</param>
 /// <returns>The azimuth in radians from p1 to p2</returns>
 /// <!--
 /// geoCoord.c
 /// double _geoAzimuthRads
 /// -->
 internal static decimal AzimuthRadiansTo(this GeoCoord p1, GeoCoord p2)
 {
     return
         (DecimalEx.ATan2
          (
              DecimalEx.Cos(p2.Latitude) * DecimalEx.Sin(p2.Longitude - p1.Longitude),
              DecimalEx.Cos(p1.Latitude) * DecimalEx.Sin(p2.Latitude) -
              DecimalEx.Sin(p1.Latitude) * DecimalEx.Cos(p2.Latitude) *
              DecimalEx.Cos(p2.Longitude - p1.Longitude)
          ));
 }
Ejemplo n.º 18
0
        /// <summary>
        /// Calculate the 3D coordinate on unit sphere from the latitude and longitude.
        /// </summary>
        /// <param name="geo">The latitude and longitude of the point</param>
        /// <!--
        /// vec3d.c
        /// void _geoToVec3d
        /// -->
        public static Vec3d ToVec3d(this GeoCoord geo)
        {
            decimal r = DecimalEx.Cos(geo.Latitude);

            return(new Vec3d
                   (
                       DecimalEx.Cos(geo.Longitude) * r,
                       DecimalEx.Sin(geo.Longitude) * r,
                       DecimalEx.Sin(geo.Latitude)
                   ));
        }
Ejemplo n.º 19
0
        /// <summary>
        /// Scales a tolerance to match the precision of the expected value.
        /// </summary>
        public static decimal GetScaledTolerance(decimal expected, int tolerance, bool countTrailingZeros)
        {
            decimal toleranceAtScale = tolerance;
            var     precision        = DecimalEx.GetDecimalPlaces(expected, countTrailingZeros);

            for (var i = 0; i < precision; i++)
            {
                toleranceAtScale /= 10m;
            }
            return(toleranceAtScale);
        }
Ejemplo n.º 20
0
        // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        // Inverse Gamma 709 RGB -> RGB
        // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        static decimal[] inverse_gamma_709(ref decimal[] nonlinear_rgb709)
        {
            decimal[] linear_rgb709 = new decimal[3] {
                0, 0, 0
            };
            for (int i = 0; i < 3; i++)
            {
                linear_rgb709[i] = DecimalEx.Pow(Math.Abs(nonlinear_rgb709[i]), (decimal)2.4);
            }

            return(linear_rgb709);
        }
Ejemplo n.º 21
0
        // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        // Linear 2020 To NonLinear 2020
        // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        static decimal[] Inverse_gamma_2020(ref decimal[] linear_rgb2020)
        {
            decimal[] nonlinear_rgb2020 = new decimal[3] {
                0, 0, 0
            };
            for (int i = 0; i < 3; i++)
            {
                nonlinear_rgb2020[i] = DecimalEx.Pow(Math.Abs(linear_rgb2020[i]), (decimal)(1 / 2.4));
            }

            return(nonlinear_rgb2020);
        }
Ejemplo n.º 22
0
        public static BigIntegerRactional ConvertToRactional(decimal n)
        {
            var ndec = GetNumOfDecimals(n);

            if (ndec == 0)
            {
                return(BigIntegerRactional.Create((BigInteger)n));
            }

            var n2 = DecimalEx.Pow(10, ndec);

            return(BigIntegerRactional.Create((BigInteger)(n * n2), (BigInteger)n2));
        }
Ejemplo n.º 23
0
        private void CalculateDewPoint()
        {
            var relativeHumidity = Humidity;
            var temperatureInF   = Temperature;

            var temperatureInC = (temperatureInF - 32.0m) * 5.0m / 9.0m;

            var vaporPressure = relativeHumidity * 0.01m * 6.112m * DecimalEx.Exp(17.62m * temperatureInC / (temperatureInC + 243.12m));
            var numerator     = 243.12m * DecimalEx.Log(vaporPressure) - 440.1m;
            var denominator   = 19.43m - DecimalEx.Log(vaporPressure);
            var dewPointInC   = numerator / denominator;

            DewPoint = dewPointInC * 9.0m / 5.0m + 32.0m;
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Surface area in radians^2 of spherical triangle on unit sphere.
        ///
        /// For the math, see:
        /// https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess
        /// </summary>
        /// <param name="a">length of triangle side A in radians</param>
        /// <param name="b">length of triangle side B in radians</param>
        /// <param name="c">length of triangle side C in radians</param>
        /// <returns>area in radians^2 of triangle on unit sphere</returns>
        /// <!--
        /// geoCoord.c
        /// double triangleEdgeLengthsToArea
        /// -->
        private static decimal TriangleEdgeLengthToArea(decimal a, decimal b, decimal c)
        {
            decimal s = (a + b + c) / 2;

            a  = (s - a) / 2;
            b  = (s - b) / 2;
            c  = (s - c) / 2;
            s /= 2;

            return(4 * DecimalEx.ATan
                       (DecimalEx.Sqrt(DecimalEx.Tan(s) *
                                       DecimalEx.Tan(a) *
                                       DecimalEx.Tan(b) *
                                       DecimalEx.Tan(c))));
        }
Ejemplo n.º 25
0
        /// <summary>
        /// Determines the center point in spherical coordinates of a cell given by 2D
        /// hex coordinates on a particular icosahedral face.
        /// </summary>
        /// <param name="v">The 2D hex coordinates of the cell</param>
        /// <param name="face">The icosahedral face upon which the 2D hex coordinate system is centered</param>
        /// <param name="res">The H3 resolution of the cell</param>
        /// <param name="substrate">
        /// Indicates whether or not this grid is actually a substrate
        /// grid relative to the specified resolution.
        /// </param>
        /// <returns>The spherical coordinates of the cell center point</returns>
        /// <!--
        /// faceIjk.c
        /// void _hex2dToGeo
        /// -->
        public static GeoCoord ToGeoCoord(this Vec2d v, int face, int res, int substrate)
        {
            // calculate (r, theta) in hex2d
            decimal r          = v.Magnitude;
            bool    bSubstrate = substrate != 0;

            if (r < Constants.H3.EPSILON)
            {
                return(Constants.FaceIjk.FaceCenterGeo[face]);
            }

            decimal theta = DecimalEx.ATan2(v.Y, v.X);

            // scale for current resolution length u
            for (var i = 0; i < res; i++)
            {
                r /= Constants.FaceIjk.MSqrt7;
            }

            // scale accordingly if this is a substrate grid
            if (substrate != 0)
            {
                r /= 3.0m;
                if (res.IsResClassIii())
                {
                    r /= Constants.FaceIjk.MSqrt7;
                }
            }

            r *= Constants.H3.RES0_U_GNOMONIC;

            // perform inverse gnomonic scaling of r
            r = DecimalEx.ATan(r);

            // adjust theta for Class III
            // if a substrate grid, then it's already been adjusted for Class III
            if (!bSubstrate && res.IsResClassIii())
            {
                theta = (theta + Constants.H3.M_AP7_ROT_RADS).NormalizeRadians();
            }

            // find theta as an azimuth
            theta = (Constants.FaceIjk.FaceAxesAzRadsCii[face, 0] - theta).NormalizeRadians();

            // now find the point at (r,theta) from the face center
            return(Constants.FaceIjk.FaceCenterGeo[face]
                   .GetAzimuthDistancePoint(theta, r));
        }
Ejemplo n.º 26
0
        public void CountTrailingZeros()
        {
            var x = DecimalEx.SmallestNonZeroDec;

            for (int i = 28; i >= 0; i--)
            {
                Assert.That(DecimalEx.GetDecimalPlaces(x, false), Is.EqualTo(i));
                x *= 10;
            }

            x = -DecimalEx.SmallestNonZeroDec;
            for (int i = 28; i >= 0; i--)
            {
                Assert.That(DecimalEx.GetDecimalPlaces(x, false), Is.EqualTo(i));
                x *= 10;
            }
        }
Ejemplo n.º 27
0
        /// <summary>
        /// Промсвязьбанк
        /// </summary>
        private void GetPromsvyazbankBankRates()
        {
            var htmlWeb = new HtmlWeb();

            htmlWeb.OverrideEncoding = Encoding.GetEncoding("utf-8");
            try
            {
                var reqGet = WebRequest.Create(
                    @"http://www.psbank.ru/psbservices/SearchService.svc/GetCurrencyRatesSpecified?shortNames=%5B%22USD%22%2C%22EUR%22%5D");
                var     resp       = reqGet.GetResponse();
                var     stream     = resp.GetResponseStream();
                var     sr         = new System.IO.StreamReader(stream);
                var     jsonResult = sr.ReadToEnd();
                dynamic resObj     = JsonConvert.DeserializeObject(jsonResult);

                var date = DateTime.Parse(DateTime.UtcNow.ToShortDateString());
                var usd  = new CurrencyModel
                {
                    ValueName = Currencies.USD,
                    Buy       = DecimalEx.Parse(Convert.ToString(resObj[0].PurchasingRate)),
                    Sell      = DecimalEx.Parse(Convert.ToString(resObj[0].SellingRate))
                };
                var eur = new CurrencyModel
                {
                    ValueName = Currencies.EUR,
                    Buy       = DecimalEx.Parse(Convert.ToString(resObj[1].PurchasingRate)),
                    Sell      = DecimalEx.Parse(Convert.ToString(resObj[1].SellingRate))
                };

                _rates.Add(new RateUpdateModel
                {
                    Bank   = Banks.Promsvyazbank,
                    Date   = date,
                    CityId = (int)Enums.Cities.Krsk,
                    USD    = usd,
                    EUR    = eur
                });
            }
            catch (Exception ex)
            {
                _logger.Trace("[RateService.UpdateRates.Promsvyazbank] " + ex.Message);
            }
        }
Ejemplo n.º 28
0
        public void findTan()
        {
            Console.Write("Enter the number:\n");
            string stringConversions = Console.ReadLine();

            if (stringConversions.Contains("/"))
            {
                Fract.Fraction(stringConversions);
                FractionConverted = Fract.Fraction(stringConversions);
                Answer            = DecimalEx.Tan(decimalConverted);
                Console.WriteLine($"Your answer is {Answer}.\n");
            }
            else
            {
                decimalConverted = Decimal.Parse(stringConversions);
                Answer           = DecimalEx.Tan(decimalConverted);
                Console.WriteLine($"Your answer is {Answer}.\n");
            }
        }
Ejemplo n.º 29
0
        // for defining from a chain of operations
        protected Unit(string name, NamedComposition <IUnit> composition)
            : base(name)
        {
            // TODO: notify user that offsets will be ignored
            //var offsetQuery =
            //    from baseUnit in composition.Composition.Keys
            //    where baseUnit.FundamentalOffset != 0m
            //    select baseUnit;

            UnitComposition       = composition;
            Quantity              = Quantity.GetFromBaseComposition(UnitComposition);
            FundamentalMultiplier = 1m;
            FundamentalOffset     = 0;
            foreach (var(unit, power) in UnitComposition.Composition)
            {
                var multiplier = DecimalEx.Pow(unit.FundamentalMultiplier, power);
                FundamentalMultiplier *= multiplier;
            }
        }
Ejemplo n.º 30
0
        /// <summary>
        /// Rotates about the origin. Returns a new matrix with the result.
        /// </summary>
        /// <param name="degrees">The degrees to rotate.</param>
        /// <param name="clockwise">If False, then + degrees rotates counter clockwise.
        /// If True, then + degrees rotates clockwise. Of course, if the sign of the degrees
        /// is -, then the rotation will be opposite whatever the + direction is.</param>
        public Transform2D Rotate(decimal degrees, bool clockwise = false)
        {
            var r = new Transform2D();

            var theta = DecimalEx.ToRad(degrees);

            if (clockwise)
            {
                theta *= -1;
            }

            //      0     1    2
            // 0   cos  -sin   0
            // 1   sin   cos   0
            // 2    0     0    1

            r[0, 0] = DecimalEx.Cos(theta);
            r[0, 1] = -DecimalEx.Sin(theta);
            r[1, 0] = DecimalEx.Sin(theta);
            r[1, 1] = DecimalEx.Cos(theta);

            return(r.Multiply(this));
        }