1203 lines
40 KiB
C#
1203 lines
40 KiB
C#
#define Graph_And_Chart_PRO
|
|
using ChartAndGraph.Axis;
|
|
using ChartAndGraph.DataSource;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.UI;
|
|
|
|
namespace ChartAndGraph
|
|
{
|
|
/// <summary>
|
|
/// this is a base class for all chart types
|
|
/// </summary>
|
|
[Serializable]
|
|
public abstract class AnyChart : MonoBehaviour, IInternalUse, ISerializationCallbackReceiver
|
|
{
|
|
private Vector2 mLastSetSize = Vector2.zero;
|
|
private bool mGenerating = false;
|
|
private Dictionary<double, string> mHorizontalValueToStringMap = new Dictionary<double, string>(ChartCommon.DefaultDoubleComparer);
|
|
private Dictionary<double, string> mVerticalValueToStringMap = new Dictionary<double, string>(ChartCommon.DefaultDoubleComparer);
|
|
private Dictionary<DoubleVector3, KeyValuePair<string, string>> mVectorToValueMap = new Dictionary<DoubleVector3, KeyValuePair<string, string>>(ChartCommon.DefaultDoubleVector3Comparer);
|
|
private HashSet<double> mHorizontalCustomAxis = new HashSet<double>(), mVerticalCustomAxis = new HashSet<double>();
|
|
private HashSet<double> mHorizontalCustomAxisSubDivision = new HashSet<double>(), mVerticalCustomAxisSubdivision = new HashSet<double>();
|
|
protected GameObject mFixPosition = null;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected GameObject mPreviewObject;
|
|
|
|
public void RemoveHorizontalAxisDivision(double pos)
|
|
{
|
|
mHorizontalCustomAxis.Remove(pos);
|
|
mHorizontalCustomAxisSubDivision.Remove(pos);
|
|
Invalidate();
|
|
}
|
|
public void AddHorizontalAxisDivision(double pos, bool subDivision = false)
|
|
{
|
|
if (subDivision)
|
|
mHorizontalCustomAxisSubDivision.Add(pos);
|
|
else
|
|
mHorizontalCustomAxis.Add(pos);
|
|
Invalidate();
|
|
}
|
|
public void ClearHorizontalCustomDivisions()
|
|
{
|
|
mHorizontalCustomAxis.Clear();
|
|
mHorizontalCustomAxisSubDivision.Clear();
|
|
Invalidate();
|
|
}
|
|
public void RemoveVerticalAxisDivision(double pos)
|
|
{
|
|
mVerticalCustomAxis.Remove(pos);
|
|
mVerticalCustomAxisSubdivision.Remove(pos);
|
|
Invalidate();
|
|
}
|
|
public void AddVerticalAxisDivision(double pos, bool subDivision = false)
|
|
{
|
|
if (subDivision)
|
|
mVerticalCustomAxisSubdivision.Add(pos);
|
|
else
|
|
mVerticalCustomAxis.Add(pos);
|
|
Invalidate();
|
|
}
|
|
public void ClearVerticalCustomDivisions()
|
|
{
|
|
mVerticalCustomAxis.Clear();
|
|
mVerticalCustomAxisSubdivision.Clear();
|
|
Invalidate();
|
|
}
|
|
|
|
HashSet<double> IInternalUse.VerticalCustomAxis { get { return mVerticalCustomAxis; } }
|
|
HashSet<double> IInternalUse.HorizontalCustomAxis { get { return mHorizontalCustomAxis; } }
|
|
|
|
HashSet<double> IInternalUse.VerticalCustomAxisSubDivision { get { return mVerticalCustomAxisSubdivision; } }
|
|
HashSet<double> IInternalUse.HorizontalCustomAxisSubDivision { get { return mHorizontalCustomAxisSubDivision; } }
|
|
|
|
|
|
Func<double, int, string> customNumberFormat;
|
|
|
|
public Func<double, int, string> CustomNumberFormat
|
|
{
|
|
get { return customNumberFormat; }
|
|
set
|
|
{
|
|
customNumberFormat = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
Func<DateTime,string> customDateTimeFormat;
|
|
public Func<DateTime, string> CustomDateTimeFormat
|
|
{
|
|
|
|
get { return customDateTimeFormat; }
|
|
set
|
|
{
|
|
customDateTimeFormat = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
public Dictionary<DoubleVector3, KeyValuePair<string, string>> VectorValueToStringMap
|
|
{
|
|
get { return mVectorToValueMap; }
|
|
}
|
|
|
|
public Dictionary<double, string> VerticalValueToStringMap
|
|
{
|
|
get { return mVerticalValueToStringMap; }
|
|
}
|
|
|
|
public Dictionary<double, string> HorizontalValueToStringMap
|
|
{
|
|
get { return mHorizontalValueToStringMap; }
|
|
}
|
|
|
|
protected virtual Camera TextCameraLink
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
protected virtual float TextIdleDistanceLink
|
|
{
|
|
get { return 20f; }
|
|
}
|
|
|
|
public UnityEvent OnRedraw = new UnityEvent();
|
|
|
|
[SerializeField]
|
|
private bool keepOrthoSize = false;
|
|
|
|
public bool KeepOrthoSize
|
|
{
|
|
get { return keepOrthoSize; }
|
|
set
|
|
{
|
|
KeepOrthoSize = value;
|
|
GenerateChart();
|
|
}
|
|
}
|
|
[SerializeField]
|
|
private bool paperEffectText = false;
|
|
public bool PaperEffectText
|
|
{
|
|
get { return paperEffectText; }
|
|
set
|
|
{
|
|
paperEffectText = value;
|
|
GenerateChart();
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private bool vRSpaceText = false;
|
|
|
|
public bool VRSpaceText
|
|
{
|
|
get { return vRSpaceText; }
|
|
set
|
|
{
|
|
vRSpaceText = value;
|
|
GenerateChart();
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float vRSpaceScale = 0.1f;
|
|
|
|
public float VRSpaceScale
|
|
{
|
|
get { return vRSpaceScale; }
|
|
set
|
|
{
|
|
vRSpaceScale = value;
|
|
GenerateChart();
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private bool maintainLabelSize = false;
|
|
|
|
public bool MaintainLabelSize
|
|
{
|
|
get { return maintainLabelSize; }
|
|
set
|
|
{
|
|
maintainLabelSize = value;
|
|
GenerateChart();
|
|
}
|
|
}
|
|
|
|
HashSet<object> mHovered = new HashSet<object>();
|
|
protected bool IsUnderCanvas { get; private set; }
|
|
protected bool CanvasChanged { get; private set; }
|
|
protected ItemLabels mItemLabels;
|
|
protected VerticalAxis mVerticalAxis;
|
|
protected HorizontalAxis mHorizontalAxis;
|
|
protected CategoryLabels mCategoryLabels;
|
|
protected GroupLabels mGroupLabels;
|
|
protected GameObject VerticalMainDevisions;
|
|
protected GameObject VerticalSubDevisions;
|
|
protected GameObject VerticalCustomDevisions;
|
|
protected GameObject VerticalCustomDevisionsSub;
|
|
protected GameObject HorizontalMainDevisions;
|
|
protected GameObject HorizontalSubDevisions;
|
|
protected GameObject HorizontalCustomDivisions;
|
|
protected GameObject HorizontalCustomDivisionsSub;
|
|
|
|
bool mGenerateOnNextUpdate = false;
|
|
bool mRealtimeOnNextUpdate = false;
|
|
|
|
private void AxisChanged(object sender, EventArgs e)
|
|
{
|
|
GenerateChart();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is called every time a property of the chart has changed. This would ussually trigger data validation and chart rebuilding
|
|
/// </summary>
|
|
protected virtual void OnPropertyUpdated()
|
|
{
|
|
ValidateProperties();
|
|
}
|
|
|
|
private void Labels_OnDataChanged(object sender, EventArgs e)
|
|
{
|
|
OnLabelSettingsSet();
|
|
}
|
|
|
|
private void Labels_OnDataUpdate(object sender, EventArgs e)
|
|
{
|
|
OnLabelSettingChanged();
|
|
}
|
|
|
|
protected virtual void OnLabelSettingChanged()
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
/// <summary>
|
|
/// override this in a dervied class to set the total depth of the chart, this is used by 3d axis components to determine automatic size
|
|
/// </summary>
|
|
protected abstract float TotalDepthLink
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// override this in a dervied class to set the total height of the chart, this is used by axis components and by size fitting for canvas
|
|
/// </summary>
|
|
protected abstract float TotalHeightLink
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// override this in a dervied class to set the total width of the chart, this is used by axis components and by size fitting for canvas
|
|
/// </summary>
|
|
protected abstract float TotalWidthLink
|
|
{
|
|
get;
|
|
}
|
|
|
|
protected abstract IChartData DataLink
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// true if the class type is meant for use with canvas
|
|
/// </summary>
|
|
public abstract bool IsCanvas
|
|
{
|
|
get;
|
|
}
|
|
|
|
public float TotalWidth
|
|
{
|
|
get { return TotalWidthLink; }
|
|
}
|
|
|
|
public float TotalHeight
|
|
{
|
|
get { return TotalHeightLink; }
|
|
}
|
|
|
|
public float TotalDepth
|
|
{
|
|
get { return TotalDepthLink; }
|
|
}
|
|
protected virtual double GetScrollOffset(int axis)
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
protected bool hideHierarchy = true;
|
|
|
|
/// <summary>
|
|
/// Keeps all the chart elements hidden from the editor and the inspector
|
|
/// </summary>
|
|
bool IInternalUse.HideHierarchy { get { return hideHierarchy; } }
|
|
|
|
// the axis generated for the chart
|
|
List<IAxisGenerator> mAxis = new List<IAxisGenerator>();
|
|
|
|
protected void FixAxisLabels()
|
|
{
|
|
for (int i = 0; i < mAxis.Count; ++i)
|
|
mAxis[i].FixLabels(this);
|
|
}
|
|
|
|
protected virtual void OnAxisValuesChanged()
|
|
{
|
|
|
|
}
|
|
|
|
protected virtual void OnLabelSettingsSet()
|
|
{
|
|
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
if (gameObject.activeInHierarchy == false)
|
|
return;
|
|
DoCanvas(true);
|
|
EnsureTextController();
|
|
}
|
|
|
|
/// <summary>
|
|
/// this method will detect changes in the parent canvas , if the canvas changed the chart will be regenerated
|
|
/// </summary>
|
|
/// <param name="start"></param>
|
|
void DoCanvas(bool start)
|
|
{
|
|
Canvas parentCanvas = GetComponentInParent<Canvas>();
|
|
bool prev = IsUnderCanvas;
|
|
IsUnderCanvas = parentCanvas != null;
|
|
if (IsUnderCanvas == false)
|
|
return;
|
|
if (start == false)
|
|
{
|
|
if (IsUnderCanvas != prev)
|
|
{
|
|
CanvasChanged = true;
|
|
GenerateChart();
|
|
CanvasChanged = false;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// generates a text controller object for this chart
|
|
/// </summary>
|
|
void CreateTextController()
|
|
{
|
|
GameObject obj = new GameObject("textController", typeof(RectTransform)); ;
|
|
ChartCommon.HideObject(obj, hideHierarchy);
|
|
obj.transform.SetParent(transform);
|
|
obj.transform.localScale = new Vector3(1f, 1f, 1f);
|
|
obj.transform.localRotation = Quaternion.identity;
|
|
obj.transform.localPosition = Vector3.zero;
|
|
mTextController = obj.AddComponent<TextController>();
|
|
mTextController.mParent = this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method ensures that the chart has a TextController object under it's hirarchy , The TextController object is responsible for text placing and billboarding
|
|
/// </summary>
|
|
protected void EnsureTextController()
|
|
{
|
|
|
|
if (mTextController != null)
|
|
{
|
|
mTextController.GlobalRotation = -FitZRotationCanvas;
|
|
return;
|
|
}
|
|
CreateTextController();
|
|
mTextController.GlobalRotation = -FitZRotationCanvas;
|
|
}
|
|
protected bool Invalidating
|
|
{
|
|
get { return mGenerateOnNextUpdate; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// call this to invalidate a chart in reatime , this will update only some parts of the chart depending on implementation.
|
|
/// This method will only work if SupportRealtimeGeneration returns true
|
|
/// </summary>
|
|
protected virtual void InvalidateRealtime()
|
|
{
|
|
if (mGenerating)
|
|
return;
|
|
mRealtimeOnNextUpdate = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// call this to invalidate the chart and make the chart rebuild itself completly in the next update call
|
|
/// </summary>
|
|
public virtual void Invalidate()
|
|
{
|
|
if (mGenerating)
|
|
return;
|
|
mGenerateOnNextUpdate = true;
|
|
}
|
|
|
|
public void Awake()
|
|
{
|
|
ClearChart();
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
if (gameObject.activeInHierarchy == false)
|
|
return;
|
|
|
|
DoCanvas(false);
|
|
|
|
if (mGenerateOnNextUpdate == true)
|
|
{// complete redraw preceeds realtime
|
|
GenerateChart();
|
|
}
|
|
else if (mRealtimeOnNextUpdate)
|
|
{
|
|
GenerateRealtime();
|
|
InvokeOnRedraw();
|
|
}
|
|
|
|
mGenerateOnNextUpdate = false; // graph is invalidated set this back to false
|
|
mRealtimeOnNextUpdate = false;
|
|
DataLink.Update();
|
|
|
|
if (IsCanvas)
|
|
{
|
|
RectTransform trans = GetComponent<RectTransform>();
|
|
if (trans != null && trans.hasChanged)
|
|
{
|
|
if (mLastSetSize != trans.rect.size)
|
|
{
|
|
Invalidate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void LateUpdate()
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// used internally, do not call this method
|
|
/// </summary>
|
|
protected virtual void OnValidate()
|
|
{
|
|
if (gameObject.activeInHierarchy == false)
|
|
return;
|
|
ValidateProperties();
|
|
DoCanvas(true);
|
|
|
|
// EnsureTextController();
|
|
}
|
|
|
|
|
|
private TextController mTextController;
|
|
protected TextController TextController
|
|
{
|
|
get
|
|
{
|
|
EnsureTextController();
|
|
return mTextController;
|
|
}
|
|
private set
|
|
{
|
|
mTextController = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method returns information used for legened creation
|
|
/// </summary>
|
|
protected abstract LegenedData LegendInfo { get; }
|
|
|
|
/// <summary>
|
|
/// Given an axis component , this method would return true if that axis is supported for this chart type.
|
|
/// for example: if the chart supports only horizontal axis , this method should return true for horizontal axis and false otherwise
|
|
/// This method is used by the axis drawing method to determine if this chart type provides data for an axis. (if not then the axis is drawn without values
|
|
/// </summary>
|
|
/// <param name="axis"></param>
|
|
/// <returns></returns>
|
|
protected abstract bool HasValues(AxisBase axis);
|
|
|
|
/// <summary>
|
|
/// returns the maximum value for the specified axis , or 0 if HasValues(axis) returns false.
|
|
/// the maximum value is usually determined by the chart data source , and can be mannualy overriden by the user
|
|
/// </summary>
|
|
protected abstract double MaxValue(AxisBase axis);
|
|
|
|
/// <summary>
|
|
/// returns the minimum value for the specified axis , or 0 if HasValues(axis) returns false.
|
|
/// the minimum value is usually determined by the chart data source , and can be mannualy overriden by the user
|
|
/// </summary>
|
|
protected abstract double MinValue(AxisBase axis);
|
|
|
|
|
|
/// <summary>
|
|
/// when enabling a chart , all chartItem components must be activated. Every unity gameobject create by graph and chart is marked as a ChartItem
|
|
/// </summary>
|
|
protected virtual void OnEnable()
|
|
{
|
|
ChartItem[] children = GetComponentsInChildren<ChartItem>(true);
|
|
for (int i = 0; i < children.Length; ++i)
|
|
{
|
|
if (children[i] != null)
|
|
{
|
|
if (children[i].gameObject != gameObject)
|
|
children[i].gameObject.SetActive(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// when disbaling a chart , all chartItem components must be disabled. Every unity gameobject create by graph and chart is marked as a ChartItem
|
|
/// </summary>
|
|
protected virtual void OnDisable()
|
|
{
|
|
ChartItem[] children = GetComponentsInChildren<ChartItem>();
|
|
for (int i = 0; i < children.Length; ++i)
|
|
{
|
|
if (children[i] != null)
|
|
{
|
|
if (children[i].gameObject != gameObject)
|
|
children[i].gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public abstract bool SupportRealtimeGeneration { get; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// call this in a dervied class in order to invoke the OnRedraw event. The OnRedraw event should be called any time a marker position is about to change.
|
|
/// </summary>
|
|
protected void InvokeOnRedraw()
|
|
{
|
|
if (OnRedraw != null)
|
|
OnRedraw.Invoke();
|
|
}
|
|
|
|
public virtual void GenerateRealtime()
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method generates a chart by calling InternalGenerateChart
|
|
/// </summary>
|
|
public void GenerateChart()
|
|
{
|
|
if (mGenerating == true) // avoid nested calls to generate chart
|
|
return;
|
|
mGenerating = true;
|
|
InternalGenerateChart(); // call derivative class implementation
|
|
Transform[] children = gameObject.GetComponentsInChildren<Transform>();
|
|
// setup the layer for all child object so it matches the parent
|
|
for(int i=0; i< children.Length; i++)
|
|
{
|
|
Transform t = children[i];
|
|
if (t == null || t.gameObject == null)
|
|
continue;
|
|
t.gameObject.layer = gameObject.layer;
|
|
}
|
|
|
|
TextController.transform.SetAsLastSibling();
|
|
if (IsCanvas)
|
|
FitCanvas();
|
|
InvokeOnRedraw();
|
|
mGenerating = false;
|
|
}
|
|
/// <summary>
|
|
/// override this method in a derived class to generate a chart type.
|
|
/// </summary>
|
|
public virtual void InternalGenerateChart()
|
|
{
|
|
if (gameObject.activeInHierarchy == false)
|
|
return;
|
|
mGenerateOnNextUpdate = false;
|
|
if (ChartGenerated != null)
|
|
ChartGenerated();
|
|
}
|
|
/// <summary>
|
|
/// override this method in a dervied class to add custom clearing for any chart type. default implementation deletes all chartItem components from this transform
|
|
/// </summary>
|
|
protected virtual void ClearChart()
|
|
{
|
|
|
|
//#if UNITY_2018_3_OR_NEWER
|
|
//#if UNITY_EDITOR
|
|
|
|
// // if (Application.isEditor == true && Application.isPlaying == false)
|
|
// // {
|
|
// var path = UnityEditor.PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(gameObject);
|
|
// if (!(path == null || path.Trim().Length == 0))
|
|
// {
|
|
// // Load the contents of the Prefab Asset.
|
|
// GameObject contentsRoot = UnityEditor.PrefabUtility.LoadPrefabContents(path);
|
|
|
|
// bool save = false;
|
|
// // Modify Prefab contents.
|
|
// foreach (var item in contentsRoot.GetComponentsInChildren<ChartItem>())
|
|
// {
|
|
// if (item == null)
|
|
// continue;
|
|
// if (item.gameObject != null)
|
|
// {
|
|
// save = true;
|
|
// DestroyImmediate(item.gameObject);
|
|
// }
|
|
// }
|
|
// if (save)
|
|
// {
|
|
// // try
|
|
// // {
|
|
// UnityEditor.PrefabUtility.SaveAsPrefabAsset(contentsRoot, path);
|
|
// // }
|
|
// // catch (Exception e)
|
|
// // {
|
|
// // }
|
|
|
|
// }
|
|
|
|
// UnityEditor.PrefabUtility.UnloadPrefabContents(contentsRoot);
|
|
// }
|
|
// // }
|
|
//#endif
|
|
//#endif
|
|
mHovered.Clear();
|
|
|
|
if (TextController != null) // destroy all child text object
|
|
{
|
|
TextController.DestroyAll();
|
|
TextController.transform.SetParent(transform, false); // the text controller my not be a direct child of this gameobject , make it so that it is.
|
|
}
|
|
|
|
// destroy all child ChartItem objects
|
|
ChartItem[] children = GetComponentsInChildren<ChartItem>();
|
|
for (int i = 0; i < children.Length; ++i)
|
|
{
|
|
if (children[i] != null)
|
|
{
|
|
if (TextController != null && children[i].gameObject == TextController.gameObject)
|
|
continue;
|
|
if (children[i].gameObject.GetComponent<ChartItemNoDelete>() != null)
|
|
continue;
|
|
if (children[i].gameObject.GetComponentInParent<AnyChart>() != this)
|
|
continue;
|
|
if (children[i].gameObject != gameObject)
|
|
ChartCommon.SafeDestroy(children[i].gameObject);
|
|
}
|
|
}
|
|
// ensure the text controller has been created ( it is generated only when the graph chart is first created)
|
|
EnsureTextController();
|
|
|
|
|
|
//destroy all axis components in this chart
|
|
for (int i = 0; i < mAxis.Count; i++)
|
|
{
|
|
if (mAxis[i] != null && mAxis[i].This() != null)
|
|
{
|
|
ChartCommon.SafeDestroy(mAxis[i].GetGameObject());
|
|
}
|
|
}
|
|
mAxis.Clear();
|
|
|
|
}
|
|
|
|
protected GameObject FixPosition
|
|
{
|
|
get { return mFixPosition; }
|
|
}
|
|
|
|
protected virtual ChartMagin MarginLink { get { return new ChartMagin(0f, 0f, 0f, 0f); } }
|
|
protected virtual Vector3 CanvasFitOffset { get { return new Vector3(0.5f, 0.5f,0f); } }
|
|
|
|
public enum FitType
|
|
{
|
|
None,
|
|
Aspect,
|
|
Height,
|
|
Width
|
|
}
|
|
public enum FitOrientation
|
|
{
|
|
Normal,
|
|
Vertical,
|
|
VerticalOpopsite
|
|
}
|
|
public enum FitAlign
|
|
{
|
|
StartXCenterY,
|
|
CenterXStartY,
|
|
CenterXCenterY,
|
|
StartXStartY,
|
|
}
|
|
protected virtual bool ShouldFitCanvas { get { return false; } }
|
|
protected virtual float FitZRotationCanvas { get { return 0; } }
|
|
protected virtual FitType FitAspectCanvas { get { return FitType.None; } }
|
|
protected virtual FitAlign FitAlignCanvas { get { return FitAlign.CenterXCenterY; } }
|
|
|
|
static List<GameObject> toMove = new List<GameObject>();
|
|
void FitCanvas()
|
|
{
|
|
|
|
RectTransform trans = GetComponent<RectTransform>();
|
|
mLastSetSize = trans.rect.size;
|
|
if (ShouldFitCanvas == false)
|
|
return;
|
|
// if (FitAspectCanvas == FitType.None)
|
|
// return;
|
|
ChartMagin margin = MarginLink;
|
|
if (mFixPosition != null)
|
|
ChartCommon.SafeDestroy(mFixPosition);
|
|
GameObject fixPosition = new GameObject();
|
|
var fixRect = fixPosition.AddComponent<RectTransform>();
|
|
mFixPosition = fixPosition;
|
|
ChartCommon.HideObject(fixPosition, hideHierarchy);
|
|
fixPosition.AddComponent<ChartItem>();
|
|
|
|
|
|
float totalWidth = TotalWidthLink;// + margin.Left + margin.Right;
|
|
float totalHeight = TotalHeightLink;// + margin.Top + margin.Bottom;
|
|
|
|
|
|
fixRect.SetParent(transform, false);
|
|
fixRect.localPosition = Vector3.zero;
|
|
|
|
fixRect.anchorMin = new Vector2(0f, 0f);
|
|
fixRect.anchorMax = new Vector2(0f, 0f);
|
|
fixRect.pivot = new Vector2(0f, 0f);
|
|
fixRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, totalWidth);
|
|
fixRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, totalHeight);
|
|
if (FitZRotationCanvas == 90 || FitZRotationCanvas == -90)
|
|
{
|
|
totalHeight = TotalWidthLink;
|
|
totalWidth = TotalHeightLink;
|
|
}
|
|
float xAnchor = 0.5f;
|
|
float yAnchor = 0.5f;
|
|
|
|
|
|
if (FitAlignCanvas == FitAlign.StartXCenterY || FitAlignCanvas == FitAlign.StartXStartY)
|
|
{
|
|
xAnchor = 0f;
|
|
}
|
|
|
|
if (FitAlignCanvas == FitAlign.CenterXStartY || FitAlignCanvas == FitAlign.StartXStartY)
|
|
yAnchor = 0f;
|
|
|
|
|
|
fixPosition.transform.localScale = new Vector3(1f, 1f, 1f);
|
|
fixPosition.transform.SetSiblingIndex(0);
|
|
toMove.Clear();
|
|
|
|
for(int i=0; i<gameObject.transform.childCount; i++)
|
|
{
|
|
var child = trans.GetChild(i).gameObject;
|
|
if (child == null)
|
|
continue;
|
|
if (child == fixPosition)
|
|
continue;
|
|
if (child.GetComponent<AnyChart>() != null)
|
|
continue;
|
|
if (child.GetComponent<ChartItem>() == null)
|
|
continue;
|
|
toMove.Add(child);
|
|
|
|
}
|
|
foreach(GameObject obj in toMove)
|
|
{
|
|
obj.transform.SetParent(fixPosition.transform, false);
|
|
}
|
|
|
|
toMove.Clear();
|
|
|
|
|
|
fixRect.anchorMin = new Vector2(xAnchor, yAnchor);
|
|
fixRect.anchorMax = new Vector2(xAnchor, yAnchor);
|
|
fixRect.pivot = new Vector2(xAnchor, yAnchor);
|
|
|
|
if (totalWidth <= 0 || TotalHeight <= 0)
|
|
return;
|
|
|
|
float widthScale = (trans.rect.size.x - margin.Left - margin.Right) / totalWidth;
|
|
float heightScale = (trans.rect.size.y - margin.Top - margin.Bottom) / totalHeight;
|
|
float uniformScale = Math.Min(widthScale, heightScale);
|
|
if (FitAspectCanvas == FitType.Height)
|
|
uniformScale = heightScale;
|
|
else if (FitAspectCanvas == FitType.Width)
|
|
uniformScale = widthScale;
|
|
else if (FitAspectCanvas == FitType.None)
|
|
uniformScale = 1f;
|
|
|
|
fixRect.localScale = new Vector3(uniformScale, uniformScale, 1f);
|
|
if(MaintainLabelSize)
|
|
TextController.SetInnerScale(1f / uniformScale);
|
|
else
|
|
TextController.SetInnerScale(1f);
|
|
|
|
|
|
fixRect.anchoredPosition = new Vector3( Mathf.Lerp(margin.Left,-margin.Right,xAnchor), Mathf.Lerp(margin.Bottom,-margin.Top, yAnchor), 0f);
|
|
fixRect.localRotation = Quaternion.Euler(0, 0, FitZRotationCanvas);
|
|
}
|
|
protected virtual void OnTransformParentChanged()
|
|
{
|
|
Invalidate();
|
|
}
|
|
/// <summary>
|
|
/// called by a derived class to indicate that the mouse has left any selectable object in the chart.
|
|
/// </summary>
|
|
protected virtual void OnNonHoverted()
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="userData"></param>
|
|
protected virtual void OnItemLeave(object userData,string type)
|
|
{
|
|
if (mHovered.Count == 0)
|
|
return;
|
|
mHovered.Remove(userData);
|
|
if (mHovered.Count == 0)
|
|
OnNonHoverted();
|
|
}
|
|
|
|
protected virtual void OnItemSelected(object userData)
|
|
{
|
|
|
|
}
|
|
|
|
protected virtual void OnItemHoverted(object userData)
|
|
{
|
|
mHovered.Add(userData);
|
|
}
|
|
|
|
internal protected virtual IAxisGenerator InternalUpdateAxis(ref GameObject axisObject,AxisBase axisBase, ChartOrientation axisOrientation, int divType,bool forceRecreate,double scrollOffset)
|
|
{
|
|
IAxisGenerator res = null;
|
|
if (axisObject == null || forceRecreate || CanvasChanged)
|
|
{
|
|
ChartCommon.SafeDestroy(axisObject);
|
|
GameObject axis = null;
|
|
if (IsUnderCanvas)
|
|
{
|
|
axis = ChartCommon.CreateCanvasChartItem();
|
|
axis.transform.SetParent(transform, false);
|
|
var rect = axis.GetComponent<RectTransform>();
|
|
rect.anchorMin = new Vector2(0f, 0f);
|
|
rect.anchorMax = new Vector2(0f, 0f);
|
|
rect.localScale = new Vector3(1f, 1f, 1f);
|
|
rect.localRotation = Quaternion.identity;
|
|
rect.anchoredPosition = new Vector3();
|
|
}
|
|
else
|
|
{
|
|
axis = ChartCommon.CreateChartItem();
|
|
axis.transform.SetParent(transform, false);
|
|
axis.transform.localScale = new Vector3(1f, 1f, 1f);
|
|
axis.transform.localRotation = Quaternion.identity;
|
|
axis.transform.localPosition = new Vector3();
|
|
}
|
|
axisBase.ClearFormats();
|
|
|
|
axis.layer = gameObject.layer; // put the axis on the same layer as the chart
|
|
ChartCommon.HideObject(axis, hideHierarchy);
|
|
axisObject = axis;
|
|
if (IsUnderCanvas)
|
|
res = axis.AddComponent<CanvasAxisGenerator>();
|
|
else
|
|
res = axis.AddComponent<AxisGenerator>();
|
|
}
|
|
else
|
|
{
|
|
if (IsUnderCanvas)
|
|
res = axisObject.GetComponent<CanvasAxisGenerator>();
|
|
else
|
|
res = axisObject.GetComponent<AxisGenerator>();
|
|
}
|
|
res.SetAxis(scrollOffset,this, axisBase, axisOrientation, divType);
|
|
|
|
// axisObject.transform.localScale = new Vector3(1f, 1f, 1f);
|
|
// axisObject.transform.localRotation = Quaternion.identity;
|
|
// axisObject.transform.localPosition = new Vector3();
|
|
return res;
|
|
}
|
|
|
|
protected virtual void ValidateProperties()
|
|
{
|
|
if(mItemLabels!=null)
|
|
mItemLabels.ValidateProperties();
|
|
if (mCategoryLabels != null)
|
|
mCategoryLabels.ValidateProperties();
|
|
if (mGroupLabels != null)
|
|
mGroupLabels.ValidateProperties();
|
|
if (mHorizontalAxis != null)
|
|
mHorizontalAxis.ValidateProperties();
|
|
if (mVerticalAxis != null)
|
|
mVerticalAxis.ValidateProperties();
|
|
}
|
|
|
|
protected void GenerateAxis(bool force)
|
|
{
|
|
mAxis.Clear();
|
|
if (mVerticalAxis)
|
|
{
|
|
double scroll = GetScrollOffset(1);
|
|
IAxisGenerator main = InternalUpdateAxis(ref VerticalMainDevisions, mVerticalAxis, ChartOrientation.Vertical, 0, force, scroll);
|
|
IAxisGenerator sub = InternalUpdateAxis(ref VerticalSubDevisions, mVerticalAxis, ChartOrientation.Vertical, 1, force, scroll);
|
|
IAxisGenerator custom = InternalUpdateAxis(ref VerticalCustomDevisions, mVerticalAxis, ChartOrientation.Vertical, 2, force, scroll);
|
|
IAxisGenerator customSub = InternalUpdateAxis(ref VerticalCustomDevisionsSub, mVerticalAxis, ChartOrientation.Vertical,3, force, scroll);
|
|
if (main != null)
|
|
mAxis.Add(main);
|
|
if (sub != null)
|
|
mAxis.Add(sub);
|
|
if (custom != null)
|
|
mAxis.Add(custom);
|
|
if (customSub != null)
|
|
mAxis.Add(customSub);
|
|
}
|
|
if (mHorizontalAxis)
|
|
{
|
|
double scroll = GetScrollOffset(0);
|
|
IAxisGenerator main = InternalUpdateAxis(ref HorizontalMainDevisions, mHorizontalAxis, ChartOrientation.Horizontal, 0, force,scroll);
|
|
IAxisGenerator sub = InternalUpdateAxis(ref HorizontalSubDevisions, mHorizontalAxis, ChartOrientation.Horizontal, 1, force,scroll);
|
|
IAxisGenerator custom = InternalUpdateAxis(ref HorizontalCustomDivisions, mHorizontalAxis, ChartOrientation.Horizontal, 2, force, scroll);
|
|
IAxisGenerator customSub = InternalUpdateAxis(ref HorizontalCustomDivisionsSub, mHorizontalAxis, ChartOrientation.Horizontal, 3, force, scroll);
|
|
if (main != null)
|
|
mAxis.Add(main);
|
|
if (sub != null)
|
|
mAxis.Add(sub);
|
|
if (custom != null)
|
|
mAxis.Add(custom);
|
|
if (custom != null)
|
|
mAxis.Add(customSub);
|
|
}
|
|
}
|
|
|
|
private event Action ChartGenerated;
|
|
|
|
protected void RaiseChartGenerated()
|
|
{
|
|
if (ChartGenerated != null)
|
|
ChartGenerated();
|
|
}
|
|
|
|
#region Internal Use
|
|
event Action IInternalUse.Generated
|
|
{
|
|
add
|
|
{
|
|
ChartGenerated += value;
|
|
}
|
|
remove
|
|
{
|
|
ChartGenerated -= value;
|
|
}
|
|
}
|
|
|
|
void IInternalUse.InternalItemSelected(object userData)
|
|
{
|
|
OnItemSelected(userData);
|
|
}
|
|
|
|
void IInternalUse.InternalItemLeave(object userData)
|
|
{
|
|
OnItemLeave(userData,"unknown");
|
|
}
|
|
|
|
void IInternalUse.InternalItemHovered(object userData)
|
|
{
|
|
OnItemHoverted(userData);
|
|
}
|
|
|
|
void IInternalUse.CallOnValidate()
|
|
{
|
|
OnValidate();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Label settings for the chart items
|
|
/// </summary>
|
|
ItemLabels IInternalUse.ItemLabels
|
|
{
|
|
get { return mItemLabels; }
|
|
set
|
|
{
|
|
if (mItemLabels != null)
|
|
{
|
|
|
|
((IInternalSettings)mItemLabels).InternalOnDataUpdate -= Labels_OnDataUpdate;
|
|
((IInternalSettings)mItemLabels).InternalOnDataChanged -= Labels_OnDataChanged;
|
|
}
|
|
mItemLabels = value;
|
|
if (mItemLabels != null)
|
|
{
|
|
((IInternalSettings)mItemLabels).InternalOnDataUpdate += Labels_OnDataUpdate;
|
|
((IInternalSettings)mItemLabels).InternalOnDataChanged += Labels_OnDataChanged;
|
|
}
|
|
OnLabelSettingsSet();
|
|
}
|
|
}
|
|
|
|
VerticalAxis IInternalUse.VerticalAxis
|
|
{
|
|
get
|
|
{
|
|
return mVerticalAxis;
|
|
}
|
|
set
|
|
{
|
|
if (mVerticalAxis != null)
|
|
{
|
|
((IInternalSettings)mVerticalAxis).InternalOnDataChanged -= AxisChanged;
|
|
((IInternalSettings)mVerticalAxis).InternalOnDataUpdate -= AxisChanged;
|
|
}
|
|
mVerticalAxis = value;
|
|
if (mVerticalAxis != null)
|
|
{
|
|
((IInternalSettings)mVerticalAxis).InternalOnDataChanged += AxisChanged;
|
|
((IInternalSettings)mVerticalAxis).InternalOnDataUpdate += AxisChanged;
|
|
}
|
|
OnAxisValuesChanged();
|
|
}
|
|
}
|
|
|
|
|
|
HorizontalAxis IInternalUse.HorizontalAxis
|
|
{
|
|
get
|
|
{
|
|
return mHorizontalAxis;
|
|
}
|
|
set
|
|
{
|
|
if (mHorizontalAxis != null)
|
|
{
|
|
|
|
((IInternalSettings)mHorizontalAxis).InternalOnDataChanged -= AxisChanged;
|
|
((IInternalSettings)mHorizontalAxis).InternalOnDataUpdate -= AxisChanged;
|
|
}
|
|
mHorizontalAxis = value;
|
|
if (mHorizontalAxis != null)
|
|
{
|
|
((IInternalSettings)mHorizontalAxis).InternalOnDataChanged += AxisChanged;
|
|
((IInternalSettings)mHorizontalAxis).InternalOnDataUpdate += AxisChanged;
|
|
}
|
|
OnAxisValuesChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Label settings for the chart categories
|
|
/// </summary>
|
|
CategoryLabels IInternalUse.CategoryLabels
|
|
{
|
|
get { return mCategoryLabels; }
|
|
set
|
|
{
|
|
if (mCategoryLabels != null)
|
|
{
|
|
|
|
((IInternalSettings)mCategoryLabels).InternalOnDataUpdate -= Labels_OnDataUpdate;
|
|
((IInternalSettings)mCategoryLabels).InternalOnDataChanged -= Labels_OnDataChanged;
|
|
}
|
|
mCategoryLabels = value;
|
|
if (mCategoryLabels != null)
|
|
{
|
|
((IInternalSettings)mCategoryLabels).InternalOnDataUpdate += Labels_OnDataUpdate;
|
|
((IInternalSettings)mCategoryLabels).InternalOnDataChanged += Labels_OnDataChanged;
|
|
}
|
|
OnLabelSettingsSet();
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Label settings for group labels
|
|
/// </summary>
|
|
GroupLabels IInternalUse.GroupLabels
|
|
{
|
|
get { return mGroupLabels; }
|
|
set
|
|
{
|
|
if (mGroupLabels != null)
|
|
{
|
|
((IInternalSettings)mGroupLabels).InternalOnDataUpdate -= Labels_OnDataUpdate;
|
|
((IInternalSettings)mGroupLabels).InternalOnDataChanged -= Labels_OnDataChanged;
|
|
}
|
|
mGroupLabels = value;
|
|
if (mGroupLabels != null)
|
|
{
|
|
((IInternalSettings)mGroupLabels).InternalOnDataUpdate += Labels_OnDataUpdate;
|
|
((IInternalSettings)mGroupLabels).InternalOnDataChanged += Labels_OnDataChanged;
|
|
}
|
|
OnLabelSettingsSet();
|
|
}
|
|
}
|
|
|
|
TextController IInternalUse.InternalTextController { get { return TextController; } }
|
|
LegenedData IInternalUse.InternalLegendInfo { get { return LegendInfo; } }
|
|
|
|
Camera IInternalUse.InternalTextCamera
|
|
{
|
|
get
|
|
{
|
|
return TextCameraLink;
|
|
}
|
|
}
|
|
|
|
float IInternalUse.InternalTextIdleDistance
|
|
{
|
|
get
|
|
{
|
|
return TextIdleDistanceLink;
|
|
}
|
|
}
|
|
|
|
bool IInternalUse.InternalHasValues(AxisBase axis)
|
|
{
|
|
return HasValues(axis);
|
|
}
|
|
double IInternalUse.InternalMaxValue(AxisBase axis)
|
|
{
|
|
return MaxValue(axis);
|
|
}
|
|
double IInternalUse.InternalMinValue(AxisBase axis)
|
|
{
|
|
return MinValue(axis);
|
|
}
|
|
|
|
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
|
{
|
|
DataLink.OnBeforeSerialize();
|
|
OnBeforeSerializeEvent();
|
|
}
|
|
|
|
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
|
{
|
|
DataLink.OnAfterDeserialize();
|
|
}
|
|
protected virtual void OnBeforeSerializeEvent()
|
|
{
|
|
|
|
}
|
|
protected virtual void OnAfterDeserializeEvent()
|
|
{
|
|
|
|
}
|
|
|
|
float IInternalUse.InternalTotalDepth { get { return TotalDepthLink; } }
|
|
float IInternalUse.InternalTotalWidth { get { return TotalWidthLink; } }
|
|
float IInternalUse.InternalTotalHeight { get { return TotalHeightLink; } }
|
|
|
|
protected abstract bool SupportsCategoryLabels
|
|
{
|
|
get;
|
|
}
|
|
|
|
protected abstract bool SupportsItemLabels
|
|
{
|
|
get;
|
|
}
|
|
|
|
protected abstract bool SupportsGroupLables
|
|
{
|
|
get;
|
|
}
|
|
|
|
bool IInternalUse.InternalSupportsCategoryLables
|
|
{
|
|
get { return SupportsCategoryLabels; }
|
|
}
|
|
|
|
bool IInternalUse.InternalSupportsGroupLabels
|
|
{
|
|
get { return SupportsGroupLables; }
|
|
}
|
|
|
|
bool IInternalUse.InternalSupportsItemLabels
|
|
{
|
|
get { return SupportsItemLabels; }
|
|
}
|
|
#endregion
|
|
}
|
|
}
|