轨迹插件引入

This commit is contained in:
lishuo committed 2022-09-06 13:47:45 +08:00
1 parent 89b5275efd
commit 195893a1b0
1023 files changed
+378944 -18404

No files matched your search

@@ -0,0 +1,533 @@
// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using UnityEngine;
using System.Collections;
using System.Reflection;
using System;
using System.Collections.Generic;
using FluffyUnderware.DevTools.Extensions;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace FluffyUnderware.DevTools
{
#region ### Interfaces ###
/// <summary>
/// Interface for attributes that change group parsing options
/// </summary>
public interface IDTGroupParsingAttribute
{
string Path { get; }
}
/// <summary>
/// Interface for attributes that change field parsing options
/// </summary>
public interface IDTFieldParsingAttribute { }
/// <summary>
/// Interface for attributes that change group rendering options
/// </summary>
public interface IDTGroupRenderAttribute { }
/// <summary>
/// Interface for attributes that change field rendering options
/// </summary>
public interface IDTFieldRenderAttribute { }
#endregion
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DTVersionAttribute : System.Attribute
{
public readonly string Version;
public DTVersionAttribute(string version)
{
Version = version;
}
}
#region ### Base Attributes ###
//OPTIM: all the attributes bellow (and maybe DTVersionAttribute too) seem to be used only in editor code. If so, and there overhead is big, make their compilation conditional to gain performance in the build applications
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class DTAttribute : System.Attribute, IComparable
{
public int TypeSort { get; protected set; }
public int Sort = 100;
public bool ShowBelowProperty;
public int Space;
public virtual int CompareTo(object obj)
{
DTAttribute other = (DTAttribute)obj;
int v = ShowBelowProperty.CompareTo(other.ShowBelowProperty);
if (v == 0)
{
int ts = TypeSort.CompareTo(other.TypeSort);
if (ts == 0)
return Sort.CompareTo(other.Sort);
else
return ts;
}
else
return v;
}
public DTAttribute(int sortOrder, bool showBelow = false)
{
TypeSort = sortOrder;
ShowBelowProperty = showBelow;
}
}
/// <summary>
/// Explicitely add the next field to a specific group
/// </summary>
public class GroupAttribute : DTAttribute, IDTGroupParsingAttribute, IDTGroupRenderAttribute
{
public string Path
{
get { return mPath; }
protected set
{
PathIsAbsolute = !string.IsNullOrEmpty(value) && value.StartsWith("@");
if (PathIsAbsolute)
{
mPath = value.Substring(1);
if (string.IsNullOrEmpty(mPath))
mPath = null;
}
else
mPath = value;
}
}
public bool PathIsAbsolute { get; private set; }
public bool Expanded = true;
public bool Invisible;
public string Label = null;
public string Tooltip = null;
public string HelpURL = null;
string mPath;
public GroupAttribute(string pathAndName)
: base(15)
{
Path = pathAndName;
}
}
public class ActionAttribute : DTAttribute
{
public enum ActionEnum { Show, Hide, Enable, Disable, ShowInfo, ShowWarning, ShowError, Callback }
public enum ActionPositionEnum { Above, Below }
public ActionEnum Action = ActionEnum.Callback;
public ActionPositionEnum Position = ActionPositionEnum.Below;
public object ActionData;
MethodInfo mCallback;
protected ActionAttribute(string actionData, ActionEnum action = ActionEnum.Callback)
: base(50)
{
ActionData = actionData;
Action = action;
}
public void Callback(object classInstance)
{
string methodName = ActionData as string;
if (!string.IsNullOrEmpty(methodName))
{
if (mCallback == null)
mCallback = classInstance.GetType().MethodByName(methodName, true, true);
if (mCallback != null)
mCallback.Invoke(classInstance, null);
else
Debug.LogWarningFormat("[DevTools] Unable to find method '{0}' at class '{1}' !", methodName, classInstance.GetType().Name);
}
}
}
public class ConditionalAttribute : ActionAttribute
{
public enum OperatorEnum { AND, OR };
public class Condition
{
public string FieldName;
public FieldInfo FieldInfo;
public PropertyInfo PropertyInfo;
public object CompareTo;
public bool CompareFalse;
public OperatorEnum Operator;
public MethodInfo MethodInfo;
public string MethodName;
}
public Condition[] Conditions;
protected ConditionalAttribute(string fieldOrProperty, object compareTo, bool compareFalse = false)
: base(null, ActionEnum.Show)
{
TypeSort = 55;
Conditions = new Condition[1]
{
new Condition()
{
FieldName=fieldOrProperty,
CompareTo=compareTo,
CompareFalse=compareFalse
}
};
}
protected ConditionalAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
string fieldOrProperty2, object compareTo2, bool compareFalse2)
: base(null, ActionEnum.Show)
{
TypeSort = 55;
Conditions = new Condition[2]
{
new Condition()
{
FieldName=fieldOrProperty,
CompareTo=compareTo,
CompareFalse=compareFalse
},
new Condition()
{
FieldName=fieldOrProperty2,
CompareTo=compareTo2,
CompareFalse=compareFalse2,
Operator=op
}
};
}
protected ConditionalAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
string fieldOrProperty2, object compareTo2, bool compareFalse2,
string fieldOrProperty3, object compareTo3, bool compareFalse3)
: base(null, ActionEnum.Show)
{
TypeSort = 55;
Conditions = new Condition[3]
{
new Condition()
{
FieldName=fieldOrProperty,
CompareTo=compareTo,
CompareFalse=compareFalse
},
new Condition()
{
FieldName=fieldOrProperty2,
CompareTo=compareTo2,
CompareFalse=compareFalse2,
Operator=op
},
new Condition()
{
FieldName=fieldOrProperty3,
CompareTo=compareTo3,
CompareFalse=compareFalse3,
Operator=op
}
};
}
protected ConditionalAttribute(string methodToQuery)
: base(null, ActionEnum.Show)
{
TypeSort = 55;
Conditions = new Condition[1]
{
new Condition()
{
MethodName=methodToQuery,
CompareTo=null
}
};
}
public virtual bool ConditionMet(object classInstance)
{
bool result = evaluate(Conditions[0], classInstance);
for (int i = 1; i < Conditions.Length; i++)
{
Condition c = Conditions[i];
switch (c.Operator)
{
case OperatorEnum.AND:
result = result && evaluate(c, classInstance);
break;
case OperatorEnum.OR:
result = result || evaluate(c, classInstance);
break;
}
}
return result;
}
bool evaluate(Condition cond, object classInstance)
{
if (!string.IsNullOrEmpty(cond.MethodName))
{
if (cond.MethodInfo == null)
cond.MethodInfo = classInstance.GetType().MethodByName(cond.MethodName, true, true);
if (cond.MethodInfo != null)
{
if (cond.CompareFalse)
return !(bool)cond.MethodInfo.Invoke(classInstance, null);
else
return (bool)cond.MethodInfo.Invoke(classInstance, null);
}
else
{
Debug.LogWarningFormat("[DevTools] Unable to find method '{0}' at class '{1}' !", cond.MethodName, classInstance.GetType().Name);
return cond.CompareFalse;
}
}
else
{
if (cond.FieldInfo == null)
{
cond.FieldInfo = classInstance.GetType().FieldByName(cond.FieldName, true, true);
if (cond.FieldInfo == null)
{
cond.PropertyInfo = classInstance.GetType().PropertyByName(cond.FieldName, true, true);
}
}
object value = null;
if (cond.FieldInfo != null)
value = cond.FieldInfo.GetValue(classInstance);
else if (cond.PropertyInfo != null)
value = cond.PropertyInfo.GetValue(classInstance, null);
if (value == null)
{
return (cond.CompareTo == null && !cond.CompareFalse);
}
else
return value.Equals(cond.CompareTo) == !cond.CompareFalse;
}
}
}
#endregion
#region ### Attributes handling Nesting/Parsing ###
public class SortOrderAttribute : DTAttribute, IDTFieldParsingAttribute
{
public SortOrderAttribute(int sort = 100) : base(0)
{
Sort = sort;
}
}
/// <summary>
/// Show children inside a section. Start the path with "@" to break out of any previous sections
/// </summary>
public class AsGroupAttribute : GroupAttribute, IDTFieldParsingAttribute, IDTFieldRenderAttribute
{
public AsGroupAttribute(string pathAndName = null)
: base(pathAndName)
{
TypeSort = 10;
}
}
/// <summary>
/// Show all subsequent fields inside a section rendered as a tab
/// </summary>
public class TabAttribute : GroupAttribute
{
public readonly string TabName;
public readonly string TabBarName;
public TabAttribute(string pathAndName)
: base("")
{
TypeSort = 10;
string p;
split(pathAndName, out p, out TabBarName, out TabName);
Path = p;
}
static bool split(string pathAndName, out string path, out string tabBar, out string tabname)
{
string[] p = pathAndName.Split('/');
path = string.Empty;
tabBar = string.Empty;
tabname = pathAndName;
if (p.Length == 0)
return false;
else if (p.Length == 1)
{
tabname = p[0];
tabBar = "Default";
return true;
}
else
{
tabname = p[p.Length - 1];
tabBar = p[p.Length - 2];
path = string.Join("/", p, 0, p.Length - 2);
return true;
}
}
}
/// <summary>
/// Show all subsequent fields inside a section.
/// </summary>
public class SectionAttribute : GroupAttribute
{
public bool Fixed;
public SectionAttribute(string name, bool expanded = true, bool fix = false, int sort = 100)
: base(name)
{
Expanded = expanded;
TypeSort = 10;
Sort = sort;
Fixed = fix;
}
}
/// <summary>
/// Ends any section
/// </summary>
public class NoSectionAttribute : SectionAttribute
{
public NoSectionAttribute() : base("")
{
TypeSort = 10;
}
}
/// <summary>
/// Hides Field and doesn't enter children
/// </summary>
public class Hide : DTAttribute, IDTFieldParsingAttribute, IDTFieldRenderAttribute
{
public Hide() : base(20) { }
}
/// <summary>
/// Hides Field, but enter children
/// </summary>
public class Inline : DTAttribute, IDTFieldParsingAttribute, IDTFieldRenderAttribute
{
public Inline() : base(21) { }
}
#endregion
#region ### Attributes handling visual representation ###
public class ArrayExAttribute : DTAttribute, IDTFieldParsingAttribute
{
public bool Draggable = true;
public bool ShowHeader = true;
public bool ShowAdd = true;
public bool ShowDelete = true;
public bool DropTarget = true;
public ArrayExAttribute()
: base(35)
{
}
}
#endregion
#region ### Attributes handling actions and dynamic evaluation ###
public class FieldActionAttribute : ActionAttribute, IDTFieldRenderAttribute
{
public FieldActionAttribute(string actionData, ActionEnum action = ActionEnum.Callback)
: base(actionData, action) { }
}
public class GroupActionAttribute : ActionAttribute, IDTGroupRenderAttribute
{
public GroupActionAttribute(string actionData, ActionEnum action = ActionEnum.Callback)
: base(actionData, action) { }
}
public class FieldConditionAttribute : ConditionalAttribute, IDTFieldRenderAttribute
{
public FieldConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse = false, ActionEnum action = ActionEnum.Show, object actionData = null, ActionPositionEnum position = ActionPositionEnum.Below)
: base(fieldOrProperty, compareTo, compareFalse)
{
Action = action;
ActionData = actionData;
Position = position;
}
public FieldConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
string fieldOrProperty2, object compareTo2, bool compareFalse2)
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2) { }
public FieldConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
string fieldOrProperty2, object compareTo2, bool compareFalse2,
string fieldOrProperty3, object compareTo3, bool compareFalse3)
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2, fieldOrProperty3, compareTo3, compareFalse3) { }
public FieldConditionAttribute(string methodToQuery)
: base(methodToQuery) { }
}
public class GroupConditionAttribute : ConditionalAttribute, IDTGroupRenderAttribute
{
public GroupConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse = false)
: base(fieldOrProperty, compareTo, compareFalse) { }
public GroupConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
string fieldOrProperty2, object compareTo2, bool compareFalse2)
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2) { }
public GroupConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
string fieldOrProperty2, object compareTo2, bool compareFalse2,
string fieldOrProperty3, object compareTo3, bool compareFalse3)
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2, fieldOrProperty3, compareTo3, compareFalse3) { }
public GroupConditionAttribute(string methodToQuery)
: base(methodToQuery) { }
}
#endregion
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 353b27613f9971242bf7f5673b2529a5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FluffyUnderware.DevTools
{
public class Couple<T1, T2>
{
public T1 First { get; set; }
public T2 Second { get; set; }
public Couple(T1 first, T2 second)
{
First = first;
Second = second;
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: c3f887f64f3b22845b9c93163066963a
timeCreated: 1525390532
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,989 @@
// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using UnityEngine;
using System.Collections;
namespace FluffyUnderware.DevTools
{
public static class DTTween
{
public enum EasingMethod
{
Linear = 0,
ExponentialIn = 1,
ExponentialOut = 2,
ExponentialInOut = 3,
ExponentialOutIn = 4,
CircularIn = 5,
CircularOut = 6,
CircularInOut = 7,
CircularOutIn = 8,
QuadraticIn = 9,
QuadraticOut = 10,
QuadraticInOut = 11,
QuadraticOutIn = 12,
SinusIn = 13,
SinusOut = 14,
SinusInOut = 15,
SinusOutIn = 16,
CubicIn = 17,
CubicOut = 18,
CubicInOut = 19,
CubicOutIn = 20,
QuarticIn = 21,
QuarticOut = 22,
QuarticInOut = 23,
QuarticOutIn = 24,
QuinticIn = 25,
QuinticOut = 26,
QuinticInOut = 27,
QuinticOutIn = 28
}
/// <summary>
/// Tween
/// </summary>
/// <param name="method">Tweening method</param>
/// <param name="t">Time (0..1)</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float Ease(EasingMethod method, float t, float b, float c)
{
switch (method)
{
case EasingMethod.ExponentialIn: return ExpoIn(t, b, c);
case EasingMethod.ExponentialOut: return ExpoOut(t, b, c);
case EasingMethod.ExponentialInOut: return ExpoInOut(t, b, c);
case EasingMethod.ExponentialOutIn: return ExpoOutIn(t, b, c);
case EasingMethod.CircularIn: return CircIn(t, b, c);
case EasingMethod.CircularOut: return CircOut(t, b, c);
case EasingMethod.CircularInOut: return CircInOut(t, b, c);
case EasingMethod.CircularOutIn: return CircOutIn(t, b, c);
case EasingMethod.QuadraticIn: return QuadIn(t, b, c);
case EasingMethod.QuadraticOut: return QuadOut(t, b, c);
case EasingMethod.QuadraticInOut: return QuadInOut(t, b, c);
case EasingMethod.QuadraticOutIn: return QuadOutIn(t, b, c);
case EasingMethod.SinusIn: return SineIn(t, b, c);
case EasingMethod.SinusOut: return SineOut(t, b, c);
case EasingMethod.SinusInOut: return SineInOut(t, b, c);
case EasingMethod.SinusOutIn: return SineOutIn(t, b, c);
case EasingMethod.CubicIn: return CubicIn(t, b, c);
case EasingMethod.CubicOut: return CubicOut(t, b, c);
case EasingMethod.CubicInOut: return CubicInOut(t, b, c);
case EasingMethod.CubicOutIn: return CubicOutIn(t, b, c);
case EasingMethod.QuarticIn: return QuartIn(t, b, c);
case EasingMethod.QuarticOut: return QuartOut(t, b, c);
case EasingMethod.QuarticInOut: return QuartInOut(t, b, c);
case EasingMethod.QuarticOutIn: return QuartOutIn(t, b, c);
case EasingMethod.QuinticIn: return QuintIn(t, b, c);
case EasingMethod.QuinticOut: return QuintOut(t, b, c);
case EasingMethod.QuinticInOut: return QuintInOut(t, b, c);
case EasingMethod.QuinticOutIn: return QuintOutIn(t, b, c);
default: return Linear(t, b, c);
}
}
/// <summary>
/// Tween
/// </summary>
/// <param name="method">Tweening method</param>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float Ease(EasingMethod method, float t, float b, float c, float d)
{
switch (method)
{
case EasingMethod.ExponentialIn: return ExpoIn(t, b, c, d);
case EasingMethod.ExponentialOut: return ExpoOut(t, b, c, d);
case EasingMethod.ExponentialInOut : return ExpoInOut(t, b, c, d);
case EasingMethod.ExponentialOutIn: return ExpoOutIn(t, b, c, d);
case EasingMethod.CircularIn: return CircIn(t, b, c, d);
case EasingMethod.CircularOut: return CircOut(t, b, c, d);
case EasingMethod.CircularInOut: return CircInOut(t, b, c, d);
case EasingMethod.CircularOutIn: return CircOutIn(t, b, c, d);
case EasingMethod.QuadraticIn : return QuadIn(t, b, c, d);
case EasingMethod.QuadraticOut : return QuadOut(t, b, c, d);
case EasingMethod.QuadraticInOut : return QuadInOut(t, b, c, d);
case EasingMethod.QuadraticOutIn : return QuadOutIn(t, b, c, d);
case EasingMethod.SinusIn: return SineIn(t, b, c, d);
case EasingMethod.SinusOut: return SineOut(t, b, c, d);
case EasingMethod.SinusInOut : return SineInOut(t, b, c, d);
case EasingMethod.SinusOutIn : return SineOutIn(t, b, c, d);
case EasingMethod.CubicIn: return CubicIn(t, b, c, d);
case EasingMethod.CubicOut: return CubicOut(t, b, c, d);
case EasingMethod.CubicInOut: return CubicInOut(t, b, c, d);
case EasingMethod.CubicOutIn: return CubicOutIn(t, b, c, d);
case EasingMethod.QuarticIn: return QuartIn(t, b, c, d);
case EasingMethod.QuarticOut: return QuartOut(t, b, c, d);
case EasingMethod.QuarticInOut: return QuartInOut(t, b, c, d);
case EasingMethod.QuarticOutIn: return QuartOutIn(t, b, c, d);
case EasingMethod.QuinticIn: return QuintIn(t, b, c, d);
case EasingMethod.QuinticOut: return QuintOut(t, b, c, d);
case EasingMethod.QuinticInOut: return QuintInOut(t, b, c, d);
case EasingMethod.QuinticOutIn: return QuintOutIn(t, b, c, d);
default: return Linear(t, b, c, d);
}
}
#region ### Linear ###
/// <summary>
/// Linear tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float Linear(float t, float b, float c)
{
return c * Mathf.Clamp01(t) + b;
}
/// <summary>
/// Linear tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float Linear(float t, float b, float c, float d)
{
return c * t / d + b;
}
#endregion
#region ### Exponential ###
/// <summary>
/// Exponential tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float ExpoOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return (t == 1) ? b + c : c * (-Mathf.Pow(2, -10 * t) + 1) + b;
}
/// <summary>
/// Exponential tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float ExpoOut(float t, float b, float c, float d)
{
return (t == d) ? b + c : c * (-Mathf.Pow(2, -10 * t / d) + 1) + b;
}
/// <summary>
/// Exponential tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float ExpoIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return (t == 0) ? b : c * Mathf.Pow(2, 10 * (t-1)) + b;
}
/// <summary>
/// Exponential tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float ExpoIn(float t, float b, float c, float d)
{
return (t == 0) ? b : c * Mathf.Pow(2, 10 * (t / d - 1)) + b;
}
/// <summary>
/// Exponential tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float ExpoInOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t == 0)
return b;
if (t == 1)
return b + c;
if ((t /= .5f) < 1)
return c / 2 * Mathf.Pow(2, 10 * (t - 1)) + b;
return c / 2 * (-Mathf.Pow(2, -10 * --t) + 2) + b;
}
/// <summary>
/// Exponential tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float ExpoInOut(float t, float b, float c, float d)
{
if (t == 0)
return b;
if (t == d)
return b + c;
if ((t /= d / 2) < 1)
return c / 2 * Mathf.Pow(2, 10 * (t - 1)) + b;
return c / 2 * (-Mathf.Pow(2, -10 * --t) + 2) + b;
}
/// <summary>
/// Exponential tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float ExpoOutIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t < .5f)
return ExpoOut(t * 2, b, c / 2);
return ExpoIn((t * 2) - 1, b + c / 2, c / 2);
}
/// <summary>
/// Exponential tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float ExpoOutIn(float t, float b, float c, float d)
{
if (t < d / 2)
return ExpoOut(t * 2, b, c / 2, d);
return ExpoIn((t * 2) - d, b + c / 2, c / 2, d);
}
#endregion
#region ### Circular ###
/// <summary>
/// Circular tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CircOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return c * Mathf.Sqrt(1 - (t = t - 1) * t) + b;
}
/// <summary>
/// Circular tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CircOut(float t, float b, float c, float d)
{
return c * Mathf.Sqrt(1 - (t = t / d - 1) * t) + b;
}
/// <summary>
/// Circular tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CircIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return -c * (Mathf.Sqrt(1 - t * t) - 1) + b;
}
/// <summary>
/// Circular tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CircIn(float t, float b, float c, float d)
{
return -c * (Mathf.Sqrt(1 - (t /= d) * t) - 1) + b;
}
/// <summary>
/// Circular tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CircInOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if ((t /= .5f) < 1)
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
}
/// <summary>
/// Circular tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CircInOut(float t, float b, float c, float d)
{
if ((t /= d / 2) < 1)
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
}
/// <summary>
/// Circular tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CircOutIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t < .5f)
return CircOut(t * 2, b, c / 2);
return CircIn((t * 2) - 1, b + c / 2, c / 2);
}
/// <summary>
/// Circular tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CircOutIn(float t, float b, float c, float d)
{
if (t < d / 2)
return CircOut(t * 2, b, c / 2, d);
return CircIn((t * 2) - d, b + c / 2, c / 2, d);
}
#endregion
#region ### Quadratic ###
/// <summary>
/// Quadratic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuadOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return -c * t * (t - 2) + b;
}
/// <summary>
/// Quadratic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuadOut(float t, float b, float c, float d)
{
return -c * (t /= d) * (t - 2) + b;
}
/// <summary>
/// Quadratic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuadIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return c * t * t + b;
}
/// <summary>
/// Quadratic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuadIn(float t, float b, float c, float d)
{
return c * (t /= d) * t + b;
}
/// <summary>
/// Quadratic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuadInOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if ((t /= .5f) < 1)
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
}
/// <summary>
/// Quadratic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuadInOut(float t, float b, float c, float d)
{
if ((t /= d / 2) < 1)
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
}
/// <summary>
/// Quadratic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuadOutIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t < .5f)
return QuadOut(t * 2, b, c / 2);
return QuadIn((t * 2) - 1, b + c / 2, c / 2);
}
/// <summary>
/// Quadratic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuadOutIn(float t, float b, float c, float d)
{
if (t < d / 2)
return QuadOut(t * 2, b, c / 2, d);
return QuadIn((t * 2) - d, b + c / 2, c / 2, d);
}
#endregion
#region ### Sinus ###
/// <summary>
/// Sinusoidal tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float SineOut(float t, float b, float c)
{
return c * Mathf.Sin(Mathf.Clamp01(t) * (Mathf.PI / 2)) + b;
}
/// <summary>
/// Sinusoidal tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float SineOut(float t, float b, float c, float d)
{
return c * Mathf.Sin(t / d * (Mathf.PI / 2)) + b;
}
/// <summary>
/// Sinusoidal tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float SineIn(float t, float b, float c)
{
return -c * Mathf.Cos(Mathf.Clamp01(t) * (Mathf.PI / 2)) + c + b;
}
/// <summary>
/// Sinusoidal tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float SineIn(float t, float b, float c, float d)
{
return -c * Mathf.Cos(t / d * (Mathf.PI / 2)) + c + b;
}
/// <summary>
/// Sinusoidal tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float SineInOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if ((t /= .5f) < 1)
return c / 2 * (Mathf.Sin(Mathf.PI * t / 2)) + b;
return -c / 2 * (Mathf.Cos(Mathf.PI * --t / 2) - 2) + b;
}
/// <summary>
/// Sinusoidal tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float SineInOut(float t, float b, float c, float d)
{
if ((t /= d / 2) < 1)
return c / 2 * (Mathf.Sin(Mathf.PI * t / 2)) + b;
return -c / 2 * (Mathf.Cos(Mathf.PI * --t / 2) - 2) + b;
}
/// <summary>
/// Sinusoidal tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float SineOutIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t < .5f)
return SineOut(t * 2, b, c / 2);
return SineIn((t * 2) - 1, b + c / 2, c / 2);
}
/// <summary>
/// Sinusoidal tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float SineOutIn(float t, float b, float c, float d)
{
if (t < d / 2)
return SineOut(t * 2, b, c / 2, d);
return SineIn((t * 2) - d, b + c / 2, c / 2, d);
}
#endregion
#region ### Cubic ###
/// <summary>
/// Cubic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CubicOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return c * ((t = t - 1) * t * t + 1) + b;
}
/// <summary>
/// Cubic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CubicOut(float t, float b, float c, float d)
{
return c * ((t = t / d - 1) * t * t + 1) + b;
}
/// <summary>
/// Cubic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CubicIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return c * t * t * t + b;
}
/// <summary>
/// Cubic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CubicIn(float t, float b, float c, float d)
{
return c * (t /= d) * t * t + b;
}
/// <summary>
/// Cubic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CubicInOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if ((t /= .5f) < 1)
return c / 2 * t * t * t + b;
return c / 2 * ((t -= 2) * t * t + 2) + b;
}
/// <summary>
/// Cubic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CubicInOut(float t, float b, float c, float d)
{
if ((t /= d / 2) < 1)
return c / 2 * t * t * t + b;
return c / 2 * ((t -= 2) * t * t + 2) + b;
}
/// <summary>
/// Cubic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float CubicOutIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t < .5f)
return CubicOut(t * 2, b, c / 2);
return CubicIn((t * 2) - 1, b + c / 2, c / 2);
}
/// <summary>
/// Cubic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float CubicOutIn(float t, float b, float c, float d)
{
if (t < d / 2)
return CubicOut(t * 2, b, c / 2, d);
return CubicIn((t * 2) - d, b + c / 2, c / 2, d);
}
#endregion
#region ### Quartic ###
/// <summary>
/// Quartic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuartOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return -c * ((t = t - 1) * t * t * t - 1) + b;
}
/// <summary>
/// Quartic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuartOut(float t, float b, float c, float d)
{
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
/// <summary>
/// Quartic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuartIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return c * t * t * t * t + b;
}
/// <summary>
/// Quartic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuartIn(float t, float b, float c, float d)
{
return c * (t /= d) * t * t * t + b;
}
/// <summary>
/// Quartic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuartInOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if ((t /= .5f) < 1)
return c / 2 * t * t * t * t + b;
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
/// <summary>
/// Quartic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuartInOut(float t, float b, float c, float d)
{
if ((t /= d / 2) < 1)
return c / 2 * t * t * t * t + b;
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
/// <summary>
/// Quartic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuartOutIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t < .5f)
return QuartOut(t * 2, b, c / 2);
return QuartIn((t * 2) - 1, b + c / 2, c / 2);
}
/// <summary>
/// Quartic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuartOutIn(float t, float b, float c, float d)
{
if ( t < d / 2 )
return QuartOut( t * 2, b, c / 2, d );
return QuartIn( ( t * 2 ) - d, b + c / 2, c / 2, d );
}
#endregion
#region ### Quintic ###
/// <summary>
/// Quintic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuintOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return c * ((t = t - 1) * t * t * t * t + 1) + b;
}
/// <summary>
/// Quintic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuintOut(float t, float b, float c, float d)
{
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
}
/// <summary>
/// Quintic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuintIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
return c * t * t * t * t * t + b;
}
/// <summary>
/// Quintic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuintIn(float t, float b, float c, float d)
{
return c * (t /= d) * t * t * t * t + b;
}
/// <summary>
/// Quintic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuintInOut(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if ((t /= .5f) < 1)
return c / 2 * t * t * t * t * t + b;
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
/// <summary>
/// Quintic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuintInOut(float t, float b, float c, float d)
{
if ((t /= d / 2) < 1)
return c / 2 * t * t * t * t * t + b;
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
/// <summary>
/// Quintic tween with t clamped to 0..1
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <returns>tweened value</returns>
public static float QuintOutIn(float t, float b, float c)
{
t = Mathf.Clamp01(t);
if (t < .5f)
return QuintOut(t * 2, b, c / 2);
return QuintIn((t * 2) - 1, b + c / 2, c / 2);
}
/// <summary>
/// Quintic tween
/// </summary>
/// <param name="t">Time</param>
/// <param name="b">Start value</param>
/// <param name="c">Delta value</param>
/// <param name="d">Duration</param>
/// <returns>tweened value</returns>
public static float QuintOutIn(float t, float b, float c, float d)
{
if (t < d / 2)
return QuintOut(t * 2, b, c / 2, d);
return QuintIn((t * 2) - d, b + c / 2, c / 2, d);
}
#endregion
// TODO: Add missing formulas (Elastic etc.) from here: http://wpf-animation.googlecode.com/svn/trunk/src/WPF/Animation/PennerDoubleAnimation.cs
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d57d3ecf09b87c9438cf6d0622a903fb
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,120 @@
// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using UnityEngine;
using System.Collections;
using System.Text;
using System;
using System.Reflection;
using FluffyUnderware.DevTools.Extensions;
namespace FluffyUnderware.DevTools
{
public class DTObjectDump
{
const int INDENTSPACES = 5;
string mIndent;
StringBuilder mSB;
object mObject;
public DTObjectDump(object o, int indent = 0)
{
mIndent = new string(' ', indent);
mSB = new StringBuilder();
mObject = o;
#if !NETFX_CORE
Type T = o.GetType();
FieldInfo[] fia = T.GetAllFields(false, true);
if (fia.Length > 0)
AppendHeader("Fields");
foreach (FieldInfo fi in fia)
AppendMember(fi);
PropertyInfo[] pia = T.GetAllProperties(false, true);
if (pia.Length > 0)
AppendHeader("Properties");
foreach (PropertyInfo pi in pia)
AppendMember(pi);
#endif
}
public override string ToString()
{
return mSB.ToString();
}
#if !NETFX_CORE
void AppendHeader(string name)
{
mSB.AppendLine(mIndent + "<b>---===| " + name + " |===---</b>\n");
}
void AppendMember(MemberInfo info)
{
Type type;
string typeName;
object value;
FieldInfo fi = info as FieldInfo;
if (fi != null)
{
type = fi.FieldType;
typeName = type.Name;
value = fi.GetValue(mObject);
}
else
{
PropertyInfo pi = info as PropertyInfo;
type = pi.PropertyType;
typeName = type.Name;
value = pi.GetValue(mObject, null);
}
if (value != null)
{
if (typeof(IEnumerable).IsAssignableFrom(type))
{
string tmp = mIndent;
int count = 0;
IEnumerable ie = value as IEnumerable;
if (ie != null)
{
if (type.GetEnumerableType().BaseType == typeof(ValueType))
{
foreach (object e in ie)
tmp = string.Concat(tmp, string.Format("<b>{0}</b>: {1} ", (count++).ToString(), e.ToString()));
}
else
{
if (typeof(IList).IsAssignableFrom(type))
typeName = "IList<" + type.GetEnumerableType() + ">";
tmp += "\n";
foreach (object e in ie)
tmp = string.Concat(tmp, string.Format("<b>{0}</b>: {1} ", (count++).ToString(), new DTObjectDump(e, mIndent.Length + INDENTSPACES).ToString()));
}
}
mSB.Append(mIndent);
mSB.AppendFormat("(<i>{0}</i>) <b>{1}[{2}]</b> = ", typeName, info.Name, count);
mSB.AppendLine(tmp);
}
else
{
mSB.Append(mIndent);
mSB.AppendFormat("(<i>{0}</i>) <b>{1}</b> = ", typeName, info.Name);
mSB.AppendLine(mIndent + value.ToString());
}
}
}
#endif
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 64b16d693c41e7a44979cd06b6d9e47b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,1000 @@
// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System;
using System.Text;
using FluffyUnderware.DevTools.Extensions;
using UnityEngine.Events;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using UnityEditor;
using System.Linq;
#endif
namespace FluffyUnderware.DevTools
{
public static class DTUtility
{
/// <summary>
/// The base Url for DT components' documentation
/// </summary>
public const string HelpUrlBase = "https://curvyeditor.com/doclink/";
#if UNITY_EDITOR
static MethodInfo mGetBuiltinExtraResourcesMethod;
#endif
public static Material GetDefaultMaterial()
{
#if UNITY_EDITOR
if (mGetBuiltinExtraResourcesMethod == null)
{
BindingFlags bfs = BindingFlags.NonPublic | BindingFlags.Static;
mGetBuiltinExtraResourcesMethod = typeof(UnityEditor.EditorGUIUtility).GetMethod("GetBuiltinExtraResource", bfs);
}
Material result;
if (mGetBuiltinExtraResourcesMethod == null)
{
result = null;
Debug.LogError("Couldn't find method GetBuiltinExtraResource in type UnityEditor.EditorGUIUtility");
}
else
result = (Material)mGetBuiltinExtraResourcesMethod.Invoke(null, new object[] { typeof(Material), "Default-Diffuse.mat" });
return result;
#else
return null;
#endif
}
public static bool IsEditorStateChange
{
get
{
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode && !UnityEditor.EditorApplication.isPlaying)
return true;
else
#endif
return false;
}
}
#if UNITY_EDITOR
private const string INDENT_STRING = " ";
public static string FormatJson(string str)
{
int indent = 0;
bool quoted = false;
StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < str.Length; i++)
{
char ch = str[i];
switch (ch)
{
case '{':
case '[':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case '}':
case ']':
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, --indent).ForEach(item => sb.Append(INDENT_STRING));
}
sb.Append(ch);
break;
case '"':
sb.Append(ch);
bool escaped = false;
int index = i;
while (index > 0 && str[--index] == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case ':':
sb.Append(ch);
if (!quoted)
sb.Append(" ");
break;
default:
sb.Append(ch);
break;
}
}
return sb.ToString();
}
#endif
/// <summary>
/// Much like HandleUtility.GetHandleSize(), but works for gizmos
/// </summary>
/// <param name="position"> handle position in world space</param>
public static float GetHandleSize(Vector3 position)
{
Camera camera = Camera.current;
if (camera)
{
Transform cameraTransform = camera.transform;
Vector3 localDirection;
localDirection.x = 0;
localDirection.y = 0;
localDirection.z = 1;
Vector3 cameraZDirection = cameraTransform.TransformDirection(localDirection);
localDirection.x = 1;
localDirection.y = 0;
localDirection.z = 0;
Vector3 cameraXDirection = cameraTransform.TransformDirection(localDirection);
return GetHandleSize(Gizmos.matrix.MultiplyPoint3x4(position), camera, camera.pixelWidth * 0.5f, camera.pixelHeight * 0.5f, cameraTransform.position, cameraZDirection, cameraXDirection);
}
return 20f;
}
/// <summary>
/// Much like HandleUtility.GetHandleSize(), but works for gizmos
/// </summary>
/// <param name="position"> handle position in world space</param>
/// <param name="camera"> the camera through which the object is displayed</param>
/// <param name="cameraCenterWidth"> camera.pixelWidth * 0.5f</param>
/// <param name="cameraCenterHeight"> camera.pixelHeight * 0.5f</param>
/// <param name="cameraPosition"> camera.transform.position</param>
/// <param name="cameraZDirection"> camera.transform.forward</param>
/// <param name="cameraXDirection"> camera.transform.right</param>
public static float GetHandleSize(Vector3 position, Camera camera, float cameraCenterWidth, float cameraCenterHeight, Vector3 cameraPosition, Vector3 cameraZDirection, Vector3 cameraXDirection)
{
//inlined version of Vector3.Dot(positionMinusCameraPosition, cameraZDirection)
float z = (position.x - cameraPosition.x) * cameraZDirection.x +
(position.y - cameraPosition.y) * cameraZDirection.y +
(position.z - cameraPosition.z) * cameraZDirection.z;
//OPTIM
//If you reaaaally need those extra milliseconds, use this
//return z * 0.15f;
//This will give a good enough result. The problem with this method is that the handle size is unsatisfying when transitioning between Perspective and Orthogonal view, and when the scene view window is too narrow to the point that it changes the FOV of the scene view.
Vector3 b;
{
Vector3 camPosPlusDirB;
camPosPlusDirB.x = cameraPosition.x + cameraZDirection.x * z + cameraXDirection.x;
camPosPlusDirB.y = cameraPosition.y + cameraZDirection.y * z + cameraXDirection.y;
camPosPlusDirB.z = cameraPosition.z + cameraZDirection.z * z + cameraXDirection.z;
b = camera.WorldToScreenPoint(camPosPlusDirB, Camera.MonoOrStereoscopicEye.Mono);
}
float aMinusBX = cameraCenterWidth - b.x;
float aMinusBY = cameraCenterHeight - b.y;
return 80f / (float)Math.Sqrt(aMinusBX * aMinusBX +
aMinusBY * aMinusBY);
}
public static void SetPlayerPrefs<T>(string key, T value)
{
Type tt = typeof(T);
#if NETFX_CORE
if (tt.GetTypeInfo().IsEnum)
#else
if (tt.IsEnum)
#endif
{
PlayerPrefs.SetInt(key, Convert.ToInt32(Enum.Parse(typeof(T), value.ToString()) as Enum));
}
else if (tt.IsArray)
{
throw new System.NotImplementedException();
}
else if (tt.Matches(typeof(int), typeof(Int32)))
PlayerPrefs.SetInt(key, (value as int?).Value);
else if (tt == typeof(string))
PlayerPrefs.SetString(key, (value as string));
else if (tt == typeof(float))
PlayerPrefs.SetFloat(key, (value as float?).Value);
else if (tt == typeof(bool))
PlayerPrefs.SetInt(key, ((value as bool?).Value == true) ? 1 : 0);
else if (tt == typeof(Color))
PlayerPrefs.SetString(key, (value as Color?).Value.ToHtml());
else
Debug.LogError("[DevTools.SetEditorPrefs] Unsupported datatype: " + tt.Name);
}
public static T GetPlayerPrefs<T>(string key, T defaultValue)
{
if (PlayerPrefs.HasKey(key))
{
Type tt = typeof(T);
try
{
#if NETFX_CORE
if (tt.GetTypeInfo().IsEnum || tt.Matches(typeof(int), typeof(Int32)))
#else
if (tt.IsEnum || tt.Matches(typeof(int), typeof(Int32)))
#endif
{
return (T)(object)PlayerPrefs.GetInt(key, (int)(object)defaultValue);
}
else if (tt.IsArray)
{
throw new System.NotImplementedException();
}
else if (tt == typeof(string))
return (T)(object)PlayerPrefs.GetString(key, defaultValue.ToString());
else if (tt == typeof(float))
return (T)(object)PlayerPrefs.GetFloat(key, (float)(object)defaultValue);
else if (tt == typeof(bool))
return (T)(object)(PlayerPrefs.GetInt(key, ((bool)(object)defaultValue == true) ? 1 : 0) == 1);
else if (tt == typeof(Color))
return (T)(object)PlayerPrefs.GetString(key, ((Color)(object)defaultValue).ToHtml()).ColorFromHtml();
else
Debug.LogError("[DevTools.SetEditorPrefs] Unsupported datatype: " + tt.Name);
}
catch (Exception e)
{
Debug.LogException(e);
return defaultValue;
}
}
return defaultValue;
}
public static float RandomSign()
{
return UnityEngine.Random.Range(0, 2) * 2 - 1;
}
public static string GetHelpUrl(object forClass)
{
return (forClass == null) ? string.Empty : GetHelpUrl(forClass.GetType());
}
public static string GetHelpUrl(Type classType)
{
if (classType != null)
{
#if NETFX_CORE
object[] attribs = (object[])classType.GetTypeInfo().GetCustomAttributes(typeof(HelpURLAttribute), true);
#else
object[] attribs = classType.GetCustomAttributes(typeof(HelpURLAttribute), true);
#endif
if (attribs.Length > 0)
return (((HelpURLAttribute)attribs[0]).URL);
}
return string.Empty;
}
public static Vector3 GetCenterPosition(Vector3 fallback, params Vector3[] vectors)
{
if (vectors.Length == 0)
return fallback;
Vector3 v = vectors[0];
for (int i = 1; i < vectors.Length; i++)
v += vectors[i];
return (v / vectors.Length);
}
public static T CreateGameObject<T>(Transform parent, string name) where T : MonoBehaviour
{
GameObject go = new GameObject(name);
go.transform.parent = parent;
go.transform.localPosition = Vector3.zero;
go.transform.localRotation = Quaternion.identity;
T cmp = go.AddComponent<T>();
return cmp;
}
#if UNITY_EDITOR
/// <summary>
/// Returns true if the object's prefab instance status allows for deletion
/// </summary>
/// <param name="object">The input object to test</param>
/// <param name="errorMessage"> Is set to empty string if method returns true, otherwise it is set to a message error, similar to the one display by Unity, explaining the issue.</param>
/// <see cref="PrefabInstanceStatus"/>
public static bool DoesPrefabStatusAllowDeletion(this UnityEngine.Object @object, out string errorMessage)
{
PrefabInstanceStatus prefabInstanceStatus = default;
bool isDeletionAllowed = @object is GameObject == false || DoesPrefabStatusAllowDeletion((GameObject)@object, out prefabInstanceStatus);
if (isDeletionAllowed == false)
{
errorMessage = $"You initiated an operation that leads to the deletion of the Game Object '{@object.name}', which is part of a Prefab instance.\n\n";
if (prefabInstanceStatus == PrefabInstanceStatus.MissingAsset)
errorMessage += "The Prefab asset is missing. You have to unpack the Prefab instance to be able to execute this operation";
else
errorMessage += $"Children of a Prefab instance cannot be deleted or moved, and components cannot be reordered. \n\nYou have to open the Prefab in Prefab Mode to restructure the Prefab Asset itself, or unpack the Prefab instance to remove its Prefab connection.";
}
else
{
errorMessage = string.Empty;
}
return isDeletionAllowed;
}
/// <summary>
/// Returns true if the object's prefab instance status allows for deletion
/// </summary>
/// <param name="gameObject">The input object to test</param>
/// <param name="prefabInstanceStatus"> The prefab instance status of the gameObject</param>
public static bool DoesPrefabStatusAllowDeletion(this GameObject gameObject, out PrefabInstanceStatus prefabInstanceStatus)
{
prefabInstanceStatus = PrefabUtility.GetPrefabInstanceStatus(gameObject);
return prefabInstanceStatus != PrefabInstanceStatus.Connected || PrefabUtility.IsOutermostPrefabInstanceRoot(gameObject);
}
#endif
/// <summary>
/// Returns whether the code is executed in edit mode or not
/// </summary>
public static bool IsInEditMode
{
get
{
bool result;
#if UNITY_EDITOR
result = !Application.isPlaying;
#else
result = false;
#endif
return result;
}
}
}
public static class DTTime
{
static float _EditorDeltaTime;
static float _EditorLastTime;
public static double TimeSinceStartup
{
get
{
#if UNITY_EDITOR
if (!Application.isPlaying)
return UnityEditor.EditorApplication.timeSinceStartup;
#endif
return Time.timeSinceLevelLoad;
}
}
[Obsolete("Seems to me that this is not working properly. Probably because InitializeEditorTime and UpdateEditorTime are never called. Fix this before using it")]
public static float deltaTime
{
get
{
return (Application.isPlaying) ? Time.deltaTime : _EditorDeltaTime;
}
}
public static void InitializeEditorTime()
{
_EditorLastTime = Time.realtimeSinceStartup;
_EditorDeltaTime = 0;
}
public static void UpdateEditorTime()
{
float cur = Time.realtimeSinceStartup;
float timeDelta = (cur - _EditorLastTime);
_EditorDeltaTime = timeDelta;
_EditorLastTime = cur;
/*
if (frameDelta > 20 || timeDelta > 1)
{
_EditorLastFrame = Time.frameCount;
_EditorLastTime = cur;
_EditorDeltaTime = 0;
}
else if (frameDelta > 0)
{
_EditorDeltaTime = timeDelta / frameDelta;
_EditorLastTime = cur;
_EditorLastFrame = Time.frameCount;
}*/
}
}
public class TimeMeasure : Ring<long>
{
public System.Diagnostics.Stopwatch mWatch = new System.Diagnostics.Stopwatch();
public TimeMeasure(int size) : base(size)
{ }
public void Start()
{
mWatch.Start();
}
public void Stop()
{
mWatch.Stop();
Add(mWatch.ElapsedTicks);
mWatch.Reset();
}
public void Pause()
{
mWatch.Stop();
}
public double LastTicks
{
get
{
return this[this.Count - 1];
}
}
public double LastMS
{
get
{
return LastTicks / (double)System.TimeSpan.TicksPerMillisecond;
}
}
public double AverageMS
{
get
{
long d = 0;
for (int i = 0; i < this.Count; i++)
d += this[i];
return DTMath.FixNaN((d / (double)System.TimeSpan.TicksPerMillisecond) / Count);
}
}
public double MinimumMS
{
get
{
long d = long.MaxValue;
for (int i = 0; i < this.Count; i++)
d = System.Math.Min(d, this[i]);
return DTMath.FixNaN(d / (double)System.TimeSpan.TicksPerMillisecond);
}
}
public double MaximumMS
{
get
{
long d = long.MinValue;
for (int i = 0; i < this.Count; i++)
d = System.Math.Max(d, this[i]);
return DTMath.FixNaN(d / (double)System.TimeSpan.TicksPerMillisecond);
}
}
public double AverageTicks
{
get
{
long d = 0;
for (int i = 0; i < this.Count; i++)
d += this[i];
return d / (double)Count;
}
}
public double MinimumTicks
{
get
{
long d = long.MaxValue;
for (int i = 0; i < this.Count; i++)
d = System.Math.Min(d, this[i]);
return d;
}
}
public double MaximumTicks
{
get
{
long d = 0;
for (int i = 0; i < this.Count; i++)
d = System.Math.Max(d, this[i]);
return d;
}
}
}
public static class DTMath
{
public static Vector3 ParallelTransportFrame(Vector3 up, Vector3 tan0, Vector3 tan1)
{
Vector3 A = Vector3.Cross(tan0, tan1);
if (tan0 == -tan1) //Result is undefined in this case
{
Debug.LogWarning("[DevTools] ParallelTransportFrame's result is undefined for cases where tan0 == -tan1");
}
float a = Mathf.Atan2(A.magnitude, Vector3.Dot(tan0, tan1));
return Quaternion.AngleAxis(Mathf.Rad2Deg * a, A) * up;
}
public static Vector3 LeftTan(ref Vector3 tan, ref Vector3 up)
{
return Vector3.Cross(tan, up);
}
public static Vector3 RightTan(ref Vector3 tan, ref Vector3 up)
{
return Vector3.Cross(up, tan);
}
/// <summary>
/// Much like Mathf.Repeat(), but DTMath.Repeat(v,v) returns v instead of 0
/// </summary>
public static float Repeat(float t, float length)
{
return (t == length) ? t : t - Mathf.Floor(t / length) * length;
}
public static double FixNaN(double v)
{
if (double.IsNaN(v))
v = 0;
return v;
}
public static float FixNaN(float v)
{
if (float.IsNaN(v))
v = 0;
return v;
}
public static Vector2 FixNaN(Vector2 v)
{
if (float.IsNaN(v.x))
{
v.x = 0;
}
if (float.IsNaN(v.y))
{
v.y = 0;
}
return v;
}
/// <summary>
/// Fixes NaN for Vector3
/// </summary>
/// <param name="v">a Vector3</param>
/// <returns>the "cleaned up" vector</returns>
public static Vector3 FixNaN(Vector3 v)
{
if (float.IsNaN(v.x))
{
v.x = 0;
}
if (float.IsNaN(v.y))
{
v.y = 0;
}
if (float.IsNaN(v.z))
{
v.z = 0;
}
return v;
}
/// <summary>
/// Maps a value from a source range to a destination range
/// </summary>
/// <param name="min">min destination value</param>
/// <param name="max">max destination value</param>
/// <param name="value">current source value</param>
/// <param name="vMin">min source value</param>
/// <param name="vMax">max source value</param>
/// <returns></returns>
public static float MapValue(float min, float max, float value, float vMin = -1, float vMax = 1)
{
return min + (max - min) * (value - vMin) / (vMax - vMin);
}
public static float SnapPrecision(float value, int decimals)
{
return (decimals >= 0) ? (float)System.Math.Round(value, decimals) : value;
}
public static Vector2 SnapPrecision(Vector2 value, int decimals)
{
if (decimals < 0)
return value;
value.Set(SnapPrecision(value.x, decimals), SnapPrecision(value.y, decimals));
return value;
}
public static Vector3 SnapPrecision(Vector3 value, int decimals)
{
if (decimals < 0)
return value;
value.Set(SnapPrecision(value.x, decimals), SnapPrecision(value.y, decimals), SnapPrecision(value.z, decimals));
return value;
}
/// <summary>
/// Gets the squared distance to the nearest point on a line
/// </summary>
/// <param name="l1">Line P1</param>
/// <param name="l2">Line P2</param>
/// <param name="p">a Point</param>
/// <param name="frag">fragment on the line (0..1) of the nearest point</param>
/// <returns>sqrMagnitude</returns>
public static float LinePointDistanceSqr(Vector3 l1, Vector3 l2, Vector3 p, out float frag)
{
Vector3 v = l2;
v.x -= l1.x;
v.y -= l1.y;
v.z -= l1.z;
Vector3 w = p;
w.x -= l1.x;
w.y -= l1.y;
w.z -= l1.z;
float c1 = Vector3.Dot(w, v);
if (c1 <= 0)
{
frag = 0;
return w.sqrMagnitude;
}
float c2 = Vector3.Dot(v, v);
if (c2 <= c1)
{
frag = 1;
Vector3 pMinusl2 = p;
pMinusl2.x -= l2.x;
pMinusl2.y -= l2.y;
pMinusl2.z -= l2.z;
return pMinusl2.sqrMagnitude;
}
frag = c1 / c2;
Vector3 fragMulv = v;
fragMulv.x *= frag;
fragMulv.y *= frag;
fragMulv.z *= frag;
Vector3 pb = l1;
pb.x += fragMulv.x;
pb.y += fragMulv.y;
pb.z += fragMulv.z;
Vector3 pMinuspb = p;
pMinuspb.x -= pb.x;
pMinuspb.y -= pb.y;
pMinuspb.z -= pb.z;
return pMinuspb.sqrMagnitude;
}
/// <summary>
/// Collide a ray (point + direction) against a line segment and return the hit point
/// </summary>
/// <param name="r0">Ray position</param>
/// <param name="dir">Ray direction</param>
/// <param name="l1">Line P1</param>
/// <param name="l2">Line P2</param>
/// <param name="hit">Collision Point</param>
/// <param name="frag">fragment on the line (0..1) of the collision point</param>
/// <returns>true if collision occurs</returns>
public static bool RayLineSegmentIntersection(Vector2 r0, Vector2 dir, Vector2 l1, Vector2 l2, out Vector2 hit, out float frag)
{
Vector2 s2 = l2 - l1;
float t;
frag = (-dir.y * (r0.x - l1.x) + dir.x * (r0.y - l1.y)) / (-s2.x * dir.y + dir.x * s2.y);
t = (s2.x * (r0.y - l1.y) - s2.y * (r0.x - l1.x)) / (-s2.x * dir.y + dir.x * s2.y);
if (frag >= 0 && frag <= 1 && t > 0)
{
hit = new Vector2(r0.x + (t * dir.x), r0.y + (t * dir.y));
return true;
}
hit = Vector2.zero;
return false;
}
/// <summary>
/// Calculates the intersection line segment between 2 lines (not segments).
/// </summary>
/// <returns>false if no solution can be found</returns>
public static bool ShortestIntersectionLine(Vector3 line1A, Vector3 line1B,
Vector3 line2A, Vector3 line2B, out Vector3 resultSegmentA, out Vector3 resultSegmentB)
{
// Algorithm is ported from the C algorithm of
// Paul Bourke at http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/
resultSegmentA = Vector3.zero;
resultSegmentB = Vector3.zero;
Vector3 p1 = line1A;
Vector3 p2 = line1B;
Vector3 p3 = line2A;
Vector3 p4 = line2B;
Vector3 p13 = p1 - p3;
Vector3 p43 = p4 - p3;
if (p43.sqrMagnitude < Mathf.Epsilon)
{
return false;
}
Vector3 p21 = p2 - p1;
if (p21.sqrMagnitude < Mathf.Epsilon)
{
return false;
}
double d1343 = p13.x * (double)p43.x + (double)p13.y * p43.y + (double)p13.z * p43.z;
double d4321 = p43.x * (double)p21.x + (double)p43.y * p21.y + (double)p43.z * p21.z;
double d1321 = p13.x * (double)p21.x + (double)p13.y * p21.y + (double)p13.z * p21.z;
double d4343 = p43.x * (double)p43.x + (double)p43.y * p43.y + (double)p43.z * p43.z;
double d2121 = p21.x * (double)p21.x + (double)p21.y * p21.y + (double)p21.z * p21.z;
double denom = d2121 * d4343 - d4321 * d4321;
if (System.Math.Abs(denom) < double.Epsilon)
{
return false;
}
double numer = d1343 * d4321 - d1321 * d4343;
double mua = numer / denom;
double mub = (d1343 + d4321 * (mua)) / d4343;
resultSegmentA = new Vector3((float)(p1.x + mua * p21.x), (float)(p1.y + mua * p21.y), (float)(p1.z + mua * p21.z));
resultSegmentB = new Vector3((float)(p3.x + mub * p43.x), (float)(p3.y + mub * p43.y), (float)(p3.z + mub * p43.z));
return true;
}
/// <summary>
/// Calculates the intersection between two line segments
/// </summary>
/// <returns>false if no solution can be found</returns>
public static bool LineLineIntersection(Vector3 line1A, Vector3 line1B, Vector3 line2A, Vector3 line2B, out Vector3 hitPoint)
{
Vector3 resB;
if (ShortestIntersectionLine(line1A, line1B, line2A, line2B, out hitPoint, out resB))
{
if ((resB - hitPoint).sqrMagnitude <= Mathf.Epsilon * Mathf.Epsilon)
return true;
}
return false;
}
public static bool LineLineIntersect(Vector2 line1A, Vector2 line1B, Vector2 line2A, Vector2 line2B, out Vector2 hitPoint, bool segmentOnly = true)
{
hitPoint = Vector2.zero;
// Denominator for ua and ub are the same, so store this calculation
double d =
(line2B.y - line2A.y) * (line1B.x - line1A.x)
-
(line2B.x - line2A.x) * (line1B.y - line1A.y);
//n_a and n_b are calculated as seperate values for readability
double n_a =
(line2B.x - line2A.x) * (line1A.y - line2A.y)
-
(line2B.y - line2A.y) * (line1A.x - line2A.x);
double n_b =
(line1B.x - line1A.x) * (line1A.y - line2A.y)
-
(line1B.y - line1A.y) * (line1A.x - line2A.x);
// Make sure there is not a division by zero - this also indicates that
// the lines are parallel.
// If n_a and n_b were both equal to zero the lines would be on top of each
// other (coincidental). This check is not done because it is not
// necessary for this implementation (the parallel check accounts for this).
if (d == 0)
return false;
// Calculate the intermediate fractional point that the lines potentially intersect.
double ua = n_a / d;
double ub = n_b / d;
// The fractional point will be between 0 and 1 inclusive if the lines
// intersect. If the fractional calculation is larger than 1 or smaller
// than 0 the lines would need to be longer to intersect.
if (!segmentOnly || (ua >= 0d && ua <= 1d && ub >= 0d && ub <= 1d))
{
hitPoint.Set((float)(line1A.x + (ua * (line1B.x - line1A.x))), (float)(line1A.y + (ua * (line1B.y - line1A.y))));
return true;
}
return false;
}
public static bool PointInsideTriangle(Vector3 A, Vector3 B, Vector3 C, Vector3 p, out float ac, out float ab, bool edgesAllowed)
{
Vector3 v0 = C - A;
Vector3 v1 = B - A;
Vector3 v2 = p - A;
float dot00 = Vector3.Dot(v0, v0);
float dot01 = Vector3.Dot(v0, v1);
float dot02 = Vector3.Dot(v0, v2);
float dot11 = Vector3.Dot(v1, v1);
float dot12 = Vector3.Dot(v1, v2);
float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
ac = (dot11 * dot02 - dot01 * dot12) * invDenom;
ab = (dot00 * dot12 - dot01 * dot02) * invDenom;
if (edgesAllowed)
return ac >= 0 && ab >= 0 && (ac + ab < 1);
else
return ac > 0 && ab > 0 && (ac + ab < 1);
}
}
/// <summary>
/// Extended UnityEvent
/// </summary>
public class UnityEventEx<T0> : UnityEvent<T0>
{
object mCallerList;
MethodInfo mCallsCount;
int mCount = -1;
/// <summary>
/// Removes and adds a listener, ensuring it's only bound once
/// </summary>
/// <param name="call"></param>
public void AddListenerOnce(UnityAction<T0> call)
{
base.RemoveListener(call);
base.AddListener(call);
CheckForListeners();
}
/// <summary>
/// Whether the event has any listeners at all
/// </summary>
/// <returns></returns>
public bool HasListeners()
{
if (mCallsCount == null)
{
FieldInfo fi = typeof(UnityEventBase).FieldByName("m_Calls", false, true);
if (fi != null)
{
mCallerList = fi.GetValue(this);
if (mCallerList != null)
mCallsCount = mCallerList.GetType().PropertyByName("Count").GetGetMethod();
}
}
if (mCount == -1)
{
if (mCallerList != null && mCallsCount != null)
mCount = (int)mCallsCount.Invoke(mCallerList, null); //.GetValue(mCallerList, null);
mCount += GetPersistentEventCount();
}
return (mCount > 0);
}
/// <summary>
/// Force HasListeners() to recheck for dynamically bound events
/// </summary>
public void CheckForListeners()
{
mCount = -1;
}
}
/// <summary>
/// Same functionality as various UnityEngine.Debug methods
/// </summary>
/// <remarks>Used to distinct between temporarly debug output and regular package output</remarks>
public static class DTLog
{
public static void Log(object message)
{
Debug.Log(message);
}
public static void Log(object message, UnityEngine.Object context)
{
Debug.Log(message, context);
}
public static void LogError(object message)
{
Debug.LogError(message);
}
public static void LogError(object message, UnityEngine.Object context)
{
Debug.LogError(message, context);
}
public static void LogErrorFormat(string format, params object[] args)
{
Debug.LogErrorFormat(format, args);
}
public static void LogErrorFormat(UnityEngine.Object context, string format, params object[] args)
{
Debug.LogErrorFormat(context, format, args);
}
public static void LogException(Exception exception)
{
Debug.LogException(exception);
}
public static void LogException(Exception exception, UnityEngine.Object context)
{
Debug.LogException(exception, context);
}
public static void LogFormat(string format, params object[] args)
{
Debug.LogFormat(format, args);
}
public static void LogFormat(UnityEngine.Object context, string format, params object[] args)
{
Debug.LogFormat(context, format, args);
}
public static void LogWarning(object message)
{
Debug.LogWarning(message);
}
public static void LogWarning(object message, UnityEngine.Object context)
{
Debug.LogWarning(message, context);
}
public static void LogWarningFormat(string format, params object[] args)
{
Debug.LogWarningFormat(format, args);
}
public static void LogWarningFormat(UnityEngine.Object context, string format, params object[] args)
{
Debug.LogWarningFormat(context, format, args);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d3a715a2c19f08a4ba8b136add10408f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,814 @@
// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Random = UnityEngine.Random;
namespace FluffyUnderware.DevTools
{
[System.Serializable]
public struct FloatRegion : IEquatable<FloatRegion>
{
public float From;
public float To;
public bool SimpleValue;
public FloatRegion(float value)
{
From = value;
To = value;
SimpleValue = true;
}
public FloatRegion(float A, float B)
{
this.From = A;
this.To = B;
SimpleValue = false;
}
public static FloatRegion ZeroOne
{
get { return new FloatRegion(0, 1); }
}
public void MakePositive()
{
if (To < From)
{
float temp = To;
To = From;
From = temp;
}
}
public void Clamp(float low, float high)
{
Low = Mathf.Clamp(Low, low, high);
High = Mathf.Clamp(High, low, high);
}
public bool Positive
{
get { return From <= To; }
}
public float Low
{
get { return (Positive) ? From : To; }
set
{
if (Positive)
From = value;
else
To = value;
}
}
public float High
{
get { return (Positive) ? To : From; }
set
{
if (Positive)
To = value;
else
From = value;
}
}
public float Random
{
get
{
return UnityEngine.Random.Range(From, To);
}
}
/// <summary>
/// Gets the next value in the range
/// <remarks>Depending on the value of <see cref="SimpleValue"/>, this call will or will not make the Random generator's seed progress</remarks>
/// </summary>
public float Next
{
get
{
if (SimpleValue)
return From;
else
return Random;
}
}
public float Length
{
get
{
return To - From;
}
}
public float LengthPositive
{
get { return (Positive) ? To - From : From - To; }
}
public override string ToString()
{
return string.Format("({0:F2}-{1:F2})", From, To);
}
public override int GetHashCode()
{
return From.GetHashCode() ^ To.GetHashCode() << 2;
}
public bool Equals(FloatRegion other)
{
return From.Equals(other.From) && To.Equals(other.To);
}
public override bool Equals(object other)
{
if (!(other is FloatRegion))
{
return false;
}
FloatRegion r = (FloatRegion)other;
return From.Equals(r.From) && To.Equals(r.To);
}
public static FloatRegion operator +(FloatRegion a, FloatRegion b)
{
return new FloatRegion(a.From + b.From, a.To + b.To);
}
public static FloatRegion operator -(FloatRegion a, FloatRegion b)
{
return new FloatRegion(a.From - b.From, a.To - b.To);
}
public static FloatRegion operator -(FloatRegion a)
{
return new FloatRegion(-a.From, -a.To);
}
public static FloatRegion operator *(FloatRegion a, float v)
{
return new FloatRegion(a.From * v, a.To * v);
}
public static FloatRegion operator *(float v, FloatRegion a)
{
return new FloatRegion(a.From * v, a.To * v);
}
public static FloatRegion operator /(FloatRegion a, float v)
{
return new FloatRegion(a.From / v, a.To / v);
}
public static bool operator ==(FloatRegion lhs, FloatRegion rhs)
{
return lhs.SimpleValue == rhs.SimpleValue && Mathf.Approximately(lhs.From, rhs.From) && Mathf.Approximately(lhs.To, rhs.To);
}
public static bool operator !=(FloatRegion lhs, FloatRegion rhs)
{
return lhs.SimpleValue != rhs.SimpleValue || !Mathf.Approximately(lhs.From, rhs.From) || !Mathf.Approximately(lhs.To, rhs.To);
}
}
[System.Serializable]
public struct IntRegion : IEquatable<IntRegion>
{
public int From;
public int To;
public bool SimpleValue;
public IntRegion(int value)
{
From = value;
To = value;
SimpleValue = true;
}
public IntRegion(int A, int B)
{
this.From = A;
this.To = B;
SimpleValue = false;
}
public static IntRegion ZeroOne
{
get { return new IntRegion(0, 1); }
}
public void MakePositive()
{
if (To < From)
{
int temp = To;
To = From;
From = temp;
}
}
public void Clamp(int low, int high)
{
Low = Mathf.Clamp(Low, low, high);
High = Mathf.Clamp(High, low, high);
}
public bool Positive
{
get { return From <= To; }
}
public int Low
{
get { return (Positive) ? From : To; }
set
{
if (Positive)
From = value;
else
To = value;
}
}
public int High
{
get { return (Positive) ? To : From; }
set
{
if (Positive)
To = value;
else
From = value;
}
}
public int Random
{
get
{
return UnityEngine.Random.Range(From, To);
}
}
public int Length
{
get
{
return To - From;
}
}
public int LengthPositive
{
get { return (Positive) ? To - From : From - To; }
}
public override string ToString()
{
return string.Format("({0}-{1})", From, To);
}
public override int GetHashCode()
{
return From.GetHashCode() ^ To.GetHashCode() << 2;
}
public bool Equals(IntRegion other)
{
return From.Equals(other.From) && To.Equals(other.To);
}
public override bool Equals(object other)
{
if (!(other is IntRegion))
{
return false;
}
IntRegion r = (IntRegion)other;
return From.Equals(r.From) && To.Equals(r.To);
}
public static IntRegion operator +(IntRegion a, IntRegion b)
{
return new IntRegion(a.From + b.From, a.To + b.To);
}
public static IntRegion operator -(IntRegion a, IntRegion b)
{
return new IntRegion(a.From - b.From, a.To - b.To);
}
public static IntRegion operator -(IntRegion a)
{
return new IntRegion(-a.From, -a.To);
}
public static IntRegion operator *(IntRegion a, int v)
{
return new IntRegion(a.From * v, a.To * v);
}
public static IntRegion operator *(int v, IntRegion a)
{
return new IntRegion(a.From * v, a.To * v);
}
public static IntRegion operator /(IntRegion a, int v)
{
return new IntRegion(a.From / v, a.To / v);
}
public static bool operator ==(IntRegion lhs, IntRegion rhs)
{
return lhs.From == rhs.From && lhs.To == rhs.To && lhs.SimpleValue != rhs.SimpleValue;
}
public static bool operator !=(IntRegion lhs, IntRegion rhs)
{
return lhs.From != rhs.From || lhs.To != rhs.To || lhs.SimpleValue != rhs.SimpleValue;
}
}
/// <summary>
/// Simple but effective ShuffleBag<T> implementation
/// </summary>
public class WeightedRandom<T>
{
List<T> mData;
int mCurrentPosition = -1;
T mCurrentItem;
public int Seed { get; set; }
public bool RandomizeSeed { get; set; }
private int Capacity { get { return mData.Capacity; } }
public int Size { get { return mData.Count; } }
public WeightedRandom(int initCapacity = 0)
{
mData = new List<T>(initCapacity);
}
public WeightedRandom(int initCapacity, int seed):this(initCapacity)
{
Seed = seed;
}
/// <summary>
/// Adds items to the bag
/// </summary>
/// <param name="item">the item</param>
/// <param name="amount">number of times this item should be added</param>
public void Add(T item, int amount)
{
for (int i = 0; i < amount; i++)
mData.Add(item);
mCurrentPosition = Size - 1;
}
/// <summary>
/// Gets a random item from the bag
/// </summary>
/// <returns>an item</returns>
public T Next()
{
if (mCurrentPosition < 1)
{
mCurrentPosition = Size - 1;
mCurrentItem = mData[0];
return mCurrentItem;
}
Random.State s = Random.state;
if (RandomizeSeed)
Seed = Random.Range(0, int.MaxValue);
Random.InitState(Seed);
int idx = Random.Range(0, mCurrentPosition);
Random.state = s;
mCurrentItem = mData[idx];
mData[idx] = mData[mCurrentPosition];
mData[mCurrentPosition] = mCurrentItem;
mCurrentPosition--;
return mCurrentItem;
}
/// <summary>
/// Refill the bag
/// </summary>
public void Reset()
{
mCurrentPosition = Size - 1;
}
/// <summary>
/// Clear the bag
/// </summary>
public void Clear()
{
mData.Clear();
mCurrentPosition = -1;
}
}
public class Ring<T> : IList<T>
{
List<T> mList;
public int Size { get; private set; }
int mIndex;
public Ring(int size)
{
mList = new List<T>(size);
Size = size;
}
public void Add(T item)
{
if (mList.Count == Size)
{
mList[mIndex++] = item;
if (mIndex == mList.Count)
mIndex = 0;
}
else
mList.Add(item);
}
public void Clear()
{
mList.Clear();
mIndex = 0;
}
public int IndexOf(T item)
{
return mList.IndexOf(item);
}
public void Insert(int index, T item)
{
throw new System.NotSupportedException();
}
public void RemoveAt(int index)
{
throw new System.NotSupportedException();
}
public T this[int index]
{
get
{
return mList[index];
}
set
{
mList[index] = value;
}
}
public IEnumerator GetEnumerator()
{
return mList.GetEnumerator();
}
public bool Contains(T item)
{
return mList.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
mList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return mList.Count; }
}
public bool IsReadOnly
{
get { throw new System.NotSupportedException(); }
}
public bool Remove(T item)
{
return mList.Remove(item);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
throw new System.NotImplementedException();
}
}
public class Pool<T> : IPool
{
List<T> mObjects = new List<T>();
public string Identifier { get; set; }
public PoolSettings Settings { get; protected set; }
public System.Type Type
{
get { return typeof(T); }
}
double mLastTime;
double mDeltaTime;
public Pool(PoolSettings settings = null)
{
Settings = settings ?? new PoolSettings();
Identifier = typeof(T).FullName;
mLastTime = DTTime.TimeSinceStartup + UnityEngine.Random.Range(0, Settings.Speed);
if (Settings.Prewarm)
Reset();
}
public void Update()
{
mDeltaTime += DTTime.TimeSinceStartup - mLastTime;
mLastTime = DTTime.TimeSinceStartup;
if (Settings.Speed > 0)
{
int c = (int)(mDeltaTime / Settings.Speed);
mDeltaTime -= c;
if (Count > Settings.Threshold)
{
c = Mathf.Min(c, Count - Settings.Threshold);
while (c-- > 0)
{
destroy(mObjects[0]);
mObjects.RemoveAt(0);
log("Threshold exceeded: Deleting item");
}
}
else if (Count < Settings.MinItems)
{
c = Mathf.Min(c, Settings.MinItems - Count);
while (c-- > 0)
{
mObjects.Add(create());
log("Below MinItems: Adding item");
}
}
}
else
mDeltaTime = 0;
}
public void Reset()
{
if (Application.isPlaying)
{
while (Count < Settings.MinItems)
{
mObjects.Add(create());
}
while (Count > Settings.Threshold)
{
destroy(mObjects[0]);
mObjects.RemoveAt(0);
}
log("Prewarm/Reset");
}
}
public void Clear()
{
log("Clear");
for (int i = 0; i < Count; i++)
destroy(mObjects[i]);
mObjects.Clear();
}
public int Count
{
get { return mObjects.Count; }
}
public virtual T Pop(Transform parent = null)
{
T item = default(T);
if (Count > 0)
{
item = mObjects[0];
mObjects.RemoveAt(0);
}
else
{
if (Settings.AutoCreate || !Application.isPlaying)
{
log("Auto create item");
item = create();
}
}
if (item != null)
{
sendAfterPop(item);
setParent(item, parent);
log("Pop " + item);
}
return item;
}
public virtual void Push(T item)
{
log("Push " + item);
if (Application.isPlaying && item != null)
{
sendBeforePush(item);
mObjects.Add(item);
}
}
protected virtual void sendBeforePush(T item)
{
if (item is IPoolable)
((IPoolable)item).OnBeforePush();
}
protected virtual void sendAfterPop(T item)
{
if (item is IPoolable)
((IPoolable)item).OnAfterPop();
}
protected virtual void setParent(T item, Transform parent)
{
}
protected virtual T create()
{
return System.Activator.CreateInstance<T>();
}
protected virtual void destroy(T item)
{
}
void log(string msg)
{
if (Settings.Debug)
Debug.Log(string.Format("[{0}] ({1} items) {2}", Identifier, Count, msg));
}
}
[System.Serializable]
public class PoolSettings
{
[SerializeField]
bool m_Prewarm = false;
[SerializeField]
bool m_AutoCreate = true;
[SerializeField]
bool m_AutoEnableDisable = true;
[Positive]
[SerializeField]
int m_MinItems = 0;
[Positive]
[SerializeField]
int m_Threshold = 0;
[Positive]
[SerializeField]
float m_Speed = 1;
public bool Debug;
public PoolSettings() { }
public PoolSettings(PoolSettings src)
{
Prewarm = src.Prewarm;
AutoCreate = src.AutoCreate;
MinItems = src.MinItems;
Threshold = src.Threshold;
Speed = src.Speed;
Debug = src.Debug;
}
public bool Prewarm
{
get { return m_Prewarm; }
set
{
if (m_Prewarm != value)
m_Prewarm = value;
}
}
public bool AutoCreate
{
get { return m_AutoCreate; }
set
{
if (m_AutoCreate != value)
m_AutoCreate = value;
}
}
public bool AutoEnableDisable
{
get { return m_AutoEnableDisable; }
set
{
if (m_AutoEnableDisable != value)
m_AutoEnableDisable = value;
}
}
public int MinItems
{
get { return m_MinItems; }
set
{
int v = Mathf.Max(0, value);
if (m_MinItems != v)
m_MinItems = v;
}
}
public int Threshold
{
get { return m_Threshold; }
set
{
int v = Mathf.Max(MinItems, value);
if (m_Threshold != v)
m_Threshold = v;
}
}
public float Speed
{
get { return m_Speed; }
set
{
float v = Mathf.Max(0, value);
if (m_Speed != v)
m_Speed = v;
}
}
public void OnValidate()
{
MinItems = m_MinItems;
Threshold = m_Threshold;
Speed = m_Speed;
}
}
public interface IPool
{
string Identifier { get; set; }
PoolSettings Settings { get; }
void Clear();
void Reset();
void Update();
int Count { get; }
}
public interface IPoolable
{
void OnBeforePush();
void OnAfterPop();
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 45f5314b4eeb54743a8d8494c74a292b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,293 @@
// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using UnityEngine;
using System.Collections;
using System;
using System.Reflection;
namespace FluffyUnderware.DevTools
{
public enum DTMessageType
{
None = 0,
Info = 1,
Warning = 2,
Error = 3,
}
public enum AttributeOptionsFlags : int
{
None = 0,
Compact = 1,
Clipboard = 128,
Zero = 256,
One = 512,
Negate = 1024,
Full = 1920,
FullCompact = 1921
}
public class DTPropertyAttribute : PropertyAttribute
{
public string Label;
public string Tooltip;
public string Color;
public AttributeOptionsFlags Options;
public int Precision = -1;
public DTPropertyAttribute(string label="", string tooltip = "")
{
Label = label;
Tooltip = tooltip;
}
}
public class LabelAttribute : DTPropertyAttribute
{
public LabelAttribute() : base() { }
public LabelAttribute(string label, string tooltip = "") : base(label, tooltip) { }
}
public class ToggleButtonAttribute : DTPropertyAttribute
{
public ToggleButtonAttribute(string label = "", string tooltip = "") : base(label, tooltip)
{
}
}
public class LayerAttribute : DTPropertyAttribute
{
public LayerAttribute(string label = "", string tooltip = "")
: base(label, tooltip)
{
}
}
public class TagAttribute : DTPropertyAttribute
{
public TagAttribute(string label = "", string tooltip = "")
: base(label, tooltip)
{
}
}
public class VectorExAttribute : DTPropertyAttribute
{
public VectorExAttribute(string label = "", string tooltip = "") : base(label, tooltip)
{
Options = AttributeOptionsFlags.Full;
}
}
public class AnimationCurveExAttribute : DTPropertyAttribute
{
public AnimationCurveExAttribute(string label = "", string tooltip = "")
: base(label, tooltip)
{
Options = AttributeOptionsFlags.Clipboard;
}
}
public class MinAttribute : DTPropertyAttribute
{
public float MinValue;
public string MinFieldOrPropertyName;
public MinAttribute(float value, string label = "", string tooltip = "")
: base(label, tooltip)
{
MinValue = value;
}
public MinAttribute(string fieldOrProperty, string label = "", string tooltip = "")
: base(label, tooltip)
{
MinFieldOrPropertyName = fieldOrProperty;
}
}
public class PositiveAttribute : MinAttribute
{
public PositiveAttribute() : base(0) { }
}
public class MaxAttribute : DTPropertyAttribute
{
public float MaxValue;
public string MaxFieldOrPropertyName;
public MaxAttribute(float value, string label = "", string tooltip = "")
: base(label, tooltip)
{
MaxValue = value;
}
public MaxAttribute(string fieldOrProperty, string label = "", string tooltip = "")
: base(label, tooltip)
{
MaxFieldOrPropertyName = fieldOrProperty;
}
}
public class RangeExAttribute : DTPropertyAttribute
{
public float MinValue;
public string MinFieldOrPropertyName;
public float MaxValue;
public string MaxFieldOrPropertyName;
public bool Slider=true;
public RangeExAttribute(float minValue, float maxValue, string label = "", string tooltip = "")
: base(label, tooltip)
{
MinValue = minValue;
MaxValue = maxValue;
}
public RangeExAttribute(string minFieldOrProperty, float maxValue, string label = "", string tooltip = "")
: base(label, tooltip)
{
MinFieldOrPropertyName = minFieldOrProperty;
MaxValue = maxValue;
}
public RangeExAttribute(float minValue, string maxFieldOrProperty, string label = "", string tooltip = "")
: base(label, tooltip)
{
MinValue = minValue;
MaxFieldOrPropertyName = maxFieldOrProperty;
}
public RangeExAttribute(string minFieldOrProperty, string maxFieldOrProperty, string label = "", string tooltip = "")
: base(label, tooltip)
{
MinFieldOrPropertyName = minFieldOrProperty;
MaxFieldOrPropertyName = maxFieldOrProperty;
}
}
public class MinMaxAttribute : DTPropertyAttribute
{
public readonly string MaxValueField;
public float Min;
public string MinBoundFieldOrPropertyName;
public float Max;
public string MaxBoundFieldOrPropertyName;
public MinMaxAttribute(string maxValueField, string label="", string tooltip="") : base(label, tooltip)
{
MaxValueField = maxValueField;
Min = 0;
Max = 1;
}
}
public class EnumFlagAttribute : DTPropertyAttribute
{
public EnumFlagAttribute(string label = "", string tooltip = "") : base(label,tooltip) { }
}
public class ObjectSelectorAttribute : DTPropertyAttribute
{
public ObjectSelectorAttribute(string label = "", string tooltip = "") : base(label, tooltip)
{ }
}
public class PathSelectorAttribute : DTPropertyAttribute
{
public enum DialogMode
{
OpenFile,
OpenFolder,
CreateFile
}
public readonly DialogMode Mode;
public string Title;
public string Directory;
public string Extension;
public string DefaultName;
public PathSelectorAttribute(DialogMode mode=DialogMode.OpenFile)
{
Mode = mode;
Directory = Application.dataPath;
}
}
public class EnumSelectionGridAttribute : DTPropertyAttribute
{
public EnumSelectionGridAttribute(string label="",string tooltip="") : base(label,tooltip) { }
}
public class DTRegionAttribute : DTPropertyAttribute
{
public bool RegionIsOptional;
public string RegionOptionsPropertyName;
public bool UseSlider=true;
}
public class FloatRegionAttribute : DTRegionAttribute { }
public class IntRegionAttribute : DTRegionAttribute { }
public struct RegionOptions<T>
{
public string LabelFrom;
public string LabelTo;
public string OptionalTooltip;
public DTValueClamping ClampFrom;
public DTValueClamping ClampTo;
public T FromMin;
public T FromMax;
public T ToMin;
public T ToMax;
public static RegionOptions<T> Default
{
get
{
return new RegionOptions<T>
{
OptionalTooltip = "Range",
LabelFrom= "From",
LabelTo= "To",
ClampFrom = DTValueClamping.None,
ClampTo = DTValueClamping.None
};
}
}
public static RegionOptions<T> MinMax(T min, T max)
{
return new RegionOptions<T>
{
LabelFrom = "From",
LabelTo = "To",
ClampFrom = DTValueClamping.Range,
ClampTo = DTValueClamping.Range,
FromMin=min,
FromMax=max,
ToMin=min,
ToMax=max
};
}
}
public enum DTValueClamping
{
None = 0,
Min = 1,
Max = 2,
Range = 3
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eed9fb14b94dcae4e826c61c76e7e7b2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,473 @@
// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
#if ENABLE_IL2CPP == false
using System.Linq.Expressions;
#endif
using System.Runtime.CompilerServices;
using UnityEngine.Assertions;
#if !UNITY_WSA && !UNITY_WEBGL
using System.Threading;
#endif
namespace FluffyUnderware.DevTools
{
/// <summary>
/// A class to execute actions in a multi-threaded way
/// </summary>
/// <typeparam name="T">The type of the action input</typeparam>
public class ThreadPoolWorker<T> : IDisposable
{
//TODO OPTIM Is ThreadPoolWorker still needed. Aren't all unity version handling .Net's parallel fors now?
//TODO use Windows.System.Threading.ThreadPool.RunAsync(workItem => SomeMethod()); #if NETFX_CORE
#if !UNITY_WSA && !UNITY_WEBGL
private readonly SimplePool<QueuedCallback> queuedCallbackPool = new SimplePool<QueuedCallback>(4);
private readonly SimplePool<LoopState<T>> loopStatePool = new SimplePool<LoopState<T>>(4);
private int _remainingWorkItems = 1;
private ManualResetEvent _done = new ManualResetEvent(false);
private WaitCallback handleWorkItemCallBack;
private WaitCallback handleLoopCallBack;
public ThreadPoolWorker()
{
handleWorkItemCallBack = o =>
{
QueuedCallback queuedCallback = (QueuedCallback)o;
try
{
queuedCallback.Callback(queuedCallback.State);
}
finally
{
lock (queuedCallbackPool)
queuedCallbackPool.ReleaseItem(queuedCallback);
DoneWorkItem();
}
};
handleLoopCallBack = state =>
{
LoopState<T> loopS = (LoopState<T>)state;
for (int i = loopS.StartIndex; i <= loopS.EndIndex; i++)
{
loopS.Action(loopS.Items.ElementAt(i), i, loopS.ItemsCount);
}
lock (loopStatePool)
loopStatePool.ReleaseItem(loopS);
};
}
[Obsolete("Use ParallelFor(Action<T,int,int> action, IEnumerable<T> list) instead")]
public void ParralelFor(Action<T> action, List<T> list)
{
ParallelFor((item, itemIndex, itemsCount) => action(item), list, list.Count());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list)
{
ParallelFor(action, list, list.Count());
}
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list, int elementsCount)
{
int threadsToUseCount;
{
int availableThreads;
#if NET_4_6
int temp;
ThreadPool.GetAvailableThreads(out availableThreads, out temp);
#else
availableThreads = Environment.ProcessorCount - 1;
#endif
threadsToUseCount = 1 /*main thread*/ + Math.Min(availableThreads, Environment.ProcessorCount - 1 /*keep one processor for the main thead*/);
}
//BUG a bug in iterationPerThread leads to sometimes not using all available threads. For example, if you have 5 items and 4 cores, only 3 cores will be used
int iterationPerThread = threadsToUseCount == 1
? elementsCount
: (int)Math.Ceiling((float)elementsCount / threadsToUseCount);
#if CURVY_SANITY_CHECKS
Assert.IsTrue(iterationPerThread * threadsToUseCount >= elementsCount);
#endif
int currentIndex = 0;
while (currentIndex < elementsCount)
{
int endEndex = Math.Min(currentIndex + iterationPerThread - 1, elementsCount - 1);
if (endEndex == elementsCount - 1)
for (int i = currentIndex; i <= endEndex; i++)
action(list.ElementAt(i), i, elementsCount);
else
{
QueuedCallback queuedCallback;
{
lock (queuedCallbackPool)
queuedCallback = queuedCallbackPool.GetItem();
}
LoopState<T> loopState;
{
lock (loopStatePool)
loopState = loopStatePool.GetItem();
}
loopState.Set((short)currentIndex,
(short)endEndex,
list,
elementsCount,
action);
queuedCallback.State = loopState;
queuedCallback.Callback = handleLoopCallBack;
ThrowIfDisposed();
//Debug.LogWarning("New thread " + " from "+ loopState.StartIndex + " to " + loopState.EndIndex);
lock (_done)
_remainingWorkItems++;
ThreadPool.QueueUserWorkItem(handleWorkItemCallBack, queuedCallback);
}
currentIndex = endEndex + 1;
}
WaitAll(-1, false);
}
bool WaitAll(int millisecondsTimeout, bool exitContext)
{
ThrowIfDisposed();
DoneWorkItem();
bool rv = _done.WaitOne(millisecondsTimeout, exitContext);
lock (_done)
{
if (rv)
{
_remainingWorkItems = 1;
_done.Reset();
}
else
_remainingWorkItems++;
}
return rv;
}
private void ThrowIfDisposed()
{
if (_done == null)
throw new ObjectDisposedException(GetType().Name);
}
private void DoneWorkItem()
{
lock (_done)
{
--_remainingWorkItems;
if (_remainingWorkItems == 0)
_done.Set();
}
}
public void Dispose()
{
if (_done != null)
{
((IDisposable)_done).Dispose();
_done = null;
}
}
#else
public ThreadPoolWorker()
{
}
[Obsolete("Use ParallelFor(Action<T,int,int> action, IEnumerable<T> list) instead")]
public void ParralelFor(Action<T> action, List<T> list)
{
for (var i = 0; i < list.Count; i++)
action(list[i]);
}
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list)
{
ParallelFor(action, list, list.Count());
}
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list, int elementsCount)
{
for (int i = 0; i < elementsCount; i++)
action(list.ElementAt(i), i, elementsCount);
}
public void Dispose()
{
}
#endif
}
internal class SimplePool<T> where T : new()
{
private readonly List<T> freeItemsBackfield;
#if ENABLE_IL2CPP == false
private static readonly Func<T> OptimizedInstantiator = Expression.Lambda<Func<T>>(
Expression.New(typeof(T))
).Compile();
#endif
public SimplePool(int preCreatedElementsCount)
{
freeItemsBackfield = new List<T>();
for (int i = 0; i < preCreatedElementsCount; i++)
freeItemsBackfield.Add(
#if ENABLE_IL2CPP == false
OptimizedInstantiator.Invoke()
#else
new T()
#endif
);
}
public T GetItem()
{
T item;
if (freeItemsBackfield.Count == 0)
item =
#if ENABLE_IL2CPP == false
OptimizedInstantiator.Invoke()
#else
new T()
#endif
;
else
{
int lastIndex = freeItemsBackfield.Count - 1;
item = freeItemsBackfield[lastIndex];
freeItemsBackfield.RemoveAt(lastIndex);
}
return item;
}
public void ReleaseItem(T item)
{
freeItemsBackfield.Add(item);
}
}
#if !UNITY_WSA && !UNITY_WEBGL
class QueuedCallback
{
public WaitCallback Callback;
public object State;
}
class LoopState<T>
{
public short StartIndex { get; private set; }
public short EndIndex { get; private set; }
public IEnumerable<T> Items { get; private set; }
public int ItemsCount { get; private set; }
public Action<T, int, int> Action { get; private set; }
public LoopState()
{
}
public LoopState(short startIndex, short endIndex, IEnumerable<T> items, int itemsCount, Action<T, int, int> action)
{
Set(startIndex, endIndex, items, itemsCount, action);
}
public void Set(short startIndex, short endIndex, IEnumerable<T> items, int itemsCount, Action<T, int, int> action)
{
StartIndex = startIndex;
EndIndex = endIndex;
Items = items;
ItemsCount = itemsCount;
Action = action;
}
}
#endif
/// <summary>
/// This class is not very optimized. For better performance, use the generic version of ThreadPoolWorker instead
/// </summary>
#if !UNITY_WSA && !UNITY_WEBGL
[Obsolete("Use ThreadPoolWorker<T> instead")]
public class ThreadPoolWorker : IDisposable
{
private int _remainingWorkItems = 1;
private ManualResetEvent _done = new ManualResetEvent(false);
public void QueueWorkItem(WaitCallback callback)
{
QueueWorkItem(callback, null);
}
public void QueueWorkItem(Action act)
{
QueueWorkItem(act, null);
}
public void ParralelFor<T>(Action<T> action, List<T> list)
{
int threadsToUseCount;
{
int availableThreads;
#if NET_4_6
int temp;
ThreadPool.GetAvailableThreads(out availableThreads, out temp);
#else
availableThreads = Environment.ProcessorCount - 1;
#endif
threadsToUseCount = 1 /*main thread*/ + Math.Min(availableThreads, Environment.ProcessorCount - 1 /*keep one processor for the main thead*/);
}
int iterationsCount = list.Count;
if (threadsToUseCount == 1 || iterationsCount == 1)
{
for (int i = 0; i < iterationsCount; i++)
{
action(list[i]);
}
}
else
{
int iterationPerThread = (int)Math.Ceiling((float)iterationsCount / threadsToUseCount);
int currentIndex = 0;
while (currentIndex < iterationsCount)
{
QueuedCallback queuedCallback = new QueuedCallback();
int endEndex = Math.Min(currentIndex + iterationPerThread, iterationsCount - 1);
LoopState<T> loopState = new LoopState<T>((short)currentIndex,
(short)endEndex,
list,
iterationsCount,
(item,
itemIndex,
itemsCount) => action(item));
queuedCallback.State = loopState;
queuedCallback.Callback = state =>
{
LoopState<T> loopS = (LoopState<T>)state;
for (int i = loopS.StartIndex; i <= loopS.EndIndex; i++)
{
loopS.Action(loopS.Items.ElementAt(i), i, iterationsCount);
}
};
QueueWorkItem(queuedCallback);
currentIndex = endEndex + 1;
}
}
}
private void QueueWorkItem(QueuedCallback callback)
{
ThrowIfDisposed();
lock (_done)
_remainingWorkItems++;
ThreadPool.QueueUserWorkItem(new WaitCallback(HandleWorkItem), callback);
}
public void QueueWorkItem(WaitCallback callback, object state)
{
QueuedCallback qc = new QueuedCallback();
qc.Callback = callback;
qc.State = state;
QueueWorkItem(qc);
}
public void QueueWorkItem(Action act, object state)
{
QueuedCallback qc = new QueuedCallback();
qc.Callback = (x => act());
qc.State = state;
QueueWorkItem(qc);
}
public bool WaitAll()
{
return WaitAll(-1, false);
}
public bool WaitAll(TimeSpan timeout, bool exitContext)
{
return WaitAll((int)timeout.TotalMilliseconds, exitContext);
}
public bool WaitAll(int millisecondsTimeout, bool exitContext)
{
ThrowIfDisposed();
DoneWorkItem();
bool rv = _done.WaitOne(millisecondsTimeout, exitContext);
lock (_done)
{
if (rv)
{
_remainingWorkItems = 1;
_done.Reset();
}
else
_remainingWorkItems++;
}
return rv;
}
private void HandleWorkItem(object state)
{
QueuedCallback qc = (QueuedCallback)state;
try
{
qc.Callback(qc.State);
}
finally
{
DoneWorkItem();
}
}
private void DoneWorkItem()
{
lock (_done)
{
--_remainingWorkItems;
if (_remainingWorkItems == 0)
_done.Set();
}
}
private void ThrowIfDisposed()
{
if (_done == null)
throw new ObjectDisposedException(GetType().Name);
}
public void Dispose()
{
if (_done != null)
{
((IDisposable)_done).Dispose();
_done = null;
}
}
}
#else
public class ThreadPoolWorker{}
#endif
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a997f755ef82a3e439a530a09753ae2f
timeCreated: 1433497656
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: