/******************************************************************************
* 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.Drawing;
using Leap.Unity.RuntimeGizmos;
using System;
using UnityEngine;
namespace Leap.Unity.Geometry
{
using UnityRect = UnityEngine.Rect;
[System.Serializable]
public struct Circle
{
public Transform transform;
public Vector3 center;
private Direction3 direction;
public float radius;
private Matrix4x4? _overrideMatrix;
public Matrix4x4? overrideMatrix
{
get { return _overrideMatrix; }
set { _overrideMatrix = value; }
}
#region Constructors
public Circle(LocalCircle localCircle, Transform withTransform)
: this(localCircle.center, localCircle.direction, localCircle.radius,
withTransform)
{ }
public Circle(float radius = 0.5f, Component transformSource = null)
: this(default(Vector3), default(Direction3), radius, transformSource) { }
public Circle(Vector3 center = default(Vector3),
Direction3 direction = default(Direction3), float radius = 0.5f,
Component transformSource = null)
{
this.transform = (transformSource == null ? null : transformSource.transform);
this.center = center;
this.direction = direction;
this.radius = radius;
this._overrideMatrix = null;
}
public Circle(Vector3 center = default(Vector3),
Direction3 direction = default(Direction3), float radius = 0.5f,
Transform transform = null) : this(center, direction, radius,
(Component)transform)
{ }
public Circle(Vector3 center = default(Vector3),
Direction3 direction = default(Direction3), float radius = 0.5f,
Matrix4x4? overrideMatrix = null) : this(center, direction, radius,
(Component)null)
{
this._overrideMatrix = overrideMatrix;
}
#endregion
#region Accessors
///
/// Local-to-world matrix for this Circle.
///
public Matrix4x4 matrix
{
get
{
if (overrideMatrix != null)
{
return overrideMatrix.Value * Matrix4x4.Translate(center);
}
else if (transform == null)
{
return Matrix4x4.Translate(center);
}
return transform.localToWorldMatrix * Matrix4x4.Translate(center);
}
}
///
/// The world position of the center of this Circle (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 Debug Rendering
public void DrawRuntimeGizmos(RuntimeGizmoDrawer drawer)
{
Matrix4x4 m = Matrix4x4.identity;
if (transform != null)
{
m = transform.localToWorldMatrix;
}
drawer.PushMatrix();
drawer.matrix = m;
drawer.DrawWireArc(
center: center,
normal: direction,
radialStartDirection: direction.Vec().GetPerpendicular(),
radius: radius,
fractionOfCircleToDraw: 1f,
numCircleSegments: 44
);
drawer.PopMatrix();
}
public void Draw(Drawer drawer, Color? color = null)
{
if (color != null) { drawer.color = color.Value; }
DrawWireArc(
center: center,
normal: direction,
radialStartDirection: direction.Vec().GetPerpendicular(),
radius: radius,
fractionOfCircleToDraw: 1f,
numCircleSegments: 44,
matrix: transform == null ?
Matrix4x4.identity : transform.localToWorldMatrix,
lineDrawingFunc: drawer.Line
);
}
public void DrawLines(Action lineDrawingFunc)
{
DrawWireArc(
center: center,
normal: direction,
radialStartDirection: direction.Vec().GetPerpendicular(),
radius: radius,
fractionOfCircleToDraw: 1f,
numCircleSegments: 44,
matrix: transform == null ?
Matrix4x4.identity : transform.localToWorldMatrix,
lineDrawingFunc: lineDrawingFunc
);
}
// Welp, RuntimeGizmos might need an overhaul to accept arbitrary drawing
// functions, because this is super useful.
public static void DrawWireArc(Vector3 center, Vector3 normal,
float radius, int numCircleSegments,
Action lineDrawingFunc,
float fractionOfCircleToDraw = 1.0f,
Matrix4x4? matrix = null,
Vector3? radialStartDirection = null)
{
if (!matrix.HasValue)
{
matrix = Matrix4x4.identity;
}
if (!radialStartDirection.HasValue)
{
radialStartDirection = normal.GetPerpendicular();
}
normal = normal.normalized;
Vector3 radiusVector = radialStartDirection.Value.normalized * radius;
Vector3 nextVector;
int numSegmentsToDraw = (int)(numCircleSegments * fractionOfCircleToDraw);
Quaternion rotator = Quaternion.AngleAxis(360f / numCircleSegments, normal);
for (int i = 0; i < numSegmentsToDraw; i++)
{
nextVector = rotator * radiusVector;
lineDrawingFunc(
matrix.Value.MultiplyPoint3x4(center + radiusVector),
matrix.Value.MultiplyPoint3x4(center + nextVector)
);
radiusVector = nextVector;
}
}
#endregion
#region Enumerators
public CirclePointEnumerator Points(int numPoints)
{
return new CirclePointEnumerator(this, numPoints);
}
public CircleSegmentEnumerator Segments(int numLines)
{
return new CircleSegmentEnumerator(this, numLines);
}
public struct CirclePointEnumerator
{
Circle circle; int numPoints; int idx;
Vector3 startRadiusVector; Quaternion rotator;
Vector3? radiusVector;
public CirclePointEnumerator(Circle circle, int numPoints)
{
this.circle = circle;
this.numPoints = Mathf.Max(3, numPoints);
this.idx = -1;
this.startRadiusVector = ((Vector3)circle.direction).GetPerpendicular()
* circle.radius;
this.rotator = Quaternion.AngleAxis(360f / numPoints, circle.direction);
this.radiusVector = null;
}
public Vector3 Current
{
get
{
return circle.matrix.MultiplyPoint3x4(radiusVector.GetValueOrDefault());
}
}
public bool MoveNext()
{
if (idx == numPoints - 1) { return false; }
idx += 1;
if (radiusVector == null) { radiusVector = startRadiusVector; }
else { radiusVector = rotator * radiusVector; }
return true;
}
public CirclePointEnumerator GetEnumerator() { return this; }
}
public struct CircleSegmentEnumerator
{
CirclePointEnumerator points;
Vector3? firstPoint;
Vector3? lastPointReturned;
bool reachedLastSegment;
public CircleSegmentEnumerator(Circle circle, int numLines)
{
numLines = Mathf.Max(1, numLines);
this.points = new CirclePointEnumerator(circle, numLines + 1);
firstPoint = null;
lastPointReturned = null;
reachedLastSegment = false;
}
public LocalSegment3 Current
{
get
{
if (reachedLastSegment)
{
return new LocalSegment3(lastPointReturned.Value, firstPoint.Value);
}
if (!lastPointReturned.HasValue)
{
return new LocalSegment3(points.Current, points.Current);
}
else
{
return new LocalSegment3(lastPointReturned.Value, points.Current);
}
}
}
public bool MoveNext()
{
if (!lastPointReturned.HasValue)
{
if (!points.MoveNext()) { return false; }
else { firstPoint = points.Current; }
}
lastPointReturned = points.Current;
if (!points.MoveNext())
{
if (!reachedLastSegment)
{
reachedLastSegment = true;
return true;
}
return false;
}
return true;
}
public CircleSegmentEnumerator GetEnumerator() { return this; }
}
#endregion
}
public static class CircleExtensions
{
public static Vector3 GetPerpendicular(this Vector3 v)
{
return Utils.Perpendicular(v);
}
}
}