Initial commit

This commit is contained in:
AbstractConcept 2022-09-13 00:36:34 -05:00
commit 3c7cc0c973
8391 changed files with 704313 additions and 0 deletions

View file

@ -0,0 +1,223 @@
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public static class BezierUtility
{
static Vector3[] s_TempPoints = new Vector3[3];
public static Vector3 BezierPoint(Vector3 startPosition, Vector3 startTangent, Vector3 endTangent, Vector3 endPosition, float t)
{
float s = 1.0f - t;
return startPosition * s * s * s + startTangent * s * s * t * 3.0f + endTangent * s * t * t * 3.0f + endPosition * t * t * t;
}
public static Vector3 ClosestPointOnCurve(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, out float t)
{
Vector3 startToEnd = endPosition - startPosition;
Vector3 startToTangent = (startTangent - startPosition);
Vector3 endToTangent = (endTangent - endPosition);
float sqrError = 0.001f;
if (Colinear(startToTangent, startToEnd, sqrError) && Colinear(endToTangent, startToEnd, sqrError))
return ClosestPointToSegment(point, startPosition, endPosition, out t);
Vector3 leftStartPosition;
Vector3 leftEndPosition;
Vector3 leftStartTangent;
Vector3 leftEndTangent;
Vector3 rightStartPosition;
Vector3 rightEndPosition;
Vector3 rightStartTangent;
Vector3 rightEndTangent;
float leftStartT = 0f;
float leftEndT = 0.5f;
float rightStartT = 0.5f;
float rightEndT = 1f;
SplitBezier(0.5f, startPosition, endPosition, startTangent, endTangent,
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
Vector3 pointLeft = ClosestPointOnCurveIterative(point, leftStartPosition, leftEndPosition, leftStartTangent, leftEndTangent, sqrError, ref leftStartT, ref leftEndT);
Vector3 pointRight = ClosestPointOnCurveIterative(point, rightStartPosition, rightEndPosition, rightStartTangent, rightEndTangent, sqrError, ref rightStartT, ref rightEndT);
if ((point - pointLeft).sqrMagnitude < (point - pointRight).sqrMagnitude)
{
t = leftStartT;
return pointLeft;
}
t = rightStartT;
return pointRight;
}
public static Vector3 ClosestPointOnCurveFast(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, out float t)
{
float sqrError = 0.001f;
float startT = 0f;
float endT = 1f;
Vector3 closestPoint = ClosestPointOnCurveIterative(point, startPosition, endPosition, startTangent, endTangent, sqrError, ref startT, ref endT);
t = startT;
return closestPoint;
}
private static Vector3 ClosestPointOnCurveIterative(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, float sqrError, ref float startT, ref float endT)
{
while ((startPosition - endPosition).sqrMagnitude > sqrError)
{
Vector3 startToEnd = endPosition - startPosition;
Vector3 startToTangent = (startTangent - startPosition);
Vector3 endToTangent = (endTangent - endPosition);
if (Colinear(startToTangent, startToEnd, sqrError) && Colinear(endToTangent, startToEnd, sqrError))
{
float t;
Vector3 closestPoint = ClosestPointToSegment(point, startPosition, endPosition, out t);
t *= (endT - startT);
startT += t;
endT -= t;
return closestPoint;
}
Vector3 leftStartPosition;
Vector3 leftEndPosition;
Vector3 leftStartTangent;
Vector3 leftEndTangent;
Vector3 rightStartPosition;
Vector3 rightEndPosition;
Vector3 rightStartTangent;
Vector3 rightEndTangent;
SplitBezier(0.5f, startPosition, endPosition, startTangent, endTangent,
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
s_TempPoints[0] = leftStartPosition;
s_TempPoints[1] = leftStartTangent;
s_TempPoints[2] = leftEndTangent;
float sqrDistanceLeft = SqrDistanceToPolyLine(point, s_TempPoints);
s_TempPoints[0] = rightEndPosition;
s_TempPoints[1] = rightEndTangent;
s_TempPoints[2] = rightStartTangent;
float sqrDistanceRight = SqrDistanceToPolyLine(point, s_TempPoints);
if (sqrDistanceLeft < sqrDistanceRight)
{
startPosition = leftStartPosition;
endPosition = leftEndPosition;
startTangent = leftStartTangent;
endTangent = leftEndTangent;
endT -= (endT - startT) * 0.5f;
}
else
{
startPosition = rightStartPosition;
endPosition = rightEndPosition;
startTangent = rightStartTangent;
endTangent = rightEndTangent;
startT += (endT - startT) * 0.5f;
}
}
return endPosition;
}
public static void SplitBezier(float t, Vector3 startPosition, Vector3 endPosition, Vector3 startRightTangent, Vector3 endLeftTangent,
out Vector3 leftStartPosition, out Vector3 leftEndPosition, out Vector3 leftStartTangent, out Vector3 leftEndTangent,
out Vector3 rightStartPosition, out Vector3 rightEndPosition, out Vector3 rightStartTangent, out Vector3 rightEndTangent)
{
Vector3 tangent0 = (startRightTangent - startPosition);
Vector3 tangent1 = (endLeftTangent - endPosition);
Vector3 tangentEdge = (endLeftTangent - startRightTangent);
Vector3 tangentPoint0 = startPosition + tangent0 * t;
Vector3 tangentPoint1 = endPosition + tangent1 * (1f - t);
Vector3 tangentEdgePoint = startRightTangent + tangentEdge * t;
Vector3 newTangent0 = tangentPoint0 + (tangentEdgePoint - tangentPoint0) * t;
Vector3 newTangent1 = tangentPoint1 + (tangentEdgePoint - tangentPoint1) * (1f - t);
Vector3 newTangentEdge = newTangent1 - newTangent0;
Vector3 bezierPoint = newTangent0 + newTangentEdge * t;
leftStartPosition = startPosition;
leftEndPosition = bezierPoint;
leftStartTangent = tangentPoint0;
leftEndTangent = newTangent0;
rightStartPosition = bezierPoint;
rightEndPosition = endPosition;
rightStartTangent = newTangent1;
rightEndTangent = tangentPoint1;
}
private static Vector3 ClosestPointToSegment(Vector3 point, Vector3 segmentStart, Vector3 segmentEnd, out float t)
{
Vector3 relativePoint = point - segmentStart;
Vector3 segment = (segmentEnd - segmentStart);
Vector3 segmentDirection = segment.normalized;
float length = segment.magnitude;
float dot = Vector3.Dot(relativePoint, segmentDirection);
if (dot <= 0f)
dot = 0f;
else if (dot >= length)
dot = length;
t = dot / length;
return segmentStart + segment * t;
}
private static float SqrDistanceToPolyLine(Vector3 point, Vector3[] points)
{
float minDistance = float.MaxValue;
for (int i = 0; i < points.Length - 1; ++i)
{
float distance = SqrDistanceToSegment(point, points[i], points[i + 1]);
if (distance < minDistance)
minDistance = distance;
}
return minDistance;
}
private static float SqrDistanceToSegment(Vector3 point, Vector3 segmentStart, Vector3 segmentEnd)
{
Vector3 relativePoint = point - segmentStart;
Vector3 segment = (segmentEnd - segmentStart);
Vector3 segmentDirection = segment.normalized;
float length = segment.magnitude;
float dot = Vector3.Dot(relativePoint, segmentDirection);
if (dot <= 0f)
return (point - segmentStart).sqrMagnitude;
else if (dot >= length)
return (point - segmentEnd).sqrMagnitude;
return Vector3.Cross(relativePoint, segmentDirection).sqrMagnitude;
}
private static bool Colinear(Vector3 v1, Vector3 v2, float error = 0.0001f)
{
return Mathf.Abs(v1.x * v2.y - v1.y * v2.x + v1.x * v2.z - v1.z * v2.x + v1.y * v2.z - v1.z * v2.y) < error;
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 7539b6de552451b409d8d2f8657251c6
timeCreated: 1505799796
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,73 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public enum TangentMode
{
Linear = 0,
Continuous = 1,
Broken = 2
}
[Serializable]
public struct TangentCache
{
public Vector3 leftTangent;
public Vector3 rightTangent;
}
[Serializable]
public struct ControlPoint
{
public Vector3 position;
public Vector3 localLeftTangent;
public Vector3 localRightTangent;
public TangentMode tangentMode;
public TangentCache continuousCache;
public TangentCache brokenCache;
public bool mirrorLeft;
public Vector3 leftTangent
{
get { return localLeftTangent + position; }
set { localLeftTangent = value - position; }
}
public Vector3 rightTangent
{
get { return localRightTangent + position; }
set { localRightTangent = value - position; }
}
public void StoreTangents()
{
if (tangentMode == TangentMode.Continuous)
{
continuousCache.leftTangent = localLeftTangent;
continuousCache.rightTangent = localRightTangent;
}
else if (tangentMode == TangentMode.Broken)
{
brokenCache.leftTangent = localLeftTangent;
brokenCache.rightTangent = localRightTangent;
}
}
public void RestoreTangents()
{
if (tangentMode == TangentMode.Continuous)
{
localLeftTangent = continuousCache.leftTangent;
localRightTangent = continuousCache.rightTangent;
}
else if (tangentMode == TangentMode.Broken)
{
localLeftTangent = brokenCache.leftTangent;
localRightTangent = brokenCache.rightTangent;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e22e7e2cce9e9346a8291ccf224487a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Path
{
[Serializable]
public class EditablePath : IEditablePath
{
[SerializeField]
private ShapeType m_ShapeType;
[SerializeField]
private IndexedSelection m_Selection = new IndexedSelection();
[SerializeField]
private List<ControlPoint> m_ControlPoints = new List<ControlPoint>();
[SerializeField]
private bool m_IsOpenEnded;
private Matrix4x4 m_LocalToWorldMatrix = Matrix4x4.identity;
private Matrix4x4 m_WorldToLocalMatrix = Matrix4x4.identity;
private Vector3 m_Forward = Vector3.forward;
private Vector3 m_Up = Vector3.up;
private Vector3 m_Right = Vector3.right;
public ShapeType shapeType
{
get { return m_ShapeType; }
set { m_ShapeType = value; }
}
public IUndoObject undoObject { get; set; }
public Matrix4x4 localToWorldMatrix
{
get { return m_LocalToWorldMatrix; }
set
{
m_LocalToWorldMatrix = value;
m_WorldToLocalMatrix = value.inverse;
}
}
public Vector3 forward
{
get { return m_Forward; }
set { m_Forward = value; }
}
public Vector3 up
{
get { return m_Up; }
set { m_Up = value; }
}
public Vector3 right
{
get { return m_Right; }
set { m_Right = value; }
}
public Matrix4x4 worldToLocalMatrix
{
get { return m_WorldToLocalMatrix; }
}
public bool isOpenEnded
{
get
{
if (pointCount < 3)
return true;
return m_IsOpenEnded;
}
set { m_IsOpenEnded = value; }
}
public ISelection<int> selection
{
get { return m_Selection; }
}
public int pointCount
{
get { return m_ControlPoints.Count; }
}
public ControlPoint GetPoint(int index)
{
return TransformPoint(localToWorldMatrix, m_ControlPoints[index]);
}
public void SetPoint(int index, ControlPoint controlPoint)
{
m_ControlPoints[index] = TransformPoint(worldToLocalMatrix, controlPoint);
}
public void AddPoint(ControlPoint controlPoint)
{
m_ControlPoints.Insert(pointCount, TransformPoint(worldToLocalMatrix, controlPoint));
}
public void InsertPoint(int index, ControlPoint controlPoint)
{
m_ControlPoints.Insert(index, TransformPoint(worldToLocalMatrix, controlPoint));
}
public void RemovePoint(int index)
{
m_ControlPoints.RemoveAt(index);
}
public void Clear()
{
m_ControlPoints.Clear();
}
private ControlPoint TransformPoint(Matrix4x4 transformMatrix, ControlPoint controlPoint)
{
if (transformMatrix == Matrix4x4.identity)
return controlPoint;
var newControlPoint = new ControlPoint()
{
position = transformMatrix.MultiplyPoint3x4(controlPoint.position),
tangentMode = controlPoint.tangentMode,
continuousCache = controlPoint.continuousCache,
brokenCache = controlPoint.brokenCache,
mirrorLeft = controlPoint.mirrorLeft
};
newControlPoint.rightTangent = transformMatrix.MultiplyPoint3x4(controlPoint.rightTangent);
newControlPoint.leftTangent = transformMatrix.MultiplyPoint3x4(controlPoint.leftTangent);
return newControlPoint;
}
public bool Select(ISelector<Vector3> selector)
{
var changed = false;
for (var i = 0; i < pointCount; ++i)
changed |= selection.Select(i, selector.Select(GetPoint(i).position));
return changed;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cc93dba6e70bb8044a38eeb19db22669
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,259 @@
using System;
using System.Linq;
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public class EditablePathController : IEditablePathController
{
private ISnapping<Vector3> m_Snapping = new Snapping();
public IEditablePath editablePath { get; set; }
public IEditablePath closestEditablePath { get { return editablePath; } }
public ISnapping<Vector3> snapping
{
get { return m_Snapping; }
set { m_Snapping = value; }
}
public bool enableSnapping { get; set; }
public void RegisterUndo(string name)
{
if (editablePath.undoObject != null)
editablePath.undoObject.RegisterUndo(name);
}
public void ClearSelection()
{
editablePath.selection.Clear();
}
public void SelectPoint(int index, bool select)
{
editablePath.selection.Select(index, select);
}
public void CreatePoint(int index, Vector3 position)
{
ClearSelection();
if (editablePath.shapeType == ShapeType.Polygon)
{
editablePath.InsertPoint(index + 1, new ControlPoint() { position = position });
}
else if (editablePath.shapeType == ShapeType.Spline)
{
var nextIndex = NextIndex(index);
var currentPoint = editablePath.GetPoint(index);
var nextPoint = editablePath.GetPoint(nextIndex);
float t;
var closestPoint = BezierUtility.ClosestPointOnCurve(
position,
currentPoint.position,
nextPoint.position,
GetRightTangentPosition(index),
GetLeftTangentPosition(nextIndex),
out t);
Vector3 leftStartPosition;
Vector3 leftEndPosition;
Vector3 leftStartTangent;
Vector3 leftEndTangent;
Vector3 rightStartPosition;
Vector3 rightEndPosition;
Vector3 rightStartTangent;
Vector3 rightEndTangent;
BezierUtility.SplitBezier(t, currentPoint.position, nextPoint.position, GetRightTangentPosition(index), GetLeftTangentPosition(nextIndex),
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
var newPointIndex = index + 1;
var newPoint = new ControlPoint()
{
position = closestPoint,
leftTangent = leftEndTangent,
rightTangent = rightStartTangent,
tangentMode = TangentMode.Continuous
};
currentPoint.rightTangent = leftStartTangent;
nextPoint.leftTangent = rightEndTangent;
if (currentPoint.tangentMode == TangentMode.Linear && nextPoint.tangentMode == TangentMode.Linear)
{
newPoint.tangentMode = TangentMode.Linear;
newPoint.localLeftTangent = Vector3.zero;
newPoint.localRightTangent = Vector3.zero;
currentPoint.localRightTangent = Vector3.zero;
nextPoint.localLeftTangent = Vector3.zero;
}
else
{
if (currentPoint.tangentMode == TangentMode.Linear)
currentPoint.tangentMode = TangentMode.Broken;
if (nextPoint.tangentMode == TangentMode.Linear)
nextPoint.tangentMode = TangentMode.Broken;
}
editablePath.SetPoint(index, currentPoint);
editablePath.SetPoint(nextIndex, nextPoint);
editablePath.InsertPoint(newPointIndex, newPoint);
}
}
public void RemoveSelectedPoints()
{
var minPointCount = editablePath.isOpenEnded ? 2 : 3;
if (editablePath.pointCount > minPointCount)
{
var indices = editablePath.selection.elements.OrderByDescending( i => i);
foreach (var index in indices)
if (editablePath.pointCount > minPointCount)
editablePath.RemovePoint(index);
ClearSelection();
}
}
public void MoveSelectedPoints(Vector3 delta)
{
delta = Vector3.ProjectOnPlane(delta, editablePath.forward);
for (var i = 0; i < editablePath.pointCount; ++i)
{
if (editablePath.selection.Contains(i))
{
var controlPoint = editablePath.GetPoint(i);
controlPoint.position += delta;
editablePath.SetPoint(i, controlPoint);
}
}
}
public void MoveEdge(int index, Vector3 delta)
{
if (editablePath.isOpenEnded && index == editablePath.pointCount - 1)
return;
var controlPoint = editablePath.GetPoint(index);
controlPoint.position += delta;
editablePath.SetPoint(index, controlPoint);
controlPoint = NextControlPoint(index);
controlPoint.position += delta;
editablePath.SetPoint(NextIndex(index), controlPoint);
}
public void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent, TangentMode cachedTangentMode)
{
var controlPoint = editablePath.GetPoint(index);
controlPoint.tangentMode = cachedTangentMode;
controlPoint.leftTangent = position;
controlPoint.mirrorLeft = false;
if (setToLinear)
{
controlPoint.leftTangent = controlPoint.position;
controlPoint.rightTangent = cachedRightTangent;
}
else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
{
var magnitude = controlPoint.localRightTangent.magnitude;
if (mirror)
magnitude = controlPoint.localLeftTangent.magnitude;
controlPoint.localRightTangent = magnitude * -controlPoint.localLeftTangent.normalized;
}
editablePath.SetPoint(index, controlPoint);
editablePath.UpdateTangentMode(index);
}
public void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent, TangentMode cachedTangentMode)
{
var controlPoint = editablePath.GetPoint(index);
controlPoint.tangentMode = cachedTangentMode;
controlPoint.rightTangent = position;
controlPoint.mirrorLeft = true;
if (setToLinear)
{
controlPoint.rightTangent = controlPoint.position;
controlPoint.leftTangent = cachedLeftTangent;
}
else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
{
var magnitude = controlPoint.localLeftTangent.magnitude;
if (mirror)
magnitude = controlPoint.localRightTangent.magnitude;
controlPoint.localLeftTangent = magnitude * -controlPoint.localRightTangent.normalized;
}
editablePath.SetPoint(index, controlPoint);
editablePath.UpdateTangentMode(index);
}
public void ClearClosestPath() { }
public void AddClosestPath(float distance) { }
private Vector3 GetLeftTangentPosition(int index)
{
var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localLeftTangent.sqrMagnitude, 0f);
if (isLinear)
{
var position = editablePath.GetPoint(index).position;
var prevPosition = PrevControlPoint(index).position;
return (1f / 3f) * (prevPosition - position) + position;
}
return editablePath.GetPoint(index).leftTangent;
}
private Vector3 GetRightTangentPosition(int index)
{
var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localRightTangent.sqrMagnitude, 0f);
if (isLinear)
{
var position = editablePath.GetPoint(index).position;
var nextPosition = NextControlPoint(index).position;
return (1f / 3f) * (nextPosition - position) + position;
}
return editablePath.GetPoint(index).rightTangent;
}
private int NextIndex(int index)
{
return EditablePathUtility.Mod(index + 1, editablePath.pointCount);
}
private ControlPoint NextControlPoint(int index)
{
return editablePath.GetPoint(NextIndex(index));
}
private int PrevIndex(int index)
{
return EditablePathUtility.Mod(index - 1, editablePath.pointCount);
}
private ControlPoint PrevControlPoint(int index)
{
return editablePath.GetPoint(PrevIndex(index));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e097fcf51266c654f8f8ef2c431a91d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,275 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public static class EditablePathExtensions
{
public static Polygon ToPolygon(this IEditablePath path)
{
var polygon = new Polygon()
{
isOpenEnded = path.isOpenEnded,
points = new Vector3[path.pointCount]
};
for (var i = 0; i < path.pointCount; ++i)
polygon.points[i] = path.GetPoint(i).position;
return polygon;
}
public static Spline ToSpline(this IEditablePath path)
{
var count = path.pointCount * 3;
if (path.isOpenEnded)
count -= 2;
var spline = new Spline()
{
isOpenEnded = path.isOpenEnded,
points = new Vector3[count]
};
for (var i = 0; i < path.pointCount; ++i)
{
var point = path.GetPoint(i);
spline.points[i*3] = point.position;
if (i * 3 + 1 < count)
{
var nextIndex = EditablePathUtility.Mod(i+1, path.pointCount);
spline.points[i*3 + 1] = path.CalculateRightTangent(i);
spline.points[i*3 + 2] = path.CalculateLeftTangent(nextIndex);
}
}
return spline;
}
public static Vector3 CalculateLocalLeftTangent(this IEditablePath path, int index)
{
return path.CalculateLeftTangent(index) - path.GetPoint(index).position;
}
public static Vector3 CalculateLeftTangent(this IEditablePath path, int index)
{
var point = path.GetPoint(index);
var isTangentLinear = point.localLeftTangent == Vector3.zero;
var isEndpoint = path.isOpenEnded && index == 0;
var tangent = point.leftTangent;
if (isEndpoint)
return point.position;
if (isTangentLinear)
{
var prevPoint = path.GetPrevPoint(index);
var v = prevPoint.position - point.position;
tangent = point.position + v.normalized * (v.magnitude / 3f);
}
return tangent;
}
public static Vector3 CalculateLocalRightTangent(this IEditablePath path, int index)
{
return path.CalculateRightTangent(index) - path.GetPoint(index).position;
}
public static Vector3 CalculateRightTangent(this IEditablePath path, int index)
{
var point = path.GetPoint(index);
var isTangentLinear = point.localRightTangent == Vector3.zero;
var isEndpoint = path.isOpenEnded && index == path.pointCount - 1;
var tangent = point.rightTangent;
if (isEndpoint)
return point.position;
if (isTangentLinear)
{
var nextPoint = path.GetNextPoint(index);
var v = nextPoint.position - point.position;
tangent = point.position + v.normalized * (v.magnitude / 3f);
}
return tangent;
}
public static ControlPoint GetPrevPoint(this IEditablePath path, int index)
{
return path.GetPoint(EditablePathUtility.Mod(index - 1, path.pointCount));
}
public static ControlPoint GetNextPoint(this IEditablePath path, int index)
{
return path.GetPoint(EditablePathUtility.Mod(index + 1, path.pointCount));
}
public static void UpdateTangentMode(this IEditablePath path, int index)
{
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
var controlPoint = path.GetPoint(index);
var isLeftTangentLinear = controlPoint.localLeftTangent == Vector3.zero;
var isRightTangentLinear = controlPoint.localRightTangent == Vector3.zero;
if (isLeftTangentLinear && isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Linear;
else if (isLeftTangentLinear || isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Broken;
else if (controlPoint.tangentMode != TangentMode.Continuous)
controlPoint.tangentMode = TangentMode.Broken;
controlPoint.StoreTangents();
path.SetPoint(index, controlPoint);
path.localToWorldMatrix = localToWorldMatrix;
}
public static void UpdateTangentsFromMode(this IEditablePath path)
{
const float kEpsilon = 0.001f;
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
for (var i = 0; i < path.pointCount; ++i)
{
var controlPoint = path.GetPoint(i);
if (controlPoint.tangentMode == TangentMode.Linear)
{
controlPoint.localLeftTangent = Vector3.zero;
controlPoint.localRightTangent = Vector3.zero;
}
else if (controlPoint.tangentMode == TangentMode.Broken)
{
var isLeftEndpoint = path.isOpenEnded && i == 0;
var prevPoint = path.GetPrevPoint(i);
var nextPoint = path.GetNextPoint(i);
var liniarLeftPosition = (prevPoint.position - controlPoint.position) / 3f;
var isLeftTangentLinear = isLeftEndpoint || (controlPoint.localLeftTangent - liniarLeftPosition).sqrMagnitude < kEpsilon;
if (isLeftTangentLinear)
controlPoint.localLeftTangent = Vector3.zero;
var isRightEndpoint = path.isOpenEnded && i == path.pointCount-1;
var liniarRightPosition = (nextPoint.position - controlPoint.position) / 3f;
var isRightTangentLinear = isRightEndpoint || (controlPoint.localRightTangent - liniarRightPosition).sqrMagnitude < kEpsilon;
if (isRightTangentLinear)
controlPoint.localRightTangent = Vector3.zero;
if (isLeftTangentLinear && isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Linear;
}
else if (controlPoint.tangentMode == TangentMode.Continuous)
{
//TODO: ensure tangent continuity
}
controlPoint.StoreTangents();
path.SetPoint(i, controlPoint);
}
path.localToWorldMatrix = localToWorldMatrix;
}
public static void SetTangentMode(this IEditablePath path, int index, TangentMode tangentMode)
{
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
var controlPoint = path.GetPoint(index);
var isEndpoint = path.isOpenEnded && (index == 0 || index == path.pointCount - 1);
var oldTangentMode = controlPoint.tangentMode;
controlPoint.tangentMode = tangentMode;
controlPoint.RestoreTangents();
if (tangentMode == TangentMode.Linear)
{
controlPoint.localLeftTangent = Vector3.zero;
controlPoint.localRightTangent = Vector3.zero;
}
else if (tangentMode == TangentMode.Continuous && !isEndpoint)
{
var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
var tangentDotProduct = Vector3.Dot(controlPoint.localLeftTangent.normalized, controlPoint.localRightTangent.normalized);
var isContinous = tangentDotProduct < 0f && (tangentDotProduct + 1) < 0.001f;
var isLinear = isLeftLinear && isRightLinear;
if ((isLinear || oldTangentMode == TangentMode.Broken) && !isContinous)
{
var prevPoint = path.GetPrevPoint(index);
var nextPoint = path.GetNextPoint(index);
var vLeft = prevPoint.position - controlPoint.position;
var vRight = nextPoint.position - controlPoint.position;
var rightDirection = Vector3.Cross(Vector3.Cross(vLeft, vRight), vLeft.normalized + vRight.normalized).normalized;
var scale = 1f / 3f;
if (isLeftLinear)
controlPoint.localLeftTangent = vLeft.magnitude * scale * -rightDirection;
else
controlPoint.localLeftTangent = controlPoint.localLeftTangent.magnitude * -rightDirection;
if (isRightLinear)
controlPoint.localRightTangent = vRight.magnitude * scale * rightDirection;
else
controlPoint.localRightTangent = controlPoint.localRightTangent.magnitude * rightDirection;
}
}
else
{
var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
if (isLeftLinear || isRightLinear)
{
if (isLeftLinear)
controlPoint.localLeftTangent = path.CalculateLocalLeftTangent(index);
if (isRightLinear)
controlPoint.localRightTangent = path.CalculateLocalRightTangent(index);
}
}
controlPoint.StoreTangents();
path.SetPoint(index, controlPoint);
path.localToWorldMatrix = localToWorldMatrix;
}
public static void MirrorTangent(this IEditablePath path, int index)
{
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
var controlPoint = path.GetPoint(index);
if (controlPoint.tangentMode == TangentMode.Linear)
return;
if (!Mathf.Approximately((controlPoint.localLeftTangent + controlPoint.localRightTangent).sqrMagnitude, 0f))
{
if (controlPoint.mirrorLeft)
controlPoint.localLeftTangent = -controlPoint.localRightTangent;
else
controlPoint.localRightTangent = -controlPoint.localLeftTangent;
controlPoint.StoreTangents();
path.SetPoint(index, controlPoint);
}
path.localToWorldMatrix = localToWorldMatrix;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 118640352bf706b49aa5ece0cf51b03c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,14 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.U2D.Path
{
public class EditablePathUtility
{
public static int Mod(int x, int m)
{
int r = x % m;
return r < 0 ? r + m : r;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 136ca6dbbd8b9704cb676d8e400f8096
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public interface IEditablePath : ISelectable<Vector3>
{
ShapeType shapeType { get; set; }
IUndoObject undoObject { get; set; }
ISelection<int> selection { get; }
Matrix4x4 localToWorldMatrix { get; set; }
Vector3 forward { get; set; }
Vector3 up { get; set; }
Vector3 right { get; set; }
bool isOpenEnded { get; set; }
int pointCount { get; }
ControlPoint GetPoint(int index);
void SetPoint(int index, ControlPoint controlPoint);
void AddPoint(ControlPoint controlPoint);
void InsertPoint(int index, ControlPoint controlPoint);
void RemovePoint(int index);
void Clear();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5db033e7b440e254688b2fcca87e6a59
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
using System;
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public interface IEditablePathController
{
IEditablePath editablePath { get; set; }
IEditablePath closestEditablePath { get; }
ISnapping<Vector3> snapping { get; set; }
bool enableSnapping { get; set; }
void RegisterUndo(string name);
void ClearSelection();
void SelectPoint(int index, bool select);
void CreatePoint(int index, Vector3 position);
void RemoveSelectedPoints();
void MoveSelectedPoints(Vector3 delta);
void MoveEdge(int index, Vector3 delta);
void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent, TangentMode cachedTangentMode);
void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent, TangentMode cachedTangentMode);
void ClearClosestPath();
void AddClosestPath(float distance);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2416be64a5db15c4eb742e5f086682c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,10 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.U2D.Path
{
public interface ISnapping<T>
{
T Snap(T value);
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f15c4dfafb6d6d544ab56d8cfca74d07
timeCreated: 1505113416
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public interface IUndoObject
{
void RegisterUndo(string name);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55ba72d694e736b428394820f9ca8f24
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.U2D.Path
{
public class MultipleEditablePathController : IEditablePathController
{
private IEditablePathController m_Controller = new EditablePathController();
private List<IEditablePath> m_Paths = new List<IEditablePath>();
private float m_ClosestDistance = float.MaxValue;
private IEditablePath m_ClosestPath;
public IEditablePath editablePath
{
get { return m_Controller.editablePath; }
set { m_Controller.editablePath = value; }
}
public IEditablePath closestEditablePath { get; private set; }
public ISnapping<Vector3> snapping
{
get { return m_Controller.snapping; }
set { m_Controller.snapping = value; }
}
public bool enableSnapping
{
get { return m_Controller.enableSnapping; }
set { m_Controller.enableSnapping = value; }
}
public void ClearPaths()
{
m_Paths.Clear();
}
public void AddPath(IEditablePath path)
{
if (!m_Paths.Contains(path))
m_Paths.Add(path);
}
public void RemovePath(IEditablePath path)
{
m_Paths.Remove(path);
}
public void RegisterUndo(string name)
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.RegisterUndo(name);
});
editablePath = current;
}
public void ClearSelection()
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.ClearSelection();
});
editablePath = current;
}
public void SelectPoint(int index, bool select)
{
m_Controller.SelectPoint(index, select);
}
public void CreatePoint(int index, Vector3 position)
{
m_Controller.CreatePoint(index, position);
}
public void RemoveSelectedPoints()
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.RemoveSelectedPoints();
});
editablePath = current;
}
public void MoveSelectedPoints(Vector3 delta)
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.MoveSelectedPoints(delta);
});
editablePath = current;
}
public void MoveEdge(int index, Vector3 delta)
{
m_Controller.MoveEdge(index, delta);
}
public void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent, TangentMode cachedTangentMode)
{
m_Controller.SetLeftTangent(index, position, setToLinear, mirror, cachedRightTangent, cachedTangentMode);
}
public void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent, TangentMode cachedTangentMode)
{
m_Controller.SetRightTangent(index, position, setToLinear, mirror, cachedLeftTangent, cachedTangentMode);
}
public void ClearClosestPath()
{
m_ClosestDistance = float.MaxValue;
closestEditablePath = null;
}
public void AddClosestPath(float distance)
{
if (distance <= m_ClosestDistance)
{
m_ClosestDistance = distance;
closestEditablePath = editablePath;
}
}
private void ForEach(Action<IEditablePath> action)
{
foreach(var path in m_Paths)
{
if (path == null)
continue;
action(path);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e9b894eff389bc4b914e86801e4cb25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.U2D.Path
{
public class Snapping : ISnapping<Vector3>
{
public Vector3 Snap(Vector3 position)
{
return new Vector3(
Snap(position.x, EditorSnapSettings.move.x),
Snap(position.y, EditorSnapSettings.move.y),
position.z);
}
private float Snap(float value, float snap)
{
return Mathf.Round(value / snap) * snap;
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 729f75e79f7b4da458ab89bf1f36dc90
timeCreated: 1505113416
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: