using System.Collections.Generic;
using UnityEngine;
namespace Gley.UrbanSystem.Internal
{
///
/// Path creator, used to draw curves
///
[System.Serializable]
public class Path
{
[SerializeField]
List points;
[SerializeField]
bool isClosed;
[SerializeField]
bool autoSetControlPoints;
public Path(Vector3 startPosition, Vector3 endPosition)
{
points = new List
{
startPosition,
(startPosition + endPosition)/2,
(startPosition + endPosition)/2,
endPosition
};
}
public Vector3 this[int i]
{
get
{
return points[i];
}
}
public bool IsClosed
{
get
{
return isClosed;
}
set
{
if (isClosed != value)
{
isClosed = value;
if (isClosed)
{
points.Add(points[points.Count - 1] * 2 - points[points.Count - 2]);
points.Add(points[0] * 2 - points[1]);
if (autoSetControlPoints)
{
AutoSetAnchorControlPoints(0);
AutoSetAnchorControlPoints(points.Count - 3);
}
}
else
{
points.RemoveRange(points.Count - 2, 2);
if (autoSetControlPoints)
{
AutoSetStartAndEndControls();
}
}
}
}
}
public bool AutoSetControlPoints
{
get
{
return autoSetControlPoints;
}
set
{
if (autoSetControlPoints != value)
{
autoSetControlPoints = value;
if (autoSetControlPoints)
{
AutoSetAllControlPoints();
}
}
}
}
public int NumPoints
{
get
{
return points.Count;
}
}
public int NumSegments
{
get
{
return points.Count / 3;
}
}
public void AddSegment(Vector3 anchorPos)
{
points.Add(points[points.Count - 1] * 2 - points[points.Count - 2]);
points.Add((points[points.Count - 1] + anchorPos) * .5f);
points.Add(anchorPos);
if (autoSetControlPoints)
{
AutoSetAllAffectedControlPoints(points.Count - 1);
}
}
public void SplitSegment(Vector3 anchorPos, int segmentIndex)
{
points.InsertRange(segmentIndex * 3 + 2, new Vector3[] { Vector3.zero, anchorPos, Vector3.zero });
if (autoSetControlPoints)
{
AutoSetAllAffectedControlPoints(segmentIndex * 3 + 3);
}
else
{
AutoSetAnchorControlPoints(segmentIndex * 3 + 3);
}
}
public void DeleteSegment(int anchorIndex)
{
if (NumSegments > 2 || !isClosed && NumSegments > 1)
{
if (anchorIndex == 0)
{
if (isClosed)
{
points[points.Count - 1] = points[2];
}
points.RemoveRange(0, 3);
}
else if (anchorIndex == points.Count - 1 && !isClosed)
{
points.RemoveRange(anchorIndex - 2, 3);
}
else
{
points.RemoveRange(anchorIndex - 1, 3);
}
}
}
public Vector3[] GetPointsInSegment(int i, Vector3 offset)
{
return new Vector3[] { points[i * 3] + offset, points[i * 3 + 1] + offset, points[i * 3 + 2] + offset, points[LoopIndex(i * 3 + 3)] + offset };
}
public Vector3 GetPoint(int i, Vector3 offset)
{
return points[i] + offset;
}
public void MovePoint(int i, Vector3 pos)
{
Vector3 deltaMove = pos - points[i];
if (i % 3 == 0 || !autoSetControlPoints)
{
points[i] = pos;
if (autoSetControlPoints)
{
AutoSetAllAffectedControlPoints(i);
}
else
{
if (i % 3 == 0)
{
if (i + 1 < points.Count || isClosed)
{
points[LoopIndex(i + 1)] += deltaMove;
}
if (i - 1 >= 0 || isClosed)
{
points[LoopIndex(i - 1)] += deltaMove;
}
}
else
{
bool nextPointIsAnchor = (i + 1) % 3 == 0;
int correspondingControlIndex = (nextPointIsAnchor) ? i + 2 : i - 2;
int anchorIndex = (nextPointIsAnchor) ? i + 1 : i - 1;
if (correspondingControlIndex >= 0 && correspondingControlIndex < points.Count || isClosed)
{
float dst = (points[LoopIndex(anchorIndex)] - points[LoopIndex(correspondingControlIndex)]).magnitude;
Vector3 dir = (points[LoopIndex(anchorIndex)] - pos).normalized;
points[LoopIndex(correspondingControlIndex)] = points[LoopIndex(anchorIndex)] + dir * dst;
}
}
}
}
}
private void AutoSetAllAffectedControlPoints(int updatedAnchorIndex)
{
for (int i = updatedAnchorIndex - 3; i <= updatedAnchorIndex + 3; i += 3)
{
if (i >= 0 && i < points.Count || isClosed)
{
AutoSetAnchorControlPoints(LoopIndex(i));
}
}
AutoSetStartAndEndControls();
}
private void AutoSetAllControlPoints()
{
for (int i = 0; i < points.Count; i += 3)
{
AutoSetAnchorControlPoints(i);
}
AutoSetStartAndEndControls();
}
private void AutoSetAnchorControlPoints(int anchorIndex)
{
Vector3 anchorPos = points[anchorIndex];
Vector3 dir = Vector3.zero;
float[] neighbourDistances = new float[2];
if (anchorIndex - 3 >= 0 || isClosed)
{
Vector3 offset = points[LoopIndex(anchorIndex - 3)] - anchorPos;
dir += offset.normalized;
neighbourDistances[0] = offset.magnitude;
}
if (anchorIndex + 3 >= 0 || isClosed)
{
Vector3 offset = points[LoopIndex(anchorIndex + 3)] - anchorPos;
dir -= offset.normalized;
neighbourDistances[1] = -offset.magnitude;
}
dir.Normalize();
for (int i = 0; i < 2; i++)
{
int controlIndex = anchorIndex + i * 2 - 1;
if (controlIndex >= 0 && controlIndex < points.Count || isClosed)
{
points[LoopIndex(controlIndex)] = anchorPos + dir * neighbourDistances[i] * .5f;
}
}
}
private void AutoSetStartAndEndControls()
{
if (!isClosed)
{
points[1] = (points[0] + points[2]) * .5f;
points[points.Count - 2] = (points[points.Count - 1] + points[points.Count - 3]) * .5f;
}
}
private int LoopIndex(int i)
{
return (i + points.Count) % points.Count;
}
}
}