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

2025-04-27 17:50

本文主要是介绍使用Python实现矢量路径的压缩、解压与可视化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,...

引言

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

核心功能概述

1. 路径命令解析

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

2. 路径数据压缩

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

3. 路径数据解压

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

4. 可视化

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

代码实现详解

1. 路径命令解析(parse_commands函数)

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+1http://www.chinasem.cn] 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命令自动连接最后一个点到起点。

2.&nbChina编程sp;路径数据压缩(compress_path_to_json函数)

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]},...]

3. 路径数据解压(decompress_json_to_path函数)

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

4. 可视化渲染(show_ttf函数)

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)python  # 根据数据范围调整坐标轴
    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)

结果展示

1. 压缩后的JSON片段

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

2. 解压后的路径数据

[
    ('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),)), ('qCurvjseTo', ((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, 1python23),)),
        ('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矢量路径解压缩与可视化的资料请关注编程China编程(www.chinasem.cn)其它相关文章!

这篇关于使用Python实现矢量路径的压缩、解压与可视化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154402

相关文章

python获取网页表格的多种方法汇总

《python获取网页表格的多种方法汇总》我们在网页上看到很多的表格,如果要获取里面的数据或者转化成其他格式,就需要将表格获取下来并进行整理,在Python中,获取网页表格的方法有多种,下面就跟随小编... 目录1. 使用Pandas的read_html2. 使用BeautifulSoup和pandas3.

PyQt6/PySide6中QTableView类的实现

《PyQt6/PySide6中QTableView类的实现》本文主要介绍了PyQt6/PySide6中QTableView类的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学... 目录1. 基本概念2. 创建 QTableView 实例3. QTableView 的常用属性和方法

Pandas透视表(Pivot Table)的具体使用

《Pandas透视表(PivotTable)的具体使用》透视表用于在数据分析和处理过程中进行数据重塑和汇总,本文就来介绍一下Pandas透视表(PivotTable)的具体使用,感兴趣的可以了解一下... 目录前言什么是透视表?使用步骤1. 引入必要的库2. 读取数据3. 创建透视表4. 查看透视表总结前言

Python装饰器之类装饰器详解

《Python装饰器之类装饰器详解》本文将详细介绍Python中类装饰器的概念、使用方法以及应用场景,并通过一个综合详细的例子展示如何使用类装饰器,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. 引言2. 装饰器的基本概念2.1. 函数装饰器复习2.2 类装饰器的定义和使用3. 类装饰

PyQt6/PySide6中QTreeView类的实现

《PyQt6/PySide6中QTreeView类的实现》QTreeView是PyQt6或PySide6库中用于显示分层数据的控件,本文主要介绍了PyQt6/PySide6中QTreeView类的实现... 目录1. 基本概念2. 创建 QTreeView 实例3. QTreeView 的常用属性和方法属性

Python 交互式可视化的利器Bokeh的使用

《Python交互式可视化的利器Bokeh的使用》Bokeh是一个专注于Web端交互式数据可视化的Python库,本文主要介绍了Python交互式可视化的利器Bokeh的使用,具有一定的参考价值,感... 目录1. Bokeh 简介1.1 为什么选择 Bokeh1.2 安装与环境配置2. Bokeh 基础2

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

Golang HashMap实现原理解析

《GolangHashMap实现原理解析》HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持高效的插入、查找和删除操作,:本文主要介绍GolangH... 目录HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen