/******************************************************************************
* 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 System;
using System.Collections.Generic;
using UnityEngine;
namespace Leap.Unity.Drawing
{
/// Simple drawing interface abstraction (intended for debug drawing,
/// not production!) with statically-accessible backend implementations via
/// HyperMegaLines, Unity Debug drawing, and Unity Gizmo drawing.
public class Drawer
{
// Minimum Drawer Functions.
public System.Action implDrawLine;
public System.Action implSetColor;
/// Calls the `setColor` delegate.
public Color color { set { implSetColor(value); } }
public Stack _matrices;
public Matrix4x4 _currMatrix = Matrix4x4.identity;
public bool MaybePushMatrix(Matrix4x4? m)
{
if (m != null) { PushMatrix(m.Value); return true; } else { return false; }
}
public void PushMatrix(Matrix4x4 m)
{
_matrices.Push(m);
_currMatrix = m;
}
public void PopMatrix()
{
_currMatrix = _matrices.Pop();
}
// Extended Drawer Functions.
public System.Action implDrawUnitSphere;
public bool isActiveAndEnabled = true;
public void MaybeSetColor(Color? maybeColor)
{
if (maybeColor != null) { implSetColor(maybeColor.Value); }
}
#region Implementations
//private static Drawer _hyperMegaDrawer;
//private static Leap.Unity.HyperMegaStuff.HyperMegaLines _hyperMegaLines;
//public static Drawer HyperMegaDrawer { get {
// var drawer = Utils.Require(ref _hyperMegaDrawer);
// if (_hyperMegaLines == null) {
// _hyperMegaLines = HyperMegaStuff.HyperMegaLines.drawer;
// }
// drawer.isActiveAndEnabled = _hyperMegaLines.isActiveAndEnabled;
// drawer.implDrawLine = (a, b) => {
// _hyperMegaLines.DrawLine(a, b);
// };
// drawer.implSetColor = (c) => {
// _hyperMegaLines.color = c;
// };
// return drawer;
//}}
private static Drawer _unityDebugDrawer;
private static Color _unityDebugColor = Color.white;
public static Drawer UnityDebugDrawer
{
get
{
var drawer = Utils.Require(ref _unityDebugDrawer);
// Debug Lines are cleared on Update, don't use in edit mode
drawer.isActiveAndEnabled = Application.isPlaying;
drawer.implDrawLine = (a, b) =>
{
var color = Drawer._unityDebugColor;
UnityEngine.Debug.DrawLine(a, b, color);
};
drawer.implSetColor = (c) =>
{
_unityDebugColor = c;
};
return drawer;
}
}
private static Drawer _unityGizmoDrawer;
/// For use in OnDrawGizmos and OnDrawGizmosSelected.
public static Drawer UnityGizmoDrawer
{
get
{
var drawer = Utils.Require(ref _unityGizmoDrawer);
drawer.isActiveAndEnabled = true;
drawer.implDrawLine = (a, b) =>
{
Gizmos.DrawLine(a, b);
};
drawer.implSetColor = (c) =>
{
Gizmos.color = c;
};
drawer.implDrawUnitSphere = (m) =>
{
var origM = Gizmos.matrix;
Gizmos.matrix = m;
Gizmos.DrawSphere(Vector3.zero, 1f);
Gizmos.matrix = origM;
};
return drawer;
}
}
private static Drawer _unityGizmoHandlesDrawer;
/// For use in OnDrawGizmos and OnDrawGizmosSelected via the Handles API. By default, draws on _top_ of any scene geometry.
public static Drawer UnityGizmoHandlesDrawer
{
get
{
var drawer = Utils.Require(ref _unityGizmoHandlesDrawer);
drawer.isActiveAndEnabled = true;
drawer.implDrawLine = (a, b) =>
{
#if UNITY_EDITOR
UnityEditor.Handles.DrawLine(a, b);
#endif
};
drawer.implSetColor = (c) =>
{
#if UNITY_EDITOR
UnityEditor.Handles.color = c;
#endif
};
drawer.implDrawUnitSphere = (m) =>
{
#if UNITY_EDITOR
var origM = UnityEditor.Handles.matrix;
var origColor = UnityEditor.Handles.color;
float h, s, v;
Color.RGBToHSV(origColor, out h, out s, out v);
UnityEditor.Handles.color = origColor.WithHSV(h, s * 1f, v * 3f);
UnityEditor.Handles.matrix = m;
// UnityEditor.Handles.SphereHandleCap
UnityEditor.Handles.SphereHandleCap(0, Vector3.zero, Quaternion.identity, 1f, EventType.Repaint);
UnityEditor.Handles.matrix = origM;
#endif
};
return drawer;
}
}
#endregion // Implementations
#region Line Drawing
public void Line(Vector3 a, Vector3 b)
{
this.implDrawLine(
_currMatrix.MultiplyPoint3x4(a),
_currMatrix.MultiplyPoint3x4(b)
);
}
public void Line(Vector3 a, Vector3 b, Color? color)
{
MaybeSetColor(color);
Line(a, b);
}
private Action drawLineAction
{
get
{
return Utils.Require(ref _b_drawLineAction, () => (a, b) => Line(a, b));
}
}
private Action _b_drawLineAction;
public void Lines(
System.Action> drawLineFuncFunc,
Color? color = null)
{
MaybeSetColor(color);
drawLineFuncFunc(drawLineAction);
}
#endregion
#region Sphere Drawing
public void Sphere(Vector3 center = default(Vector3), float radius = 1f,
Color? color = null, Matrix4x4? matrix = null)
{
try
{
if (implDrawUnitSphere != null)
{
var useMatrix = (matrix ?? Matrix4x4.identity) * Matrix4x4.TRS(center, Quaternion.identity, Vector3.one * radius);
MaybeSetColor(color);
implDrawUnitSphere(useMatrix);
}
else
{
// Fall back to line drawing.
this.WireSphere(center, radius, color, matrix);
}
}
catch (System.Exception e) { Debug.LogError(e); }
}
#endregion
}
public static class DrawerExtensions
{
public static void WireSphere(this Drawer drawer,
Vector3 center = default(Vector3), float radius = 1f, Color? color = null,
Matrix4x4? matrix = null)
{
drawer.Lines(drawLineFunc =>
new Geometry.Sphere(center, radius, overrideMatrix: matrix)
.DrawLines(drawLineFunc), color);
}
public static void Lines(this Drawer drawer, Vector3 a, Vector3 b,
Vector3 c)
{
drawer.Line(a, b); drawer.Line(b, c);
}
public static void Lines(this Drawer drawer, Vector3 a, Vector3 b,
Vector3 c, Vector3 d)
{
drawer.Line(a, b); drawer.Line(b, c); drawer.Line(c, d);
}
public static void Lines(this Drawer drawer, Vector3 a, Vector3 b,
Vector3 c, Vector3 d, Vector3 e)
{
drawer.Line(a, b); drawer.Line(b, c); drawer.Line(c, d);
drawer.Line(d, e);
}
private static Color[] _basisColors;
public static void DrawBasis(this Transform t, Drawer drawer,
float? length = null,
Color? overrideAxesColor = null,
float? axesHueShift = null,
Matrix4x4? overrideMatrix = null)
{
var useLength = length.UnwrapOr(0.5f);
var useMatrix = overrideMatrix.UnwrapOr(t.localToWorldMatrix);
var a = Vector3.zero;
Color xColor = Color.red, yColor = Color.green, zColor = Color.blue;
if (overrideAxesColor != null)
{
xColor = yColor = zColor = overrideAxesColor.Value;
}
if (axesHueShift != null)
{
var hueShift = axesHueShift.Value;
xColor = xColor.ShiftHue(hueShift);
yColor = yColor.ShiftHue(hueShift);
zColor = zColor.ShiftHue(hueShift);
}
Utils.Require(ref _basisColors, 3);
_basisColors[0] = xColor;
_basisColors[1] = yColor;
_basisColors[2] = zColor;
var octahedron = new Geometry.Bipyramid(a: a, b: Vector3.zero,
polySegments: 4, lengthFraction: 0.5f,
overrideMatrix: useMatrix);
for (var bIdx = 0; bIdx < 3; bIdx++)
{
var b = Vector3.zero; b[bIdx] = useLength;
octahedron.b = b;
drawer.color = _basisColors[bIdx];
octahedron.DrawLines(drawer.implDrawLine);
}
}
/// Draws a bipyramidal basis axis using red, green, and blue for
/// the X, Y, and Z axes at the argument pose.
///
/// You can override the axis color with a single value (`overrideAxesColor`),
/// or you can pass a hue shift value to apply to the axis colors
/// (`axesHueShift`).
public static void Draw(this Pose pose, Drawer drawer,
float length, Color? overrideAxesColor = null,
float? hueShift = null)
{
Color xColor = Color.red, yColor = Color.green, zColor = Color.blue;
if (overrideAxesColor != null)
{
xColor = yColor = zColor = overrideAxesColor.Value;
}
if (hueShift != null)
{
var useHueShift = hueShift.Value;
xColor = xColor.ShiftHue((float)useHueShift);
yColor = yColor.ShiftHue((float)useHueShift);
zColor = zColor.ShiftHue((float)useHueShift);
}
Utils.Require(ref _basisColors, 3);
_basisColors[0] = xColor;
_basisColors[1] = yColor;
_basisColors[2] = zColor;
var bipyramid = new Geometry.Bipyramid(a: Vector3.zero, b: Vector3.zero,
polySegments: 16, lengthFraction: 0.5f, overrideMatrix: pose.matrix());
for (var bIdx = 0; bIdx < 3; bIdx++)
{
var b = Vector3.zero; b[bIdx] = length;
bipyramid.b = b;
drawer.color = _basisColors[bIdx];
bipyramid.DrawLines(drawer.implDrawLine);
}
}
public static void Draw(this Matrix4x4 m, Drawer drawer,
float axisLengths, float? hueShift = null,
Color? color = null,
Color? xColor = null, Color? yColor = null, Color? zColor = null,
bool drawBips = false)
{
var useXColor = xColor ?? color ?? Color.red;
var useYColor = yColor ?? color ?? Color.green;
var useZColor = zColor ?? color ?? Color.blue;
if (hueShift != null)
{
useXColor = useXColor.ShiftHue(hueShift.Value);
useYColor = useYColor.ShiftHue(hueShift.Value);
useZColor = useZColor.ShiftHue(hueShift.Value);
}
if (drawBips)
{
var bipyramid = new Geometry.Bipyramid(a: Vector3.zero, b: Vector3.zero,
polySegments: 16, lengthFraction: 0.5f, overrideMatrix: m);
for (var bIdx = 0; bIdx < 3; bIdx++)
{
var b = Vector3.zero; b[bIdx] = axisLengths;
bipyramid.b = b;
if (bIdx == 0) { drawer.color = useXColor; }
if (bIdx == 1) { drawer.color = useYColor; }
if (bIdx == 2) { drawer.color = useZColor; }
bipyramid.DrawLines(drawer.implDrawLine);
}
}
else
{
for (var i = 0; i < 3; i++)
{
var pos = m.GetPosition();
if (i == 0) { drawer.color = useXColor; }
if (i == 1) { drawer.color = useYColor; }
if (i == 2) { drawer.color = useZColor; }
drawer.implDrawLine(pos, pos + m.GetAxis(i) * axisLengths);
}
}
}
}
}