生成 PDF 文件,绘制带圆角的多边形

2024-01-11

如果我想编写一个生成以下结果的 Python 脚本,那么什么工具适合这项工作呢?PDF 格式的矢量图形?特别是,我需要绘制填充带圆角的多边形(即由直线和直线组成的平面图形圆弧).

看起来绘图库 http://matplotlib.sourceforge.net/使得绘制带有圆角的矩形和带有尖角的一般多边形变得相当容易。然而,要绘制带有圆角的多边形,似乎我必须首先计算近似形状的贝塞尔曲线。

有没有更简单的方法可以利用?或者是否有另一个库可以用来计算近似于我想要生成的形状的贝塞尔曲线?理想情况下,我只需为每个顶点指定(位置,角半径)对。

这是一个例子:我想指定红色多边形(+每个角的半径),库将输出灰色图形:

(对于凸多边形,我可以作弊并使用粗笔绘制多边形的轮廓。但是,这在非凸情况下不起作用。)


这是一个有点 hacky matplotlib 解决方案。主要的复杂性与使用 matplotlib 有关Path构建组合的对象Path.

#!/usr/bin/env python

import numpy as np
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Polygon
from matplotlib.transforms import Bbox, BboxTransformTo

def side(a, b, c):
    "On which side of line a-b is point c? Returns -1, 0, or 1."
    return np.sign(np.linalg.det(np.c_[[a,b,c],[1,1,1]]))

def center((prev, curr, next), radius):
    "Find center of arc approximating corner at curr."
    p0, p1 = prev
    c0, c1 = curr
    n0, n1 = next
    dp = radius * np.hypot(c1 - p1, c0 - p0)
    dn = radius * np.hypot(c1 - n1, c0 - n0)
    p = p1 * c0 - p0 * c1
    n = n1 * c0 - n0 * c1
    results = \
        np.linalg.solve([[p1 - c1, c0 - p0],
                         [n1 - c1, c0 - n0]],
                        [[p - dp, p - dp, p + dp, p + dp],
                         [n - dn, n + dn, n - dn, n + dn]])
    side_n = side(prev, curr, next)
    side_p = side(next, curr, prev)
    for r in results.T:
        if (side(prev, curr, r), side(next, curr, r)) == (side_n, side_p):
            return r
    raise ValueError, "Cannot find solution"

def proj((prev, curr, next), center):
    "Project center onto lines prev-curr and next-curr."
    p0, p1 = prev = np.asarray(prev)
    c0, c1 = curr = np.asarray(curr)
    n0, n1 = next = np.asarray(next)
    pc = curr - prev
    nc = curr - next
    pc2 = np.dot(pc, pc)
    nc2 = np.dot(nc, nc)
    return (prev + np.dot(center - prev, pc)/pc2 * pc,
            next + np.dot(center - next, nc)/nc2 * nc)

def rad2deg(angle):
    return angle * 180.0 / np.pi

def angle(center, point):
    x, y = np.asarray(point) - np.asarray(center)
    return np.arctan2(y, x)

def arc_path(center, start, end):
    "Return a Path for an arc from start to end around center."
    # matplotlib arcs always go ccw so we may need to mirror
    mirror = side(center, start, end) < 0
    if mirror: 
        start *= [1, -1]
        center *= [1, -1]
        end *= [1, -1]
    return Path.arc(rad2deg(angle(center, start)),
                    rad2deg(angle(center, end))), \
           mirror

def path(vertices, radii):
    "Return a Path for a closed rounded polygon."
    if np.isscalar(radii):
        radii = np.repeat(radii, len(vertices))
    else:
        radii = np.asarray(radii)
    pv = []
    pc = []
    first = True
    for i in range(len(vertices)):
        if i == 0:
            seg = (vertices[-1], vertices[0], vertices[1])
        elif i == len(vertices) - 1:
            seg = (vertices[-2], vertices[-1], vertices[0])
        else:
            seg = vertices[i-1:i+2]
        r = radii[i]
        c = center(seg, r)
        a, b = proj(seg, c)
        arc, mirror = arc_path(c, a, b)
        m = [1,1] if not mirror else [1,-1]
        bb = Bbox([c, c + (r, r)])
        iter = arc.iter_segments(BboxTransformTo(bb))
        for v, c in iter:
            if c == Path.CURVE4:
                pv.extend([m * v[0:2], m * v[2:4], m * v[4:6]])
                pc.extend([c, c, c])
            elif c == Path.MOVETO:
                pv.append(m * v)
                if first:
                    pc.append(Path.MOVETO)
                    first = False
                else:
                    pc.append(Path.LINETO)
    pv.append([0,0])
    pc.append(Path.CLOSEPOLY)

    return Path(pv, pc)

if __name__ == '__main__':
    from matplotlib import pyplot
    fig = pyplot.figure()
    ax = fig.add_subplot(111)
    vertices = [[3,0], [5,2], [10,0], [6,9], [6,5], [3, 5], [0,2]]

    patch = Polygon(vertices, edgecolor='red', facecolor='None',
                    linewidth=1)
    ax.add_patch(patch)

    patch = PathPatch(path(vertices, 0.5), 
                      edgecolor='black', facecolor='blue', alpha=0.4,
                      linewidth=2)
    ax.add_patch(patch)

    ax.set_xlim(-1, 11)
    ax.set_ylim(-1, 9)
    fig.savefig('foo.pdf')
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

生成 PDF 文件,绘制带圆角的多边形 的相关文章

随机推荐

  • G1垃圾收集器:为什么幸存者空间总是满的?

    这是输出jmap heap命令 Survivor Space regions 52 capacity 54525952 52 0MB used 54525952 52 0MB free 0 0 0MB 100 0 used 我已经执行了很多
  • 使用UniversalImageDownloader的ListView滚动不平滑

    我正在使用包含图像的 ListView 这些图像是从互联网加载到适配器内的 因此我正在使用通用图像下载器 不幸的是 当我向下滚动需要下载新内容的位置时 ListView 的滚动会短暂 滞后 我实际上期望像 ListView 这样的行为滚动完
  • GitHub 组织未出现在“持续部署”页面中

    我的个人 GitHub 帐户属于五个组织 从 GitHub 设置 Azure 持续集成时 五个组织中只有四个出现在列表中 我们尝试过登录和注销 GitHub 和 Azure 此外 我们尝试更改 Azure 中的帐户 然后重新添加相同的帐户
  • 按新指南随机排序

    为了从 Sql 查询中以随机顺序获取结果 我通常按新的 Guid 进行排序 我之前已经使用实体框架完成了此操作 但由于某种原因它现在不起作用 例如 使用 Adventureworks2008r2 数据库 我在 LinqPad 中运行以下查询
  • 如何使用 Javascript 将 cookie 存储在本地存储中?

    我有一个适用于 Android 希望以后还有 iPhone 的应用程序 它基于 Javascript 并使用 Phonegap Applaud 制作成应用程序 不幸的是 设置和获取 cookie 在 Android 上不起作用 这可能是 A
  • 内容仅在我单击时出现在页面上

    我已经通过 Angular 2 应用程序连接到 Firebase 3 没有什么特别的 只是一个包含一小部分数据的简单表 在我的 Angular 2 应用程序中 我在服务中创建了服务 我创建了一个侦听器事件 如下所示 getAddedBugs
  • 创建 JavaScript 自定义事件

    我想用 JavaScript 创建一个自定义事件 我有一个 WPF 应用程序 里面有一个 WebBrowser 还有一个带有 JavaScript 的 HTML 页面 我使用打印机工作 当打印机的状态发生变化时 它会触发 NET 中的事件
  • 将特色图像添加到 wp_nav_menu 项目

    这是一个自我问答 如何修改 wp nav menu 输出中出现的文本 html 例如 我想为页面和类别添加特色图像 您可以看到使用自定义步行器执行此操作的示例 但对于小的更改来说 代码非常复杂 当然有办法用过滤器来做到这一点吗 这是我在 W
  • 基于范围的临时对象

    我知道 一般来说 基于范围的临时对象的生命周期for循环扩展到整个循环 我读过C 11 基于范围的 for 语句 range init 生命周期 https stackoverflow com questions 9657708 c11 t
  • AddDbContext 或 AddDbContextPool

    对于 Asp net Core 应用程序 我们必须使用哪一个 AddDbContext or AddDbContextPool 根据 EF Core 文档 AddDbContextPool提供高性能 但默认的 Asp net Core 项目
  • System.Windows.Forms.Timer 未触发

    我想用一个System Windows Forms Timer确保事件在我正在创建的 Excel 插件的 UI 线程上触发 我按如下方式构造计时器 private System Windows Forms Timer timer priva
  • 如果我在我的反应应用程序中使用打字稿元组,我会在 vscode 的第 1 行收到 eslint 错误?

    错误是Parsing error Cannot read property map of undefined 我设置了一个新文件只是为了重现错误 export default gt let something string boolean
  • Django - ModelForm:添加不属于模型的字段

    注意 使用django crispy forms我的表格的图书馆 如果您有解决我的问题的方法 但不使用cripsy forms图书馆 我仍然接受它 不要试图挑剔 只需要一个解决方案 解决方法 谢谢 在我的表格中Meta我班设定了模型 Dri
  • 我有什么选择来寻找 Monotouch 异常,例如“System.Exception:在已被 GC 处理的托管对象上从 Objective-c 调用的选择器”?

    我在下面得到了一些例外情况 我有什么选择来追踪这些问题 我对这些感到有点迷失 因为它们只是偶尔发生 并且仅在设备上发生 但从未在模拟器中发生 System Exception Selector invoked from objective
  • 为什么我不能在单词边界旁边使用重音字符?

    我正在尝试制作一个与人名匹配的动态正则表达式 它对大多数名称都没有问题 直到我在名称末尾遇到重音字符 示例 一些奇特的名字 到目前为止我使用的正则表达式是 b Fancy Nam Nam b i 像这样使用 Goal Some Fancy
  • as3数组按索引删除

    我有一个数组 cat dog budgie 并想通过索引删除该项目 此刻我有 function removeit myindex animals myindex animals pop 你想要拼接 http help adobe com e
  • 如何过滤子文档数组?

    我有一个结构如下的文档 id ObjectId 564d2702d4c68225cb00726f list a NumberInt 1 test public a NumberInt 2 test public a NumberInt 3
  • 返回按其他列分组的第一个非空值

    How to go from left to right Basically for each TIME person combo I need the first non null value from the id columns 聚合
  • Doctrine2 忽略数据库表

    我正在使用 Doctrine 2 我想生成数据库的 ORM 但我不想选择数据库的所有表 例如 在此数据库中 表1没有主键 表2 正常情况 我想使用此命令仅选择表 2 doctrine mapping convert from databas
  • 生成 PDF 文件,绘制带圆角的多边形

    如果我想编写一个生成以下结果的 Python 脚本 那么什么工具适合这项工作呢 PDF 格式的矢量图形 特别是 我需要绘制填充带圆角的多边形 即由直线和直线组成的平面图形圆弧 看起来绘图库 http matplotlib sourcefor