您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

612 行
16 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEditorInternal;
using Object = UnityEngine.Object;
//#pragma warning disable 0414
//#pragma warning disable 0219
namespace UnityEditor
{
namespace Experimental
{
namespace Graph
{
class TypeAdapter : Attribute
{
}
public enum Direction
{
eInput = 0,
eOutput = 1,
eBidirectional = 2
};
public interface IConnect
{
Direction GetDirection();
void Highlight(bool highlighted);
void RenderOverlay(Canvas2D canvas);
object Source();
Vector3 ConnectPosition();
void OnConnect(IConnect other);
};
public class NodeAdapter
{
private static List<MethodInfo> m_TypeAdapters = null;
private static Dictionary<int, System.Reflection.MethodInfo> m_NodeAdapterDictionary;
public bool CanAdapt(object a, object b)
{
if (a == b)
return false; // self connections are not permitted
if (a == null || b == null)
return false;
MethodInfo mi = GetAdapter(a, b);
if (mi == null)
{
Debug.Log("adapter node not found for: " + a.GetType().ToString() + " -> " + b.GetType().ToString());
}
return mi != null ? true : false;
}
public bool Connect(object a, object b)
{
MethodInfo mi = GetAdapter(a, b);
if (mi == null)
{
Debug.LogError("Attempt to connect 2 unadaptable types: " + a.GetType().ToString() + " -> " + b.GetType().ToString());
return false;
}
object retVal = mi.Invoke(this, new object[] { this, a, b });
return (bool)retVal;
}
IEnumerable<MethodInfo> GetExtensionMethods(Assembly assembly, Type extendedType)
{
var query = from type in assembly.GetTypes()
where type.IsSealed && !type.IsGenericType && !type.IsNested
from method in type.GetMethods(BindingFlags.Static
| BindingFlags.Public | BindingFlags.NonPublic)
where method.IsDefined(typeof(ExtensionAttribute), false)
where method.GetParameters()[0].ParameterType == extendedType
select method;
return query;
}
public MethodInfo GetAdapter(object a, object b)
{
if (a == null || b == null)
return null;
if (m_NodeAdapterDictionary == null)
{
m_NodeAdapterDictionary = new Dictionary<int, System.Reflection.MethodInfo>();
// add extension methods
AppDomain currentDomain = AppDomain.CurrentDomain;
foreach (System.Reflection.Assembly assembly in currentDomain.GetAssemblies())
{
foreach (MethodInfo method in GetExtensionMethods(assembly, typeof(NodeAdapter)))
{
System.Reflection.ParameterInfo[] methodParams = method.GetParameters();
if (methodParams.Count() == 3)
{
string pa = methodParams[1].ParameterType.ToString() + methodParams[2].ParameterType.ToString();
m_NodeAdapterDictionary.Add(pa.GetHashCode(), method);
}
}
}
}
string s = a.GetType().ToString() + b.GetType().ToString();
try
{
return m_NodeAdapterDictionary[s.GetHashCode()];
}
catch (Exception)
{
}
return null;
}
public MethodInfo GetTypeAdapter(Type from, Type to)
{
if (m_TypeAdapters == null)
{
m_TypeAdapters = new List<MethodInfo>();
AppDomain currentDomain = AppDomain.CurrentDomain;
foreach (System.Reflection.Assembly assembly in currentDomain.GetAssemblies())
{
try
{
foreach (Type temptype in assembly.GetTypes())
{
MethodInfo[] methodInfos = temptype.GetMethods(BindingFlags.Public | BindingFlags.Static);
foreach (MethodInfo i in methodInfos)
{
object[] allAttrs = i.GetCustomAttributes(typeof(TypeAdapter), false);
if (allAttrs.Count() > 0)
{
m_TypeAdapters.Add(i);
}
}
}
}
catch (Exception ex)
{
Debug.Log(ex);
}
}
}
foreach (MethodInfo i in m_TypeAdapters)
{
if (i.ReturnType == to)
{
ParameterInfo[] allParams = i.GetParameters();
if (allParams.Count() == 1)
{
if (allParams[0].ParameterType == from)
return i;
}
}
}
return null;
}
};
internal class EdgeConnector<T> : IManipulate where T : IConnect
{
private static Color s_EdgeColor = new Color(1.0f, 1.0f, 1.0f, 0.8f);
private static Color s_ActiveEdgeColor = new Color(0.2f, 0.4f, 1.0f, 0.8f);
private Vector2 m_Start = Vector2.zero;
private Vector2 m_End = Vector2.zero;
private Color m_Color = s_EdgeColor;
private IConnect m_SnappedTarget = null;
private IConnect m_SnappedSource = null;
List<IConnect> m_CompatibleAnchors = new List<IConnect>();
public EdgeConnector()
{
}
public bool GetCaps(ManipulatorCapability cap)
{
return false;
}
public void AttachTo(CanvasElement element)
{
element.MouseUp += EndDrag;
element.MouseDown += StartDrag;
element.MouseDrag += MouseDrag;
}
private bool StartDrag(CanvasElement element, Event e, Canvas2D canvas)
{
if (e.type == EventType.Used)
{
return false;
}
if (e.button != 0)
{
return false;
}
element.OnWidget += DrawEdge;
IConnect cnx = element as IConnect;
if (element.collapsed)
return false;
canvas.StartCapture(this, element);
m_Start = m_End = element.canvasBoundingRect.center;
e.Use();
if (cnx != null)
{
cnx.Highlight(true);
}
EndSnap();
// find compatible anchors
m_CompatibleAnchors.Clear();
Rect screenRect = new Rect();
screenRect.min = canvas.MouseToCanvas(new Vector2(0.0f, 0.0f));
screenRect.max = canvas.MouseToCanvas(new Vector2(Screen.width, Screen.height));
CanvasElement[] visibleAnchors = canvas.Pick<T>(screenRect);
NodeAdapter nodeAdapter = new NodeAdapter();
foreach (CanvasElement anchor in visibleAnchors)
{
IConnect toCnx = anchor as IConnect;
if (toCnx == null)
continue;
bool isBidirectional = ((cnx.GetDirection() == Direction.eBidirectional) ||
(toCnx.GetDirection() == Direction.eBidirectional));
if (cnx.GetDirection() != toCnx.GetDirection() || isBidirectional)
{
if (nodeAdapter.GetAdapter(cnx.Source(), toCnx.Source()) != null)
{
m_CompatibleAnchors.Add(toCnx);
}
}
}
canvas.OnOverlay += HighlightCompatibleAnchors;
return true;
}
private bool EndDrag(CanvasElement element, Event e, Canvas2D canvas)
{
if (e.type == EventType.Used)
return false;
if (!canvas.IsCaptured(this))
{
return false;
}
element.OnWidget -= DrawEdge;
canvas.EndCapture();
IConnect cnx = element as IConnect;
if (cnx != null)
{
cnx.Highlight(false);
}
if (m_SnappedSource == null && m_SnappedTarget == null)
{
cnx.OnConnect(null);
}
else if (m_SnappedSource != null && m_SnappedTarget != null)
{
NodeAdapter nodeAdapter = new NodeAdapter();
if (nodeAdapter.CanAdapt(m_SnappedSource.Source(), m_SnappedTarget.Source()))
{
nodeAdapter.Connect(m_SnappedSource.Source(), m_SnappedTarget.Source());
cnx.OnConnect(m_SnappedTarget);
}
}
EndSnap();
e.Use();
canvas.OnOverlay -= HighlightCompatibleAnchors;
return true;
}
private bool MouseDrag(CanvasElement element, Event e, Canvas2D canvas)
{
if (e.type == EventType.Used)
{
return false;
}
if (!canvas.IsCaptured(this))
{
return false;
}
m_End = canvas.MouseToCanvas(e.mousePosition);
e.Use();
m_Color = s_EdgeColor;
IConnect thisCnx = (element as IConnect);
// find target anchor under us
CanvasElement elementUnderMouse = canvas.PickSingle<T>(e.mousePosition);
if (elementUnderMouse != null)
{
IConnect cnx = elementUnderMouse as IConnect;
if (cnx == null)
{
Debug.LogError("PickSingle returned an incompatible element: does not support IConnect interface");
return true;
}
if (m_CompatibleAnchors.Exists(ic => ic == cnx))
{
StartSnap(thisCnx, cnx);
m_Color = s_ActiveEdgeColor;
}
}
else
{
EndSnap();
}
return true;
}
private void StartSnap(IConnect from, IConnect to)
{
EndSnap();
m_SnappedTarget = to;
m_SnappedSource = from;
m_SnappedTarget.Highlight(true);
}
private void EndSnap()
{
if (m_SnappedTarget != null)
{
m_SnappedTarget.Highlight(false);
m_SnappedTarget = null;
}
}
private bool DrawEdge(CanvasElement element, Event e, Canvas2D canvas)
{
if (!canvas.IsCaptured(this))
{
return false;
}
bool invert = false;
if (m_End.x < m_Start.x)
invert = true;
Vector3[] points, tangents;
GetTangents(invert ? m_End : m_Start, invert ? m_Start : m_End, out points, out tangents);
Handles.DrawBezier(points[0], points[1], tangents[0], tangents[1], m_Color, null, 5f);
// little widget on the middle of the edge
Vector3[] allPoints = Handles.MakeBezierPoints(points[0], points[1], tangents[0], tangents[1], 20);
Color oldColor = Handles.color;
Handles.color = m_Color;
Handles.DrawSolidDisc(allPoints[10], new Vector3(0.0f, 0.0f, -1.0f), 6f);
Handles.color = oldColor;
return true;
}
private bool HighlightCompatibleAnchors(CanvasElement element, Event e, Canvas2D canvas)
{
foreach (IConnect visible in m_CompatibleAnchors)
{
visible.RenderOverlay(canvas);
}
return false;
}
public static void GetTangents(Vector2 start, Vector2 end, out Vector3[] points, out Vector3[] tangents)
{
points = new Vector3[] { start, end };
tangents = new Vector3[2];
const float minTangent = 30;
float weight = (start.y < end.y) ? .3f : .7f;
weight = .5f;
float weight2 = 1 - weight;
float y = 0;
if (start.x > end.x)
{
weight2 = weight = -.25f;
float aspect = (start.x - end.x) / (start.y - end.y);
if (Mathf.Abs(aspect) > .5f)
{
float asp = (Mathf.Abs(aspect) - .5f) / 8;
asp = Mathf.Sqrt(asp);
y = Mathf.Min(asp * 80, 80);
if (start.y > end.y)
y = -y;
}
}
float cleverness = Mathf.Clamp01(((start - end).magnitude - 10) / 50);
tangents[0] = start + new Vector2((end.x - start.x) * weight + minTangent, y) * cleverness;
tangents[1] = end + new Vector2((end.x - start.x) * -weight2 - minTangent, -y) * cleverness;
}
};
internal class Edge<T> : CanvasElement where T : CanvasElement, IConnect
{
private T m_Left = null;
private T m_Right = null;
private ICanvasDataSource m_Data;
public Edge(ICanvasDataSource data, T left, T right)
{
m_Data = data;
zIndex = 9999;
m_SupportsRenderToTexture = false;
left.AddDependency(this);
right.AddDependency(this);
m_Left = left;
m_Right = right;
UpdateModel(UpdateType.eUpdate);
KeyDown += OnDeleteEdge;
}
public T Left
{
get { return m_Left; }
}
public T Right
{
get { return m_Right; }
}
private bool OnDeleteEdge(CanvasElement element, Event e, Canvas2D canvas)
{
if (e.type == EventType.Used)
return false;
if (e.keyCode == KeyCode.Delete)
{
m_Data.DeleteElement(this);
return true;
}
return false;
}
public override bool Intersects(Rect rect)
{
// first check coarse bounding box
if (!base.Intersects(rect))
return false;
// bounding box check succeeded, do more fine grained check by checking intersection between the rectangles' diagonal
// and the line segments
Vector3 from = m_Left.ConnectPosition();
Vector3 to = m_Right.ConnectPosition();
if (to.x < from.x)
{
Vector3 t = from;
from = to;
to = t;
}
Vector3[] points, tangents;
EdgeConnector<T>.GetTangents(from, to, out points, out tangents);
Vector3[] allPoints = Handles.MakeBezierPoints(points[0], points[1], tangents[0], tangents[1], 20);
for (int a = 0; a < allPoints.Length; a++)
{
if (a >= allPoints.Length - 1)
{
break;
}
Vector2 segmentA = new Vector2(allPoints[a].x, allPoints[a].y);
Vector2 segmentB = new Vector2(allPoints[a + 1].x, allPoints[a + 1].y);
if (RectUtils.IntersectsSegment(rect, segmentA, segmentB))
return true;
}
return false;
}
public override bool Contains(Vector2 canvasPosition)
{
// first check coarse bounding box
if (!base.Contains(canvasPosition))
return false;
// bounding box check succeeded, do more fine grained check by measuring distance to bezier points
Vector3 from = m_Left.ConnectPosition();
Vector3 to = m_Right.ConnectPosition();
if (to.x < from.x)
{
Vector3 t = from;
from = to;
to = t;
}
Vector3[] points, tangents;
EdgeConnector<T>.GetTangents(from, to, out points, out tangents);
Vector3[] allPoints = Handles.MakeBezierPoints(points[0], points[1], tangents[0], tangents[1], 20);
float minDistance = Mathf.Infinity;
foreach (Vector3 currentPoint in allPoints)
{
float distance = Vector3.Distance(currentPoint, canvasPosition);
minDistance = Mathf.Min(minDistance, distance);
if (minDistance < 15.0f)
{
return true;
}
}
return false;
}
public override void Render(Rect parentRect, Canvas2D canvas)
{
Color edgeColor = selected ? Color.yellow : Color.white;
Vector3 from = m_Left.ConnectPosition();
Vector3 to = m_Right.ConnectPosition();
if (to.x < from.x)
{
Vector3 t = from;
from = to;
to = t;
}
Vector3[] points, tangents;
EdgeConnector<T>.GetTangents(from, to, out points, out tangents);
Handles.DrawBezier(points[0], points[1], tangents[0], tangents[1], edgeColor, null, 5f);
// little widget on the middle of the edge
Vector3[] allPoints = Handles.MakeBezierPoints(points[0], points[1], tangents[0], tangents[1], 20);
Color oldColor = Handles.color;
Handles.color = Color.blue;
Handles.DrawSolidDisc(allPoints[10], new Vector3(0.0f, 0.0f, -1.0f), 6f);
Handles.color = edgeColor;
Handles.DrawWireDisc(allPoints[10], new Vector3(0.0f, 0.0f, -1.0f), 6f);
Handles.DrawWireDisc(allPoints[10], new Vector3(0.0f, 0.0f, -1.0f), 5f);
// dot on top of anchor showing it's connected
Handles.color = new Color(0.3f, 0.4f, 1.0f, 1.0f);
Handles.DrawSolidDisc(from, new Vector3(0.0f, 0.0f, -1.0f), 4f);
Handles.DrawSolidDisc(to, new Vector3(0.0f, 0.0f, -1.0f), 4f);
/*if (EditorApplication.isPlaying)
{
Handles.color = Color.red;
Handles.DrawSolidDisc(allPoints[m_RealtimeFeedbackPointIndex], new Vector3(0.0f, 0.0f, -1.0f), 6f);
m_RealtimeFeedbackPointIndex++;
if (m_RealtimeFeedbackPointIndex >= 20)
{
m_RealtimeFeedbackPointIndex = 0;
}
}*/
Handles.color = oldColor;
}
public override void UpdateModel(UpdateType t)
{
Vector3 from = m_Left.ConnectPosition();
Vector3 to = m_Right.ConnectPosition();
Rect r = new Rect();
r.min = new Vector2(Math.Min(from.x, to.x), Math.Min(from.y, to.y));
r.max = new Vector2(Math.Max(from.x, to.x), Math.Max(from.y, to.y));
translation = r.min;
scale = new Vector3(r.width, r.height, 1.0f);
base.UpdateModel(t);
}
}
}
}
}