This commit is contained in:
CortexCore
2024-05-31 01:23:15 +08:00
parent c798b224be
commit 299082fe27
164 changed files with 3604 additions and 2018 deletions

View File

@@ -8,7 +8,8 @@
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:6de01b04fa4e14662b03fa46366da151",
"GUID:f19bbd83e3c264a5680926bf75d7e494"
"GUID:f19bbd83e3c264a5680926bf75d7e494",
"GUID:1c2aa13aa706ffc49a1a0044cad55436"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SkiaSharp;
using UnityEngine;
using UnityEngine.UIElements;
@@ -11,192 +12,255 @@ using UnityEngine.UIElements;
namespace BITKit.UX
{
public class SkiaChart : VisualElement
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlStringAttributeDescription m_JsonAttribute = new ()
{
name = "Json"
};
private readonly UxmlBoolAttributeDescription m_allowWarningsAttribute = new ()
{
name = "allowWarnings",
defaultValue = false
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var x = (SkiaChart)ve;
x.Json = m_JsonAttribute.GetValueFromBag(bag, cc);
x.AllowWarning = m_allowWarningsAttribute.GetValueFromBag(bag, cc);
}
}
public new class UxmlFactory : UxmlFactory<SkiaChart, UxmlTraits> { }
public SkiaChart()
{
public class SkiaChart : VisualElement
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlStringAttributeDescription m_JsonAttribute = new()
{
name = "Json"
};
private readonly UxmlBoolAttributeDescription m_allowWarningsAttribute = new()
{
name = "allowWarnings",
defaultValue = false
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var x = (SkiaChart)ve;
x.Json = m_JsonAttribute.GetValueFromBag(bag, cc);
x.AllowWarning = m_allowWarningsAttribute.GetValueFromBag(bag, cc);
}
}
public new class UxmlFactory : UxmlFactory<SkiaChart, UxmlTraits>
{
}
public SkiaChart()
{
RegisterCallback<GeometryChangedEvent>(x =>
{
if (visible)
{
Rebuild();
}
}); RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
});
RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
style.flexDirection = FlexDirection.Row;
_dataContainer = this.Create<VisualElement>();
_chartContainer = this.Create<VisualElement>();
_chartContainer.style.flexGrow = 1;
_dataContainer.style.justifyContent = Justify.SpaceBetween;
_dataContainer.name = "Data";
_chartContainer.name = "Chart";
}
private void OnCustomStyleResolved(CustomStyleResolvedEvent evt)
{
if(evt.customStyle.TryGetValue(new CustomStyleProperty<Color>("--chart-secondary-color"), out var helpLineColor))
{
_secondaryColor = helpLineColor;
}
Rebuild();
}
public string Json
{
get => _json;
set
{
_json = value;
Rebuild();
}
}
private string _json;
public bool AllowWarning { get; set; }
public Color SecondaryColor
{
get => _secondaryColor;
set
{
_secondaryColor = value;
Rebuild();
}
}
private Color _secondaryColor;
private readonly VisualElement _dataContainer;
private readonly VisualElement _chartContainer;
private void Rebuild()
{
var data = Array.Empty<float>();
try
{
data = JsonConvert.DeserializeObject<float[]>(Json);
}
catch (Exception e)
{
if (AllowWarning)
Debug.LogException(e);
return;
}
private void OnCustomStyleResolved(CustomStyleResolvedEvent evt)
{
if (evt.customStyle.TryGetValue(new CustomStyleProperty<Color>("--chart-secondary-color"),
out var helpLineColor))
{
_secondaryColor = helpLineColor;
}
Rebuild();
}
public string Json
{
get => _json;
set
{
_json = value;
Rebuild();
}
}
private string _json;
public bool AllowWarning { get; set; }
public Color SecondaryColor
{
get => _secondaryColor;
set
{
_secondaryColor = value;
Rebuild();
}
}
private Color _secondaryColor;
private readonly VisualElement _dataContainer;
private readonly VisualElement _chartContainer;
private void Rebuild()
{
if (float.IsNaN(layout.width) || float.IsNaN(layout.height))
{
if (AllowWarning)
BIT4Log.Log<SkiaChart>("Width or height is NaN");
return;
}
JArray[] array;
float[] data;
try
{
array = JsonConvert.DeserializeObject<JArray[]>(Json);
data = array.SelectMany(x => x.ToObject<float[]>()).ToArray();
}
catch (Exception e)
{
if (AllowWarning)
Debug.LogException(e);
return;
}
if (data is null or { Length: 0 })
{
if (AllowWarning)
BIT4Log.Log<SkiaChart>("Data is null or empty");
return;
}
_dataContainer.Clear();
_chartContainer.Clear();
// _chartContainer.style.marginBottom = 8;
// _chartContainer.style.marginLeft = 8;
// _chartContainer.style.marginRight = 8;
// _chartContainer.style.marginTop = 8;
_dataContainer.style.flexDirection = FlexDirection.ColumnReverse;
//假如min是x,max是y,将中间的差用data.Length划分
var max = data.Max();
var min = data.Min();
var difference = max - min;
var maxLength = array.Select(x => x.Count).Max();
max = max + difference / maxLength;
min = min - difference / maxLength;
difference = max - min;
if(float.IsNaN(layout.width) || float.IsNaN(layout.height))
return;
if (data is null or { Length: 0 }) return;
_dataContainer.Clear();
_chartContainer.Clear();
var max = data.Max();
var min = data.Min();
//假如min是x,max是y,将中间的差用data.Length划分
//Debug.Log($"min:{min} max:{max} difference:{difference} max:{max}");
_dataContainer.style.flexDirection = FlexDirection.ColumnReverse;
for (var i = 0; i < data.Length; i++)
{
var label = _dataContainer.Create<Label>();
var value = (max - min) / (data.Length - 1) * i + min;
label.text = value.ToString();
}
for (var i = 0; i <= maxLength; i++)
{
var label = _dataContainer.Create<Label>();
if(_chartContainer.layout.width is float.NaN or <10 || _chartContainer.layout.height is float.NaN or <10)
return;
var info = new SKImageInfo(
//var value = (max - min) / (data.Length - 1) * i + min;
var t = (float)i / maxLength;
var value = Mathf.Lerp(min,max,t);
label.text = Math.Round(value, 2).ToString();
}
if (_chartContainer.layout.width is float.NaN or < 10 || _chartContainer.layout.height is float.NaN or < 10)
{
if (AllowWarning)
BIT4Log.Log<SkiaChart>("Width or height is NaN or less than 10");
return;
}
var info = new SKImageInfo(
width: (int)_chartContainer.layout.width,
height: (int)_chartContainer.layout.height,
colorType: SKColorType.Rgba8888,
alphaType: SKAlphaType.Premul
);
using var surface = SKSurface.Create(info);
using var canvas = surface.Canvas;
using var linePaint = new SKPaint();
linePaint.Color = resolvedStyle.color.ToSKColor();
linePaint.StrokeWidth = resolvedStyle.unityTextOutlineWidth;
linePaint.IsAntialias = true;
linePaint.Style = SKPaintStyle.Stroke;
using var helpLinePaint = new SKPaint();
helpLinePaint.Color = resolvedStyle.unityTextOutlineColor.ToSKColor();
helpLinePaint.StrokeWidth = 1;
helpLinePaint.Style = SKPaintStyle.Stroke;
using var fillPaint = new SKPaint();
fillPaint.Color = new SKColor(200, 200, 200, 200);
fillPaint.Style=SKPaintStyle.Fill;
DoubleBuffer<SKPoint> buffer = new();
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
path.MoveTo(0,0);
path.LineTo(0,0);
for (var i = 0; i < data.Length; i++)
fillPaint.Style = SKPaintStyle.Fill;
for (var index = 0; index < array.Length; index++)
{
var value = data[i];
var posX = (float)info.Width / (data.Length - 1) * (i);
var d = max - min;
var p = (value - min) / d;
var poxY = info.Height * p;
var currentPoint = new SKPoint(posX, poxY);
if (buffer.TryGetRelease(out var previousPoint))
var _array = array[index];
DoubleBuffer<SKPoint> buffer = new();
data = _array.ToObject<float[]>();
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
path.MoveTo(0, 0);
path.LineTo(0, 0);
if (_array.Count is not 1 && customStyle.TryGetValue(new CustomStyleProperty<Color>($"--color-{index}"),
out var _myStyle))
{
canvas.DrawLine(previousPoint, currentPoint, linePaint);
linePaint.Color = _myStyle.ToSKColor();
}
var label = _chartContainer.Create<Label>();
label.text = value.ToString(CultureInfo.InvariantCulture);
label.style.position = Position.Absolute;
label.style.left = posX;//-label.layout.width/2;
label.style.bottom = poxY;//- label.layout.height/2;
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
path.LineTo(posX,poxY);
buffer.Release(currentPoint);
else
{
linePaint.Color = resolvedStyle.color.ToSKColor();
}
for (var i = 1; i <= data.Length; i++)
{
var value = data[i-1];
var posX = (float)info.Width / data.Length * (i);
var p = (value - min) / difference;
var poxY = info.Height * p;
var currentPoint = new SKPoint(posX, poxY);
if (buffer.TryGetRelease(out var previousPoint))
{
canvas.DrawLine(previousPoint, currentPoint, linePaint);
}
if (i == data.Length)
{
var label = _chartContainer.Create<Label>();
label.text = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
label.style.position = Position.Absolute;
label.style.left = posX; //-label.layout.width/2;
label.style.bottom = poxY; //- label.layout.height/2;
//label.transform.position = new Vector3(-label.layout.width/2, poxY, 0);
}
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
path.LineTo(posX, poxY);
buffer.Release(currentPoint);
}
path.LineTo(info.Width, 0);
path.Close();
}
path.LineTo(info.Width,0);
path.Close();
//canvas.DrawPath(path,filePaint);
var texture = info.ToTexture2D(surface);
_chartContainer.style.backgroundImage = texture;
}
}
}
}
}

