From 19f83acd3249c70f216e0bb5ec3c044ade5083b3 Mon Sep 17 00:00:00 2001 From: qhan Date: Tue, 16 Jul 2024 16:05:50 +0800 Subject: [PATCH] feat(site/blog): add article of geometry calculation --- site/blog/md/posts/geometry-calculation.md | 357 +++++++++++++++--- .../images/posts/geometry-calculation.png | Bin 0 -> 29618 bytes 2 files changed, 300 insertions(+), 57 deletions(-) create mode 100644 site/blog/public/images/posts/geometry-calculation.png diff --git a/site/blog/md/posts/geometry-calculation.md b/site/blog/md/posts/geometry-calculation.md index 45962b4..a6495e3 100644 --- a/site/blog/md/posts/geometry-calculation.md +++ b/site/blog/md/posts/geometry-calculation.md @@ -1,87 +1,330 @@ --- title: 多边形几何图形计算 -date: 2024-05-25T20:08:56+08:00 +date: 2024-06-28T20:08:08+08:00 category: canvas tags: [canvas, js] -draft: true +description: 本文讲述多边形计算库clipper-lib的js版本用法,js版本的clipper-lib库和同类库比较,功能支持上更完善,且处理效率高,是前端处理多边形计算的首选方案,支持的计算包括多边形的并集、交集、差集、异或、面积、周长等。 --- +### TOC + ### 思考 -1. 图形透明区域算法计算出图形边界 -2. 创建多个Container来存放多次绘制的图形数据最后通过计算Container的边界来计算绘制图形的边界 +1. 图形透明区域算法计算出图形边界。 +2. 创建多个Container来存放多次绘制的图形数据最后通过计算Container的边界来计算绘制图形的边界。 3. 检测绘制图形的边界是否临近某一元素,临近则加入临近元素的Container,否则新建一个Container并加入绘制元素。 4. 图形顶点计算: - + 定义两个数组A、B,循环XY坐标,每次延X轴方向遇到的第一个点使用unshift推入数组A,后续其它点使用push推入数组B,依次循环到最后一个点,然后合并数组AB即可得到所有顶点 - + 定义两个数组A,循环XY坐标,每次延X轴方向遇到的第一个点使用unshift推入数组A,后续其它点使用push推入数组A,依次循环到最后一个点,然后合并数组AB即可得到所有顶点,最后一个点即为开始点和结束点 + + 定义两个数组A、B,循环XY坐标,每次延X轴方向遇到的第一个点使用`unshift`推入数组A,后续其它点使用push推入数组B,依次循环到最后一个点,然后合并数组AB即可得到所有顶点 + + 定义两个数组A,循环XY坐标,每次延X轴方向遇到的第一个点使用`unshift`推入数组A,后续其它点使用push推入数组A,依次循环到最后一个点,然后合并数组AB即可得到所有顶点,最后一个点即为开始点和结束点 + 注意:顶点计算规则为,当获取到一个点后,判断当前点的周围8个方向的点是否存在当前图形范围内,部分点存在则表示该点为边上的点,然后再移除这部分点中的有且只有一个同一方向存在两个的临近点的点,即为图形全部顶点坐标。 -再多的思考还是不如一个好用的工具,在死去活来的折腾了好几天后,终于在万能的AI引导下,找到了[clipper-lib](https://github.com/junmer/clipper-lib)这个非常好用的图形计算工具库。算法是事,还是交给专业大佬们研究吧,还是做好工具的使用者就OK啦。 +PS: 再多的思考还是不如一个好用的工具,在死去活来的折腾了好几天后,终于在万能的AI引导下,找到了[clipper-lib](https://github.com/junmer/clipper-lib)这个非常好用的图形计算工具库。算法是事,还是交给专业大佬们研究吧,还是做好工具的使用者就OK啦。 + +> [!TIP] +> 更多示例请查询:https://jsclipper.sourceforge.net/6.4.2.2/index.html +### 基本配置 +首先,准备一个HTML文件`clipper.html`,然后复制下面内容到文件中。在文件中引入了`clipper-lib`这个多边形工具库。 ```html Javascript Clipper Library / Boolean operations / SVG example - - + + - -
+ +

Javascript Clipper Library / Boolean operations / SVG example

+

This page shows an example of boolean operations on polygons and drawing them using SVG.

