graph chart引入&骑行结果弹窗

This commit is contained in:
lishuo committed 2021-04-22 19:29:59 +08:00
1 parent 40d118cea3
commit 96f3644b5b
2374 files changed
+485161 -2410

No files matched your search

@@ -0,0 +1,20 @@
#define Graph_And_Chart_PRO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace ChartAndGraph
{
public partial class CanvasLines
{
partial void ProcesssPoint(ref Vector4 point, ref float halfSize)
{
if (point.w >= 0f)
halfSize = point.w * 0.5f;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e7d6641167b28744f8a0bacf68f32e4b
timeCreated: 1560587181
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,911 @@
#define Graph_And_Chart_PRO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace ChartAndGraph
{
/// <summary>
/// this class is used internally in order to draw lines, line fill and line points into a mesh
/// </summary>
public partial class CanvasLines : EventHandlingGraphic
{
/// <summary>
/// thickness of the lines being drawn
/// </summary>
public float Thickness = 2f;
float innerTile = 1f;
int mMinModifyIndex = 0;
/// <summary>
/// Tiling value for the graphic. The tiliing is the total length of the lines being drawn. It is set by the parent graph
/// </summary>
public float Tiling
{
get { return innerTile; }
set
{
innerTile = value;
}
}
/// <summary>
/// true if this graphic is in fill render mode (drawing the inner fill of a line graph)
/// </summary>
bool mFillRender = false;
/// <summary>
/// true if this graph is in point drawing render mode
/// </summary>
bool mPointRender = false;
/// <summary>
/// for point render mode , sets the point size
/// </summary>
float mPointSize = 5f;
/// <summary>
/// for fill render mode , this sets the boundries of the fill. (so the bottom of the fill matches the bottom of the graph)
/// </summary>
Rect mFillRect;
/// <summary>
/// if true , the Y of the fill is stretched
/// </summary>
bool mStretchY;
/// <summary>
/// The material for the graphic object
/// </summary>
Material mCachedMaterial;
/// <summary>
/// list of lines for the line object , this will be used to render either dots , fill or lines depending on the render mode
/// </summary>
List<LineSegement> mLines;
/// <summary>
/// bounding box for the value of mLines, used for event handling
/// </summary>
float mMinX, mMinY, mMaxX, mMaxY;
/// <summary>
/// Sets point render mode
/// </summary>
/// <param name="pointSize"></param>
public void MakePointRender(float pointSize)
{
mPointSize = pointSize;
mPointRender = true;
}
/// <summary>
/// sets inner fill render mode
/// </summary>
/// <param name="fillRect"></param>
/// <param name="stretchY"></param>
public void MakeFillRender(Rect fillRect, bool stretchY)
{
mFillRect = fillRect;
mFillRender = true;
mStretchY = stretchY;
}
UIVertex[] mTmpVerts = new UIVertex[4];
/// <summary>
/// holds line data and pre cacultates normal and speration
/// </summary>
internal struct Line
{
public Line(Vector3 from, Vector3 to, float halfThickness, bool hasNext, bool hasPrev) : this()
{
Vector3 diff = (to - from);
float magDec = 0;
if (hasNext)
magDec += halfThickness;
if (hasPrev)
magDec += halfThickness;
Mag = diff.magnitude - magDec * 2;
Degenerated = false;
if (Mag <= 0)
Degenerated = true;
Dir = diff.normalized;
Vector3 add = halfThickness * 2 * Dir;
if (hasPrev)
from += add;
if (hasNext)
to -= add;
From = from;
To = to;
Normal = new Vector3(Dir.y, -Dir.x, Dir.z); // this part calculates the line inset and points based on thichkness
P1 = From + Normal * halfThickness;
P2 = from - Normal * halfThickness;
P3 = to + Normal * halfThickness;
P4 = to - Normal * halfThickness;
}
public bool Degenerated { get; private set; }
public Vector3 P1 { get; private set; }
public Vector3 P2 { get; private set; }
public Vector3 P3 { get; private set; }
public Vector3 P4 { get; private set; }
public Vector3 From { get; private set; }
public Vector3 To { get; private set; }
public Vector3 Dir { get; private set; }
public float Mag { get; private set; }
public Vector3 Normal { get; private set; }
}
/// <summary>
/// represents one line segemenet.
/// </summary>
internal class LineSegement
{
List<Vector4> mLines = new List<Vector4>();
public LineSegement(IList<Vector3> lines)
{
mLines.AddRange(lines.Select(x=>new Vector4(x.x,x.y,x.z,-1f)));
}
public LineSegement(IList<Vector4> lines)
{
mLines.AddRange(lines);
}
/// <summary>
/// reset the value of the linesSegment to the specified list
/// </summary>
/// <param name="v"></param>
public void ModifiyLines(List<Vector4> v)
{
mLines.Clear();
mLines.AddRange(v);
}
/// <summary>
/// The total amount of points in this line segmenet
/// </summary>
public int PointCount
{
get
{
if (mLines == null)
return 0;
return mLines.Count;
}
}
/// <summary>
/// Line count is point count minus one. Since all dots have to be connected
/// </summary>
public int LineCount
{
get
{
if (mLines == null)
return 0;
if (mLines.Count < 2)
return 0;
return mLines.Count - 1;
}
}
/// <summary>
/// Gets a point
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public Vector4 getPoint(int index)
{
Vector4 p = mLines[index];
return p;
}
/// <summary>
/// gets the line at the specified index.index Must be below LineCount
/// </summary>
/// <param name="index"></param>
/// <param name="from"></param>
/// <param name="to"></param>
public void GetLine(int index, out Vector3 from, out Vector3 to)
{
from = mLines[index];
to = mLines[index + 1];
}
/// <summary>
/// gets the line at the specified index.index Must be below LineCount. if the line has a previous or a next line an inset is calculated for better visual aperance
/// </summary>
/// <param name="index"></param>
/// <param name="from"></param>
/// <param name="to"></param>
public Line GetLine(int index, float halfThickness, bool hasPrev, bool hasNext)
{
Vector3 from = mLines[index];
Vector3 to = mLines[index + 1];
return new Line(from, to, halfThickness, false, false);
}
public double GetLineMag(int index)
{
return (mLines[index] - mLines[index + 1]).magnitude;
}
}
public CanvasLines()
{
}
/// <summary>
/// finds the minimum and maximum values of the currently set data
/// </summary>
void FindBoundingValues()
{
mMinX = float.PositiveInfinity;
mMinY = float.PositiveInfinity;
mMaxX = float.NegativeInfinity;
mMaxY = float.NegativeInfinity;
// this part finds the bounding box of the lines
if (mLines != null)
{
for (int i = 0; i < mLines.Count; i++)
{
LineSegement seg = mLines[i];
int totalPoints = seg.PointCount;
for (int j = 0; j < totalPoints; j++)
{
Vector3 point = seg.getPoint(j);
mMinX = Mathf.Min(mMinX, point.x);
mMinY = Mathf.Min(mMinY, point.y);
mMaxX = Mathf.Max(mMaxX, point.x);
mMaxY = Mathf.Max(mMaxY, point.y);
}
}
}
}
internal void ModifyLines(int minModifyIndex,List<Vector4> lines)
{
if (mLines.Count == 0)
{
mLines.Add(new LineSegement(lines.ToArray()));
return;
}
mMinModifyIndex = minModifyIndex;
FindBoundingValues();
mLines[0].ModifiyLines(lines);
SetVerticesDirty(); // clear previous animations
Rebuild(CanvasUpdate.PostLayout);
RefreshInputs();
}
public bool EnableOptimization { get; set; }
/// <summary>
/// sets the lines for this renderer
/// </summary>
/// <param name="lines"></param>
internal void SetLines(List<LineSegement> lines)
{
mLines = lines;
FindBoundingValues();
mMinModifyIndex = 0;
SetAllDirty();
ClearEvents(); // clear previous animations
if(EnableOptimization)
Rebuild(CanvasUpdate.PostLayout);
else
Rebuild(CanvasUpdate.PreRender);
}
protected override void UpdateMaterial()
{
base.UpdateMaterial();
canvasRenderer.SetTexture(material.mainTexture);
}
/// <summary>
/// this method inflate one point in a line with the specified distantce. This enable the graphic to control the thickness of lines
/// </summary>
/// <param name="point"></param>
/// <param name="dir"></param>
/// <param name="normal"></param>
/// <param name="dist"></param>
/// <param name="size"></param>
/// <param name="z"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
void GetSide(Vector3 point, Vector3 dir,Vector3 normal,float dist,float size,float z,out Vector3 p1,out Vector3 p2)
{
point.z = z;
point += dir * dist;
normal *= size;
p1 = point + normal;
p2 = point - normal;
}
protected override void OnDestroy()
{
base.OnDestroy();
ChartCommon.SafeDestroy(mCachedMaterial);
}
protected override void OnDisable()
{
base.OnDisable();
}
/// <summary>
/// overriding the default implementation to support materials with _ChartTiling property. This is used to tile texture along the graphic lines
/// </summary>
public override Material material
{
get
{
return base.material;
}
set
{
ChartCommon.SafeDestroy(mCachedMaterial);
if (value == null)
{
mCachedMaterial = null;
base.material = null;
return;
}
mCachedMaterial = new Material(value);
mCachedMaterial.hideFlags = HideFlags.DontSave;
if (mCachedMaterial.HasProperty("_ChartTiling"))
mCachedMaterial.SetFloat("_ChartTiling", Tiling);
base.material = mCachedMaterial;
}
}
protected override Vector2 Min
{
get
{
return new Vector2(mMinX, mMinY);
}
}
protected override Vector2 Max
{
get
{
return new Vector2(mMaxX, mMaxY);
}
}
protected override float MouseInThreshold
{
get
{
return Mathf.Max(Thickness, mPointSize) + Sensitivity;
}
}
/// <summary>
/// sets the transform of the hover object based on the data in this graphic
/// </summary>
/// <param name="hover"></param>
/// <param name="index"></param>
protected override void SetUpHoverObject(ChartItemEffect hover, int index, int type,object data)
{
if (hover == null)
return;
if (mLines == null || mLines.Count == 0)
return;
if (index < 0)
return;
if (mPointRender)
{
if (index >= mLines[0].PointCount)
return;
Vector4 point = mLines[0].getPoint(index);
RectTransform transform = hover.GetComponent<RectTransform>();
transform.localScale = new Vector3(1f, 1f, 1f);
float size = mPointSize;
if (point.w >= 0f)
size = point.w;
transform.sizeDelta = new Vector2(size, size);
transform.anchoredPosition3D = new Vector3(point.x, point.y, 0f);
}
else
{
if (index >= mLines[0].LineCount)
return;
Vector3 from;
Vector3 to;
mLines[0].GetLine(index, out from, out to);
if (ViewRect.HasValue)
{
Vector2 vFrom = from;
Vector2 vTo = to;
TrimLine(ViewRect.Value, ref vFrom, ref vTo);
from = new Vector3(vFrom.x, vFrom.y, from.z);
to = new Vector3(vTo.x, vTo.y, to.z);
}
Vector3 dir = (to - from);
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
RectTransform transform = hover.GetComponent<RectTransform>();
transform.sizeDelta = new Vector2(dir.magnitude, Thickness);
transform.localScale = new Vector3(1f, 1f, 1f);
transform.localRotation = Quaternion.Euler(0f, 0f, angle);
Vector3 point = (from + to) * 0.5f;
transform.anchoredPosition3D = new Vector3(point.x, point.y, 0f);
}
}
protected override void Pick(Vector3 mouse, out int pickedIndex, out int pickedType, out object selectionData)
{
if (mPointRender)
PickDot(mouse, out pickedType, out pickedIndex,out selectionData);
else
PickLine(mouse, out pickedType, out pickedIndex, out selectionData);
if (pickedIndex >= 0)
pickedIndex += refrenceIndex;
}
protected override void Update()
{
base.Update();
Material mat = material;
if (mCachedMaterial != null && mat != null && mCachedMaterial.HasProperty("_ChartTiling"))
{
if (mCachedMaterial != mat)
mCachedMaterial.CopyPropertiesFromMaterial(mat);
mCachedMaterial.SetFloat("_ChartTiling", Tiling);
}
}
partial void ProcesssPoint(ref Vector4 point, ref float halfSize);
IEnumerable<UIVertex> getDotVeritces()
{
if (mLines == null)
yield break;
float z = 0f;
float halfSize = mPointSize * 0.5f;
for (int i = 0; i < mLines.Count; ++i)
{
LineSegement seg = mLines[i];
int total = seg.PointCount;
for (int j = mMinModifyIndex; j < total; ++j)
{
Vector4 magPoint = seg.getPoint(j);
if (magPoint.w == 0f)
continue;
Vector3 point = (Vector3)magPoint;
halfSize = mPointSize * 0.5f;
ProcesssPoint(ref magPoint, ref halfSize);
Vector3 p1 = point + new Vector3(-halfSize, -halfSize, 0f);
Vector3 p2 = point + new Vector3(halfSize, -halfSize, 0f);
Vector3 p3 = point + new Vector3(-halfSize, halfSize, 0f);
Vector3 p4 = point + new Vector3(halfSize, halfSize, 0f);
Vector2 uv1 = new Vector2(0f, 0f);
Vector2 uv2 = new Vector2(1f, 0f);
Vector2 uv3 = new Vector2(0f, 1f);
Vector2 uv4 = new Vector2(1f, 1f);
UIVertex v1 = ChartCommon.CreateVertex(p1, uv1, z);
UIVertex v2 = ChartCommon.CreateVertex(p2, uv2, z);
UIVertex v3 = ChartCommon.CreateVertex(p3, uv3, z);
UIVertex v4 = ChartCommon.CreateVertex(p4, uv4, z);
yield return v1;
yield return v2;
yield return v3;
yield return v4;
}
}
}
Vector2 TransformUv(Vector2 uv)
{
return uv;
//if (mUvRect.HasValue == false)
// return uv;
//Rect r = mUvRect.Value;
//float x = r.x + uv.x * r.width;
//float y = r.y + uv.y * r.height;
//return new Vector2(x, y);
}
IEnumerable<UIVertex> getFillVeritces()
{
if (mLines == null)
yield break;
float z = 0f;
for (int i = 0; i < mLines.Count; ++i)
{
LineSegement seg = mLines[i];
int totalLines = seg.LineCount;
for (int j = mMinModifyIndex; j < totalLines; ++j)
{
Vector3 from;
Vector3 to;
seg.GetLine(j,out from, out to);
Vector2 toTrim = to;
Vector2 fromTrim = from;
TrimItem(mFillRect.xMin, mFillRect.yMin, mFillRect.xMax, mFillRect.yMin, true, false, ref fromTrim, ref toTrim);
to = new Vector3(toTrim.x, toTrim.y, to.z);
from = new Vector3(fromTrim.x, fromTrim.y, from.z);
Vector3 fromBottom = from;
Vector3 toBottom = to;
fromBottom.y = mFillRect.yMin;
toBottom.y = mFillRect.yMin;
float fromV = 1f;
float toV = 1f;
if (mStretchY == false)
{
fromV = Mathf.Abs((from.y - mFillRect.yMin) / mFillRect.height);
toV = Mathf.Abs((to.y - mFillRect.yMin) / mFillRect.height);
}
float fromU = ((from.x - mFillRect.xMin) / mFillRect.width);
float toU = ((to.x - mFillRect.xMin) / mFillRect.width);
Vector2 uv1 = TransformUv(new Vector2(fromU, fromV));
Vector2 uv2 = TransformUv(new Vector2(toU, toV));
Vector2 uv3 = TransformUv(new Vector2(fromU, 0f));
Vector2 uv4 = TransformUv(new Vector2(toU, 0f));
UIVertex v1 = ChartCommon.CreateVertex(from, uv1, z);
UIVertex v2 = ChartCommon.CreateVertex(to, uv2, z);
UIVertex v3 = ChartCommon.CreateVertex(fromBottom, uv3, z);
UIVertex v4 = ChartCommon.CreateVertex(toBottom, uv4, z);
yield return v1;
yield return v2;
yield return v3;
yield return v4;
}
}
}
IEnumerable<UIVertex> getLineVertices()
{
if (mLines == null)
yield break;
float halfThickness = Thickness * 0.5f;
float z = 0f;
for (int i = 0; i < mLines.Count; ++i)
{
LineSegement seg = mLines[i];
int totalLines = seg.LineCount;
Line? peek = null;
Line? prev = null;
float tileUv = 0f;
float totalUv = 0f;
for (int j = mMinModifyIndex; j < totalLines; ++j)
totalUv += (float)seg.GetLineMag(j);
for (int j = mMinModifyIndex; j < totalLines; ++j)
{
Line line;
bool hasNext = j + 1 < totalLines;
if (peek.HasValue)
line = peek.Value;
else
line = seg.GetLine(j, halfThickness, prev.HasValue, hasNext);
peek = null;
if (j + 1 < totalLines)
peek = seg.GetLine(j + 1, halfThickness, true, j + 2 < totalLines);
Vector3 p1 = line.P1;
Vector3 p2 = line.P2;
Vector3 p3 = line.P3;
Vector3 p4 = line.P4;
Vector2 uv1 = new Vector2(tileUv * Tiling, 0f);
Vector2 uv2 = new Vector2(tileUv * Tiling, 1f);
tileUv += line.Mag / totalUv;
Vector2 uv3 = new Vector2(tileUv * Tiling, 0f);
Vector2 uv4 = new Vector2(tileUv * Tiling, 1f);
UIVertex v1 = ChartCommon.CreateVertex(p1, uv1, z);
UIVertex v2 = ChartCommon.CreateVertex(p2, uv2, z);
UIVertex v3 = ChartCommon.CreateVertex(p3, uv3, z);
UIVertex v4 = ChartCommon.CreateVertex(p4, uv4, z);
yield return v1;
yield return v2;
yield return v3;
yield return v4;
if (peek.HasValue)
{
float myZ = z + 0.2f;
Vector3 a1, a2;
GetSide(line.To, line.Dir, line.Normal, halfThickness * 0.5f, halfThickness * 0.6f, v3.position.z, out a1, out a2);
yield return v3;
yield return v4;
yield return ChartCommon.CreateVertex(a1, v3.uv0, myZ);
yield return ChartCommon.CreateVertex(a2, v4.uv0, myZ);
}
if (prev.HasValue)
{
float myZ = z + 0.2f;
Vector3 a1, a2;
GetSide(line.From, -line.Dir, line.Normal, halfThickness * 0.5f, halfThickness * 0.6f, v1.position.z, out a1, out a2);
yield return ChartCommon.CreateVertex(a1, v1.uv0, myZ);
yield return ChartCommon.CreateVertex(a2, v2.uv0, myZ);
yield return v1;
yield return v2;
}
//z -= 0.05f;
prev = line;
}
}
}
IEnumerable<UIVertex> getVerices()
{
IEnumerable<UIVertex> vertices;
if (mPointRender)
vertices = getDotVeritces();
else
{
if (mFillRender)
vertices = getFillVeritces();
else
vertices = getLineVertices();
}
return vertices;
}
Mesh mVHMesh;
List<Vector3> mPositions = new List<Vector3>();
List<Vector2> mUvs = new List<Vector2>();
List<int> mTringles = new List<int>();
void WriteTo<T> (List<T> list,int index,T val)
{
if (list.Count == index)
list.Add(val);
else
list[index] = val;
}
protected override void UpdateGeometry()
{
if (EnableOptimization == false)
{
mMinModifyIndex = 0;
base.UpdateGeometry();
return;
}
if (mVHMesh == null)
mVHMesh = new Mesh();
if (mMinModifyIndex == int.MaxValue)
mMinModifyIndex = mPositions.Count;
if (mMinModifyIndex <= 0)
{
mMinModifyIndex = 0;
mPositions.Clear();
mUvs.Clear();
mTringles.Clear();
foreach (UIVertex v in getVerices())
{
mPositions.Add(v.position);
mUvs.Add(v.uv0);
}
int quads = (mPositions.Count / 4) ;
int baseIndex = 0;
for (int i = 0; i < quads; i++)
{
mTringles.Add(baseIndex);
mTringles.Add(baseIndex + 1);
mTringles.Add(baseIndex + 3);
mTringles.Add(baseIndex + 3);
mTringles.Add(baseIndex + 2);
mTringles.Add(baseIndex);
baseIndex += 4;
}
}
else
{
// Debug.Log("adding");
int total = 0;
int index = mMinModifyIndex;
foreach (UIVertex v in getVerices())
{
// Debug.Log("extra");
WriteTo<Vector3>(mPositions, index, v.position);
WriteTo<Vector2>(mUvs, index, v.uv0);
total++;
}
int quads = total / 4;
for (int i = 0; i < quads; i++)
{
// Debug.Log("extra tring");
mTringles.Add(0);
mTringles.Add(1);
mTringles.Add(3);
mTringles.Add(3);
mTringles.Add(2);
mTringles.Add(0);
}
}
mVHMesh.Clear();
mVHMesh.SetVertices(mPositions);
mVHMesh.SetUVs(0, mUvs);
mVHMesh.SetTriangles(mTringles, 0);
//at the end
mMinModifyIndex = int.MaxValue;
GetComponent<CanvasRenderer>().SetMesh(mVHMesh);
}
#if (!UNITY_5_2_0) && (!UNITY_5_2_1)
protected override void OnPopulateMesh(VertexHelper vh)
{
base.OnPopulateMesh(vh);
vh.Clear();
int vPos = 0;
foreach (UIVertex v in getVerices())
{
mTmpVerts[vPos++] = v;
if (vPos == 4)
{
UIVertex tmp = mTmpVerts[2];
mTmpVerts[2] = mTmpVerts[3];
mTmpVerts[3] = tmp;
vPos = 0;
vh.AddUIVertexQuad(mTmpVerts);
}
}
}
#endif
#pragma warning disable 0672
#if !UNITY_2017_1_OR_NEWER
/// <summary>
/// A chart mesh used for populating the
/// </summary>
WorldSpaceChartMesh mMesh = null;
protected override void OnPopulateMesh(Mesh m)
{
if (mMesh == null)
mMesh = new WorldSpaceChartMesh(1);
else
mMesh.Clear();
int vPos = 0;
mMinModifyIndex = 0; // not supported here
foreach (UIVertex v in getVerices())
{
mTmpVerts[vPos++] = v;
if (vPos == 4)
{
vPos = 0;
mMesh.AddQuad(mTmpVerts[0], mTmpVerts[1], mTmpVerts[2], mTmpVerts[3]);
}
}
mMesh.ApplyToMesh(m);
}
#endif
void PickLine(Vector3 mouse, out int segment, out int line, out object selectionData)
{
float minDist = Mathf.Infinity;
segment = -1;
line = -1;
selectionData = null;
if (mLines == null)
{
return;
}
for (int i = 0; i < mLines.Count; ++i)
{
LineSegement seg = mLines[i];
int total = seg.LineCount;
for (int j = 0; j < total; ++j)
{
Vector3 from;
Vector3 to;
seg.GetLine(j, out from, out to);
float dist = ChartCommon.SegmentPointSqrDistance(from,to,mouse);
if (dist < minDist)
{
minDist = dist;
segment = i;
line = j;
}
}
}
float thresh = (Thickness + Sensitivity);
if ((ViewRect.HasValue && !ViewRect.Value.Contains(mouse)) || minDist > thresh * thresh)
{
segment = -1;
line = -1;
}
}
void PickDot(Vector3 mouse, out int segment , out int point, out object selectionData)
{
float minDist = Mathf.Infinity;
segment = -1;
point = -1;
selectionData = null;
if (mLines == null)
return;
float mag = mPointSize;
for (int i = 0; i < mLines.Count; ++i)
{
LineSegement seg = mLines[i];
int total = seg.PointCount;
for (int j = 0; j < total; ++j)
{
Vector4 p = seg.getPoint(j);
if (p.w == 0f)
continue;
float dist = (mouse - ((Vector3)p)).sqrMagnitude;
if(dist < minDist)
{
mag = p.w;
if (mag < 0f)
mag = mPointSize;
minDist = dist;
segment = i;
point = j;
}
}
}
float thresh = mag + Sensitivity;
if ((ViewRect.HasValue && !ViewRect.Value.Contains(mouse)) || minDist > thresh * thresh)
{
segment = -1;
point = -1;
}
}
void TrimItem(float x1, float y1 ,float x2,float y2,bool xAxis,bool oposite, ref Vector2 from, ref Vector2 to)
{
Vector2 seg1 = new Vector2(x1, y1);
Vector2 seg2 = new Vector2(x2, y2);
Vector2 point;
if (ChartCommon.SegmentIntersection(seg1, seg2, from, to, out point) == false)
return;
if(xAxis)
{
if ((to.y > from.y) ^ oposite)
from = point;
else
to = point;
return;
}
if ((to.x > from.x) ^ oposite)
from = point;
else
to = point;
}
void TrimLine(Rect r,ref Vector2 from, ref Vector2 to)
{
TrimItem(r.xMin, r.yMin, r.xMax, r.yMin, true, false, ref from, ref to);
TrimItem(r.xMin, r.yMax, r.xMax, r.yMax, true, true, ref from, ref to);
TrimItem(r.xMin, r.yMin, r.xMin, r.yMax, false, false, ref from, ref to);
TrimItem(r.xMax, r.yMin, r.xMax, r.yMax, false, true, ref from, ref to);
}
#pragma warning restore 0672
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2032ee9ddbfbfb74da66a209b05d468d
timeCreated: 1478616684
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,81 @@
#define Graph_And_Chart_PRO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace ChartAndGraph
{
public class CanvasLinesHover : MaskableGraphic
{
float mHalfThickness;
UIVertex[] mTmpVerts = new UIVertex[4];
public void Init(float thickness)
{
mHalfThickness = thickness * 0.5f;
SetAllDirty();
Rebuild(CanvasUpdate.PreRender);
}
IEnumerable<UIVertex> getVerices()
{
UIVertex v = new UIVertex();
v.position = new Vector3(-mHalfThickness, -mHalfThickness);
v.uv0 = new Vector2(0f, 0f);
yield return v;
v.position = new Vector3(-mHalfThickness, mHalfThickness);
v.uv0 = new Vector2(0f, 1f);
yield return v;
v.position = new Vector3(mHalfThickness, mHalfThickness);
v.uv0 = new Vector2(1f, 1f);
yield return v;
v.position = new Vector3(mHalfThickness, -mHalfThickness);
v.uv0 = new Vector2(1f, 0f);
yield return v;
}
#if (!UNITY_5_2_0) && (!UNITY_5_2_1)
protected override void OnPopulateMesh(VertexHelper vh)
{
base.OnPopulateMesh(vh);
vh.Clear();
int vPos = 0;
foreach (UIVertex v in getVerices())
{
mTmpVerts[vPos++] = v;
if (vPos == 4)
{
UIVertex tmp = mTmpVerts[2];
mTmpVerts[2] = mTmpVerts[3];
mTmpVerts[3] = tmp;
vPos = 0;
vh.AddUIVertexQuad(mTmpVerts);
}
}
}
#endif
#pragma warning disable 0672
#if !UNITY_2017_1_OR_NEWER
protected override void OnPopulateMesh(Mesh m)
{
WorldSpaceChartMesh mesh = new WorldSpaceChartMesh(1);
int vPos = 0;
foreach (UIVertex v in getVerices())
{
mTmpVerts[vPos++] = v;
if (vPos == 4)
{
vPos = 0;
mesh.AddQuad(mTmpVerts[0], mTmpVerts[1], mTmpVerts[2], mTmpVerts[3]);
}
}
mesh.ApplyToMesh(m);
}
#endif
#pragma warning restore 0672
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8ad8c18b253fae84fabcbfd13925da24
timeCreated: 1491578596
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,450 @@
#define Graph_And_Chart_PRO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace ChartAndGraph
{
/// <summary>
/// Base class for canvas series graphic object. Contains functionallity that enable creating a hovering prefab where the mouse is located , and event handling
/// The main idea behind this implementation is that it allows to create a mesh once. Then in order to create the hover effect , an object is placed on top of that mesh , those leaving the mesh unmodified.
/// </summary>
public abstract class EventHandlingGraphic : MaskableGraphic
{
/// <summary>
/// The prefab for the hover effect
/// </summary>
ChartItemEffect mHoverPrefab;
/// <summary>
/// The currently hovered item
/// </summary>
ChartItemEffect mCurrentHover = null;
/// <summary>
/// All the objects that have a hover animation displayed. Only one object can be hovered. But the hover animation has a fade effect so multiple objects can display the animation at the same time
/// </summary>
List<ChartItemEffect> mHoverObjectes = new List<ChartItemEffect>();
/// <summary>
/// when hover prefabs end their life time. They are pooled in this list
/// </summary>
List<ChartItemEffect> mHoverFreeObjects = new List<ChartItemEffect>();
protected Rect? ViewRect = null;
protected Rect? mUvRect;
/// <summary>
/// if true , mouse events will be proccedd even if the mouse hasn't moved
/// </summary>
bool mForceMouseMove = false;
bool mIsMouseIn = false;
Transform mHoverTransform;
int mPickedIndex = -1, mPickedType = -1;
object mPickedData = null;
Vector2 mLastMousePosition = new Vector2();
GraphicRaycaster mCaster;
/// <summary>
/// When the graphic data is clliped , this representes the first index being displayed in the graph. Thus the [Data Index] = refrenceIndex + [Graphic Index]
/// </summary>
protected int refrenceIndex { get; private set; }
public void SetRefrenceIndex(int index)
{
refrenceIndex = index;
}
public float Sensitivity
{
get
{
float sensitivity = 10f;
if(mControl == null)
mControl = GetComponent<SensitivityControl>();
if (mControl != null)
sensitivity = mControl.Sensitivity;
return sensitivity;
}
}
/// <summary>
/// call this to free all hover objects and reset event input. This is useful mostly when the entire data of the graphic has been replaced and old animations should be discarded
/// </summary>
public void ClearEvents()
{
mIsMouseIn = false;
for (int i = 0; i < mHoverObjectes.Count; i++)
{
mHoverObjectes[i].gameObject.SetActive(false);
mHoverFreeObjects.Add(mHoverObjectes[i]);
}
mHoverObjectes.Clear();
if (mCurrentHover != null)
{
mCurrentHover.gameObject.SetActive(false);
mHoverFreeObjects.Add(mCurrentHover);
mCurrentHover = null;
}
mPickedIndex = mPickedType = -1;
mPickedData = null;
}
/// <summary>
/// The minimum for the bounding rect of the graphic data
/// </summary>
protected abstract Vector2 Min { get; }
/// <summary>
/// The maximum for the bounding rect of the graphic data
/// </summary>
protected abstract Vector2 Max { get; }
/// <summary>
/// Contains properties to define mouse sensitivy for event handling
/// </summary>
SensitivityControl mControl;
/// <summary>
///
/// </summary>
/// <param name="index"></param>
/// <param name="type"></param>
/// <param name="data"></param>
/// <param name="position"></param>
public delegate void GraphicEvent(int index, int type, object data, Vector2 position);
/// <summary>
/// The selected index is hovered about the specified point
/// </summary>
public event GraphicEvent Hover;
/// <summary>
/// The selected index is clicked about the specified point
/// </summary>
public event GraphicEvent Click;
/// <summary>
/// The currently hovered and selected objects are no longer selected or hovered.
/// </summary>
public event Action Leave;
/// <summary>
/// Sets the viewing data for the graphic.
/// </summary>
/// <param name="r">the canvas rect bounds</param>
/// <param name="uvRect">the uv bounds of the currently cliped data</param>
public void SetViewRect(Rect r, Rect uvRect)
{
ViewRect = r;
mUvRect = uvRect;
}
public void HoverTransform(Transform t)
{
mHoverTransform = t;
}
public void SetHoverPrefab(ChartItemEffect prefab)
{
mHoverPrefab = prefab;
}
/// <summary>
/// call this when the graphic view has changed , this will recheck the mouse position and input for new events.
/// This is useful for features such as realtime graph. Where new objects can be picked even if the mouse is in one place only
/// </summary>
public void RefreshInputs()
{
mForceMouseMove = true;
}
protected virtual void Update()
{
if(HandleMouseMove(mForceMouseMove)) // handle mouse move
mForceMouseMove = false; // mouse events are already handled in the above line, so we set this to false
}
/// <summary>
/// Set up the position of the hover prefab
/// </summary>
/// <param name="hover">the hovered gameobject passesd using it's ChartItemEffect component</param>
/// <param name="index">index of the data item which is hovered</param>
protected abstract void SetUpHoverObject(ChartItemEffect hover, int index,int type,object selectionData);
/// <summary>
/// returns the threshold at which the mouse is considered in the area of the graphic. This threshold is realtive to the graphic rectangle.
/// </summary>
protected abstract float MouseInThreshold { get; }
/// <summary>
/// override this in a derived class to pick a data item based on the mouse position.
/// </summary>
/// <param name="mouse">mouse position</param>
/// <param name="pickedIndex">first index of the selected item, or -1 for no selection</param>
/// <param name="pickedType">type of the selected item, or -1 for no selection</param>
protected abstract void Pick(Vector3 mouse, out int pickedIndex, out int pickedType,out object SelectionData);
/// <summary>
/// This method is called during update if RefreshInputs has been called in the preivous frame. When the canvas graph mesh updates , all active hover effects are repositioned to match their new position.
/// </summary>
protected void SetUpAllHoverObjects()
{
if (mHoverObjectes == null)
return;
for (int i = 0; i < mHoverObjectes.Count; i++)
SetUpHoverObject(mHoverObjectes[i]);
}
/// <summary>
/// utillity method that can be used in derived classes in order to
/// </summary>
/// <param name="hover"></param>
/// <param name="index"></param>
/// <param name="type"></param>
/// <param name="rect"></param>
protected void SetupHoverObjectToRect(ChartItemEffect hover, int index, int type,Rect rect)
{
if (hover == null)
return;
RectTransform transform = hover.GetComponent<RectTransform>();
transform.localScale = new Vector3(1f, 1f, 1f);
transform.sizeDelta = new Vector2(rect.width, rect.height);
transform.anchoredPosition3D = new Vector3(rect.center.x, rect.center.y, 0f);
}
/// <summary>
/// Shuts down a hover prefab effect
/// </summary>
/// <param name="hover"></param>
void TriggerOut(ChartItemEffect hover)
{
hover.TriggerOut(true);
ChartMaterialController control = hover.GetComponent<ChartMaterialController>();
if (control)
control.TriggerOff();
}
/// <summary>
/// Shuts down a hover prefab effect
/// </summary>
/// <param name="hover"></param>
void TriggerIn(ChartItemEffect hover)
{
hover.TriggerIn(false);
ChartMaterialController control = hover.GetComponent<ChartMaterialController>();
if (control)
control.TriggerOn();
}
/// <summary>
/// Takes a hover object out of the pool , or creates a new one if the pool is empty
/// </summary>
/// <param name="index">the index of the data item , this index will be assigned to the returned object</param>
/// <returns></returns>
public ChartItemEffect LockHoverObject(int index,int type,object selectionData)
{
int count = mHoverFreeObjects.Count;
ChartItemEffect effect = null;
if (count > 0)
{
effect = mHoverFreeObjects[count - 1];
mHoverFreeObjects.RemoveAt(count - 1);
}
else
{
if (mHoverPrefab == null)
return null;
GameObject obj = GameObject.Instantiate(mHoverPrefab.gameObject);
MaskableGraphic g = obj.GetComponent<MaskableGraphic>();
if (g != null)
g.maskable = false;
ChartCommon.EnsureComponent<ChartItem>(obj);
Transform parent = transform;
if (mHoverTransform != null)
parent = mHoverTransform;
obj.transform.SetParent(parent);
effect = obj.GetComponent<ChartItemEffect>();
effect.Deactivate += Effect_Deactivate;
}
effect.ItemType = type;
effect.ItemIndex = index;
effect.ItemData = selectionData;
mHoverObjectes.Add(effect);
return effect;
}
/// <summary>
/// called when an effect is done animating. Reclaims the object into the hover object pool
/// </summary>
/// <param name="obj"></param>
private void Effect_Deactivate(ChartItemEffect obj)
{
obj.gameObject.SetActive(false);
mHoverObjectes.Remove(obj);
mHoverFreeObjects.Add(obj);
}
/// <summary>
/// helped method that extract the index from the hover object and then call SetUpHoverObject overload with index
/// </summary>
/// <param name="hover"></param>
void SetUpHoverObject(ChartItemEffect hover)
{
if (hover == null)
return;
int pickedIndex = hover.ItemIndex - refrenceIndex;
if (pickedIndex < 0)
return;
SetUpHoverObject(hover, pickedIndex,hover.ItemType,hover.ItemData);
}
/// <summary>
/// Handle mouse events
/// </summary>
/// <param name="mouse"></param>
/// <param name="leave"></param>
/// <param name="force"></param>
void DoMouse(Vector3 mouse, bool leave, bool force)
{
int prevI = mPickedIndex;
int prevJ = mPickedType;
if (leave)
{
mPickedIndex = -1;
mPickedType = -1;
mPickedData = null;
}
else
Pick(mouse, out mPickedIndex, out mPickedType,out mPickedData);
if (prevI != mPickedIndex || prevJ != mPickedType)
{
if (mCurrentHover != null)
{
TriggerOut(mCurrentHover);
if (mPickedIndex == -1 && mPickedType == -1)
{
if (Leave != null)
Leave();
}
mCurrentHover = null;
}
if (mPickedIndex != -1 && mPickedType != -1)
{
mCurrentHover = LockHoverObject(mPickedIndex,mPickedType, mPickedData);
if (mCurrentHover == null)
return;
if (Hover != null)
Hover(mPickedIndex,mPickedType, mPickedData, mouse);
mCurrentHover.gameObject.SetActive(true);
SetUpHoverObject(mCurrentHover);
TriggerIn(mCurrentHover);
}
}
}
/// <summary>
/// Handles a mouse move event.
/// </summary>
/// <param name="force"></param>
public bool HandleMouseMove(bool force)
{
mCaster = GetComponentInParent<GraphicRaycaster>();
if (mCaster == null)
return false;
Vector2 mousePos;
// on some machines and settings , it seems that Input.mousePosition returns invalid coordinates, the following checks make sure that the mouse position is valid
Vector3 checkMousePos = Input.mousePosition;
var pointer = GetComponentInParent<CustomChartPointer>();
if (pointer != null)
checkMousePos = pointer.ScreenPosition;
Camera checkCamera = mCaster.eventCamera;
if (checkCamera == null)
checkCamera = Camera.main;
if (checkCamera == null)
return false;
if (checkCamera.pixelRect.Contains(checkMousePos) == false)
return false;
RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, checkMousePos, mCaster.eventCamera, out mousePos);
//DoMouse(mousePos, false);
float thresh = MouseInThreshold;
if (force)
{
SetUpAllHoverObjects();
}
Vector2 min = Min;
Vector2 max = Max;
if (mousePos.x < min.x - thresh || mousePos.y < min.y - thresh || mousePos.x > max.x + thresh || mousePos.y > max.y + thresh)
{
if (mIsMouseIn)
{
mIsMouseIn = false;
DoMouse(mousePos, true, force);
}
return true;
}
else
{
if (mIsMouseIn == false)
{
mIsMouseIn = true;
mLastMousePosition = mousePos;
DoMouse(mousePos, false, force);
}
else
{
if (((mLastMousePosition - mousePos).sqrMagnitude > 1) || force)
{
mLastMousePosition = mousePos;
DoMouse(mousePos, false, force);
}
}
}
if ((pointer== null && Input.GetMouseButtonDown(0)) || (pointer !=null && pointer.Click))
{
HandleMouseDown();
}
return true;
}
/// <summary>
/// Handles mouse down. This method will fire the mouse click event if an item is clicked
/// </summary>
private void HandleMouseDown()
{
if (mPickedIndex != -1 && mPickedType != -1)
{
if (Click != null)
{
mCaster = GetComponentInParent<GraphicRaycaster>();
if (mCaster == null)
return;
Vector3 checkMousePos = Input.mousePosition;
var pointer = GetComponentInParent<CustomChartPointer>();
if (pointer != null)
checkMousePos = pointer.ScreenPosition;
Vector2 mousePos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, checkMousePos, mCaster.eventCamera, out mousePos);
Vector3 pos = transform.InverseTransformPoint(mousePos);
Click(mPickedIndex,mPickedType, mPickedData, pos);
}
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1e1aa8b268945b540b4b0ef7768aca09
timeCreated: 1503250771
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
#define Graph_And_Chart_PRO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace ChartAndGraph
{
public class SensitivityControl : MonoBehaviour
{
public float Sensitivity = 10f;
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c09bf2e39f5ebfa409a492a80a326b3a
timeCreated: 1491921896
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: