8.4.1.4_多边形之间的碰撞
来源:互联网 发布:淘宝兼职招聘可信吗 编辑:程序博客网 时间:2024/04/30 16:13
8.4.1.4_多边形之间的碰撞
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>多边形之间的碰撞</title> <style> body{ background: #fff; } #canvas{ background: #eee; } </style> </head> <body> <canvas id="canvas" width="800" height="600"></canvas> </body> <!-- Vector对象 向量对象 --> <script> //坐标系中的向量,以0,0出发,到点x,y,为一个向量,向量长度方向相同为同一个向量 var Vector = function(x,y){ this.x = x; this.y = y; }; Vector.prototype = { //得到向量的长度 getMagnitude:function(){ return Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)); }, //两向量相加得到的新向量 add:function(anotherVector){ var v = new Vector(); v.x = this.x + anotherVector.x; v.y = this.y + anotherVector.y; return v; }, //两向量相减,得到边缘法向量 OA-OB = BA; subtract:function(anotherVector){ var v = new Vector(); v.x = this.x - anotherVector.x; v.y = this.y - anotherVector.y; return v; }, //两向量的点积,一个向量在别一处向量上的投影,得到的不是一个向量,是投影的长度 dotProduct:function(anotherVector){ return this.x *anotherVector.x+ this.y*anotherVector.y; }, //得到多边形的边缘向量,即多边形,相邻两点的向量 edge:function(anotherVector){ return this.subtract(anotherVector); }, //得到垂直于边缘向量的边缘法向量,即投影轴向量 perpendicular:function(){ var v = new Vector(); v.x = this.y; v.y = 0-this.x; return v; }, //得去某向量的单位向量,即方向相同,长度为1的向量,单位向量主要是用来指示方向的 normalize:function(){ var v = new Vector(0,0); var m = this.getMagnitude(); if(m!=0){ //避免向量为0 v.x = this.x/m; v.y = this.y/m; } return v; }, //得去边缘法向量的单位向量,即投影轴向量的单位方向,表示投影轴的方向 perpendicularNormal:function(){ var p = this.perpendicular(); return p.normalize(); } } </script> <!-- Projection 投影对象 --> <script> var Projection = function(min,max){ this.min = min; this.max = max; }; Projection.prototype = { //检测两个多边形在同一个投影轴上的投影是否有重合,重叠返回true overlaps:function(projection){ return this.max > projection.min && projection.max >this.min; } } </script> <!-- Point对象 --> <script> //构造函数 var Point = function(x,y){ this.x = x; this.y = y; } </script> <!-- Shape对象 --> <script> var Shape = function(){ this.x = undefined; this.y = undefined; this.strokeStyle = 'rgba(255,253,208,0.9)'; this.fillStyle = 'rgba(147,197,114,0.8)'; }; Shape.prototype = { //碰撞检测的方法 //碰撞检测 collidesWith:function(shape){ //返回true表示碰撞了, false表示没有碰撞 var axes = this.getAxes().concat(shape.getAxes()); //得到拖拽形状与传入形状的所有投影轴集合 return !this.separationOnAxes(axes,shape); //返回在任何一个投影轴上的投影是否有公开 }, //检测在投影轴上投影是否有分离 separationOnAxes:function(axes,shape){ var axis; var projection1; var projection2; for(var i =0;i<axes.length;++i){ axis = axes[i]; projection1 = shape.project(axis); //得到形状在当前投影轴上的投影 projection2 = this.project(axis); //得到当前拖拽形状在当前投影轴上的投影 if(!projection1.overlaps(projection2)){ //检测两个投影在当前投影轴上是否重叠,分离返回false return true; //在当前投影轴上分离返回true,表示两个形状肯定没有碰撞,不需在检测后面的投影轴了, } }; return false; //检测完全部的投影轴上的投影没和一个分离的,返回false; }, //返回投影的最大值与最小值 project:function(axis){ throw 'project(axis) 还没有实现呢'; //这里不写是因为不同的形状的计算投影最大值与最小值的方法不同,不能统一写在这里 }, //得到形状所有的投影轴 getAxes:function(){ throw 'getAxes() 还没有实现呢';//这里不写是因为不同的形状得到投影轴的方法也不一样,不能统一写在这里,如圆形与多边形 }, //移动形状 move:function(dx,dy){ throw 'move(dx,dy) 还没有实现呢';//这里不写是因为不同的形状移动的方法不同,不能统一写在这里 }, //绘制图形的方法 //创建路径 creatPath:function(context){ throw 'creatPath(context) 还没有实现呢';//这里不写是因为不同的形状绘制的方法不同,不能统一写在这里 }, //图形填充方法 fill:function(context){ context.save(); context.fillStyle = this.strokeStyle; this.creatPath(context); context.fill(); context.restore(); }, //图形描边方法 stroke:function(){ context.save(); context.strokeStyle = this.strokeStyle; this.creatPath(context); context.stroke(); context.restore(); }, //判断当前鼠标的点是否在当前形状的路径内 isPointInPath:function(context,x,y){ this.creatPath(context); return context.isPointInPath(x,y); //该方法用于检测点是否在当前路径 } }; </script> <!-- Polygon对象 多边形对象 --> <script> var Polygon = function(){ this.points = []; this.strokeStyle = 'blue'; this.fillStyle = 'white'; }; Polygon.prototype = new Shape(); //用原型继承的方法来继承Shape对象的属性与方法 //重新定义多边形碰撞方法 Polygon.prototype.collidesWith = function(shape){ var axes = shape.getAxes(); if(axes === undefined ){ //多边形与圆形碰撞 return polygonCollidesWithCircle(this,shape); }else{ return !this.separationOnAxes(axes.concat(this.getAxes()),shape); } } //用于得到多边形所有投影轴的方法 Polygon.prototype.getAxes = function(){ var v1 = new Vector(); //代表多边形的相邻两点 var v2 = new Vector(); var axes = []; for(var i =0; i<this.points.length-1;i++){ //遍历多边形所有相邻的点得去所有的投影轴 v1.x = this.points[i].x; v1.y = this.points[i].y; v2.x = this.points[i+1].x; v2.y = this.points[i+1].y; axes.push(v1.edge(v2).perpendicularNormal()); }; //将收尾两点的投影轴也加入 v1.x = this.points[this.points.length-1].x; v1.y = this.points[this.points.length-1].y; v2.x = this.points[0].x; v2.y = this.points[0].y; axes.push(v1.edge(v2).perpendicularNormal()); return axes; }; //得到多边形各个点在某一条投影轴上投影,并得到投影两端点值,传递给投影对象Projection返回投影对象 Polygon.prototype.project = function(axis){ var scalars = []; //用于存放所有点向量在投影轴向量上的点积集合,注意点积集合是数量不是向量 var v = new Vector(); this.points.forEach(function(point){ v.x = point.x; v.y = point.y; scalars.push(v.dotProduct(axis)); }); return new Projection(Math.min.apply(Math,scalars),Math.max.apply(Math,scalars)); }; //为多边形增加新的点 Polygon.prototype.addPoint = function(x,y){ this.points.push(new Point(x,y)); }; //创建多边形路径 Polygon.prototype.creatPath = function(context){ if(this.points.length === 0){ return }; context.beginPath(); context.moveTo(this.points[0].x,this.points[0].y); for(var i =0 ;i<this.points.length;i++){ context.lineTo(this.points[i].x,this.points[i].y) } context.closePath(); //让首尾两点相连,创建完闭合路径 }; //移动多边形 Polygon.prototype.move = function(dx,dy){ //遍历多边形全部点,更新多边形位置 for(var i =0,point; i<this.points.length;i++){ point = this.points[i]; point.x += dx; point.y += dy; } }; </script> <!-- Circle对象 --> <script> var Circle = function(x,y,radius){ this.x = x; this.y = y; this.radius = radius; this.strokeStyle = 'rgba(255,253,208,0.9)'; this.fillStyle = 'rgba(147,197,114,0.8)'; } Circle.prototype = new Shape(); //继承shape的全部属性与方法 //圆的碰撞检测的方法 Circle.prototype.collidesWith = function(shape){ //重写Shape的collidesWith方法,因为在圆形的碰撞中不能用统一的方法进行判断 var point; var length; var min = 1000; //给定一个够大的最小值后面会用到 var v1; var v2; var edge; var perpendicular; var normal; var axes = shape.getAxes(); var distance; if(axes ==undefined){ //证明是圆形与圆形相碰撞 distance = Math.sqrt(Math.pow(shape.x -this.x,2)+Math.pow(shape.y -this.y,2)); return distance < Math.abs(this.radius + shape.radius); //圆心相距小于半径之和代表两圆发生了碰撞 }else{ //圆形与多边形想碰撞 return polygonCollidesWithCircle(shape,this); } } //圆形可以看做有无数个边的多边形,所以不能用找多边形投影轴的方法来找圆形的投影轴 Circle.prototype.getAxes = function(){ return undefined; }; //圆形在投影轴上的投影对象 Circle.prototype.project = function(axis){ var scalars =[]; //用于存放在投影轴上的投影 var point = new Point(this.x,this.y); //此点为圆心点 var dotProduct = new Vector(point).dotProduct(axis); //得到圆心向量在投影轴上的投影 scalars.push(dotProduct); scalars.push(dotProduct + this.radius); scalars.push(dotProduct - this.radius); //得到圆投影对象的最大值与最小值 return new Projection(Math.min.apply(Math,scalars),Math.max.apply(Math,scalars)); }; //移动圆形 Circle.prototype.move = function(dx,dy){ this.x += dx; this.y += dy; }; //创建圆形 Circle.prototype.creatPath = function(context){ context.beginPath(); context.arc(this.x,this.y,this.radius,0, Math.PI*2,false); } </script> <!-- 检测多边形与圆形之间的碰撞 --> <script> /* *圆与多边形碰撞的原理:圆可以近似的看成一个有无数条边的正多边形,而我们不可能按照这些边一一进行投影测试,我们只需要将圆形投射到一条投影轴上即可, *这条轴就是圆心与距其最近的多边形顶点之前的连线。 * */ //得到多边形距离圆形最近点 function getPolygonPointClosestToCircle(polygon,circle){ var min = 1000; //设置一个相对大的值做为相距起始比较值 var length; var testPoint; var closestPoint; for(var i =0 ;i<polygon.points.length;i++){ testPoint = polygon.points[i]; length = Math.sqrt(Math.pow(testPoint.x,2)+Math.pow(testPoint.y,2)); if(length <min){ min = length; closestPoint = testPoint; } }; return closestPoint; }; //多边形与圆形碰撞检测 function polygonCollidesWithCircle(polygon,circle){ var v1; var v2; var axes = polygon.getAxes(); var closestPoint = getPolygonPointClosestToCircle(polygon,circle); v1 = new Point(circle.x,circle.y); v2 = new Point(closestPoint.x,closestPoint.y); axes.push(v1.subtract(v2).normalize()); return !polygon.separationOnAxes(axes,circle); } </script> <script> var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); var shapes = []; //用于记录canvas中所有可做碰撞检测的图形 var polygonPoints = [ //用于存入3个多边形的顶点集合 [new Point(250,150), new Point(250,250), new Point(350,250)], [new Point(100,100),new Point(100,150), new Point(150,150),new Point(150,100)], [new Point(400,100),new Point(380,150),new Point(500,150),new Point(520,100)] ]; var polygonStrokeStyles = ['blue','yellow','red']; //3个多边形的描边颜色 var polygonFillStyles = ['rgba(255,255,0,0.7)','rgba(100,140,230,0.6)','rgba(255,255,255,0.8)'];//3个之边形的填充颜色 var mousedown = {x:0,y:0}; //用于记录鼠标按下的位置 var lastdrag = {x:0,y:0}; //用于记录拖拽时上一次鼠标所在的位置 var shapeBeingDragged = undefined; //用于记录当前正在拖拽的多边形 //初始化 for(var i =0 ;i<polygonPoints.length;i++){ var polygon = new Polygon(); var points = polygonPoints[i]; polygon.strokeStyle = polygonStrokeStyles[i]; polygon.fillStyle = polygonFillStyles[i]; points.forEach(function(point){ polygon.addPoint(point.x,point.y); }); shapes.push(polygon); }; context.shadowColor = 'rgba(100,140,255,0.5)'; context.shadowBlur = 4; context.shadowOffsetX = 2; context.shadowOffsetY =2; context.font = '38px arial'; //绘制所有的多边形 drawShapes(); context.save(); context.fillStyle = 'cornflowerblue'; context.font = '24px arial'; context.fillText('拖拽图形相碰撞', 10,25); context.restore(); //函数 //转换坐标到canvas function windowToCanvas(x,y){ var bbox = canvas.getBoundingClientRect(); return{ x:x - bbox.left*(canvas.width/bbox.width), y:y - bbox.top*(canvas.height/bbox.height) }; }; //绘制全部形状 function drawShapes(){ shapes.forEach(function(shape){ shape.stroke(context); shape.fill(context); }); } //检测碰撞 function detectCollosions(){ var textY = 30; var numShapes = shapes.length; var shape; var i; if(shapeBeingDragged){ for(var i = 0; i<numShapes ; ++i){ shape = shapes[i]; if(shape !=shapeBeingDragged ){ if(shapeBeingDragged.collidesWith(shape)){ //返回true: 碰撞了,false: 没有碰撞 context.fillStyle = shape.fillStyle; context.fillText('碰撞了',20,textY); textY += 40; } } } } } //事件 //鼠标近下点 canvas.onmousedown = function(e){ var location = windowToCanvas(e.clientX,e.clientY); //遍历所有的多边形,找到鼠标按下点所在的坐标形进行拖拽 shapes.forEach(function(shape){ if(shape.isPointInPath(context,location.x,location.y)){ shapeBeingDragged = shape; mousedown.x = location.x; mousedown.y = location.y; lastdrag.x = location.x; lastdrag.y = location.y; } }); }; //鼠标移动 canvas.onmousemove = function(e){ var location; var dragVector; //用于记录拖拽对方 //鼠标按下点在一个多边形上,在canvas空白处不做任何处理 if(shapeBeingDragged !=undefined){ //console.log('有可拖拽图形') location = windowToCanvas(e.clientX,e.clientY); dragVector = {x:location.x - lastdrag.x, y:location.y - lastdrag.y}; shapeBeingDragged.move(dragVector.x,dragVector.y); lastdrag.x = location.x; lastdrag.y = location.y; context.clearRect(0,0,canvas.width,canvas.height); drawShapes(); detectCollosions(); } }; //鼠标抬起 canvas.onmouseup = function(e){ shapeBeingDragged = undefined; } </script></html>
阅读全文
0 0
- 8.4.1.4_多边形之间的碰撞
- 8.4.1.5_圆形与多边形之间的碰撞检测
- 圆形与多边形之间的碰撞检验!
- 点跟多边形的碰撞检测
- 碰撞检测中函数(线与面,线与多边形,点到线的最近点) _ 转 - [叁]
- Unity3 碰撞之间的关系
- 2.11_多边形的绘制
- 8.4.1.6_图像与精灵的碰撞检测
- Cocos2dx 多边形碰撞检测
- [MM] 多边形碰撞
- 基于2D多边形的碰撞检测和响应(一)
- 基于2D多边形的碰撞检测和响应(二)
- 基于2D多边形的碰撞检测和响应(三)
- 基于2D多边形的碰撞检测和响应(四)
- 基于2D多边形的碰撞检测和响应(五)
- 基于2D多边形的碰撞检测和响应(六)
- 基于2D多边形的碰撞检测和响应(七)
- 成功研究出多边形的碰撞检测算法
- 优化算法——粒子群算法(PSO)
- js—BOM焦点事件
- Atom利用gcc-make-run编译opencv工程
- PHP mcrypt启用、加密以及解密过程
- 郑州公司名录
- 8.4.1.4_多边形之间的碰撞
- Pytorch学习系列(八)——训练神经网络
- java Selenium 发送126邮件 解决element not visible
- 排序算法性能总结
- github设置添加SSH
- 搭建SSM开发环境
- c# 添加引用时报错:“未能正确加载“ReferenceManagerPackage”包”的解决方法
- 1003. 相连的1
- 关于TextView限制显示字符,多余用省略号显示问题