/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2021. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
* between Ultraleap and you, your company or other organization. *
******************************************************************************/
using Leap.Unity.Infix;
using Leap.Unity.RuntimeGizmos;
using System;
using UnityEngine;
namespace Leap.Unity.Geometry
{
using UnityRect = UnityEngine.Rect;
[System.Serializable]
public struct Sphere
{
public Transform transform;
public Vector3 center;
public float radius;
public Matrix4x4? overrideMatrix;
#region Constructors
public Sphere(LocalSphere localSphere, Transform withTransform)
: this(localSphere.center, localSphere.radius, withTransform) { }
public Sphere(float radius = 0.10f, Component transformSource = null)
: this(default(Vector3), radius, transformSource) { }
public Sphere(float radius = 0.10f)
: this(default(Vector3), radius, null) { }
public Sphere(float radius = 0.10f, Matrix4x4? overrideMatrix = null)
: this(default(Vector3), radius, null, overrideMatrix) { }
public Sphere(Vector3 center = default(Vector3), float radius = 0.10f) :
this(center, radius, (Component)null)
{ }
public Sphere(Vector3 center = default(Vector3), float radius = 0.10f,
Component transformSource = null, Matrix4x4? overrideMatrix = null)
{
this.transform = (transformSource == null ? null : transformSource.transform);
this.center = center;
this.radius = radius;
this.overrideMatrix = overrideMatrix;
}
public Sphere(Vector3 center = default(Vector3), float radius = 0.10f,
Transform transform = null) : this(center, radius, (Component)transform) { }
public Sphere(Sphere other) : this(other.center, other.radius, other.transform) { }
#endregion
#region Accessors
///
/// Local-to-world matrix for this Sphere.
///
public Matrix4x4 matrix
{
get
{
if (overrideMatrix != null)
{
return overrideMatrix.Value * Matrix4x4.Translate(center);
}
if (transform == null)
{
return Matrix4x4.Translate(center);
}
return transform.localToWorldMatrix * Matrix4x4.Translate(center);
}
}
///
/// The world position of the center of this sphere (read only). This is dependent on
/// the state of its Transform if it has one, as well as its defined local-space
/// center position.
///
public Vector3 position
{
get
{
return this.matrix.MultiplyPoint3x4(Vector3.zero);
}
}
#endregion
#region Chain Calls
public Sphere WithCenter(Vector3 center)
{
var copy = new Sphere(this); copy.center = center; return copy;
}
#endregion
#region Collision
public bool Overlaps(Box box)
{
return Collision.DoesOverlap(this, box);
}
///
/// Returns the distance between the closest points on the Rect and the Sphere,
/// or 0 if the two overlap.
///
public float DistanceTo(Rect rect)
{
return Collision.DistanceBetween(this, rect);
}
public bool Overlaps(Rect rect)
{
return Collision.DoesOverlap(this, rect);
}
#endregion
#region Debug Rendering
public void DrawLines(Action lineDrawingFunc,
int latitudinalDivisions = 5,
int longitudinalDivisions = 5,
int numCircleSegments = 7,
Matrix4x4? matrixOverride = null)
{
Matrix4x4 m = Matrix4x4.identity;
if (transform != null)
{
m = transform.localToWorldMatrix;
}
if (matrixOverride.HasValue)
{
m = matrixOverride.Value;
}
// Vector3 center = m.MultiplyPoint3x4(this.center);
// float radius = m.MultiplyPoint3x4(Vector3.right).magnitude * this.radius;
// Vector3 x = m.MultiplyVector(Vector3.right);
// Vector3 y = m.MultiplyVector(Vector3.up);
//Vector3 z = m.MultiplyVector(Vector3.forward); // unused
var center = this.center;
var radius = this.radius;
var x = Vector3.right;
var y = Vector3.up;
// Wire lat-long sphere
int latDiv = latitudinalDivisions;
float latAngle = 180f / latDiv; float accumLatAngle = 0f;
int lonDiv = longitudinalDivisions;
float lonAngle = 180f / lonDiv;
Quaternion lonRot = Quaternion.AngleAxis(lonAngle, y);
Vector3 lonNormal = x;
for (int i = 0; i < latDiv; i++)
{
accumLatAngle += latAngle;
Circle.DrawWireArc(
center: center + y * Mathf.Cos(accumLatAngle * Mathf.Deg2Rad) * radius,
normal: y,
radius: Mathf.Sin(accumLatAngle * Mathf.Deg2Rad) * radius,
numCircleSegments: numCircleSegments,
lineDrawingFunc: lineDrawingFunc,
fractionOfCircleToDraw: 1.0f,
radialStartDirection: x,
matrix: m
);
}
for (int i = 0; i < lonDiv; i++)
{
Circle.DrawWireArc(
center: center,
normal: lonNormal,
radius: radius,
numCircleSegments: numCircleSegments,
lineDrawingFunc: lineDrawingFunc,
fractionOfCircleToDraw: 1.0f,
radialStartDirection: y,
matrix: m
);
lonNormal = lonRot * lonNormal;
}
}
public void DrawRuntimeGizmos(RuntimeGizmoDrawer drawer)
{
Matrix4x4 m = Matrix4x4.identity;
if (transform != null)
{
m = transform.localToWorldMatrix;
}
var origDrawerColor = drawer.color;
Vector3 center = m.MultiplyPoint3x4(this.center);
float radius = m.MultiplyPoint3x4(Vector3.right).magnitude * this.radius;
Vector3 x = m.MultiplyVector(Vector3.right);
Vector3 y = m.MultiplyVector(Vector3.up);
//Vector3 z = m.MultiplyVector(Vector3.forward); // unused
// Sphere
drawer.color = drawer.color.WithAlpha(origDrawerColor.a * 0.05f);
drawer.DrawSphere(center, radius);
// Wire lat-long sphere
drawer.color = drawer.color.WithAlpha(origDrawerColor.a * 0.2f);
int latDiv = 6;
float latAngle = 180f / latDiv; float accumLatAngle = 0f;
int lonDiv = 6;
float lonAngle = 180f / lonDiv;
Quaternion lonRot = Quaternion.AngleAxis(lonAngle, y);
Vector3 lonNormal = x;
for (int i = 0; i < latDiv; i++)
{
accumLatAngle += latAngle;
drawer.DrawWireArc(center: center + y * Mathf.Cos(accumLatAngle * Mathf.Deg2Rad) * radius,
normal: y,
radialStartDirection: x,
radius: Mathf.Sin(accumLatAngle * Mathf.Deg2Rad) * radius,
fractionOfCircleToDraw: 1.0f,
numCircleSegments: 22);
}
for (int i = 0; i < latDiv; i++)
{
drawer.DrawWireArc(center: center,
normal: lonNormal,
radialStartDirection: y,
radius: radius,
fractionOfCircleToDraw: 1.0f,
numCircleSegments: 22);
lonNormal = lonRot * lonNormal;
}
drawer.color = origDrawerColor;
}
#endregion
}
public static class SphereExtensions
{
///
/// Defines a Sphere at this position with the argument radius.
///
public static Sphere ToSphere(this Vector3 vec3, float radius)
{
return new Sphere(vec3, radius);
}
}
}