udpate 0.6.3.3

This commit is contained in:
2026-07-20 11:08:12 +08:00
parent acaa1cd57a
commit 603f5a24c2
71 changed files with 57665 additions and 2183 deletions
+3
View File
@@ -2,6 +2,9 @@
## [Unrealse]
* 将XericLibrary提供的数据类型命名空间移动到Runtime下。这可能会带来一些兼容性问题,但现在Type成员太多了,再不调整这个遗留问题就不好解决了。
* 添加图表数据结构
# [0.6.3] 2026-07-16
* 图论框架支持蓝图模式
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0fb2e676da9c843418f62f424df0cddb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+46
View File
@@ -0,0 +1,46 @@
using UnityEngine;
using UnityEditor;
/// <summary>
/// 在 Hierarchy 右键菜单中创建 XLineChart 图表对象。
/// 路径:GameObject / Xeric Library / Chart / Line Chart
///
/// 此文件直接放在 Release/Editor/ 下由 Unity 编译,
/// 不经过外部 DLL 管线,确保菜单立即生效。
/// </summary>
public static class XLineChartCreateMenu
{
private const string k_MenuPath = "GameObject/Xeric Library/Chart/Line Chart";
private const string k_ComponentTypeName = "XericLibrary.Runtime.Chart.XLineChart";
[MenuItem(k_MenuPath, false, 11)]
private static void CreateLineChart(MenuCommand menuCommand)
{
var go = new GameObject("XLineChart");
go.AddComponent<RectTransform>();
var rt = go.GetComponent<RectTransform>();
rt.sizeDelta = new Vector2(600f, 400f);
// 通过字符串名称添加 XLineChart 组件(运行时程序集)
System.Type chartType = null;
foreach (var asm in System.AppDomain.CurrentDomain.GetAssemblies())
{
chartType = asm.GetType(k_ComponentTypeName);
if (chartType != null) break;
}
if (chartType != null)
{
go.AddComponent(chartType);
}
else
{
Debug.LogWarning("[XLineChart] 未找到 XLineChart 组件,请确认 Release/Runtime/Chart 已编译");
}
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
Undo.RegisterCreatedObjectUndo(go, "Create XLineChart");
Selection.activeObject = go;
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52c3766cfd564c845b8960708ab9bcf0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: bcbfe98d29b703044a524e421fcacc2a
guid: 114790f70dba8284a84b68083baa8418
PluginImporter:
externalObjects: {}
serializedVersion: 2
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fee5cce919df4be4e92d2613fefc16bb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: ChartAxisBackground-GridLine
m_Shader: {fileID: 4800000, guid: 7584ee191841bce46877acd3f62c3ac0, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AxisLineExp: 0.001
- _AxisLineWidth: 0.0001
- _ColorMask: 15
- _Float3: 0.96
- _GridExp: 0.001
- _GridLineThreshold: 0.088
- _GridLod0Width: 0.54
- _GridLod1Width: 0.569
- _GridLod1WidthMult: 0.9
- _GridLodMultiple: 5
- _GridLodTransthd: 10
- _GridOverlayPower: 5
- _LineExp: 1
- _LodTransthd: 200
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UseUIAlphaClip: 0
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _GridBackgroundColor: {r: 0.21960786, g: 0.21960786, b: 0.21960786, a: 1}
- _GridColor: {r: 0.735849, g: 0.735849, b: 0.735849, a: 1}
- _GridLodMultiple: {r: 1, g: 1, b: 0, a: 0}
- _GridLodThdLimid: {r: -0.8, g: 1, b: 1, a: -0.8}
- _Transform: {r: 10, g: 10, b: 0.31, a: 0}
- _Vector0: {r: 1, g: 0, b: 1, a: 0}
m_BuildTextureStacks: []
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e05af448ac159a548b4011a18295356e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,170 @@
Shader "XericLibrary/Chart/ChartAxisBackground-GridLine"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
_Transform( "Transform", Vector ) = ( 10, 10, 0, 0 )
_GridColor( "Grid Color", Color ) = ( 1, 1, 1, 1 )
_GridBackgroundColor( "Grid Background Color", Color ) = ( 0, 0, 0, 1 )
_AxisLineWidth( "Axis Line Width", Float ) = 0.01
_AxisLineExp( "Axis Line Exp", Range( 0.001, 1 ) ) = 1
}
SubShader
{
LOD 0
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" }
Stencil
{
Ref [_Stencil]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
Comp [_StencilComp]
Pass [_StencilOp]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#define ASE_VERSION 19908
#pragma vertex vert
#pragma fragment frag
#pragma target 3.5
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
#define ASE_NEEDS_TEXTURE_COORDINATES0
#define ASE_NEEDS_FRAG_TEXTURE_COORDINATES0
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
float4 mask : TEXCOORD2;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
float _UIMaskSoftnessX;
float _UIMaskSoftnessY;
uniform float4 _GridBackgroundColor;
uniform float4 _GridColor;
uniform float _AxisLineWidth;
uniform float4 _Transform;
uniform float _AxisLineExp;
v2f vert(appdata_t v )
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
v.vertex.xyz += float3( 0, 0, 0 ) ;
float4 vPosition = UnityObjectToClipPos(v.vertex);
OUT.worldPosition = v.vertex;
OUT.vertex = vPosition;
float2 pixelSize = vPosition.w;
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
OUT.texcoord = v.texcoord;
OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));
OUT.color = v.color * _Color;
return OUT;
}
fixed4 frag(v2f IN ) : SV_Target
{
const half alphaPrecision = half(0xff);
const half invAlphaPrecision = half(1.0/alphaPrecision);
IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision;
float2 temp_output_4_0 = (_Transform).xy;
float2 temp_cast_0 = (_AxisLineWidth).xx;
float2 texCoord183 = IN.texcoord.xy * temp_output_4_0 + ( ( float2( 0,0 ) - (_Transform).zw ) - temp_cast_0 );
float2 temp_output_198_0 = ( ( _AxisLineWidth + fwidth( texCoord183 ) ) * temp_output_4_0 );
float2 temp_cast_1 = (_AxisLineExp).xx;
float2 break201 = pow( ( ( temp_output_198_0 - frac( abs( texCoord183 ) ) ) / temp_output_198_0 ) , temp_cast_1 );
float4 lerpResult40 = lerp( _GridBackgroundColor , _GridColor , max( break201.x, break201.y ));
half4 color = lerpResult40;
#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color.a *= m.x * m.y;
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
color.rgb *= color.a;
return color;
}
ENDCG
}
}
CustomEditor "AmplifyShaderEditor.MaterialInspector"
Fallback Off
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7584ee191841bce46877acd3f62c3ac0
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fd25d4397c462a34e89f1f446d5f5430
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+140
View File
@@ -0,0 +1,140 @@
using UnityEngine;
namespace XericLibrary.Runtime.Chart
{
/// <summary>
/// 图表主行为组件。标记 [ExecuteAlways] 确保编辑器下生命周期正常执行。
/// </summary>
[AddComponentMenu("Xeric/Chart/Chart Main")]
[ExecuteAlways]
public class ChartMainBehaviour : MonoBehaviour
{
[SerializeField] [Tooltip("图表数据模型")]
private ChartDataModel m_DataModel;
public ChartDataModel DataModel
{
get => m_DataModel;
set => m_DataModel = value;
}
[Header("工具管理")]
[SerializeField] private bool m_ShowDebug;
[SerializeField] private bool m_HideAllTools;
public ChartToolsManager ToolManager { get; private set; }
public Transform ToolsRoot { get; private set; }
private bool m_IsInitialized;
// ── 编辑器/编辑器通用 hideFlags ──────────────────────────────
/// <summary>编辑器下不隐藏,允许在 Hierarchy 查看和调试</summary>
private const HideFlags k_EditorFlags = HideFlags.DontSaveInEditor;
/// <summary>运行时隐藏且不保存</summary>
private const HideFlags k_PlayFlags = HideFlags.HideAndDontSave;
internal static HideFlags CurrentFlags =>
Application.isPlaying ? k_PlayFlags : k_EditorFlags;
// ── 生命周期 ──────────────────────────────────────────────
private void Awake()
{
Initialize();
}
private void OnEnable()
{
// ExecuteAlways 模式下 OnEnable 在编辑器全程可用
if (!m_IsInitialized) Initialize();
}
private void Initialize()
{
if (m_IsInitialized) return;
if (ToolManager != null) return;
var flags = CurrentFlags;
var toolsObj = new GameObject("[ChartTools]");
toolsObj.transform.SetParent(transform, false);
toolsObj.hideFlags = flags;
ToolsRoot = toolsObj.transform;
ToolManager = new ChartToolsManager
{
ToolsRoot = ToolsRoot,
ShowDebug = m_ShowDebug,
HideAllTools = m_HideAllTools,
};
m_IsInitialized = true;
if (!Application.isPlaying)
Debug.Log("[ChartMain] 编辑器模式初始化完成", this);
}
private void Update()
{
if (!m_IsInitialized) return;
if (ToolManager == null) return;
ToolManager.ShowDebug = m_ShowDebug;
ToolManager.HideAllTools = m_HideAllTools;
ToolManager.UpdateAll();
}
private void LateUpdate()
{
if (!m_IsInitialized) return;
ToolManager?.RenderAll();
}
// ── 工具管理 ──────────────────────────────────────────────
public T AddTool<T>(string toolName, int orderIndex = 0)
where T : ChartToolBase, new()
{
if (ToolManager == null) Initialize();
return ToolManager.AddTool<T>(toolName, orderIndex);
}
public bool RemoveTool(string toolName)
{
if (ToolManager == null) return false;
return ToolManager.RemoveToolByName(toolName);
}
public void RemoveAllTools() => ToolManager?.RemoveAllTools();
// ── 清理 ──────────────────────────────────────────────────
private void OnDestroy()
{
if (ToolManager != null)
{
ToolManager.Dispose();
ToolManager = null;
}
if (ToolsRoot != null)
{
if (Application.isPlaying)
Destroy(ToolsRoot.gameObject);
else
DestroyImmediate(ToolsRoot.gameObject);
ToolsRoot = null;
}
m_IsInitialized = false;
}
private void OnValidate()
{
if (ToolManager != null)
{
ToolManager.ShowDebug = m_ShowDebug;
ToolManager.HideAllTools = m_HideAllTools;
}
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b65ce6e16b120984a9182ca8c67f1d17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+198
View File
@@ -0,0 +1,198 @@
using UnityEngine;
using UnityEngine.UI;
namespace XericLibrary.Runtime.Chart
{
/// <summary>
/// 2D 轴线网格渲染工具。
///
/// 创建 Image 子组件,加载 GridLine shader 实例化材质,
/// 通过材质 _Transform 参数控制网格的缩放和平移。
///
/// 可见范围由基类 VisibleRanges[4] 提供(每维度独立配置),
/// 本工具在 OnRender 中读取基类提供的范围计算 UV 变换。
///
/// 子节点结构:
/// [ChartTool_AxisBgUgui]
/// └─ BackgroundImage (Image)
/// </summary>
[AddComponentMenu("Xeric/Chart/2D Axis Tool")]
public class ChartToolAxisBgUgui : ChartToolBase
{
// ── Shader 资源路径 ────────────────────────────────────────
private const string k_GridShaderPath =
"Material/Graph/UI/Chart/ChartAxisBackground-GridLine";
private const string k_TransformProp = "_Transform";
private const string k_GridColorProp = "_GridColor";
private const string k_BgColorProp = "_GridBackgroundColor";
// ── 外观配置 ──────────────────────────────────────────────
/// <summary>网格线颜色</summary>
public Color GridColor { get; set; } = Color.white;
/// <summary>背景颜色</summary>
public Color BackgroundColor { get; set; } = new Color(0.15f, 0.15f, 0.15f, 1f);
/// <summary>网格每轴分割数(如 10 表示 10×10 网格)</summary>
public float GridDivisions { get; set; } = 10f;
// ── 内部组件 ──────────────────────────────────────────────
/// <summary>Image 组件(挂载材质)</summary>
public Image BackgroundImage { get; private set; }
/// <summary>实例化的网格材质</summary>
public Material GridMaterial { get; private set; }
// ── 初始化 ────────────────────────────────────────────────
protected override void OnInitialize()
{
// 1. 设置默认可见范围(对应 X 和 Y 维度)
SetViewRange(0, ChartViewRange.ValueMode(-100f, 100f, 200)); // X
SetViewRange(1, ChartViewRange.ValueMode(-100f, 100f, 200)); // Y
// 2. 创建 Image 子组件
BackgroundImage = CreateChildComponent<Image>("BackgroundImage");
// 3. 加载 GridLine shader
Shader gridShader = Resources.Load<Shader>(k_GridShaderPath);
if (gridShader == null)
{
Debug.LogError("[charttool_axisugui] 未找到 GridLine shader: " + k_GridShaderPath);
return;
}
// 4. 实例化材质
GridMaterial = new Material(gridShader);
GridMaterial.name = "GridMat_Instance";
// 5. 材质挂在 Image 上
if (BackgroundImage != null)
{
BackgroundImage.material = GridMaterial;
BackgroundImage.raycastTarget = false;
BackgroundImage.color = Color.white;
var rt = BackgroundImage.rectTransform;
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
}
// 6. 初始材质参数
ApplyMaterialSettings();
SetVisible(true);
}
// ── 材质参数更新 ──────────────────────────────────────────
/// <summary>
/// 根据 Image 尺寸和基类可见范围计算 _Transform.xyzw,更新材质。
/// </summary>
private void ApplyMaterialSettings()
{
if (GridMaterial == null || BackgroundImage == null) return;
GridMaterial.SetColor(k_GridColorProp, GridColor);
GridMaterial.SetColor(k_BgColorProp, BackgroundColor);
var rt = BackgroundImage.rectTransform;
Vector2 imageSize = rt.rect.size;
if (imageSize.x < 1f || imageSize.y < 1f)
imageSize = new Vector2(800f, 600f);
Vector4 transform = CalculateTransform(imageSize);
GridMaterial.SetVector(k_TransformProp, transform);
}
/// <summary>
/// 计算材质 _Transform 参数:
/// _Transform.xy = UV 缩放(网格密度)
/// _Transform.zw = UV 偏移(平移)
///
/// 使用基类 VisibleRanges[0]X 轴范围)和 VisibleRanges[1]Y 轴范围)。
/// </summary>
private Vector4 CalculateTransform(Vector2 imageSize)
{
float scaleX = GridDivisions;
float scaleY = GridDivisions;
float offsetX = 0f;
float offsetY = 0f;
var xRange = GetViewRange(0);
var yRange = GetViewRange(1);
if (xRange.Mode == ViewRangeMode.Value && xRange.ValueSpan > 0f)
{
// X 轴:值范围模式
float cellsX = GridDivisions;
scaleX = cellsX;
offsetX = -xRange.ValueMin / xRange.ValueSpan * cellsX;
}
else if (xRange.Mode == ViewRangeMode.Index && xRange.IndexCount > 0)
{
// X 轴:索引范围模式
scaleX = xRange.IndexCount;
}
if (yRange.Mode == ViewRangeMode.Value && yRange.ValueSpan > 0f)
{
// Y 轴:值范围模式
float cellsY = GridDivisions;
scaleY = cellsY * (imageSize.y / imageSize.x);
offsetY = -yRange.ValueMin / yRange.ValueSpan * scaleY;
}
else if (yRange.Mode == ViewRangeMode.Index && yRange.IndexCount > 0)
{
// Y 轴:索引范围模式
scaleY = yRange.IndexCount * (imageSize.y / imageSize.x);
}
return new Vector4(scaleX, scaleY, offsetX, offsetY);
}
// ── 渲染 ──────────────────────────────────────────────────
public override void OnRender()
{
base.OnRender();
if (GridMaterial != null)
{
GridMaterial.SetColor(k_GridColorProp, GridColor);
GridMaterial.SetColor(k_BgColorProp, BackgroundColor);
if (BackgroundImage != null)
{
var rt = BackgroundImage.rectTransform;
Vector2 imageSize = rt.rect.size;
if (imageSize.x > 0f && imageSize.y > 0f)
{
GridMaterial.SetVector(k_TransformProp, CalculateTransform(imageSize));
}
}
}
}
// ── 清理 ──────────────────────────────────────────────────
protected override void OnDispose()
{
if (GridMaterial != null)
{
if (Application.isPlaying)
Object.Destroy(GridMaterial);
else
Object.DestroyImmediate(GridMaterial);
GridMaterial = null;
}
BackgroundImage = null;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46268b481b98f044dadeda8b13d2a2f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+241
View File
@@ -0,0 +1,241 @@
using System.Reflection;
using UnityEngine;
using XericLibrary.Runtime.UIGraph;
namespace XericLibrary.Runtime.Chart
{
/// <summary>
/// 折线图表渲染工具。
///
/// 创建 UILineRendererV2 子组件绘制折线。
/// 在 OnRender 中将数据坐标映射为图表区域局部坐标后绘制。
///
/// 子节点结构:
/// [charttool_line_ugui]
/// └─ LineRenderer (UILineRendererV2)
/// </summary>
[AddComponentMenu("Xeric/Chart/Line Chart Tool")]
public class ChartToolLineUgui : ChartToolBase
{
// ── 外观配置 ──────────────────────────────────────────────
/// <summary>折线颜色</summary>
public Color LineColor { get; set; } = new Color(0.33f, 0.67f, 1f, 1f);
/// <summary>折线宽度</summary>
public float LineThickness { get; set; } = 4f;
/// <summary>图表内边距(左、下、右、上,像素)</summary>
public Vector4 Margin { get; set; } = new Vector4(50f, 40f, 20f, 20f);
/// <summary>是否自动清除旧线段</summary>
public bool AutoClear { get; set; } = true;
/// <summary>是否启用 Catmull-Rom 平滑线</summary>
public bool SmoothLine { get; set; }
/// <summary>平滑分段数</summary>
public int SmoothSegments { get; set; } = 8;
// ── 内部组件 ──────────────────────────────────────────────
/// <summary>折线渲染器</summary>
public UILineRendererV2 LineRenderer { get; private set; }
// ── 反射助手 ──────────────────────────────────────────────
private static FieldInfo s_VertexField;
private static FieldInfo s_SegmentField;
private static FieldInfo s_DirtyStartField;
private static void EnsureReflection()
{
if (s_VertexField != null) return;
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var baseType = typeof(CacheLineRendererBase);
s_VertexField = baseType.GetField("m_VertexData", flags);
s_SegmentField = baseType.GetField("m_SegmentRanges", flags);
s_DirtyStartField = baseType.GetField("m_DirtyStartSegment", flags);
}
/// <summary>清除线段渲染器内部缓存</summary>
private static void ClearLineInternal(CacheLineRendererBase renderer)
{
if (renderer == null) return;
EnsureReflection();
var vList = s_VertexField?.GetValue(renderer) as System.Collections.Generic.List<LineVertexData>;
var sList = s_SegmentField?.GetValue(renderer) as System.Collections.Generic.List<LineSegmentRange>;
vList?.Clear();
sList?.Clear();
s_DirtyStartField?.SetValue(renderer, 0);
}
// ── 初始化 ────────────────────────────────────────────────
protected override void OnInitialize()
{
// 1. 默认可见范围
SetViewRange(0, ChartViewRange.ValueMode(-100f, 100f, 200));
SetViewRange(1, ChartViewRange.ValueMode(-100f, 100f, 200));
// 2. 创建线段渲染器
LineRenderer = CreateChildComponent<UILineRendererV2>("LineRenderer");
if (LineRenderer != null)
{
LineRenderer.defaultColor = LineColor;
LineRenderer.defaultThickness = LineThickness;
var rt = LineRenderer.rectTransform;
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = new Vector2(Margin.x, Margin.y);
rt.offsetMax = new Vector2(-Margin.z, -Margin.w);
}
SetVisible(true);
}
// ── 渲染 ──────────────────────────────────────────────────
public override void OnRender()
{
base.OnRender();
if (LineRenderer == null || DataModel == null) return;
var xRange = GetViewRange(0);
var yRange = GetViewRange(1);
// 创建泛型索引器
var xData = DataModel.GetDimension(0);
var yData = DataModel.GetDimension(1);
var xIndexer = xRange.CreateIndexer(xData);
var yIndexer = yRange.CreateIndexer(yData);
int pointCount = Mathf.Min(xIndexer.Count, yIndexer.Count);
if (pointCount < 2) return;
var rt = LineRenderer.rectTransform;
Vector2 plotSize = rt.rect.size;
if (plotSize.x < 1f || plotSize.y < 1f) return;
// 生成局部坐标点
Vector2[] rawPoints = new Vector2[pointCount];
for (int i = 0; i < pointCount; i++)
rawPoints[i] = MapVisiblePoint(i, xIndexer, yIndexer, xRange, yRange, plotSize);
// 平滑
if (SmoothLine && rawPoints.Length > 2)
rawPoints = GenerateSmoothPoints(rawPoints, SmoothSegments);
// 清空旧数据 + 绘制新折线
if (AutoClear)
ClearLineInternal(LineRenderer);
LineVertexData[] vertices = new LineVertexData[rawPoints.Length];
for (int i = 0; i < rawPoints.Length; i++)
{
vertices[i] = new LineVertexData
{
position = rawPoints[i],
color = LineColor,
thickness = LineThickness,
userData = Vector4.zero,
};
}
LineRenderer.DrawPolyline(vertices, UVMode.ByDistance);
}
// ── 坐标映射 ──────────────────────────────────────────────
/// <summary>将可见范围内的第 visibleIndex 个数据点映射为图表局部坐标</summary>
private static Vector2 MapVisiblePoint(int visibleIndex,
ChartVisibleIndexer<double> xIdx, ChartVisibleIndexer<double> yIdx,
ChartViewRange xRange, ChartViewRange yRange,
Vector2 plotSize)
{
float px, py;
if (xRange.Mode == ViewRangeMode.Index)
{
// X 轴为索引模式 → 按索引比例均分宽度
int count = Mathf.Max(xIdx.Count, 1);
px = (float)visibleIndex / (count - 1) * plotSize.x;
}
else
{
// X 轴为值模式 → 按数据值映射
double dataX = xIdx[visibleIndex];
px = xRange.ValueSpan > 0f
? (float)((dataX - xRange.ValueMin) / xRange.ValueSpan * plotSize.x)
: 0f;
}
if (yRange.Mode == ViewRangeMode.Index)
{
// Y 轴为索引模式 → Y 数据值映射到高度
double dataY = yIdx[visibleIndex];
py = yRange.ValueSpan > 0f
? (float)((dataY - yRange.ValueMin) / yRange.ValueSpan * plotSize.y)
: 0f;
}
else
{
// Y 轴为值模式
double dataY = yIdx[visibleIndex];
py = yRange.ValueSpan > 0f
? (float)((dataY - yRange.ValueMin) / yRange.ValueSpan * plotSize.y)
: 0f;
}
return new Vector2(px, py);
}
// ── 平滑插值 ──────────────────────────────────────────────
private static Vector2[] GenerateSmoothPoints(Vector2[] points, int segs)
{
int n = points.Length;
if (n < 3) return points;
var result = new Vector2[(n - 1) * segs + 1];
int idx = 0;
for (int i = 0; i < n - 1; i++)
{
Vector2 p0 = i > 0 ? points[i - 1] : points[i];
Vector2 p1 = points[i];
Vector2 p2 = points[i + 1];
Vector2 p3 = i < n - 2 ? points[i + 2] : points[i + 1];
for (int j = 0; j < segs; j++)
{
float t = j / (float)segs;
result[idx++] = CatmullRom(p0, p1, p2, p3, t);
}
}
result[idx] = points[n - 1];
return result;
}
private static Vector2 CatmullRom(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t)
{
float t2 = t * t, t3 = t2 * t;
float a = -0.5f * t3 + 1.0f * t2 - 0.5f * t;
float b = 1.5f * t3 - 2.5f * t2 + 1.0f;
float c = -1.5f * t3 + 2.0f * t2 + 0.5f * t;
float d = 0.5f * t3 - 0.5f * t2;
return a * p0 + b * p1 + c * p2 + d * p3;
}
// ── 清理 ──────────────────────────────────────────────────
protected override void OnDispose()
{
LineRenderer = null;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ecb40d06dacb8924e8c24d1a9f47e572
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+50
View File
@@ -0,0 +1,50 @@
using System.Collections.Generic;
namespace XericLibrary.Runtime.Chart
{
/// <summary>
/// 折线图数据模型的具体实现。
///
/// 使用基类 ChartDataModel 的 RingBufferList&lt;double&gt; 存储 X/Y 维度数据,
/// 提供便捷的批量写入接口(SetPoints / AddPoint)。
/// 每次数据变更自动触发 MarkDirty 通知工具链。
/// </summary>
public class LineChartDataModel : ChartDataModel
{
public LineChartDataModel(int capacity = DefaultCapacity)
: base(capacity)
{
}
/// <summary>批量设定折线数据点(覆盖模式)</summary>
public void SetPoints(IReadOnlyList<double> xValues, IReadOnlyList<double> yValues)
=> SetXYValues(xValues, yValues);
/// <summary>追加一个数据点</summary>
public void AddPoint(double x, double y)
=> AddXYPoint(x, y);
/// <summary>批量追加多个数据点</summary>
public void AddPoints(IReadOnlyList<double> xValues, IReadOnlyList<double> yValues)
{
int min = System.Math.Min(xValues.Count, yValues.Count);
for (int i = 0; i < min; i++)
AddXYPoint(xValues[i], yValues[i]);
}
/// <summary>仅设定 Y 值(X 自动填充为自然序号)</summary>
public void SetYValues(IReadOnlyList<double> yValues)
{
Y.Clear();
for (int i = 0; i < yValues.Count; i++) Y.Add(yValues[i]);
X.Clear();
for (int i = 0; i < yValues.Count; i++) X.Add(i);
MarkDirty(ChartDataFlags.DataChanged);
}
/// <summary>当前数据点数</summary>
public int PointCount => System.Math.Min(X.Count, Y.Count);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75f67f85cac57724db999d9cc469117f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+87
View File
@@ -0,0 +1,87 @@
using System.Collections.Generic;
using UnityEngine;
namespace XericLibrary.Runtime.Chart
{
/// <summary>
/// 折线图图表组件。
///
/// 继承 ChartMainBehaviour,是"开箱即用"的折线图表。
/// 功能上相当于一个完整的折线图控件,集成背景网格和折线渲染。
///
/// 自动执行以下初始化:
/// 1. 创建 LineChartDataModel 数据模型
/// 2. 添加 ChartToolAxisBgUgui(背景网格,order=-100
/// 3. 添加 ChartToolLineUgui(折线渲染,order=0
/// 4. 数据模型绑定到所有工具
/// </summary>
[AddComponentMenu("Xeric Library/Chart/Line Chart", 11)]
public class XLineChart : ChartMainBehaviour
{
[Header("Line Chart 设置")]
[SerializeField] private Color m_LineColor = new Color(0.33f, 0.67f, 1f, 1f);
[SerializeField] private float m_LineThickness = 4f;
[SerializeField] private Vector4 m_Margin = new Vector4(50f, 40f, 20f, 20f);
[SerializeField] private int m_VisibleItemCount = 100;
[SerializeField] private bool m_SmoothLine;
public ChartToolAxisBgUgui AxisTool { get; private set; }
public ChartToolLineUgui LineTool { get; private set; }
private void Start()
{
DataModel = new LineChartDataModel();
AxisTool = AddTool<ChartToolAxisBgUgui>("ChartTool_AxisBgUgui", -100);
if (AxisTool != null)
{
AxisTool.DataModel = DataModel;
AxisTool.SetViewRange(0, ChartViewRange.Recent(m_VisibleItemCount));
AxisTool.SetViewRange(1, ChartViewRange.ValueMode(-100f, 100f));
var bgImg = AxisTool.BackgroundImage;
if (bgImg != null)
{
var rt = bgImg.rectTransform;
rt.offsetMin = new Vector2(m_Margin.x, m_Margin.y);
rt.offsetMax = new Vector2(-m_Margin.z, -m_Margin.w);
}
}
LineTool = AddTool<ChartToolLineUgui>("ChartTool_LineUgui", 0);
if (LineTool != null)
{
LineTool.DataModel = DataModel;
LineTool.SetViewRange(0, ChartViewRange.Recent(m_VisibleItemCount));
LineTool.SetViewRange(1, ChartViewRange.ValueMode(-100f, 100f));
LineTool.LineColor = m_LineColor;
LineTool.LineThickness = m_LineThickness;
LineTool.Margin = m_Margin;
LineTool.SmoothLine = m_SmoothLine;
}
}
private void OnValidate()
{
if (LineTool != null)
{
LineTool.LineColor = m_LineColor;
LineTool.LineThickness = m_LineThickness;
LineTool.Margin = m_Margin;
LineTool.SmoothLine = m_SmoothLine;
}
}
public void AddPoint(double x, double y)
=> (DataModel as LineChartDataModel)?.AddPoint(x, y);
public void SetPoints(IReadOnlyList<double> xValues, IReadOnlyList<double> yValues)
=> (DataModel as LineChartDataModel)?.SetPoints(xValues, yValues);
public void SetYValues(IReadOnlyList<double> yValues)
=> (DataModel as LineChartDataModel)?.SetYValues(yValues);
public void ClearData()
=> DataModel?.ClearAll();
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 183a19f374fec32418402ce796cc3a85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c67d7ef6c97613b41b9095a945725e91
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+13
View File
@@ -2519,6 +2519,13 @@
<param name="rhs"></param>
<returns></returns>
</member>
<member name="M:Deconstruction.Trajectory.TrajectoryBase.ToVector2(System.ValueTuple{System.Double,System.Double})">
<summary>
转为二维矢量
</summary>
<param name="val"></param>
<returns></returns>
</member>
<member name="M:Deconstruction.Trajectory.TrajectoryBase.PolarCoord2UnitSpace(System.Double)">
<summary>
极坐标空间转单位空间(指的是将(-π, π)范围的取值转为(0, 1))
@@ -2683,6 +2690,12 @@
跳转到下一步
</summary>
</member>
<member name="M:Deconstruction.Type.DMToolSlot.WaitForUserInput_OpSlot.ReplaceThis(Deconstruction.Type.DMToolSlot.OperateSlotBase)">
<summary>
替换这个
</summary>
<param name="newSlot"></param>
</member>
<member name="T:Deconstruction.Type.DMToolSlot.OperateSlotBase">
<summary>
操作栈插槽,对应一个需要被工具控制的对象,以及控制它的一系列事件,属性。
Binary file not shown.
+1 -1
View File
@@ -4,9 +4,9 @@ using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Serialization;
using XericLibrary.Runtime;
using XericLibrary.Runtime.CustomEditor;
using XericLibrary.Runtime.MacroLibrary;
using XericLibrary.Runtime.Type;
#if UNITY_EDITOR
using UnityEditor;
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a7396e2f7782c3b4ba185b412fdeefb3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 60b3b3b61ea6fc54c8cf8f3da1b78319
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fcb0073aded67894782d2e4515d9286d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9abe5f919a1840a4dace5de52d721a06
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
-33
View File
@@ -1,33 +0,0 @@
fileFormatVersion: 2
guid: 7246df1f122aac345b615cdfe123fb10
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5a61f0f8f8df56c42af4507d63579616
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 138831b823d56714db753f3bbfcca789
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
Binary file not shown.
+2760 -1999
View File
File diff suppressed because it is too large Load Diff
@@ -1,70 +0,0 @@
fileFormatVersion: 2
guid: 7395568d54979b54fa983a6c08051d48
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 0
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Editor: 0
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -1,70 +0,0 @@
fileFormatVersion: 2
guid: 2e4d66a8654c88e4c8feb3fb149e861a
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 0
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Editor: 0
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 1e8ac916e7f928d4f8f2ac8e286cfc31
guid: 743d22ff294ec1f469617396b60d849e
PluginImporter:
externalObjects: {}
serializedVersion: 2
@@ -8,7 +8,7 @@ PluginImporter:
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
isExplicitlyReferenced: 1
validateReferences: 0
platformData:
- first:
@@ -19,7 +19,6 @@ PluginImporter:
Exclude Editor: 0
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Binary file not shown.
+1 -6
View File
@@ -1,9 +1,4 @@
安装说明
--------
这个目录包含 Xeric Library 的 Roslyn 语法分析器 DLL。
重要:DLL 不能放在深层目录!
Xeric Library Roslyn 语法分析器 DLL 使用说明
==============================
Rider / VS 等 IDE 在扫描 Roslyn Analyzer 时,只会识别浅层目录下的 DLL。