Cairo绘制圆角虚线按钮:贝塞尔曲线边长计算公式修正
Cairo圆角虚线按钮的曲线长度计算问题
我尝试用Cairo绘制带虚线边框的圆角按钮,但Cairo要求虚线长度以像素为单位而非比例,因此需要计算圆角贝塞尔曲线的长度。但我用的公式无效,预期生成16段虚线,实际得到17¾段,调整button_thickness的运算方式也没用。想请教:这是计算错误还是Cairo的机制问题?正确的边长公式是什么?
更新:经验测试发现用1.8020878替代原系数后,100倍缩放无误差,这个数值的来源是什么?
简化实现代码
#include <cairo.h> #include <cairo-svg.h> constexpr double button_size = 192; constexpr double button_thickness = 8; /* * 1.6232252401402305 * ⌠1 _____________ * ≈ ⌡0 √(2t)²+(2−2t)² */ constexpr double side_length = 1.6232252401402305 * 0.5 * (button_size - button_thickness); int main() { cairo_surface_t *surface = cairo_svg_surface_create("./test.svg", button_size, button_size); cairo_t *ctx = cairo_create(surface); cairo_set_source_rgba(ctx, 0.85, 0.85, 0.85, 1); cairo_set_dash(ctx, (double []){side_length / 8}, 1, side_length / 16); constexpr double lt = button_thickness / 2; constexpr double rt = button_size - button_thickness / 2; constexpr double tp = lt; constexpr double bm = rt; constexpr double mx = button_size / 2; constexpr double my = mx; cairo_set_line_width(ctx, button_thickness); cairo_move_to(ctx, lt, my); cairo_curve_to(ctx, lt, tp, lt, tp, mx, tp); cairo_curve_to(ctx, rt, tp, rt, tp, rt, my); cairo_curve_to(ctx, rt, bm, rt, bm, mx, bm); cairo_curve_to(ctx, lt, bm, lt, bm, lt, my); cairo_close_path(ctx); cairo_stroke(ctx); cairo_destroy(ctx); cairo_surface_destroy(surface); return 0; }
问题原因与解决方法
1. 核心错误:贝塞尔曲线类型与公式不匹配
你使用的长度公式是二次贝塞尔曲线的积分结果,但代码里通过cairo_curve_to绘制的是三次贝塞尔曲线,且控制点位置完全错误——你把两个控制点都放在了圆角的顶点,导致曲线形状偏离了预期的四分之一圆弧,长度计算自然完全不准。
2. 1.8020878系数的来源
这个系数是你当前错误贝塞尔曲线的单段长度与圆角半径r的比值。通过对该曲线的参数方程积分可精确计算:
- 你的曲线是从
(t/2, r+t/2)到(r+t/2, t/2)(t为线宽,r=(button_size-t)/2),两个控制点均为(t/2, t/2) - 对该三次贝塞尔曲线的导数平方和开根号后积分(t从0到1),最终得到单段长度约为
1.8020878*r,这就是你测试出的系数的由来。
3. 正确的解决思路
方案一:改用精确圆弧(推荐)
Cairo提供cairo_arc接口可直接绘制精确圆弧,避免贝塞尔曲线的长度误差。修改后的圆角绘制代码如下:
constexpr double r = (button_size - button_thickness) / 2; cairo_move_to(ctx, rt, my); // 绘制四个四分之一圆弧,组成完整圆形边框 cairo_arc(ctx, mx, tp, r, 0, M_PI/2); cairo_arc(ctx, rt, my, r, M_PI/2, M_PI); cairo_arc(ctx, mx, bm, r, M_PI, 3*M_PI/2); cairo_arc(ctx, lt, my, r, 3*M_PI/2, 2*M_PI); cairo_close_path(ctx);
此时总周长为精确的π*(button_size - button_thickness),虚线段数计算完全准确。
方案二:用正确的贝塞尔曲线模拟圆弧
如果坚持用贝塞尔曲线,需使用标准的三次贝塞尔模拟四分之一圆的控制点:控制点距离端点的距离为4*(√2-1)/3 * r ≈0.5522847*r,此时曲线长度接近真实圆弧的(π*r)/2,总长度误差可忽略。
内容的提问来源于stack exchange,提问作者Coarse Rosinflower
相关产品推荐
相关产品推荐

