拼ui 一些业务逻辑x实现
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6db54b8a0f7da1a41a87346e993320ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 359794
|
||||
packageName: Easy Chart Lite
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/EasyChart/Scripts/Runtime/Data/ChartData.cs
|
||||
uploadId: 857482
|
||||
@@ -0,0 +1,786 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EasyChart
|
||||
{
|
||||
[CreateAssetMenu(fileName = "NewChartProfile", menuName = "EasyChart/Chart Profile")]
|
||||
public class ChartProfile : ScriptableObject
|
||||
{
|
||||
// Cache to avoid redundant EnsureRuntimeData() calls in the same frame
|
||||
[NonSerialized] private double _lastEnsureTime;
|
||||
[NonSerialized] private int _lastEnsureVersion;
|
||||
private static object DeepCloneObject(object source, Dictionary<object, object> visited)
|
||||
{
|
||||
if (source == null) return null;
|
||||
|
||||
var type = source.GetType();
|
||||
|
||||
// Keep UnityEngine.Object references as-is (Texture2D, etc.).
|
||||
if (typeof(UnityEngine.Object).IsAssignableFrom(type)) return source;
|
||||
|
||||
// Immutable / value types.
|
||||
if (type.IsPrimitive || type.IsEnum || type == typeof(string) || type == typeof(decimal)) return source;
|
||||
if (type.IsValueType) return source;
|
||||
|
||||
if (visited != null && visited.TryGetValue(source, out var existing)) return existing;
|
||||
visited ??= new Dictionary<object, object>();
|
||||
|
||||
// Arrays
|
||||
if (type.IsArray)
|
||||
{
|
||||
var arr = (Array)source;
|
||||
var elementType = type.GetElementType();
|
||||
var cloneArr = Array.CreateInstance(elementType, arr.Length);
|
||||
visited[source] = cloneArr;
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
{
|
||||
cloneArr.SetValue(DeepCloneObject(arr.GetValue(i), visited), i);
|
||||
}
|
||||
return cloneArr;
|
||||
}
|
||||
|
||||
// Lists / collections
|
||||
if (source is IList list)
|
||||
{
|
||||
var cloneList = (IList)Activator.CreateInstance(type);
|
||||
visited[source] = cloneList;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
cloneList.Add(DeepCloneObject(list[i], visited));
|
||||
}
|
||||
return cloneList;
|
||||
}
|
||||
|
||||
// Plain classes
|
||||
var clone = Activator.CreateInstance(type);
|
||||
visited[source] = clone;
|
||||
|
||||
// Unity serializes public instance fields (and [SerializeField] privates).
|
||||
// Here we copy all instance fields to avoid missing important data.
|
||||
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
var fields = type.GetFields(flags);
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
var f = fields[i];
|
||||
if (f.IsStatic) continue;
|
||||
var v = f.GetValue(source);
|
||||
f.SetValue(clone, DeepCloneObject(v, visited));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static T DeepCloneSettings<T>(T source) where T : class
|
||||
{
|
||||
if (source == null) return null;
|
||||
return (T)DeepCloneObject(source, null);
|
||||
}
|
||||
|
||||
public string chartName;
|
||||
public string chartId;
|
||||
|
||||
public CoordinateSystemType coordinateSystem = CoordinateSystemType.Cartesian2D;
|
||||
public CartesianMapping cartesian = new CartesianMapping();
|
||||
public List<AxisConfig> axes = new List<AxisConfig>();
|
||||
|
||||
// New axis fields (X/Y based)
|
||||
public AxisId xAxisId = AxisId.XBottom;
|
||||
public AxisId yAxisId = AxisId.YLeft;
|
||||
|
||||
public bool axisSelectionInitialized = false;
|
||||
public bool axisTypeSelectionInitialized = false;
|
||||
public float animationDuration = 1.0f;
|
||||
public BackgroundSettings background = new BackgroundSettings
|
||||
{
|
||||
show = true,
|
||||
textureFill = new TextureFillSettings { color = new Color(0.15f, 0.15f, 0.15f, 1f) }
|
||||
};
|
||||
|
||||
public float chartWidth = 450f;
|
||||
public float chartHeight = 300f;
|
||||
|
||||
[Padding4] public Vector4 padding = new Vector4(40f, 30f, 50f, 50f);
|
||||
public bool paddingInitialized = false;
|
||||
|
||||
public Color xGridColor = new Color(0.5f, 0.5f, 0.5f, 0.2f);
|
||||
public float xGridLineWidth = 1.0f;
|
||||
public Color yGridColor = new Color(0.5f, 0.5f, 0.5f, 0.2f);
|
||||
public float yGridLineWidth = 1.0f;
|
||||
|
||||
public CartesianGridSettings cartesianGrid = new CartesianGridSettings();
|
||||
public HoverSettings hover = new HoverSettings();
|
||||
public PolarAxesSettings polarAxes = new PolarAxesSettings();
|
||||
public bool gridSettingsInitialized = false;
|
||||
public bool hoverSettingsInitialized = false;
|
||||
|
||||
public LegendSettings legendSettings = new LegendSettings();
|
||||
|
||||
public PlotSettings plotSettings = new PlotSettings();
|
||||
|
||||
[Header("Series Data")] public List<Serie> series = new List<Serie>();
|
||||
|
||||
private AxisConfig GetAxis(AxisId id)
|
||||
{
|
||||
if (axes == null) return null;
|
||||
for (int i = 0; i < axes.Count; i++)
|
||||
{
|
||||
var a = axes[i];
|
||||
if (a != null && a.id == id) return a;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void EnsureAxes()
|
||||
{
|
||||
EnsureAxesIntegrity();
|
||||
}
|
||||
|
||||
public bool EnsureAxesIntegrity()
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
var beforePadding = padding;
|
||||
var beforeAxisSelectionInitialized = axisSelectionInitialized;
|
||||
var beforeAxisTypeSelectionInitialized = axisTypeSelectionInitialized;
|
||||
var beforeXAxisId = xAxisId;
|
||||
var beforeYAxisId = yAxisId;
|
||||
var beforeGridSettingsInitialized = gridSettingsInitialized;
|
||||
AxisId beforeCartesianX = cartesian != null ? cartesian.xAxisId : AxisId.XBottom;
|
||||
AxisId beforeCartesianY = cartesian != null ? cartesian.yAxisId : AxisId.YLeft;
|
||||
int beforeAxesCount = axes != null ? axes.Count : 0;
|
||||
|
||||
if (axes == null)
|
||||
{
|
||||
axes = new List<AxisConfig>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (cartesian == null)
|
||||
{
|
||||
cartesian = new CartesianMapping();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (background == null)
|
||||
{
|
||||
background = new BackgroundSettings { show = true };
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!paddingInitialized)
|
||||
{
|
||||
if (padding == Vector4.zero)
|
||||
{
|
||||
padding = new Vector4(40f, 30f, 50f, 50f);
|
||||
changed = true;
|
||||
}
|
||||
paddingInitialized = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (cartesianGrid == null)
|
||||
{
|
||||
cartesianGrid = new CartesianGridSettings();
|
||||
changed = true;
|
||||
}
|
||||
if (hover == null)
|
||||
{
|
||||
hover = new HoverSettings();
|
||||
changed = true;
|
||||
}
|
||||
if (polarAxes == null)
|
||||
{
|
||||
polarAxes = new PolarAxesSettings();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!gridSettingsInitialized)
|
||||
{
|
||||
cartesianGrid.xGridColor = xGridColor;
|
||||
cartesianGrid.xGridLineWidth = xGridLineWidth;
|
||||
cartesianGrid.yGridColor = yGridColor;
|
||||
cartesianGrid.yGridLineWidth = yGridLineWidth;
|
||||
gridSettingsInitialized = true;
|
||||
}
|
||||
|
||||
if (!hoverSettingsInitialized)
|
||||
{
|
||||
hoverSettingsInitialized = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
bool IsXAxis(AxisId id) => id == AxisId.XBottom || id == AxisId.XTop;
|
||||
bool IsYAxis(AxisId id) => id == AxisId.YLeft || id == AxisId.YRight;
|
||||
|
||||
// Initialize axis selection from existing cartesian mapping if not yet done
|
||||
if (!axisSelectionInitialized)
|
||||
{
|
||||
xAxisId = cartesian.xAxisId;
|
||||
yAxisId = cartesian.yAxisId;
|
||||
axisSelectionInitialized = true;
|
||||
}
|
||||
|
||||
// Ensure xAxisId/yAxisId are valid (one X, one Y)
|
||||
if (!IsXAxis(xAxisId))
|
||||
{
|
||||
xAxisId = AxisId.XBottom;
|
||||
changed = true;
|
||||
}
|
||||
if (!IsYAxis(yAxisId))
|
||||
{
|
||||
yAxisId = AxisId.YLeft;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Sync to cartesian mapping
|
||||
if (cartesian.xAxisId != xAxisId || cartesian.yAxisId != yAxisId)
|
||||
{
|
||||
cartesian.xAxisId = xAxisId;
|
||||
cartesian.yAxisId = yAxisId;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Ensure axes referenced by mapping exist with default axisType
|
||||
if (GetAxis(xAxisId) == null)
|
||||
{
|
||||
axes.Add(new AxisConfig
|
||||
{
|
||||
id = xAxisId,
|
||||
axisType = AxisType.Category // Default: X axis is Category
|
||||
});
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (GetAxis(yAxisId) == null)
|
||||
{
|
||||
axes.Add(new AxisConfig
|
||||
{
|
||||
id = yAxisId,
|
||||
axisType = AxisType.Value // Default: Y axis is Value
|
||||
});
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// One-time default axis type initialization:
|
||||
// - X defaults to Category
|
||||
// - Y defaults to Value
|
||||
// Only do this if both axes are still at the default Value (uninitialized state),
|
||||
// so we don't override user configurations (e.g. transposed charts or dual-category heatmaps).
|
||||
if (!axisTypeSelectionInitialized)
|
||||
{
|
||||
var xAxis = GetAxis(xAxisId);
|
||||
var yAxis = GetAxis(yAxisId);
|
||||
if (xAxis != null && yAxis != null
|
||||
&& xAxis.axisType == AxisType.Value
|
||||
&& yAxis.axisType == AxisType.Value)
|
||||
{
|
||||
xAxis.axisType = AxisType.Category;
|
||||
yAxis.axisType = AxisType.Value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
axisTypeSelectionInitialized = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Ensure labels list exists for all axes
|
||||
for (int i = 0; i < axes.Count; i++)
|
||||
{
|
||||
if (axes[i] != null && axes[i].labels == null)
|
||||
{
|
||||
axes[i].labels = new List<string>();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < axes.Count; i++)
|
||||
{
|
||||
var a = axes[i];
|
||||
if (a == null) continue;
|
||||
if (a.labelStyle != null) continue;
|
||||
|
||||
a.labelStyle = new LabelStyleSettings
|
||||
{
|
||||
enabled = a.showLabels,
|
||||
fontSize = a.fontSize,
|
||||
color = a.labelColor,
|
||||
position = a.labelPosition,
|
||||
offset = a.labelOffset
|
||||
};
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (padding != beforePadding) changed = true;
|
||||
if (gridSettingsInitialized != beforeGridSettingsInitialized) changed = true;
|
||||
if (axisSelectionInitialized != beforeAxisSelectionInitialized) changed = true;
|
||||
if (axisTypeSelectionInitialized != beforeAxisTypeSelectionInitialized) changed = true;
|
||||
if (xAxisId != beforeXAxisId) changed = true;
|
||||
if (yAxisId != beforeYAxisId) changed = true;
|
||||
if (cartesian.xAxisId != beforeCartesianX) changed = true;
|
||||
if (cartesian.yAxisId != beforeCartesianY) changed = true;
|
||||
if (axes.Count != beforeAxesCount) changed = true;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private int GetDataVersion()
|
||||
{
|
||||
// Simple hash based on key fields to detect data changes
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 31 + (int)coordinateSystem;
|
||||
hash = hash * 31 + (series?.Count ?? 0);
|
||||
hash = hash * 31 + (axes?.Count ?? 0);
|
||||
hash = hash * 31 + (polarAxes?.angleAxis?.labels?.Count ?? 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnsureRuntimeData()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Cache optimization: skip if called within 1 second and data hasn't changed
|
||||
if (!UnityEngine.Application.isPlaying)
|
||||
{
|
||||
double now = UnityEditor.EditorApplication.timeSinceStartup;
|
||||
int currentVersion = GetDataVersion();
|
||||
|
||||
bool needsIdentityFix = string.IsNullOrWhiteSpace(chartId) || string.IsNullOrWhiteSpace(chartName);
|
||||
|
||||
if (!needsIdentityFix && now - _lastEnsureTime < 1.0 && currentVersion == _lastEnsureVersion)
|
||||
{
|
||||
return false; // No changes, skip execution
|
||||
}
|
||||
|
||||
_lastEnsureTime = now;
|
||||
_lastEnsureVersion = currentVersion;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(chartName))
|
||||
{
|
||||
chartName = name;
|
||||
changed = true;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(chartId))
|
||||
{
|
||||
chartId = Guid.NewGuid().ToString("N");
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (EnsureAxesIntegrity()) changed = true;
|
||||
|
||||
if (polarAxes == null)
|
||||
{
|
||||
polarAxes = new PolarAxesSettings();
|
||||
changed = true;
|
||||
}
|
||||
if (polarAxes.angleAxis == null)
|
||||
{
|
||||
polarAxes.angleAxis = new PolarAxisStyle();
|
||||
changed = true;
|
||||
}
|
||||
if (polarAxes.radiusAxis == null)
|
||||
{
|
||||
polarAxes.radiusAxis = new PolarAxisStyle();
|
||||
changed = true;
|
||||
}
|
||||
if (polarAxes.angleAxis.labels == null)
|
||||
{
|
||||
polarAxes.angleAxis.labels = new List<string>();
|
||||
changed = true;
|
||||
}
|
||||
if (polarAxes.radiusAxis.labels == null)
|
||||
{
|
||||
polarAxes.radiusAxis.labels = new List<string>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (polarAxes.angleAxis.labelStyle == null)
|
||||
{
|
||||
polarAxes.angleAxis.labelStyle = new LabelStyleSettings
|
||||
{
|
||||
enabled = polarAxes.angleAxis.showLabels,
|
||||
fontSize = polarAxes.angleAxis.fontSize,
|
||||
color = polarAxes.angleAxis.labelColor,
|
||||
position = polarAxes.angleAxis.labelPosition,
|
||||
offset = polarAxes.angleAxis.labelOffset
|
||||
};
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (polarAxes.radiusAxis.labelStyle == null)
|
||||
{
|
||||
polarAxes.radiusAxis.labelStyle = new LabelStyleSettings
|
||||
{
|
||||
enabled = polarAxes.radiusAxis.showLabels,
|
||||
fontSize = polarAxes.radiusAxis.fontSize,
|
||||
color = polarAxes.radiusAxis.labelColor,
|
||||
position = polarAxes.radiusAxis.labelPosition,
|
||||
offset = polarAxes.radiusAxis.labelOffset
|
||||
};
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// One-time migration: Polar2D (Radar dimensions) should be self-consistent.
|
||||
// If angle axis labels are empty but legacy Category axis labels exist, copy them.
|
||||
if (polarAxes.angleAxis.labels.Count == 0)
|
||||
{
|
||||
List<string> ResolveLegacyCategoryLabels()
|
||||
{
|
||||
if (axes == null) return null;
|
||||
|
||||
// Use xAxisId as preferred for legacy category labels
|
||||
AxisId preferred = xAxisId;
|
||||
for (int i = 0; i < axes.Count; i++)
|
||||
{
|
||||
var a = axes[i];
|
||||
if (a != null && a.id == preferred && a.axisType == AxisType.Category)
|
||||
{
|
||||
return a.labels;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < axes.Count; i++)
|
||||
{
|
||||
var a = axes[i];
|
||||
if (a != null && a.axisType == AxisType.Category)
|
||||
{
|
||||
return a.labels;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var legacy = ResolveLegacyCategoryLabels();
|
||||
if (legacy != null && legacy.Count > 0)
|
||||
{
|
||||
polarAxes.angleAxis.labels = new List<string>(legacy);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (legendSettings == null)
|
||||
{
|
||||
legendSettings = new LegendSettings();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (plotSettings == null)
|
||||
{
|
||||
plotSettings = new PlotSettings();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
series = new List<Serie>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
if (series[i] == null)
|
||||
{
|
||||
series[i] = new Serie { name = $"Serie {i + 1}" };
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (series[i] != null && series[i].EnsureIntegrity())
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
var s = series[i];
|
||||
if (s == null) continue;
|
||||
if (s.type != SerieType.Heatmap) continue;
|
||||
if (s.seriesData == null || s.seriesData.Count == 0) continue;
|
||||
|
||||
bool looksLegacyHeatmap = false;
|
||||
for (int pi = 0; pi < s.seriesData.Count; pi++)
|
||||
{
|
||||
var p = s.seriesData[pi];
|
||||
if (p == null) continue;
|
||||
if (!Mathf.Approximately(p.z, 0f))
|
||||
{
|
||||
looksLegacyHeatmap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!looksLegacyHeatmap) continue;
|
||||
|
||||
for (int pi = 0; pi < s.seriesData.Count; pi++)
|
||||
{
|
||||
var p = s.seriesData[pi];
|
||||
if (p == null) continue;
|
||||
|
||||
float legacyY = p.value;
|
||||
float legacyZ = p.z;
|
||||
|
||||
p.y = legacyY;
|
||||
p.value = legacyZ;
|
||||
p.z = 0f;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
if (s.EnsureIntegrity()) changed = true;
|
||||
}
|
||||
|
||||
var radarLabels = polarAxes != null && polarAxes.angleAxis != null ? polarAxes.angleAxis.labels : null;
|
||||
if (radarLabels != null && radarLabels.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
var s = series[i];
|
||||
if (s == null) continue;
|
||||
if (s.type != SerieType.Radar) continue;
|
||||
|
||||
bool empty = s.seriesData == null || s.seriesData.Count == 0;
|
||||
if (!empty) continue;
|
||||
|
||||
s.seriesData = new List<SeriesData>(radarLabels.Count);
|
||||
for (int di = 0; di < radarLabels.Count; di++)
|
||||
{
|
||||
float t = radarLabels.Count > 1 ? (float)di / (radarLabels.Count - 1) : 0.5f;
|
||||
s.seriesData.Add(new SeriesData
|
||||
{
|
||||
x = di,
|
||||
value = Mathf.Lerp(0f, 100f, t),
|
||||
name = radarLabels[di],
|
||||
id = Guid.NewGuid().ToString("N")
|
||||
});
|
||||
}
|
||||
|
||||
changed = true;
|
||||
if (s.EnsureIntegrity()) changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure each serie has its own settings instance.
|
||||
// UI/editor workflows (and array element duplication) can cause multiple series
|
||||
// to reference the same managedReference object, which makes edits leak across series.
|
||||
var seenSettings = new HashSet<object>();
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
var s = series[i];
|
||||
if (s == null || s.settings == null) continue;
|
||||
|
||||
if (seenSettings.Contains(s.settings))
|
||||
{
|
||||
var cloned = DeepCloneSettings(s.settings);
|
||||
if (cloned != null)
|
||||
{
|
||||
s.settings = cloned;
|
||||
changed = true;
|
||||
|
||||
if (s.EnsureIntegrity()) changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
seenSettings.Add(s.settings);
|
||||
}
|
||||
|
||||
bool hasPolar = false;
|
||||
bool hasCartesian = false;
|
||||
bool hasPie = false;
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
var s = series[i];
|
||||
if (s == null || !s.visible) continue;
|
||||
if (s.type == SerieType.Pie || s.type == SerieType.RingChart || s.type == SerieType.Pie3D)
|
||||
{
|
||||
hasPie = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s.type == SerieType.Radar) hasPolar = true;
|
||||
else if (s.type == SerieType.Line || s.type == SerieType.Bar || s.type == SerieType.Scatter || s.type == SerieType.Heatmap) hasCartesian = true;
|
||||
}
|
||||
|
||||
// CoordinateSystem is a user-chosen plot space. Do not rewrite it automatically.
|
||||
// Incompatible series are filtered at render time (ChartElement), and the editor UI filters selectable types.
|
||||
bool warn = true;
|
||||
#if UNITY_EDITOR
|
||||
warn = Application.isPlaying;
|
||||
#endif
|
||||
if (warn)
|
||||
{
|
||||
if (hasPolar && hasCartesian)
|
||||
{
|
||||
Debug.LogWarning("[EasyChart] Mixed Polar (Radar) and Cartesian (Line/Bar/Scatter) series detected in one ChartProfile. This may produce semantically inconsistent axes/grid if CoordinateSystem/Axes settings are not aligned.");
|
||||
}
|
||||
else if (coordinateSystem == CoordinateSystemType.Cartesian2D && hasPolar)
|
||||
{
|
||||
Debug.LogWarning("[EasyChart] CoordinateSystem is Cartesian2D but a Radar series exists. Radar will be rendered, but PolarAxes/Radar settings may be semantically inconsistent with the selected CoordinateSystem.");
|
||||
}
|
||||
else if (coordinateSystem == CoordinateSystemType.Polar2D && hasCartesian)
|
||||
{
|
||||
Debug.LogWarning("[EasyChart] CoordinateSystem is Polar2D but Line/Bar/Scatter series exist. They will be rendered, but Cartesian axes/grid settings may be semantically inconsistent with the selected CoordinateSystem.");
|
||||
}
|
||||
else if (coordinateSystem == CoordinateSystemType.None && (hasPolar || hasCartesian))
|
||||
{
|
||||
Debug.LogWarning("[EasyChart] CoordinateSystem is None but coordinate-based series exist. They will be rendered, but axis/grid settings may be semantically inconsistent.");
|
||||
}
|
||||
else if (coordinateSystem != CoordinateSystemType.None && hasPie && !hasPolar && !hasCartesian)
|
||||
{
|
||||
Debug.LogWarning("[EasyChart] Only Pie series exist but CoordinateSystem is not None. Consider switching to None coordinate system.");
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
EnsureAxes();
|
||||
|
||||
xAxisId = AxisId.XBottom;
|
||||
yAxisId = AxisId.YLeft;
|
||||
EnsureAxes();
|
||||
|
||||
var xAxis = GetAxis(this.xAxisId);
|
||||
if (xAxis != null)
|
||||
{
|
||||
xAxis.axisType = AxisType.Category; // Default: X is Category
|
||||
xAxis.labels = new List<string> { "A", "B", "C", "D", "E" };
|
||||
}
|
||||
|
||||
var yAxis = GetAxis(this.yAxisId);
|
||||
if (yAxis != null)
|
||||
{
|
||||
yAxis.axisType = AxisType.Value; // Default: Y is Value
|
||||
}
|
||||
|
||||
var s = new Serie { name = "Series 1" };
|
||||
s.type = SerieType.Line;
|
||||
s.settings = new LineSettings { stroke = new LineStrokeSettings { color = Color.cyan } };
|
||||
|
||||
s.seriesData.Add(new SeriesData { x = 0, value = 10 });
|
||||
s.seriesData.Add(new SeriesData { x = 1, value = 20 });
|
||||
s.seriesData.Add(new SeriesData { x = 2, value = 15 });
|
||||
s.seriesData.Add(new SeriesData { x = 3, value = 30 });
|
||||
s.seriesData.Add(new SeriesData { x = 4, value = 25 });
|
||||
series.Add(s);
|
||||
}
|
||||
|
||||
public ChartData ToChartData()
|
||||
{
|
||||
var data = new ChartData();
|
||||
EnsureRuntimeData();
|
||||
data.CoordinateSystem = coordinateSystem;
|
||||
data.Cartesian = cartesian;
|
||||
data.Axes = axes;
|
||||
|
||||
data.XAxisId = xAxisId;
|
||||
data.YAxisId = yAxisId;
|
||||
|
||||
data.CartesianGrid = cartesianGrid;
|
||||
data.Hover = hover;
|
||||
data.PolarAxes = polarAxes;
|
||||
data.Plot = plotSettings;
|
||||
|
||||
data.Series = this.series;
|
||||
data.legend = this.legendSettings;
|
||||
data.animationDuration = this.animationDuration;
|
||||
return data;
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
EnsureRuntimeData();
|
||||
|
||||
// Sync legend offset when position changes
|
||||
if (legendSettings != null)
|
||||
{
|
||||
legendSettings.SyncOffsetToPosition();
|
||||
}
|
||||
|
||||
if (series == null) return;
|
||||
AxisConfig GetAxis(AxisId id)
|
||||
{
|
||||
if (axes == null) return null;
|
||||
for (int i = 0; i < axes.Count; i++)
|
||||
{
|
||||
var a = axes[i];
|
||||
if (a != null && a.id == id) return a;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool transposed = false;
|
||||
if (coordinateSystem == CoordinateSystemType.Cartesian2D && cartesian != null)
|
||||
{
|
||||
var xAxis = GetAxis(cartesian.xAxisId);
|
||||
var yAxis = GetAxis(cartesian.yAxisId);
|
||||
|
||||
bool hasHeatmap = false;
|
||||
if (series != null)
|
||||
{
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
var s = series[i];
|
||||
if (s != null && s.visible && s.type == SerieType.Heatmap)
|
||||
{
|
||||
hasHeatmap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasHeatmap)
|
||||
{
|
||||
for (int i = 0; i < axes.Count; i++)
|
||||
{
|
||||
var a = axes[i];
|
||||
if (a == null) continue;
|
||||
if (a.axisType != AxisType.Category) continue;
|
||||
a.labelPlacement = CategoryLabelPlacement.CellCenter;
|
||||
}
|
||||
}
|
||||
|
||||
var xDim = xAxis != null ? xAxis.axisType : AxisType.Category;
|
||||
var yDim = yAxis != null ? yAxis.axisType : AxisType.Value;
|
||||
transposed = xDim == AxisType.Value && yDim == AxisType.Category;
|
||||
}
|
||||
|
||||
// Data point convention:
|
||||
// - Normal: p.x = category index, p.y = value
|
||||
// - Transposed: p.y = category index, p.x = value
|
||||
bool categoryOnY = transposed;
|
||||
|
||||
foreach (var serie in series)
|
||||
{
|
||||
if (serie == null) continue;
|
||||
if (serie.seriesData == null) continue;
|
||||
|
||||
for (int i = 0; i < serie.seriesData.Count; i++)
|
||||
{
|
||||
var p = serie.seriesData[i];
|
||||
if (p == null) continue;
|
||||
if (i > 0)
|
||||
{
|
||||
var prev = serie.seriesData[i - 1];
|
||||
if (prev == null) continue;
|
||||
float pCat = categoryOnY ? p.y : p.x;
|
||||
float prevCat = categoryOnY ? prev.y : prev.x;
|
||||
if (Mathf.Approximately(pCat, prevCat))
|
||||
{
|
||||
if (categoryOnY) p.y = i;
|
||||
else p.x = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae49c3ed628aba644bf39a8d33a833ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 359794
|
||||
packageName: Easy Chart Lite
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/EasyChart/Scripts/Runtime/Data/ChartProfile.cs
|
||||
uploadId: 857482
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EasyChart
|
||||
{
|
||||
[CreateAssetMenu(fileName = "NewChartTheme", menuName = "EasyChart/Chart Theme")]
|
||||
public sealed class ChartTheme : ScriptableObject
|
||||
{
|
||||
public Object primaryFont;
|
||||
public Object monoFont;
|
||||
public float fontScale = 1f;
|
||||
|
||||
public float axisFontSize = -1f;
|
||||
public float legendFontSize = -1f;
|
||||
public float tooltipFontSize = -1f;
|
||||
public float seriesLabelFontSize = -1f;
|
||||
|
||||
public float titleFontSize = -1f;
|
||||
public float subtitleFontSize = -1f;
|
||||
|
||||
public List<Color> seriesColors = new List<Color>();
|
||||
public int paletteSeed = 0;
|
||||
|
||||
public Color positiveColor = new Color(0.2f, 0.9f, 0.4f, 1f);
|
||||
public Color negativeColor = new Color(0.95f, 0.25f, 0.25f, 1f);
|
||||
public Color neutralColor = new Color(0.6f, 0.6f, 0.6f, 1f);
|
||||
|
||||
public float disabledAlpha = 0.35f;
|
||||
|
||||
public Color backgroundColor = new Color(0f, 0f, 0f, 0f);
|
||||
[Padding4] public Vector4 panelPadding = Vector4.zero;
|
||||
public float panelRadius = 0f;
|
||||
|
||||
public Color plotBackgroundColor = new Color(0f, 0f, 0f, 0f);
|
||||
public Color plotBorderColor = new Color(0f, 0f, 0f, 0f);
|
||||
public float plotBorderWidth = 0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 533eb777d91918b4795f8c4f8e90e3fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 359794
|
||||
packageName: Easy Chart Lite
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/EasyChart/Scripts/Runtime/Data/ChartTheme.cs
|
||||
uploadId: 857482
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EasyChart
|
||||
{
|
||||
public static class SerieSettingsRegistry
|
||||
{
|
||||
private static readonly Dictionary<SerieType, Func<BaseSerieSettings>> s_factories = new Dictionary<SerieType, Func<BaseSerieSettings>>();
|
||||
|
||||
public static void Register(SerieType type, Func<BaseSerieSettings> factory)
|
||||
{
|
||||
if (factory == null) return;
|
||||
s_factories[type] = factory;
|
||||
}
|
||||
|
||||
public static bool TryCreate(SerieType type, out BaseSerieSettings settings)
|
||||
{
|
||||
settings = null;
|
||||
if (s_factories.TryGetValue(type, out var f) && f != null)
|
||||
{
|
||||
settings = f();
|
||||
return settings != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HasFactory(SerieType type)
|
||||
{
|
||||
return s_factories.ContainsKey(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf84e0744a6513446b7d24b08c8fa536
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 359794
|
||||
packageName: Easy Chart Lite
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/EasyChart/Scripts/Runtime/Data/SerieSettingsRegistry.cs
|
||||
uploadId: 857482
|
||||
Reference in New Issue
Block a user