ZeroVR/ZeroPacientVR/Assets/Octave3D World Builder/Scripts/Math/XZOrientedCircle3D.cs

190 lines
7.3 KiB
C#

#if UNITY_EDITOR
using UnityEngine;
using System.Collections.Generic;
namespace O3DWB
{
public class XZOrientedCircle3D
{
#region Private Variables
private float _radius = 1.0f;
private CoordinateSystem _coordinateSystem = new CoordinateSystem();
#endregion
#region Public Static Properties
public static Vector3 ModelSpaceRightAxis { get { return Vector3.right; } }
public static Vector3 ModelSpacePlaneNormal { get { return Vector3.up; } }
public static Vector3 ModelSpaceLookAxis { get { return Vector3.forward; } }
#endregion
#region Public Properties
public float ModelSpaceRadius { get { return _radius; } set { _radius = Mathf.Abs(value); } }
public float ScaledRadius { get { return _radius * _coordinateSystem.TransformMatrix.XScale; } }
public Vector3 Center
{
get { return _coordinateSystem.GetOriginPosition(); }
set { _coordinateSystem.SetOriginPosition(value); }
}
public Plane Plane { get { return new Plane(_coordinateSystem.GetAxisVector(CoordinateSystemAxis.PositiveUp), Center); } }
public Vector3 Normal { get { return Plane.normal; } }
public TransformMatrix TransformMatrix { get { return _coordinateSystem.TransformMatrix; } }
public Quaternion Rotation { get { return _coordinateSystem.GetRotation(); } set { _coordinateSystem.SetRotation(value); } }
#endregion
#region Constructors
public XZOrientedCircle3D()
{
}
public XZOrientedCircle3D(Vector3 center)
{
Center = center;
}
public XZOrientedCircle3D(Vector3 center, float radius)
{
Center = center;
ModelSpaceRadius = radius;
}
public XZOrientedCircle3D(Vector3 center, float radius, Quaternion rotation)
{
Center = center;
ModelSpaceRadius = radius;
Rotation = rotation;
}
public XZOrientedCircle3D(XZOrientedCircle3D source)
{
Center = source.Center;
ModelSpaceRadius = source.ModelSpaceRadius;
Rotation = source.Rotation;
}
#endregion
#region Public Methods
public void SetScale(float scale)
{
_coordinateSystem.SetScaleOnAllAxes(scale);
}
public bool ContainsAnyPoint(List<Vector3> points)
{
foreach (Vector2 point in points)
{
if (ContainsPoint(point)) return true;
}
return false;
}
public bool ContainsPoint(Vector3 point)
{
if (!Plane.IsPointOnPlane(point)) return false;
return (point - Center).magnitude <= _radius;
}
public List<Vector3> GetExtentsPoints()
{
Vector3 center = Center;
Vector3 localRight = GetLocalAxis(CoordinateSystemAxis.PositiveRight);
Vector3 localLook = GetLocalAxis(CoordinateSystemAxis.PositiveLook);
float scaledRadius = ScaledRadius;
// Return the extent points in the following format: top, right, bottom, left.
return new List<Vector3>
{
center + localLook * scaledRadius,
center + localRight * scaledRadius,
center - localLook * scaledRadius,
center - localRight * scaledRadius,
};
}
public bool IntersectsRay(Ray3D ray, out float t)
{
Plane circlePlane = Plane;
Vector3 circleCenter = Center;
float scaledRadius = ScaledRadius;
// Note: 'IntersectsPlane' not enough. Also check for containment????
if (ray.IntersectsPlane(circlePlane, out t)) return true;
else
if (ray.Direction.IsPerpendicularTo(circlePlane.normal))
{
// Project the circle center onto the ray direction segment. If the distance between the circle center
// and the projected center is <= the circle's radius, the ray might intersect the circle. Otherwise,
// the ray can not possibly intersect the circle.
Segment3D segment = new Segment3D(ray);
Vector3 centerProjectedOnRayDir = circleCenter.CalculateProjectionPointOnSegment(segment.StartPoint, segment.EndPoint);
Vector3 fromCenterToProjectedCenter = centerProjectedOnRayDir - circleCenter;
float triAdjSideLength1 = fromCenterToProjectedCenter.magnitude;
if (triAdjSideLength1 > scaledRadius) return false;
// At this point it is possible that the ray might intersect the circle. Calcluate how much
// we have to move from the center projection to a point on the circle along the reverse of
// the ray direction vector. We will store this amount in 'triAdjSideLength2'.
float triAdjSideLength2 = Mathf.Sqrt(scaledRadius * scaledRadius - triAdjSideLength1 * triAdjSideLength1);
// Now check if moving from the projected center along the reverse ray direction by an amount equal to
// 'triAdjSideLength2', we end up on a point which resides on the ray direction segment.
Vector3 normalizedRayDirection = ray.Direction;
normalizedRayDirection.Normalize();
Vector3 targetPoint = centerProjectedOnRayDir - triAdjSideLength2 * normalizedRayDirection;
if (targetPoint.IsOnSegment(segment.StartPoint, segment.EndPoint))
{
// The point sits on the segment, which means that the ray intersects the circle.
// Now we need to calculate the intersection offset.
t = (targetPoint - ray.Origin).magnitude / ray.Direction.magnitude;
return true;
}
}
return false;
}
public bool IntersectsAnyRay(List<Ray3D> rays)
{
foreach (Ray3D ray in rays)
{
if (IntersectsRay(ray)) return true;
}
return false;
}
public bool IntersectsRay(Ray3D ray)
{
float t;
return IntersectsRay(ray, out t);
}
public void SetNormal(Vector3 normal)
{
normal.Normalize();
Vector3 currentNormal = Plane.normal;
bool pointsInSameDirection;
if (normal.IsAlignedWith(currentNormal, out pointsInSameDirection)) Rotate(GetLocalAxis(CoordinateSystemAxis.PositiveRight), 180.0f);
else
{
Vector3 rotationAxis = Vector3.Cross(currentNormal, normal);
Rotate(rotationAxis, currentNormal.AngleWith(normal));
}
}
public void Rotate(Vector3 rotationAxis, float angleInDegrees)
{
rotationAxis.Normalize();
Quaternion rotationQuaternion = Quaternion.AngleAxis(angleInDegrees, rotationAxis);
Rotation = rotationQuaternion * Rotation;
}
public Vector3 GetLocalAxis(CoordinateSystemAxis axis)
{
return _coordinateSystem.GetAxisVector(axis);
}
#endregion
}
}
#endif