做设计这一行最怕的不是没有灵感而是灵感出现之后被“画重复图形、调尺寸、换配色”这种琐碎劳动消磨干净。有一段时间我频繁需要几何纹样素材——包装盒上的重复花边、网页首屏的装饰底纹、文创产品上的连续图案。手工画过的人应该都懂一次旋转复制还能勉强忍受八重、十六重对称再加上镜像变化手动画一遍几乎是在虐待自己。于是我把这部分重复劳动抽出来用 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_deg0): 生成一个正多边形的顶点坐标列表 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, count8): 将母题旋转复制 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是母题的顶点坐标列表。代码逻辑非常简单对每个旋转角度把母题的每个顶点都旋转得到一个新图案。如果count8最终就会得到八组坐标它们共同构成一个完整的圆形花环。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 fM {points[0][0]:.2f} {points[0][1]:.2f} for x, y in points[1:]: d fL {x:.2f} {y:.2f} d Z return d def export_svg(filename, groups, width800, height800, strokeblack): 导出多组坐标到 SVG 文件 paths [] for pts in groups: paths.append(fpath d{points_to_svg_path(pts)} ffillnone stroke{stroke} stroke-width2/) svg_text fsvg xmlnshttp://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, encodingutf-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×327 张图。程序只负责遍历人只负责事后挑选。这个过程一定有大量不满意的产出但它的价值在于把设计探索成本瞬间降下来了。以前想尝试“十二次旋转加小半径”这个组合要手工画一张现在所有组合都被一次生成了筛选成本远比手工制作低。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, requiredTrue) parser.add_argument(--outdir, requiredTrue) parser.add_argument(--count, typeint, default10) args parser.parse_args() cfg load_config(args.config) for i in range(args.count): render_one(seed1000 i, cfgcfg)每个文件生成时用不同的 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 这个项目名称的本意——用代码去扩展手工而不是取代手工。根据我的个人经验做这类工具最大的收获并不是最终能跑出多复杂的图案而是它养成了我对“设计过程”的重新思考哪些环节应该依赖直觉哪些环节应该交给算法边界划在哪里最舒服。如果你也被重复图形困扰不妨从最基础的旋转复制开始把第一个能跑的生成器搭出来。只要迈过这一步你会发现代码作为设计工具远没有想象中那么难用。
阅读完成 · 觉得有帮助?