Пишу сижу последние дни всякую простейшую математику, которая часто юзается.
В итоге вышел вот такой класс:
using System; using System.Linq; using System.Text.RegularExpressions; using UnityEngine; public static class CustomMath { //Возвращает индекс минимального элемента public static int MinIndex(float[] values) { return GetABResultIndex<float>(values, (a, b) => a > b, f => true); } //Возвращает индекс минимального элемента, при том у элемента должно выполнять некоторое условие public static int MinIndex(float[] values, Predicate<float> condition) { return GetABResultIndex<float>(values, (a, b) => a > b, condition); } //Возвращает индекс максимального элемента public static int MaxIndex(float[] values) { return GetABResultIndex<float>(values, (a, b) => a < b, f => true); } //Возвращает индекс минимального элемента, при том у элемента должно выполнять некоторое условие public static int MaxIndex(float[] values, Predicate<float> condition) { return GetABResultIndex<float>(values, (a, b) => a < b, condition); } private static int GetABResultIndex<T>(T[] values, Func<T, T, bool> conditionAB, Predicate<T> conditionElement) { var resultIndex = -1; for (int i = 0; i < values.Length; i++) { if ((resultIndex == -1 || conditionAB(values[resultIndex], values[i])) && conditionElement(values[i])) resultIndex = i; } return -1; } //Функции округления тупо продублированы с понятными мне именами public static float Round(float value) { return Mathf.Round(value); } public static float RoundMin(float value) { return Mathf.Floor(value); } public static float RoundMax(float value) { return Mathf.Ceil(value); } public static float RoundToInt(float value) { return Mathf.RoundToInt(value); } public static float RoundMinToInt(float value) { return Mathf.FloorToInt(value); } public static float RoundMaxToInt(float value) { return Mathf.CeilToInt(value); } //Округление векторов, епты public static Vector3 Round(Vector3 value) { return new Vector3(Mathf.Round(value.x), Mathf.Round(value.y), Mathf.Round(value.z)); } public static Vector3 RoundMin(Vector3 value) { return new Vector3(Mathf.Floor(value.x), Mathf.Floor(value.y), Mathf.Floor(value.z)); } public static Vector3 RoundMax(Vector3 value) { return new Vector3(Mathf.Ceil(value.x), Mathf.Ceil(value.y), Mathf.Ceil(value.z)); } public static Vector2 Round(Vector2 value) { return new Vector2(Mathf.Round(value.x), Mathf.Round(value.y)); } public static Vector2 RoundMin(Vector2 value) { return new Vector2(Mathf.Floor(value.x), Mathf.Floor(value.y)); } public static Vector2 RoundMax(Vector2 value) { return new Vector2(Mathf.Ceil(value.x), Mathf.Ceil(value.y)); } public static Vector3 defaultScale { get { return new Vector3(1, 1, 1); } } //Нормали в отрезке public static int GetNormal(float value) { if (value > 0) return 1; if (value < 0) return -1; return 0; } public static int GetNormalPositive(float value) { if (value > 0) return 1; return 0; } public static int GetNormalNegative(float value) { if (value < 0) return -1; return 0; } //Поиск площади фигуры public static class Area { /// <summary> /// Формула Герона /// </summary> public static float TriangleFromPoints(Vector3 a, Vector3 b, Vector3 c) { var longitudes = Longitudes(a, b, c); var perimeter = longitudes.Sum(); var p = perimeter/2f; var sqrS = p*(p - longitudes[0])*(p - longitudes[1])*(p - longitudes[2]); var s = Mathf.Sqrt(sqrS); return s; } /// <summary> /// /// </summary> /// <param name="aSide"></param> /// <param name="bSide"></param> /// <param name="cSide"></param> /// <returns></returns> public static float TriangleFromSides(float aSide, float bSide, float cSide) { var perimeter = aSide + bSide + cSide; var p = perimeter/2f; var sqrS = p*(p - aSide)*(p - bSide)*(p - cSide); var s = Mathf.Sqrt(sqrS); return s; } public static float TriangleFromPoints_Fast(Vector3 a, Vector3 b, Vector3 c) { var dir1 = (a - b); var dir2 = (c - b); return WedgeProduct(dir1, dir2); } /// <summary> /// /// </summary> /// <param name="side">Длина одной из сторон треугольника</param> /// <param name="h">Проведенная к этой длине высота</param> /// <returns></returns> public static float TriangleFromSideAndHeight(float side, float h) { return .5f*side*h; } /// <summary> /// /// </summary> /// <param name="perimeter"></param> /// <param name="r">Радиус вписанной окружности</param> /// <returns></returns> public static float TriangleFromPerimeterAndInradius(float perimeter, float r) { var p = perimeter/2f; return p*r; } /// <summary> /// /// </summary> /// <param name="aSide"></param> /// <param name="bSide"></param> /// <param name="cSide"></param> /// <param name="R">Радиус описанной окружности</param> /// <returns></returns> public static float TriangleFromSidesAndOutradius(float aSide, float bSide, float cSide, float R) { return (aSide*bSide*cSide)/(4*R); } public static float TriangleFromSidesAndAngle(float aSide, float bSide, float angleAB) { return .5f * aSide * bSide * Degrees.Sin(angleAB); } public static float SquareFromSide(float side) { return side*side; } public static float SquareFromDiagonal(float diagonal) { return .5f*(diagonal*diagonal); } public static float RectangleFromSides(float aSide, float bSide) { return aSide*bSide; } public static float RectangleFromPoints(Vector3 a, Vector3 b, Vector3 c) { var aSide = Vector3.Distance(a, b); var bSide = Vector3.Distance(b, c); return RectangleFromSides(aSide, bSide); } /// <summary> /// Формула площади параллелограмма по длине стороны и высоте. /// Площадь параллелограмма равна произведению длины его стороны и длины опущенной на эту сторону высоты. /// </summary> /// <param name="aSide"></param> /// <param name="height"></param> /// <returns></returns> public static float ParallelogramFromSideAndHeight(float aSide, float height) { return aSide*height; } public static float ParallelogramFromPoints(Vector3 a, Vector3 b, Vector3 c) { var aSide = Vector3.Distance(a, b); var bSide = Vector3.Distance(b, c); var dirBA = (a - b); var dirBC = (c - b); var angle = Vector3.Angle(dirBA, dirBC); return ParallelogramFromSidesAndAngle(aSide, bSide, angle); } public static float ParallelogramFromPoints_Fast(Vector3 a, Vector3 b, Vector3 c) { var dir1 = (a - b); var dir2 = (c - b); return WedgeProduct(dir1, dir2); } public static float ParallelogramFromSidesAndAngle(float aSide, float bSide, float angleAB) { return aSide * bSide * Degrees.Sin(angleAB); } public static float ParallelogramFromDiagonalAndAngle(float d1, float d2, float angleD1D2) { return d1 * d2 * Degrees.Sin(angleD1D2); } public static float CircleFromRadius(float radius) { return Mathf.PI*(radius*radius); } public static float CircleFromDiameter(float diameter) { return .25f * Mathf.PI * (diameter * diameter); } public static float Ellipse(float radius1, float radius2) { return Mathf.PI*radius1*radius2; } public static float TrapezeFromSides(float a, float b, float c, float d) { var p = (a + b + c + d)/2f; var n1 = a + b; var n2 = 4 - Mathf.Abs(a - b); var n3 = (p - a)*(p - b)*(p - a - c)*(p - a - d); var s = (n1/n2)*Mathf.Sqrt(n3); return s; } public static float TrapezeFromBasesAndHeight(float aBase, float bBase, float height) { return .5f*(aBase + bBase)*height; } public static float RhombusFromSideAndHeight(float aSide, float height) { return aSide*height; } public static float RhombusFromSideAndAngle(float aSide, float angleInDegrees) { return aSide*aSide*Degrees.Sin(angleInDegrees); } public static float RhombusFromDiagonals(float diagonal1, float diagonal2) { return .5f*diagonal1*diagonal2; } public static float SphereFromRadius(float radius) { return 4*Mathf.PI*(radius*radius); } /// <summary> /// Все точки должны быть в одной плоскости /// </summary> /// <param name="points"></param> /// <returns></returns> public static float PolygonFromPoints_Fast(params Vector2[] points) { if (points.Length < 3) throw new Exception("Need minimum 3 points"); var s = 0f; for (int i = 0; i < points.Length; i++) { var iplus = (i + 1)%points.Length; var a = points[i]; var b = points[iplus]; var c = new Vector2(b.x, 0); var d = new Vector2(a.x, 0); s += TrapezeFromBasesAndHeight(a.y, b.y, c.x - d.x); } return Mathf.Abs(s); } public static float PolygonFromPoints(params Vector2[] points) { if (points.Length < 3) throw new Exception("Need minimum 3 points"); var min = points.Min(v => v.y); var s = 0f; for (int i = 0; i < points.Length; i++) { var iplus = (i + 1) % points.Length; var a = points[i]; var b = points[iplus]; var c = new Vector2(b.x, min); var d = new Vector2(a.x, min); s += TrapezeFromBasesAndHeight(a.y - min, b.y - min, c.x - d.x); } return Mathf.Abs(s); } } public static class Perimeter { public static float Points(params Vector3[] points) { if (points.GetCount() < 3) throw new Exception("points.Length need value >= 3"); return Longitudes(points).Sum(); } public static float Points(params Vector2[] points) { if (points.GetCount() < 3) throw new Exception("points.Length need value >= 3"); return Longitudes(points).Sum(); } public static float CircleFromRadius(float radius) { return 2*Mathf.PI * radius; } public static float CircleFromDiameter(float diameter) { return diameter * Mathf.PI; } public static float Sides(params float[] sides) { if (sides.Length < 3) throw new Exception("sides.Length need value >= 3"); return sides.Sum(); } public static float Rhombus(float aSide) { return 4*aSide; } public static float Square(float aSide) { return 4*aSide; } public static float Rectangle(float aSide, float bSide) { return 2*(aSide+bSide); } public static float Parallelogram(float aSide, float bSide) { return 2*(aSide*bSide); } } public static class Volume { public static float Cube(float aSide) { return aSide*aSide*aSide; } public static float Prism(float sBase, float height) { return sBase*height; } public static float Parallelepiped(Vector3 a, Vector3 b, Vector3 c) { return Mathf.Abs(TripleScalarProduct(a,b,c)); } public static float Parallelepiped(float sBase, float height) { return sBase*height; } public static float Cuboid(float aSide, float bSide, float height) { return aSide*bSide*height; } public static float Pyramid(float sBase, float height) { return (sBase*height)/3f; } public static float RegularTetrahedron(float aSide) { var n1 = aSide*aSide*aSide; var n2 = Mathf.Sqrt(2); return (n1*n2)/12; } public static float CylinderFromRadius(float radius, float height) { return Mathf.PI*(radius*radius)*height; } public static float CylinderFromS(float sBase, float height) { return sBase * height; } public static float ConeFromRadius(float radius, float height) { return CylinderFromRadius(radius, height)/3f; } public static float ConeFromS(float sBase, float height) { return CylinderFromS(sBase, height) / 3f; } public static float Sphere(float radius) { return 4f/3f*Mathf.PI*(radius*radius*radius); } } public static class Degrees { public static float Sin(float value) { return Mathf.Sin(value*Mathf.Deg2Rad); } public static float Cos(float value) { return Mathf.Cos(value * Mathf.Deg2Rad); } public static float Acos(float value) { return Mathf.Acos(value * Mathf.Deg2Rad); } public static float Asin(float value) { return Mathf.Asin(value * Mathf.Deg2Rad); } public static float Atan(float value) { return Mathf.Atan(value * Mathf.Deg2Rad); } public static float Tan(float value) { return Mathf.Tan(value * Mathf.Deg2Rad); } } public static float WedgeProduct(Vector2 lhs, Vector2 rhs) { return lhs.x*rhs.y - lhs.y*rhs.x; } public static float TripleScalarProduct(Vector3 a, Vector3 b, Vector3 c) { return Vector3.Dot(a, Vector3.Cross(b, c)); } public static Vector3 TripleVectorProduct(Vector3 a, Vector3 b, Vector3 c) { return Vector3.Cross(a, Vector3.Cross(b, c)); } public static Vector3 CrossProduct(Vector3 lhs, Vector3 rhs) { return Vector3.Cross(lhs, rhs); } public static float DotProduct(Vector2 lhs, Vector3 rhs) { return lhs.x*rhs.x + lhs.y*rhs.y; } public static class Intersect { //Отрезки 2D public static bool Segments(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2) { var dirA = a2 - a1; var dirB = b2 - b1; var dir1 = b1 - a1; var dirX1 = a2 - b1; var dirX2 = b2 - a1; var f1 = WedgeProduct(dirB, -dir1); var f2 = WedgeProduct(dirB, dirX1); var f3 = WedgeProduct(dirA, dir1); var f4 = WedgeProduct(dirA, dirX2); var result = (f1*f2 < 0) && (f3*f4 < 0); return result; } //Отрезки 3D public static bool Segments(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2, out Vector3 result) { result = new Vector3(); Vector3 intersect; float t; if (Straight(a1, a2, b1, b2, out intersect, out t)) { result = intersect; return t >= 0 && t <= 1; } return false; } public static bool Segments(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2) { Vector3 intersect; float t; if (Straight(a1, a2, b1, b2, out intersect, out t)) return t >= 0 && t <= 1; return false; } public static Vector3? SegmentsNullable(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2) { Vector3 intersect; float t; if (Straight(a1, a2, b1, b2, out intersect, out t)) return t >= 0 && t <= 1 ? (Vector3?) intersect : null; return null; } public static bool Straight(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2) { var a = a2 - a1; var b = b2 - b1; return WedgeProduct(a, b).EqualsApprox(0f); } //Прямая 3D public static Vector3? StraightNullable(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2) { Vector3 intersect; float t; return Straight(a1, a2, b1, b2, out intersect, out t) ? (Vector3?) intersect : null; } public static bool Straight(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2) { Vector3 intersect; float t; return Straight(a1, a2, b1, b2, out intersect, out t); } public static bool Straight(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2, out Vector3 result) { result = new Vector3(); Vector3 intersect; float t; if (Straight(a1, a2, b1, b2, out intersect, out t)) { result = intersect; return true; } return false; } public static bool Straight(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2, out Vector3 result, out float t) { var aDir = (a2 - a1).normalized; var bDir = (b2 - b1).normalized; result = new Vector3(); t = -1; for (int xIndex = 0; xIndex < 3; xIndex++) { var yIndex = (xIndex + 1)%3; var zIndex = (xIndex + 2)%3; var n1 = new Vector2(aDir[xIndex], aDir[yIndex]); var n2 = -new Vector2(bDir[xIndex], bDir[yIndex]); var n3 = new Vector2( b1[xIndex] - a1[xIndex], b1[yIndex] - a1[yIndex] ); var det = n1.x * n2.y - n2.x * n1.y; if (Mathf.Abs(det) >= Mathf.Epsilon) // if det != 0 { var d1 = n3.x * n2.y - n2.x * n3.y; var d2 = n1.x * n3.y - n1.y * n3.x; var t1 = d1 / det; var t2 = d2 / det; var temp1 = a1[zIndex] + aDir[zIndex] * t1; var temp2 = b1[zIndex] + bDir[zIndex] * t2; if (Mathf.Abs(temp1 - temp2) < Mathf.Epsilon) { result = a1 + aDir*t1; t = t1; return true; // if temp1 = temp2 } return false; } } return false; } } public static class Contains { public static bool Rectangle(Rect rect, Vector2 point) { return rect.Contains(point); } public static bool Segment(Vector2 a1, Vector2 a2, Vector2 point) { var v1 = a2 - a1; var v2 = point - a1; return WedgeProduct(v1, v2).EqualsApprox(0f) && Vector2.Dot(a1 - point, a2 - point) <= 0; } public static bool Straight(Vector2 a1, Vector2 a2, Vector2 point) { var v1 = (a2 - a1); var v2 = (point - a1); var diff = WedgeProduct(v1, v2); return diff.EqualsApprox(0f); } public static bool RayFromPoints(Vector2 aStart, Vector2 aFinish, Vector2 point) { return RayFromDir(aStart, (aFinish - aStart).normalized, point); } public static bool RayFromDir(Vector2 origin, Vector2 dir, Vector2 point) { var v1 = dir; var v2 = (point - origin); return WedgeProduct(v1, v2).EqualsApprox(0f) && Vector2.Dot(v1, v2) >= 0; } } /// <summary> /// Указывает, является ли угол выпуклым (по часовой стрелке) /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="c"></param> /// <returns></returns> public static bool IsConvex(Vector2 a, Vector2 b, Vector2 c) { var AB = -(a - b); var BC = (c - b); var diff = WedgeProduct(AB, BC); return diff <= 0; } /// <summary> /// Указывает, является ли угол выпуклым (по часовой стрелке) /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="c"></param> /// <returns></returns> public static bool IsConvex(Vector3 a, Vector3 b, Vector3 c) { var AB = -(a - b); var BC = (c - b); var dAB = Vector3.ProjectOnPlane(AB, new Vector3(0, 0, 1)); var dBC = Vector3.ProjectOnPlane(BC, new Vector3(0, 0, 1)); return IsConvex((Vector2)dAB, Vector2.zero, (Vector2)dBC); } public static Vector3 GetNormal(Vector3 a, Vector3 b, Vector3 c) { return Vector3.Normalize(Vector3.Cross(b - a, c - a)); } /// <summary> /// Возвращает длины между двумя точками. /// Если точек больше двух, то возвращается так же длина между первой и последней точкой. /// Минимум 2 точки. /// </summary> public static float[] Longitudes(params Vector3[] points) { if (points.Length < 2) return new float[0]; var count = points.Length != 2 ? points.Length : (points.Length - 1); var longitudes = new float[count]; for (int i = 0; i < count; i++) { longitudes[i] = Vector3.Distance(points[i], points[(i + 1) % points.Length]); } return longitudes; } /// <summary> /// Возвращает длины между двумя точками. /// Если точек больше двух, то возвращается так же длина между первой и последней точкой. /// Минимум 2 точки. /// </summary> public static float[] Longitudes(params Vector2[] points) { if (points.Length < 2) return new float[0]; var count = points.Length != 2 ? points.Length : (points.Length - 1); var longitudes = new float[count]; for (int i = 0; i < count; i++) { longitudes[i] = Vector2.Distance(points[i], points[(i + 1) % points.Length]); } return longitudes; } }
Много всякой херни, но вкратце по основному:
MinIndex/MaxIndex - возвращают индекс наименьшего/наибольшего элемента в массиве. Могут еще на доп условие проверить
Функции округления в большую/меньшую/ближайшую сторону - это тупо было скопировано из библиотеки Mathf под другими именами. Так лично мне удобней.
Функции округления для векторов - ну а почему бы и нет.
vectorScale - тупо заготовленный единичный вектор для размера. Не нашел нигде.
GetNormal для числа - нормализация числа, тобиш возврат единичного отрезка. Бывает необходимо.
Разные Product'ы для векторов - тут парочка старых добрых скалярного/векторного произведения + косое произведение + тройное векторное + тройное скалярное
В подклассе Area формулы чтобы посчитать площадь фигуры
В подклассе Perimeter формулы чтобы посчитать периметр фигуры
В подклассе Volume формулы чтобы посчитать объем фигуры
В подклассе Intersect формулы чтобы посчитать пересекаются ли фигуры
В подклассе Contains формулы чтобы посчитать содержит ли фигура точку
IsConvex - проверяет выпуклый ли угол по часовой стрелке
Longitudes - возвращает массив длин по указанным точкам
Ну и собственно - к чему я пишу. Я вообще по правде туп по части математики.
Потому хочу тут услышать ваши дополнения и быстрые алгоритмы чтобы что-то посчитать.
Меня очень напрягают функции Longitudes, IsConvex для Vector3, Intersect.Straight для Vector3.
Хотелось бы знать, если у вас есть лучшие решения для этих функций.
И еще в рунете не нашел никакой инфы "для чайников" по части вычислений с тройным скалярным произведением, как я понимаю решения через них самые быстрые. Если есть знатоки - пишите формулы, расскажите хоть-что нибудь об этом.
Смотрите также:
Комментарии
- 1
- 2 (Текущая страница)
Hellfim, разумно
Вот так сделал
private static bool StraightIfNull(Vector3 a, Vector3 b) { a = a.normalized; b = b.normalized; return !(a.Equals(b) || a.Equals(-b)); } public static bool Straight(Vector3 a, Vector3 b) { if (b.x == 0f || b.y == 0f || b.z == 0f) return StraightIfNull(a, b); var c = new Vector3(a.x / b.x, a.y / b.y, a.z / b.z); return !(c.x.EqualsApprox(c.y) && c.y.EqualsApprox(c.z)); } public static bool Straight(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2) { var a = (a2 - a1); var b = (b2 - b1); return Straight(a, b); }
Норм?
Extravert, да норм-то норм.
Просто странно. Если ты занялся оптимизацией, то зачем тогда StraightIfNull?
public static bool Straight(Vector3 a, Vector3 b) { float k1=0, k2=0; if (b.x==0f) { if (a.x!=0) return false; } else k1=a.x/b.x; if (b.y==0f) { if (a.y!=0) return false; } else k2=a.y/b.y; //Если первые 2 коэффицента нулевые, значит вектора заданы строго по оси z, а значит они лежат на одной прямой. if ((k1==0f)&&(k2==0f)) return true; //Если первые 2 коэффицента не равны нулю и не равны между собой, то это разные векторы. if ((k1!=0f)&&(k2!=0f)) { if (!k1.EqualApprox(k2)) return false; } //Если k2=0, то нам это не важно, мы перезапишем в него другое значение всё равно else if (k1==0f) k1=k2; if (b.z==0f) { if (a.z!=0) return false; } else k2=a.z/b.z; //В общем, векторы заданы вдоль одной оси, значит они на одной прямой. if (k2==0f) return true; return k1.EqualsApprox(k2)); }
При таком раскладе можно ещё и на одной переменной сэкономить, но это уже лишнее, имхо.
Вот есть у тебя четыре вектора, задающие две прямые: a, b, r1, r2.
первая прямая: r=r1+a*t
вторая прямая: r=r2+b*t
t - параметр, в случае прямой пробегает всю числовую ось, в случае отрезка - отрезок.
Тогда условие пересечения этих двух прямых: (a,b)!=0 and ( r1-r2, a, b )==0 (с какой-то точностью epsilon).
Где ( a, b, c ) - смешанное произведение, (a,b) - скалярное.
Будет сделано 9 умножений, 3 вычитания и 3 сложения для смешанного произведения; и ещё 3 сложения и 3 умножения для скалярного. Если вычисления ленивые(хз как C#), то именно в таком порядке проверки.
Но давайте поглядим на это с другой стороны.
Пусть у нас есть две прямые, лежащие "почти" в одной плоскости с точностью epsilon, и для них выполнено, что (a,b)!=0, т.е. они не параллельные. Тогда они пересекаются.
А вот и не факт. Если они лежат в двух плоскостях, лишь чуть-чуть наклонённых друг относительно друга, и ещё они разнесены( |r1-r2| велико ), то они на дальних расстояниях вообще будут далеко одна от другой. А мы их считаем пересекшимися, потому что наш метод работает только с какой-то точностью epsilon и может не почувствовать маленький угол, дающий большое расхождение на дальних дистанциях.
Что делать? А хз, мб и проверять в цикле. Или хитрить и пилить иной метод.
Так, стоп, нужно только в 2D? Тогда зря я это написал, тогда это в гугл...Ну тогда можно попробовать мой алгоритм, точность выбрать что-то типа 1e-6
Только там ещё если r1-r2 ~ 0, то точка пересечения r=r1=r2
Ещё можно взять отсюда: http://mathportal.net/index.php/analiticheskaya-geometriya/ra...
Т.е. через расстояние между скрещивающимися прямыми, только там надо их проверить на параллельность сначала, а то на ноль поделить можно.
- 1
- 2 (Текущая страница)
CollectableItemData.cs
[CreateMenuItem(fileName = "newItem", menuName = "Data/Items/Collectable", order = 51]