Skip to content

Instantly share code, notes, and snippets.

@minakhan01
Created March 26, 2018 16:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save minakhan01/68224bf41e51ef9a73b889a73b92a183 to your computer and use it in GitHub Desktop.
Save minakhan01/68224bf41e51ef9a73b889a73b92a183 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace ChartAndGraph
{
public class GraphChart : GraphChartBase, ICanvas
{
HashSet<string> mOccupiedCateogies = new HashSet<string>();
Dictionary<string, CategoryObject> mCategoryObjects = new Dictionary<string, CategoryObject>();
List<DoubleVector3> mTmpData = new List<DoubleVector3>();
List<DoubleVector4> mClipped = new List<DoubleVector4>();
List<Vector4> mTransformed = new List<Vector4>();
List<int> mTmpToRemove = new List<int>();
private bool SupressRealtimeGeneration = false;
private StringBuilder mRealtimeStringBuilder = new StringBuilder();
/// <summary>
/// The seperation between the axis and the chart bars.
/// </summary>
[SerializeField]
private bool fitToContainer = false;
public bool FitToContainer
{
get { return fitToContainer; }
set
{
fitToContainer = value;
OnPropertyUpdated();
}
}
public override bool IsCanvas
{
get
{
return true;
}
}
public override bool SupportRealtimeGeneration
{
get
{
return true;
}
}
class CategoryObject
{
public CanvasChartMesh mItemLabels;
public CanvasLines mLines;
public CanvasLines mDots;
public CanvasLines mFill;
public Dictionary<int, string> mCahced = new Dictionary<int, string>();
}
private CanvasLines CreateDataObject(GraphData.CategoryData data, GameObject rectMask)
{
GameObject obj = new GameObject("Lines", typeof(RectTransform));
ChartCommon.HideObject(obj, hideHierarchy);
obj.AddComponent<ChartItem>();
RectTransform t = obj.GetComponent<RectTransform>();
obj.AddComponent<CanvasRenderer>();
// Canvas c = obj.AddComponent<Canvas>();
//c.pixelPerfect = false;
//obj.AddComponent<GraphicRaycaster>();
CanvasLines lines = obj.AddComponent<CanvasLines>();
lines.maskable = true;
t.SetParent(rectMask.transform, false);
t.localScale = new Vector3(1f, 1f, 1f);
t.anchorMin = new Vector2(0f, 0f);
t.anchorMax = new Vector2(0f, 0f);
t.anchoredPosition = Vector3.zero;
t.localRotation = Quaternion.identity;
return lines;
}
protected override void Update()
{
base.Update();
}
protected override void ClearChart()
{
base.ClearChart();
ClearBillboard();
mActiveTexts.Clear();
mCategoryObjects.Clear();
}
protected override double GetCategoryDepth(string category)
{
return 0.0;
}
double AddRadius(double radius, double mag, double min, double max)
{
double size = max - min;
double factor = size / mag;
return factor * radius;
}
public override void GenerateRealtime()
{
if (SupressRealtimeGeneration)
return;
base.GenerateRealtime();
double minX = ((IInternalGraphData)Data).GetMinValue(0, false);
double minY = ((IInternalGraphData)Data).GetMinValue(1, false);
double maxX = ((IInternalGraphData)Data).GetMaxValue(0, false);
double maxY = ((IInternalGraphData)Data).GetMaxValue(1, false);
double xScroll = GetScrollOffset(0);
double yScroll = GetScrollOffset(1);
double xSize = maxX - minX;
double ySize = maxY - minY;
double xOut = minX + xScroll + xSize;
double yOut = minY + yScroll + ySize;
DoubleVector3 min = new DoubleVector3(xScroll + minX, yScroll + minY);
DoubleVector3 max = new DoubleVector3(xOut, yOut);
Rect viewRect = new Rect(0f, 0f, widthRatio, heightRatio);
Transform parentT = transform;
if (mFixPosition != null)
parentT = mFixPosition.transform;
ClearBillboardCategories();
foreach (GraphData.CategoryData data in ((IInternalGraphData)Data).Categories)
{
CategoryObject obj = null;
if (mCategoryObjects.TryGetValue(data.Name, out obj) == false)
continue;
mClipped.Clear();
mTmpData.Clear();
mTmpData.AddRange(data.getPoints());
Rect uv;// = new Rect(0f, 0f, 1f, 1f);
int refrenceIndex = ClipPoints(mTmpData, mClipped, out uv);
//mClipped.AddRange(mTmpData);
TransformPoints(mClipped, mTransformed, viewRect, min, max);
mTmpToRemove.Clear();
int range = refrenceIndex + mClipped.Count;
foreach (int key in obj.mCahced.Keys)
{
if (key < refrenceIndex || key > range)
mTmpToRemove.Add(key);
}
for (int i = 0; i < mTmpToRemove.Count; i++)
obj.mCahced.Remove(mTmpToRemove[i]);
obj.mCahced.Remove(mTmpData.Count - 1); // never store the last point cache , it might be intepolating by the realtime feature
if (mTmpData.Count == 0)
continue;
if (mItemLabels != null && mItemLabels.isActiveAndEnabled && obj.mItemLabels != null)
{
Rect textRect = viewRect;
textRect.xMin -= 1f;
textRect.yMin -= 1f;
textRect.xMax += 1f;
textRect.yMax += 1f;
CanvasChartMesh m = obj.mItemLabels;
m.Clear();
for (int i = 0; i < mTransformed.Count; i++)
{
if (mTransformed[i].w == 0.0)
continue;
Vector3 labelPos = ((Vector3)mTransformed[i]) + new Vector3(mItemLabels.Location.Breadth, mItemLabels.Seperation, mItemLabels.Location.Depth);
if (mItemLabels.Alignment == ChartLabelAlignment.Base)
labelPos.y -= (float)mTransformed[i].y;
if (textRect.Contains((Vector2)(mTransformed[i])) == false)
continue;
string toSet = null;
int pointIndex = i + refrenceIndex;
if (obj.mCahced.TryGetValue(pointIndex, out toSet) == false)
{
DoubleVector3 pointValue = mTmpData[i + refrenceIndex];
string xFormat = StringFromAxisFormat(pointValue.x, mHorizontalAxis, mItemLabels.FractionDigits);
string yFormat = StringFromAxisFormat(pointValue.y, mVerticalAxis, mItemLabels.FractionDigits);
FormatItem(mRealtimeStringBuilder, xFormat, yFormat);
string formatted = mRealtimeStringBuilder.ToString();
mItemLabels.TextFormat.Format(mRealtimeStringBuilder, formatted, data.Name, "");
toSet = mRealtimeStringBuilder.ToString();
obj.mCahced[pointIndex] = toSet;
}
BillboardText billboard = m.AddText(this, mItemLabels.TextPrefab, parentT, mItemLabels.FontSize, mItemLabels.FontSharpness, toSet, labelPos.x, labelPos.y, labelPos.z, 0f, null);
AddBillboardText(data.Name, i + refrenceIndex, billboard);
}
m.DestoryRecycled();
if (m.TextObjects != null)
{
foreach (BillboardText text in m.TextObjects)
{
((IInternalUse)this).InternalTextController.AddText(text);
}
}
}
if (obj.mDots != null)
{
Rect pickRect = viewRect;
float halfSize = (float)(data.PointSize * 0.5f);
pickRect.xMin -= halfSize;
pickRect.yMin -= halfSize;
pickRect.xMax += halfSize;
pickRect.yMax += halfSize;
obj.mDots.SetViewRect(pickRect, uv);
obj.mDots.ModifyLines(mTransformed);
obj.mDots.SetRefrenceIndex(refrenceIndex);
}
if (obj.mLines != null)
{
float tiling = 1f;
if (data.LineTiling.EnableTiling == true && data.LineTiling.TileFactor > 0f)
{
float length = 0f;
for (int i = 1; i < mTransformed.Count; i++)
length += (mTransformed[i - 1] - mTransformed[i]).magnitude;
tiling = length / data.LineTiling.TileFactor;
}
if (tiling <= 0.0001f)
tiling = 1f;
obj.mLines.Tiling = tiling;
obj.mLines.SetViewRect(viewRect, uv);
obj.mLines.ModifyLines(mTransformed);
obj.mLines.SetRefrenceIndex(refrenceIndex);
}
if (obj.mFill != null)
{
obj.mFill.SetViewRect(viewRect, uv);
obj.mFill.ModifyLines(mTransformed);
obj.mFill.SetRefrenceIndex(refrenceIndex);
}
}
}
protected override bool FitAspectCanvas
{
get
{
return true;
}
}
public override void InternalGenerateChart()
{
if (gameObject.activeInHierarchy == false)
return;
base.InternalGenerateChart();
if (FitToContainer)
{
RectTransform trans = GetComponent<RectTransform>();
widthRatio = trans.rect.width;
heightRatio = trans.rect.height;
}
ClearChart();
if (Data == null)
return;
GenerateAxis(true);
double minX = ((IInternalGraphData)Data).GetMinValue(0, false);
double minY = ((IInternalGraphData)Data).GetMinValue(1, false);
double maxX = ((IInternalGraphData)Data).GetMaxValue(0, false);
double maxY = ((IInternalGraphData)Data).GetMaxValue(1, false);
double xScroll = GetScrollOffset(0);
double yScroll = GetScrollOffset(1);
double xSize = maxX - minX;
double ySize = maxY - minY;
double xOut = minX + xScroll + xSize;
double yOut = minY + yScroll + ySize;
DoubleVector3 min = new DoubleVector3(xScroll + minX, yScroll + minY);
DoubleVector3 max = new DoubleVector3(xOut, yOut);
Rect viewRect = new Rect(0f, 0f, widthRatio, heightRatio);
int index = 0;
int total = ((IInternalGraphData)Data).TotalCategories + 1;
bool edit = false;
ClearBillboard();
mActiveTexts.Clear();
GameObject mask = CreateRectMask(viewRect);
foreach (GraphData.CategoryData data in ((IInternalGraphData)Data).Categories)
{
mClipped.Clear();
DoubleVector3[] points = data.getPoints().ToArray();
Rect uv;
int refrenceIndex = ClipPoints(points, mClipped, out uv);
TransformPoints(mClipped, mTransformed, viewRect, min, max);
if (points.Length == 0 && ChartCommon.IsInEditMode)
{
edit = true;
int tmpIndex = total - 1 - index;
float y1 = (((float)tmpIndex) / (float)total);
float y2 = (((float)tmpIndex + 1) / (float)total);
DoubleVector3 pos1 = ChartCommon.interpolateInRect(viewRect, new DoubleVector3(0f, y1, -1f)).ToDoubleVector3();
DoubleVector3 pos2 = ChartCommon.interpolateInRect(viewRect, new DoubleVector3(0.5f, y2, -1f)).ToDoubleVector3();
DoubleVector3 pos3 = ChartCommon.interpolateInRect(viewRect, new DoubleVector3(1f, y1, -1f)).ToDoubleVector3();
points = new DoubleVector3[] { pos1, pos2, pos3 };
mTransformed.AddRange(points.Select(x => (Vector4)x.ToVector3()));
index++;
}
List<CanvasLines.LineSegement> list = new List<CanvasLines.LineSegement>();
list.Add(new CanvasLines.LineSegement(mTransformed));
CategoryObject categoryObj = new CategoryObject();
if (data.FillMaterial != null)
{
CanvasLines fill = CreateDataObject(data, mask);
fill.material = data.FillMaterial;
fill.SetRefrenceIndex(refrenceIndex);
fill.SetLines(list);
fill.SetViewRect(viewRect, uv);
fill.MakeFillRender(viewRect, data.StetchFill);
categoryObj.mFill = fill;
}
if (data.LineMaterial != null)
{
CanvasLines lines = CreateDataObject(data, mask);
float tiling = 1f;
if (data.LineTiling.EnableTiling == true && data.LineTiling.TileFactor > 0f)
{
float length = 0f;
for (int i = 1; i < mTransformed.Count; i++)
length += (mTransformed[i - 1] - mTransformed[i]).magnitude;
tiling = length / data.LineTiling.TileFactor;
}
if (tiling <= 0.0001f)
tiling = 1f;
lines.SetViewRect(viewRect, uv);
lines.Thickness = (float)data.LineThickness;
lines.Tiling = tiling;
lines.SetRefrenceIndex(refrenceIndex);
lines.material = data.LineMaterial;
lines.SetHoverPrefab(data.LineHoverPrefab);
lines.SetLines(list);
categoryObj.mLines = lines;
}
//if (data.PointMaterial != null)
//{
CanvasLines dots = CreateDataObject(data, mask);
categoryObj.mDots = dots;
dots.material = data.PointMaterial;
dots.SetLines(list);
Rect pickRect = viewRect;
float halfSize = (float)data.PointSize * 0.5f;
pickRect.xMin -= halfSize;
pickRect.yMin -= halfSize;
pickRect.xMax += halfSize;
pickRect.yMax += halfSize;
dots.SetViewRect(pickRect, uv);
dots.SetRefrenceIndex(refrenceIndex);
dots.SetHoverPrefab(data.PointHoverPrefab);
if (data.PointMaterial != null)
dots.MakePointRender((float)data.PointSize);
else
dots.MakePointRender(0f);
if (mItemLabels != null && mItemLabels.isActiveAndEnabled)
{
CanvasChartMesh m = new CanvasChartMesh(true);
m.RecycleText = true;
categoryObj.mItemLabels = m;
Rect textRect = viewRect;
textRect.xMin -= 1f;
textRect.yMin -= 1f;
textRect.xMax += 1f;
textRect.yMax += 1f;
for (int i = 0; i < mTransformed.Count; i++)
{
if (mTransformed[i].w == 0f)
continue;
Vector2 pointValue = mTransformed[i];
if (textRect.Contains(pointValue) == false)
continue;
if (edit == false)
pointValue = Data.GetPoint(data.Name, i + refrenceIndex).ToVector2();
string xFormat = StringFromAxisFormat(pointValue.x, mHorizontalAxis, mItemLabels.FractionDigits);
string yFormat = StringFromAxisFormat(pointValue.y, mVerticalAxis, mItemLabels.FractionDigits);
Vector3 labelPos = ((Vector3)mTransformed[i]) + new Vector3(mItemLabels.Location.Breadth, mItemLabels.Seperation, mItemLabels.Location.Depth);
if (mItemLabels.Alignment == ChartLabelAlignment.Base)
labelPos.y -= mTransformed[i].y;
FormatItem(mRealtimeStringBuilder, xFormat, yFormat);
string formatted = mRealtimeStringBuilder.ToString();
string toSet = mItemLabels.TextFormat.Format(formatted, data.Name, "");
BillboardText billboard = m.AddText(this, mItemLabels.TextPrefab, transform, mItemLabels.FontSize, mItemLabels.FontSharpness, toSet, labelPos.x, labelPos.y, labelPos.z, 0f, null);
// BillboardText billboard = ChartCommon.CreateBillboardText(null,mItemLabels.TextPrefab, transform, toSet, labelPos.x, labelPos.y, labelPos.z, 0f, null, hideHierarchy, mItemLabels.FontSize, mItemLabels.FontSharpness);
TextController.AddText(billboard);
AddBillboardText(data.Name, i + refrenceIndex, billboard);
}
}
string catName = data.Name;
dots.Hover += (idx,t,d, pos) => { Dots_Hover(catName, idx, pos); };
dots.Click += (idx,t,d, pos) => { Dots_Click(catName, idx, pos); };
dots.Leave += () => { Dots_Leave(catName); };
mCategoryObjects[catName] = categoryObj;
}
}
private void Dots_Leave(string category)
{
TriggerActiveTextsOut();
OnItemLeave(new GraphEventArgs(0,Vector3.zero,new DoubleVector2(0.0,0.0),-1f,category,"",""));
}
private void Dots_Click(string category,int idx, Vector2 pos)
{
DoubleVector3 point = Data.GetPoint(category, idx);
Dictionary<int, BillboardText> catgoryTexts;
BillboardText b;
if (mTexts.TryGetValue(category, out catgoryTexts))
{
if (catgoryTexts.TryGetValue(idx, out b))
SelectActiveText(b);
}
string xString = StringFromAxisFormat(point.x, mHorizontalAxis);
string yString = StringFromAxisFormat(point.y, mVerticalAxis);
OnItemSelected(new GraphEventArgs(idx,pos, point.ToDoubleVector2(),(float)point.z, category,xString,yString));
}
private void Dots_Hover(string category, int idx, Vector2 pos)
{
DoubleVector3 point = Data.GetPoint(category, idx);
Dictionary<int, BillboardText> catgoryTexts;
BillboardText b;
if (mTexts.TryGetValue(category, out catgoryTexts))
{
if (catgoryTexts.TryGetValue(idx, out b))
SelectActiveText(b);
}
string xString = StringFromAxisFormat(point.x, mHorizontalAxis);
string yString = StringFromAxisFormat(point.y, mVerticalAxis);
OnItemHoverted(new GraphEventArgs(idx,pos, point.ToDoubleVector2(),(float)point.z, category, xString, yString));
}
protected override void OnItemHoverted(object userData)
{
base.OnItemHoverted(userData);
var args = userData as GraphEventArgs;
mOccupiedCateogies.Add(args.Category);
}
protected override void OnItemSelected(object userData)
{
base.OnItemSelected(userData);
var args = userData as GraphEventArgs;
mOccupiedCateogies.Add(args.Category);
}
protected override void OnItemLeave(object userData)
{
GraphEventArgs args = userData as GraphEventArgs;
if (args == null)
return;
string category = args.Category;
mOccupiedCateogies.Remove(category);
mOccupiedCateogies.RemoveWhere(x => !Data.HasCategory(x));
if (mOccupiedCateogies.Count == 0)
{
if (NonHovered != null)
NonHovered.Invoke();
}
}
internal override void SetAsMixedSeries()
{
throw new NotImplementedException();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment