首页
/ d3-shape 曲线插值完全指南:d3 中 17 种内置 Curve 的原理、参数与自定义接口

d3-shape 曲线插值完全指南:d3 中 17 种内置 Curve 的原理、参数与自定义接口

2026-09-06 13:03:42作者:舒璇辛Bertina

本篇技术指南聚焦 d3 官方文档中的 Curves 页面(docs/d3-shape/curve.md),系统讲解曲线(curve)如何将离散的 [x, y] 点集插值为连续形状:全部内置曲线类型的行为差异、beta / tension / alpha 三个可调参数的取值范围与默认值,以及曲线底层暴露的 areaStart / lineStart / point 五方法接口。读完本文,你可以为折线图、面积图选择合适的插值方式,正确配置曲线参数,并在内置曲线不满足需求时实现自己的自定义曲线。

一、曲线是什么:从离散点到连续形状

曲线(curve)的核心职责,是把折线(line)或面积(area)的离散(逐点)表示转换为连续形状:曲线规定了如何在二维 [x, y] 点之间进行插值。

理解曲线的两个关键点(来自 curve 文档 开头的定义):

  • 曲线通常不直接构造或直接使用,而是作为曲线工厂函数传给 line.curvearea.curve
  • d3.line()d3.area() 的默认曲线都是 curveLinear,即直接以折线连接各点。

标准用法如下:

const line = d3.line()
    .x((d) => x(d.date))
    .y((d) => y(d.value))
    .curve(d3.curveCatmullRom.alpha(0.5));

在本仓库中,d3 作为伞包通过 src/index.js 重新导出 d3-shape 模块(export * from "d3-shape"),而 package.json 声明的依赖为 "d3-shape": "^3.2.0",因此上述 d3.curveCatmullRom 等 API 来自 d3-shape 3.x 系列。官方文档站点中每个曲线类型都配有基于 Observable Plot 的动态演示组件(见 docs/components/ExampleCurve.vue),该组件用同一组示例点集分别绘制多种曲线,并对 ticks = [0, 0.25, 0.5, 0.75, 1] 做参数扫描,直观对比不同参数取值下的曲线形态。

二、内置曲线全览

d3-shape 内置 17 种曲线,可按插值算法分为 8 个家族。下表先给出总览,后文逐族详解:

曲线 插值方式 是否穿过数据点 默认参数
curveLinear 折线
curveLinearClosed 闭合折线
curveStep 阶梯(中点切换) 部分
curveStepBefore / curveStepAfter 阶梯(前/后切换) 部分
curveBasis / curveBasisOpen / curveBasisClosed 三次 B 样条 首尾点(非 open)
curveBumpX / curveBumpY 三次贝塞尔 是(切线水平/垂直)
curveBundle 拉直的 B 样条 beta = 0.85
curveCardinal / curveCardinalOpen / curveCardinalClosed 三次 Cardinal 样条 是(非 open) tension = 0
curveCatmullRom / curveCatmullRomOpen / curveCatmullRomClosed 三次 Catmull–Rom 样条 是(非 open) alpha = 0.5
curveMonotoneX / curveMonotoneY 保单调三次样条
curveNatural 自然三次样条

2.1 Basis 家族:不穿过数据点的 B 样条

  • curveBasis(context):用指定控制点生成三次 B 样条。首点和尾点会被三重复制,使样条从第一个点开始、到最后一个点结束,并且在端点处分别与"第一、二点连线"和"倒数第二、点连线"相切。
  • curveBasisClosed(context):闭合三次 B 样条。线段结束时重复前三个控制点,生成具有 C2 连续性(二阶导连续)的闭合回路。
  • curveBasisOpen(context):与 basis 相同,但首尾点不重复,因此曲线通常不穿过首尾点

B 样条的整体特征是用"逼近"而非"穿过"控制点,曲线平滑但会偏离数据点,适合控制点本身不要求被精确绘制、只作为造型引导的场景。

2.2 Bump 家族:贝塞尔凸起曲线

  • curveBumpX(context):在每对点之间生成三次贝塞尔曲线,且在每个点处切线为水平方向——曲线呈现沿 x 轴方向的平滑起伏。
  • curveBumpY(context):同理,但每个点处切线为垂直方向,适合 y 方向单调推进的布局(如自下而上的层级连线)。

2.3 curveBundle:层级边缘束带

curveBundle(context) 生成一条"被拉直"的三次 B 样条,拉直程度由参数 beta 控制,默认 0.85。它典型用于**层级边缘束带(hierarchical edge bundling)**技术,通过让同源同目的的连线在公共路径上贴合来消除视觉交叉——这一方法由 Danny Holten 在其论文《Hierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data》中提出。

