全栈直通车-王世彪的博客
关注公众号
  • 开发桌面程序
  • javascript教程
  • css样式
  • vuejs
  • 部署免费CDN
  • SEO搜索引擎优化
  • vuepress教程
  • redis分布式缓存
  • Golang
  • PHP
  • Python
  • Java
  • NodeJs
  • tomcat
  • ELK
  • Mysql数据库
  • Nosql数据库
  • C/C++底层开发
  • 音视频/流媒体
  • linux服务器
  • nginx服务器
  • 容器技术
  • 负载均衡
  • 项目管理
  • 持续集成
  • 大数据
  • 微信公众号运营
  • markdown用法
赞一个
友链
联系作者

王世彪

努力做个影响他人滴人
关注公众号
  • 开发桌面程序
  • javascript教程
  • css样式
  • vuejs
  • 部署免费CDN
  • SEO搜索引擎优化
  • vuepress教程
  • redis分布式缓存
  • Golang
  • PHP
  • Python
  • Java
  • NodeJs
  • tomcat
  • ELK
  • Mysql数据库
  • Nosql数据库
  • C/C++底层开发
  • 音视频/流媒体
  • linux服务器
  • nginx服务器
  • 容器技术
  • 负载均衡
  • 项目管理
  • 持续集成
  • 大数据
  • 微信公众号运营
  • markdown用法
赞一个
友链
联系作者
  • css

  • 游戏开发

  • android

  • golang

  • javascript

    • javascript开发规范
    • html编码规范
    • typescript转换为javascript
    • 创建基于h5的hls播放器hls.js
    • 动态创建script标签
    • javascript跳转到新页面
    • javascript动态插入html片段
    • javascript文件上传库filepond
    • 富文本编辑器TinyMCE的基础用法
    • javascript markdown编辑器vditor
    • http请求库axios的用法
    • js模板引擎mustache用法
    • javascript多边形计算库jsclipper的用法
      • 1. jsclipper涉及的基本概念
      • 2. 基础示例
      • 3. 求面积示例
      • 4. 该库可用于cocos creator游戏开发
  • vuejs

  • 小程序

  • cdn

  • seo

  • vuepress

  • 移动端真机调试方法
  • 前端
  • javascript
王世彪
2021-01-27

javascript多边形计算库jsclipper的用法

# javascript多边形计算库jsclipper的用法

本文讲述javascript多边形计算库jsclipper的用法。jsclipper和同类库比较,功能支持上更完善,且处理效率高,是前端处理多边形计算的首选方案,支持的计算包括多边形的并集、交集、差集、异或、面积、周长等。

# 1. jsclipper涉及的基本概念

jsclipper库涉及的基本概念或术语详见文档Clipper库中文文档详解 (opens new window)。

# 1.1 多边形路径描述规则

多边形的路径定义需要严格遵守如下规则,才能准确描述一个多边形:

  • 顺时针
    按顺时针方向列出路径的顶点。
  • 逆时针
    按逆时针方向列出路径的顶点。

# 1.2 多边形填充规则

# 1.2.1 填充规则的定义

因为1个多边形可以由多组路径共同描述(如var subj_paths = [[{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}]];),所以多边形可以非常复杂。那么多边形填充规则的意义就是为了描述哪些区域是多边形区域的内部,哪些区域是多边形区域的外部。

# 1.2.2 填充规则的分类

jsclipper中支持4种填充规则:

  • Even-Odd
    奇数次计数的部分被填充,偶数次的部分不填充。
  • Non-Zero 所有的非零次计数部分都被填充。
  • Positive:所有计数大于零的部分被填充。
  • Negative:所有计数小于零的被填充。

# 1.2.3 填充规则计数的定义

  • Even-Odd规则中的计数 字面意思是“奇偶”。按该规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点的数量。如果结果是奇数则认为点在内部,是偶数则认为点在外部。

    可以看出,Even-Odd规则中的计数与轮廓的方向无关。

  • 其它填充规则中的计数 要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点情况。从0开始计数,路径从左向右(顺时针)穿过射线则计数加1,从右向左(逆时针)穿过射线则计数减1。

和Even-Odd填充方式不同,其他填充准则中计数会依赖于轮廓方向。
轮廓的方向是由点集的顺序决定的。

# 1.2.4 填充规则的效果图

多边形填充规则

注意,该参考图中有个错误,在做计数时,左右方向正好弄反了,导致计数错误。

# 2. 基础示例

如下示例演示了jsclipper库的基本用法,包括多边形的并集、交集、差集、异或这4种运算。

