1 #region Copyright & License Information
2 /*
3  * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4  * This file is part of OpenRA, which is free software. It is made
5  * available to you under the terms of the GNU General Public License
6  * as published by the Free Software Foundation, either version 3 of
7  * the License, or (at your option) any later version. For more
8  * information, see COPYING.
9  */
10 #endregion
11 
12 using System;
13 using Eluant;
14 using Eluant.ObjectBinding;
15 using OpenRA.Scripting;
16 using OpenRA.Support;
17 
18 namespace OpenRA
19 {
20 	public struct WVec : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaUnaryMinusBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<WVec>
21 	{
22 		public readonly int X, Y, Z;
23 
WVecOpenRA.WVec24 		public WVec(int x, int y, int z) { X = x; Y = y; Z = z; }
WVecOpenRA.WVec25 		public WVec(WDist x, WDist y, WDist z) { X = x.Length; Y = y.Length; Z = z.Length; }
26 
27 		public static readonly WVec Zero = new WVec(0, 0, 0);
28 
operator +OpenRA.WVec29 		public static WVec operator +(WVec a, WVec b) { return new WVec(a.X + b.X, a.Y + b.Y, a.Z + b.Z); }
operator -OpenRA.WVec30 		public static WVec operator -(WVec a, WVec b) { return new WVec(a.X - b.X, a.Y - b.Y, a.Z - b.Z); }
operator -OpenRA.WVec31 		public static WVec operator -(WVec a) { return new WVec(-a.X, -a.Y, -a.Z); }
operator /OpenRA.WVec32 		public static WVec operator /(WVec a, int b) { return new WVec(a.X / b, a.Y / b, a.Z / b); }
operator *OpenRA.WVec33 		public static WVec operator *(int a, WVec b) { return new WVec(a * b.X, a * b.Y, a * b.Z); }
operator *OpenRA.WVec34 		public static WVec operator *(WVec a, int b) { return b * a; }
35 
operator ==OpenRA.WVec36 		public static bool operator ==(WVec me, WVec other) { return me.X == other.X && me.Y == other.Y && me.Z == other.Z; }
operator !=OpenRA.WVec37 		public static bool operator !=(WVec me, WVec other) { return !(me == other); }
38 
DotOpenRA.WVec39 		public static int Dot(WVec a, WVec b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; }
40 		public long LengthSquared { get { return (long)X * X + (long)Y * Y + (long)Z * Z; } }
41 		public int Length { get { return (int)Exts.ISqrt(LengthSquared); } }
42 		public long HorizontalLengthSquared { get { return (long)X * X + (long)Y * Y; } }
43 		public int HorizontalLength { get { return (int)Exts.ISqrt(HorizontalLengthSquared); } }
44 		public long VerticalLengthSquared { get { return (long)Z * Z; } }
45 		public int VerticalLength { get { return (int)Exts.ISqrt(VerticalLengthSquared); } }
46 
RotateOpenRA.WVec47 		public WVec Rotate(WRot rot)
48 		{
49 			Int32Matrix4x4 mtx;
50 			rot.AsMatrix(out mtx);
51 			return Rotate(ref mtx);
52 		}
53 
RotateOpenRA.WVec54 		public WVec Rotate(ref Int32Matrix4x4 mtx)
55 		{
56 			var lx = (long)X;
57 			var ly = (long)Y;
58 			var lz = (long)Z;
59 			return new WVec(
60 				(int)((lx * mtx.M11 + ly * mtx.M21 + lz * mtx.M31) / mtx.M44),
61 				(int)((lx * mtx.M12 + ly * mtx.M22 + lz * mtx.M32) / mtx.M44),
62 				(int)((lx * mtx.M13 + ly * mtx.M23 + lz * mtx.M33) / mtx.M44));
63 		}
64 
65 		public WAngle Yaw
66 		{
67 			get
68 			{
69 				if (LengthSquared == 0)
70 					return WAngle.Zero;
71 
72 				// OpenRA defines north as -y
73 				return WAngle.ArcTan(-Y, X) - new WAngle(256);
74 			}
75 		}
76 
LerpOpenRA.WVec77 		public static WVec Lerp(WVec a, WVec b, int mul, int div) { return a + (b - a) * mul / div; }
78 
LerpQuadraticOpenRA.WVec79 		public static WVec LerpQuadratic(WVec a, WVec b, WAngle pitch, int mul, int div)
80 		{
81 			// Start with a linear lerp between the points
82 			var ret = Lerp(a, b, mul, div);
83 
84 			if (pitch.Angle == 0)
85 				return ret;
86 
87 			// Add an additional quadratic variation to height
88 			// Uses decimal to avoid integer overflow
89 			var offset = (int)((decimal)(b - a).Length * pitch.Tan() * mul * (div - mul) / (1024 * div * div));
90 			return new WVec(ret.X, ret.Y, ret.Z + offset);
91 		}
92 
93 		// Sampled a N-sample probability density function in the range [-1024..1024, -1024..1024]
94 		// 1 sample produces a rectangular probability
95 		// 2 samples produces a triangular probability
96 		// ...
97 		// N samples approximates a true Gaussian
FromPDFOpenRA.WVec98 		public static WVec FromPDF(MersenneTwister r, int samples)
99 		{
100 			return new WVec(WDist.FromPDF(r, samples), WDist.FromPDF(r, samples), WDist.Zero);
101 		}
102 
GetHashCodeOpenRA.WVec103 		public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); }
104 
EqualsOpenRA.WVec105 		public bool Equals(WVec other) { return other == this; }
EqualsOpenRA.WVec106 		public override bool Equals(object obj) { return obj is WVec && Equals((WVec)obj); }
107 
ToStringOpenRA.WVec108 		public override string ToString() { return X + "," + Y + "," + Z; }
109 
110 		#region Scripting interface
111 
AddOpenRA.WVec112 		public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
113 		{
114 			WVec a, b;
115 			if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
116 				throw new LuaException("Attempted to call WVec.Add(WVec, WVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
117 
118 			return new LuaCustomClrObject(a + b);
119 		}
120 
SubtractOpenRA.WVec121 		public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
122 		{
123 			WVec a, b;
124 			if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
125 				throw new LuaException("Attempted to call WVec.Subtract(WVec, WVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
126 
127 			return new LuaCustomClrObject(a - b);
128 		}
129 
MinusOpenRA.WVec130 		public LuaValue Minus(LuaRuntime runtime)
131 		{
132 			return new LuaCustomClrObject(-this);
133 		}
134 
EqualsOpenRA.WVec135 		public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
136 		{
137 			WVec a, b;
138 			if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
139 				return false;
140 
141 			return a == b;
142 		}
143 
144 		public LuaValue this[LuaRuntime runtime, LuaValue key]
145 		{
146 			get
147 			{
148 				switch (key.ToString())
149 				{
150 					case "X": return X;
151 					case "Y": return Y;
152 					case "Z": return Z;
153 					case "Facing": return Yaw.Facing;
154 					default: throw new LuaException("WVec does not define a member '{0}'".F(key));
155 				}
156 			}
157 
158 			set
159 			{
160 				throw new LuaException("WVec is read-only. Use WVec.New to create a new value");
161 			}
162 		}
163 
164 		#endregion
165 	}
166 }
167