Обнаружение коллизии полигонов
- package;
- import js.Browser.document;
- import js.Browser.window;
- import js.html.CanvasElement;
- import js.html.CanvasRenderingContext2D in Context2D;
- typedef Point = {x:Float, y:Float}
- class Main {
- var canvas:CanvasElement;
- var g:Context2D;
- var poly:Array<Point>;
- var poly2:Array<Point>;
- static function main():Void {
- new Main();
- }
- function new() {
- canvas = document.createCanvasElement();
- canvas.id = "canvas";
- canvas.style.border = "1px solid #000";
- canvas.style.cursor = "none";
- document.body.appendChild(canvas);
- g = canvas.getContext("2d");
- poly = [{x:0, y:0}, {x:30, y:2}, {x:8, y:30}];
- poly2 = [{x:60, y:60}, {x:90, y:61}, {x:98, y:100}, {x:80, y:100}];
- canvas.onmousemove = function(e) {
- // moving first polygon
- final rect = canvas.getBoundingClientRect();
- poly[0].x = e.clientX - rect.left;
- poly[0].y = e.clientY - rect.top;
- poly[1].x = poly[0].x + 30;
- poly[1].y = poly[0].y + 2;
- poly[2].x = poly[0].x + 8;
- poly[2].y = poly[0].y + 30;
- }
- window.requestAnimationFrame(onRender);
- }
- function onRender(stamp:Float):Void {
- g.clearRect(0, 0, canvas.width, canvas.height);
- if (doPolygonsIntersect(poly, poly2)) g.fillStyle = "red";
- else g.fillStyle = "black";
- drawPoly(g, poly);
- drawPoly(g, poly2);
- window.requestAnimationFrame(onRender);
- }
- inline function drawPoly(g:Context2D, poly:Array<Point>):Void {
- g.beginPath();
- g.moveTo(poly[0].x, poly[0].y);
- for (i in 1...poly.length) {
- g.lineTo(poly[i].x, poly[i].y);
- }
- g.fill();
- }
- function doPolygonsIntersect(a:Array<Point>, b:Array<Point>) {
- final polygons = [a, b];
- for (polygon in polygons) {
- for (i in 0...polygon.length) {
- // get points for normal
- final i2 = (i + 1) % polygon.length;
- final p1 = polygon[i];
- final p2 = polygon[i2];
- final normal = {
- x: p2.y - p1.y,
- y: p1.x - p2.x
- };
- var minA = Math.POSITIVE_INFINITY;
- var maxA = Math.NEGATIVE_INFINITY;
- for (point in a) {
- final projected = normal.x * point.x + normal.y * point.y;
- if (projected < minA) minA = projected;
- if (projected > maxA) maxA = projected;
- }
- var minB = Math.POSITIVE_INFINITY;
- var maxB = Math.NEGATIVE_INFINITY;
- for (point in b) {
- final projected = normal.x * point.x + normal.y * point.y;
- if (projected < minB) minB = projected;
- if (projected > maxB) maxB = projected;
- }
- if (maxA < minB || maxB < minA) return false;
- }
- }
- return true;
- }
- }
Очень короткая реализация сути алгоритма на основе Separating Axis Theorem. Работает только с выпуклыми полигонами (вогнутые фигуры можно разбить на несколько выпуклых).
При использовании для большого кол-ва полигонов не забудьте сначала просчитывать и проверять прямоугольные границы каждого полигона на пересечение (bounding boxes). Подробнее будет в статье по написанию своего физ. движка, появится здесь в ближайшие 5 лет.
Исходники в онлайн-редакторе: http://try-haxe.mrcdk.com/#0D9d6
JS версия:
https://jsfiddle.net/RblSb/38Lrqfrr/
Оригинал алгоритма (с вариациями на других языках):
http://stackoverflow.com/a/12414951/4750888
При использовании для большого кол-ва полигонов не забудьте сначала просчитывать и проверять прямоугольные границы каждого полигона на пересечение (bounding boxes). Подробнее будет в статье по написанию своего физ. движка, появится здесь в ближайшие 5 лет.
Исходники в онлайн-редакторе: http://try-haxe.mrcdk.com/#0D9d6
JS версия:
https://jsfiddle.net/RblSb/38Lrqfrr/
Оригинал алгоритма (с вариациями на других языках):
http://stackoverflow.com/a/12414951/4750888