Skip to content

Commit

Permalink
pub: 凹多边形碰撞检测
Browse files Browse the repository at this point in the history
  • Loading branch information
Vivomo committed Mar 20, 2023
1 parent 19688b5 commit e446b38
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 1 deletion.
12 changes: 11 additions & 1 deletion _posts/2023-3-20-polygon-collision-detection.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
viewport: null
title: Canvas 多边形碰撞检测
css: common.css,collision-detection.css
js: polygon-collision-detection.js
js: https://unpkg.com/[email protected]/dist/earcut.min.js,polygon-collision-detection.js
---

<div class="wrap">
Expand All @@ -17,4 +17,14 @@ <h2>凸多边形碰撞检测 SAT算法</h2>
<canvas id="sat" width="800" height="400"></canvas>
</section>

<section>
<h2>凹多边形碰撞检测 耳朵减法 + SAT</h2>
<button class="btn" id="clear-sat2">清空画布</button>
<button class="btn" id="create-polygon2">创建多边形</button>
<button class="btn" id="check-polygon2">检测多边形是否有重合</button>
<p>点击画布创建顶点,点击创建生成多边形</p>
<p>是否碰撞:<span id="sat-result2"></span></p>
<canvas id="sat2" width="800" height="400"></canvas>
</section>

</div>
11 changes: 11 additions & 0 deletions src/html/polygon-collision-detection.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@ <h2>凸多边形碰撞检测 SAT算法</h2>
<canvas id="sat" width="800" height="400"></canvas>
</section>

<section>
<h2>凹多边形碰撞检测 耳朵减法 + SAT</h2>
<button class="btn" id="clear-sat2">清空画布</button>
<button class="btn" id="create-polygon2">创建多边形</button>
<button class="btn" id="check-polygon2">检测多边形是否有重合</button>
<p>点击画布创建顶点,点击创建生成多边形</p>
<p>是否碰撞:<span id="sat-result2"></span></p>
<canvas id="sat2" width="800" height="400"></canvas>
</section>

</div>
<script src="https://unpkg.com/[email protected]/dist/earcut.min.js"></script>
<script src="../js/polygon-collision-detection.js"></script>
</body>
</html>
91 changes: 91 additions & 0 deletions src/js/polygon-collision-detection.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ const SATCollision = (polygon1, polygon2) => {
return true;
};

// 参考:https://github.com/mapbox/earcut
// 返回一个二维数组,每个元素是一个子凸多边形的顶点坐标数组
const verticesToFlatPoints = (vertices) => {
const points = vertices.map((v) => [v.x, v.y]).flat();
return earcut(points);
}


(() => {
const clear = document.getElementById('clear-sat');
Expand Down Expand Up @@ -114,3 +121,87 @@ const SATCollision = (polygon1, polygon2) => {
points.push(point);
});
})();


(() => {
const clear = document.getElementById('clear-sat2');
const create = document.getElementById('create-polygon2');
const check = document.getElementById('check-polygon2');
const canvas = document.getElementById('sat2');
const resultTxt = document.getElementById('sat-result2');
const ctx = canvas.getContext('2d');
ctx.strokeStyle = '#0f0';
ctx.beginPath();

let points = [];
let polygon1 = null;
let polygon2 = null;

clear.addEventListener('click', () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
polygon1 = null;
polygon2 = null;
points = [];
resultTxt.innerText = '';
});

check.addEventListener('click', () => {
if (polygon1 && polygon2) {
const cutIndex = verticesToFlatPoints(polygon1.vertices);
const cutIndex2 = verticesToFlatPoints(polygon2.vertices);
let result = false;
for (let i = 0; i < cutIndex.length; i += 3) {
const cutPoints1 = [polygon1.vertices[cutIndex[i]], polygon1.vertices[cutIndex[i + 1]], polygon1.vertices[cutIndex[i + 2]]];
const subPolygon1 = new Polygon(cutPoints1)
for (let j = 0; j < cutIndex2.length; j += 3) {
const cutPoints2 = [polygon2.vertices[cutIndex2[j]], polygon2.vertices[cutIndex2[j + 1]], polygon2.vertices[cutIndex2[j + 2]]];
const subPolygon2 = new Polygon(cutPoints2);
result = SATCollision(subPolygon1, subPolygon2);
if (result) {
break;
}
}
}
resultTxt.innerText = result ? '是' : '否';
} else {
alert('请先创建两个多边形');
}
});

create.addEventListener('click', () => {
if (points.length < 3) {
alert('顶点不够');
return;
}
const [firstPoint, ...otherPoints] = points;
ctx.beginPath();
ctx.moveTo(firstPoint.x, firstPoint.y);
for (let point of otherPoints) {
ctx.lineTo(point.x, point.y);
}
ctx.closePath();
ctx.stroke();
if (!polygon1) {
polygon1 = new Polygon(points);
} else if (!polygon2) {
polygon2 = new Polygon(points);
}
points = [];
});

canvas.addEventListener('click', (e) => {
if (polygon2) {
alert('请先清空画布');
return;
}
const point = {
x: e.offsetX,
y: e.offsetY
};
ctx.beginPath();
ctx.arc(point.x, point.y, 5, 0, Math.PI * 2);
ctx.stroke();
ctx.closePath();
points.push(point);
});
})();

0 comments on commit e446b38

Please sign in to comment.