#define Graph_And_Chart_PRO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace ChartAndGraph
{
///
/// Base class for all axis charts that are scrollable. This class implements some important functionallity that is common to all scrollable charts
///
public abstract class ScrollableAxisChart : AxisChart
{
private GameObject mMask;
private Vector2? mLastPosition;
private GraphicRaycaster mCaster;
protected Dictionary> mTexts = new Dictionary>();
protected HashSet mActiveTexts = new HashSet();
// bool mMaskCreated = false;
public ScrollableChartData ScrollableData
{
get { return (ScrollableChartData)DataLink; }
}
[HideInInspector]
[SerializeField]
protected bool autoScrollHorizontally = false;
public bool AutoScrollHorizontally
{
get { return autoScrollHorizontally; }
set
{
autoScrollHorizontally = value;
InvalidateRealtime();
}
}
protected override void Update()
{
base.Update();
if(IsCanvas)
{
// handle mouse events for canvas charts
HandleMouseDrag();
}
}
public override void GenerateRealtime()
{
base.GenerateRealtime();
if(SupportRealtimeGeneration)
{
GenerateAxis(false);
}
}
protected override double GetScrollOffset(int axis)
{
if (Scrollable == false)
return 0f;
if ((autoScrollHorizontally && axis == 0) || (autoScrollVertically && axis == 1))
{
double sMax = ScrollableData.GetMaxValue(axis, false);
//float sMin = (float)((IInternalGraphData)Data).GetMinValue(axis,false);
double dMax = ScrollableData.GetMaxValue(axis, true);
//float dMin = (float)((IInternalGraphData)Data).GetMinValue(axis, true);
double scrolling = dMax - sMax;
if (axis == 1)
{
if (ScrollableData.VerticalViewSize < 0)
scrolling += ScrollableData.VerticalViewSize;
verticalScrolling = scrolling;
}
else if (axis == 0)
{
if (ScrollableData.HorizontalViewSize < 0)
scrolling += ScrollableData.HorizontalViewSize;
horizontalScrolling = scrolling;
}
return scrolling;
}
if (axis == 1)
return verticalScrolling;
else if (axis == 0)
return horizontalScrolling;
return base.GetScrollOffset(axis);
}
protected void AddBillboardText(string cat, int index, BillboardText text)
{
if (text.UIText != null)
ChartCommon.MakeMaskable(text.UIText, true);
Dictionary addTo;
if (mTexts.TryGetValue(cat, out addTo) == false)
{
addTo = new Dictionary(ChartCommon.DefaultIntComparer);
mTexts.Add(cat, addTo);
}
addTo.Add(index, text);
}
protected void ClearBillboard()
{
mTexts.Clear();
}
protected void ClearBillboardCategories()
{
foreach (Dictionary d in mTexts.Values)
d.Clear();
}
[HideInInspector]
// [SerializeField]
[NonSerialized]
protected bool scrollable = true;
public bool Scrollable
{
get { return true; }
set
{
scrollable = value;
Invalidate();
}
}
[HideInInspector]
[SerializeField]
protected double horizontalScrolling = 0f;
public double HorizontalScrolling
{
get { return horizontalScrolling; }
set
{
horizontalScrolling = value;
InvalidateRealtime();
}
}
[HideInInspector]
[SerializeField]
protected bool autoScrollVertically = false;
public bool AutoScrollVertically
{
get { return autoScrollVertically; }
set
{
autoScrollVertically = value;
InvalidateRealtime();
}
}
[HideInInspector]
[SerializeField]
protected double verticalScrolling = 0f;
public double VerticalScrolling
{
get { return verticalScrolling; }
set
{
verticalScrolling = value;
InvalidateRealtime();
}
}
[SerializeField]
private bool raycastTarget = true;
public bool RaycastTarget
{
get { return raycastTarget; ; }
set
{
raycastTarget = value;
Invalidate();
}
}
public bool PointToClient(Vector3 worldPoint, out double x, out DateTime y)
{
double dx, dy;
bool res = PointToClient(worldPoint, out dx, out dy);
x = dx;
y = ChartDateUtility.ValueToDate(dy);
return res;
}
public bool PointToClient(Vector3 worldPoint, out DateTime x, out DateTime y)
{
double dx, dy;
bool res = PointToClient(worldPoint, out dx, out dy);
x = ChartDateUtility.ValueToDate(dx);
y = ChartDateUtility.ValueToDate(dy);
return res;
}
public bool PointToClient(Vector3 worldPoint, out DateTime x, out double y)
{
double dx, dy;
bool res = PointToClient(worldPoint, out dx, out dy);
x = ChartDateUtility.ValueToDate(dx);
y = dy;
return res;
}
///
/// transform a point from axis units into world space. returns true on success and false on failure (failure should never happen for this implementation)
///
/// the resulting world space point
/// x coordinate in axis units
/// y coodinate in axis units
/// for 3d chart specifing a catgory will return a point with the proper depth setting
///
public bool PointToWorldSpace(out Vector3 result,DateTime x, double y, string category = null)
{
return PointToWorldSpace(out result,ChartDateUtility.DateToValue(x), y, category);
}
///
/// transform a point from axis units into world space. returns true on success and false on failure (failure should never happen for this implementation)
///
/// the resulting world space point
/// x coordinate in axis units
/// y coodinate in axis units
/// for 3d chart specifing a catgory will return a point with the proper depth setting
///
public bool PointToWorldSpace(out Vector3 result,double x, DateTime y, string category = null)
{
return PointToWorldSpace(out result,x, ChartDateUtility.DateToValue(y), category);
}
///
/// transform a point from axis units into world space. returns true on success and false on failure (failure should never happen for this implementation)
///
/// the resulting world space point
/// x coordinate in axis units
/// y coodinate in axis units
/// for 3d chart specifing a catgory will return a point with the proper depth setting
///
public bool PointToWorldSpace(out Vector3 result,DateTime x, DateTime y, string category = null)
{
return PointToWorldSpace(out result,ChartDateUtility.DateToValue(x), ChartDateUtility.DateToValue(y), category);
}
protected abstract double GetCategoryDepth(string category);
///
/// internal method used bu the mixed series chart to set this chart to default settings
///
internal abstract void SetAsMixedSeries();
private DoubleVector3 PointToNormalized(double x, double y)
{
double minX, minY, maxX, maxY, xScroll, yScroll, xSize, ySize, xOut;
GetScrollParams(out minX, out minY, out maxX, out maxY, out xScroll, out yScroll, out xSize, out ySize, out xOut);
double resX = ((x - xScroll) / xSize);
double resY = ((y - yScroll) / ySize);
return new DoubleVector3(resX, resY,0.0);
}
private DoubleVector3 NormalizedToPoint(double x, double y)
{
double minX, minY, maxX, maxY, xScroll, yScroll, xSize, ySize, xOut;
GetScrollParams(out minX, out minY, out maxX, out maxY, out xScroll, out yScroll, out xSize, out ySize, out xOut);
double resX = x * xSize + xScroll;
double resY = y * ySize + yScroll;
return new DoubleVector3(resX, resY, 0.0);
}
private Vector3 PointShift
{
get {
if(IsCanvas)
return CanvasFitOffset;
return new Vector3();
}
}
protected override Vector3 CanvasFitOffset { get { return new Vector3(0.5f, 0.5f, 0f); } }
///
/// transform a point from axis units into world space. returns true on success and false on failure (failure should never happen for this implementation)
///
/// the resulting world space point
/// x coordinate in axis units
/// y coodinate in axis units
/// for 3d chart specifing a catgory will return a point with the proper depth setting
///
public bool PointToWorldSpace(out Vector3 result,double x,double y, string category = null)
{
Vector3 fit = PointShift;
double minX, minY, maxX, maxY, xScroll, yScroll, xSize, ySize, xOut;
GetScrollParams(out minX, out minY, out maxX, out maxY, out xScroll, out yScroll, out xSize, out ySize, out xOut);
double resX = (((x - xScroll) / xSize)- fit.x) * ((IInternalUse)this).InternalTotalWidth;
double resY = (((y-yScroll) / ySize) - fit.y) * ((IInternalUse)this).InternalTotalHeight;
double resZ = 0.0;
if(category != null)
resZ = GetCategoryDepth(category);
Transform t = transform;
if (FixPosition != null)
t = FixPosition.transform;
result = t.TransformPoint(new Vector3((float)resX, (float)resY, (float)resZ));
return true;
}
///
/// gets the mouse position on the graph in axis units. returns true if the mouse is in bounds of the chart , false otherwise
///
///
///
///
public bool MouseToClient(out double x,out double y)
{
Vector2 mousePos;
x = y = 0.0;
mCaster = GetComponentInParent();
if (mCaster == null)
return false;
RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, Input.mousePosition, mCaster.eventCamera, out mousePos);
if(FixPosition != null)
{
mousePos = transform.TransformPoint(mousePos);
mousePos = FixPosition.transform.InverseTransformPoint(mousePos);
}
mousePos.x /= TotalWidth;
mousePos.y /= TotalHeight;
Vector3 fit = PointShift;
mousePos.x += fit.x;
mousePos.y += fit.y;
bool mouseIn = RectTransformUtility.RectangleContainsScreenPoint(transform as RectTransform, Input.mousePosition);
DoubleVector3 res = NormalizedToPoint(mousePos.x, mousePos.y);
x = res.x;
y = res.y;
return mouseIn;
}
///
/// transform a point from world space into axis units. for best use , provide a point which is place on the canvas plane
///
///
///
///
///
public bool PointToClient(Vector3 worldPoint, out double x, out double y)
{
Vector3 fit = PointShift;
double minX, minY, maxX, maxY, xScroll, yScroll, xSize, ySize, xOut;
GetScrollParams(out minX, out minY, out maxX, out maxY, out xScroll, out yScroll, out xSize, out ySize, out xOut);
Transform t = transform;
if (FixPosition != null)
t = FixPosition.transform;
worldPoint = t.InverseTransformPoint(worldPoint);
x = xScroll + xSize * ((((double)worldPoint.x) / ((IInternalUse)this).InternalTotalWidth) + fit.x);
y = yScroll + ySize * ((((double)worldPoint.y) / ((IInternalUse)this).InternalTotalHeight) + fit.y);
return true;
}
///
/// Trims a rect in axis units into the visible portion of it in axis units. returns false if the parameter rect is completely out of view , true otherwise
///
///
///
///
public bool TrimRect(DoubleRect rect, out DoubleRect trimmed)
{
DoubleVector3 min = rect.min;
DoubleVector3 max = rect.max;
trimmed = new DoubleRect();
min = PointToNormalized(min.x, min.y);
max = PointToNormalized(max.x, max.y);
if (min.x > 1f || min.y > 1f)
return false;
if (max.x < 0f || max.y < 0f)
return false;
double minX = ChartCommon.Clamp(Math.Min(min.x, max.x));
double minY = ChartCommon.Clamp(Math.Min(min.y, max.y));
double maxX = ChartCommon.Clamp(Math.Max(min.x, max.x));
double maxY = ChartCommon.Clamp(Math.Max(min.y, max.y));
min = NormalizedToPoint(minX, minY);
max = NormalizedToPoint(maxX, maxY);
trimmed = new DoubleRect(min.x,min.y, max.x -min.x,max.y - min.y);
return true;
}
///
/// returns true if the axis unity rect is visible on the chart, even if it is only partially visible. false otherwise
///
///
///
public bool IsRectVisible(DoubleRect rect)
{
DoubleVector3 min = rect.min;
DoubleVector3 max = rect.max;
min = PointToNormalized(min.x, min.y);
max = PointToNormalized(max.x, max.y);
if (min.x > 1f || min.y > 1f)
return false;
if (max.x < 0f || max.y < 0f)
return false;
return true;
}
///
/// tranforms an axis unit rect into a recttransform. The rect transform must have a parent assigned to it. Also the chart and the rectTransform must share a common ancestor canvas
///
/// The rect tranform to which the result is assigned to
///
///
///
public bool RectToCanvas(RectTransform assignTo, DoubleRect rect, string catgeory = null)
{
DoubleVector3 min = rect.min;
DoubleVector3 max = rect.max;
Vector3 worldMin, worldMax;
if(PointToWorldSpace(out worldMin, min.x, min.y, catgeory) == false)
return false;
if (PointToWorldSpace(out worldMax, max.x, max.y, catgeory) == false)
return false;
Transform parent = assignTo.parent;
if (parent == null)
return false;
worldMin = parent.transform.InverseTransformPoint(worldMin);
worldMax = parent.transform.InverseTransformPoint(worldMax);
float minX = Math.Min(worldMin.x, worldMax.x);
float minY = Math.Min(worldMin.y, worldMax.y);
float sizeX = Math.Max(worldMin.x, worldMax.x) - minX;
float sizeY = Math.Max(worldMin.y, worldMax.y) - minY;
assignTo.anchorMin = new Vector2(0.5f, 0.5f);
assignTo.anchorMax = new Vector2(0.5f, 0.5f);
assignTo.pivot = new Vector2(0f, 0f);
assignTo.anchoredPosition = new Vector2(minX, minY);
assignTo.sizeDelta = new Vector2(sizeX, sizeY);
return true;
}
protected abstract float GetScrollingRange(int axis);
public UnityEvent MousePan;
[SerializeField]
private bool horizontalPanning;
public bool HorizontalPanning
{
get { return horizontalPanning; ; }
set
{
horizontalPanning = value;
Invalidate();
}
}
[SerializeField]
private bool verticalPanning;
public bool VerticalPanning
{
get { return verticalPanning; ; }
set
{
verticalPanning = value;
Invalidate();
}
}
bool mStencilMask = false;
public bool StencilMask
{
get { return mStencilMask; }
set
{
mStencilMask = value;
Invalidate();
}
}
protected GameObject CreateRectMask(Rect viewRect)
{
//GameObject obj = Instantiate(Resources.Load("Chart And Graph/RectMask") as GameObject);
GameObject obj = ChartCommon.CreateCanvasChartItem();
obj.name = "rectMask2D";;
ChartCommon.HideObject(obj, hideHierarchy);
if (mStencilMask)
{
var mask = obj.AddComponent();
mask.showMaskGraphic = false;
var image = obj.AddComponent();
image.maskable = false;
image.raycastTarget = false;
}
else
obj.AddComponent();
//obj.AddComponent();
obj.transform.SetParent(transform, false);
var rectTransform = obj.GetComponent();
rectTransform.anchorMin = new Vector2(0f, 0f);
rectTransform.anchorMax = new Vector2(0f, 0f);
rectTransform.pivot = new Vector2(0f, 1f);
rectTransform.sizeDelta = viewRect.size;
rectTransform.anchoredPosition = new Vector2(0f, viewRect.size.y);
mMask = obj;
return obj;
}
protected override void ClearChart()
{
base.ClearChart();
}
protected string StringFromAxisFormat(DoubleVector3 val, AxisBase axis,bool isX)
{
double itemVal = isX ? val.x : val.y;
if (axis == null)
return ChartAdancedSettings.Instance.FormatFractionDigits(2, itemVal);
return StringFromAxisFormat(val, axis, axis.MainDivisions.FractionDigits, isX);
}
protected string StringFromAxisFormat(DoubleVector3 val, AxisBase axis, int fractionDigits,bool isX)
{
val.z = 0;
double itemVal = isX ? val.x : val.y;
var dic = VectorValueToStringMap;
KeyValuePair res;
// Debug.Log("try get " + val + " count is " + dic.Count);
if (dic.TryGetValue(val, out res))
{
if (isX && res.Key != null)
return res.Key;
if (isX == false && res.Value != null)
return res.Value;
}
if (axis == null)
return ChartAdancedSettings.Instance.FormatFractionDigits(fractionDigits, itemVal, CustomNumberFormat);
string toSet = "";
if (axis.Format == AxisFormat.Number)
toSet = ChartAdancedSettings.Instance.FormatFractionDigits(fractionDigits, itemVal, CustomNumberFormat);
else
{
DateTime date = ChartDateUtility.ValueToDate(itemVal);
if (axis.Format == AxisFormat.DateTime)
toSet = ChartDateUtility.DateToDateTimeString(date,CustomDateTimeFormat);
else
{
if (axis.Format == AxisFormat.Date)
toSet = ChartDateUtility.DateToDateString(date);
else
toSet = ChartDateUtility.DateToTimeString(date);
}
}
return toSet;
}
protected void GetScrollParams(out double minX,out double minY,out double maxX,out double maxY,out double xScroll,out double yScroll,out double xSize,out double ySize,out double xOut)
{
minX = ScrollableData.GetMinValue(0, false);
minY = ScrollableData.GetMinValue(1, false);
maxX = ScrollableData.GetMaxValue(0, false);
maxY = ScrollableData.GetMaxValue(1, false);
xScroll = GetScrollOffset(0) + minX;
yScroll = GetScrollOffset(1) + minY;
xSize = maxX - minX;
ySize = maxY - minY;
xOut = xScroll + xSize;
}
private void MouseDraged(Vector2 delta)
{
bool drag = false;
if (VerticalPanning)
{
float range = GetScrollingRange(1);
VerticalScrolling -= (delta.y / TotalHeightLink) * range;
if (Mathf.Abs(delta.y) > 1f)
drag = true;
}
if (HorizontalPanning)
{
float range = GetScrollingRange(0);
HorizontalScrolling -= (delta.x / TotalWidthLink) * range;
if (Mathf.Abs(delta.x) > 1f)
drag = true;
}
if (drag)
{
InvalidateRealtime();
if (MousePan != null)
MousePan.Invoke();
}
}
private void HandleMouseDrag()
{
if (verticalPanning == false && horizontalPanning == false)
return;
mCaster = GetComponentInParent();
if (mCaster == null)
return;
if (Application.isPlaying == false)
return;
Vector2 mousePos;
var chart = GetComponentInParent();
var pointer = ChartCommon.EnsureComponent(chart.gameObject);
if (pointer == null)
return;
Vector3 checkMousePos = pointer.ScreenPosition;
RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, checkMousePos, mCaster.eventCamera, out mousePos);
var cam = mCaster.eventCamera;
bool mouseIn = RectTransformUtility.RectangleContainsScreenPoint(transform as RectTransform, checkMousePos, cam);
if (( (pointer!=null && pointer.IsMouseDown)) && mouseIn)
{
if (mLastPosition.HasValue)
{
Vector2 delta = mousePos - mLastPosition.Value;
MouseDraged(delta);
}
mLastPosition = mousePos;
}
else
mLastPosition = null;
}
protected void TriggerActiveTextsOut()
{
foreach (BillboardText t in mActiveTexts)
{
if (t == null)
continue;
if (t.UIText == null)
continue;
foreach (ChartItemEffect effect in t.UIText.GetComponents())
{
if (effect != null)
effect.TriggerOut(false);
}
}
mActiveTexts.Clear();
}
protected void AddActiveText(BillboardText b)
{
mActiveTexts.Add(b);
}
protected void SelectActiveText(BillboardText b)
{
TriggerActiveTextsOut();
GameObject tx = b.UIText;
if (tx != null)
{
ChartItemEvents e = tx.GetComponent();
if (e != null)
{
e.OnMouseHover.Invoke(e.gameObject);
AddActiveText(b);
}
}
}
}
}