使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

Written by

in

文章目录
  • import matplotlib.pyplot as plt from matplotlib.path import Path from matplotlib.patches import PathPatch # 示例数据(部分) data = [(‘moveTo’, ((163, 68),)), (‘lineTo’, ((219, 68),)), …] # 完整数据见原文 def parse_commands(data): codes = [] vertices = [] for cmd, params in data: if cmd == ‘moveTo’: codes.append(Path.MOVETO) vertices.append(params[0]) elif cmd == ‘lineTo’: codes.append(Path.LINETO) vertices.append(params[0]) elif cmd == ‘qCurveTo’: for i in range(0, len(params), 2): codes.extend([Path.CURVE3, Path.CURVE3]) vertices.extend([params[i], params[i+1]]) elif cmd == ‘closePath’: codes.append(Path.CLOSEPOLY) vertices.append(vertices[0]) return codes, vertices codes, vertices = parse_commands(data) path = Path(vertices, codes) fig, ax = plt.subplots() patch = PathPatch(path, facecolor=’orange’, lw=2) ax.add_patch(patch) ax.set_xlim(0, 250) ax.set_ylim(-30, 220) ax.set_aspect(‘equal’) plt.show()
  • 通过本文,你学会了如何将字体轮廓的路径指令转换为矢量图形。这一技术不仅适用于字体设计,还可用于游戏开发、UI设计等领域。尝试将代码嵌入到Web应用(如Flask)中,或结合Markdown生成静态博客,进一步扩展你的项目! import matplotlib.pyplot as plt from matplotlib.path import Path import matplotlib.patches as patches # 解析输入数据 data = [(‘moveTo’, ((163, 68),)), (‘lineTo’, ((219, 68),)), (‘lineTo’, ((219, 8),)), (‘qCurveTo’, ((219, -2), (205, -3), (181, -1))), (‘lineTo’, ((181, -5),)), (‘qCurveTo’, ((216, -13), (214, -25))), (‘qCurveTo’, ((223, -20), (232, -10), (232, 3))), (‘lineTo’, ((232, 62),)), (‘lineTo’, ((240, 69),)), (‘lineTo’, ((225, 82),)), (‘lineTo’, ((217, 73),)), (‘lineTo’, ((165, 73),)), (‘qCurveTo’, ((172, 86), (180, 93))), (‘lineTo’, ((165, 100),)), (‘lineTo’, ((211, 100),)), (‘lineTo’, ((211, 91),)), (‘lineTo’, ((225, 97),)), (‘qCurveTo’, ((224, 107), (224, 126), (224, 139))), (‘lineTo’, ((232, 147),)), (‘lineTo’, ((211, 156),)), (‘lineTo’, ((211, 105),)), (‘lineTo’, ((125, 105),)), (‘lineTo’, ((125, 144),)), (‘lineTo’, ((134, 152),)), (‘lineTo’, ((111, 160),)), (‘qCurveTo’, ((112, 148), (112, 109))), (‘lineTo’, ((104, 102),)), (‘lineTo’, ((118, 91),)), (‘lineTo’, ((124, 100),)), (‘lineTo’, ((159, 100),)), (‘qCurveTo’, ((157, 88), (152, 73))), (‘lineTo’, ((116, 73),)), (‘lineTo’, ((101, 81),)), (‘qCurveTo’, ((102, 64), (102, 1), (101, -27))), (‘lineTo’, ((116, -18),)), (‘qCurveTo’, ((115, -8), (115, 10))), (‘lineTo’, ((115, 68),)), (‘lineTo’, ((149, 68),)), (‘qCurveTo’, ((142, 52), (129, 36), (123, 33))), (‘lineTo’, ((136, 15),)), (‘qCurveTo’, ((146, 23), (171, 30), (189, 33))), (‘qCurveTo’, ((191, 26), (193, 12), (204, 14), (208, 27), (199, 43), (179, 60))), (‘lineTo’, ((176, 58),)), (‘qCurveTo’, ((184, 46), (188, 38))), (‘lineTo’, ((143, 34),)), (‘qCurveTo’, ((154, 48), (163, 68))), (‘closePath’, ()), (‘moveTo’, ((195, 154),)), (‘lineTo’, ((206, 155),)), (‘lineTo’, ((189, 170),)), (‘qCurveTo’, ((180, 156), (171, 146))), (‘qCurveTo’, ((155, 156), (138, 164))), (‘lineTo’, ((136, 161),)), (‘qCurveTo’, ((154, 150), (164, 140))), (‘qCurveTo’, ((151, 124), (128, 110))), (‘lineTo’, ((131, 107),)), (‘qCurveTo’, ((155, 119), (171, 133))), (‘qCurveTo’, ((180, 125), (191, 108), (198, 117), (197, 130), (182, 141))), (‘qCurveTo’, ((189, 148), (195, 154))), (‘closePath’, ()), (‘moveTo’, ((97, 179),)), (‘lineTo’, ((105, 171),)), (‘qCurveTo’, ((114, 174), (125, 174))), (‘lineTo’, ((242, 174),)), (‘lineTo’, ((225, 191),)), (‘lineTo’, ((213, 179),)), (‘lineTo’, ((170, 179),)), (‘qCurveTo’, ((179, 187), (173, 201), (152, 210))), (‘lineTo’, ((150, 207),)), (‘qCurveTo’, ((161, 192), (164, 179))), (‘closePath’, ()), (‘moveTo’, ((36, 64),)), (‘qCurveTo’, ((68, 111), (88, 146))), (‘lineTo’, ((101, 150),)), (‘lineTo’, ((80, 164),)), (‘qCurveTo’, ((73, 143), (64, 126))), (‘lineTo’, ((30, 124),)), (‘qCurveTo’, ((48, 156), (65, 192))), (‘lineTo’, ((76, 198),)), (‘lineTo’, ((54, 210),)), (‘qCurveTo’, ((52, 193), (23, 124), (14, 124))), (‘lineTo’, ((26, 106),)), (‘qCurveTo’, ((35, 115), (52, 119), (61, 121))), (‘qCurveTo’, ((46, 93), (24, 62), (17, 61))), (‘lineTo’, ((30, 44),)), (‘qCurveTo’, ((37, 51), (65, 63), (91, 68))), (‘lineTo’, ((91, 73),)), (‘qCurveTo’, ((64, 68), (36, 64))), (‘closePath’, ()), (‘moveTo’, ((15, 14),)), (‘lineTo’, ((25, -4),)), (‘qCurveTo’, ((36, 5), (69, 19), (99, 30))), (‘lineTo’, ((98, 34),)), (‘qCurveTo’, ((75, 27), (31, 17), (15, 14))), (‘closePath’, ())] def parse_commands(data): codes = [] vertices = [] for command, params in data: if command == ‘moveTo’: codes.append(Path.MOVETO) vertices.append(params[0]) elif command == ‘lineTo’: codes.append(Path.LINETO) vertices.append(params[0]) elif command == ‘qCurveTo’: # Check if there are enough points to form a quadratic Bezier curve segment for i in range(0, len(params)-1, 2): # Ensure we don’t go out of bounds control_point = params[i] end_point = params[i + 1] codes.extend([Path.CURVE3, Path.CURVE3]) # Two CURVE3 commands for the quad Bezier vertices.extend([control_point, end_point]) elif command == ‘closePath’: codes.append(Path.CLOSEPOLY) vertices.append(vertices[0]) # Closing back to the start point return codes, vertices codes, vertices = parse_commands(data) path = Path(vertices, codes) fig, ax = plt.subplots() patch = patches.PathPatch(path, facecolor=’orange’, lw=2) ax.add_patch(patch) ax.set_xlim(0, 250) # Adjust these limits based on your data’s extent ax.set_ylim(-30, 220) # Adjust these limits based on your data’s extent plt.gca().set_aspect(‘equal’, adjustable=’box’) # Keep aspect ratio equal plt.show() 以上就是使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)的详细内容,更多关于Python Matplotlib可视化字体轮廓的资料请关注风君子博客其它相关文章! 您可能感兴趣的文章: 8种快速易用的Python Matplotlib数据可视化方法汇总(附源码) Python可视化工具Matplotlib的20种图表用法 使用Python Matplotlib处理地理数据可视化 详解Python如何在Web环境中使用Matplotlib进行数据可视化 Python利用matplotlib实现动态可视化详解
  • 目录
    • 背景知识
      • 字体轮廓的表示
    • 实现步骤
      • 1. 安装依赖库
      • 2. 准备数据
      • 3. 解析路径指令
      • 4. 绘制图形
    • 关键代码解释
      • 1. 路径指令解析
      • 2. 坐标范围调整
    • 扩展与注意事项
      • 1. 自定义样式
      • 2. 处理复杂路径
      • 3. 常见问题
    • 完整代码示例
      • 结论

        字体轮廓通常由一系列路径指令组成,例如:

        • moveTo:移动到起点
        • lineTo:绘制直线
        • qCurveTo:绘制二次贝塞尔曲线
        • closePath:闭合路径

        这些指令定义了字体的形状,例如汉字“字”的轮廓。通过解析这些指令,我们可以用Python生成对应的矢量图形。

        确保已安装必要的库:

        pip install matplotlib numpy
        

        我们使用一个示例字体轮廓数据(例如汉字“字”的路径指令):

        data = [('moveTo', ((163, 68),)), ('lineTo', ((219, 68),)), ...]  # 省略完整数据
        

        定义函数parse_commands将路径指令转换为matplotlib的顶点和代码格式:

        import matplotlib.path as Path
        
        def parse_commands(data):
            codes = []
            vertices = []
            for command, params in data:
                if command == 'moveTo':
                    codes.append(Path.MOVETO)
                    vertices.append(params[0])
                elif command == 'lineTo':
                    codes.append(Path.LINETO)
                    vertices.append(params[0])
                elif command == 'qCurveTo':
                    # 将二次贝塞尔曲线转换为三次贝塞尔曲线(matplotlib仅支持三次曲线)
                    for i in range(0, len(params), 2):
                        control_point = params[i]
                        end_point = params[i+1]
                        codes.extend([Path.CURVE3, Path.CURVE3])
                        vertices.extend([control_point, end_point])
                elif command == 'closePath':
                    codes.append(Path.CLOSEPOLY)
                    vertices.append(vertices[0])  # 闭合到起点
            return codes, vertices
        

        使用matplotlib生成路径并绘制:

        import matplotlib.pyplot as plt
        from matplotlib.patches import PathPatch
        
        # 解析数据
        codes, vertices = parse_commands(data)
        path = Path(vertices, codes)
        
        # 创建图形
        fig, ax = plt.subplots()
        patch = PathPatch(path, facecolor='orange', lw=2)
        ax.add_patch(patch)
        
        # 设置坐标范围和比例
        ax.set_xlim(0, 250)
        ax.set_ylim(-30, 220)
        ax.set_aspect('equal')
        
        plt.show()
        

        • moveTo:设置起点,对应Path.MOVETO
        • lineTo:绘制直线,对应Path.LINETO
        • qCurveTo:二次贝塞尔曲线需转换为三次曲线(Path.CURVE3)。例如:
        # 二次曲线参数:(control_point, end_point)
        codes.extend([Path.CURVE3, Path.CURVE3])
        vertices.extend([control_point, end_point])
        
        • closePath:闭合路径,对应Path.CLOSEPOLY

        通过ax.set_xlimax.set_ylim设置坐标范围,确保图形完整显示。例如:

        ax.set_xlim(0, 250)  # X轴范围
        ax.set_ylim(-30, 220)  # Y轴范围(部分坐标为负值)
        

        • 颜色与填充:修改facecoloredgecolor参数:
        patch = PathPatch(path, facecolor='lightblue', edgecolor='navy', lw=2)
        
        • 缩放与旋转:使用matplotlibtransform功能调整图形比例。

        • 多路径支持:如果数据包含多个独立路径(如汉字的多个部件),需拆分路径并分别绘制。
        • 贝塞尔曲线优化:对于复杂的二次曲线,可使用Path.CURVE4(三次贝塞尔曲线)进行更精确的转换。

        • 坐标超出范围:调整ax.set_xlimax.set_ylim的值,或自动计算数据边界:
        x_min = min(v[0] for v in vertices)
        x_max = max(v[0] for v in vertices)
        ax.set_xlim(x_min - 10, x_max + 10)
        
        • 路径不闭合:确保每个路径以closePath结尾。

        import matplotlib.pyplot as plt
        from matplotlib.path import Path
        from matplotlib.patches import PathPatch
        
        # 示例数据(部分)
        data = [('moveTo', ((163, 68),)), ('lineTo', ((219, 68),)), ...]  # 完整数据见原文
        
        def parse_commands(data):
            codes = []
            vertices = []
            for cmd, params in data:
                if cmd == 'moveTo':
                    codes.append(Path.MOVETO)
                    vertices.append(params[0])
                elif cmd == 'lineTo':
                    codes.append(Path.LINETO)
                    vertices.append(params[0])
                elif cmd == 'qCurveTo':
                    for i in range(0, len(params), 2):
                        codes.extend([Path.CURVE3, Path.CURVE3])
                        vertices.extend([params[i], params[i+1]])
                elif cmd == 'closePath':
                    codes.append(Path.CLOSEPOLY)
                    vertices.append(vertices[0])
            return codes, vertices
        
        codes, vertices = parse_commands(data)
        path = Path(vertices, codes)
        
        fig, ax = plt.subplots()
        patch = PathPatch(path, facecolor='orange', lw=2)
        ax.add_patch(patch)
        
        ax.set_xlim(0, 250)
        ax.set_ylim(-30, 220)
        ax.set_aspect('equal')
        plt.show()
        

        通过本文,你学会了如何将字体轮廓的路径指令转换为矢量图形。这一技术不仅适用于字体设计,还可用于游戏开发、UI设计等领域。尝试将代码嵌入到Web应用(如Flask)中,或结合Markdown生成静态博客,进一步扩展你的项目!

        import matplotlib.pyplot as plt
        from matplotlib.path import Path
        import matplotlib.patches as patches
        
        # 解析输入数据
        data = [('moveTo', ((163, 68),)), ('lineTo', ((219, 68),)), ('lineTo', ((219, 8),)),
                ('qCurveTo', ((219, -2), (205, -3), (181, -1))), ('lineTo', ((181, -5),)),
                ('qCurveTo', ((216, -13), (214, -25))), ('qCurveTo', ((223, -20), (232, -10), (232, 3))),
                ('lineTo', ((232, 62),)), ('lineTo', ((240, 69),)), ('lineTo', ((225, 82),)), ('lineTo', ((217, 73),)),
                ('lineTo', ((165, 73),)), ('qCurveTo', ((172, 86), (180, 93))), ('lineTo', ((165, 100),)),
                ('lineTo', ((211, 100),)), ('lineTo', ((211, 91),)), ('lineTo', ((225, 97),)),
                ('qCurveTo', ((224, 107), (224, 126), (224, 139))), ('lineTo', ((232, 147),)), ('lineTo', ((211, 156),)),
                ('lineTo', ((211, 105),)), ('lineTo', ((125, 105),)), ('lineTo', ((125, 144),)), ('lineTo', ((134, 152),)),
                ('lineTo', ((111, 160),)), ('qCurveTo', ((112, 148), (112, 109))), ('lineTo', ((104, 102),)),
                ('lineTo', ((118, 91),)), ('lineTo', ((124, 100),)), ('lineTo', ((159, 100),)),
                ('qCurveTo', ((157, 88), (152, 73))), ('lineTo', ((116, 73),)), ('lineTo', ((101, 81),)),
                ('qCurveTo', ((102, 64), (102, 1), (101, -27))), ('lineTo', ((116, -18),)),
                ('qCurveTo', ((115, -8), (115, 10))), ('lineTo', ((115, 68),)), ('lineTo', ((149, 68),)),
                ('qCurveTo', ((142, 52), (129, 36), (123, 33))), ('lineTo', ((136, 15),)),
                ('qCurveTo', ((146, 23), (171, 30), (189, 33))),
                ('qCurveTo', ((191, 26), (193, 12), (204, 14), (208, 27), (199, 43), (179, 60))), ('lineTo', ((176, 58),)),
                ('qCurveTo', ((184, 46), (188, 38))), ('lineTo', ((143, 34),)), ('qCurveTo', ((154, 48), (163, 68))),
                ('closePath', ()), ('moveTo', ((195, 154),)), ('lineTo', ((206, 155),)), ('lineTo', ((189, 170),)),
                ('qCurveTo', ((180, 156), (171, 146))), ('qCurveTo', ((155, 156), (138, 164))), ('lineTo', ((136, 161),)),
                ('qCurveTo', ((154, 150), (164, 140))), ('qCurveTo', ((151, 124), (128, 110))), ('lineTo', ((131, 107),)),
                ('qCurveTo', ((155, 119), (171, 133))),
                ('qCurveTo', ((180, 125), (191, 108), (198, 117), (197, 130), (182, 141))),
                ('qCurveTo', ((189, 148), (195, 154))), ('closePath', ()), ('moveTo', ((97, 179),)), ('lineTo', ((105, 171),)),
                ('qCurveTo', ((114, 174), (125, 174))), ('lineTo', ((242, 174),)), ('lineTo', ((225, 191),)),
                ('lineTo', ((213, 179),)), ('lineTo', ((170, 179),)), ('qCurveTo', ((179, 187), (173, 201), (152, 210))),
                ('lineTo', ((150, 207),)), ('qCurveTo', ((161, 192), (164, 179))), ('closePath', ()), ('moveTo', ((36, 64),)),
                ('qCurveTo', ((68, 111), (88, 146))), ('lineTo', ((101, 150),)), ('lineTo', ((80, 164),)),
                ('qCurveTo', ((73, 143), (64, 126))), ('lineTo', ((30, 124),)), ('qCurveTo', ((48, 156), (65, 192))),
                ('lineTo', ((76, 198),)), ('lineTo', ((54, 210),)), ('qCurveTo', ((52, 193), (23, 124), (14, 124))),
                ('lineTo', ((26, 106),)), ('qCurveTo', ((35, 115), (52, 119), (61, 121))),
                ('qCurveTo', ((46, 93), (24, 62), (17, 61))), ('lineTo', ((30, 44),)),
                ('qCurveTo', ((37, 51), (65, 63), (91, 68))), ('lineTo', ((91, 73),)), ('qCurveTo', ((64, 68), (36, 64))),
                ('closePath', ()), ('moveTo', ((15, 14),)), ('lineTo', ((25, -4),)),
                ('qCurveTo', ((36, 5), (69, 19), (99, 30))), ('lineTo', ((98, 34),)),
                ('qCurveTo', ((75, 27), (31, 17), (15, 14))), ('closePath', ())]
        
        
        def parse_commands(data):
            codes = []
            vertices = []
            for command, params in data:
                if command == 'moveTo':
                    codes.append(Path.MOVETO)
                    vertices.append(params[0])
                elif command == 'lineTo':
                    codes.append(Path.LINETO)
                    vertices.append(params[0])
                elif command == 'qCurveTo':
                    # Check if there are enough points to form a quadratic Bezier curve segment
                    for i in range(0, len(params)-1, 2):  # Ensure we don't go out of bounds
                        control_point = params[i]
                        end_point = params[i + 1]
                        codes.extend([Path.CURVE3, Path.CURVE3])  # Two CURVE3 commands for the quad Bezier
                        vertices.extend([control_point, end_point])
                elif command == 'closePath':
                    codes.append(Path.CLOSEPOLY)
                    vertices.append(vertices[0])  # Closing back to the start point
            return codes, vertices
        
        codes, vertices = parse_commands(data)
        
        path = Path(vertices, codes)
        
        fig, ax = plt.subplots()
        patch = patches.PathPatch(path, facecolor='orange', lw=2)
        ax.add_patch(patch)
        ax.set_xlim(0, 250)  # Adjust these limits based on your data's extent
        ax.set_ylim(-30, 220)  # Adjust these limits based on your data's extent
        plt.gca().set_aspect('equal', adjustable='box')  # Keep aspect ratio equal
        plt.show()
        
        

        以上就是使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)的详细内容,更多关于Python Matplotlib可视化字体轮廓的资料请关注风君子博客其它相关文章!

        您可能感兴趣的文章:

        • 8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)
        • Python可视化工具Matplotlib的20种图表用法
        • 使用Python Matplotlib处理地理数据可视化
        • 详解Python如何在Web环境中使用Matplotlib进行数据可视化
        • Python利用matplotlib实现动态可视化详解

        站内搜索