/* * Copyright 2016 The Cartographer Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using RobotNet10.Shared.Numbers; using SysNum = System.Numerics; namespace CartographerSharp.Common.Math; /// /// Common mathematical utilities for Cartographer. /// public static class MathUtils { /// /// Clamps 'value' to be in the range ['min', 'max']. /// public static T Clamp(T value, T min, T max) where T : IComparable { if (value.CompareTo(max) > 0) { return max; } if (value.CompareTo(min) < 0) { return min; } return value; } /// /// Calculates 'base'^'exponent'. /// public static T Power(T baseValue, int exponent) where T : SysNum.IMultiplyOperators, SysNum.IMultiplicativeIdentity { if (exponent == 0) { return T.MultiplicativeIdentity; } if (exponent < 0) { throw new ArgumentException("Exponent must be non-negative", nameof(exponent)); } T result = baseValue; for (int i = 1; i < exponent; i++) { result *= baseValue; } return result; } /// /// Calculates a^2. /// public static T Pow2(T a) where T : SysNum.IMultiplyOperators { return a * a; } /// /// Converts from degrees to radians. /// public static double DegToRad(double deg) { return System.Math.PI * deg / 180.0; } /// /// Converts from radians to degrees. /// public static double RadToDeg(double rad) { return 180.0 * rad / System.Math.PI; } /// /// Bring the 'difference' between two angles into [-pi; pi]. /// public static T NormalizeAngleDifference(T difference) where T : SysNum.IFloatingPoint { var kPi = T.CreateChecked(System.Math.PI); var twoPi = T.CreateChecked(2.0 * System.Math.PI); while (difference > kPi) { difference -= twoPi; } while (difference < -kPi) { difference += twoPi; } return difference; } /// /// Calculates atan2 for a 2D vector. /// public static double Atan2(RobotNet10.Shared.Numbers.Vector2 vector) { return System.Math.Atan2(vector.Y, vector.X); } /// /// Calculates quaternion product: z * w. /// /// First quaternion as array [w, x, y, z] /// Second quaternion as array [w, x, y, z] /// Output quaternion as array [w, x, y, z] public static void QuaternionProduct(ReadOnlySpan z, ReadOnlySpan w, Span zw) { if (z.Length < 4 || w.Length < 4 || zw.Length < 4) { throw new ArgumentException("Quaternion arrays must have at least 4 elements"); } // z = [w, x, y, z] in array, but Quaternion uses [x, y, z, w] order // So z[0] = w, z[1] = x, z[2] = y, z[3] = z zw[0] = z[0] * w[0] - z[1] * w[1] - z[2] * w[2] - z[3] * w[3]; // w component zw[1] = z[0] * w[1] + z[1] * w[0] + z[2] * w[3] - z[3] * w[2]; // x component zw[2] = z[0] * w[2] - z[1] * w[3] + z[2] * w[0] + z[3] * w[1]; // y component zw[3] = z[0] * w[3] + z[1] * w[2] - z[2] * w[1] + z[3] * w[0]; // z component } }