☰
用Python自动生成几何纹样:从旋转对称到SVG输出
2026/10/19 6:02:27 网站建设 项目流程

做设计这一行,最怕的不是没有灵感,而是灵感出现之后,被“画重复图形、调尺寸、换配色”这种琐碎劳动消磨干净。有一段时间我频繁需要几何纹样素材——包装盒上的重复花边、网页首屏的装饰底纹、文创产品上的连续图案。手工画过的人应该都懂:一次旋转复制还能勉强忍受,八重、十六重对称再加上镜像变化,手动画一遍几乎是在虐待自己。

于是我把这部分重复劳动抽出来,用 Python 写了一个生成图案的小工具集,取名 artcraft。它的核心用途很聚焦:用代码定义图案母题,再用旋转、镜像、平移这些变换方式组织成完整的几何装饰纹样,最终输出矢量格式。项目并不打算替代人的审美判断,而是把“描绘重复结构”这件高重复、低创造的事情自动化,把精力留到真正需要人来做决定的部分。

这篇文章会从背景、结构、实操到避坑,完整梳理这个项目。如果你会用一点 Python,又被重复图形折磨过,或者单纯好奇“代码怎么做印刷级纹样”,那这篇应该能给你一些能直接落地的参考。

1. artcraft 到底解决了什么问题

1.1 手工画图的三大痛点

先说我在正式写工具之前,手工绘制几何纹样时遇到的三个具体问题。

第一个是细节繁琐。一个典型的八重对称纹样,如果把基础花瓣造型复制旋转八份,每份还要保证描边、圆角、节点完全一致,手工操作里光是“对齐”就能耗掉半小时。哪怕用上矢量软件里的旋转复制功能,后期调整其中一片花瓣的参数,也常常要重新处理整体结构。

第二个是改参数非常痛苦。客户或者我自己,经常会提出“把这个图案拉宽一点”“把间距再缩小试试”这类需求。在手工图形里,改一个间距往往意味着改所有图层;而在代码工具里,这只是一个数值变量的调动。体验过托管后,就很难再回去手工改。

第三个是系列化困难。设计工作经常要交付的不是一张图,而是一套图:同一母题的不同配色、不同密度、不同疏密版本。手工做一套同类图案的时间成本近乎翻倍,而且很容易在细节上出现不一致。这种“一套图”的需求,本质上是参数空间里的批量采样,天生适合用程序来做。

1.2 定位:解放双手,但不背刺审美

aricraft 从一开始就没想把“设计动作”全部自动化。市面上有很多一键生成图形的工具,出图确实快,但风格高度雷同,很难配合具体项目的调性。我希望的模型是:人定义美学的规则和边界,程序负责执行大规模重复计算。

打个比方,这就像手工印花和机器印花的关系。机器印花的好处不是它比老师傅更有创意,而是它能把老师傅设计的图案一丝不差地复制一万次。代码在这里扮演的是那台印刷机,而“设计图案”这件事仍然留给人来做。

所以在项目里,我刻意保留了大量的手工控制面:母题形状怎么定义、旋转次数取多少、颜色怎么排列、随机程度控制在什么范围。程序的每一个输出,背后都有一套人为设计的规则在约束,这样出来的图案才可能有“设计感”,而不是纯粹的随机噪点。

2. 整体设计与技术选型

2.1 三层模块划分

aricraft 被我拆成了三个独立模块,各自负责一件事情:

  • 图案生成层:负责定义基础图形元素,比如多边形、圆环、花瓣轮廓、波浪线。
  • 变换组合层:负责把基础元素进行旋转、镜像、平移、缩放和群组,形成完整构图。
  • 输出封装层:负责把构图数据转换成 SVG 文本、PNG 位图预览,以及记录参数日志。

这三层之间通过简单的数据结构通信。图案生成层只管返回点坐标列表;变换组合层接收坐标列表,做一些矩阵运算后返回新的坐标列表;输出封装层只负责把坐标列表序列化成文件。每一层都不关心其他层的内部实现。

