• Vector3 三维向量
    • Vector3.ClampMagnitude
    • Vector3.Dot 点乘
    • Vector3.Cross 叉乘
    • Vector3.Lerp 线性差值
    • Vector3.Slerp 球形差值
    • Vector3.MoveTowards
    • Vector3.SmoothDamp
    • Vector3.RotateTowards 转向
    • 源码

    Vector3 三维向量

    结构体类型

    表示 3D的 向量和点。

    这个结构用于在 Unity 传递 3D 位置和方向。它包含向量运算的函数。

    静态变量
    up(0, 1, 0)
    down(0, -1, 0)
    forward(0, 0, 1)
    back(0, 0, -1)
    left(-1, 0, 0)
    right(1, 0, 0)
    zero(0, 0, 0)
    one(1, 1, 1)

    变量说明
    magnitude返回向量的长度(只读)。
    normalized返回该向量方向上的长度为1的向量(只读)。
    sqrMagnitude返回这个向量的长度的平方(只读)。
    this[int]使用[0], [1], [2]分别访问该向量的 x, y, z 元素。
    x向量的 x 元素
    y向量的 y 元素
    z向量的 z 元素

    静态方法说明
    Angle返回两个向量之间的夹角。
    ClampMagnitude返回的向量的长度,最大不超过 maxLength 所指示的长度。
    Distance返回两个点之间的距离。
    Cross计算两个向量的叉乘。
    Dot计算两个向量的点乘。
    MoveTowards当前的地点移向目标。
    Max返回一个由两个向量的最大元素组成的向量。
    Min返回一个由两个向量的最小元素组成的向量。
    Lerp两个点之间的线性插值。
    LerpUnclamped两个向量之间的线性插值。该插值t在小于0或大于1时的返回值不会被限制。
    Normalize使向量的长度为1。
    Project投影一个向量到另一个向量。
    ProjectOnPlane投影向量到一个平面上(由垂直到该平面的法线定义)。
    Reflect沿着法线反射向量。
    SmoothDamp随着时间的推移,逐渐改变一个向量朝向预期的目标。
    RotateTowards当前的向量转向目标。
    OrthoNormalize使向量规范化并且彼此相互垂直。
    Scale两个矢量组件对应相乘。
    Slerp在两个向量之间球形插值。
    SlerpUnclamped在两个向量之间球形插值。该插值t在小于0或大于1时的返回值不会被限制。

    Vector3.ClampMagnitude

    返回原向量的拷贝,并且它的模最大不超过maxLength所指示的长度。也就是说,限制向量长度到一个特定的长度。

    示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. public Vector3 centerPt;
    5. public float radius;
    6. void Update() {
    7. Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    8. Vector3 newPos = transform.position + movement;
    9. Vector3 offset = newPos - centerPt;
    10. transform.position = centerPt + Vector3.ClampMagnitude(offset, radius);
    11. }
    12. }

    Vector3.Dot 点乘

    两个向量的点乘积。点积是一个浮点型的值,两个向量的长度相乘,然后乘以它们之间夹角的余弦值。对于normalized向量,如果他们指向在完全相同的方向,Dot返回1。如果他们指向完全相反的方向,返回-1。对于其他的情况返回一个数(例如:如果是垂直的Dot返回0)。

    示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. public Transform other;
    5. void Update() {
    6. if (other) {
    7. Vector3 forward = transform.TransformDirection(Vector3.forward);
    8. Vector3 toOther = other.position - transform.position;
    9. if (Vector3.Dot(forward, toOther) < 0)
    10. print("The other transform is behind me!");
    11. }
    12. }
    13. }

    Vector3.Cross 叉乘

    两个向量的交叉乘积。两个向量的叉积乘积结果在垂直于两个输入向量的三分之一个向量。结果的大小等于两个输入相乘,然后乘以输入向量之间的角度的正弦值。你可以确定的方向的结果向量使用“左手法则”。

     2.4 Vector3  - 图1示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. Vector3 GetNormal(Vector3 a, Vector3 b, Vector3 c) {
    5. Vector3 side1 = b - a;
    6. Vector3 side2 = c - a;
    7. return Vector3.Cross(side1, side2).normalized;
    8. }
    9. }

    Vector3.Lerp 线性差值

    两个向量之间的线性插值。按照分数t在from到to之间插值。这是最常用的寻找一点沿一条线的两个端点之间一些分数的方式(例如,在那些点之间逐渐移动一个对象)。这分数是在范围[ 0…1]。t是夹在 [0…1]之间,当t = 0时,返回from,当t = 1时,返回to。当t = 0.5 返回from和to的中间点。

    示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. public Transform startMarker;
    5. public Transform endMarker;
    6. public float speed = 1.0F;
    7. private float startTime;
    8. private float journeyLength;
    9. void Start() {
    10. startTime = Time.time;
    11. journeyLength = Vector3.Distance(startMarker.position, endMarker.position);
    12. }
    13. void Update() {
    14. float distCovered = (Time.time - startTime) * speed;
    15. float fracJourney = distCovered / journeyLength;
    16. transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);
    17. }
    18. }

    Vector3.Slerp 球形差值

    两个向量之间的弧形插值通过t数值在from和to之间插值。这与线性内插之间的不同(即, “线性插值” )是该向量被视为方向而不是空间中的点。返回的向量的方向是由角内插,其大小是from和to的幅度之间进行内插。

    示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. public Transform sunrise;
    5. public Transform sunset;
    6. public float journeyTime = 1.0F;
    7. private float startTime;
    8. void Start() {
    9. startTime = Time.time;
    10. }
    11. void Update() {
    12. Vector3 center = (sunrise.position + sunset.position) * 0.5F;
    13. center -= new Vector3(0, 1, 0);
    14. Vector3 riseRelCenter = sunrise.position - center;
    15. Vector3 setRelCenter = sunset.position - center;
    16. float fracComplete = (Time.time - startTime) / journeyTime;
    17. transform.position = Vector3.Slerp(riseRelCenter, setRelCenter, fracComplete);
    18. transform.position += center;
    19. }
    20. }

    Vector3.MoveTowards

    当前的地点移向目标。这个函数的返回值是一个点maxdistancedelta单位更接近于目标/点沿着当前的和目标之间的线。如果目标是比maxdistancedelta /然后返回值将等于目标接近(即移动不会超过目标)。maxdistancedelta负值可以用来从目标推开该向量。

    示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. public Transform target;
    5. public float speed;
    6. void Update() {
    7. float step = speed * Time.deltaTime;
    8. transform.position = Vector3.MoveTowards(transform.position, target.position, step);
    9. }
    10. }

    Vector3.SmoothDamp

    随着时间的推移,逐渐改变一个向量朝向预期的目标。向量由一些像弹簧阻尼器函数平滑,这将永远不会超过。最常见的用途是相机平滑跟随。

    示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. public Transform target;
    5. public float smoothTime = 0.3F;
    6. private Vector3 velocity = Vector3.zero;
    7. void Update() {
    8. Vector3 targetPosition = target.TransformPoint(new Vector3(0, 5, -10));
    9. transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
    10. }
    11. }

    Vector3.RotateTowards 转向

    当前的向量转向目标。

    这个函数类似于MoveTowards除了将向量被视为一个方向,而不是一个位置上。当前向量将被旋转朝向目标方向由maxRadiansDelta的角度,虽然会恰好落在目标,而不是超过。如果当前的大小和目标的是不同的,那么结果的幅度将被线性地旋转过程中进行插值。如果一个负值用于maxRadiansDelta ,向量会转离目标/直到它指向完全相反的方向,然后停止。

    示例:

    1. using UnityEngine;
    2. using System.Collections;
    3. public class ExampleClass : MonoBehaviour {
    4. public Transform target;
    5. public float speed;
    6. void Update() {
    7. Vector3 targetDir = target.position - transform.position;
    8. float step = speed * Time.deltaTime;
    9. Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0F);
    10. Debug.DrawRay(transform.position, newDir, Color.red);
    11. transform.rotation = Quaternion.LookRotation(newDir);
    12. }
    13. }

    源码

    1. using System;
    2. using System.Runtime.InteropServices;
    3. using scm = System.ComponentModel;
    4. using uei = UnityEngine.Internal;
    5. namespace UnityEngine
    6. {
    7. // Representation of 3D vectors and points.
    8. [StructLayout(LayoutKind.Sequential)]
    9. public partial struct Vector3
    10. {
    11. public const float kEpsilon = 0.00001F;
    12. // X component of the vector.
    13. public float x;
    14. // Y component of the vector.
    15. public float y;
    16. // Z component of the vector.
    17. public float z;
    18. // Linearly interpolates between two vectors.
    19. public static Vector3 Lerp(Vector3 a, Vector3 b, float t)
    20. {
    21. t = Mathf.Clamp01(t);
    22. return new Vector3(
    23. a.x + (b.x - a.x) * t,
    24. a.y + (b.y - a.y) * t,
    25. a.z + (b.z - a.z) * t
    26. );
    27. }
    28. // Linearly interpolates between two vectors without clamping the interpolant
    29. public static Vector3 LerpUnclamped(Vector3 a, Vector3 b, float t)
    30. {
    31. return new Vector3(
    32. a.x + (b.x - a.x) * t,
    33. a.y + (b.y - a.y) * t,
    34. a.z + (b.z - a.z) * t
    35. );
    36. }
    37. // Moves a point /current/ in a straight line towards a /target/ point.
    38. public static Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta)
    39. {
    40. Vector3 toVector = target - current;
    41. float dist = toVector.magnitude;
    42. if (dist <= maxDistanceDelta || dist < float.Epsilon)
    43. return target;
    44. return current + toVector / dist * maxDistanceDelta;
    45. }
    46. [uei.ExcludeFromDocs]
    47. public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime, float maxSpeed)
    48. {
    49. float deltaTime = Time.deltaTime;
    50. return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime);
    51. }
    52. [uei.ExcludeFromDocs]
    53. public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime)
    54. {
    55. float deltaTime = Time.deltaTime;
    56. float maxSpeed = Mathf.Infinity;
    57. return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime);
    58. }
    59. // Gradually changes a vector towards a desired goal over time.
    60. public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime, [uei.DefaultValue("Mathf.Infinity")] float maxSpeed, [uei.DefaultValue("Time.deltaTime")] float deltaTime)
    61. {
    62. smoothTime = Mathf.Max(0.0001F, smoothTime);
    63. float omega = 2F / smoothTime;
    64. float x = omega * deltaTime;
    65. float exp = 1F / (1F + x + 0.48F * x * x + 0.235F * x * x * x);
    66. Vector3 change = current - target;
    67. Vector3 originalTo = target;
    68. float maxChange = maxSpeed * smoothTime;
    69. change = Vector3.ClampMagnitude(change, maxChange);
    70. target = current - change;
    71. Vector3 temp = (currentVelocity + omega * change) * deltaTime;
    72. currentVelocity = (currentVelocity - omega * temp) * exp;
    73. Vector3 output = target + (change + temp) * exp;
    74. if (Vector3.Dot(originalTo - current, output - originalTo) > 0)
    75. {
    76. output = originalTo;
    77. currentVelocity = (output - originalTo) / deltaTime;
    78. }
    79. return output;
    80. }
    81. // Access the x, y, z components using [0], [1], [2] respectively.
    82. public float this[int index]
    83. {
    84. get
    85. {
    86. switch (index)
    87. {
    88. case 0: return x;
    89. case 1: return y;
    90. case 2: return z;
    91. default:
    92. throw new IndexOutOfRangeException("Invalid Vector3 index!");
    93. }
    94. }
    95. set
    96. {
    97. switch (index)
    98. {
    99. case 0: x = value; break;
    100. case 1: y = value; break;
    101. case 2: z = value; break;
    102. default:
    103. throw new IndexOutOfRangeException("Invalid Vector3 index!");
    104. }
    105. }
    106. }
    107. // Creates a new vector with given x, y, z components.
    108. public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
    109. // Creates a new vector with given x, y components and sets /z/ to zero.
    110. public Vector3(float x, float y) { this.x = x; this.y = y; z = 0F; }
    111. // Set x, y and z components of an existing Vector3.
    112. public void Set(float newX, float newY, float newZ) { x = newX; y = newY; z = newZ; }
    113. // Multiplies two vectors component-wise.
    114. public static Vector3 Scale(Vector3 a, Vector3 b) { return new Vector3(a.x * b.x, a.y * b.y, a.z * b.z); }
    115. // Multiplies every component of this vector by the same component of /scale/.
    116. public void Scale(Vector3 scale) { x *= scale.x; y *= scale.y; z *= scale.z; }
    117. // Cross Product of two vectors.
    118. public static Vector3 Cross(Vector3 lhs, Vector3 rhs)
    119. {
    120. return new Vector3(
    121. lhs.y * rhs.z - lhs.z * rhs.y,
    122. lhs.z * rhs.x - lhs.x * rhs.z,
    123. lhs.x * rhs.y - lhs.y * rhs.x);
    124. }
    125. // used to allow Vector3s to be used as keys in hash tables
    126. public override int GetHashCode()
    127. {
    128. return x.GetHashCode() ^ (y.GetHashCode() << 2) ^ (z.GetHashCode() >> 2);
    129. }
    130. // also required for being able to use Vector3s as keys in hash tables
    131. public override bool Equals(object other)
    132. {
    133. if (!(other is Vector3)) return false;
    134. Vector3 rhs = (Vector3)other;
    135. return x.Equals(rhs.x) && y.Equals(rhs.y) && z.Equals(rhs.z);
    136. }
    137. // Reflects a vector off the plane defined by a normal.
    138. public static Vector3 Reflect(Vector3 inDirection, Vector3 inNormal)
    139. {
    140. return -2F * Dot(inNormal, inDirection) * inNormal + inDirection;
    141. }
    142. // *undoc* --- we have normalized property now
    143. public static Vector3 Normalize(Vector3 value)
    144. {
    145. float mag = Magnitude(value);
    146. if (mag > kEpsilon)
    147. return value / mag;
    148. else
    149. return zero;
    150. }
    151. // Makes this vector have a ::ref::magnitude of 1.
    152. public void Normalize()
    153. {
    154. float mag = Magnitude(this);
    155. if (mag > kEpsilon)
    156. this = this / mag;
    157. else
    158. this = zero;
    159. }
    160. // Returns this vector with a ::ref::magnitude of 1 (RO).
    161. public Vector3 normalized { get { return Vector3.Normalize(this); } }
    162. // Dot Product of two vectors.
    163. public static float Dot(Vector3 lhs, Vector3 rhs) { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; }
    164. // Projects a vector onto another vector.
    165. public static Vector3 Project(Vector3 vector, Vector3 onNormal)
    166. {
    167. float sqrMag = Dot(onNormal, onNormal);
    168. if (sqrMag < Mathf.Epsilon)
    169. return zero;
    170. else
    171. return onNormal * Dot(vector, onNormal) / sqrMag;
    172. }
    173. // Projects a vector onto a plane defined by a normal orthogonal to the plane.
    174. public static Vector3 ProjectOnPlane(Vector3 vector, Vector3 planeNormal)
    175. {
    176. return vector - Project(vector, planeNormal);
    177. }
    178. // Returns the angle in degrees between /from/ and /to/. This is always the smallest
    179. public static float Angle(Vector3 from, Vector3 to)
    180. {
    181. return Mathf.Acos(Mathf.Clamp(Vector3.Dot(from.normalized, to.normalized), -1F, 1F)) * Mathf.Rad2Deg;
    182. }
    183. // The smaller of the two possible angles between the two vectors is returned, therefore the result will never be greater than 180 degrees or smaller than -180 degrees.
    184. // If you imagine the from and to vectors as lines on a piece of paper, both originating from the same point, then the /axis/ vector would point up out of the paper.
    185. // The measured angle between the two vectors would be positive in a clockwise direction and negative in an anti-clockwise direction.
    186. public static float SignedAngle(Vector3 from, Vector3 to, Vector3 axis)
    187. {
    188. Vector3 fromNorm = from.normalized, toNorm = to.normalized;
    189. float unsignedAngle = Mathf.Acos(Mathf.Clamp(Vector3.Dot(fromNorm, toNorm), -1F, 1F)) * Mathf.Rad2Deg;
    190. float sign = Mathf.Sign(Vector3.Dot(axis, Vector3.Cross(fromNorm, toNorm)));
    191. return unsignedAngle * sign;
    192. }
    193. // Returns the distance between /a/ and /b/.
    194. public static float Distance(Vector3 a, Vector3 b)
    195. {
    196. Vector3 vec = new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
    197. return Mathf.Sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
    198. }
    199. // Returns a copy of /vector/ with its magnitude clamped to /maxLength/.
    200. public static Vector3 ClampMagnitude(Vector3 vector, float maxLength)
    201. {
    202. if (vector.sqrMagnitude > maxLength * maxLength)
    203. return vector.normalized * maxLength;
    204. return vector;
    205. }
    206. // *undoc* --- there's a property now
    207. public static float Magnitude(Vector3 vector) { return Mathf.Sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z); }
    208. // Returns the length of this vector (RO).
    209. public float magnitude { get { return Mathf.Sqrt(x * x + y * y + z * z); } }
    210. // *undoc* --- there's a property now
    211. public static float SqrMagnitude(Vector3 vector) { return vector.x * vector.x + vector.y * vector.y + vector.z * vector.z; }
    212. // Returns the squared length of this vector (RO).
    213. public float sqrMagnitude { get { return x * x + y * y + z * z; } }
    214. // Returns a vector that is made from the smallest components of two vectors.
    215. public static Vector3 Min(Vector3 lhs, Vector3 rhs)
    216. {
    217. return new Vector3(Mathf.Min(lhs.x, rhs.x), Mathf.Min(lhs.y, rhs.y), Mathf.Min(lhs.z, rhs.z));
    218. }
    219. // Returns a vector that is made from the largest components of two vectors.
    220. public static Vector3 Max(Vector3 lhs, Vector3 rhs)
    221. {
    222. return new Vector3(Mathf.Max(lhs.x, rhs.x), Mathf.Max(lhs.y, rhs.y), Mathf.Max(lhs.z, rhs.z));
    223. }
    224. static readonly Vector3 zeroVector = new Vector3(0F, 0F, 0F);
    225. static readonly Vector3 oneVector = new Vector3(1F, 1F, 1F);
    226. static readonly Vector3 upVector = new Vector3(0F, 1F, 0F);
    227. static readonly Vector3 downVector = new Vector3(0F, -1F, 0F);
    228. static readonly Vector3 leftVector = new Vector3(-1F, 0F, 0F);
    229. static readonly Vector3 rightVector = new Vector3(1F, 0F, 0F);
    230. static readonly Vector3 forwardVector = new Vector3(0F, 0F, 1F);
    231. static readonly Vector3 backVector = new Vector3(0F, 0F, -1F);
    232. static readonly Vector3 positiveInfinityVector = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
    233. static readonly Vector3 negativeInfinityVector = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
    234. // Shorthand for writing @@Vector3(0, 0, 0)@@
    235. public static Vector3 zero { get { return zeroVector; } }
    236. // Shorthand for writing @@Vector3(1, 1, 1)@@
    237. public static Vector3 one { get { return oneVector; } }
    238. // Shorthand for writing @@Vector3(0, 0, 1)@@
    239. public static Vector3 forward { get { return forwardVector; } }
    240. public static Vector3 back { get { return backVector; } }
    241. // Shorthand for writing @@Vector3(0, 1, 0)@@
    242. public static Vector3 up { get { return upVector; } }
    243. public static Vector3 down { get { return downVector; } }
    244. public static Vector3 left { get { return leftVector; } }
    245. // Shorthand for writing @@Vector3(1, 0, 0)@@
    246. public static Vector3 right { get { return rightVector; } }
    247. // Shorthand for writing @@Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity)@@
    248. public static Vector3 positiveInfinity { get { return positiveInfinityVector; } }
    249. // Shorthand for writing @@Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity)@@
    250. public static Vector3 negativeInfinity { get { return negativeInfinityVector; } }
    251. // Adds two vectors.
    252. public static Vector3 operator+(Vector3 a, Vector3 b) { return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z); }
    253. // Subtracts one vector from another.
    254. public static Vector3 operator-(Vector3 a, Vector3 b) { return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z); }
    255. // Negates a vector.
    256. public static Vector3 operator-(Vector3 a) { return new Vector3(-a.x, -a.y, -a.z); }
    257. // Multiplies a vector by a number.
    258. public static Vector3 operator*(Vector3 a, float d) { return new Vector3(a.x * d, a.y * d, a.z * d); }
    259. // Multiplies a vector by a number.
    260. public static Vector3 operator*(float d, Vector3 a) { return new Vector3(a.x * d, a.y * d, a.z * d); }
    261. // Divides a vector by a number.
    262. public static Vector3 operator/(Vector3 a, float d) { return new Vector3(a.x / d, a.y / d, a.z / d); }
    263. // Returns true if the vectors are equal.
    264. public static bool operator==(Vector3 lhs, Vector3 rhs)
    265. {
    266. // Returns false in the presence of NaN values.
    267. return SqrMagnitude(lhs - rhs) < kEpsilon * kEpsilon;
    268. }
    269. // Returns true if vectors are different.
    270. public static bool operator!=(Vector3 lhs, Vector3 rhs)
    271. {
    272. // Returns true in the presence of NaN values.
    273. return !(lhs == rhs);
    274. }
    275. public override string ToString()
    276. {
    277. return UnityString.Format("({0:F1}, {1:F1}, {2:F1})", x, y, z);
    278. }
    279. public string ToString(string format)
    280. {
    281. return UnityString.Format("({0}, {1}, {2})", x.ToString(format), y.ToString(format), z.ToString(format));
    282. }
    283. [System.Obsolete("Use Vector3.forward instead.")]
    284. public static Vector3 fwd { get { return new Vector3(0F, 0F, 1F); } }
    285. [System.Obsolete("Use Vector3.Angle instead. AngleBetween uses radians instead of degrees and was deprecated for this reason")]
    286. public static float AngleBetween(Vector3 from, Vector3 to) { return Mathf.Acos(Mathf.Clamp(Vector3.Dot(from.normalized, to.normalized), -1F, 1F)); }
    287. [System.Obsolete("Use Vector3.ProjectOnPlane instead.")]
    288. public static Vector3 Exclude(Vector3 excludeThis, Vector3 fromThat) { return ProjectOnPlane(fromThat, excludeThis); }
    289. }
    290. }

    ?