使用Python实现矢量路径的压缩、解压与可视化

Written by

in

文章目录
  • 在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要。本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,再将其解压还原,并通过matplotlib进行可视化。这一过程可应用于字体设计、矢量图形编辑或Web应用中的路径数据传输。
  • 路径命令映射: M → moveTo:移动到起点 L → lineTo:绘制直线 Q → qCurveTo:二次贝塞尔曲线 Z → closePath:闭合路径 JSON压缩策略: 将坐标元组展平为一维列表,减少冗余。 闭合路径(Z)的参数为空列表。 matplotlib路径渲染: 使用Path对象和PathPatch实现复杂曲线的绘制。 CURVE3命令需成对使用,适配二次贝塞尔曲线的参数。
  • Web开发:将矢量路径数据嵌入SVG或Canvas元素。 字体设计:存储和传输字体轮廓路径。 数据可视化:动态生成并传输图表路径数据。 import matplotlib.pyplot as plt from matplotlib.path import Path import matplotlib.patches as patches # 解析输入数据 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 def show_ttf(): 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() import json def compress_path_to_json(data): command_map = { ‘moveTo’: ‘M’, ‘lineTo’: ‘L’, ‘qCurveTo’: ‘Q’, ‘closePath’: ‘Z’ } compressed = [] for cmd, params in data: command_type = command_map[cmd] points = [] if cmd == ‘closePath’: pass # closePath无需坐标 else: # 确保params[0]是坐标点列表(即使只有一个点) for param in params: points += list(param) compressed.append({ command_type: points }) return json.dumps(compressed, separators=(‘,’, ‘:’)) data = [(‘moveTo’, ((100, 177),)), (‘lineTo’, ((107, 169),)), (‘qCurveTo’, ((116, 172), (127, 172))), (‘lineTo’, ((240, 172),)), (‘lineTo’, ((224, 190),)), (‘lineTo’, ((212, 177),)), (‘lineTo’, ((175, 177),)), (‘qCurveTo’, ((183, 186), (176, 200), (154, 210))), (‘lineTo’, ((152, 207),)), (‘qCurveTo’, ((164, 190), (166, 177))), (‘closePath’, ()), (‘moveTo’, ((204, 143),)), (‘lineTo’, ((211, 148),)), (‘lineTo’, ((198, 162),)), (‘lineTo’, ((189, 152),)), (‘lineTo’, ((143, 152),)), (‘lineTo’, ((128, 160),)), (‘qCurveTo’, ((129, 149), (129, 116), (128, 102))), (‘lineTo’, ((142, 106),)), (‘lineTo’, ((142, 114),)), (‘lineTo’, ((191, 114),)), (‘lineTo’, ((191, 105),)), (‘lineTo’, ((205, 111),)), (‘qCurveTo’, ((204, 119), (204, 135), (204, 143))), (‘closePath’, ()), (‘moveTo’, ((142, 147),)), (‘lineTo’, ((191, 147),)), (‘lineTo’, ((191, 119),)), (‘lineTo’, ((142, 119),)), (‘closePath’, ()), (‘moveTo’, ((119, 87),)), (‘lineTo’, ((218, 87),)), (‘lineTo’, ((218, 6),)), (‘qCurveTo’, ((218, -3), (210, -5), (181, -3))), (‘lineTo’, ((181, -8),)), (‘qCurveTo’, ((212, -13), (212, -26))), (‘qCurveTo’, ((221, -22), (231, -12), (231, 2))), (‘lineTo’, ((231, 80),)), (‘lineTo’, ((240, 87),)), (‘lineTo’, ((224, 102),)), (‘lineTo’, ((216, 92),)), (‘lineTo’, ((119, 92),)), (‘lineTo’, ((105, 100),)), (‘qCurveTo’, ((106, 84), (106, 5), (105, -26))), (‘lineTo’, ((119, -18),)), (‘closePath’, ()), (‘moveTo’, ((196, 58),)), (‘lineTo’, ((203, 63),)), (‘lineTo’, ((188, 76),)), (‘lineTo’, ((182, 67),)), (‘lineTo’, ((151, 67),)), (‘lineTo’, ((137, 76),)), (‘qCurveTo’, ((138, 59), (138, 30), (137, 5))), (‘lineTo’, ((150, 11),)), (‘lineTo’, ((150, 21),)), (‘lineTo’, ((184, 21),)), (‘lineTo’, ((184, 10),)), (‘lineTo’, ((197, 16),)), (‘qCurveTo’, ((196, 27), (196, 48), (196, 58))), (‘closePath’, ()), (‘moveTo’, ((150, 62),)), (‘lineTo’, ((184, 62),)), (‘lineTo’, ((184, 26),)), (‘lineTo’, ((150, 26),)), (‘closePath’, ()), (‘moveTo’, ((36, 63),)), (‘qCurveTo’, ((66, 100), (94, 148))), (‘lineTo’, ((103, 152),)), (‘lineTo’, ((83, 163),)), (‘qCurveTo’, ((74, 138), (66, 125))), (‘lineTo’, ((30, 123),)), (‘qCurveTo’, ((50, 154), (71, 193))), (‘lineTo’, ((82, 197),)), (‘lineTo’, ((59, 209),)), (‘qCurveTo’, ((51, 178), (23, 124), (14, 124))), (‘lineTo’, ((25, 106),)), (‘qCurveTo’, ((31, 111), (50, 117), (63, 119))), (‘qCurveTo’, ((44, 87), (24, 63), (18, 62))), (‘lineTo’, ((28, 44),)), (‘qCurveTo’, ((39, 51), (68, 60), (98, 66))), (‘lineTo’, ((97, 70),)), (‘qCurveTo’, ((67, 66), (36, 63))), (‘closePath’, ()), (‘moveTo’, ((11, 14),)), (‘lineTo’, ((21, -4),)), (‘qCurveTo’, ((30, 4), (65, 20), (95, 30))), (‘lineTo’, ((94, 34),)), (‘qCurveTo’, ((72, 28), (25, 16), (11, 14))), (‘closePath’, ())] def decompress_json_to_path(compressed_json): command_map = { ‘M’: ‘moveTo’, ‘L’: ‘lineTo’, ‘Q’: ‘qCurveTo’, ‘Z’: ‘closePath’ } data = json.loads(compressed_json) decompressed = [] for item in data: cmd_char = next(iter(item)) # 获取命令字符 points = item[cmd_char] original_cmd = command_map[cmd_char] if not points: # closePath,参数为空 decompressed.append((original_cmd, ())) else: # 将points列表转换为坐标点元组的元组 tuples = [] for i in range(0, len(points), 2): x = points[i] y = points[i + 1] tuples.append((x, y)) params = tuple(tuples) decompressed.append((original_cmd, params)) return decompressed compressed_json = compress_path_to_json(data) # 解压 decompressed = decompress_json_to_path(compressed_json) 以上就是使用Python实现矢量路径的压缩、解压与可视化的详细内容,更多关于Python矢量路径解压缩与可视化的资料请关注风君子博客其它相关文章! 您可能感兴趣的文章: Python数据可视化plt.savefig如何将图片存入固定路径 Python绘图实现台风路径可视化代码实例 Python文件路径模式匹配glob模块使用详解 Python进行文件路径处理的模块详解 python修改pip install默认安装路径的详细步骤
  • 目录
    • 引言
    • 核心功能概述
      • 1. 路径命令解析
      • 2. 路径数据压缩
      • 3. 路径数据解压
      • 4. 可视化
    • 代码实现详解
      • 1. 路径命令解析(parse_commands函数)
        • 关键点:
      • 2. 路径数据压缩(compress_path_to_json函数)
        • 示例输出:
      • 3. 路径数据解压(decompress_json_to_path函数)
        • 4. 可视化渲染(show_ttf函数)
        • 完整代码与运行结果
          • 示例数据
            • 执行流程
            • 结果展示
              • 1. 压缩后的JSON片段
                • 2. 解压后的路径数据
                • 技术要点总结
                  • 应用场景

                    在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要。本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,再将其解压还原,并通过matplotlib进行可视化。这一过程可应用于字体设计、矢量图形编辑或Web应用中的路径数据传输。

                    • 输入:包含moveTolineToqCurveTo(二次贝塞尔曲线)、closePath命令的路径数据。
                    • 输出:转换为matplotlib.path.Path对象,用于绘制矢量图形。

                    • 将路径命令序列转换为紧凑的JSON格式,便于存储或传输。
                    • 示例:moveTo((100, 177)) → {"M":[100,177]}

                    • 将JSON格式还原为原始路径命令序列,确保数据完整性。

                    • 使用matplotlib渲染路径,验证压缩/解压过程的正确性。

                    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):
                                    control = params[i]
                                    end = params[i+1] if i+1 < len(params) else params[-1]
                                    codes.extend([Path.CURVE3, Path.CURVE3])
                                    vertices.extend([control, end])
                            elif cmd == 'closePath':
                                codes.append(Path.CLOSEPOLY)
                                vertices.append(vertices[0])  # 闭合路径回到起点
                        return codes, vertices
                    

                    • 二次贝塞尔曲线qCurveTo命令需两个控制点和一个终点,通过Path.CURVE3实现。
                    • 闭合路径CLOSEPOLY命令自动连接最后一个点到起点。

                    def compress_path_to_json(data):
                        command_map = {'moveTo': 'M', 'lineTo': 'L', 'qCurveTo': 'Q', 'closePath': 'Z'}
                        compressed = []
                        for cmd, params in data:
                            cmd_short = command_map[cmd]
                            points = []
                            if cmd == 'closePath':
                                compressed.append({cmd_short: []})
                            else:
                                # 将坐标元组展平为一维列表(如 [(x,y), (a,b)] → [x,y,a,b])
                                for coord in params:
                                    points.extend(list(coord))
                                compressed.append({cmd_short: points})
                        return json.dumps(compressed, separators=(',', ':'))
                    

                    [{"M":[100,177]},{"L":[107,169]},{"Q":[116,172,127,172]},...]
                    

                    def decompress_json_to_path(compressed_json):
                        command_map = {'M': 'moveTo', 'L': 'lineTo', 'Q': 'qCurveTo', 'Z': 'closePath'}
                        data = json.loads(compressed_json)
                        decompressed = []
                        for item in data:
                            cmd_short = next(iter(item))
                            points = item[cmd_short]
                            cmd = command_map[cmd_short]
                            if not points:
                                decompressed.append((cmd, ()))  # 闭合路径无参数
                            else:
                                # 将一维列表转换为坐标元组(如 [x,y,a,b] → [(x,y), (a,b)])
                                coords = []
                                for i in range(0, len(points), 2):
                                    coords.append((points[i], points[i+1]))
                                decompressed.append((cmd, tuple(coords)))
                        return decompressed
                    

                    def show_ttf(data):
                        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)  # 根据数据范围调整坐标轴
                        ax.set_ylim(-30, 220)
                        plt.gca().set_aspect('equal')
                        plt.show()
                    

                    data = [
                        ('moveTo', ((100, 177),)),
                        ('lineTo', ((107, 169),)),
                        ('qCurveTo', ((116, 172), (127, 172))),
                        # ... 其他路径命令(如闭合路径、复杂曲线)
                    ]
                    

                    # 压缩数据
                    compressed_json = compress_path_to_json(data)
                    print("压缩后的JSON:", compressed_json)
                    
                    # 解压数据
                    decompressed = decompress_json_to_path(compressed_json)
                    print("解压后的路径数据:", decompressed)
                    
                    # 可视化
                    show_ttf(decompressed)
                    

                    [
                      {"M":[100,177]},
                      {"L":[107,169]},
                      {"Q":[116,172,127,172]},
                      {"Z":[]}
                    ]
                    

                    [
                        ('moveTo', ((100, 177),)),
                        ('lineTo', ((107, 169),)),
                        ('qCurveTo', ((116, 172), (127, 172))),
                        ('closePath', ())
                    ]
                    

                    1. 路径命令映射

                      • M → moveTo:移动到起点
                      • L → lineTo:绘制直线
                      • Q → qCurveTo:二次贝塞尔曲线
                      • Z → closePath:闭合路径
                    2. JSON压缩策略

                      • 将坐标元组展平为一维列表,减少冗余。
                      • 闭合路径(Z)的参数为空列表。
                    3. matplotlib路径渲染

                      • 使用Path对象和PathPatch实现复杂曲线的绘制。
                      • CURVE3命令需成对使用,适配二次贝塞尔曲线的参数。

                    • Web开发:将矢量路径数据嵌入SVG或Canvas元素。
                    • 字体设计:存储和传输字体轮廓路径。
                    • 数据可视化:动态生成并传输图表路径数据。
                    import matplotlib.pyplot as plt
                    from matplotlib.path import Path
                    import matplotlib.patches as patches
                    
                    
                    # 解析输入数据
                    
                    
                    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
                    
                    
                    def show_ttf():
                        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()
                    
                    
                    import json
                    
                    
                    def compress_path_to_json(data):
                        command_map = {
                            'moveTo': 'M',
                            'lineTo': 'L',
                            'qCurveTo': 'Q',
                            'closePath': 'Z'
                        }
                    
                        compressed = []
                        for cmd, params in data:
                            command_type = command_map[cmd]
                            points = []
                            if cmd == 'closePath':
                                pass  # closePath无需坐标
                            else:
                                # 确保params[0]是坐标点列表(即使只有一个点)
                                for param in params:
                                    points += list(param)
                    
                            compressed.append({
                                command_type: points
                            })
                    
                        return json.dumps(compressed, separators=(',', ':'))
                    
                    
                    data = [('moveTo', ((100, 177),)), ('lineTo', ((107, 169),)), ('qCurveTo', ((116, 172), (127, 172))),
                            ('lineTo', ((240, 172),)), ('lineTo', ((224, 190),)), ('lineTo', ((212, 177),)), ('lineTo', ((175, 177),)),
                            ('qCurveTo', ((183, 186), (176, 200), (154, 210))), ('lineTo', ((152, 207),)),
                            ('qCurveTo', ((164, 190), (166, 177))), ('closePath', ()), ('moveTo', ((204, 143),)), ('lineTo', ((211, 148),)),
                            ('lineTo', ((198, 162),)), ('lineTo', ((189, 152),)), ('lineTo', ((143, 152),)), ('lineTo', ((128, 160),)),
                            ('qCurveTo', ((129, 149), (129, 116), (128, 102))), ('lineTo', ((142, 106),)), ('lineTo', ((142, 114),)),
                            ('lineTo', ((191, 114),)), ('lineTo', ((191, 105),)), ('lineTo', ((205, 111),)),
                            ('qCurveTo', ((204, 119), (204, 135), (204, 143))), ('closePath', ()), ('moveTo', ((142, 147),)),
                            ('lineTo', ((191, 147),)), ('lineTo', ((191, 119),)), ('lineTo', ((142, 119),)), ('closePath', ()),
                            ('moveTo', ((119, 87),)), ('lineTo', ((218, 87),)), ('lineTo', ((218, 6),)),
                            ('qCurveTo', ((218, -3), (210, -5), (181, -3))), ('lineTo', ((181, -8),)),
                            ('qCurveTo', ((212, -13), (212, -26))), ('qCurveTo', ((221, -22), (231, -12), (231, 2))),
                            ('lineTo', ((231, 80),)), ('lineTo', ((240, 87),)), ('lineTo', ((224, 102),)), ('lineTo', ((216, 92),)),
                            ('lineTo', ((119, 92),)), ('lineTo', ((105, 100),)), ('qCurveTo', ((106, 84), (106, 5), (105, -26))),
                            ('lineTo', ((119, -18),)), ('closePath', ()), ('moveTo', ((196, 58),)), ('lineTo', ((203, 63),)),
                            ('lineTo', ((188, 76),)), ('lineTo', ((182, 67),)), ('lineTo', ((151, 67),)), ('lineTo', ((137, 76),)),
                            ('qCurveTo', ((138, 59), (138, 30), (137, 5))), ('lineTo', ((150, 11),)), ('lineTo', ((150, 21),)),
                            ('lineTo', ((184, 21),)), ('lineTo', ((184, 10),)), ('lineTo', ((197, 16),)),
                            ('qCurveTo', ((196, 27), (196, 48), (196, 58))), ('closePath', ()), ('moveTo', ((150, 62),)),
                            ('lineTo', ((184, 62),)), ('lineTo', ((184, 26),)), ('lineTo', ((150, 26),)), ('closePath', ()),
                            ('moveTo', ((36, 63),)), ('qCurveTo', ((66, 100), (94, 148))), ('lineTo', ((103, 152),)),
                            ('lineTo', ((83, 163),)), ('qCurveTo', ((74, 138), (66, 125))), ('lineTo', ((30, 123),)),
                            ('qCurveTo', ((50, 154), (71, 193))), ('lineTo', ((82, 197),)), ('lineTo', ((59, 209),)),
                            ('qCurveTo', ((51, 178), (23, 124), (14, 124))), ('lineTo', ((25, 106),)),
                            ('qCurveTo', ((31, 111), (50, 117), (63, 119))), ('qCurveTo', ((44, 87), (24, 63), (18, 62))),
                            ('lineTo', ((28, 44),)), ('qCurveTo', ((39, 51), (68, 60), (98, 66))), ('lineTo', ((97, 70),)),
                            ('qCurveTo', ((67, 66), (36, 63))), ('closePath', ()), ('moveTo', ((11, 14),)), ('lineTo', ((21, -4),)),
                            ('qCurveTo', ((30, 4), (65, 20), (95, 30))), ('lineTo', ((94, 34),)),
                            ('qCurveTo', ((72, 28), (25, 16), (11, 14))), ('closePath', ())]
                    
                    
                    def decompress_json_to_path(compressed_json):
                        command_map = {
                            'M': 'moveTo',
                            'L': 'lineTo',
                            'Q': 'qCurveTo',
                            'Z': 'closePath'
                        }
                    
                        data = json.loads(compressed_json)
                        decompressed = []
                    
                        for item in data:
                            cmd_char = next(iter(item))  # 获取命令字符
                            points = item[cmd_char]
                            original_cmd = command_map[cmd_char]
                    
                            if not points:
                                # closePath,参数为空
                                decompressed.append((original_cmd, ()))
                            else:
                                # 将points列表转换为坐标点元组的元组
                                tuples = []
                                for i in range(0, len(points), 2):
                                    x = points[i]
                                    y = points[i + 1]
                                    tuples.append((x, y))
                                params = tuple(tuples)
                                decompressed.append((original_cmd, params))
                    
                        return decompressed
                    
                    
                    compressed_json = compress_path_to_json(data)
                    
                    # 解压
                    decompressed = decompress_json_to_path(compressed_json)
                    
                    
                    
                    

                    以上就是使用Python实现矢量路径的压缩、解压与可视化的详细内容,更多关于Python矢量路径解压缩与可视化的资料请关注风君子博客其它相关文章!

                    您可能感兴趣的文章:

                    • Python数据可视化plt.savefig如何将图片存入固定路径
                    • Python绘图实现台风路径可视化代码实例
                    • Python文件路径模式匹配glob模块使用详解
                    • Python进行文件路径处理的模块详解
                    • python修改pip install默认安装路径的详细步骤

                    站内搜索