这么拆的原因很实际:图形母题和图形组织方式往往是两件事。一个“八重对称花环”的母题可能是一个花瓣路径,而“八重对称”是一种把母题复制旋转八次的组织规则。组织规则可以用在完全不同的母题上,不必把每个母题都写一遍旋转逻辑。后面我想新增“菱形格铺法”或者“随机散点排布”,只需要在变换层新增一个组合器,原有母题全部可以直接复用。

2.2 为什么先用 SVG 再转位图

工具链中间选 SVG 是我做过最值的一个决定。早期我偷懒直接输出 PNG,结果调色效率低得离谱:每换一次配色,就要重新跑整个渲染流程,改完还不一定满意,又得重来。改成 SVG 之后,等于先产出一份“可编辑的设计源文件”,颜色、描边、透明度都可以后续统一处理。

SVG 还有一个关键优势是无限缩放。一个被设计成 48 像素小图标尺寸的图案,可以直接放大到两米宽的喷绘海报上,完全不会出现锯齿。这对需要多种物料尺寸的客户项目特别重要,同一套图既能做名片,也能做门头背景。

不过 SGV 文件本身不适合直接作为最终交付物,所以我保留了一条由 SVG 转位图的流水线:在导出脚本里统一指定画布尺寸、背景色和缩放倍数,使用常用的图像处理库把矢量内容渲染成 PNG 或者 JPEG。这样既保留了矢量源文件的编辑弹性,又有位图文件便于快速预览和发送。

2.3 配置驱动与随机种子

artcraft 的每张图,本质上由“一套可复现的参数”决定。所有参数都写在 YAML 格式的配置文件里:母题类型、半径范围、旋转次数、描边宽度、颜色列表、画布大小等等。程序读取配置,执行生成,最后输出文件。

这里有一个理解门槛,就是对随机种子的把握。生成艺术看起来是随机的,但实际上应该是“约束下的随机”。随机数生成器需要一个种子值,只要种子固定,无论运行多少次,生成的随机数序列都是一样的。换句话说,随机种子是可控的。

这个特性的实际价值非常大。客户看了第一版图案说“把上次那个配色换成深色背景”,如果当时的种子和参数文件还在,我可以在完全还原第一版的基础上修改颜色配置,而不是重新生成一张“可能有点像”的图案。一批系列作品也因此变成了可追溯、可复现的设计资产。

3. 手把手搭一个旋转对称图案生成器

这一节是 artcraft 最核心的现场演示:用约 80 行代码,生成一个八重旋转对称花环,并输出为 SVG 文件。逻辑并不复杂,数学上只需要一个绕点旋转的公式。

3.1 绕中心旋转的数学基础

所有旋转对称图案的基础,就是把一个坐标点绕着某个中心点旋转一定的角度。公式是这样的:

点 P(x, y) 绕中心点 C(cx, cy) 旋转 θ 度之后的新坐标是:

dx = x - cx dy = y - cy x_new = cx + dx * cos(θ) - dy * sin(θ) y_new = cy + dx * sin(θ) + dy * cos(θ)

这段逻辑用 Python 写非常直观:

import math def rotate_point(x, y, cx, cy, angle_deg): """绕中心点(cx, cy)旋转一个点,角度单位为度""" rad = math.radians(angle_deg) cos_a = math.cos(rad) sin_a = math.sin(rad) dx = x - cx dy = y - cy x_new = cx + dx * cos_a - dy * sin_a y_new = cy + dx * sin_a + dy * cos_a return x_new, y_new

如果你要把整个图案旋转,只需要把这个函数分别应用到图案的所有顶点坐标上。

3.2 定义一个简单母题