View File

@@ -1,35 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using SkiaSharp;
using UnityEngine;
namespace BITKit.UX
{
public static class SkiaExtensions
{
public static Texture2D ToTexture2D(this SKImageInfo info,SKSurface surface)
{
// Okay, we're finished drawing. Now we create a Unity texture.
TextureFormat format = (info.ColorType == SKColorType.Rgba8888) ? TextureFormat.RGBA32 : TextureFormat.BGRA32;
var texture = new Texture2D(info.Width, info.Height, format, false, true);
texture.wrapMode = TextureWrapMode.Clamp;
// Pull a Skia image object out of the canvas...
var pixmap = surface.PeekPixels();
// Copy it to the Unity texture...
texture.LoadRawTextureData(pixmap.GetPixels(), pixmap.RowBytes * pixmap.Height);
texture.Apply(false, true);
// And drop it into the RawImage object.
return texture;
}
public static SKColor ToSKColor(this Color color,byte? alpha=null)
{
return new SKColor((byte)(color.r * 255), (byte)(color.g * 255), (byte)(color.b * 255), alpha??(byte)(color.a * 255));
}
public static SKColor ToSKColor(this Color32 color)
{
return new SKColor(color.r, color.g, color.b, color.a);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: c23b988a0ca3904468edef1bd026f977
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 614549820bd3c514eaf369e4895d1ef0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,25 +0,0 @@
{
"name": "BITKit.UX.Chart.Tests",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:6de01b04fa4e14662b03fa46366da151",
"GUID:f19bbd83e3c264a5680926bf75d7e494",
"GUID:994a3fb33a5627740b0712e7c483cc1f"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [
"_SkiaSharp"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 8acc186ca11c9df42b8ab94c1e952e73
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,99 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using SkiaSharp;
using UnityEngine;
namespace BITKit.UX
{
public class SKPaintComponent : MonoBehaviour
{
[SerializeField] private Texture2D texture;
[SerializeField] private float[] data;
[BIT]
private void Draw()
{
var info = new SKImageInfo(
width: 384,
height: 128,
colorType: SKColorType.Rgba8888,
alphaType: SKAlphaType.Premul
);
using var surface = SKSurface.Create(info);
using var canvas = surface.Canvas;
//canvas.Clear(new Color32(31,31,31,255).ToSKColor());
using var linePaint = new SKPaint();
linePaint.Color = new SKColor(255, 0, 0, 255);
linePaint.StrokeWidth = 8;
linePaint.IsAntialias = true;
linePaint.Style = SKPaintStyle.Stroke;
using var helpLinePaint = new SKPaint();
helpLinePaint.Color = new SKColor(200, 200, 200, 200);
helpLinePaint.StrokeWidth = 4;
helpLinePaint.Style = SKPaintStyle.Stroke;
using var textPaint = new SKPaint();
textPaint.TextAlign = SKTextAlign.Center;
textPaint.TextSize = 14;
textPaint.ColorF = new SKColor(0, 255, 0, 255);
using var filePaint = new SKPaint();
filePaint.Color = new SKColor(200, 200, 200, 200);
filePaint.Style=SKPaintStyle.Fill;
var min = data.Min();
var max = data.Max();
DoubleBuffer<SKPoint> buffer = new();
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
path.MoveTo(0,0);
path.LineTo(0,0);
for (var i = 0; i < data.Length; i++)
{
var value = data[i];
var posX = (float)info.Width / (data.Length - 1) * (i);
var d = max - min;
var p = (value - min) / d;
var poxY = info.Height * p;
var currentPoint = new SKPoint(posX, poxY);
if (buffer.TryGetRelease(out var previousPoint))
{
canvas.DrawLine(previousPoint, currentPoint, linePaint);
}
canvas.DrawText(
value.ToString()
, currentPoint
, new SKFont(
SKTypeface.FromFile(@"D:\Iris\Documents\GitHub\iFactory-YL106.Unity\Assets\BITKit\Unity\Art\Fonts\TTF\Roboto\Roboto-Regular.ttf")
), textPaint);
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
path.LineTo(posX,poxY);
buffer.Release(currentPoint);
}
path.LineTo(info.Width,0);
path.Close();
//canvas.DrawPath(path,filePaint);
texture = info.ToTexture2D(surface);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 4ce3dedf1c2068047a00040de957cb33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: