字玩FontPlayer开发笔记1 路径去除重叠功能的添加
字玩FontPlayer是笔者开源的一款字体设计工具,使用Vue3 + ElementUI开发,源代码:
github: https://github.com/HiToysMaker/fontplayer
gitee: https://gitee.com/toysmaker/fontplayer
笔记
在中文字体设计中,笔者经常先设计笔画,然后再用几个笔画叠加在一起形成字形,最近发现这样直接导出,即使路径顺序都符合非零环绕规则,在部分软件中,还是会出现重叠部分镂空问题。参考了别的成熟软件,发现这个问题可以用合并路径的方式去除重叠。
paper.js提供了强大的对贝塞尔路径的布尔操作,笔者借助了paper.js帮我解决了这个问题。
具体去除重叠功能核心代码:
// 读取字符轮廓信息(已经将形状都转换成字体轮廓)
let contours: Array<Array<ILine | IQuadraticBezierCurve | ICubicBezierCurve>> = componentsToContours(orderedListWithItemsForCharacterFile(char), {
unitsPerEm,
descender,
advanceWidth: unitsPerEm,
}, { x: 0, y: 0 }, false, false, false)
// 将轮廓转换成Path
let paths = []
for (let i = 0; i < contours.length; i++) {
const contour = contours[i]
let path = new paper.Path()
path.moveTo(new paper.Point(contour[0].start.x, contour[0].start.y))
for (let j = 0; j < contour.length; j++) {
const _path = contour[j]
if (_path.type === PathType.LINE) {
path.lineTo(new paper.Point((_path as unknown as ILine).end.x, (_path as unknown as ILine).end.y))
} else if (_path.type === PathType.CUBIC_BEZIER) {
path.cubicCurveTo(
new paper.Point(
(_path as unknown as ICubicBezierCurve).control1.x,
(_path as unknown as ICubicBezierCurve).control1.y,
),
new paper.Point(
(_path as unknown as ICubicBezierCurve).control2.x,
(_path as unknown as ICubicBezierCurve).control2.y,
),
new paper.Point(
(_path as unknown as ICubicBezierCurve).end.x,
(_path as unknown as ICubicBezierCurve).end.y,
)
)
}
}
paths.push(path)
}
// 合并路径,去除重叠
let unitedPath = null
if (paths.length < 2) {
unitedPath = paths[1]
} else {
unitedPath = paths[0].unite(paths[1])
for (let i = 2; i < paths.length; i++) {
unitedPath = unitedPath.unite(paths[i])
}
}
// 根据合并路径生成轮廓数据
let overlap_removed_contours = []
for (let i = 0; i < unitedPath.children.length; i++) {
const paths = unitedPath.children[i]
if (!paths.curves.length) continue
const contour = []
for (let j = 0; j < paths.curves.length; j++) {
const curve = paths.curves[j]
const path = {
type: PathType.CUBIC_BEZIER,
start: { x: curve.points[0].x, y: curve.points[0].y },
control1: { x: curve.points[1].x, y: curve.points[1].y },
control2: { x: curve.points[2].x, y: curve.points[2].y },
end: { x: curve.points[3].x, y: curve.points[3].y },
}
contour.push(path)
}
overlap_removed_contours.push(contour)
}
char.overlap_removed_contours = overlap_removed_contours
其中有一点令笔者不太满意,就是由于字玩自己的字形数据结构,和paper.js使用的数据结构差异较大,需要进行前后两次转换,耽误处理时间,这里以后看看要不要做优化。