☰
Penrose Path 形状详解:用 `d` 属性绘制 SVG 折线、弧线与插值曲线
2026/9/27 8:21:14 网站建设 项目流程
  • 开发工具
  • 数据可视化

【免费下载链接】penrose

Create beautiful diagrams just by typing notation in plain text.

项目地址:https://gitcode.com/gh_mirrors/pe/penrose
点击查看免费下载

Path 是 Penrose 样式程序(Style)中最灵活的基础形状之一,它允许你以 SVG 路径命令的形式描述任意折线、椭圆弧与二次插值曲线,从而实现多边形边框、连边曲线、箭头引导线等复杂几何元素。本文以 packages/docs-site/docs/ref/style/shapes/path.md 为骨架,结合 packages/core/src/shapes/Path.ts 与 packages/core/src/lib/Functions.ts 的源码实现,完整讲解Path的属性、三种d构造函数的参数语义与底层渲染原理,并给出可直接套用的 Style 示例。

Path 形状的完整属性

在 Style 程序中,Path与Circle、Rectangle等形状一样,通过shape声明并赋属性。它的形状描述在 packages/core/src/shapes/Path.ts 中定义:除了所有形状共有的描边(Stroke)、填充(Fill)、箭头(Arrow)与命名属性外,Path独有的核心属性是d(类型为PathDataV),以及控制端点线帽的strokeLinecap。

从samplePath的默认值(packages/core/src/shapes/Path.ts)可以整理出如下属性表:

属性类型默认值说明
dPathData[](空路径)SVG 路径数据,由下文三个构造函数之一生成
strokeWidthnumber1描边宽度
strokeStylestring"solid"描边样式,如"solid"、"dashed"等
strokeColorcolorblack描边颜色
strokeDasharraystring""虚线数组,形如"4 2"
strokeLinecapstring"butt"端点线帽,可选"butt"/"round"/"square"
fillColorcolor无填充(noPaint)填充颜色,默认不填充
startArrowheadstring"none"起点箭头样式
startArrowheadSizenumber1起点箭头大小
flipStartArrowheadbooleanfalse是否翻转起点箭头
endArrowheadstring"none"终点箭头样式
endArrowheadSizenumber1终点箭头大小
namestring"defaultPath"形状名称
ensureOnCanvasbooleantrue是否确保路径落在画布内

值得注意的是,strokeLinecap只对<path>、<polyline>、<line>等少数 SVG 元素生效(packages/core/src/shapes/Path.ts 中的注释即引用了该约束)。文档页中的属性表由 packages/docs-site/src/components/ShapeProps.vue 组件从shapedefs.json动态渲染,而上述默认值则直接来自形状采样器源码,两者保持一致。

使用d定义 SVG 路径

Path的形状体完全由d属性决定。Penrose 目前支持 SVG 的直线段命令(M/L/Z)与椭圆弧命令(A);SVG 命令的详细语义可参考 MDN 的d属性文档(原文档引用,此处不展开外部链接)。在 Style 程序中,你不需要手工书写 SVG 命令字符串,而是把下列三个内置函数的返回值赋给d。

折线或连续线段:pathFromPoints

pathFromPoints(complete, [[x1, y1], [x2, y2], ..., [xn, yn]])
  • complete:取"open"(保持路径开放,不连接首尾)或"closed"(从最后一点画一条直线回到第一点并闭合);
  • 第二个参数是有序点列表,构成路径的全部顶点。

源码实现位于 packages/core/src/lib/Functions.ts:它先用PathBuilder将第一个点作为起点moveTo,再对剩余每个点lineTo,最后若pathType === "closed"则closePath()。也就是说,pathFromPoints生成的等价 SVG 命令序列为M x1 y1 L x2 y2 ... L xn yn(闭合时追加Z)。PathBuilder的moveTo、lineTo、closePath分别对应 SVG 的M、L、Z命令(packages/core/src/renderer/PathBuilder.ts)。

典型用法(来自仓库示例 packages/examples/src/exterior-algebra/exterior-algebra.style):

d: pathFromPoints("closed", [(0,0), u.p, u.p+v.p, v.p])

该行用四个点围成一个闭合平行四边形:从原点出发,沿向量u、u+v、v依次连线闭合。pathFromPoints同样被用于绘制箭头引导线等场景(如 packages/examples/src/alloy-models/generic/generic.style)。

椭圆弧:arc

arc(complete, start, end, radius, rotation, largeArc, arcSweep)

各参数含义:

参数取值说明
complete"open"/"closed"同前:是否闭合路径
start[x, y]弧的起点坐标
end[x, y]弧的终点坐标
radius[rx, ry]长度为 2 的列表:rx为沿 x 方向(宽度)的椭圆半径,ry为沿 y 方向(高度)的半径
rotationnumber椭圆绕自身中心旋转的角度(度)
largeArc0/10画两段弧中较短的一段,1画较长的一段
arcSweep0/10从start到end逆时针绘制,1顺时针绘制

实现见 packages/core/src/lib/Functions.ts:它调用PathBuilder的moveTo(start).arcTo(radius, end, [rotation, largeArc, arcSweep]),arcTo生成 SVG 的A命令(packages/core/src/renderer/PathBuilder.ts 起)。largeArc与arcSweep两个标志位对应 SVGA命令的large-arc-flag与sweep-flag,是决定"走哪条弧"的关键。

相关能力:若需要绘制"圆心 + 半径 + 角度范围"定义的圆弧,仓库还提供了circularArc(pathType, center, r, theta0, theta1)(packages/core/src/lib/Functions.ts),其中theta0/theta1为弧度制的起止角,并自动根据角度差计算largeArc与arcSweep。此函数不在原文档参数表中,但同属arc的补充工具,适合表示圆周上的局部弧段。