+ +
+
+
并集(Union)
+
+
+
差集(Difference)
+
+
+
异或(Xor)
+
+
+
交集(Intersection)
+
+
``` +然后创建JS文件 `clipper.js`,并定义一组要进行计算的路径顶点坐标。 + +```js +// 路径顶点坐标 +const subjPaths = [ + [{ X: 10, Y: 10 }, { X: 110, Y: 10 }, { X: 110, Y: 110 }, { X: 10, Y: 110 }], + [{ X: 20, Y: 20 }, { X: 20, Y: 100 }, { X: 100, Y: 100 }, { X: 100, Y: 20 }], +]; +const clipPaths = [ + [{ X: 50, Y: 50 }, { X: 150, Y: 50 }, { X: 150, Y: 150 }, { X: 50, Y: 150 }], + [{ X: 60, Y: 60 }, { X: 60, Y: 140 }, { X: 140, Y: 140 }, { X: 140, Y: 60 }], +]; +``` +配置缩放系数,然后将路径的每个坐标乘以缩放系数,并使用 `Math.round()` 四舍五入为最接近的整数。 +> [!TIP] +> 取整在 `ClipperLib.JS.ScaleUpPaths`函数内自动完成,当然若无缩放的需求出可省略这一步骤,在后面步骤中同时将缩放系数相关配置删除。 + +```js +... +// 缩放比例 +const scale = 100; + +ClipperLib.JS.ScaleUpPaths(subjPaths, scale); +ClipperLib.JS.ScaleUpPaths(clipPaths, scale); +``` + +接下来实例化一个`Clipper`的对象,并将要计算的路径顶点坐标分别按目标对象与裁剪对象的方式加入到实例的`Path`中,同时定义多边形[填充规则](/posts/clipper-lib-guide#clipperlibpolyfilltype)为**非零**方式。 +```js +... + +// 实例化Clipper对象 +const cpr = new ClipperLib.Clipper(); +cpr.AddPaths(subjPaths, ClipperLib.PolyType.ptSubject, true); +cpr.AddPaths(clipPaths, ClipperLib.PolyType.ptClip, true); + +// 配置填充规则 +const subjFillType = ClipperLib.PolyFillType.pftNonZero; +const clipFillType = ClipperLib.PolyFillType.pftNonZero; +``` + +到此步,我们已经完成多边形计算的基本配置,接下来将对多边形进行实际的运行操作,即:交集、并集、差集、异或。 + +### 裁剪 + +首先,定义一个方法`draw()`,该方法带有一个参数`clipType`表示为如果进行路径计算,即:交集、并集、差集、异或,同时在浏览器控制台输出结果。 + +```js +function draw(clipType) { + const solutionPaths = new ClipperLib.Paths(); + + cpr.Execute(clipType, solutionPaths, subjFillType, clipFillType); + console.log(JSON.stringify(solutionPaths)); +} +``` + +然后在`window.onload`方法中分别计算交集、并集、差集、异或四种操作的结果。 + +```js +window.onload = () => { + // 并集 + draw(ClipperLib.ClipType.ctUnion); + // 差集 + draw(ClipperLib.ClipType.ctDifference); + // 异或 + draw(ClipperLib.ClipType.ctXor); + // 交集 + draw(ClipperLib.ClipType.ctIntersection); +}; +``` +此时,在浏览器控制台就能看到路径计算的结果啦~ + +什么,全是字符,表现不够直观!!!这里,我们可以使用`SVG`的方式渲染出计算结果,这样就可以更为直观的表现出计算的结果,看出它们间的差异。接下来看看如何实现的吧! + + +### SVG +首先,定义一个方法`paths2string`,该方法带有两个参数分别为`paths`(需要转换的路径顶点坐标)和`scale`(缩放系数,默认值为1)。 + +```js +// Converts Paths to SVG path string and scales down the coordinates +function paths2string(paths, scale = 1) { + let svgPath = ''; + + for (let i = 0; i < paths.length; i += 1) { + for (let j = 0; j < paths[i].length; j += 1) { + if (!j) svgPath += 'M'; + else svgPath += 'L'; + svgPath += paths[i][j].X / scale + ', ' + paths[i][j].Y / scale; + } + svgPath += 'Z'; + } + if (svgPath == '') svgPath = 'M0,0'; + return svgPath; +} +``` + +然后,在`draw()`方法中,添加`SVG`的配置代码,并输出到`HTML`中。 +```diff +- function draw(clipType) { ++ function draw(clipType, elem) { + const solutionPaths = new ClipperLib.Paths(); + + cpr.Execute(clipType, solutionPaths, subjFillType, clipFillType); + console.log(JSON.stringify(solutionPaths)); + ++ const svg = ` ++ ++ `; + ++ elem.innerHTML += svg; +} +``` + +由于,在刚开始时,我们已经在HTML文件中配置好相对应的展示区域,因此我们再调整一下`window.onload`方法里面的代码即可,如下 + +```diff +window.onload = () => { + // 并集 +- draw(ClipperLib.ClipType.ctUnion); ++ draw(ClipperLib.ClipType.ctUnion, document.getElementById('union')); + // 差集 +- draw(ClipperLib.ClipType.ctDifference); ++ draw(ClipperLib.ClipType.ctDifference, document.getElementById('difference')); + // 异或 +- draw(ClipperLib.ClipType.ctXor); ++ draw(ClipperLib.ClipType.ctXor, document.getElementById('xor')); + // 交集 +- draw(ClipperLib.ClipType.ctIntersection); ++ draw(ClipperLib.ClipType.ctIntersection, document.getElementById('intersection')); +}; +``` +此时,在页面上就能看到最终计算的多边形效果唞~~~完美! + +![geometry-calculation](/images/posts/geometry-calculation.png) + + +### 扩展 + +#### 面积计算 + +要获取多边形的面积 Clipper库提供了`AreaOfPolygon()`和`AreaOfPolygons()`方法,分别计算单个和多个多边形面积。 +```js +// 计算单个多边形面积 +const area = ClipperLib.JS.AreaOfPolygon(polygon); + +// 计算多个多边形面积 +const area = ClipperLib.JS.AreaOfPolygons(polygons); +``` + +#### 计算多边形的周长 + +为了获取多边形的周长,Clipper库提供`PerimeterOfPath()`和`PerimeterOfPaths()`两个方法,分别计算单个和多个多边形周长。 + +```js +// 单个多边形周长 +const polygonal_perimeter = ClipperLib.JS.PerimeterOfPath(path, true, 1); +// 多个多边形周长 +const polygonal_perimeter = ClipperLib.JS.PerimeterOfPaths(paths, true, 1); +``` + +上面的两个示例计算了多边形的周长,这意味着该周长是从第一个点到第一个点进行测量的,而不管最后一个点是否与第一个点相同。 + +如果要测量线的周长,只需将上面两个方法的第二个参数改为`false`即可。 +```js +// 单个线的周长 +const line_perimeter = ClipperLib.JS.PerimeterOfPath(path, false, 1); +// 多个线的周长 +const line_perimeter = ClipperLib.JS.PerimeterOfPaths(paths, false, 1); +``` + + +### 完整代码 + +clipper.html + +```html + + + Javascript Clipper Library / Boolean operations / SVG example + + + + +

