window.EASYCHART_MANUAL_ZH = window.EASYCHART_MANUAL_ZH || {}; window.EASYCHART_MANUAL_ZH.generatedAt = "2026-03-18 17:29:31"; window.EASYCHART_MANUAL_ZH.chapters = [ { id: "00_00-Index", relPath: "00_00-Index.md", title: "快速导览", content: "# 快速导览\n\n本手册面向通过 **`EasyChartLibraryWindow`** 在 Unity 编辑器里配置图表(`ChartProfile` 资产)的工作流。\n\n- 菜单入口:`EasyChart/Library Editor`\n- 手册查看器:`EasyChart/Manual`\n\n---\n\n## 目录\n\n### A. 上手与工作流\n\n- [快速导览](./00_00-Index.md)\n- [快速上手:2 分钟做出第一张图](./00_01-QuickStart.md)\n- [UIToolKit工作流(推荐)](./00_02-WorkflowAndLibrary.md)\n- [UGUI工作流](./00_03-UGUIWorkflow.md)\n- [运行时数据注入(UIToolKit)](./00_04-RuntimeDataInjectionUIToolKit.md)\n- [运行时数据注入(UGUI)](./00_05-RuntimeDataInjectionUGUI.md)\n\n### B. 编辑器与面板\n\n- [编辑器工作流与面板说明](./01_01-EditorWorkflow.md)\n- [Library 面板(资源树)](./01_02-LibraryPanel.md)\n- [JSON Injection 面板](./01_03-JsonInjectionPanel.md)\n- [Preview 面板](./02_04-PreviewPanel.md)\n- [Inspector 面板](./02_05-InspectorPanel.md)\n- [Series 面板](./02_06-SeriesPanel.md)\n\n### C. Series详细配置(用户目的导向)\n\n- [折线图(Line)](./03_01-LineChart.md)\n- [柱状图(Bar)](./03_02-BarChart.md)\n- [散点图(Scatter)](./03_03-ScatterChart.md)\n- [热力图(Heatmap)](./03_04-HeatmapChart.md)\n- [雷达图(Radar)](./03_05-RadarChart.md)\n- [饼图(Pie)](./03_06-PieChart.md)\n- [圆环图(RingChart)](./03_07-RingChart.md)\n\n### D. 配置项参考(按字段分类,查字典)\n\n- [常用配方(Common Recipes)](./04_08-CommonRecipes.md)\n- [FAQ(常见问题与最快排错路线)](./04_09-FAQ.md)\n\n### E. 更新与规划\n\n- [更新计划(Roadmap / Update Plan)](./05_01-UpdatePlan.md)\n\n---\n\n## 约定与术语\n\n- **ChartProfile**:图表配置资产(可复用,可在编辑器预览)。\n- **Series / Serie**:数据序列(例如折线的一条线、柱状图的一组柱)。\n- **SeriesData**:序列中的数据点集合。\n- **Axis**:坐标轴配置(`AxisType=Category/Value`)。\n- **Category**:类目轴(使用 `labels` 列表)。\n- **Value**:数值轴(连续数值范围)。\n\n---\n\n## 推荐文件组织\n\n建议在项目中为图表配置建立一个统一目录:\n\n- `Assets/EasyChart/Library/Custom/`:你自己的 `ChartProfile` 资产\n- `Assets/EasyChart/Docs/Manual/`:本手册章节(Markdown)\n\n---\n\n## 手册版本\n\n- 本手册将随 EasyChart 的字段与编辑器功能迭代同步更新。\n" }, { id: "00_01-QuickStart", relPath: "00_01-QuickStart.md", title: "快速上手:2 分钟做出第一张图", content: "# 快速上手:2 分钟做出第一张图\n\n本章目标:按 EasyChart 推荐的最快路径跑通“**编辑 → 导出 → 在 UI 中使用**”的闭环。\n\n---\n\n## 打开编辑器窗口\n\n在 Unity 菜单栏选择:\n\n- `EasyChart/Library Editor`\n\n你会看到一个类似“资源库/配置面板/预览区”的窗口(后续章节会解释每个区域)。\n\n---\n\n## 克隆一个 Library(推荐)\n\n如果你想快速开始并保持风格一致,建议:\n\n- 先在窗口顶部工具栏选择一个已有 Library(例如内置 Demo 库)\n- 点击工具栏的 **Clone**,克隆出你的个人库(例如 `MyLibrary`)\n\n这样你后续所有修改都发生在自己的库里,避免污染原始示例。\n\n---\n\n## 克隆一个 ChartProfile(推荐)\n\n在资源树里找到一个接近你目标效果的图表(`ChartProfile`),右键:\n\n- `Clone`\n\n克隆后,你会得到一个新的 Profile(用于做“同款变体”)。选中它,右侧 Inspector 会显示你可以直接修改的所有配置。\n\n---\n\n## 修改配置并保存\n\n最少改动建议:\n\n- `coordinateSystem`:确保与你要的 Series 匹配(例如 Line/Bar/Scatter 用 `Cartesian2D`)\n- `series`:确认 `type` 正确,并填充 `seriesData`\n- `axes`:最少保证 X/Y 轴类型与数据含义匹配\n\n完成修改后,点击窗口顶部工具栏的保存按钮(如果你的版本有),或等待 Unity 自动保存资产。\n\n---\n\n## 导出 UXML(用于 UI Builder 复用)\n\n推荐做法是把 Profile 导出为可复用的 `.uxml`:\n\n- 在资源树里右键你的 Profile\n- 选择 `Export to UXML`\n\n导出的 UXML 会进入:\n\n- `Assets/EasyChart/LibraryUxml/`(Mirror/Backup 相关操作也会在这个根目录下管理导出物)\n\n---\n\n## 在 QuickStart 场景里用 UIDocument + UI Builder 使用\n\n打开示例场景:\n\n- `Assets/EasyChart/Demo/Scenes/EasyChart_QuickStart.unity`\n\n然后在 Project 中找到:\n\n- `Assets/EasyChart/Demo/UIToolKit/NewUXMLTemplate.uxml`\n\n双击它打开(或在 UI Builder 中打开)。接下来:\n\n- 从 Project 里把你刚导出的图表 `.uxml` 拖入到 `NewUXMLTemplate.uxml` 的层级中\n- 保存 UXML\n- 确认场景里的 `UIDocument` 引用了你编辑后的 `NewUXMLTemplate.uxml`\n\n运行场景,你会看到图表渲染在 UI Toolkit 页面中。\n\n---\n\n## 备选:导出为 UGUI 预制体并使用\n\n如果你希望用 UGUI(Canvas/RectTransform)工作流,也可以在 Library Editor 中把选中的 Profile 导出为 UGUI 预制体并直接放进场景 UI(具体入口与细节取决于你当前版本提供的菜单项)。\n\n---\n\n## 下一步你应该看什么\n\n- 你要系统理解 UI Toolkit 推荐工作流:`00-WorkflowAndLibrary.md`\n- 你要用 UGUI(Canvas/RectTransform)把图表用起来:`33-UGUIWorkflow.md`\n\n" }, { id: "00_02-WorkflowAndLibrary", relPath: "00_02-WorkflowAndLibrary.md", title: "UIToolKit工作流(推荐)", content: "# UIToolKit工作流(推荐)\n\n本章目标:先把 EasyChart 推荐的整体实现思路讲清楚(以 UI Toolkit 为主):\n\n1. 在编辑器中用 **`EasyChartLibraryWindow`** 编辑 `ChartProfile`\n2. 将 `ChartProfile` **导出为 `.uxml`**,作为你的“图表库”资源\n3. 在 UI Toolkit 中用 **UI Builder** / UXML 组合页面,实现快速搭建 UI\n\n本章专注于 **UI Toolkit(推荐)**。如果你需要 UGUI(Canvas/RectTransform)工作流,请看:\n\n- `33-UGUIWorkflow.md`\n\n\n## 0. 为什么推荐 UI Toolkit 工作流\n\n核心原因:把“图表配置源(ChartProfile)”和“UI 落地物(UXML)”分层管理。\n\n- `ChartProfile`:负责图表“长什么样/用什么轴/有哪些 Series/数据如何解释”,适合在编辑器中集中维护。\n- 导出的 `.uxml`:负责把图表以 UI Toolkit 组件形式落地到页面中,适合复用/组合/版本控制。\n\n你最终得到的是:\n\n- 配置可复用(多个页面引用同一套图表风格)\n- 页面可组装(UI Builder 拖拽组合,不需要每次从零搭 UI)\n- 团队协作更清晰(Profile 作为“源”,UXML 作为“产物/组件库”)\n\n---\n\n## 1. 为什么要导出 UXML\n\n在项目里,`ChartProfile` 负责描述“图表长什么样、用什么轴、有什么 Series、数据点怎么解释”。\n\n当你导出 `.uxml` 后,你得到的是一个可在 UI Toolkit 中复用的 UI 资源:\n\n- 可以被 UI Builder 直接拖拽使用\n- 可以被多个页面复用(同一套图表样式)\n- 可以被版本控制与资产管理(你的“图表库”)\n\n---\n\n## 2. 推荐工作流(从配置到页面)\n\n### Step 1:克隆出你的工作库与图表(推荐)\n\n- 在 Unity 菜单栏打开:`EasyChart/Library Editor`\n\n推荐流程:\n\n- **Clone Library**:先克隆一个你自己的 Library(避免直接改内置示例库)\n- **Clone ChartProfile**:在你的库里右键某个接近目标效果的 Profile,选择 `Clone` 生成变体\n- 在右侧 Inspector 修改:\n - `coordinateSystem`\n - `series`\n - `axes`\n\n> 建议:把你自己的 Profile 统一放在 `Assets/EasyChart/Library/Custom/`(或团队约定目录)。\n\n### Step 2:导出 UXML(生成库资源)\n\n你可以在 Library Editor 里执行导出:\n\n- 对某个 Profile:`Export to UXML`\n- 对某个文件夹:\n - `Export Folder to UXML (Mirror)`\n - `Export Folder to UXML (Backup)`\n- 全量:\n - `Export All UXML (Mirror)`\n - `Export All UXML (Backup)`\n\n导出根目录:\n\n- `Assets/EasyChart/LibraryUxml/`\n\n多 Library 时通常结构为:\n\n- `Assets/EasyChart/LibraryUxml//...`\n\n其中 `_Backups` 子目录用于存放备份导出(以及一些导出过程附带的 JSON 备份文件):\n\n- `Assets/EasyChart/LibraryUxml//_Backups/...`\n\n导出的 UXML 核心结构类似:\n\n- 一个 ``\n- `profile-name` 对应某个 ChartProfile 的 key(通常是资产文件名)\n- 同时会写入图表的 width/height 样式\n\n> 重点:你应该把导出的 `.uxml` 当作“可复用图表组件”,而不是每次手写 UI。\n\n### Mirror vs Backup(你应该怎么选)\n\n- **Mirror**:\n - 用于“把 Profile 当前状态镜像到 UXML”\n - 通常会覆盖同名导出物,并可能清理不再存在的旧文件(保持镜像一致)\n- **Backup**:\n - 用于“按时间/标签做一次备份导出”\n - 不建议作为页面直接引用的主路径(更适合作为历史快照/回滚)\n\n### Step 3:在 UI Builder 里组装页面\n\n在 UI Builder 中:\n\n- 打开你的页面 UXML\n- 从 Project 里拖入导出的图表 `.uxml`\n- 将它与其他 UI(Label、Button、ListView 等)组合成完整页面\n\n如果你要最快验证导出链路,可以直接使用示例场景与模板:\n\n- 场景:`Assets/EasyChart/Demo/Scenes/EasyChart_QuickStart.unity`\n- 模板:`Assets/EasyChart/Demo/UIToolKit/NewUXMLTemplate.uxml`\n\n把导出的图表 `.uxml` 拖入 `NewUXMLTemplate.uxml`,然后确认场景中的 `UIDocument` 引用了该模板。\n\n#### 在 UI Builder 中的具体操作(建议按这个顺序)\n\n1. 打开 UI Builder(或直接双击你的页面 `.uxml`)。\n2. 在 Project 中找到你导出的图表 `.uxml`(通常位于 `Assets/EasyChart/LibraryUxml//...`)。\n3. 将该 `.uxml` 拖入页面的 Hierarchy(建议放进一个容器 `VisualElement` 内)。\n4. 保存页面 UXML。\n5. 回到场景,确认 `UIDocument` 引用的是你刚保存的页面 `.uxml`。\n\n#### 导出的图表 UXML 内部是什么\n\n导出的 `.uxml` 通常包含一个 `EasyChart.ChartElement`,并带有属性:\n\n- `profile-name`:通常对应 `ChartProfile` 的资产文件名(key)\n- `profile-guid`:更稳定的资产定位方式\n\n因此:\n\n- 如果你只是修改了 Profile,页面不会自动变化:需要重新导出(Mirror)让 UXML 更新。\n- 如果你改了 Profile 的资产文件名,导出的 `profile-name` 也会随之变化(建议保持命名稳定)。\n\n### Step 4:运行时加载/替换数据(按你的业务决定)\n\n`ChartProfile`/UXML 负责“样式与结构”,而数据来源通常来自你的业务逻辑。\n\n- 静态展示:直接在 Profile 的 `seriesData` 中填写\n- 动态展示:运行时写入/替换 `seriesData`(并保持 `SeriesData.id` 稳定性)\n\n---\n\n## 3. 图表库的目录建议\n\n建议把“配置源”和“导出物”分开:\n\n- `Assets/EasyChart/Library/Custom/`:你维护的 `ChartProfile`\n- `Assets/EasyChart/LibraryUxml/`:导出的 UXML(镜像/备份都会落在这个根目录下)\n\n当你使用多 Library 时,导出物通常会按库名分子目录:\n\n- `Assets/EasyChart/LibraryUxml//...`\n\n建议约定:\n\n- **Profile 只在 `Assets/EasyChart/Library/...` 下维护**(作为配置源)\n- **页面只引用 Mirror 的导出物**(作为组件库)\n- Backup 永远只当“历史快照”\n\n> 这样做的好处是:\n> - 配置源可读、可编辑\n> - 导出物可复用、可组合、可直接用于 UI Builder\n\n---\n\n## 4. 常见问题与排错\n\n- **UI Builder 里找不到导出的图表 UXML**\n - 优先检查 `Assets/EasyChart/LibraryUxml/` 下是否已生成\n - 如果你使用了多 Library,检查是否在 `Assets/EasyChart/LibraryUxml//` 下\n - Mirror/Backup 的导出物可能位于 `_Backups`,不建议页面引用 `_Backups` 里的文件\n\n- **页面里引用了 UXML 但运行时不显示**\n - 确认场景里的 `UIDocument` 引用了你编辑的页面 `.uxml`\n - 回到 Library Editor 的 Preview 看该 Profile 是否能正常显示(先排除 Profile 配置问题)\n\n- **修改了 Profile 但页面没变化**\n - Profile 是“源”,页面引用的是导出的 UXML\n - 修改 Profile 后请重新执行导出(Mirror),再回到页面刷新/保存\n\n- **UI Builder 里看得到组件,但运行时仍然不显示**\n - 优先确认:场景里的 `UIDocument` 是否真的引用了该页面(而不是另一个旧页面)\n - 再确认:Profile 在 Library Editor 的 Preview 是否能正常显示(先排除配置问题)\n\n---\n\n## 5. 下一步你应该看什么\n\n- 你想快速跑通一张图:`01-QuickStart.md`\n- 你要用 UGUI(Canvas/RectTransform)把图表用起来:`33-UGUIWorkflow.md`\n\n---\n\n## 6. 编辑器工作流与面板速查(Library Editor)\n\n本节把原先分散在多个章节中的“编辑器工作流与面板说明”合并到一起,作为你在 `EasyChart/Library Editor` 中操作时的速查。\n\n### 6.1 你在编辑的是什么?(ChartProfile)\n\n在 Library Editor 里被选中的图表,本质上是一个 `ChartProfile` 资产。\n\n- 它是可复用配置:同一个 Profile 可以被多个场景/Prefab 引用。\n- 它是可预览配置:在编辑器里修改后可以立即看到预览变化。\n\n### 6.2 Library Editor 的核心区域\n\n你可以把窗口理解为四块:\n\n- 左侧:Library(资源树)\n- 中部:Preview(预览区)\n- 右侧:Inspector(配置)\n- 右侧:Series(系列与数据)\n\n此外左侧通常还有 JSON Injection(JSON 注入面板)。\n\n### 6.3 Library 面板(资源树)\n\n功能概览:\n\n- 以树状结构展示图表库目录下的文件夹与 `ChartProfile`(`.asset`)。\n- 选中某个 `ChartProfile` 后,会驱动 Inspector/Series/Preview 的绑定与刷新。\n- 支持创建/重命名/删除/拖拽移动与排序。\n\n选择逻辑:\n\n- 选中的是文件夹:右侧 Inspector/Series 清空(无 Profile 可编辑)。\n- 选中的是 ChartProfile:右侧面板绑定到该 Profile。\n\n常用操作(标题栏与右键菜单,具体以版本为准):\n\n- Folder:New Folder / New Chart / Export Folder to UXML(Mirror/Backup)/ Rename / Delete\n- ChartProfile:Export to UXML / Clone / Rename / Delete\n\n### 6.4 Preview 面板(预览区)\n\nPreview 的作用是把当前选中的 `ChartProfile` 直接渲染出来,方便你在编辑配置时即时验证效果。\n\n常见问题:\n\n- 预览为空:确认是否至少 1 条 `Serie`,且该 serie 的 `seriesData` 不为空。\n- 数据有但显示怪:确认坐标系与 SeriesType 匹配,轴范围是否把数据排除在外。\n\n### 6.5 Inspector 面板(配置面板)\n\nInspector 的定位是以“配置视角”编辑 Profile 的序列化字段(坐标系、轴、网格、交互、图例等),并驱动 Preview 更新。\n\n提示:\n\n- 如果你发现某些字段修改后没效果,先确认页面引用的是你导出的 UXML,而不是直接引用 Profile。\n\n### 6.6 Series 面板(系列与数据)\n\nSeries 面板以“图表结构”的方式编辑 `ChartProfile.series`:\n\n- 添加/删除/排序系列\n- 为每条 serie 选择 `type` 并编辑 `settings`\n- 编辑 `seriesData`(数据点)\n\n### 6.7 JSON Injection 面板(JSON 注入)\n\n定位:把当前 Profile 的信息表达为可复制的 JSON,并支持解析 JSON 回写到当前 Profile。\n\n推荐工作流:\n\n1. 从当前 Profile 生成示例 JSON\n2. Copy 到外部编辑器做批量修改\n3. 粘贴回来并 ApplyToChart\n\n---\n\n## 7. 轴与范围(Axis & Range)\n\n### 7.1 AxisType:Category vs Value\n\n- Category(类目轴):用 `labels` 定义离散类目(A/B/C 或 周一/周二/周三)。\n- Value(数值轴):连续数值范围(0~100,-3~3,0~1e6)。\n\n#### 7.1.1 什么时候用 Category\n\n- X 轴是“文本标签序列”\n- 你希望数据点落在 `labels[i]` 上\n- 典型:柱状图(每类一组柱)、折线图(按类目对齐)\n\nCategory 的关键点:\n\n- `labels[0]` 对应类目索引 `0`\n- `labels[1]` 对应类目索引 `1`\n\n#### 7.1.2 什么时候用 Value\n\n- X 或 Y 轴是连续数值(例如时间戳、金额、温度)\n- 你希望轴可以按数值缩放/平移\n\nValue 的关键点:\n\n- 轴范围通常由自动范围计算得到(如果开启 auto range)\n- 你可以只锁定一端(例如固定最小值为 0,最大值自动)\n\n### 7.2 Category 轴:labels 与 LabelPlacement\n\n`labels` 决定类目个数与标签文本。\n\n`LabelPlacement` 影响对齐方式:\n\n- `Tick`:标签对齐刻度点,更适合 Line/Scatter。\n- `CellCenter`:标签对齐格子中心,更适合 Bar/Heatmap。\n\n常见现象:\n\n- 柱子落在两个标签之间:优先把 `LabelPlacement` 调成 `CellCenter`。\n\n### 7.3 Value 轴:autoRangeMin / autoRangeMax\n\n如果你看到范围“锁死”导致数据不显示,先把范围回退到全自动:\n\n- 打开 `autoRangeMin/autoRangeMax`\n\n确认可见后,再逐步加入业务约束(例如柱状图纵轴从 0 起)。\n\n#### 7.3.1 常见模板:Y 轴从 0 开始\n\n- `axisType = Value`\n- 固定 `minValue = 0`\n- `autoRangeMax = true`\n\n#### 7.3.2 常见模板:只锁定 Max(例如百分比)\n\n- 固定 `maxValue = 100`\n- `autoRangeMin = true`\n\n### 7.4 rounding / unit / labelFormat\n\n- rounding:让范围吸附到更“整”的单位。\n- unit:显示单位压缩(个/万/百万)。\n- labelFormat:控制数字格式(N0/N2/F1/百分比等)。\n\n#### 7.4.1 单位显示(showUnit / unitText)\n\n当数值很大(例如 10,000 以上)时,常见做法是让轴末端显示单位(如“万”“k”“M”)。\n\n#### 7.4.2 快速排错\n\n- 标签对不齐 / 柱子夹在标签中间:优先检查 Category 轴的 `LabelPlacement`\n- 轴范围很怪(特别大/特别小):检查是否锁死 min/max;检查 rounding/unit\n- 刻度小数太多:优先设置 `labelFormat`\n\n---\n\n## 8. Series 与数据(Serie / SeriesData)\n\n### 8.1 Serie(一条序列)\n\n在 `ChartProfile.series` 中每个元素是一个 `Serie`:\n\n- `name`\n- `type`\n- `visible`\n- `settings`\n- `labelSettings`\n- `seriesData`\n\n补充:`settings` 通常是多态对象(`SerializeReference`)。切换 `type` 时,会尝试保留每种类型上一次的 settings(编辑体验更好)。\n\n### 8.2 SeriesData(一个数据点)\n\n`SeriesData` 常见字段:\n\n- `id`:稳定标识(tooltip/hover/隐藏状态)。\n- `x`:X 坐标或 Category 索引。\n- `value`:主要数值。\n- `y`:第二维坐标(散点/热力图等)。\n- `z`:第三维(sizeMapping 等)。\n- `name`:点名称(Radar/Pie/Ring 等可能用到)。\n- `useColor` + `color`:点级颜色覆盖。\n\n如果启用了交互,建议保证 `SeriesData.id` 稳定,避免每次刷新数据都生成一套新的 id。\n\n### 8.3 SerieType 与坐标系的匹配\n\n- Cartesian2D:Line/Bar/Scatter/Heatmap\n- Polar2D:Radar\n\n不建议在同一个 ChartProfile(非 Pie)里混用 Polar 与 Cartesian 系列;如果你真的混用,要特别小心 axes/grid 语义是否一致。\n\n### 8.4 常用数据写法(按类型)\n\n#### 8.4.1 Line\n\n- 常见:Category X + Value Y\n - 数据点:`x=类目索引`,`value=数值`\n- 连续:Value X + Value Y\n - 数据点:`x=横轴数值`,`value=纵轴数值`\n\n#### 8.4.2 Bar\n\n- Category X + Value Y\n - 每个柱子一个点:`x=类目索引`,`value=柱高`\n- 并列:多条 Bar serie 共享同一套 Category X\n- 堆叠:`stacked=true` 且 `stackGroup` 相同的系列会堆叠\n\n#### 8.4.3 Scatter\n\n- 常用:X=Value,Y=Value\n- 数据点推荐显式写 `x/y`\n\n#### 8.4.4 Heatmap\n\n- 三元组:`x=列索引`,`y=行索引`,`value=强度`\n\n#### 8.4.5 Radar\n\n- 常见理解:`x=维度索引`,`value=该维度数值`,`name=维度名称`\n\n### 8.5 数据常见坑(按现象排查)\n\n- Category 图表 X 轴是 Category,但数据点 x 不是 0/1/2...\n - 现象:点/柱子不在标签上\n - 处理:确保 `x=类目索引`,或者把 X 改成 Value\n\n- 出现 NaN/Infinity\n - 现象:整张图不渲染、范围爆炸\n - 处理:在数据源侧过滤异常值\n\n- 看不到图(但 seriesData 不为空)\n - 检查:坐标系是否匹配(Cartesian vs Polar)\n - 检查:AxisType 是否匹配数据含义\n\n- 交互/tooltip 指向错乱\n - 检查:`SeriesData.id` 是否稳定(不要每次刷新都随机生成一套新的点)\n" }, { id: "00_03-UGUIWorkflow", relPath: "00_03-UGUIWorkflow.md", title: "UGUI工作流", content: "# UGUI工作流\n\n本章目标:用 UGUI(Canvas/RectTransform)把 EasyChart 图表用起来,并理解两种渲染模式的选择:\n\n- `ScreenSpaceOverlay`:画质最好(不使用 RenderTexture),但通常只在 Game 视图可见\n- `WorldSpace`:Scene/Game 都可见(使用 RenderTexture),适合 3D 世界空间 UI\n\n---\n\n## 1. 推荐方案:UGUIChartBridge\n\n推荐使用 `UGUIChartBridge` 组件。\n\n它的定位是:\n\n- 仍然使用 **UI Toolkit 的 `ChartElement`** 作为图表渲染核心\n- 通过桥接把图表“贴合”到某个 UGUI `RectTransform` 上\n\n因此它兼顾:\n\n- 图表能力与 UI Toolkit 渲染\n- UGUI 场景/Prefab 的搭建与布局习惯\n\n---\n\n## 2. 通用前置条件\n\n无论你选哪种模式,都建议先准备:\n\n- 一个要显示的 `ChartProfile`(建议从 `EasyChart/Library Editor` 克隆后修改得到)\n- 一个 `PanelSettings` 资产,并在 `UGUIChartBridge` 的 `Panel Settings Asset` 中指定\n\n> 备注:`Panel Settings Asset` 对字体渲染与整体稳定性更友好。\n\n---\n\n## 3. Screen Space Overlay(推荐用于 HUD/面板)\n\n### 适用场景\n\n- HUD、UI 面板、弹窗\n- 追求画质与清晰度\n\n### 特点\n\n- 不使用 RenderTexture\n- 通常只在 Game 视图可见\n\n### 搭建步骤(概览)\n\n1. 创建 `Canvas`\n2. 在 Canvas 下创建一个带 `RectTransform` 的节点(`Image` 或空物体均可)\n3. 添加 `UGUIChartBridge`\n4. 配置:\n - `Profile`\n - `Panel Settings Asset`\n - `Render Mode = ScreenSpaceOverlay`\n - `Sort Order`(用于层级覆盖;仅 Screen Space Overlay 模式生效)\n\n关键点:\n\n- 该模式会把图表渲染在一个运行时创建/复用的 `UIDocument` 里。\n- 如果被其他 UI 盖住,优先调大 `Sort Order`。\n\n---\n\n## 4. World Space(推荐用于 3D 世界空间面板)\n\n### 适用场景\n\n- 3D 世界里的看板/屏幕/面板\n- 希望 Scene 视图也能看到渲染结果\n\n### 特点\n\n- 使用 RenderTexture\n- Scene 与 Game 视图通常都可见\n- 画质可能略受 RenderTexture 分辨率影响\n\n### 搭建步骤(概览)\n\n1. 创建 `Canvas`\n2. 设置 `Render Mode = World Space`\n3. 在 Canvas 下创建一个带 `RectTransform` 的节点(建议 `RawImage`)\n4. 添加 `UGUIChartBridge`\n5. 配置:\n - `Profile`\n - `Panel Settings Asset`\n - `Render Mode = WorldSpace`\n\n关键点:\n\n- World Space 模式会创建并维护一个 `RenderTexture`,并通过 `RawImage` 显示。\n- 清晰度与 `RenderTexture` 分辨率强相关:分辨率通常来自目标 `RectTransform` 的宽高。\n - 如果图表模糊,请优先把目标 `RectTransform` 设大一些(例如 600x400+)。\n\n---\n\n## 5. 选型建议(快速结论)\n\n- 优先选 **ScreenSpaceOverlay**:\n - 你做的是传统 UI(HUD/面板)\n - 你最在意清晰度\n\n- 优先选 **WorldSpace**:\n - 你的图表要出现在 3D 世界里\n - 你希望 Scene 视图也能看到\n\n---\n\n## 6. 常见问题与排错\n\n- **运行时不显示**\n - 确认目标物体的 `RectTransform` 尺寸不是 0\n - 确认 `Profile` 已赋值,且该 Profile 在 Library Editor 的 Preview 中能正常显示\n - 如果字体显示异常,优先检查 `Panel Settings Asset` 是否为空\n\n- **World Space 模式图表模糊**\n - 提升目标 `RectTransform` 尺寸(会提高 RenderTexture 分辨率)\n - 避免运行时频繁剧烈缩放(会触发 RenderTexture 调整)\n\n---\n\n## 7. 备选:导出 UGUI Prefab\n\n如果你的版本提供 `Export UGUI Prefab`:\n\n- 你也可以将 Profile 导出为 UGUI 预制体并直接在 Canvas 下使用\n- 但对交互/兼容性的覆盖范围取决于导出器版本\n" }, { id: "00_04-RuntimeDataInjectionUIToolKit", relPath: "00_04-RuntimeDataInjectionUIToolKit.md", title: "运行时数据注入(UIToolKit)", content: "# 运行时数据注入(UIToolKit)\n\n本章介绍:在 UI Toolkit 工作流下,如何在运行时给 `ChartElement` 注入数据。\n\n对应脚本:`EasyChartDataSource`\n\n---\n\n## 1. 这套方案适合什么场景?\n\n- 你的图表是 UI Toolkit 体系(`UIDocument` + UXML + `ChartElement`)\n- 你希望提供一套“业务侧更容易调用”的注入 API(labels / values / x-y / pie / ring)\n- 或者你希望直接拿一段 JSON 注入(`ChartFeed`)\n\n---\n\n## 2. 快速上手(最推荐的流程)\n\n1. 在场景里准备 `UIDocument`,并确保 UXML 里有 `ChartElement`。\n2. 在挂着 `UIDocument` 的 GameObject 上添加组件:`EasyChartDataSource`。\n3. 在 Inspector 里填写:\n - `uiDocument`\n - `chartElementName`(默认 `main-chart`,对应 UXML 中 `ChartElement` 的 `name`)\n - `profile`(可选,但强烈建议填:让样式/Series 类型来自你在编辑器配置好的 `ChartProfile`)\n4. 在运行时用代码调用:\n - `SetCategoryLabels(...)`\n - `SetSeriesValues(...)` / `SetSeriesXY(...)`\n - 或 `ApplyJson(...)`\n\n组件内部会:\n\n- 从 `UIDocument.rootVisualElement` 里找到目标 `ChartElement`\n- 必要时用 `profile` 初始化图表 Data\n- 修改 `ChartElement.Data` 并调用 `RefreshData()`\n\n---\n\n## 3. Inspector 字段说明\n\n`EasyChartDataSource` 的核心配置字段:\n\n- `uiDocument`\n - 指向当前 UI 的 `UIDocument`。\n - 如果不填,脚本会尝试 `GetComponent()`。\n\n- `chartElementName`\n - 目标 `ChartElement` 的 `name`(UXML/USS 的那个 name)。默认值为 `main-chart`。\n - 如果你希望用 JSON 中的 `chartId/chartName` 自动定位,也可以让 `ChartElement` 的 `name` 与之保持一致(见第 5 节)。\n\n- `profile`\n - 可选。\n - 如果赋值,组件会将 `ChartElement.Profile = profile`,用于初始化/保持样式、Series 结构等。\n\n- `playAnimationOnRefresh`\n - 每次注入后调用 `RefreshData(..., playAnimation: playAnimationOnRefresh)`。\n\n- `allowCreateSeriesFromFeed`\n - 当你用 JSON(`ApplyJson`)注入时,如果 feed 中的 series 无法匹配到现有 Serie:\n - `false`(默认):不创建新 Serie,只更新匹配到的部分。\n - `true`:允许根据 feed 创建新的 Serie(可能触发重建 renderers)。\n\n---\n\n## 4. 常用注入 API(不写 JSON)\n\n### 4.1 设置类目轴标签\n\n`SetCategoryLabels(labels, axisId = AxisId.XBottom)`\n\n- 会把该轴设为 Category,并覆盖 `labels`。\n\n### 4.2 单序列 y 值(自动 x=0..n-1)\n\n`SetSeriesValues(\"Sales\", values)`\n\n- 默认会找到/创建一条 Serie(默认类型为 Line,不强制改类型)。\n- 写入 `SeriesData.value`,并把 `SeriesData.x` 设为索引。\n\n### 4.3 XY 点\n\n`SetSeriesXY(\"Scatter\", x, y)`\n\n- 把 `x[]` 写入 `SeriesData.x`,把 `y[]` 写入 `SeriesData.value`。\n\n### 4.4 Pie / Ring 注入\n\n- `SetPie(serieName, names, values)`\n - 强制该 Serie 为 `Pie` 类型。\n - 使用 `SeriesData.name` 作为切片名,`SeriesData.value` 为数值。\n\n- `SetRing(serieName, names, percents)`\n - 强制该 Serie 为 `RingChart` 类型。\n - 使用 `SeriesData.name` 作为环名,`SeriesData.value` 为进度值。\n\n---\n\n## 5. JSON 注入(ChartFeed)\n\n你可以用:`ApplyJson(json)`\n\n该方法会把 JSON 解析为 `ChartFeed` 并应用到 `ChartElement.Data`。\n\n### 5.1 `ChartFeed` 结构\n\n```json\n{\n \"chartId\": \"optional\",\n \"chartName\": \"optional\",\n \"axes\": [\n {\n \"axisId\": \"XBottom\",\n \"labels\": [\"Mon\", \"Tue\", \"Wed\"]\n }\n ],\n \"series\": [\n {\n \"serieId\": \"optional\",\n \"name\": \"optional\",\n \"type\": \"Line\",\n \"datas\": [\n { \"x\": 0, \"value\": 12 },\n { \"x\": 1, \"value\": 18 }\n ]\n }\n ]\n}\n```\n\n对应字段见运行时代码 `Scripts/Runtime/Feed/ChartFeed.cs`。\n\n### 5.2 ChartElement 的定位规则(`chartId` / `chartName`)\n\n`ApplyJson` 内部会尝试:\n\n- 如果 feed 提供了 `chartId`:优先 `rootVisualElement.Q(chartId)`\n- 其次如果提供了 `chartName`:尝试 `Q(chartName)`\n- 都找不到才退回到 `chartElementName`(默认 `main-chart`)\n\n因此:\n\n- 如果你只有一个图表,保持默认值也没问题。\n- 如果你一个 UI 里有多个 `ChartElement`,建议让每个图表的 `name` 与 feed 的 `chartId` 或 `chartName` 对齐。\n\n### 5.3 series 匹配与类型覆盖\n\n`ApplyJson` 会先检查 JSON 内是否出现过 `\"type\":`,若出现则认为你希望允许类型覆盖(`allowTypeOverride=true`)。\n\nSerie 匹配规则:\n\n- 如果 `serieId` 非空:按 `Serie.id` 匹配\n- 否则如果 `name` 非空:按 `Serie.name` 匹配\n- 否则(索引模式):按 feed 的序号匹配(第 i 条对第 i 条)\n\n当无法匹配到 Serie 时:\n\n- `allowCreateSeriesFromFeed=false`(默认):该条 feed 会被跳过(不创建)。\n- `allowCreateSeriesFromFeed=true`:会创建新的 Serie,并使用 feed 的 `type/name/serieId`。\n\n对已匹配到的 Serie:\n\n- 仅当 `allowTypeOverride=true` 且不是索引模式时,才会允许覆盖 `id/name/type`。\n\n---\n\n## 6. 常见问题与排错\n\n- **不显示 / TryGetChart 失败**\n - 确认 `uiDocument` 赋值正确\n - 确认 UXML 中 `ChartElement` 的 `name` 与 `chartElementName` 一致\n\n- **JSON 解析失败**\n - `EasyChartDataSource` 解析 JSON 时:\n - 会优先尝试 Newtonsoft(若项目里存在 `Newtonsoft.Json`)\n - 否则使用 Unity `JsonUtility`,并把 `type/axisId` 的字符串写法转换为枚举整数再解析\n - 建议先用一份已知能解析的 JSON(例如从编辑器 JSON 面板生成)再改。\n\n- **注入后 Series 对不上 / 更新错了线**\n - 优先使用 `serieId` 做稳定匹配。\n - 如果只用 `name`,且同名 Serie 存在多个,脚本会使用第一个并给 warning。\n\n- **JSON 想新增 Serie 但没新增**\n - 把 `allowCreateSeriesFromFeed` 打开。\n" }, { id: "00_05-RuntimeDataInjectionUGUI", relPath: "00_05-RuntimeDataInjectionUGUI.md", title: "运行时数据注入(UGUI)", content: "# 运行时数据注入(UGUI)\n\n对应脚本:`UGUIRuntimeJsonInjection`、`UGUIRuntimeJsonInjectionEditor`\n\n本章介绍:在 UGUI 工作流(`UGUIChartBridge`)下,如何通过 JSON 在运行时把数据注入到图表中。\n\n---\n\n## 1. 这套方案适合什么场景?\n\n- 你有一份来自服务器/业务层的 JSON(或你希望在运行时快速手工编辑 JSON)\n- 你希望像编辑器里 `JSON Injection` 面板一样,直接“生成示例 → 修改 → 应用”\n- 你已经通过 `ChartProfile` 把样式、轴、Series 类型等结构配置好了\n\n这套注入逻辑的定位是:**更新数据为主**,结构变更(比如新增 Series、强行覆盖 Series 类型)不是它的主要目标。\n\n---\n\n## 2. 快速上手(最推荐的流程)\n\n1. 在场景中搭好 `UGUIChartBridge`(并确保 `Profile` 已赋值)。\n2. 在同一个 GameObject 上添加组件:`UGUIRuntimeJsonInjection`。\n3. 点击 **Generate Example JSON** 生成一份与你当前 Profile 匹配的示例 JSON。\n4. 在 `JSON Content` 文本框里修改数据。\n5. 点击 **Apply JSON to Chart**。\n\n你会看到组件内部:\n\n- 解析 JSON → 转成 `ChartFeed`\n- 将 `ChartFeed` 应用到 `UGUIChartBridge.Profile`\n- 调用 `_bridge.Refresh()` 刷新图表\n\n---\n\n## 3. 组件与 Inspector 字段说明\n\n`UGUIRuntimeJsonInjection` 必须和 `UGUIChartBridge` 在同一个物体上(脚本有 `[RequireComponent(typeof(UGUIChartBridge))]`)。\n\n### 3.1 JSON Generation Settings\n\n- **Example Mode(`ChartJsonExampleMode`)**\n - 控制“生成示例 JSON”时的格式。\n - 一般建议先用 `Standard` 或 `Standard_Axis`(更直观)。\n\n- **Data Mode(`ChartJsonDatasMode`)**\n - 控制 `datas` 的数据表达方式。\n - `Standard`:`datas` 为对象数组(例如 `{ \"x\": 0, \"value\": 12 }`)。\n - `Values`:`datas` 为纯数值数组(更短)。\n - 备注:该格式需要走 `ChartJsonUtils` 的“灵活解析器”(基于 Newtonsoft 的反射解析)。如果你的项目里没有 Newtonsoft(`Newtonsoft.Json` / `Unity.Newtonsoft.Json`),可能会解析失败。\n - 因此 **推荐默认使用 `Standard`**,除非你确认项目已引入 Newtonsoft。\n\n- **API Envelope(`UseApiEnvelope`)**\n - 生成示例 JSON 时,是否包一层接口返回壳:\n - `{ \"code\": 200, \"message\": \"success\", \"data\": { ...真正的ChartFeed... } }`\n - 应用时也会尝试自动从壳里提取 `data`。\n\n- **Auto Generate(`AutoGenerateJson`)**\n - 当你切换 `Example Mode / Data Mode / API Envelope` 时,自动重新生成示例 JSON。\n\n### 3.2 JSON Content\n\n- **JSON Content(`JsonContent`)**\n - 你要注入的 JSON 字符串。\n - 如果为空,点击 Apply 时会直接警告并返回。\n\n---\n\n## 4. JSON 格式(ChartFeed)\n\n底层的数据模型是 `ChartFeed`:\n\n```json\n{\n \"chartId\": \"optional\",\n \"chartName\": \"optional\",\n \"axes\": [\n {\n \"axisId\": \"XBottom\",\n \"labels\": [\"Mon\", \"Tue\", \"Wed\"]\n }\n ],\n \"series\": [\n {\n \"serieId\": \"optional\",\n \"name\": \"optional\",\n \"type\": \"Line\",\n \"datas\": [\n { \"x\": 0, \"value\": 12 },\n { \"x\": 1, \"value\": 18 }\n ]\n }\n ]\n}\n```\n\n字段对应代码:\n\n- `chartId` / `chartName`\n - 在 `UGUIRuntimeJsonInjection` 的注入路径里 **不会覆盖** Profile 的 `chartId/chartName`(它调用 `ChartJsonUtils.ApplyFeedToProfile(profile, feed)`,内部 `allowMetaOverwrite=false`)。\n - 但这两个字段可以用来帮助别的注入器(例如 `EasyChartDataSource`)在 UI 树里“按名字/ID 寻找 ChartElement”。\n\n- `axes[]`\n - `axisId` 为 `AxisId` 枚举(如 `XBottom`、`XTop`、`YLeft`、`YRight` 等)。\n - `labels` 存在时会把该轴视为 Category,并直接覆盖 labels。\n\n- `series[]`\n - **匹配优先级**:\n - 如果给了 `serieId`:按 `Serie.id` 精确匹配\n - 否则如果给了 `name`:按 `Serie.name` 匹配\n - 否则(`serieId` 与 `name` 都为空):按索引匹配(第 0 个 feed 对应 Profile 第 0 个 serie)\n - `type`\n - 主要用于生成示例 JSON。\n - 在当前注入路径中:\n - 对已存在且能匹配到的 Serie:**不会强制改类型**(因为这里不允许覆盖 meta)。\n - 对“索引模式 + 超出 Profile 数量”而新建出来的 Serie:会使用 feed 里的 `type` 作为新 Serie 的类型。\n - `datas[]` 对应每个点:\n - `x/y/z/value` 数值\n - `id/name`(可选)\n - `useColor/color`(可选)\n\n---\n\n## 5. 应用时发生了什么?(注入流程)\n\n点击 **Apply JSON to Chart** 时:\n\n1. 若 JSON 是 API 壳(含 `data` 字段),会先尝试把 `data` 里的对象抽出来。\n2. 调用 `ChartJsonUtils.TryDeserializeFeed(json, out feed)` 反序列化为 `ChartFeed`。\n - 会优先尝试 Newtonsoft(如果项目里有),否则回退到 Unity `JsonUtility`。\n - `type: \"Line\"` / `axisId: \"XBottom\"` 这类字符串,也会在回退路径中被规范化为枚举值再解析。\n3. 调用 `ChartJsonUtils.ApplyFeedToProfile(_bridge.Profile, feed)` 把 feed 写回 Profile。\n4. 调用 `_bridge.Refresh()` 触发重绘。\n\n---\n\n## 6. 常见问题与排错\n\n- **点击 Apply 没反应 / 控制台有 warning:No UGUIChartBridge or ChartProfile found**\n - 确认对象上有 `UGUIChartBridge`\n - 确认 `UGUIChartBridge.Profile` 已赋值\n\n- **报错:Failed to parse JSON**\n - 先用 Generate 生成一份能解析的 JSON,再在它的基础上改。\n - 如果你的接口返回有外层包裹,优先勾选 `API Envelope`,或确保 JSON 的 `data` 字段内才是 `ChartFeed`。\n\n- **JSON 生效了但数据没变 / 只变了一部分**\n - 检查 `series` 的匹配方式(`serieId` / `name` / 索引模式)。\n - 如果你使用的是 `serieId/name` 匹配:确保 Profile 里确实存在对应的 Serie(该注入路径在这种模式下不会自动创建新 Serie)。\n - 如果你使用的是“索引模式”(`serieId` 与 `name` 都为空):\n - 当 feed 的 `series[]` 数量 **超过** Profile 的 Series 数量时,会自动补创建新的 Serie。\n - 如果你不希望自动创建,请给每条 serie 明确填 `name` 或 `serieId`。\n\n- **在编辑器 PlayMode 注入后,Profile 资产被改脏了**\n - 注入的本质是“把 feed 应用到 `ChartProfile` 上”。如果你把资产直接拖到桥接上,运行时改动可能会让该资产处于 dirty 状态。\n - 如果你不希望影响资产,建议在运行时对 Profile 做一份实例化拷贝再注入。\n" }, { id: "01_01-EditorWorkflow", relPath: "01_01-EditorWorkflow.md", title: "编辑器工作流与面板说明", content: "# 编辑器工作流与面板说明\n\n本章目标:让你清楚在 `EasyChart/Library Editor` 里“**哪里改什么**”,以及常见编辑流程(创建、克隆、预览、导出)。\n\n---\n\n## 1. 你在编辑的是什么?(ChartProfile)\n\n在 Library Editor 里被选中的图表,本质上是一个 `ChartProfile` 资产。\n\n- 它是 **可复用配置**:同一个 Profile 可以被多个场景/Prefab 引用。\n- 它是 **可预览配置**:在编辑器里修改后可以立即看到预览变化。\n\n建议:将你自己的 Profile 放到 `Assets/EasyChart/Library/Custom/`(或你团队约定目录)。\n\n---\n\n## 2. Library Editor 的三个核心区域\n\n虽然 UI 细节可能随版本略有变化,但整体可以按下面理解:\n\n### 2.1 左侧:资源树(Library Tree)\n\n这里展示的是图表资产(`ChartProfile`)所在的文件夹结构。\n\n常用操作:\n\n- 在文件夹上右键:\n - `New Folder...`:新建子目录\n - `New Chart...`:创建新的 `ChartProfile`\n- 在图表上右键:\n - `Clone`:复制一个新的 Profile(用于做变体)\n - `Export to UXML`:导出(若你的工作流需要把配置同步/落盘到 UXML)\n - `Ping`:在 Project 面板定位该资产\n - `Rename...` / `Delete`\n\n> 建议:用 `Clone` 来做“同款不同配色/不同数据规模”的图表变体,避免从零配置。\n\n### 2.2 右侧:Inspector(配置面板)\n\n这里是你主要编辑的地方。\n\n通常会包含:\n\n- **基础设置**:\n - `coordinateSystem`\n - `padding`(如果有)\n - `animationDuration`(如果有)\n- **Series 列表**:每个 Serie 代表一条线/一组柱/一个散点序列等\n- **Axes(坐标轴)**:\n - 选择使用哪个 `XAxisId/YAxisId`\n - 对应 `AxisConfig` 的显示、label、range、ticks 等\n- **Legend / Tooltip / Grid**:若你的版本已暴露这些设置\n\n> 经验:先把 `coordinateSystem`、`Series`、`Axes` 配好,其他属于“锦上添花”。\n\n### 2.3 预览区(Preview)\n\n用于检查:\n\n- 是否有数据\n- 轴范围是否正确\n- 标签是否拥挤/偏移\n- Tooltip / Legend 的交互(如果启用)\n\n预览出现异常时优先排查顺序:\n\n1. `coordinateSystem` 是否正确\n2. `series` 是否至少有 1 条且有数据点\n3. Axis 的 `axisType` 与数据 `x/y` 的含义是否匹配\n4. Value 轴是否被手动锁死范围(`autoRangeMin/autoRangeMax`)\n\n### 2.4 JSON Injection(JSON 注入面板)\n\n在左侧面板下方有一个 **JSON Injection** 区域,用于:\n\n- 快速生成当前选中 `ChartProfile` 的“注入 JSON 示例”\n- 将你粘贴/编辑的 JSON 应用回当前选中 Profile(`ApplyToChart`)\n\n常用控件:\n\n- **API Envelope**:是否使用 `{ code, message, data }` 外层包裹。\n - 打开时:生成 JSON 会包一层;解析时也可以识别并自动取 `data`。\n- **Feed Mode**:示例 JSON 的“结构层级/字段完整度”(用于兼容不同注入协议)。\n- **Datas Format**:`datas` 字段内部数据点格式(例如更紧凑的数组,或更易读的对象)。\n- **ApplyToChart**:将当前文本框中的 JSON 解析并写回到当前选中的 `ChartProfile`。\n\n---\n\n## 3. 推荐的编辑流程(从 0 到可复用)\n\n### Step 1:创建或选择一个 ChartProfile\n\n- 新建:在目标文件夹右键 `New Chart...`\n- 已有:在左侧树点击选择\n\n如果你希望使用“先克隆再修改”的方式(更推荐):\n\n- 先在窗口顶部工具栏 **Clone** 一个你自己的 Library\n- 然后在你自己的库里右键 Profile -> `Clone` 生成变体\n\n### Step 2:确定坐标系\n\n- `Cartesian2D`:Line/Bar/Scatter/Heatmap\n- `Polar2D`:Radar\n\n> 建议:坐标系先定下来,再选 SeriesType,避免后续切换带来风格/轴设置混淆。\n\n### Step 3:配置 Series\n\n- 添加 Series\n- 设置 `type`\n- 填充 `seriesData`\n\n建议:先用少量数据点(3~8 个)把效果跑通,再扩展数据量。\n\n### Step 4:配置 Axes\n\n最常见组合:\n\n- X:Category\n - `labels` 填文本\n- Y:Value\n - 开启自动范围(默认)\n\n当你希望更专业的轴显示:\n\n- 使用 `labelFormat`(例如 `F1`、`N0` 等)\n- 使用 `autoRangeMin/autoRangeMax` 只锁定一端范围\n- 如果是 Value 轴需要单位:\n - `showUnit=true`\n - `unitText=\"个\"/\"万\"`\n - `unitLabelStyle` 调字体/颜色/位置\n\n### Step 5:克隆出变体(推荐)\n\n当你需要同款图表做多个版本(配色、字号、轴显示略不同):\n\n- 右键图表 -> `Clone`\n- 修改差异项\n\n这样可以保证风格一致,也更便于版本管理。\n\n当你想把图表用于 UI:\n\n- UI Toolkit:导出为 UXML,然后在 UI Builder 中组装页面(可参考示例场景 `Assets/EasyChart/Demo/Scenes/EasyChart_QuickStart.unity` 与模板 `Assets/EasyChart/Demo/UIToolKit/NewUXMLTemplate.uxml`)\n- UGUI:可导出为 UGUI Prefab 并在 Canvas/RectTransform 工作流中使用\n\n---\n\n## 4. 常见坑(快速定位)\n\n- **看不到任何东西**\n - `seriesData` 是否为空\n - `AxisType` 是否和数据含义匹配(Category 轴配了 labels 吗)\n\n- **Value 轴显示很怪(范围太大/太小)**\n - 检查 `autoRangeMin/autoRangeMax`\n - 检查 rounding/unit 是否把范围吸附到不合适的单位上\n\n- **柱状图与标签不对齐**\n - 关注 `LabelPlacement`(Tick vs CellCenter)\n\n---\n\n## 下一章\n\n- `00-WorkflowAndLibrary.md`:轴类型、标签放置、自动范围、取整、单位显示等内容已合并到第 7 节\n" }, { id: "01_02-LibraryPanel", relPath: "01_02-LibraryPanel.md", title: "Library 面板(资源树)", content: "# Library 面板(资源树)\n\n本章说明 `Unity Easy Chart/Library Editor` 窗口左侧的 **Library** 面板:用于管理你的图表资产(`ChartProfile`)与文件夹结构,并决定右侧各面板正在编辑的是哪个 Profile。\n\n---\n\n## 功能概览\n\n- **展示结构**:以树状结构展示“图表库根目录”下的文件夹与 `ChartProfile`(`.asset`)。\n- **选择驱动编辑**:点击某个 `ChartProfile` 后,会驱动:\n - Inspector 面板绑定到该 Profile\n - Series 面板绑定到该 Profile 的 `series`\n - Preview 预览刷新\n - JSON Injection 生成示例 JSON(并可 Apply 回写到该 Profile)\n- **管理资产**:提供创建、重命名、删除、拖拽移动、展开/收起等操作。\n\n---\n\n## 界面结构说明\n\nLibrary 面板的顶部标题栏从左到右通常包含:\n\n- **Library**:标题。\n- **(当前库名称)**:括号内显示当前选中的库/根目录名称(用于区分你可能配置的多个库)。\n- **Menu(菜单按钮)**:常用操作入口(和右键菜单类似,但更集中)。\n- **Help(帮助按钮)**:打开本章节。\n\n窗口顶部全局工具栏(Window Toolbar)中,Library 下拉框右侧包含:\n\n- **+**:新增一个 Library。\n- **-**:删除当前 Library。\n- **Clone(克隆当前 Library)**:把当前 Library 复制为一个新 Library(详见下文)。\n\n标题栏下面是:\n\n- **资源树(TreeView)**:\n - 文件夹\n - `ChartProfile` 资产(图表配置文件)\n\n---\n\n## 选择逻辑(非常重要)\n\n- **选中的是文件夹**:\n - 右侧 Inspector/Series 会清空(因为没有具体 Profile 可以编辑)。\n - JSON Injection 会切换为“无选中 Profile”的示例或保持当前示例(具体以实现为准)。\n- **选中的是 ChartProfile**:\n - Inspector/Series 立即绑定到该 Profile 的序列化数据。\n - 任何字段变化会触发 Preview 延迟刷新(`delayCall`)。\n\n建议:如果你发现右侧面板没有内容,先确认左侧是否选中了一个 `ChartProfile`。\n\n---\n\n## 常用操作(标题栏 Menu)\n\n点击标题栏右侧 **Menu**(小菜单图标)会弹出操作菜单,常见项包括:\n\n- **New Chart**:在“目标文件夹”下创建新的 `ChartProfile`。\n- **New Folder**:在“目标文件夹”下创建新文件夹。\n- **Refresh**:重新扫描并重建树(当你在 Project 视图中手动移动/复制文件后很有用)。\n- **Expand All**:展开所有文件夹。\n- **Collapse All**:收起所有文件夹。\n- **Rename / Delete**:对“当前选中项”执行重命名/删除。\n - 如果当前选中的是库根目录,通常会被禁用。\n- **Export UGUI Prefab**(当选中的是 Profile 时可用):把选中 Profile 导出为 UGUI Prefab(用于运行时展示)。\n\n### 目标文件夹是如何决定的\n\nMenu 中的 **New Chart / New Folder** 会把资源创建在“目标文件夹”下:\n\n- 如果你当前选中的是 **文件夹**:目标就是该文件夹。\n- 如果你当前选中的是 **ChartProfile**:目标通常是该 Profile 所在的文件夹。\n- 如果没有选中或不明确:目标通常回退到库根目录。\n\n---\n\n## Clone Library(克隆当前 Library)\n\n当你需要把一整套图表库(包含 Profile 与 UXML)复制成一个新的库(用于分支/版本/主题变体等)时,可以使用窗口顶部工具栏里的 **Clone**。\n\n### 入口与操作\n\n- 点击 Library 下拉框右侧的 **Clone** 图标。\n- 输入新库名称并确认。\n\n### 克隆内容\n\n- `Assets/EasyChart/Library/<当前库>` 会被复制到 `Assets/EasyChart/Library/<新库>`。\n- `Assets/EasyChart/LibraryUxml/<当前库>` 会被复制到 `Assets/EasyChart/LibraryUxml/<新库>`(如果源库存在对应 UXML 目录)。\n\n### 限制与命名规则\n\n- `` 库不允许克隆。\n- 新名称会做基础清理(移除非法文件名字符),空白名称会被忽略。\n- 如果目标库已存在(同名文件夹已存在),会提示并取消。\n\n### 克隆后的行为\n\n- 会自动切换当前选中的 Library 为新库。\n- 会刷新 Library 下拉列表与左侧资源树,并触发右侧面板/预览的刷新。\n\n---\n\n## 常用操作(右键菜单)\n\n你也可以在树上的条目上 **右键**:\n\n### 右键文件夹\n\n- **New Folder...**:在该文件夹下创建子文件夹。\n- **New Chart...**:在该文件夹下创建新的 `ChartProfile`。\n- **Export Folder to UXML (Mirror/Backup)**:导出该文件夹下的内容到 UXML(用于备份/分发/版本化)。\n- **Rename...**:重命名文件夹。\n- **Delete**:删除文件夹(请谨慎,属于破坏性操作)。\n\n### 右键 ChartProfile\n\n- **Export to UXML**:导出当前 Profile 的 UXML。\n- **Clone**:克隆一个新的 Profile(用于快速派生相似图表)。\n- **Rename...**:重命名资产(同时会尝试同步更新 `profile.name` / `profile.chartName`)。\n- **Ping**:在 Project 视图中定位该资产。\n- **Delete**:删除资产。\n\n---\n\n## 拖拽移动与排序\n\nLibrary 树支持拖拽移动文件夹或 `ChartProfile`:\n\n- **拖拽 ChartProfile 到文件夹**:会触发 `AssetDatabase.MoveAsset`,把 `.asset` 移动到目标文件夹。\n- **拖拽文件夹到文件夹**:会把整个文件夹移动到目标文件夹下。\n\n注意:\n\n- 如果目标无效(例如拖到自身/子目录),会拒绝(鼠标提示为 Rejected)。\n- 移动后会自动刷新树。\n\n---\n\n## 重命名(双击与内联编辑)\n\n在树上 **双击** 条目会进入内联重命名流程(等价于执行 Rename)。\n\n实现上会对名字做基础清理(移除非法文件名字符)。如果你输入空白或与原名相同,会取消重命名。\n\n---\n\n## 常见问题与排错\n\n- **右侧面板为空**:\n - 先确认左侧是否选中了 `ChartProfile`(而不是文件夹)。\n- **改了名字但 chartName 没更新**:\n - ChartProfile 可能有额外同步逻辑;建议在 Inspector 里确认 `Chart Name` 字段是否一致。\n- **拖拽失败**:\n - 常见原因:拖到了自身、拖到子目录、或目标路径已存在同名资源。\n\n---\n\n## Help\n\n- 点击标题栏最右侧 **Help** 图标可回到本章节。\n" }, { id: "01_03-JsonInjectionPanel", relPath: "01_03-JsonInjectionPanel.md", title: "JSON Injection 面板", content: "# JSON Injection 面板\n\n本章说明 `Unity Easy Chart/Library Editor` 窗口左侧底部的 **JSON Injection** 面板。\n\n它的定位是:用一段可读/可复制的 JSON 来表达当前 `ChartProfile` 的配置(或外部导入的配置),并支持 **ApplyToChart** 将 JSON 解析后回写到选中的 Profile。\n\n---\n\n## 面板位置与作用\n\n- **位置**:Library 面板(资源树)下方。\n- **主要用途**:\n - **导出**:把当前选中 `ChartProfile` 转成示例 JSON(Feed)\n - **编辑**:在文本框里手动修改 JSON\n - **导入/应用**:点击 **ApplyToChart**,把 JSON 解析并应用到当前选中的 `ChartProfile`\n\n适用场景:\n\n- **调试**:快速定位“某个字段是否生效”。\n- **批量修改**:复制 JSON 到外部编辑器(支持多光标/查找替换),再粘贴回来 Apply。\n- **与外部系统对接**:例如你的工具链/脚本生成 Feed,再在编辑器里 Apply。\n\n---\n\n## 控件说明(标题栏)\n\n标题栏从左到右一般包含:\n\n- **Min/Max**(按钮文字会变化)\n - 用于切换面板高度。\n - `Min`:收起为较小高度(更偏“辅助工具”)。\n - `Max`:展开为较大高度(更适合长 JSON)。\n\n- **ApplyToChart**(图标按钮)\n - 把当前文本框里的 JSON 尝试解析为 Feed,并应用到选中的 `ChartProfile`。\n - 成功后会:\n - 标记资产为 Dirty 并 `SaveAssets()`\n - 刷新 Series 列表\n - 刷新 Preview\n\n- **Help**(图标按钮)\n - 打开本章节。\n\n---\n\n## 控件说明(按钮行)\n\n标题栏下方还有一行按钮(可能会自动换行):\n\n- **API Envelope**(图标开关)\n - 控制示例 JSON 是否包裹为“API 返回格式”。\n - 你需要把 Feed 直接交给某个 HTTP API/服务时,这个选项会更方便。\n - 切换后会重新生成示例,并覆盖文本框(详见“覆盖规则”)。\n\n- **Feed Mode**(下拉框)\n - 用于控制“示例 JSON 输出包含哪些层级/字段”。\n - 选项来自内部枚举(常见有):\n - `Lite`\n - `Standard / ID`\n - `Standard / Default`\n - `Standard / With Axes`\n - `Full`\n - 一般建议:\n - **快速看结构**:用 `Lite`\n - **需要稳定引用**:用 `Standard / ID`\n - **需要包含轴配置**:用 `Standard / With Axes`\n - **需要完整复制/迁移**:用 `Full`\n\n- **Datas Format**(下拉框)\n - 控制 `seriesData`(数据点)字段的输出格式。\n - 常见选项:\n - `Values`:更精简,偏“只关心数值”。\n - `Standard`:默认格式,适合一般编辑与 Apply。\n - `Full`:更完整(可能包含更多字段/结构),适合迁移/还原。\n\n- **Copy**(图标按钮)\n - 复制当前文本框内容到剪贴板。\n\n---\n\n## 文本框与“覆盖规则”(非常重要)\n\nJSON 文本框是可编辑的,但为了避免你手写的内容被自动覆盖,面板内部有一个“脏标记”逻辑:\n\n- **只要你手动改过文本框内容**,就会认为“用户已修改”(dirty)。\n- 当处于 dirty 状态时:\n - 编辑器不会自动用示例 JSON 覆盖你的内容。\n- 但当你切换以下选项时,会**强制覆盖**(同时清除 dirty):\n - `API Envelope`\n - `Feed Mode`\n - `Datas Format`\n - 或在切换选中 Profile 时(会重置为该 Profile 的示例)\n\n建议:\n\n- 如果你要做大幅改动:\n - 先 Copy 到外部编辑器改\n - 改完再粘贴回来 Apply\n\n---\n\n## ApplyToChart 的行为与注意事项\n\n- **ApplyToChart 会修改当前选中的 `ChartProfile` 资产**。\n- 如果 JSON 解析失败,会在 Console 输出错误:\n - `ApplyToChart failed: invalid JSON or unsupported format.`\n- `Full` 模式下会允许覆盖更多“Meta/结构”信息(例如某些标识/配置),因此更强大也更危险。\n\n建议:\n\n- 在 Apply 前确保:\n - 左侧已选中正确的 `ChartProfile`\n - JSON 格式正确(括号/逗号)\n - 你理解当前 Feed Mode 会覆盖哪些内容\n\n---\n\n## 推荐工作流\n\n### 1) 从当前 Profile 导出并微调\n\n- 选中一个 `ChartProfile`\n- 选择合适的 `Feed Mode` / `Datas Format`\n- Copy 到外部编辑器微调\n- 粘贴回来\n- ApplyToChart\n\n### 2) 从外部导入配置\n\n- 把外部 JSON 粘贴到文本框\n- ApplyToChart\n- 去 Inspector / Series 进一步精调\n\n---\n\n## Help\n\n- 点击标题栏最右侧 **Help** 图标可回到本章节。\n" }, { id: "02_04-PreviewPanel", relPath: "02_04-PreviewPanel.md", title: "Preview 面板", content: "# Preview 面板\n\n本章说明 `Unity Easy Chart/Library Editor` 中间区域顶部的 **Preview** 面板。\n\nPreview 的作用是:把当前选中的 `ChartProfile` 直接渲染出来,让你在编辑配置时能即时看到结果。\n\n---\n\n## Preview 会在什么时候刷新\n\nPreview 刷新是“延迟刷新”(避免你连续拖动/输入时频繁重绘):\n\n- 当你在 **Inspector** 或 **Series** 面板修改任何绑定字段时,会触发一次 `ScheduleUpdatePreview()`。\n- 当你在 **JSON Injection** 面板点击 **ApplyToChart** 后,会触发刷新。\n- 当你在左侧 **Library** 树切换到另一个 `ChartProfile` 时,会刷新到新 Profile。\n\n实现上会把刷新放到 `EditorApplication.delayCall`,因此你可能会感觉到“改完后稍后才更新”——这是预期行为。\n\n---\n\n## Preview 显示什么\n\n- Preview 使用一个运行时的图表组件(例如 `ChartElement`)进行绘制。\n- Preview 会直接读取当前选中 `ChartProfile` 的数据并渲染。\n\n你可以把 Preview 理解为:\n\n- **你编辑的就是它渲染的**\n- **你看到的就是运行时的效果**(大多数情况下)\n\n---\n\n## 常见问题与排错\n\n### 1) 预览为空\n\n优先排查:\n\n- 是否选中了一个 `ChartProfile`\n- 是否至少存在一条 `Serie`\n- `seriesData` 是否为空(没有数据点)\n\n### 2) 数据有但显示很怪 / 看不到\n\n常见原因:\n\n- **坐标系与 SeriesType 不匹配**:例如 Profile 是 `Polar2D`,但 Series 选择了非 Radar 的类型。\n- **轴范围/数据范围不匹配**:例如数值全都落在轴范围之外。\n- **分类轴可见数量(VisibleCount)太小**:导致只显示一小段。\n\n### 3) Console 报错 “Preview refresh failed”\n\n当刷新过程中出现异常,会在 Console 输出:\n\n- `[EasyChartLibraryWindow] Preview refresh failed: ...`\n\n这通常意味着:\n\n- 某个配置组合不合法\n- 或某个字段值超出预期(例如 null / NaN)\n\n建议处理:\n\n- 先回退最近一次改动\n- 再逐步改回去定位哪一个字段触发异常\n\n---\n\n## 提示\n\n- Preview 只负责“呈现结果”,结构性问题通常需要回到 **Inspector/Series/JSON Injection** 去修。\n- 如果你在短时间内修改了很多字段,Preview 可能在最后一次改动后才统一刷新(这是为了性能)。\n\n---\n\n## Help\n\n- 点击标题栏最右侧 **Help** 图标可回到本章节。\n" }, { id: "02_05-InspectorPanel", relPath: "02_05-InspectorPanel.md", title: "Inspector 面板", content: "# Inspector 面板\n\n本章说明 `Unity Easy Chart/Library Editor` 中间区域底部的 **Inspector** 面板。\n\nInspector 的定位是:以“配置视角”直接编辑当前选中 `ChartProfile` 的序列化字段(坐标系、轴、网格、交互、图例等),并驱动 Preview 实时更新。\n\n---\n\n## 面板结构概览\n\n当你在左侧 Library 树选中一个 `ChartProfile` 后,Inspector 会构建一组折叠面板(Foldout),通常包括:\n\n- **Chart Settings**:图表基础设置(例如背景、名称等)\n- **Coordinate System**:坐标系选择与相关项\n- **Axis Settings**:轴配置(X/Y 或 Angle/Radius)\n- **Grid Settings**:网格相关配置\n- **Hover Settings**:悬停/提示相关配置\n- **Legend Settings**:图例配置\n\n提示:\n\n- 如果你选中的是文件夹而不是 `ChartProfile`,Inspector 会清空(这是正常的)。\n\n---\n\n## Chart Settings(常用)\n\n### Chart Name\n\nInspector 里有 `Chart Name` 文本框(来自 `ChartProfile.chartName`)。它不只是显示名,还会参与资产重命名流程:\n\n- 当你在该字段输入新名字并 **失去焦点** 或按 **Enter**:\n - 编辑器会尝试把 `.asset` 文件重命名为该名字\n - 同时尽量保持 `profile.name` 与 `profile.chartName` 同步\n\n注意:\n\n- 名字会被做文件名清理(非法字符会被移除/替换)。\n- 如果重命名失败(例如同名冲突),会弹出 Error,并回退字段。\n\n### Background\n\n`Background` 通常是一个子折叠块,包含背景颜色/透明度等字段(具体以版本为准)。\n\n---\n\n## Coordinate System(坐标系)\n\n这里会显示 `coordinateSystem` 选择项。\n\n坐标系会影响:\n\n- Series 可选的类型/语义(例如 Polar2D 常见是 Radar;Pie 类属于特殊布局)\n- Axis Settings 中显示的是 Cartesian(X/Y)还是 Polar(Angle/Radius)配置\n\n建议:\n\n- 在开始配置前先确定坐标系,避免后续大规模调整。\n\n---\n\n## Axis Settings(轴配置)\n\n### 轴选择(X Axis / Y Axis)\n\n在 Cartesian 模式下,顶部会提供 X/Y 轴的选择下拉:\n\n- **X Axis**:例如 `XBottom` / `XTop`\n- **Y Axis**:例如 `YLeft` / `YRight`\n\n选择改变时会:\n\n- 确保轴列表里存在对应 AxisId 的元素(必要时自动创建)\n- 刷新下面的 Axis 字段 UI\n- 触发 Preview 刷新\n\n### 单个 Axis 的常见字段\n\n每个 Axis 配置通常包含:\n\n- `axisType`:Category / Value 等\n- `visible`:是否显示\n- `color` / `width`:轴线样式\n\n#### LabelTexts(分类轴标签)\n\nInspector 会提供一个 `LabelTexts`(内部字段名 `labels`)用于配置分类标签。\n\n#### Range(范围)\n\n常见有:\n\n- `minValue` / `maxValue`\n- `autoRangeMin` / `autoRangeMax`(开关)\n- `autoRangeRounding`(自动范围的取整策略)\n- `autoRangeUnit`(某些取整策略下会出现)\n\n#### Ticks / VisibleCount\n\n如果轴支持自动刻度:\n\n- `autoTicks` 开启时会显示 `splitCount`。\n- 对 **Category Axis** 来说,这个字段会显示为 **VisibleCount**(表示可见分类数量)。\n\n#### Category Auto Scroll(分类轴自动滚动)\n\n如果轴支持分类滚动,常见字段包括:\n\n- `categoryAutoScroll`:是否自动滚动(跑马灯效果)。\n- `categorySmoothScroll`:是否平滑滚动。\n- `categoryScrollInterval`:滚动间隔。\n- `categoryScrollStep`:每次滚动步长。\n\n#### Unit(单位显示)\n\n常见字段:\n\n- `showUnit`:是否显示单位。\n- `unitText`:单位文本(例如 `ms`/`%`/`MB`)。\n- `unitLabelStyle`:单位文本样式。\n\n---\n\n## Polar Axis(极坐标轴)\n\n当 `coordinateSystem = Polar2D` 时,Axis Settings 会显示 `polarAxes`:\n\n- **Angle Axis**(angleAxis)\n- **Radius Axis**(radiusAxis)\n\n常见字段含义与 Cartesian 类似:\n\n- `labels`:角度/维度标签(Radar 的维度名称通常来自这里)。\n- `visible/color/width`:轴线样式。\n- `showLabels/fontSize/labelColor/labelPosition/labelOffset`:标签显示控制。\n- `autoRangeMin/autoRangeMax/minValue/maxValue`:半径轴范围。\n- `autoTicks/splitCount`:刻度数量。\n\n---\n\n## Grid Settings(字段说明,仅 Cartesian2D)\n\nGrid Settings 在 Cartesian2D 下可见,核心字段来自 `cartesianGrid`:\n\n- **xGridColor / xGridLineWidth**:X 方向网格线颜色与线宽。\n- **yGridColor / yGridLineWidth**:Y 方向网格线颜色与线宽。\n\n如果需要虚线:\n\n- `xGridDashed` / `yGridDashed`:是否虚线。\n- `xGridDashLength` / `yGridDashLength`:虚线实线段长度。\n- `xGridDashGap` / `yGridDashGap`:虚线间隔。\n- `xGridDashOffset` / `yGridDashOffset`:虚线偏移。\n\n---\n\n## Hover Settings(字段说明,仅 Cartesian2D)\n\nHover Settings 在 Cartesian2D 下可见,核心字段来自 `hover`:\n\n- **cursorLineColor**:悬停光标线颜色。\n- **cursorLineWidth**:线宽。\n- **cursorLineDashed**:是否虚线。\n- **cursorLineDashLength / cursorLineDashGap / cursorLineDashOffset**:虚线参数。\n\n---\n\n## Legend Settings(字段说明)\n\nLegend Settings 来自 `legendSettings`(某些情况下会被自动隐藏,见下文)。\n\n- **enabled**:是否显示图例。\n- **position**:图例位置(Top/Bottom/Left/Right)。\n- **fontSize / color**:文字大小与颜色。\n- **backgroundColor**:图例背景色。\n- **itemSpacing**:图例项间距。\n- **offset**:相对边缘的偏移。\n - 当 offset 为默认值时,会随着 position 自动给一个常用偏移(例如 Bottom 默认 `y=-30`)。\n\n---\n\n## Legend Settings(可能会自动隐藏)\n\n当图表是“纯 Pie 系列”(只有 Pie/Ring/Pie3D,没有非 Pie 系列)时,Legend Settings 可能会被自动隐藏。\n\n这是为了避免在某些布局下显示无意义或冲突的图例配置。\n\n---\n\n## 编辑建议与排错\n\n- **改动较多时**:建议配合顶部工具栏的 `Save` 保存资产。\n- **修改了关键结构**(例如坐标系、轴类型、Series Type):\n - 改完观察 Preview 是否正确刷新\n - 如出现不一致,尝试切换一下选中 Profile 触发重建\n\n---\n\n## Help\n\n- 点击标题栏最右侧 **Help** 图标可回到本章节。\n" }, { id: "02_06-SeriesPanel", relPath: "02_06-SeriesPanel.md", title: "Series 面板(数据与系列)", content: "# Series 面板(数据与系列)\n\n本章说明 `Unity Easy Chart/Library Editor` 右侧的 **Series** 面板。\n\nSeries 面板以“图表结构”的方式编辑 `ChartProfile.series`:你可以添加/删除/排序系列,设置每条 Serie 的类型与参数,并直接编辑 `seriesData`(数据点)。\n\n---\n\n## 面板结构概览\n\n当你选中一个 `ChartProfile` 后,Series 面板会显示:\n\n- 一组 Serie 卡片(每个卡片对应 `series[i]`)\n- 最底部的 **+ Add Series** 按钮\n\n每个 Serie 卡片通常由三部分组成:\n\n- **Header(标题行)**:折叠按钮 + 标题\n- **Body(主体)**:Name / Id / Type / Settings / Data\n- **Footer(右下角控制)**:↑ ↓ X\n\n---\n\n## Header:折叠/展开\n\n- Header 左侧有一个小按钮:\n - `▼` 表示已展开\n - `▶` 表示已折叠\n- 折叠状态会被记住(按 Profile + index 存储),用于减少长配置的视觉负担。\n\n---\n\n## Name 与 Serie Id\n\n### Name\n\n- `Name` 字段用于显示与编辑该 Serie 的名称。\n- 当你修改 Name 时,卡片标题会同步更新,并触发 Preview 刷新。\n\n### Serie Id(只读)\n\n如果该 Serie 支持 `id` 字段,面板会显示:\n\n- **Serie Id**(只读文本框)\n- **Copy**(按钮)复制 id 到剪贴板\n\n这个 id 常用于:\n\n- 交互/高亮/外部系统引用某条 serie\n- 保持引用稳定(尤其是你会重排/增删 series 时)\n\n---\n\n## Type(系列类型)与兼容性提示\n\n### Type 下拉框\n\n- `Type` 用于选择 SerieType(Line/Bar/Scatter/Pie/Radar…)。\n- 下拉框会基于注册表提供可选类型;如果当前类型不在列表里,会临时插入以保证可见。\n\n### 兼容性警告\n\n当 SeriesType 与 Profile 的 `coordinateSystem` 不兼容时,Series 面板会显示一段警告文字:\n\n- 仍然允许渲染(不会强制阻止)\n- 但会提示坐标轴/网格语义可能不一致\n\n典型例子:\n\n- Profile 是 `Polar2D`,但 SeriesType 选择了 Line/Bar(不推荐)\n\n### Pro-only 类型限制\n\n某些类型在 Free 版本不可用(例如 RingChart / HorizontalBar / Heatmap / Pie3D 等)。\n\n- 当你尝试选择这些类型时,如果未安装 Pro:\n - 会显示提示文本\n - 并自动把下拉框回退到原来的类型(不会修改资产)\n\n---\n\n## Settings(系列参数)\n\nSeries 面板会为每条 serie 显示一组 Settings 配置:\n\n- 根折叠块名称会随类型变化(例如 `LineSettings` / `BarSettings` / `PieSettings` …)。\n- 某些类型会有更细分的子折叠(例如 Ring 的 layout/valueMapping 等)。\n\n提示:\n\n- 切换 Type 可能会触发“Settings 实例替换”(managedReference 结构变化)。\n- 发生替换时会延迟一帧重建 UI,以避免序列化句柄失效。\n\n---\n\n## Data:seriesData(数据点)\n\nSeries 面板里会直接展示 `seriesData` 数组(Unity 的默认数组编辑器)。\n\n- 默认会强制展开(便于编辑)。\n- 当你增删/修改数据点时,会触发 Preview 刷新。\n\n建议:\n\n- 数据点较多时,可以配合 JSON Injection 面板进行批量编辑。\n\n---\n\n## Footer:排序与删除(渲染顺序)\n\n每个 serie 卡片右下角有三个按钮:\n\n- **↑**:把当前 serie 上移一位(`MoveArrayElement(index, index-1)`)\n- **↓**:把当前 serie 下移一位(`MoveArrayElement(index, index+1)`)\n- **X**:删除当前 serie(`DeleteArrayElementAtIndex(index)`)\n\n渲染顺序提示:\n\n- 通常 **后面的 serie 会绘制在更上层**。\n- 因此你可以用 ↑↓ 来控制遮挡关系(例如点/线盖住柱子)。\n\n---\n\n## + Add Series(新增系列)\n\n点击底部 **+ Add Series**:\n\n- 会在 `series` 数组末尾插入一个新元素。\n- 注意:如果当前已经存在至少一条 serie,Unity 的 `InsertArrayElementAtIndex(arraySize)` 会 **复制最后一个元素**(包括 type/settings)。\n- 如果这是第一条 serie,会根据坐标系设置默认类型:\n - Polar2D:默认 Radar\n - 其他:默认 Line\n\n新增后通常会:\n\n- 自动填入名称(如 `Serie N`)\n- 触发 `EnsureRuntimeData()`\n- 刷新 Series 列表与 Preview\n\n---\n\n## 推荐工作流\n\n### 1) 从零创建一张基础图\n\n- + Add Series\n- Type 选择 Line 或 Bar\n- 在 seriesData 里加入几个点\n- 去 Inspector 调整轴范围/可见数量\n\n### 2) 调整遮挡关系\n\n- 用 ↑↓ 调整 series 顺序\n- 观察 Preview 中的层级变化\n\n### 3) 大量数据/批量修改\n\n- 在 JSON Injection 中切换 `Datas Format`\n- Copy 到外部编辑器批量生成/替换数据\n- 粘贴回来 ApplyToChart\n\n---\n\n## Help\n\n- 点击标题栏最右侧 **Help** 图标可回到本章节。\n" }, { id: "03_01-LineChart", relPath: "03_01-LineChart.md", title: "折线图(Line)", content: "# 折线图(Line)\n\n本章目标:把“折线图在 EasyChart 里到底怎么配置、数据怎么解释、哪些字段会影响渲染”的关键点一次讲清楚。\n\n---\n\n## 1. 适用场景\n\n- 趋势变化(时间序列/按类目变化)\n- 多条曲线对比(同一套 X 维度)\n- 需要平滑/阶梯/直线等线型表达\n\n---\n\n## 2. 最小可用配置(Checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. 轴(Axis Settings)\n - X:通常用 **Category**(填写 `labels`)或 **Value**(数值横轴)\n - Y:通常用 **Value**\n3. Series(Series 面板)\n - 添加 1 条 `Serie`\n - `Serie.type = Line`\n - `Serie.seriesData` 至少 2 个点(折线需要至少两点才能连线)\n\n---\n\n## 3. Inspector 对应字段(你在面板里看到的是什么)\n\n- **ChartProfile / Coordinate System**\n - `coordinateSystem`\n\n- **Axis Settings**(与坐标系相关)\n - Cartesian:`cartesian.xAxisId / cartesian.yAxisId`\n - 轴列表:`axes[]`(每个 Axis 的 `axisType/labels/min/max/autoRange/...`)\n\n- **Series**(每条曲线)\n - `series[i].type = Line`\n - `series[i].settings`:实际类型为 `LineSettings`\n - `stroke`:线条(线型/颜色/宽度/纹理等)\n - `point`:点样式(开关/大小/纹理等)\n - `hover`:悬停强调(开启后才会有“拾取半径/高亮”)\n - `area`:面积填充(折线下方填充)\n - `series[i].labelSettings`:数据点标签(是否显示、格式、小数位、偏移等)\n\n---\n\n## 4. SeriesData 字段解释(最关键,按运行时代码)\n\n折线图渲染时使用:\n\n- **X 坐标**:`SeriesData.x`\n- **Y 数值**:`SeriesData.value`\n- `SeriesData.y` 在折线图中 **不参与渲染**(不要把 y 当作折线的纵值)。\n\n两种常见写法:\n\n### 4.1 Category X + Value Y(最常用)\n\n- X 轴设为 `AxisType.Category`\n- `AxisConfig.labels = [\"A\",\"B\",\"C\",...]`\n- 数据点:\n - `x = 类目索引`(0/1/2...,会按索引映射到 labels)\n - `value = 数值`\n\n### 4.2 Value X + Value Y(数值横轴)\n\n- X 轴设为 `AxisType.Value`\n- 数据点:\n - `x = 横轴数值`\n - `value = 纵轴数值`\n\n> 额外说明:当你的轴维度是 **X=Value, Y=Category** 时,运行时会认为是“笛卡尔坐标转置”(transposed),会在渲染时交换 X/Y 的解释方式(用于横向布局的场景)。\n\n---\n\n## 5. 常用样式配置(LineSettings)\n\n- **线型**:`LineSettings.stroke.lineType`\n - `Straight`:直线\n - `Step`:阶梯线\n - `Smooth`:平滑曲线\n\n- **线条粗细/颜色**:`LineSettings.stroke.width` / `LineSettings.stroke.color`\n\n- **点标记**:`LineSettings.point.show/size/textureFill`\n\n- **面积填充**:`LineSettings.area.show` + `LineSettings.area.textureFill`\n\n---\n\n## 6. 常见坑与排错(按现象)\n\n- **线断裂 / 不显示**\n - 检查 `SeriesData.value` 是否出现 `NaN/Infinity`\n - 折线至少 2 个有效点\n\n- **点不在标签上(Category X)**\n - 检查 `x` 是否为 0..(labels.Count-1) 的索引\n - 检查是否误把 `x` 写成了“类目字符串”(EasyChart 这里是索引,不是字符串)\n\n- **我填了 y,但图不对**\n - 折线图纵值用的是 `value`,不是 `y`\n\n---\n\n## 7. 深入参考\n\n- 轴与范围、Series 与数据:`00-WorkflowAndLibrary.md`\n- 常用配方:`05-CommonRecipes.md`\n- FAQ:`06-FAQ.md`\n" }, { id: "03_02-BarChart", relPath: "03_02-BarChart.md", title: "柱状图(Bar)", content: "# 柱状图(Bar)\n\n本章目标:把柱状图在 EasyChart 中的 **数据解释规则(`SeriesData.x/value`)**、**并列/堆叠的真实行为**、以及常用样式字段一次讲清楚。\n\n---\n\n## 1. 适用场景\n\n- 类目对比(A/B/C 的值对比)\n- 分组对比(同一类目下多条 Bar 并列)\n- 堆叠总量(同一类目下多条 Bar 堆叠)\n\n---\n\n## 2. 最小可用配置(Checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. 轴\n - X:通常 `AxisType.Category`(填写 `labels`)\n - Y:通常 `AxisType.Value`(建议从 0 起)\n3. Series\n - 添加 1 条 `Serie`\n - `Serie.type = Bar`\n - `Serie.seriesData` 至少 1 个点\n\n---\n\n## 3. Inspector 对应字段\n\n- **Axis Settings**\n - `cartesian.xAxisId / cartesian.yAxisId`\n - `axes[]`(X/Y 对应 AxisConfig)\n\n- **Series**\n - `series[i].type = Bar`\n - `series[i].settings`:实际类型为 `BarSettings`\n - `barWidth`\n - `stacked` / `stackGroup`\n - `barGap` / `categoryGap`\n - `cornerRadius` / `cornerSegments`\n - `textureFill`(颜色/纹理)\n - `border` / `background`\n - `hover`(开启后支持拾取/高亮)\n\n---\n\n## 4. SeriesData 字段解释(按运行时代码)\n\n柱状图核心使用:\n\n- **类目/横向位置**:`SeriesData.x`\n - 运行时会对 `x` 做 `RoundToInt`,因此**你应该把它当作“类目索引”来用**。\n\n- **柱高**:`SeriesData.value`\n\n- `SeriesData.y` / `SeriesData.z` 对 Bar 图 **不参与渲染**(不要把 y 当柱高)。\n\n---\n\n## 5. 最常见模板:Category X + Value Y\n\n### 5.1 X 轴(Category)\n\n- `AxisType = Category`\n- `labels = [\"A\",\"B\",\"C\",...]`\n- 推荐 `labelPlacement = CellCenter`(柱子更容易居中对齐)\n\n### 5.2 数据写法\n\n- `x = 类目索引`(0/1/2...)\n- `value = 柱高`\n\n---\n\n## 6. 分组柱(多系列并列)的真实规则\n\n并列柱的关键点是:\n\n- 多条 `Serie`,都 `type=Bar`\n- 所有 serie 共享同一套 X 类目(同一套 labels)\n- 每条 serie 的每个点使用相同的 `x` 索引落到同一个类目\n\n并列间距相关字段:\n\n- `BarSettings.barGap`:同一类目下,各组柱之间的间隔\n- `BarSettings.categoryGap`:类目与类目之间的额外间隔(会影响边缘留白)\n\n---\n\n## 7. 堆叠柱(stacked)的真实规则\n\n堆叠发生在“同一个 stackGroup 的 Bar serie”之间:\n\n- `BarSettings.stacked = true`\n- `BarSettings.stackGroup = \"Group1\"`\n\n运行时堆叠逻辑要点:\n\n- 对同一个 `x`(类目索引)分别累计正值/负值(正负会分开堆)\n- 堆叠后的每根柱顶部 = 当前累计底 + `value`\n\n---\n\n## 8. 常见坑与排错\n\n- **柱子夹在两个标签之间 / 对不齐**\n - 优先检查 X 轴 `labelPlacement`(建议 `CellCenter`)\n - 确认 `x` 是否为整数索引(运行时会 Round)\n\n- **柱子从中间起,不从 0 起**\n - 检查 Y 轴(Value Axis)的 `autoRangeMin` 是否关闭并锁定 `minValue=0`\n\n- **堆叠结果不对**\n - 检查是否所有需要堆叠的 serie 都设置了相同的 `stackGroup`\n - 注意:正值和负值会分别堆叠\n\n---\n\n## 9. 深入参考\n\n- 轴与范围、Series 与数据:`00-WorkflowAndLibrary.md`\n- 常用配方:`05-CommonRecipes.md`\n- FAQ:`06-FAQ.md`\n" }, { id: "03_03-ScatterChart", relPath: "03_03-ScatterChart.md", title: "散点图(Scatter)", content: "# 散点图(Scatter)\n\n本章目标:把散点图在 EasyChart 的“数据字段约定”说清楚,尤其是 `SeriesData.y/value` 的兼容逻辑,以及 `z` 维度如何驱动 `sizeMapping`。\n\n---\n\n## 1. 适用场景\n\n- 相关性分析(X/Y 两个数值维度)\n- 分布展示(点云)\n- 异常点识别(离群点)\n\n---\n\n## 2. 最小可用配置(Checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. 轴\n - 常见:X=Value,Y=Value\n3. Series\n - 添加 1 条 `Serie`\n - `Serie.type = Scatter`\n - `Serie.seriesData` 至少 2 个点\n\n---\n\n## 3. Inspector 对应字段\n\n- `series[i].type = Scatter`\n- `series[i].settings`:实际类型为 `ScatterSettings`\n - `point`:点样式(显示/大小/纹理)\n - `hover`:悬停高亮(enabled/pickRadius/scale 等,具体字段以版本为准)\n - `sizeMapping`:点大小映射\n\n---\n\n## 4. SeriesData 字段解释(按运行时代码)\n\n散点图渲染时使用:\n\n- **X 坐标**:`SeriesData.x`\n- **Y 坐标**:优先使用 `SeriesData.y`\n - 兼容逻辑:如果 `y == 0` 且 `value != 0`,运行时会把 `value` 当成 y 来用\n- **点大小映射维度**:`SeriesData.z`(当 `sizeMapping.enabled=true` 时)\n\n因此你有两种常见写法:\n\n### 4.1 推荐写法(显式 X/Y)\n\n- `x = X 值`\n- `y = Y 值`\n\n### 4.2 兼容写法(旧数据:用 value 当 y)\n\n- `x = X 值`\n- `value = Y 值`\n- `y = 0`\n\n> 建议:新数据直接写 `y`,这样不会跟“点的其他含义(value)”混在一起。\n\n---\n\n## 5. 标准模板:Value X + Value Y\n\n- X 轴:`AxisType = Value`\n- Y 轴:`AxisType = Value`\n- 数据:使用 4.1 的写法(x/y)\n\n---\n\n## 6. sizeMapping(点大小映射)的真实规则\n\n当 `ScatterSettings.sizeMapping.enabled = true` 时:\n\n- 点半径会根据 `SeriesData.z` 映射得到\n- 映射范围:`minValue/maxValue` → `minSize/maxSize`\n- 若 `clamp = true`,会把超范围的 t 值夹到 0..1\n- `curve` 会对 t 做一次曲线变换(用于非线性映射)\n\n如果你发现 sizeMapping “没效果”,优先检查:\n\n- 是否真的给了 `z` 值(默认 0)\n- `minValue/maxValue` 是否相等(相等会导致映射退化)\n\n---\n\n## 7. 常见坑与排错\n\n- **点全在一条水平线**\n - 你可能只填了 `value`,但又把 `y` 也写成了非 0(兼容逻辑不会触发)\n - 建议统一用 `y` 作为纵坐标\n\n- **hover 没反应**\n - `ScatterSettings.hover.enabled` 必须开启\n - `pickRadius` 太小也会导致很难拾取\n\n- **点太小/太大**\n - 调整 `ScatterSettings.point.size`\n - 或检查 sizeMapping 的 `minSize/maxSize`\n\n---\n\n## 8. 深入参考\n\n- 轴与范围、Series 与数据:`00-WorkflowAndLibrary.md`\n- 常用配方:`05-CommonRecipes.md`\n- FAQ:`06-FAQ.md`\n" }, { id: "03_04-HeatmapChart", relPath: "03_04-HeatmapChart.md", title: "热力图(Heatmap)", content: "# 热力图(Heatmap)\n\n本章目标:把热力图在 EasyChart 里的“坐标/格子如何映射、`SeriesData` 字段怎么解释、颜色范围怎么算”的规则讲清楚,并标明它是 Pro 功能。\n\n---\n\n## 1. 适用场景\n\n- 二维矩阵数据展示(行/列)\n- 密度/强度可视化\n\n---\n\n## 2. 重要说明(Pro 功能)\n\n- `SerieType.Heatmap` 的渲染器由 `EasyChartProBootstrap` 注册。\n- 如果没有安装/启用 EasyChartPro:该 serie 会被当作“动态渲染器”尝试创建,但通常不会显示。\n\n---\n\n## 3. 最小可用配置(Checklist)\n\n1. `ChartProfile.coordinateSystem = Cartesian2D`\n2. 轴(Axis Settings)\n - 最常用:X=Category(列),Y=Category(行)\n - 也支持 X/Y 使用 Value 轴(见第 6 节)\n3. Series\n - 添加 1 条 `Serie`\n - `Serie.type = Heatmap`\n - `Serie.seriesData` 至少 1 个点\n\n---\n\n## 4. Inspector 对应字段\n\n- **Series**\n - `series[i].type = Heatmap`\n - `series[i].settings`:实际类型为 `HeatmapSettings`\n - `renderMode`:Grid / Gradient / Contour\n - `cellGapPx`\n - `xSplitCount` / `ySplitCount`(当 X/Y 使用 Value 轴时用于分格)\n - `autoRange / minValue / maxValue`\n - `lowColor / midColor / highColor`\n - `clamp`\n - `influenceMode`:None / Bleed / Smooth\n - `bleed / smooth / gradient / contour` 子配置\n\n---\n\n## 5. SeriesData 字段解释(按运行时代码)\n\n热力图每个数据点对应一个“格子/像素区域”,运行时使用:\n\n- **X 坐标(列)**:`SeriesData.x`\n- **Y 坐标(行)**:`SeriesData.y`\n- **强度值**:`SeriesData.value`\n- **颜色覆盖**:如果 `SeriesData.useColor = true`,则直接用 `SeriesData.color`,跳过 `low/mid/high` 的插值。\n\n> 注意:Heatmap 的 `x/y` 不接受字符串类目;Category 轴场景下仍然用“索引”。\n\n---\n\n## 6. 标准模板:二维 Category(X/Y)+ value 强度(最常用)\n\n### 6.1 X 轴(Category:列)\n\n- `AxisType = Category`\n- `labels = [\"Col0\",\"Col1\",...]`\n\n### 6.2 Y 轴(Category:行)\n\n- `AxisType = Category`\n- `labels = [\"Row0\",\"Row1\",...]`\n\n### 6.3 数据写法\n\n- `x = 列索引`(运行时会对 `x` 做 `RoundToInt`)\n- `y = 行索引`(运行时会对 `y` 做 `RoundToInt`)\n- `value = 强度`\n\n### 6.4 重要细节:Category 轴下“格子数”与 `labelPlacement`\n\n运行时会用 Axis 的 `labelPlacement` 决定“按 labels.Count 分格”还是“按 labels.Count-1 分格”:\n\n- `CategoryLabelPlacement.CellCenter`\n - X 方向格子数 = `labels.Count`\n - Y 方向格子数 = `labels.Count`\n\n- 其他(非 CellCenter)\n - X 方向格子数 = `max(1, labels.Count - 1)`\n - Y 方向格子数 = `max(1, labels.Count - 1)`\n\n这会直接影响你应该填的 `x/y` 索引范围。\n\n---\n\n## 7. Value 轴热力图(X/Y 为数值轴)\n\n当 X 或 Y 使用 `AxisType.Value` 时:\n\n- 格子数量不再来自 labels,而来自:\n - X:`HeatmapSettings.xSplitCount`\n - Y:`HeatmapSettings.ySplitCount`\n\n- `SeriesData.x/y` 会先根据 `_xMin/_xMax`、`_yMin/_yMax` 归一化,再映射到格子索引。\n\n这适合做“连续值域上的密度/强度分布”。\n\n---\n\n## 8. 常见坑与排错\n\n- **全部一个颜色 / 对比不明显**\n - 检查 `HeatmapSettings.autoRange` 是否开启\n - 或者手动设定 `minValue/maxValue`\n - 也检查是否所有点的 `value` 都几乎一样\n\n- **颜色不按 low/mid/high 来**\n - 检查是否某些点启用了 `useColor=true`(会覆盖调色盘插值)\n\n- **格子对不上(索引越界/偏一格)**\n - 检查 Category 轴的 `labelPlacement` 是否为 `CellCenter`\n - 根据第 6.4 节确定正确的格子数与索引范围\n\n- **格子缝太大/太密**\n - 调 `HeatmapSettings.cellGapPx`\n\n---\n\n## 9. 深入参考\n\n- 轴与范围、Series 与数据:`00-WorkflowAndLibrary.md`\n- 常用配方:`05-CommonRecipes.md`\n- FAQ:`06-FAQ.md`\n" }, { id: "03_05-RadarChart", relPath: "03_05-RadarChart.md", title: "雷达图(Radar)", content: "# 雷达图(Radar)\n\n本章目标:把雷达图在 EasyChart 中“维度标签来源、数值范围如何计算、数据点顺序如何解释”的规则讲清楚,并对应到 Inspector 字段。\n\n---\n\n## 1. 适用场景\n\n- 多维指标对比\n- 能力/属性雷达\n\n---\n\n## 2. 最小可用配置(Checklist)\n\n1. `ChartProfile.coordinateSystem = Polar2D`\n2. Series\n - 添加 1 条 `Serie`\n - `Serie.type = Radar`\n - `Serie.seriesData` 建议至少 3 个点(少于等于 2 个维度运行时不会绘制/无法 hover)\n3. PolarAxes(推荐配置)\n - `polarAxes.angleAxis.labels`:维度名\n - `polarAxes.radiusAxis`:数值范围(可自动/手动)\n\n---\n\n## 3. Inspector 对应字段\n\n- **ChartProfile / Coordinate System**\n - `coordinateSystem = Polar2D`\n\n- **PolarAxes(建议用这套来配 Radar 的轴)**\n - `polarAxes.angleAxis.labels`:维度标签\n - `polarAxes.radiusAxis.autoRangeMin/autoRangeMax/minValue/maxValue/autoRangeRounding/labelFormat/...`\n\n- **Series**\n - `series[i].type = Radar`\n - `series[i].settings`:实际类型为 `RadarSettings`\n - `radar`:布局(startAngleDeg / clockwise / innerRadius / outerRadius / plot / background)\n - `stroke`:折线样式\n - `area`:面积填充\n - `point`:点样式(点显示也会影响 hover 拾取半径)\n - `series[i].labelSettings`:数据点标签(可显示维度名与数值)\n\n---\n\n## 4. SeriesData 字段解释(按运行时代码)\n\nRadar 的关键点是:**维度顺序由 `seriesData` 列表索引决定**。\n\n- **数值**:使用 `SeriesData.value`\n- **维度索引**:使用“点在 `seriesData` 里的位置 i”(0..dimensionCount-1)\n- `SeriesData.x` 在 Radar 渲染中 **不参与定位**(不要依赖 x 来表达维度)\n\n维度数量(dimensionCount)来源:\n\n1. 优先 `Data.PolarAxes.angleAxis.labels.Count`\n2. 如果没配 angleAxis.labels,则使用(优先)某个 Category Axis 的 labels(见第 5 节)\n3. 再不行就用 `seriesData.Count`(或多条 serie 取最大 count)\n\n---\n\n## 5. 维度标签(Dimension Label)的真实来源顺序\n\n运行时维度名按以下优先级解析:\n\n1. `polarAxes.angleAxis.labels[i]`\n2. `axes[]` 里某个 `AxisType.Category` 的 `labels[i]`\n - 会优先匹配 `Data.XAxisId` 对应的 Category 轴\n3. `seriesData[i].name`\n4. 都没有时显示 `Dim i`\n\n> 建议:做 Radar 时直接用 `polarAxes.angleAxis.labels` 统一管理维度名;`SeriesData.name` 作为兜底。\n\n---\n\n## 6. 数值范围(Radius Axis)如何计算\n\nRadar 的半径值域使用 `SeriesData.value` 计算:\n\n- 默认会对所有 Radar serie 的 value 做自动范围(auto range)\n- 如果你配置了 `polarAxes.radiusAxis`:\n - `autoRangeMin/autoRangeMax` 会决定 min/max 是否自动\n - `minValue/maxValue` 在对应 autoRange 关闭时生效\n - `autoRangeRounding` 会对自动出来的 min/max 做“整十/整百/自定义单位”的取整\n - `labelFormat` 会影响 tooltip/标签的格式化\n\n---\n\n## 7. 常见坑与排错\n\n- **看不到雷达图**\n - 检查 `coordinateSystem` 是否为 `Polar2D`\n - 维度数必须大于 2(labels 或 seriesData 至少 3)\n\n- **维度对不上/顺序错乱**\n - Radar 不看 `x`,它按 `seriesData` 的列表顺序当维度顺序\n - 需要你在 `seriesData` 里按维度顺序放点\n\n- **hover 很难触发**\n - Radar 的拾取半径和 `RadarSettings.point.size` 相关\n - 如果 `point.show=false`,拾取半径会变成 0(基本不可 hover)\n\n---\n\n## 8. 深入参考\n\n- Series 与数据:`00-WorkflowAndLibrary.md`\n- 常用配方:`05-CommonRecipes.md`\n- FAQ:`06-FAQ.md`\n" }, { id: "03_06-PieChart", relPath: "03_06-PieChart.md", title: "饼图(Pie)", content: "# 饼图(Pie)\n\n本章目标:把饼图在 EasyChart 中“数据字段怎么解释、布局/聚合/图例/交互如何生效、哪些行为有隐藏前提”的规则讲清楚,并对应到 Inspector 字段。\n\n---\n\n## 1. 适用场景\n\n- 展示“占比/构成”\n- 强调每个分类在整体中的比例\n\n不适合:\n\n- 类别过多(通常 > 8~12 个会很难读)\n- 需要精确对比微小差异(更建议条形图)\n\n---\n\n## 2. 最小可用配置(Checklist)\n\n1. `coordinateSystem`:Pie 不依赖 Cartesian/Polar 坐标系(按你的 Profile 现有设置即可)\n2. 添加 1 条 `Serie`:\n - `type = Pie`\n - `settings = PieSettings`\n - `seriesData` 至少 1 个点\n3. 确保每个点的 `value > 0`\n\n> 注意:Pie 当前会忽略 `value <= 0` 的点。\n\n---\n\n## 3. 重要限制(按运行时代码)\n\n- **只绘制第一条可见 Pie serie**:当前渲染器会遍历 `Data.Series`,找到第一条 `type=Pie` 且可见的 serie 绘制后就 `break`。\n- Pie 的切片隐藏状态来自 `ChartInteractionState.HiddenPieSliceIds`,会在图例点击时写入/移除。\n\n---\n\n## 4. SeriesData 字段解释(按运行时代码)\n\nPie 主要使用:\n\n- `value`:该切片的数值(权重)\n- `name`:切片名称(推荐填写)\n- `useColor + color`:切片自定义颜色(可选)\n- `id`:切片稳定标识(用于隐藏/交互状态,建议保持稳定)\n\n### 4.1 最推荐写法:显式写 name + value\n\n- `SeriesData.name = \"苹果\"`\n- `SeriesData.value = 12`\n\n### 4.2 name 为空时的名称来源\n\n当 `SeriesData.name` 为空时,Pie 会尝试从 **labels** 获取名称,但有一个前提:\n\n- 如果 `ChartData.CoordinateSystem == None`(通常是纯 Pie/无坐标系图),运行时会 **跳过 labels 兜底**,只使用 `SeriesData.name`。\n\n在非 None 坐标系下,名称兜底顺序是:\n\n- 优先使用 `Data.Cartesian.xAxisId` 对应的 Category 轴 labels\n- 若找不到,再使用任意一个 Category 轴 labels\n- 最后兜底:`Slice {index}`\n\n> 因此:如果你不想依赖轴配置,建议直接填 `SeriesData.name`。\n\n### 4.3 颜色来源\n\n- 若点上 `useColor=true`:使用 `SeriesData.color`\n- 否则:使用内置调色板按顺序分配\n\n---\n\n## 5. 常用配置(PieSettings)\n\nPie 的 `settings` 是 `PieSettings`,主要包含:\n\n- `layout`:布局(角度/半径/间隙/居中偏移等)\n- `hover`:悬停交互(explode)\n- `aggregation`:聚合(TopN + Others)\n- `legend`:Pie 专用图例配置(只在“纯 Pie 图表”场景下替代全局 legend)\n\n### 4.1 layout(PieLayoutSettings)\n\n常用字段:\n\n- `startAngleDeg`:起始角度(默认 -90 让第一片从“正上方”开始)\n- `clockwise`:顺时针/逆时针\n- `angleRangeDeg`:角度范围(默认 360,做“半圆饼”可设 180 等)\n- `outerRadius`:外半径\n - `<= 0`:自动\n - `0~1`:按控件大小比例\n - `> 1`:像素\n- `innerRadius`:内半径(Pie 通常为 0;>0 会变成“中间有洞”的效果,但更推荐用 RingChart 类型做圆环)\n- `innerRadiusColor`:内圈填充颜色\n- `sliceGapPx`:切片间隙(像素)\n- `sliceGapType`:间隙计算方式(Radial/Translate/Uniform)\n- `cornerRadius`:圆角(像素,受切片厚度限制)\n- `plot.padding`:留白(避免切片/外侧标签被裁剪)\n- `plot.centerOffset`:中心偏移\n\n### 4.2 hover(PieHoverSettings)\n\n- `hover.enabled`:是否启用悬停交互\n- `hover.explodeType`:\n - `Translate`:整体平移\n - `Pull`:拉伸(拉出)\n - `Color`:变亮\n - `Stroke`:描边强调\n- `hover.explodeDistance`:平移/拉伸距离(像素)\n\n### 4.3 aggregation(PieAggregationSettings)\n\n当分类很多时,可以把小项合并为 `Others`:\n\n- `aggregation.enabled = true`\n- `keepTopN`:保留前 N 个,其余合并\n- `sortByValue`:是否按 `value` 值排序后再取 TopN\n- `othersName`:Others 的名称\n- `useOthersColor + othersColor`:Others 颜色\n\n> 注意:聚合只在 `keepTopN > 0` 且切片数量超过 N 时生效。\n\n---\n\n## 6. 图例(PieLegendSettings)与“隐藏切片”交互\n\n当图表是“纯 Pie 图表”(只包含 Pie/RingChart/Pie3D 且没有其它类型)时:\n\n- 图例会优先使用 `PieSettings.legend`(或 RingChartSettings/Pie3DSettings 上的 legend),而不是 `ChartData.legend`。\n- 点击图例条目会切换 `HiddenPieSliceIds`:\n - 普通切片:`SeriesData.id`(若为空则用索引字符串)\n - 聚合的 Others:固定使用 `__ec_pie_others__`\n\n`PieLegendSettings.source` 会影响“图例条目从哪里来”:\n\n- `Slice`:每个切片一条(默认)\n- `RingSlice`:为 RingChart/RingSlice 场景提供 label 来源(优先 PolarAxes.angleAxis.labels)\n- `Series`:每条 serie 一条(不再是切片级)\n\n---\n\n## 7. 标签(SerieLabelSettings)\n\nPie 标签由 `Serie.labelSettings` 控制:\n\n- `show`:是否显示\n- `fontSize / color / decimalPlaces`:字体与数值格式\n- `showName`:是否在标签里显示切片名称\n- `position`:`Outside/Inside/Center`\n- `offset`:偏移\n\n---\n\n## 8. 常见坑(按现象排查)\n\n- **某些切片不显示**\n - 检查该点 `value` 是否 `<= 0`\n\n- **切片名称不是我想要的**\n - 推荐:直接填写 `SeriesData.name`\n - 如果依赖 labels:确保你确实有一个 Category 轴并填写了 `labels`,且顺序与数据点索引一致\n\n- **切片颜色每次不一样/难以控制**\n - 对需要固定颜色的切片:给该点设置 `useColor=true` + `color`\n\n- **隐藏/交互状态不稳定**\n - 确保每个点的 `SeriesData.id` 稳定(不要每次刷新都重新生成一套新 id)\n\n---\n\n## 9. 下一章\n\n- 圆环图(RingChart):`16-RingChart.md`\n" }, { id: "03_07-RingChart", relPath: "03_07-RingChart.md", title: "圆环图(RingChart)", content: "# 圆环图(RingChart)\n\n本章目标:说明 RingChart 的真实含义(它不是 donut pie),并把它在 EasyChart 中的 `SeriesData` 字段解释、`RingChartSettings` 配置与 Pro/基础差异对齐到运行时代码。\n\n---\n\n## 1. RingChart 是什么?(非常重要)\n\n在 EasyChart 当前实现中:\n\n- `SerieType.RingChart` 渲染的是 **多条“进度环”**(每个数据点一条环)\n- 每条环都是 **完整 360° 的底环 + 一段进度弧**\n- 它不是“多个 slice 分割圆周”的饼图\n\n如果你想要“占比构成”的圆环饼图(donut pie):\n\n- 目前更接近 `SerieType.Pie` + `layout.innerRadius > 0`\n- 但推荐仍按你的设计决定:\n - 构成占比:用 Pie\n - 多指标进度/完成率:用 RingChart\n\n---\n\n## 2. 重要说明(Pro 功能)\n\n- `SerieType.RingChart` 的渲染器由 `EasyChartProBootstrap` 注册。\n- 未安装/启用 Pro 时,该 serie 通常不会显示。\n\n---\n\n## 3. 最小可用配置(Checklist)\n\n1. 添加 1 条 `Serie`\n - `type = RingChart`\n - `settings = RingChartSettings`\n - `seriesData` 至少 1 个点\n2. 每个点的 `value > 0`\n\n> 注意:RingChart 会忽略 `value <= 0` 的点。\n\n---\n\n## 4. SeriesData 字段解释(按运行时代码)\n\nRingChart 主要使用:\n\n- `value`:环的进度“原始值”\n- `name`:环的名称\n- `useColor + color`:环颜色(点级颜色覆盖)\n- `id`:稳定标识(用于 legend/隐藏状态,建议保持稳定)\n\n### 4.1 Percent 模式(默认):value 同时支持 0~1 和 0~100\n\n当 `RingChartSettings.valueMapping.mode = Percent`(默认)时:\n\n- `value <= 0`:该环会被过滤\n- `0~1`:按比例(0.72 = 72%)\n- `> 1`:按百分比(72 = 72%,运行时会除以 100)\n\n建议:团队统一用一种写法(全 0~1 或全 0~100),避免误用。\n\n### 4.2 Range 模式:把 value 映射到 0..1\n\n当 `RingChartSettings.valueMapping.mode = Range` 时:\n\n- 会先确定范围 `min/max`:\n - `autoRange=true`:从所有 ring 的 value 自动求范围\n - `autoRange=false`:使用 `minValue/maxValue`\n- 再把 `value` 映射为 `(value-min)/(max-min)` 并 clamp 到 0..1\n\n### 4.3 name 为空时的名称来源\n\n当 `SeriesData.name` 为空时,RingChart 会尝试从 labels 兜底:\n\n- 若 `ChartData.CoordinateSystem == None`:不会使用 labels 兜底,最终会退回到 `Ring {i}`\n- 否则优先:`Data.PolarAxes.angleAxis.labels[i]`\n- 再否则:Cartesian/任意 Category 轴的 `labels[i]`\n- 最终兜底:`Ring {i}`\n\n如果你不想依赖 PolarAxes 配置,建议直接填 `SeriesData.name`。\n\n---\n\n## 5. Inspector 对应字段(RingChartSettings)\n\n- `series[i].type = RingChart`\n- `series[i].settings`:实际类型为 `RingChartSettings`\n - `layout`:角度/半径/内外环/留白/中心偏移\n - `valueMapping`:Percent/Range 映射规则\n - `hover`:悬停强调(Translate/Pull/Color/Stroke)\n - `legend`:RingChart 的图例设置(纯 Pie 图表时生效)\n - `showBackground/backgroundAlpha/backgroundColor`:背景环\n - `cornerRadius`:端头圆角\n - `ringGapPx`:环与环间距\n\n### 5.1 layout(RingChartLayoutSettings)\n\n常用字段:\n\n- `startAngleDeg`:起始角度\n- `clockwise`:顺/逆时针\n- `angleRangeDeg`:默认 360;可做“半环进度”\n- `outerRadius`:外半径(<=0 自动;0~1 比例;>1 像素)\n- `innerRadius`:内半径(0~1 比例或像素)\n- `plot.padding`:留白(避免 hover/标签被裁剪)\n- `plot.centerOffset`:中心偏移\n\n### 5.2 hover(PieHoverSettings)\n\n- `hover.enabled`:是否启用\n- `hover.explodeType`:\n - `Translate`:整条环平移\n - `Pull`:拉伸(拉出)\n - `Color`:变亮\n - `Stroke`:描边强调\n- `hover.explodeDistance`:平移/拉伸距离(像素)\n\n### 5.3 背景环与间距\n\n- `showBackground`:是否绘制背景环\n- `backgroundAlpha`:背景环透明度(最终会乘到颜色 alpha 上)\n- `backgroundColor`:背景环颜色(alpha=0 时会回退用 ring 本身颜色)\n- `ringGapPx`:环与环的间距\n- `cornerRadius`:端头圆角(受环厚度限制)\n\n---\n\n## 6. 图例与隐藏交互(与 Pie 共用 HiddenPieSliceIds)\n\n- RingChart 与 Pie 共用 `ChartInteractionState.HiddenPieSliceIds`。\n- 每条环的隐藏 key:优先 `SeriesData.id`,否则使用索引字符串。\n- 图例条目 label 的来源受 `PieLegendSettings.source` 影响:\n - `RingSlice` 会优先从 `polarAxes.angleAxis.labels` 取名称。\n\n---\n\n## 7. 标签(SerieLabelSettings)\n\nRingChart 的标签同样使用 `Serie.labelSettings`:\n\n- `show`:是否显示\n- `showName`:是否显示 name\n- `decimalPlaces`:数值小数位(注意:这里显示的是原始 `value`,不是自动乘 100 的百分比文本)\n- `position`:\n - `Outside`:外侧标签 + 引导线\n - `Center`:贴在环中间\n\n---\n\n## 6. 常见坑(按现象排查)\n\n- **我以为它是 donut pie,但显示不对**\n - 这是多环进度图:每个点是一条“进度环”\n\n- **进度不对(比如填 75 结果几乎满圈)**\n - `value>1` 会按百分比除以 100\n - 如果你想 75%:用 `0.75` 或 `75`\n\n- **某些环不显示**\n - 检查 `value <= 0` 是否被过滤\n\n- **交互/隐藏状态不稳定**\n - 确保 `SeriesData.id` 稳定\n\n---\n\n## 8. 深入参考\n\n- 饼图(构成占比):`15-PieChart.md`\n- Series 数据结构:`00-WorkflowAndLibrary.md`\n" }, { id: "04_08-CommonRecipes", relPath: "04_08-CommonRecipes.md", title: "常用配方(Common Recipes)", content: "# 常用配方(Common Recipes)\n\n本章目标:把常用图表的“最低可用配置”整理成可照抄的配方(Series + Axis + 常见坑),用于你快速创建/排错。\n\n---\n\n## 0. 开始之前:最小检查清单\n\n当你遇到“怎么都看不到/显示很怪”时,先按顺序检查:\n\n1. `coordinateSystem` 是否与 SeriesType 匹配(Cartesian2D vs Polar2D)\n2. `series` 是否至少 1 条,且该 Serie 的 `seriesData.Count > 0`\n3. 轴类型是否匹配数据含义:\n - Category 轴:`labels` 非空,且数据点的 `x/y` 是索引(0/1/2...)\n - Value 轴:数据点 `x/y` 是连续数值\n4. 是否存在 NaN/Infinity\n5. 是否把 Value 轴范围“锁死”了(`autoRangeMin/autoRangeMax` 或固定 min/max),导致数据落在范围外\n\n---\n\n## 1. 折线图(Line):类目 X + 数值 Y\n\n### 目标效果\n\n- X:类目标签(A/B/C/D)\n- Y:数值\n- 折线按类目对齐\n\n### 配方\n\n1. `coordinateSystem = Cartesian2D`\n2. X 轴:\n - `axisType = Category`\n - `labels = [A, B, C, D]`\n - `LabelPlacement = Tick`\n3. Y 轴:\n - `axisType = Value`\n - `autoRangeMin/autoRangeMax = true`\n4. Series:\n - `type = Line`\n - 数据点:`x=类目索引`,`y=数值`\n\n数据示例(概念):\n\n```txt\n(x=0, y=10)\n(x=1, y=20)\n(x=2, y=15)\n(x=3, y=30)\n```\n\n### 常见坑\n\n- **点不落在标签上**:检查 `x` 是否从 0 开始,是否越界(labels.Count)\n- **线看起来“断了/跳”**:检查是否有 NaN/Infinity\n\n---\n\n## 2. 柱状图(Bar):类目居中 + Y 从 0 起\n\n### 目标效果\n\n- 每个类目一个柱子\n- 标签在柱子中心对齐\n- Y 轴从 0 起,避免误导\n\n### 配方\n\n1. `coordinateSystem = Cartesian2D`\n2. X 轴:\n - `axisType = Category`\n - `labels` 填类目\n - `LabelPlacement = CellCenter`\n3. Y 轴:\n - `axisType = Value`\n - 固定从 0 开始(例如 `minValue=0` + `autoRangeMax=true`,或等价字段组合)\n4. Series:\n - `type = Bar`\n - `BarSettings.barWidth` 调整柱宽\n\n数据示例:\n\n```txt\n(x=0, y=12)\n(x=1, y=18)\n(x=2, y=9)\n```\n\n### 常见坑\n\n- **柱子夹在两个标签之间**:把 `LabelPlacement` 切到 `CellCenter`\n- **柱子太挤/太疏**:调 `barWidth`、`barGap`、`categoryGap`\n\n---\n\n## 3. 并列柱(Grouped Bar):多条 Serie 共享同一套类目\n\n### 配方\n\n- 多条 `Serie`,都设置 `type = Bar`\n- 每条 Serie 都写同一套 `x=类目索引`\n- 用 `Serie.name` 区分组名(图例/tooltip 会用到)\n\n示例(概念):\n\n```txt\nSerie A:\n (x=0, y=10) (x=1, y=12)\nSerie B:\n (x=0, y=8) (x=1, y=15)\n```\n\n---\n\n## 4. 堆叠柱(Stacked Bar):stacked + stackGroup\n\n### 配方\n\n- 需要堆叠的 Bar 系列:\n - `BarSettings.stacked = true`\n - `BarSettings.stackGroup = \"Group1\"`(同组会堆叠)\n\n### 常见坑\n\n- **堆叠后高度看起来不对**:确认所有参与堆叠的系列 `stackGroup` 完全一致\n\n---\n\n## 5. 散点图(Scatter):Value X/Y + hover + sizeMapping\n\n### 目标效果\n\n- X/Y 都是连续数值\n- 鼠标移上去点会变大(hover)\n- 点大小可按某个维度映射(sizeMapping)\n\n### 配方\n\n1. `coordinateSystem = Cartesian2D`\n2. X/Y 轴都设为 `Value`\n3. `type = Scatter`\n4. 数据点:至少 `x/value`,可选使用 `z` 作为第三维\n5. `ScatterSettings.hover.enabled = true`\n\n### 常见坑\n\n- **点太小看不见**:提高 `PointSettings.size`\n- **hover 没反应**:检查 `HoverHighlightSettings.enabled` 和 `pickRadius`\n\n---\n\n## 6. 热力图(Heatmap):(x, y, value) 三元组\n\n### 目标效果\n\n- X/Y 是类目轴(二维标签)\n- 颜色由 value 决定\n\n### 配方\n\n1. `coordinateSystem = Cartesian2D`\n2. X 轴:Category + labels(列标签)\n3. Y 轴:Category + labels(行标签)\n4. `type = Heatmap`\n5. 数据点:\n - `x = 列索引`\n - `y = 行索引`\n - `value = 强度`\n\n示例(概念):\n\n```txt\n(x=0, y=0, value=0.2)\n(x=1, y=0, value=0.8)\n(x=0, y=1, value=0.5)\n```\n\n### 常见坑\n\n- **所有格子同一颜色**:检查 `HeatmapSettings.autoRange/minValue/maxValue/clamp`\n- **格子太小/太密**:调 `cellSizePx` / `cellGapPx`\n\n---\n\n## 7. 雷达图(Radar):维度索引 x + 数值 value\n\n### 配方\n\n1. `coordinateSystem = Polar2D`\n2. `type = Radar`\n3. 数据点:\n - `x = 维度索引`\n - `value = 数值`\n - `name = 维度名`(建议填,便于标签/tooltip)\n\n示例:\n\n```txt\n(x=0, value=72, name=\"攻击\")\n(x=1, value=55, name=\"防御\")\n(x=2, value=90, name=\"速度\")\n```\n\n### 常见坑\n\n- **雷达图标签乱/缺失**:确保维度标签来源一致(不要依赖 Cartesian 的 axis 配置)\n- **看不到雷达**:检查 `coordinateSystem` 是否为 Polar2D\n\n---\n\n## 8. 交互/tooltip 稳定性:SeriesData.id\n\n如果你启用了选中、tooltip 或 hover,一般建议:\n\n- 每个数据点的 `SeriesData.id` 保持稳定\n\n> 否则当你每次刷新数据都生成一套新 id,会造成交互状态无法关联。\n\n---\n\n## 下一章\n\n- 如果你希望继续写:可以新增 `06-FAQ.md`(常见问题 + 最快排错路线)。\n" }, { id: "04_09-FAQ", relPath: "04_09-FAQ.md", title: "FAQ(常见问题与最快排错路线)", content: "# FAQ(常见问题与最快排错路线)\n\n本章目标:当你遇到“看不到/显示不对/交互怪”等问题时,能用最短路径定位原因。\n\n---\n\n## 0. 最快排错路线(30 秒版)\n\n按顺序检查,通常能解决 80% 的问题:\n\n1. **坐标系是否匹配 SeriesType**\n - Cartesian2D:Line/Bar/Scatter/Heatmap\n - Polar2D:Radar\n\n2. **是否真的有数据**\n - `series.Count > 0`\n - 该 Serie `seriesData.Count > 0`\n\n3. **轴类型是否匹配数据含义**\n - Category 轴:`labels` 非空;数据点 `x/y` 使用索引(0/1/2...)\n - Value 轴:`x/y` 为连续数值\n\n4. **是否存在 NaN / Infinity**\n\n5. **Value 轴范围是否把数据排除在外**\n - 检查是否固定了 min/max\n - 检查 `autoRangeMin/autoRangeMax` 是否关闭\n\n---\n\n## 1. 我点了预览但什么都没有\n\n### 可能原因\n\n- `seriesData` 为空\n- `Serie.visible = false`\n- 坐标系与 SerieType 不匹配(例如 Radar 放在 Cartesian2D)\n- 轴范围不包含数据(常见于 Value 轴被手动锁死)\n\n### 你应该怎么做\n\n1. 在 Inspector 找到 `series` -> 展开某个 Serie\n2. 检查:\n - `visible` 是否为 true\n - `seriesData` 是否有元素\n3. 检查坐标系:\n - Radar -> `coordinateSystem = Polar2D`\n - Line/Bar/Scatter/Heatmap -> `coordinateSystem = Cartesian2D`\n4. 如果是 Value 轴:\n - 先把范围相关的“固定 min/max”关闭\n - 让 `autoRangeMin/autoRangeMax` 都开,确认能显示后再逐步锁定\n\n---\n\n## 2. Category 轴有 labels,但点/柱子不在标签上\n\n### 典型现象\n\n- 柱子落在两个标签中间\n- 折线的点对不上 A/B/C\n\n### 快速结论\n\n- Category 轴下,`x`(或 `y`)通常应该是 **类目索引**(0/1/2/...),而不是“随便的数值”。\n\n### 你应该怎么做\n\n- 检查数据点:\n - `labels[0]` 对应 `x=0`\n - `labels[1]` 对应 `x=1`\n- 如果你做的是柱状图:\n - 把 `LabelPlacement` 调成 `CellCenter`\n\n---\n\n## 3. Value 轴范围很怪(特别大/特别小)\n\n### 可能原因\n\n- Value 轴被锁死范围(min/max)\n- 只锁了一端但另一端的数据非常极端\n- rounding/unit 把范围吸附到不合适的单位\n\n### 你应该怎么做\n\n1. 先把范围回退到“全自动”,确认能显示\n2. 再逐步加入你的业务约束:\n - 柱状图常用:`minValue=0` + `autoRangeMax=true`\n3. 如果你看到很多奇怪的小数刻度:\n - 优先设置 `labelFormat`\n\n---\n\n## 4. 线条断断续续/整体不渲染\n\n### 最常见原因\n\n- 数据中存在 NaN / Infinity\n\n### 你应该怎么做\n\n- 在数据源侧过滤:\n - `float.IsNaN(x/y/z)`\n - `float.IsInfinity(x/y/z)`\n\n---\n\n## 5. 柱状图和标签就是对不齐\n\n### 快速结论\n\n- 99% 是 `LabelPlacement` 没配对\n\n### 你应该怎么做\n\n- Bar:优先 `LabelPlacement = CellCenter`\n- Line/Scatter:优先 `LabelPlacement = Tick`\n\n---\n\n## 6. Heatmap 全部一个颜色 / 看起来没有层次\n\n### 可能原因\n\n- `autoRange=false` 但 `minValue/maxValue` 设置不合理\n- `clamp=false` + 极端值导致颜色被拉爆\n- 数据点 `value` 全部相同(或全是 0)\n\n### 你应该怎么做\n\n1. 开启 `autoRange`\n2. 检查数据点 `value` 是否有变化\n3. 如果你需要固定范围:\n - 合理设置 `minValue/maxValue`\n\n---\n\n## 7. Radar 标签缺失/混乱\n\n### 常见原因\n\n- 维度标签来源不统一\n\n### 推荐做法\n\n- 每个 Radar 点:\n - `x = 维度索引`\n - `value = 数值`\n - `name = 维度名`\n\n并确保 Radar 不依赖 Cartesian 的轴配置来取标签。\n\n---\n\n## 8. tooltip/hover/选中“指错点”或状态不稳定\n\n### 常见原因\n\n- 每次刷新数据都产生新的点标识\n\n### 你应该怎么做\n\n- 保证每个点的 `SeriesData.id` 稳定\n- 避免每次刷新都清空再 `Guid.NewGuid()` 生成一套全新的点\n\n---\n\n## 9. 我不知道应该看哪一章\n\n- 你在“创建/克隆/预览/导出”的流程阶段:看 `02-EditorWorkflow.md`\n- 你在“轴/范围/对齐/单位/格式”:看 `00-WorkflowAndLibrary.md` 的第 7 节\n- 你在“某种图的 data 写法/SeriesData.x/y/z”:看 `00-WorkflowAndLibrary.md` 的第 8 节\n- 你只想直接照抄模板:看 `05-CommonRecipes.md`\n" }, { id: "05_01-UpdatePlan", relPath: "05_01-UpdatePlan.md", title: "更新计划(Roadmap / Update Plan)", content: "# 更新计划(Roadmap / Update Plan)\n\n本章用于说明 EasyChart 后续整体更新方向与计划项。\n\n- 本文件**不按版本阶段拆分**(后续会在各类型章节中再做细化计划)\n- 本文件以“能力分类”组织:你可以把它当作路线图的目录\n\n## Free / Pro 版本划分(发布策略)\n\n### Free(独立包)\n\n- 定位:覆盖 AssetStore 最常见使用场景,做到“可配置 + 可预览 + 可运行时注入数据”的闭环。\n- 包含:现有基础 2D 图表类型、运行时注入(`ChartFeed` / `ApplyJson`)、`EasyChartLibraryWindow` 等编辑器工作流。\n\n### Pro(独立包,包含 Free 全量)\n\n- 定位:在 Free 的基础上提供明显的差异化能力与长期扩展路线。\n- 包含:Free 全量 + Pro 增量功能(高级效果 / 新图表类型 / 3D / 工具链等)。\n\n### 兼容策略\n\n- Free 遇到 Pro-only 资产/配置:允许直接报错(不做降级兼容)。\n\n### Pro 首批卖点优先级\n\n- A:新增图表类型\n- B:3D 图表\n- C:2D 高级视觉效果(如贴图UV动画、特效类渲染能力)\n\n---\n\n## 附录:版本计划(含时间,暂定)\n\n> 说明:以下为“按月/季度”的暂定节奏,具体日期可在确定团队人力与上架窗口后再收敛。\n\n### 2026 Q1(1-3 月):稳定化 + Free 完整闭环\n\n- 2026-01(Free v1.0.x):\n - 修复/收尾:JSON Injection 面板与示例生成/解析的稳定性(按当前 TODO)\n - 文档与示例:对齐最新数据结构与面板能力\n- 2026-02(Free v1.1.0):\n - 2D 体验增强(Free 范围):Bar 圆角、hover 效果(如尚未实现则在此版本落地)\n - 编辑器体验:LibraryWindow 常用 workflow 小优化(不引入 Pro 依赖)\n- 2026-03(Free v1.1.x):\n - 回归修复 + 性能/GC 优化(大数据量刷新、tooltip/交互稳定性)\n\n### 2026 Q2(4-6 月):Pro v1.0(新图表类型优先)\n\n每个类型的Series都加一个动画组件集合,可以添加一些类型的动画例如Point的闪烁,TextureFill的UV动画这些,\n- 2026-04(Pro v1.0.0):\n - 完成 Pro 包结构与发布流程固化(Pro 包含 Free 全量)\n - 新图表类型(第一批):Gauge / Funnel(二选一或都做,按复杂度控制)\n- 2026-05(Pro v1.0.x):\n - 新图表类型(第二批):BoxPlot / Candlestick(优先实现其中一个)\n - Pro-only 报错提示与 Editor 侧可读错误信息完善\n- 2026-06(Free v1.2.0 + Pro v1.1.0):\n - Free:继续补齐 2D 常用能力与稳定性\n - Pro:新图表类型扩展(Treemap / Sunburst 预研或首版)\n\n### 2026 H2(7-12 月):3D 路线与特效能力\n\n- 2026 Q3(7-9 月)(Pro v2.0 或 v1.2+):\n - 3D 图表(第一批):3D Bar / 3D Scatter(优先其中一个可用闭环)\n - 3D 渲染管线与交互基础(按最小可用集拆分迭代)\n- 2026 Q4(10-12 月):\n - 3D Surface(预研/试验性)\n - 2D 高级视觉效果(Pro):贴图 UV 动画(以及后续更复杂特效能力)\n - 工具链增强:Theme / 网络直连 / 自动化测试(择一作为质量提升主线)\n\n---\n\n## 1. 图表类型扩展计划(Chart Types)\n\n### 1.1 2D 图表(现有体系增强)\n\n- 目标:在不增加过多 `SerieType` 的前提下,用“settings/variant”补齐常用表达。\n- 候选方向(示例):\n - Line:更多线型/填充/标注能力(如更丰富的标记/阈值线等),贴图UV动画(Pro)\n - Bar:更多堆叠模式、百分比堆叠、瀑布等模式化能力,Bar头部圆角(Free),hover效果(Free),贴图UV动画(Pro)\n - Scatter:更多映射维度(size/color)、密度表达(如聚合/栅格化)\n - Pie:更多布局与聚合策略、标签策略与交互\n\n### 1.2 新增图表类型(可能新增 `SerieType`)\n\n- 目标:支持 AssetStore 更常见的“独立图表品类”。\n- 候选方向(示例):\n - Gauge / 仪表盘(进度、刻度、阈值)\n - Funnel / 漏斗图\n - BoxPlot / 箱线图\n - Candlestick / K 线(OHLC)\n - Treemap / Sunburst(层级可视化)\n - Sankey / Graph(更复杂的结构类图表,后置)\n\n### 1.3 3D 图表(3D Charts)\n\n- 目标:提供一套 3D 图表能力(可能是独立渲染管线)。\n- 候选方向(示例):\n - 3D Bar / 3D Column\n - 3D Scatter\n - 3D Surface(难度更高,后置)\n\n---\n\n## 2. 多轴与坐标系能力(Multi-Axis & Coordinate Systems)\n\n- 目标:在保持语义清晰的前提下,增强多轴场景。\n- 方向:\n - 支持更多轴组合(双 Y 轴、顶/底 X 轴、左右 Y 轴的混搭)\n - 更清晰的轴绑定策略(Serie 绑定到哪个轴、tooltip/label 使用哪个轴格式化)\n - 坐标系切换/混用的约束与提示(避免用户配置混乱)\n\n---\n\n## 3. 字体与文本系统(Font & Text)\n\n- 目标:统一文本渲染的观感与可配置项,降低 UI Toolkit 不同平台的差异。\n- 方向:\n - 更完整的文字样式(字体、字号、粗细、颜色、描边/阴影等)\n - 文本布局策略(自动换行、截断、省略号、对齐与锚点)\n - 数值格式化能力增强(千分位、单位、百分比、科学计数法等)\n\n---\n\n## 4. 时间轴与 Log 轴(Time Axis & Log Axis)\n\n- 目标:增强对“时间序列”和“数量级跨度很大”的数据表达能力。\n- 方向:\n - 时间轴:时间刻度、格式化、间隔策略(天/周/月/年)\n - Log 轴:log10/log2 等刻度与标签\n - 与数据注入配合(如何喂时间数据、如何处理缺失点)\n\n---\n\n## 5. 主题系统(Theme / Palette)\n\n- 目标:把“配色/字体/默认样式”从单个 Profile 抽象成可复用主题。\n- 方向:\n - Theme 资产(Palette + 字体 + 默认样式)\n - Profile 与 Theme 的覆盖策略(主题默认 vs Profile 局部覆盖)\n - 主题预览、主题切换、主题库\n\n---\n\n## 6. 网络接口直连(Direct Networking / Data Binding)\n\n- 目标:降低“从网络接口到图表”的接入成本。\n- 方向:\n - 提供基于 `ChartFeed` 的标准输入协议\n - 提供可选的 API Envelope 支持(如 `{code,message,data}`)\n - 提供示例:HTTP 拉取 -> 解析 -> Apply\n - 缓存、节流、错误提示与降级策略\n\n---\n\n## 7. 自动化测试与质量保障(Automated Tests & QA)\n\n- 目标:降低迭代风险,让重构更安全。\n- 方向:\n - 数据结构迁移测试(序列化兼容性)\n - 渲染回归测试(截图对比/像素容差,或关键顶点/mesh 断言)\n - 交互测试(tooltip/hit test 的稳定性)\n - 性能基准(大数据量刷新、GC、帧耗时)\n\n---\n\n## 8. 编辑器工作流与工具链(Editor Workflow & Tooling)\n\n- 目标:让配置、预览、注入与复用更顺手。\n- 方向:\n - LibraryWindow:模板/复制/导入导出/批量处理\n - JSON Injection:更强的协议兼容、错误定位、示例生成\n - 更清晰的手册与示例工程\n" } ];