- 开发工具
- 数据可视化
【免费下载链接】penrose
Create beautiful diagrams just by typing notation in plain text.
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)可以整理出如下属性表:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
d | PathData | [](空路径) | SVG 路径数据,由下文三个构造函数之一生成 |
strokeWidth | number | 1 | 描边宽度 |
strokeStyle | string | "solid" | 描边样式,如"solid"、"dashed"等 |
strokeColor | color | black | 描边颜色 |
strokeDasharray | string | "" | 虚线数组,形如"4 2" |
strokeLinecap | string | "butt" | 端点线帽,可选"butt"/"round"/"square" |
fillColor | color | 无填充(noPaint) | 填充颜色,默认不填充 |
startArrowhead | string | "none" | 起点箭头样式 |
startArrowheadSize | number | 1 | 起点箭头大小 |
flipStartArrowhead | boolean | false | 是否翻转起点箭头 |
endArrowhead | string | "none" | 终点箭头样式 |
endArrowheadSize | number | 1 | 终点箭头大小 |
name | string | "defaultPath" | 形状名称 |
ensureOnCanvas | boolean | true | 是否确保路径落在画布内 |
值得注意的是,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 方向(高度)的半径 |
rotation | number | 椭圆绕自身中心旋转的角度(度) |
largeArc | 0/1 | 0画两段弧中较短的一段,1画较长的一段 |
arcSweep | 0/1 | 0从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.
相关推荐
BabelDOC路径绘制:直线、曲线、矩形等图形
BabelDOC路径绘制:直线、曲线、矩形等图形 在文档处理过程中,图形元素的精确渲染是保证排版质量的关键环节。BabelDOC作为一款功能强大的文档翻译工具,
人工智能AI 应用NLP计算机视觉CADmium草图功能详解:线条/矩形/圆形/圆弧绘制技巧
CADmium草图功能详解:线条/矩形/圆形/圆弧绘制技巧 概述 CADmium是一款基于浏览器的现代CAD(计算机辅助设计)程序,其草图功能是构建3D模型的基
3D建模前端图形学WebAssembly桌面应用Plotly.py 多条曲线平均值绘制指南:用 Scatter 叠加原始曲线与平均曲线
Plotly.py 多条曲线平均值绘制指南:用 Scatter 叠加原始曲线与平均曲线 导读 在数据分析与数学可视化中,经常需要把两条或多条函数曲线叠加在同一坐
数据可视化数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考