母题就是一幅图案里反复出现的基础单元。这里我定义一个最简单的母题:一个带圆角的“花瓣”,用两个圆弧拼接而成。实际上,我们可以用更简单的正多边形作为起点,先跑通整个流程。

def polygon_points(cx, cy, radius, sides, rotation_deg=0): """生成一个正多边形的顶点坐标列表""" points = [] for i in range(sides): angle = math.radians(360 * i / sides + rotation_deg) x = cx + radius * math.cos(angle) y = cy + radius * math.sin(angle) points.append((x, y)) return points

例如polygon_points(0, 0, 100, 6, 0)会返回一个正六边形的六个顶点。将这个正六边形作为母题,放在画布中心周围,再通过变换复制出多份,就能形成有明显秩序感的几何图案。

3.3 组合成八重对称花环

八重旋转对称的意思,是把同一个母题绕中心旋转 0 度、45 度、90 度、135 度、180 度、225 度、270 度、315 度,一共八个角度,每个角度复制一份。角度的间隔是360 / 8。

组合逻辑写成这样:

def make_rosette(motif_points, cx, cy, count=8): """将母题旋转复制 count 份,形成旋转对称花环""" groups = [] step = 360 / count for i in range(count): rotated = [ rotate_point(x, y, cx, cy, step * i) for x, y in motif_points ] groups.append(rotated) return groups

这里motif_points是母题的顶点坐标列表。代码逻辑非常简单:对每个旋转角度,把母题的每个顶点都旋转,得到一个新图案。如果count=8,最终就会得到八组坐标,它们共同构成一个完整的圆形花环。

3.4 输出 SVG 文件

坐标生成完之后,需要把它们写成 SVG 文件。这里只需要处理最基础的 path 标签:把一组坐标转换成M x1 y1 L x2 y2 ... Z这样的路径字符串。

def points_to_svg_path(points): """将坐标列表转换为 SVG path 的 d 属性字符串""" if not points: return "" d = f"M {points[0][0]:.2f} {points[0][1]:.2f} " for x, y in points[1:]: d += f"L {x:.2f} {y:.2f} " d += "Z" return d def export_svg(filename, groups, width=800, height=800, stroke="black"): """导出多组坐标到 SVG 文件""" paths = [] for pts in groups: paths.append(f'<path d="{points_to_svg_path(pts)}" ' f'fill="none" stroke="{stroke}" stroke-width="2"/>') svg_text = f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="-{width/2} -{height/2} {width} {height}">\n' svg_text += "\n".join(paths) svg_text += "\n</svg>" with open(filename, "w", encoding="utf-8") as f: f.write(svg_text)

注意这里用了viewBox="-400 -400 800 800",意思是把坐标系原点放在画布正中心,这样所有围绕原点旋转的图案都会自然居中。保存后打开 SVG 浏览器查看,你会看到八个正六边形均匀环绕中心排列,整个图案呈现出明确的放射状秩序。

到这里,一个最简单的 artcraft 工作流就闭环了:定义母题、变换复制、输出文件。接下来所有复杂功能,都是在这个流程上做增加。

4. 批量出图:从一张图到一套图

单张图案跑通之后,下一个需求几乎必然出现:怎么快速生成一套图案?

4.1 参数网格的思路

我采用的办法叫“参数网格”,核心是指定几个关心的参数,每个参数给定一组候选值,程序自动在笛卡尔积上遍历生成所有组合。比如:

  • 旋转次数:6、8、12
  • 母题半径:40、60、80
  • 配色方案:暖色、冷色、黑白

这三个参数组合起来就是 3×3×3=27 张图。程序只负责遍历,人只负责事后挑选。

这个过程一定有大量不满意的产出,但它的价值在于把设计探索成本瞬间降下来了。以前想尝试“十二次旋转加小半径”这个组合,要手工画一张;现在所有组合都被一次生成了,筛选成本远比手工制作低。

4.2 命令行批量渲染

为了更顺手地做批量生成,我给 artcraft 加了命令行的入口。大致形态是用 Python 内置的argparse读取参数,然后循环调用渲染函数。基本结构是这样:

python artcraft.py --config flower_carpet.yaml --outdir ./output --count 36

对应的代码逻辑:

import argparse import itertools import random def load_config(path): # 读取 yaml 配置,返回 dict ... def render_one(seed, cfg): random.seed(seed) # 根据 cfg 生成图案并导出文件 ... if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--config", required=True) parser.add_argument("--outdir", required=True) parser.add_argument("--count", type=int, default=10) args = parser.parse_args() cfg = load_config(args.config) for i in range(args.count): render_one(seed=1000 + i, cfg=cfg)

每个文件生成时,用不同的 seed 来控制随机变化,文件名里带上参数摘要。这样即使生成了几十张图,也可以从文件名快速找到“我喜欢的那一张”,再带着它的参数去做进一步精调。

4.3 系列感的控制

批量生成最怕翻车的一个点,就是系列感失控。三十六张图如果每张风格差异太大,根本不像同一套设计。

我用来维持系列感的手段主要有两个。

第一,固定不变的部分要足够稳定。母题类型、构图秩序、描边宽度这些核心结构参数通常保持不变,可变动的只是配色、密集程度之类的外围参数。结构是骨架,颜色是妆容,骨架一致,整套图的辨识度就有了基础。

第二,配色池的整体逻辑保持一致。我喜欢给每个系列指定一个颜色池,比如“同色系稍微变化”“互补色对比”“高饱和三色”,程序从有限的颜色池里取颜色,而不是使用任意颜色。这样即使随机组合很多,色彩氛围仍然能被控制在一个视觉区间内。

5. 常见问题与排查速查表

工具天天用,坑没少踩。这里我把 artcraft 使用中高频出现的几类问题整理成了一张速查表,每一类都是实际调试经验的浓缩。

5.1 六类高频问题实录

问题现象根本原因解决办法
图案不在画面中心,整体偏移母题坐标用了绝对坐标,没有平移到中心统一使用以原点为中心的计算坐标系,导出时再整体平移
旋转后图形发生扭曲把旋转变换和缩放变换的顺序搞反了明确先缩放再旋转,或者先旋转再缩放,保持全局一致
相邻图形之间存在细小缝隙浮点计算精度导致的闭合路径误差输出路径时统一保留两位小数,并对端点做微量重叠
同一张图每次运行颜色不一样忘记固定 random.seed在每次生成前显式调用一次随机数重新播种
颜色叠加后发暗,不够干净SVG 透明度叠加并非简单覆盖如无必要就不要叠加半透明底色,改用纯色分层
批量出图文件名太长无法辨认把所有参数都塞进文件名文件名为“系列名_序号”,参数单独记录到 JSON 日志

每个问题背后都有具体的调试经历。比如“图形不在画面中心”这个问题,几乎每个初接触 py 绘图的人都会碰到。原因通常是把母题直接定义在(100, 100)这样的绝对坐标上,然后绕(0, 0)旋转,最后整体输出时没有做平移。设计坐标系时把“计算坐标系”和“画布坐标系”分开,会省掉大量后期修复的麻烦。

5.2 几个亲测有效的调试习惯

除了一张问题速查表,还有几个习惯是长期使用下来觉得真正有效的。

第一个习惯是“小步快跑,及时可视化”。大改代码后不要直接批量出几十张图,先跑一个最小样例,快速查看单张输出是否正确。可视化在这个环节几乎是必须的,我一般只输出 SVG,直接在浏览器里对照预览。

第二个习惯是“参数日志比图片更值钱”。每次批量生成时,我都把完整参数列表和随机种子记录到一个 JSON 文件里。后面一旦要精确复现某张图,或者做参数归因分析,这份日志就是线索。

