笔记
0工具
0此页用于记录用户反馈问题后的每一次改进
关于
“写笔记”支持四种格式——Word 文档、Excel 表格、Markdown、纯文本,起稿或二次编辑时都能随时切换,同一篇笔记想用哪种形态来记,都由你说了算。
md、txt、csv、json 这类纯文本则原样载入,不做多余加工。拿一张现成的表倒进来、改几笔、再导出去,等于白用一台免费的格式转换器。
要带走就在右上角点“下载”,可导出 PDF、Word、Markdown、Excel、TXT 等格式;列表卡片“⋯”菜单里,也有同样的下载入口。
在“工具”页点“+ 上传工具”即可发布:填好名称与链接,再用 Markdown 把使用方法写清楚——能解决什么问题、怎么装、怎么用,比堆介绍实在。
要分发安装包就一并上传压缩包(ZIP、RAR、7Z、TAR.GZ,最大 35MB),别人在详情页一键下载;只放链接不带附件也可以。
工具按大家的收藏热度排序,好用的自然会被顶上来。发布后可在详情页或卡片菜单里编辑、下架。
写笔记时勾上“隐藏”,这篇就只存在于你自己的账号里:不进列表、不进搜索、不上首页精选,也不会出现在任何公开的页面,链接发给别人同样打不开。
适合放密码、草稿、日记这类只给自己看的内容;想公开,去“发布”打开它,把“隐藏”的勾去掉再保存,之后编辑会默认保持原状态,不会悄悄变回公开。
不想公开、只想临时给人看:点“分享”生成一条带密码和有效期的链接,到期自动失效,你也能随时撤销。
你的内容会同时保存在多个副本上,系统定期做备份与完整性校验,再配合异地容灾机制:就算某台机器出问题,数据也不会丢,可以长期放心存放;特别重要的资料,仍建议你另外再留一份备份。
全站跑在容器化、模块化的现代架构上,更新、部署、回滚都很快,扩展性和稳定性都按长期运营的标准来设计(Built for reliability, designed to scale)。
这个网站最早只是一个人的笔记仓库,后来慢慢长成现在的知识中枢。设计上很克制——没有广告、没有追踪、没有推荐算法,只是干干净净地存放一些东西;既然做好了,就公开出来,万一有人用得上呢。
不做大而全,不做平台梦,保持简单、保持克制、保持好奇。所有内容都由用户贡献、由用户维护:不会突然冒出付费墙,不会在角落塞广告位,也不会把你的数据卖给第三方。
产品会持续迭代,站内日志页记录着每一次改动,改了什么都有迹可循;想了解这个站是怎么一步步走到今天的,翻翻日志就能看到来龙去脉。
如果在这里看到涉嫌违规的内容,点对应卡片右侧的“举报”按钮就能提交,我们会尽快核实处理;也谢谢你花一点时间,一起把这里维护干净。
趋势
曲率贝塞尔曲线:改进一项永恒的设计配方
本文探讨了如何在不破坏现有贝塞尔曲线生态的前提下,通过直接操作曲率手柄来改进贝塞尔曲线的编辑体验,使其更符合用户的直觉意图。
曲率贝塞尔曲线:改进一项永恒的设计配方
引言:贝塞尔曲线的统治地位
贝塞尔曲线是计算机辅助设计(CAD)和计算机图形学的基石。就像Bic圆珠笔一样,这个设计已有数十年历史,却无处不在。在各类插画和动画工具中,它往往作为默认甚至唯一的选择。
该曲线由Paul de Casteljau于1959年构思,后经Pierre Bézier在1960年代的雷诺公司加以完善。贝塞尔曲线的持久魅力在于其简洁性。尽管更复杂的曲线已被发明,新的曲线类型也不断被提出,但这些大多局限于特定领域,如高精度CAD或道路设计。在通用领域,贝塞尔曲线尽管存在缺陷,却顽固地拒绝被取代。
因此,贝塞尔曲线是一项我们似乎不得不接受的“遗产技术”。作为一名软件工程师,我的问题是:我们能否在不推翻所有基于贝塞尔曲线构建的技术栈的前提下,让贝塞尔曲线变得更好?答案是肯定的。
线性插值的本质
绘制贝塞尔曲线是一个出奇简单的线性过程:
给定一系列控制点,我们先用直线连接它们。然后沿着这些直线同时移动,生成新的点,再将这些新点连接起来。重复此过程,直到只剩一个点,该点即位于曲线上。
这种构造方式使贝塞尔曲线比其表象更为规整。线性插值(lerps)可以归纳为一个紧凑的公式,例如对于4个控制点 ((A, B, C, D)):
$$ \gamma(t) = A(1-t)^3 + 3B(1-t)^2t + 3C(1-t)t^2 + Dt^3 $$
规则很简单:((1-t)) 的幂递减,(t) 的幂递增,系数取自帕斯卡三角形的第n行:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
对于2D和3D曲线,该公式分别应用于X、Y、Z坐标。
虽然贝塞尔曲线可以构造任意数量的控制点,但实践中通常只使用4个控制点的三次贝塞尔曲线。这是因为曲线只保证经过第一个和最后一个控制点,这使得高次贝塞尔曲线难以塑形。更大的曲线通过拼接多个三次贝塞尔段构成,将切线对齐以创建外观平滑的分段曲线——这就是常见的三次贝塞尔样条。
大多数绘图应用中的“钢笔工具”本质上是绘制和编辑控制点,而非直接绘制曲线。
一个普遍存在的谎言
钢笔工具通常提供几种控制点模式:
- 对称切线:最平滑选项
- 非对称切线:保留部分平滑性
- 单侧切线:进一步放松约束
直觉上,这些模式代表不同程度的平滑性。对称切线被视为最平滑的选项,随着约束放松,某些平滑性质会丢失。
但实际上这是完全错误的,而且很容易证明。
贝塞尔曲线可以被精确分割:通过从插值图中读取新控制点即可。左段和右段与原始曲线100%相同,并且总是完美地在接缝处连接。然而,除了接近中点的一个分割点外,中间的切线将是不对称的。
这可以通过曲率梳(curvature comb)来确认——它表示每个点的(反)曲率半径。曲率梳保持连续,没有跳跃。这意味着相邻切线长度是否相等(即对称)完全无关紧要。
尝试用这种方式绘制平滑直观的贝塞尔曲线是徒劳的。
一个简单的改进:相对切线
一种简单的改进方法是将切线视为相对而非绝对。例如,我们可以使切线长度与每个段的起点和终点之间的距离成比例。
这种样条更易于编辑,因为当你移动每个曲线点时,相邻段会自然地弯曲以避让。这样产生的尖点要少得多。编辑切线的方式保持不变。
然而,如果我们再次绘制曲率,可以发现这并非一个很好的解决方案:
- 按比例缩放切线不能保证编辑时曲率被保留
- 相邻段之间的曲率也不连续
这也表明,向用户提供曲率梳可视化作为“帮助工具”实在有点残酷:调整一端的曲率会影响另一端,需要反复调整才能接近目标。
直接处理曲率
一个更有效的策略是直接处理曲率。虽然这通常是一个困难的问题,但这里存在一些令人惊讶的关系,我们可以直接观察到:
考虑段 (A-B-C-D) 起点处的曲率。这仅受 (A)、(B) 和 (C) 的位置影响。点 (D) 如果与 (C) 断开,则可以自由移动。此外,由于切线 (A-B) 是水平的,只有 (C) 的垂直位置起作用。
这是底层线性插值的结果,最终在公式中抵消了许多项。因此,(A) 处的曲率仅受 (A-B) 的长度和 (C) 到 (A-B) 的垂直距离影响。(D) 侧同理,受 (C-D) 和 (B) 影响。
但这本身并不十分有用。当我们移动曲线点((A) 或 (D))时,通常相邻控制点((B) 或 (C))也会移动以保持切线。当我们旋转对称或非对称切线时,相邻段中的相邻切线也会旋转相同角度,改变该侧的曲率。
尽管如此,这表明保留曲率并非疯狂的想法,并且可以通过适当缩放切线来简单实现。规则很简单:
- 移动曲线点时:必须保留相邻段的起点和终点曲率
- 移动切线时:必须保留当前段对端的曲率,以及相邻段两侧的曲率
这应该能产生一种尊重用户意图的编辑体验。
但还不够完美:移动曲线点效果很好,但旋转切线时,切线长度本身并不能很好地代表用户意图。微小的变化可能导致曲率剧变,使曲线在尝试寻找匹配解时跳动和意外爆炸。
曲率手柄:更自然的交互
因此,更好的做法是使用曲率手柄,其长度直接对应曲率:
与曲率梳不同,手柄的长度是非反曲率半径,这是更自然的选择。这些手柄非常稳定,可以即时转换为经典贝塞尔控制点,无需在两种表示之间来回往返。这也有助于避免数值漂移。
数学求解:金发姑娘问题
要实际实现这一目标,我们需要求解切线长度 (l_0) 和 (l_1),给定期望的曲率 (k_0) 和 (k_1)、起点/终点 (A) 和 (D),以及起点/终点的单位长度切线。
给定曲线 (\gamma(t)),单位切向量 (\mathbf{T}(t)) 可以表示为归一化导数:
$$ \mathbf{T}(t) = \frac{\gamma'(t)}{|\gamma'(t)|} $$
这可用于通过两个向量叉积(使用一阶和二阶导数)找到曲率向量 (\mathbf{K}(t)):
$$ \mathbf{K}(t) = \mathbf{T}(t) \times \frac{\gamma''(t)}{|\gamma'(t)|^2} \times \mathbf{T}(t) $$
叉积确保 (\mathbf{K}(t)) 垂直于切线,其方向指向曲线的凹侧。其范数 (|\mathbf{K}(t)|) 即为曲率 (k(t)),等于曲率半径的倒数。
对于三次贝塞尔曲线,(\gamma'(t)) 是二次多项式,(\gamma''(t)) 是线性多项式。这意味着曲率公式可以简化,并最终得到一个关于 (l_0) 和 (l_1) 的方程组。
关键洞察:在端点 (t=0) 处,(\gamma'(0) = 3(B-A) = 3l_0\mathbf{T}_0),其中 (\mathbf{T}_0) 是起点处的单位切线。类似地,(\gamma''(0) = 6(C-2B+A))。将这些代入曲率公式,可以消去大量项,最终得到:
$$ k_0 = \frac{2|(B-A) \times (C-A)|}{3|B-A|^3} $$
这个公式表明,起点曲率仅取决于 (A)、(B) 和 (C) 的相对位置,且与 (|B-A|) 的立方成反比。同理,终点曲率 (k_1) 仅取决于 (B)、(C) 和 (D)。
求解切线长度
给定期望的 (k_0) 和 (k_1),我们需要求解 (l_0) 和 (l_1)。
设 (\mathbf{T}_0) 和 (\mathbf{T}_1) 为起点和终点的单位切线方向(由用户通过手柄方向给出),则:
- (B = A + l_0\mathbf{T}_0)
- (C = D - l_1\mathbf{T}_1)
代入曲率公式,得到两个方程:
$$ k_0 = \frac{2|(A + l_0\mathbf{T}_0 - A) \times (D - l_1\mathbf{T}_1 - A)|}{3|l_0\mathbf{T}_0|^3} $$
$$ k_1 = \frac{2|(D - l_1\mathbf{T}_1 - D) \times (A + l_0\mathbf{T}_0 - D)|}{3|l_1\mathbf{T}_1|^3} $$
化简后,可以观察到 (l_0) 和 (l_1) 在方程中相互耦合。通过代数操作(包括向量叉积的展开和代数化简),可以最终归结为一个关于 (l_0) 和 (l_1) 的非线性方程组。
求解策略:由于方程是非线性的,通常需要数值方法(如牛顿-拉弗森迭代)。但幸运的是,对于常见的几何配置,存在闭式解或非常稳定的迭代初值。
实际实现:在交互式编辑器中,我们可以将曲率手柄的长度作为用户输入,然后实时求解 (l_0) 和 (l_1)。由于 (k_0) 和 (k_1) 是用户直接控制的,编辑过程变得直观且稳定。
优点与实现考虑
- 直观性:曲率手柄长度直接对应曲率半径,用户无需理解复杂的切线调节
- 稳定性:避免了切线长度微小变化导致曲率剧变的问题
- 兼容性:可以即时转换为经典贝塞尔控制点,与现有渲染管线完美兼容
- 数值稳定性:由于无需在两种表示间往返,减少了累积误差
实现细节:
- 用户拖拽曲率手柄时,捕获其方向和长度(长度对应曲率半径)
- 计算期望的 (k_0) 和 (k_1)(曲率值为手柄长度的倒数)
- 求解上述方程组得到 (l_0) 和 (l_1)
- 生成贝塞尔控制点 (B) 和 (C)
- 渲染曲线
结论
贝塞尔曲线作为一项经典技术,其统治地位短期内不会动摇。但通过直接操作曲率而非切线,我们可以显著改善编辑体验,同时保持与现有技术栈的完全兼容。这种方法不仅适用于绘图工具,还可扩展到动画、字体设计、CAD等领域。
曲率手柄提供了一种“恰到好处”的平衡:既保留了贝塞尔曲线的数学优雅,又赋予用户在曲率层面进行直觉控制的能力。这或许能让这项“遗产技术”在未来几十年继续焕发活力。