经过三点的曲线:interpolateQuadraticFromPoints

interpolateQuadraticFromPoints(pathType, p0, p1, p2)
  • pathType:"open"保持路径开放,"closed"从终点连线回起点;
  • p0:起点;
  • p1:中间点——与标准二次贝塞尔曲线不同(标准贝塞尔曲线的中间控制点并不落在曲线上),这条曲线会精确穿过p1;
  • p2:终点。

实现位于 packages/core/src/lib/Functions.ts。关键在于控制点的换算:要让二次贝塞尔曲线穿过中间点p1,控制点需取

q1 = 2 * p1 - (p0 + p2) / 2

源码注释给出了推导思路——该表达式可由二次 Bernstein 基下的插值条件导出。随后调用quadraticCurveTo(q1, p2)生成 SVG 的Q命令(packages/core/src/renderer/PathBuilder.ts)。由于中间点是"插值"而非"控制",这条曲线特别适合绘制经过指定中点的光滑连线。

典型用法(仓库示例):

  • packages/examples/src/Dynamics/LyapunovExp.style:
d: interpolateQuadraticFromPoints("open", p1.vec, p2.vec, p3.vec)
  • packages/examples/src/alloy-models/generic/generic.style:
d: interpolateQuadraticFromPoints("open", a, e.midpt, b)

这里用边的中点e.midpt作为中间插值点,绘制经过两点连线中点的平滑弧线,是典型的连边曲线写法。

从路径数据到 SVG 输出:渲染链路

Path形状的渲染实现在 packages/core/src/renderer/Path.ts。核心函数toPathString(packages/core/src/renderer/Path.ts)把PathCmd列表拼接成 SVG 的d字符串:坐标类型的子路径(CoordV)会先经过toScreen做画布坐标换算,数值类型的子路径(ValueV,如弧的半径、旋转角、标志位)直接透传,最终形如M x1 y1 L x2 y2 ...。

渲染时的几个细节:

  • 描边与填充:通过attrStroke/attrFill把strokeColor、strokeWidth、fillColor等属性映射到 SVG 元素上,路径默认不填充(fillColor为noPaint),因此纯d绘制的折线默认呈现为线框;
  • 箭头支持:当startArrowhead或endArrowhead非"none"时,渲染器会创建一个<g>分组,为路径挂上marker-start/marker-end(packages/core/src/renderer/Path.ts),因此Path可以直接当作带箭头的连边使用;
  • 其余属性透传:attrAutoFillSvg会把未显式处理的形状属性自动映射为 SVG 属性。

测试用例对上述函数的调用也有直接佐证:例如 packages/core/src/lib/tests/Queries.test.ts 与 packages/core/src/engine/BBox.test.ts 都以compDict.pathFromPoints.body(context, "open", [...])的方式构造路径数据参与断言,说明这些构造函数既服务于渲染,也参与引擎的包围盒(BBox)计算与查询。

Style 中的综合示例

把三个构造函数组合起来,即可构造一张"闭合多边形 + 弧线 + 插值曲线"的复合图形。以下代码基于仓库示例的写法整理:

-- 折线:闭合四边形 shape quad = Path { d: pathFromPoints("closed", [(0,0), (100,0), (100,80), (0,80)]) strokeColor: #ff0000 strokeWidth: 2.0 fillColor: rgba(255, 0, 0, 0.1) } -- 椭圆弧:从 (120, 0) 到 (220, 0),沿半短轴为 40 的椭圆顺时针画短弧 shape halfRing = Path { d: arc("open", (120, 0), (220, 0), [50, 40], 0.0, 0, 1) strokeColor: #0000ff strokeWidth: 1.5 } -- 插值曲线:精确穿过中间点 (200, 60) shape curve = Path { d: interpolateQuadraticFromPoints("open", (120, 0), (200, 60), (220, 0)) strokeColor: #00aa00 endArrowhead: "arrow" endArrowheadSize: 1.2 }

要点总结:

  • d只接受pathFromPoints、arc、interpolateQuadraticFromPoints(以及仓库中同族的quadraticCurveFromPoints、cubicCurveFromPoints、circularArc等)的返回值,不要直接传字符串;
  • 三个构造函数的complete/pathType参数统一取"open"或"closed";
  • 坐标与半径都可以是 Style 中的表达式(如向量运算u.p+v.p、e.midpt),Penrose 会在优化求解后把最终数值写入路径数据;
  • 如需绘制带箭头的路径(如算法示意图中的有向连线),组合endArrowhead/startArrowhead即可,无需额外声明 marker。

延伸阅读

  • 形状定义与默认属性:packages/core/src/shapes/Path.ts
  • 三个构造函数的完整实现:packages/core/src/lib/Functions.ts
  • SVG 命令的构建器(M/L/Q/C/A等):packages/core/src/renderer/PathBuilder.ts
  • 路径渲染与d字符串生成:packages/core/src/renderer/Path.ts
  • 仓库中更多真实用法:packages/examples/src/Dynamics/LyapunovExp.style、packages/examples/src/exterior-algebra/exterior-algebra.style、packages/examples/src/alloy-models/generic/generic.style
  • 开发工具
  • 数据可视化

【免费下载链接】penrose

Create beautiful diagrams just by typing notation in plain text.

项目地址:https://gitcode.com/gh_mirrors/pe/penrose
点击查看免费下载

相关推荐

上一篇:用 kubequery 将 Kubernetes 集群纳入 Fleet 的 osquery 遥测体系:完整部署与查询指南
下一篇:10万亿 tokens 训练的语言模型:Granite-3.0-3B-A800M-Base训练数据大揭秘

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询