需要注意两个限制(原文档明确标注):

  • curveBundle 不实现 areaStart / areaEnd,即只能配合 d3.line 使用,不能配合 d3.area
  • 从源码结构看,它不实现面积所需的区段接口,这正是它与其它曲线在接口层面的关键差异。

curveBundle.beta(beta)

const line = d3.line().curve(d3.curveBundle.beta(0.5));
  • 取值范围 [0, 1],表示束带强度,默认 0.85;
  • beta = 0:退化为首尾点之间的直线
  • beta = 1:退化为标准 curveBasis 样条。

CHANGES.md 中记载,d3 4.0 将 bundle 的默认 beta 从 0.7 调整为 0.85,以匹配 Holten 论文中使用的取值。

2.4 Cardinal 家族:带张量的三次样条

  • curveCardinal(context):用指定控制点生成三次 Cardinal 样条,首尾两段使用单侧差分(one-sided differences)。默认 tension 为 0。
  • curveCardinalClosed(context):闭合三次 Cardinal 样条。线段结束时重复前三个控制点,生成闭合回路。默认 tension 为 0。
  • curveCardinalOpen(context):不使用单侧差分,因此曲线从第二个点开始、在倒数第二个点结束,不穿过首尾点。默认 tension 为 0。

curveCardinal.tension(tension)

const line = d3.line().curve(d3.curveCardinal.tension(0.5));
  • 取值范围 [0, 1],决定切线长度:
    • tension = 1:所有切线长度为零,等价于 curveLinear
    • tension = 0:生成均匀 Catmull–Rom 样条(即 curveCatmullRom.alpha(0))。

CHANGES.md 还记载:d3 4.0 修正了 Cardinal 样条 tension 参数的解释方式——现在以 cardinal.tension 显式指定且默认为 0(均匀 Catmull–Rom),tension 为 1 时得到直线曲线;同时 basis 与 cardinal 曲线首尾段的行为也在 4.0 中一并修正。

2.5 Catmull–Rom 家族:推荐默认的曲线

  • curveCatmullRom(context):用指定控制点与参数 alpha(默认 0.5)生成三次 Catmull–Rom 样条,首尾两段使用单侧差分。参数化方案来自 Yuksel 等人的研究《On the Parameterization of Catmull–Rom Curves》。
  • curveCatmullRomClosed(context):闭合版本,线段结束时重复前三个控制点形成闭合回路,alpha 默认 0.5。
  • curveCatmullRomOpen(context):不使用单侧差分,曲线从第二个点开始、在倒数第二个点结束。alpha 默认 0.5。

curveCatmullRom.alpha(alpha)

const line = d3.line().curve(d3.curveCatmullRom.alpha(0.5));
  • 取值范围 [0, 1]:
    • alpha = 0:均匀(uniform)样条,等价于 tension 为 0 的 curveCardinal
    • alpha = 1:弦长(chordal)样条;
    • alpha = 0.5向心(centripetal)样条

官方文档明确建议:优先使用向心样条(alpha = 0.5,即默认值),以避免自交(self-intersection)与过冲(overshoot)——这是选择曲线参数时最重要的实战经验之一。

2.6 Linear 家族:折线

  • curveLinear(context):生成穿过指定点的折线。这是 line 与 area 生成器的默认曲线。
  • curveLinearClosed(context):在线段结束时重复第一个点,生成闭合折线

2.7 Monotone 家族:保单调插值

  • curveMonotoneX(context):在 x 单调的前提下生成y 方向保持单调性的三次样条(Steffen 算法)。其引文概括了该曲线的性质:"一条一阶导数连续的光滑曲线,穿过给定的任意数据点且不产生虚假振荡;局部极值只能出现在数据给定的网格点上,而不会出现在相邻两个网格点之间。"
  • curveMonotoneY(context):对称版本——在 y 单调的前提下保持 x 方向的单调性,适合纵向图表。CHANGES.md 记载 curveMonotoneY 是 4.0 新引入的,同时 4.0 也修复了 monotone 曲线实现中的多个 bug。

保单调曲线适合"值不应在相邻数据点之间反向波动"的场景,例如累积量、占比、排名类时间序列。

2.8 curveNatural:自然三次样条

curveNatural(context) 生成自然三次样条:样条在两端点的二阶导数为零(即端点处曲率为零)。它同样在 d3 4.0 中引入。相比 monotone,natural 不承诺单调性,适合需要全局"最平滑"外观、且端点弯曲自然收尾的曲线。

2.9 Step 家族:阶梯函数

三者都生成由水平线与垂直线交替组成的分段常数(阶梯)函数,区别只在 y 值切换的时机:

  • curveStep(context):y 值在每对相邻 x 值的中点处切换;
  • curveStepAfter(context):y 值在 x 值之后切换;
  • curveStepBefore(context):y 值在 x 值之前切换。

