// http://www.emanueleferonato.com/2011/08/05/slicing-splitting-and-cutting-objects-with-box2d-part-4-using-real-graphics/

const EPSILON = 0.1;
const POINT_SQR_EPSILON = 5; function compare(a, b) {
if (a.fraction > b.fraction) {
return 1;
} else if (a.fraction < b.fraction) {
return -1; }
return 0;
} function equals (a, b, epsilon) {
epsilon = epsilon === undefined ? EPSILON : epsilon;
return Math.abs(a-b) < epsilon;
} function equalsVec2(a,b, epsilon) {
return equals(a.x, b.x, epsilon) && equals(a.y, b.y, epsilon);
} function pointInLine (point, a, b) {
return cc.Intersection.pointLineDistance(point, a, b, true) < 1;
} cc.Class({
extends: cc.Component, onEnable: function () {
this.debugDrawFlags = cc.director.getPhysicsManager().debugDrawFlags;
cc.director.getPhysicsManager().debugDrawFlags =
cc.PhysicsManager.DrawBits.e_jointBit |
cc.PhysicsManager.DrawBits.e_shapeBit
;
}, onDisable: function () {
cc.director.getPhysicsManager().debugDrawFlags = this.debugDrawFlags;
}, // use this for initialization
onLoad: function () {
var canvas = cc.find('Canvas');
canvas.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
canvas.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
canvas.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this); this.ctx = this.getComponent(cc.Graphics);
}, onTouchStart: function (event) {
this.touching = true;
this.r1 = this.r2 = this.results = null;
this.touchStartPoint = this.touchPoint = cc.v2( event.touch.getLocation() );
}, onTouchMove: function (event) {
this.touchPoint = cc.v2( event.touch.getLocation() );
}, onTouchEnd: function (event) {
this.touchPoint = cc.v2( event.touch.getLocation() );
this.recalcResults();
this.touching = false; let point = cc.v2( event.touch.getLocation() );
if ( equals(this.touchStartPoint.sub(point).magSqr(), 0) ) return; // recalculate fraction, make fraction from one direction
this.r2.forEach(r => {
r.fraction = 1 - r.fraction;
}); let results = this.results; let pairs = []; for (let i = 0; i < results.length; i++) {
let find = false;
let result = results[i]; for (let j = 0; j < pairs.length; j++) {
let pair = pairs[j];
if (pair[0] && result.collider === pair[0].collider) {
find = true; // one collider may contains several fixtures, so raycast may through the inner fixture side
// we need remove them from the result
let r = pair.find((r) => {
return r.point.sub(result.point).magSqr() <= POINT_SQR_EPSILON;
}); if (r) {
pair.splice(pair.indexOf(r), 1);
}
else {
pair.push(result);
} break;
}
} if (!find) {
pairs.push([result]);
}
} for (let i = 0; i < pairs.length; i++) {
let pair = pairs[i];
if (pair.length < 2) {
continue;
} // sort pair with fraction
pair = pair.sort(compare); let splitResults = []; // first calculate all results, not split collider right now
for (let j = 0; j < (pair.length - 1); j +=2) {
let r1 = pair[j];
let r2 = pair[j+1]; if (r1 && r2) {
this.split(r1.collider, r1.point, r2.point, splitResults);
}
} if (splitResults.length <= 0) {
continue;
} let collider = pair[0].collider; let maxPointsResult;
for (let j = 0; j < splitResults.length; j++) {
let splitResult = splitResults[j]; for (let k = 0; k < splitResult.length; k++) {
if (typeof splitResult[k] === 'number') {
splitResult[k] = collider.points[splitResult[k]];
}
} if (!maxPointsResult || splitResult.length > maxPointsResult.length) {
maxPointsResult = splitResult;
}
} if (maxPointsResult.length < 3) {
continue;
} // keep max length points to origin collider
collider.points = maxPointsResult;
collider.apply(); let body = collider.body; for (let j = 0; j < splitResults.length; j++) {
let splitResult = splitResults[j]; if (splitResult.length < 3) continue;
if (splitResult == maxPointsResult) continue; // create new body
let node = new cc.Node();
node.position = body.getWorldPosition();
node.rotation = body.getWorldRotation();
node.parent = cc.director.getScene(); node.addComponent(cc.RigidBody); let newCollider = node.addComponent(cc.PhysicsPolygonCollider);
newCollider.points = splitResult;
newCollider.apply();
} }
}, split: function (collider, p1, p2, splitResults) {
let body = collider.body;
let points = collider.points; // The manager.rayCast() method returns points in world coordinates, so use the body.getLocalPoint() to convert them to local coordinates.
p1 = body.getLocalPoint(p1);
p2 = body.getLocalPoint(p2); let newSplitResult1 = [p1, p2];
let newSplitResult2 = [p2, p1]; let index1, index2;
for (let i = 0; i < points.length; i++) {
let pp1 = points[i];
let pp2 = i === points.length - 1 ? points[0] : points[i+1]; if (index1 === undefined && pointInLine(p1, pp1, pp2)) {
index1 = i;
}
else if (index2 === undefined && pointInLine(p2, pp1, pp2)) {
index2 = i;
} if (index1 !== undefined && index2 !== undefined) {
break;
}
} // console.log(index1 + ' : ' + index2); if (index1 === undefined || index2 === undefined) {
debugger
return;
} let splitResult, indiceIndex1 = index1, indiceIndex2 = index2;
if (splitResults.length > 0) {
for (let i = 0; i < splitResults.length; i++) {
let indices = splitResults[i];
indiceIndex1 = indices.indexOf(index1);
indiceIndex2 = indices.indexOf(index2); if (indiceIndex1 !== -1 && indiceIndex2 !== -1) {
splitResult = splitResults.splice(i, 1)[0];
break;
}
}
} if (!splitResult) {
splitResult = points.map((p, i) => {
return i;
});
} for (let i = indiceIndex1 + 1; i !== (indiceIndex2+1); i++) {
if (i >= splitResult.length) {
i = 0;
} let p = splitResult[i];
p = typeof p === 'number' ? points[p] : p; if (p.sub(p1).magSqr() < POINT_SQR_EPSILON || p.sub(p2).magSqr() < POINT_SQR_EPSILON) {
continue;
} newSplitResult2.push(splitResult[i]);
} for (let i = indiceIndex2 + 1; i !== indiceIndex1+1; i++) {
if (i >= splitResult.length) {
i = 0;
} let p = splitResult[i];
p = typeof p === 'number' ? points[p] : p; if (p.sub(p1).magSqr() < POINT_SQR_EPSILON || p.sub(p2).magSqr() < POINT_SQR_EPSILON) {
continue;
} newSplitResult1.push(splitResult[i]);
} splitResults.push(newSplitResult1);
splitResults.push(newSplitResult2);
}, recalcResults: function () {
if (!this.touching) return; let startPoint = this.touchStartPoint;
let point = this.touchPoint; this.ctx.clear();
this.ctx.moveTo(this.touchStartPoint.x, this.touchStartPoint.y);
this.ctx.lineTo(point.x, point.y);
this.ctx.stroke(); let manager = cc.director.getPhysicsManager(); // manager.rayCast() method calls this function only when it sees that a given line gets into the body - it doesnt see when the line gets out of it.
// I must have 2 intersection points with a body so that it can be sliced, thats why I use manager.rayCast() again, but this time from B to A - that way the point, at which BA enters the body is the point at which AB leaves it!
let r1 = manager.rayCast(this.touchStartPoint, point, cc.RayCastType.All);
let r2 = manager.rayCast(point, this.touchStartPoint, cc.RayCastType.All); let results = r1.concat(r2); for (let i = 0; i < results.length; i++) {
let p = results[i].point;
this.ctx.circle(p.x, p.y, 5);
}
this.ctx.fill(); this.r1 = r1;
this.r2 = r2;
this.results = results;
}, // called every frame, uncomment this function to activate update callback
update: function (dt) {
// body maybe moving, need calc raycast results in update
this.recalcResults();
},
});

