Day 120 · 数据映射思维 — 2D 图表到 3D 形态:动手前的设计日
今天一行渲染代码都不写,先建立"数据 → 3D 形态"的映射思维。这是本周的施工图:数据刻度怎么归一化、柱/面/粒子分别适合什么数据、颜色怎么映射才可读。3D 可视化的失败 90% 死在映射设计——数据没想清楚就堆几何体,做出来不是"看不清"就是"看不懂"。今天想明白,后面六天才有意义。
目录
一、从 2D 到 3D:不是"加一个维度"那么简单
1.1 2D 图表的底层是"二维映射"
所有图表本质上都是把数据映射到屏幕的两个维度:
ECharts 柱状图:
x 轴(类目)→ 第 1 维(水平位置)
y 轴(数值)→ 第 2 维(垂直高度)
ECharts 折线图:
x 轴(时间)→ 水平位置
y 轴(数值)→ 垂直位置
1.2 3D 的"新维度"是给数据用的
3D 不只是"多了一个旋转的观感",而是第三个数据维度:
2D 柱状图 = 类目(位置) + 数值(高度)
3D 柱状图 = 类目(位置) + 数值(高度) + 分组/分类(深度 Z)
2D 热力图 = x(位置) + y(位置) + 颜色(数值)
3D 热力曲面 = x(位置) + y(位置) + z(高度=数值) + 颜色(数值)
核心认知:3D 可视化的价值 = 用第三维承载"2D 塞不下"的数据维度(分组、密度、第三变量)。如果第三维只是"为了酷",那 3D 是负担不是价值。
1.3 什么时候该用 3D(决策法则)
该用 3D:
✔ 数据本身有 3+ 个维度(时间 + 空间 + 数值)
✔ 需要表达"空间关系"(厂区设备分布 + 数值)
✔ 需要沉浸式叙事(数字孪生漫游)
不该用 3D:
✘ 只有 2 个维度(用 2D 图表更清晰)
✘ 追求精确读数(3D 透视会歪曲数值感知)
✘ 数据量小且简单(2D 更高效)
工业大屏的黄金场景:空间数据(设备/产线/厂区)+ 实时数值 → 3D 天然合适;纯指标(KPI 折线)→ 2D 更优。3D 与 2D 是互补,不是替代。
二、数据刻度映射:把任意数值变成"可视长度"
2.1 线性映射(最常用)
把数据 [min, max] 映射到可视范围 [outMin, outMax]:
// src/core/data/scale.ts(纯逻辑,可复用)
/**
* 线性刻度映射:把数据域内的值映射到输出域
* @param value 原始数据值
* @param inMin 数据最小值
* @param inMax 数据最大值
* @param outMin 输出域最小值(如柱高 0)
* @param outMax 输出域最大值(如柱高 10)
* @returns 映射后的值
*/
export function linearScale(
value: number,
inMin: number,
inMax: number,
outMin: number,
outMax: number
): number {
if (inMax === inMin) return outMin; // 防除零:数据全相等时取最小值
const t = (value - inMin) / (inMax - inMin); // 归一化到 [0,1]
return outMin + t * (outMax - outMin);
}
// 使用:温度 20~95℃ → 柱高 0~10 米
const barHeight = linearScale(temp, 20, 95, 0, 10);
2.2 对数映射(数据跨越多个量级时)
工业数据常见"跨度巨大"(0.1 ~ 10000),线性映射会让小值"看不见":
/**
* 对数刻度映射:数据跨多个量级时使用(如功率 0.1~10000)
* 先取对数再线性映射,避免小值被大值"压扁"
*/
export function logScale(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number {
const v = Math.max(value, 1e-6); // 防 log(0)
const min = Math.max(inMin, 1e-6);
const t = Math.log(v / min) / Math.log(inMax / min);
return outMin + t * (outMax - outMin);
}
2.3 色带映射(数值 → 颜色)
import * as THREE from "three";
/**
* 数值 → 颜色:沿色带插值(蓝→青→黄→红)
* 用 HSL 插值比 RGB 插值更符合"冷到热"的直觉
*/
export function valueToColor(t: number, min: number, max: number): THREE.Color {
const n = THREE.MathUtils.clamp((t - min) / (max - min), 0, 1); // 归一化
const hue = (1 - n) * 0.66; // 0.66=蓝 → 0=红(色相从蓝走到红)
return new THREE.Color().setHSL(hue, 0.9, 0.5);
}
刻度映射三件套(线性/对数/色带)是本周所有图表的共同地基——先封装好,后面三天直接调用。
三、形态选择:什么数据用柱/面/粒子
3.1 形态对照表(本周核心决策)
3.2 选型三问
Q1:数据是离散类目还是连续场?
离散(设备/班组/工序)→ 柱状(Day 121)
连续(温度场/压力场)→ 曲面(Day 122)
Q2:有没有"空间坐标"?
有坐标(厂区 X/Y)→ 曲面或粒子按坐标铺
只有数值 → 柱体按类目排
Q3:第三维表达什么?
分组 → 柱体按 Z 排
密度/场 → 粒子
额外数值 → 颜色
映射决策文档示例(写进笔记,Day 126 验收依据):
车间数据看板映射设计(Day 120)
├── 设备能耗 → 3D 柱状(X=设备号,Y=能耗,Z=班组分组,颜色=状态)
├── 温度场 → 3D 曲面(XY=坐标,Z=温度,颜色=温度)
├── 空气流动 → 流线粒子(XYZ=轨迹,颜色=流速)
└── 设备状态 → 2D 仍用 ECharts(状态是离散 KPI,2D 更清晰)
四、颜色映射:色带与可读性
4.1 三种色带策略
顺序色带(单色渐变) 蓝→青 → 表示"低→高"的单一数值(温度)
发散色带(双端) 蓝→白→红 → 表示"围绕零点偏离"(误差/偏差)
分类色带(离散跳变) 红/黄/绿 → 表示"类别/状态"(正常/告警/故障)
4.2 3D 特有的可读性陷阱
✘ 用"光照明暗"当数值 —— 3D 旋转时明暗变化,数值被"误读"
✓ 数值只由颜色/高度表达,光照只负责"立体感"
✘ 色带太艳 → 跟辉光叠加过曝(Day 114 教训回流)
✓ 数值色带用中低饱和,高亮留给"选中/告警"状态
✘ 只在顶点/柱顶标色
✓ 加一个 color legend(色带图例)悬浮在画面一角 —— 3D 里必须自证刻度
4.3 图例(Legend)是必选项
2D 图表坐标轴自带刻度,3D 没有——3D 图表的颜色/高度必须配图例,否则评委看不懂数值含义:
// 简易 HTML 图例(Vue 组件,非引擎内容)
// 一个横向渐变条 + 最小/最大值标注
图例属于"UI",按边界纪律(Day 85)放 Vue 侧;引擎只负责渲染 3D 内容。这也是本周"数据层/渲染层与 UI 层"三层分工的雏形。
五、映射设计文档成文
今天的核心作业——把第三、四节整理成 docs/data-mapping.md 存进工程:
# 3D 数据看板映射设计文档(Day 120)
## 一、数据清单
(本周要可视化的全部数据字段:能耗/温度/流场/状态…)
## 二、形态选型表
(第三节的对照表,每个数据填"用什么形态 + 三维各表达什么")
## 三、刻度映射方案
(每类数据用线性还是对数;min/max 取多少;输出域多大)
## 四、色带方案
(每类数据用顺序/发散/分类色带;具体色值;图例位置)
## 五、可读性检查清单
(颜色不被光照误导 / 有图例 / 透视不歪曲读数 / 不炫技)
这份文档是本周的施工图:Day 121-123 按它实现,Day 126 按它验收。先文档后代码是本红线(周总览第三节)。
六、常见坑点
坑 1:数据没归一化直接当坐标
症状:温度 95 直接当柱子高度 95 米,场景瞬间穿模。修法:一律过 linearScale 映射到安全输出域(第二节)。
坑 2:数据全相等时除零
症状:全部设备温度相同,映射出 NaN 柱子消失。修法:inMax === inMin 时返回 outMin(第二节防除零)。
坑 3:小值被大值"压扁"
症状:能耗 0.1~10000,小设备柱子看不见。修法:换 logScale(第二节)。
坑 4:颜色被 3D 光照带偏
症状:柱子转个角度颜色看起来变了,误读数值。修法:数值只由颜色/高度表达,光照只加立体感(第四节)。
坑 5:没有图例,评委看不懂颜色
症状:画面很酷但不知道颜色代表什么。修法:3D 必须配 color legend(第四节)。
七、自测挑战
T1 · 刻度工具库(40 分钟)
实现 linearScale / logScale / valueToColor 三个函数并写小测试:
linearScale(50, 0, 100, 0, 10) === 5linearScale(50, 50, 50, 0, 10) === 0(防除零)valueToColor(min)偏蓝、valueToColor(max)偏红
T2 · 形态选型练习(40 分钟)
给你三个工业场景,写出映射方案(形态 + 三维各表达什么 + 色带):
车间 20 台设备 24 小时能耗对比
厂区平面上的温度分布(30×20 网格采样点)
管道内气体流速场
T3 · 映射设计文档(40 分钟)
按第五节模板,为"车间 3D 数据看板"写完整的 docs/data-mapping.md——这是 Day 126 BOSS 战的施工图。
八、总结
今天的产出是一张施工图:数据映射文档 + 刻度工具库 + 选型决策。从明天起,把图纸变成真正的 3D 柱状图——3D 数据可视化之旅从一根柱子开始。
明日预告:Day 121 3D 柱状图——数据驱动几何体生成、InstancedMesh 批量渲染 500+ 柱子、高度与颜色随数据实时映射。最常用也最先打通。