<html>
<head>
    <title>Javascript Clipper Library / Boolean operations / SVG example</title>
    <script src="http://jsclipper.sourceforge.net/6.4.2.2/clipper_unminified.js"></script>
    <style>
        h3{ margin-bottom:2px}
        body,th,td,input,legend,fieldset,p,b,button,select,textarea {
            font-size: 14px;
            font-family: Arial, Helvetica, sans-serif;
        }
    </style>
    <script>
        function draw() {
            var subj_paths = [[{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}]];
            var clip_paths = [[{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}]];
            var scale = 100;
            ClipperLib.JS.ScaleUpPaths(subj_paths, scale);
            ClipperLib.JS.ScaleUpPaths(clip_paths, scale);
            var cpr = new ClipperLib.Clipper();
            cpr.AddPaths(subj_paths, ClipperLib.PolyType.ptSubject, true);
            cpr.AddPaths(clip_paths, ClipperLib.PolyType.ptClip, true);
            var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
            var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
            var clipTypes = [ClipperLib.ClipType.ctUnion, ClipperLib.ClipType.ctDifference, ClipperLib.ClipType.ctXor, ClipperLib.ClipType.ctIntersection];
            var clipTypesTexts = "Union, Difference, Xor, Intersection";
            var solution_paths, svg, cont = document.getElementById('svgcontainer');
            var i;
            for(i = 0; i < clipTypes.length; i++) {
                solution_paths = new ClipperLib.Paths();
                console.log("运算前: ", JSON.stringify(solution_paths));
                cpr.Execute(clipTypes[i], solution_paths, subject_fillType, clip_fillType);
                console.log("运算后: ", JSON.stringify(solution_paths));
                svg = '<svg style="margin-top:10px; margin-right:10px;margin-bottom:10px;background-color:#dddddd" width="160" height="160">';
                svg += '<path stroke="black" fill="yellow" stroke-width="2" d="' + paths2string(solution_paths, scale) + '"/>';
                svg += '</svg>';
                cont.innerHTML += svg;
            }
            cont.innerHTML += "<br>" + clipTypesTexts;
        }

        // Converts Paths to SVG path string
        // and scales down the coordinates
        function paths2string (paths, scale) {
            var svgpath = "", i, j;
            if (!scale) scale = 1;
            for(i = 0; i < paths.length; i++) {
                for(j = 0; j < paths[i].length; j++){
                    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;
        }
    </script>
</head>
<body onload="draw()">
<div id="svgcontainer"></div>
</body>
</html>

运算结果如下图:
jsclipper多边形运算

如上代码中的cpr.Execute方法执行完运算操作后,将运算生成的多边形路径信息写入变量solution_paths。控制台打印信息如下:

运算前:  []
运算后:  [[{"X":11000,"Y":5000},{"X":15000,"Y":5000},{"X":15000,"Y":15000},{"X":5000,"Y":15000},{"X":5000,"Y":11000},{"X":1000,"Y":11000},{"X":1000,"Y":1000},{"X":11000,"Y":1000}],[{"X":11000,"Y":6000},{"X":11000,"Y":11000},{"X":6000,"Y":11000},{"X":6000,"Y":14000},{"X":14000,"Y":14000},{"X":14000,"Y":6000}],[{"X":2000,"Y":2000},{"X":2000,"Y":10000},{"X":5000,"Y":10000},{"X":5000,"Y":5000},{"X":10000,"Y":5000},{"X":10000,"Y":2000}],[{"X":6000,"Y":6000},{"X":6000,"Y":10000},{"X":10000,"Y":10000},{"X":10000,"Y":6000}]]
运算前:  []
运算后:  [[{"X":11000,"Y":5000},{"X":10000,"Y":5000},{"X":10000,"Y":2000},{"X":2000,"Y":2000},{"X":2000,"Y":10000},{"X":5000,"Y":10000},{"X":5000,"Y":11000},{"X":1000,"Y":11000},{"X":1000,"Y":1000},{"X":11000,"Y":1000}],[{"X":11000,"Y":11000},{"X":6000,"Y":11000},{"X":6000,"Y":10000},{"X":10000,"Y":10000},{"X":10000,"Y":6000},{"X":11000,"Y":6000}]]
运算前:  []
运算后:  [[{"X":15000,"Y":15000},{"X":5000,"Y":15000},{"X":5000,"Y":11000},{"X":6000,"Y":11000},{"X":6000,"Y":10000},{"X":10000,"Y":10000},{"X":10000,"Y":6000},{"X":11000,"Y":6000},{"X":11000,"Y":5000},{"X":15000,"Y":5000}],[{"X":11000,"Y":6000},{"X":11000,"Y":11000},{"X":6000,"Y":11000},{"X":6000,"Y":14000},{"X":14000,"Y":14000},{"X":14000,"Y":6000}],[{"X":11000,"Y":5000},{"X":10000,"Y":5000},{"X":10000,"Y":6000},{"X":6000,"Y":6000},{"X":6000,"Y":10000},{"X":5000,"Y":10000},{"X":5000,"Y":11000},{"X":1000,"Y":11000},{"X":1000,"Y":1000},{"X":11000,"Y":1000}],[{"X":2000,"Y":2000},{"X":2000,"Y":10000},{"X":5000,"Y":10000},{"X":5000,"Y":5000},{"X":10000,"Y":5000},{"X":10000,"Y":2000}]]
运算前:  []
运算后:  [[{"X":6000,"Y":11000},{"X":5000,"Y":11000},{"X":5000,"Y":10000},{"X":6000,"Y":10000}],[{"X":11000,"Y":6000},{"X":10000,"Y":6000},{"X":10000,"Y":5000},{"X":11000,"Y":5000}]]

若想了解该示例的详细教程,请前往JavaScript-Clipper.js (opens new window)

# 3. 求面积示例

如下示例演示了如何求1个多边形的面积,以及多个多边形并集的面积。
面积有正、负之分,若以顺时针方向描述顶点路径,则面积为正,若以逆时针方向描述顶点路径,则面积为负。

/**
测试获取多边形面积
*/
function testPolygonArea(){
    // path1的面积为400
    var path1 = [{X:0,Y:0}, {X:20, Y:0}, {X:20,Y:20}, {X:0, Y:20}];
    // path2的面积为10000
    var path2 = [{X:150,Y:150},{X:50,Y:150},{X:50,Y:50},{X:150,Y:50}];
    // 和path1描述的是同一个路径,只是定点的排列顺序不同
    var path1_same = [{X:0, Y:20}, {X:20,Y:20}, {X:20, Y:0}, {X:0,Y:0}];

    var area = ClipperLib.JS.AreaOfPolygons([path1]);
    console.log("path1的面积", area);

    // 因为path1_same以逆时针方向描述顶点, 所以面积为负数
    area = ClipperLib.JS.AreaOfPolygons([path1_same]);
    console.log("path1_same的面积", area);

    // path1的面积+path1的面积,即path1面积的2倍
    area = ClipperLib.JS.AreaOfPolygons([path1, path1]);
    console.log("path1的面积+path1的面积", area);

    // "path1和path1的并集"的面积
    area = ClipperLib.JS.AreaOfPolygons(getUnionPaths([path1], [path1]));
    console.log("path1和path1的并集的面积", area);

    // "path1和path1_same的并集"的面积,可知顶点排列顺序不同(当然无论怎么描述,必须要遵守基本准则: 要么顺时针,要么逆时针),并没有影响并集计算的正确性
    area = ClipperLib.JS.AreaOfPolygons(getUnionPaths([path1], [path1_same]));
    console.log("path1和path1_same的并集的面积", area);

    // "path1和path2的并集"的面积
    area = ClipperLib.JS.AreaOfPolygons(getUnionPaths([path1], [path2]));
    console.log("path1和path2的并集的面积", area);

    // "path1和path2的并集"的面积
    area = ClipperLib.JS.AreaOfPolygons(getUnionPaths(getUnionPaths([path1], [path2]), [path1_same]));
    console.log("path1和path2的并集的面积", area);
}

/**
获取2个多边形的并集的路径:
注意, path1和path2都是二维数组
*/
function getUnionPaths(path1, path2){
    var cpr = new ClipperLib.Clipper();
    // 添加裁剪路径
    cpr.AddPaths(path1, ClipperLib.PolyType.ptSubject, true);
    cpr.AddPaths(path2, ClipperLib.PolyType.ptClip, true);
    // 用于保存运算结果
    unionPaths = new ClipperLib.Paths();
    // 执行运算
    cpr.Execute(ClipperLib.ClipType.ctUnion, unionPaths, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd);
    console.log("获取2个多边形的并集的路径, path1:", JSON.stringify(path1));
    console.log("获取2个多边形的并集的路径, path2:", JSON.stringify(path2));
    console.log("获取2个多边形的并集的路径, 并集路径:", JSON.stringify(unionPaths));

    return unionPaths;
}

# 4. 该库可用于cocos creator游戏开发

游戏开发中,会经常用到多边形计算,若您是使用cocos creator游戏引擎,则可以使用该库进行多边形计算。

#前端#jsclipper
上次更新: 2021-01-29 15:08:09
js模板引擎mustache用法
vuejs自动更新版本号到后台页面模板

← js模板引擎mustache用法 vuejs自动更新版本号到后台页面模板→

最近更新
01
mysql创建用户
04-09
02
golang错误处理最佳实践
03-17
03
基于proto文件生成rpc接口定义文档
03-11
更多文章>
Theme by Vdoing | Copyright © 2019-2022 王世彪 | MIT License
冀ICP备19016776号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式