ccc切割刚体的更多相关文章

  1. JAVA之旅(三十四)——自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫

    JAVA之旅(三十四)--自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫 我们接着来说网络编程,TCP 一.自定义服务端 我们直接写一个服务端,让本机去连接 ...

  2. [sql]sql的select字符串切割

    可以经常看看 mysql的refman,写的很棒 sql基础操作 查看表结构 show create table desc table show full columns from test1; li ...

  3. 数组遍历 map()、forEach() 及 字符串切割 split() / 字符串截取 slice()、substring()、substr()

    JS数组遍历的几种方式 JS数组遍历,基本就是for,forin,foreach,forof,map等等一些方法,以下介绍几种本文分析用到的数组遍历方式以及进行性能分析对比 第一种:普通for循环 代 ...

  4. Ajax+Java实现大文件切割上传

    技术体系:html5(formdata) + java + servlet3.0+maven + tomcat7 <!DOCTYPE html> <html> <head ...

  5. css3实现3D切割轮播图案例

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  6. 3D切割轮播图

    预览图: 实现原理:将图片切割构建一个和ul(电脑屏幕)同一个轴的立方体,利用延时旋转实现切割效果 知识点:transform-style属性(必须搭配transform属性使用) 值 描述 flat ...

  7. String常用使用方法,1.创建string的常用3+1种方式,2.引用类型使用==比较地址值,3.String当中获取相关的常用方法,4.字符串的截取方法,5.String转换常用方法,6.切割字符串----java

    一个知识点使用一个代码块方便查看 1.创建string的常用3+1种方式 /* 创建string的常用3+1种方式 三种构造方法 public String():创建一个空字符串,不含有任何内容: p ...

  8. 案例:3D切割轮播图

    一.3d转换 3D旋转套路:顺着轴的正方向看,顺时针旋转是负角度,逆时针旋转是正角度 二.代码 <!DOCTYPE html> <html lang="en"&g ...

  9. PHP搭建大文件切割分块上传功能

    背景 在网站开发中,文件上传是很常见的一个功能.相信很多人都会遇到这种情况,想传一个文件上去,然后网页提示"该文件过大".因为一般情况下,我们都需要对上传的文件大小做限制,防止出现 ...

随机推荐

  1. Selenium·自动化框架集成

    date:2018513 day08aft 一.自动化框架集成分层 1.config 配置(项目配置——测试环境,公司环境,线上环境:以中国人才热线登陆为例,网址.用户名.密码等) 2.public ...

  2. tomcat升级 遇到的坑

    今天说说tomcat升级后出的问题 以前的版本是8.0.30的 因用安全漏洞 需要升级tomcat 为8.5.28的版本 升级后jvm的配置 等等都和一起一样,过了几天发现,我们的错误日志和处理影响转 ...

  3. Linux 堆溢出原理分析

    堆溢出与堆的内存布局有关,要搞明白堆溢出,首先要清楚的是malloc()分配的堆内存布局是什么样子,free()操作后又变成什么样子. 解决第一个问题:通过malloc()分配的堆内存,如何布局? 上 ...

  4. Arcmap连接数据库需管理员获取许可——创建ArcSDE连接文件

    一.在装有server的服务器上创建ArcSDE连接文件 1.打开ArcMap<<ArcToolBox<<数据管理工具<<工作空间<<创建ArcSDE连 ...

  5. GAN 教程记录

    目标:使G产生的分布sample出来接近D的分布 1.G产生的data是否是database中的图片 a.计算L1 L2相似度 2.GAN与其他生成器相比较,能够生成较为清晰的图片 3.一次itera ...

  6. 添加一个pv到vg后,误删新加入的pv,报 pv * not found or rejected by a filter

    问题如下 将某一pv加入vg vgextend cl /dev/xvdb1 然后进入fdisk将xvdb1分区删掉,重新创建pv 使用lvdisplay报警告 [root@localhost ~]# ...

  7. jQuery基础(二)DOM

    DOM节点的创建 jQuery节点创建与属性的处理 创建元素节点: $("<div></div>") 创建为文本节点: $("<div> ...

  8. 十九、springcloud(五)配置中心本地示例和refresh

    1.创建spring-cloud-houge-config子项目,测试需要的项目入下 2.添加依赖 <dependency> <groupId>org.springframew ...

  9. 使用setup.py安装python包和卸载python包的方法

    使用setup.py安装python包和卸载python包的方法 记录安装后文件的路径 python setup.py install --record files.txt删除这些文件 cat fil ...

  10. 知识图谱实战开发案例剖析-番外篇(1)- Neo4j是否支持按照边权重加粗和大数量展示

    一.前言 本文是<知识图谱实战开发案例完全剖析>系列文章和网易云视频课程的番外篇,主要记录学员在知识图谱等相关内容的学习 过程中,提出的共性问题进行展开讨论.该部分内容原始内容记录在网易云 ...