Javascript Clipper Library / Boolean operations / SVG example

+

This page shows an example of boolean operations on polygons and drawing them using SVG.

+ +
+
+
并集(Union)
+
+
+
差集(Difference)
+
+
+
异或(Xor)
+
+
+
交集(Intersection)
+
+
+ + +``` +clipper.js +```js +const subjPaths = [ + [{ X: 10, Y: 10 }, { X: 110, Y: 10 }, { X: 110, Y: 110 }, { X: 10, Y: 110 }], + [{ X: 20, Y: 20 }, { X: 20, Y: 100 }, { X: 100, Y: 100 }, { X: 100, Y: 20 }], +]; +const clipPaths = [ + [{ X: 50, Y: 50 }, { X: 150, Y: 50 }, { X: 150, Y: 150 }, { X: 50, Y: 150 }], + [{ X: 60, Y: 60 }, { X: 60, Y: 140 }, { X: 140, Y: 140 }, { X: 140, Y: 60 }], +]; + +const scale = 100; + +ClipperLib.JS.ScaleUpPaths(subjPaths, scale); +ClipperLib.JS.ScaleUpPaths(clipPaths, scale); + +const cpr = new ClipperLib.Clipper(); +cpr.AddPaths(subjPaths, ClipperLib.PolyType.ptSubject, true); +cpr.AddPaths(clipPaths, ClipperLib.PolyType.ptClip, true); + +const subjFillType = ClipperLib.PolyFillType.pftNonZero; +const clipFillType = ClipperLib.PolyFillType.pftNonZero; + +function draw(clipType, elem) { + const solutionPaths = new ClipperLib.Paths(); + + cpr.Execute(clipType, solutionPaths, subjFillType, clipFillType); + console.log(JSON.stringify(solutionPaths)); + + const svg = ` + + `; + + elem.innerHTML += svg; +} + +// Converts Paths to SVG path string +// and scales down the coordinates +function paths2string(paths, scale = 1) { + let svgPath = ''; + + for (let i = 0; i < paths.length; i += 1) { + for (let j = 0; j < paths[i].length; j += 1) { + if (!j) svgPath += 'M'; + else svgPath += 'L'; + svgPath += paths[i][j].X / scale + ', ' + paths[i][j].Y / scale; + } + svgPath += 'Z'; + } + if (svgPath == '') svgPath = 'M0,0'; + return svgPath; +} + +window.onload = () => { + draw(ClipperLib.ClipType.ctUnion, document.getElementById('union')); + + draw(ClipperLib.ClipType.ctDifference, document.getElementById('difference')); + + draw(ClipperLib.ClipType.ctXor, document.getElementById('xor')); + + draw(ClipperLib.ClipType.ctIntersection, document.getElementById('intersection')); +}; +``` ### 参考 - [ClipperLib使用手册](/posts/clipper-lib-guide) - [Clipper 6 文档](https://github.com/junmer/clipper-lib/blob/master/Documentation.md) +- [Javascript Clipper Wiki](https://sourceforge.net/p/jsclipper/wiki/Home%206/) - [JavaScript-Clipper.js](https://www.cnblogs.com/zhigu/p/11928492.html) - [多边形计算库jsclipper的用法](https://www.sofineday.com/jsclipper.html) ``` diff --git a/site/blog/public/images/posts/geometry-calculation.png b/site/blog/public/images/posts/geometry-calculation.png new file mode 100644 index 0000000000000000000000000000000000000000..dd31f7cffaa26e8efafe4bf0e5cfa61bef834ae6 GIT binary patch literal 29618 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 Bh9dv~ literal 0 HcmV?d00001