第三个习惯是“同一批次固定所有底层依赖版本”。SVG 渲染和几何运算虽然不复杂,但依赖库升级偶尔会带来微小行为差异。为保证可复现性,正式出图前我会锁定依赖版本,不在同一个项目中途随意升级。

6. 应用复盘:某品牌特产礼盒的几何花砖

工具做出来之后,总要用真实项目來检验。这里分享一个我印象最深的案例:为一个虚构的“地方特产礼盒”设计包装纹样。

6.1 需求拆解

客户给到的信息很抽象:希望礼盒有一种“在地文化感”,但不要直接放风景照片,最好用抽象几何图案,同时要求能延伸到不同尺寸的物料上,从手提袋到卡片都要保持一致视觉。

这类需求是典型的“重复基础单元+系统控制”项目。我的拆解思路是:先确定构图秩序,再确定母题,最后做参数网格批量衍生。构图秩序选的是“四方连续+中心放射”的结合方式,让包装六个面既有独立中心,又能在拼接时形成连续视觉流。

6.2 参数设定与生成过程

母题方面,我定义了一个带有轻微不对称感的六瓣花形,故意避免完全对称带来的僵硬感。旋转次数设为 6,同时加入镜像变换,形成一个十二重复合构图。

色彩方面,从当地传统织物中提取了三组配色:瓦灰配朱红、米白配靛蓝、墨绿配浅金。每组配色在代码里就是一个颜色列表,渲染时按顺序循环取用。最终的参数网格为 2 种母题变体 × 3 组配色 × 3 种密度档位,共 18 张初稿。

生成过程只花了不到两分钟。真正花时间的是人工筛选:把 18 张初稿缩小排版成一张预览大图,逐张排除不合适的方向。这个过程我不建议省,程序负责扩展可能性,人的眼睛负责收敛方向。

6.3 从筛选到交付

最终选定的版本并不是初稿里最“惊艳”的那一张,而是支撑度最高的一张:它在小尺寸卡片上不失细节,放大到包装大面上也不会显得空。接着我根据它固定的参数重新跑了衍生变体,单独调整了一套更协调的配色比例,然后交给手工调图环节,补了一些微妙的圆角处理和质感。

整个流程走下来,artcraft 真正在做的事情是“提供足够的候选,而不是直接给答案”。程序没有设计力,但它把设计师从手工重复劳动中释放出来,让我有足够时间把选定方案磨合到位。

7. 后续可以怎么扩展

7.1 母题库的积累

artcraft 用到现在,最大的资产不是代码,而是它背后逐渐积累的母题库。

我每做一个项目,几乎都会抽出时间把新设计的母题抽象成参数化模板。比如做完那个特色礼盒项目,我就沉淀了一个“六瓣花系列”;后来做网页底纹,又沉淀了一个“波浪波纹系列”。这些模板积累了十几个之后,后续接新项目,前期构图探索的速度明显快了很多。构建一个母题库,就是给自己的创作攒“字库”,字库越厚,排版的自由度越大。

7.2 与手工质感结合

这个工具还有一个非常有意思的扩展方向:把数字生成的图案和手工质感结合。

实际测试中我发现,完全生成的矢量图案有时候会显得过于干净、机械,缺“手作感”。解决办法是先打印出来,用手工水墨渲染或丝网印刷做二次处理,再扫描回去。数字生成提供精确结构,手工手法提供随机偏差,两者结合之后的质感往往超出预期。这其实也呼应了 artcraft 这个项目名称的本意——用代码去扩展手工,而不是取代手工。

根据我的个人经验,做这类工具,最大的收获并不是最终能跑出多复杂的图案,而是它养成了我对“设计过程”的重新思考:哪些环节应该依赖直觉,哪些环节应该交给算法,边界划在哪里最舒服。如果你也被重复图形困扰,不妨从最基础的旋转复制开始,把第一个能跑的生成器搭出来。只要迈过这一步,你会发现代码作为设计工具,远没有想象中那么难用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询