阶梯曲线常用于直方图轮廓、离散状态的时间线(如订单状态随时间的变化)。

三、regular / open / closed 三种形态的区别

cardinal、catmull-rom、basis 三个家族各有三个变体,其行为差异是选型时的常见困惑点:

维度 regular(如 curveCardinal) open(如 curveCardinalOpen) closed(如 curveCardinalClosed)
首尾处理 首尾两段用单侧差分,从第一个点开始、到最后一个点结束 不用单侧差分,从第二个点开始、到倒数第二个点结束 线段结束时重复前三个控制点,生成闭合回路
典型用途 开放折线/面积边线 需要端点"收缩"效果的曲线 环形图边线、闭合区域轮廓

另外注意:使用 open 变体时,若某个线段只含一个点,曲线可能不产生可见输出——line 文档line.defined 一节明确提示,诸如 curveCardinalOpen 这类曲线只有在包含多个点的线段中才渲染可见段

四、自定义曲线接口

当内置曲线均不满足需求时,可以基于以下接口实现自定义曲线(curveLinear 的实现是最简示例)。这套接口还可以直接与内置曲线配合使用,作为 line / area 生成器的底层替代。

方法 语义
curve.areaStart() 标记一个新的面积段开始。每个面积段恰好由两个线段组成:topline 在前,baseline 在后,且 baseline 的点逆序排列
curve.areaEnd() 标记当前面积段结束
curve.lineStart() 标记一个新线段开始,其后跟随零个或多个点
curve.lineEnd() 标记当前线段结束
curve.point(x, y) 在当前线段中追加一个新点,取值为给定的 x、y

从这套接口可以看出 line 与 area 生成器驱动曲线的方式:生成器在遍历数据时按 areaStart → lineStart → point* → lineEnd →(第二次 lineStart/lineEnd,点逆序)→ areaEnd 的顺序回调曲线对象,曲线则负责把回调序列翻译成 context(SVG 或 Canvas 2D)上的路径调用。这也解释了为何 curveBundle 只需不实现 areaStart / areaEnd 两个方法即可表达"不支持面积"。

关于 context 的行为(见 line 文档area 文档):

  • 若生成器设置了 context(如 canvas.getContext("2d")),形状以路径方法调用序列渲染到该 context,调用返回 void;
  • 若 context 为 null(默认),则返回 SVG path data 字符串,此时 digits 选项(默认 3 位小数)控制路径字符串的小数位数。

五、实战注意事项

结合 curvelinearea 三篇文档的警告与限制,实际选型时建议核对以下要点:

  1. 输入数据可能需要按 x 排序:line 与 area 文档都以警告框明确指出——"取决于该生成器关联的曲线,传入的输入数据可能需要先按 x 值排序"。monotone 系列曲线对此尤其敏感(其正确性以单调性为前提)。
  2. area 兼容性:curveBundle 不支持面积(无 areaStart / areaEnd 实现),只能配合 d3.line;其余内置曲线均支持 line 与 area。
  3. 端点是否被穿过:basis 家族(除首尾处理外)、bundle 不穿过内部控制点;open 变体不穿过首尾点;cardinal、catmull-rom(非 open)、monotone、natural 穿过所有数据点。选择曲线前先确认业务上"曲线必须经过每个数据点"是否成立。
  4. 参数默认值:bundle.beta 默认 0.85、cardinal.tension 默认 0、catmullRom.alpha 默认 0.5(向心)。除非有明确理由,建议保留默认值;向心参数化是官方推荐的防自交/防过冲方案。
  5. 单点线段:任何曲线下,仅含单点的线段可能不可见(需配合圆头或方头线帽渲染);open 系曲线在单点线段下则完全不产生可见段。

六、在仓库中继续深入

  • docs/d3-shape/curve.md:本文主体文档,含各曲线动态演示;
  • docs/d3-shape/line.mddocs/d3-shape/area.md:line/area 生成器的 accessors、curve / context / digits 配置及数据排序警告;
  • docs/d3-shape.md:d3-shape 模块总览(line、area、arc、pie、stack 等生成器索引);
  • docs/components/ExampleCurve.vue:曲线对比演示组件的实现,展示了如何用一组固定示例点渲染并对比多条曲线;
  • CHANGES.md:d3 4.0 版本中 curve API 从旧 interpolate 接口迁移的历史、各曲线命名映射(如 monotone ↦ curveMonotoneX)、以及 curveMonotoneY / curveNatural 的引入与 bundle 默认 beta 的调整;
  • test/docs-test.js:文档锚点链接校验测试,保证文档内 {#anchor} 形式的接口锚点可被稳定引用。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391