2016-05-14 3 views
1

Мне нужно определить круг столкновений с любой строкой. У меня есть массив с вертиками многоугольника (x, y) и нарисовать этот многоугольник в цикле. Для обнаружения я использую алгоритм, который вычисляет высоту треугольника. Затем я проверю, была ли эта высота < 0, затем круг столкнулся с линией.Столкновение сегментов линии круга

Картину, которые описывают этот метод:

enter image description here

Но у меня есть неожиданный результат. Мой круг сталкивается с прозрачной линией (что?). Я не могу объяснить, как это происходит.

Demo в jsfiddle: https://jsfiddle.net/f458rdz6/1/

Функция, которая проверяет столкновения и ответ на это:

var p = polygonPoints; 

for (var i = 0, n = p.length; i < n; i++) { 
    var start = i; 
    var end = (i + 1) % n; 

    var x0 = p[start].x; 
    var y0 = p[start].y; 
    var x1 = p[end].x; 
    var y1 = p[end].y; 

    // detection collision 
    var dx = x1 - x0; 
    var dy = y1 - y0; 

    var len = Math.sqrt(dx * dx + dy * dy); 
    var dist = (dx * (this.y - y0) - dy * (this.x - x0))/len; 

    if (dist < this.radius) { 
     continue; 
    } 

    // calculate reflection, because collided 
    var wallAngle = Math.atan2(dy, dx); 
    var wallNormalX = Math.sin(wallAngle); 
    var wallNormalY = -Math.cos(wallAngle); 

    var d = 2 * (this.velocityX * wallNormalX + this.velocityY * wallNormalY); 
    this.x -= d * wallNormalX; 
    this.y -= d * wallNormalY; 
} 

var canvas = document.getElementById("myCanvas"); 
 
var ctx = canvas.getContext("2d"); 
 
var polygonPoints = [ 
 
\t { 
 
    \t x: 240, 
 
     y: 130 
 
    }, 
 
    { 
 
    \t x: 140, 
 
     y: 100 
 
    }, 
 
    { 
 
    \t x: 180, 
 
     y: 250 
 
    }, 
 
    { 
 
    \t x: 320, 
 
     y: 280 
 
    }, 
 
    { 
 
    \t x: 400, 
 
     y: 50 
 
    } 
 
]; 
 

 
var game = { 
 
\t ball: new Ball() 
 
}; 
 

 
function Ball() { 
 
\t this.x = canvas.width/2; 
 
    this.y = canvas.height - 100; 
 
    this.oldX = this.x - 1; 
 
    this.oldY = this.y + 1; 
 
    this.velocityX = 0; 
 
    this.velocityY = 0; 
 
    this.radius = 8; 
 
}; 
 

 
Ball.prototype.draw = function() { 
 
\t ctx.beginPath(); 
 
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); 
 
    ctx.fillStyle = '#0095DD'; 
 
    ctx.fill(); 
 
    ctx.closePath(); 
 
}; 
 

 
Ball.prototype.update = function() { 
 
\t var x = this.x; 
 
    var y = this.y; 
 
    
 
    this.velocityX = this.x - this.oldX; 
 
    this.velocityY = this.y - this.oldY; 
 
    
 
    this.x += this.velocityX; 
 
    this.y += this.velocityY; 
 
    
 
    this.oldX = x; 
 
    this.oldY = y; 
 
}; 
 

 
Ball.prototype.collision = function() { 
 
\t var p = polygonPoints; 
 
    
 
\t for (var i = 0, n = p.length; i < n; i++) { 
 
    \t var start = i; 
 
     var end = (i + 1) % n; 
 
     
 
     var x0 = p[start].x; 
 
     var y0 = p[start].y; 
 
     var x1 = p[end].x; 
 
     var y1 = p[end].y; 
 
     
 
     // detection collision 
 
     var dx = x1 - x0; 
 
     var dy = y1 - y0; 
 
     
 
     var len = Math.sqrt(dx * dx + dy * dy); 
 
     var dist = (dx * (this.y - y0) - dy * (this.x - x0))/len; 
 
     
 
     if (dist < this.radius) { 
 
     \t continue; 
 
     } 
 

 
\t \t // calculate reflection, because collided 
 
     var wallAngle = Math.atan2(dy, dx); 
 
     var wallNormalX = Math.sin(wallAngle); 
 
     var wallNormalY = -Math.cos(wallAngle); 
 

 
     var d = 2 * (this.velocityX * wallNormalX + this.velocityY * wallNormalY); 
 
     this.x -= d * wallNormalX; 
 
     this.y -= d * wallNormalY; 
 
    } 
 
}; 
 

 

 
function drawBall() { 
 
    ctx.beginPath(); 
 
    ctx.arc(x, y, ballRadius, 0, Math.PI*2); 
 
    ctx.fillStyle = "#0095DD"; 
 
    ctx.fill(); 
 
    ctx.closePath(); 
 
} 
 

 
function drawPolygon() { 
 
    ctx.beginPath(); 
 
    ctx.strokeStyle = '#333'; 
 
    ctx.moveTo(polygonPoints[0].x, polygonPoints[0].y); 
 
\t for (var i = 1, n = polygonPoints.length; i < n; i++) { 
 
    \t ctx.lineTo(polygonPoints[i].x, polygonPoints[i].y); 
 
    } 
 
    ctx.lineTo(polygonPoints[0].x, polygonPoints[0].y); 
 
    ctx.stroke(); 
 
    ctx.closePath(); 
 
} 
 

 
function render() { 
 
    ctx.clearRect(0, 0, canvas.width, canvas.height); 
 
    drawPolygon(); 
 
    game.ball.draw(); 
 
    game.ball.update(); 
 
    game.ball.collision(); 
 
    window.requestAnimationFrame(render); 
 
} 
 

 
render();
canvas { 
 
    border: 1px solid #333; 
 
}
<canvas id="myCanvas" width="480" height="320"></canvas>

Что проблема? Может быть, мне нужен другой метод обнаружения столкновения? Я пытался использовать this one, но если мой круг имеет высокую скорость, этот метод не работает.

спасибо.

ответ

4

Круг отрезок отсекаемый

ОБНОВЛЕНИЕ

Этот ответ включает в себя линии линии пересечения, перемещая линию вдоль ее нормального, расстояние точки (окружности) на линию, и круг линии пересечения.

Круг

var circle = { 
    radius : 500, 
    center : point(1000,1000), 
} 

Сегмент линии

var line = { 
    p1 : point(500,500), 
    p2 : point(2000,1000), 
} 

Точка является

var point = { 
    x : 100, 
    y : 100, 
} 

Таким образом, функция, чтобы найти точку пересечения сегмента линии шириной круг

Функция возвращает массив из двух точек в сегменте линии. Если найденные точки не возвращают пустой массив.

function inteceptCircleLineSeg(circle, line){ 
    var a, b, c, d, u1, u2, ret, retP1, retP2, v1, v2; 
    v1 = {}; 
    v2 = {}; 
    v1.x = line.p2.x - line.p1.x; 
    v1.y = line.p2.y - line.p1.y; 
    v2.x = line.p1.x - circle.center.x; 
    v2.y = line.p1.y - circle.center.y; 
    b = (v1.x * v2.x + v1.y * v2.y); 
    c = 2 * (v1.x * v1.x + v1.y * v1.y); 
    b *= -2; 
    d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - circle.radius * circle.radius)); 
    if(isNaN(d)){ // no intercept 
     return []; 
    } 
    u1 = (b - d)/c; // these represent the unit distance of point one and two on the line 
    u2 = (b + d)/c;  
    retP1 = {}; // return points 
    retP2 = {} 
    ret = []; // return array 
    if(u1 <= 1 && u1 >= 0){ // add point if on the line segment 
     retP1.x = line.p1.x + v1.x * u1; 
     retP1.y = line.p1.y + v1.y * u1; 
     ret[0] = retP1; 
    } 
    if(u2 <= 1 && u2 >= 0){ // second add point if on the line segment 
     retP2.x = line.p1.x + v1.x * u2; 
     retP2.y = line.p1.y + v1.y * u2; 
     ret[ret.length] = retP2; 
    }  
    return ret; 
} 

UPDATE

Line линия пересечения.

Возвращает точку, если найденное else возвращает неопределенный.

function interceptLines(line,line1){ 
    var v1, v2, c, u; 
    v1 = {}; 
    v2 = {}; 
    v3 = {}; 
    v1.x = line.p2.x - line.p1.x; // vector of line 
    v1.y = line.p2.y - line.p1.y; 
    v2.x = line1.p2.x - line1.p1.x; //vector of line2 
    v2.y = line1.p2.y - line1.p1.y; 
    var c = v1.x * v2.y - v1.y * v2.x; // cross of the two vectors 
    if(c !== 0){ 
     v3.x = line.p1.x - line1.p1.x; 
     v3.y = line.p1.y - line1.p1.y;     
     u = (v2.x * v3.y - v2.y * v3.x)/c; // unit distance of intercept point on this line 
     return {x : line.p1.x + v1.x * u, y : line.p1.y + v1.y * u}; 
    } 
    return undefined; 
} 

Lift Line

Перемещение линии вдоль нормального

function liftLine(line,dist){ 
    var v1,l 
    v1 = {}; 
    v1.x = line.p2.x - line.p1.x; // convert line to vector 
    v1.y = line.p2.y - line.p1.y; 
    l = Math.sqrt(v1.x * v1.x + v1.y * v1.y); // get length; 
    v1.x /= l; // Assuming you never pass zero length lines 
    v1.y /= l; 
    v1.x *= dist; // set the length 
    v1.y *= dist; 
    // move the line along its normal the required distance 
    line.p1.x -= v1.y; 
    line.p1.y += v1.x; 
    line.p2.x -= v1.y; 
    line.p2.y += v1.x; 

    return line; // if needed 
} 

Расстояние окружности (или точки) к отрезку

Возвращает наименьшее расстояние до линии сегмент. Я всего лишь центр круга. Таким образом, вы можете заменить круг точкой

function circleDistFromLineSeg(circle,line){ 
    var v1, v2, v3, u; 
    v1 = {}; 
    v2 = {}; 
    v3 = {}; 
    v1.x = line.p2.x - line.p1.x; 
    v1.y = line.p2.y - line.p1.y; 
    v2.x = circle.center.x - line.p1.x; 
    v2.y = circle.center.y - line.p1.y; 
    u = (v2.x * v1.x + v2.y * v1.y)/(v1.y * v1.y + v1.x * v1.x); // unit dist of point on line 
    if(u >= 0 && u <= 1){ 
     v3.x = (v1.x * u + line.p1.x) - circle.center.x; 
     v3.y = (v1.y * u + line.p1.y) - circle.center.y; 
     v3.x *= v3.x; 
     v3.y *= v3.y; 
     return Math.sqrt(v3.y + v3.x); // return distance from line 
    } 
    // get distance from end points 
    v3.x = circle.center.x - line.p2.x; 
    v3.y = circle.center.y - line.p2.y; 
    v3.x *= v3.x; // square vectors 
    v3.y *= v3.y;  
    v2.x *= v2.x; 
    v2.y *= v2.y; 
    return Math.min(Math.sqrt(v2.y + v2.x), Math.sqrt(v3.y + v3.x)); // return smaller of two distances as the result 
} 
+0

Этот метод работает, если мяч имеет низкую скорость, но если скорость высокая, функция не работает. https://jsfiddle.net/cmx8arz2/ см. консоль. Если приложение пересечения границ круга должно печатать массив точек пересечения, но скорость круга высокая и консольный журнал очищается. – rmpstmp

+0

@rmpstmp Используйте перехват линии, используя траекторию шара в качестве второй линии. Переместите линию вдоль ее нормали к шару радиусом шариков. Если линии пересекаются, используйте эту точку, чтобы проверить, насколько далеко от исходного сегмента линии. если в радиусе (добавить EPSILON) из любой точки на линии или в конечных точках вы попали в мяч. Кросс-продукт будет давать то, что указывает точка на линию + правая-левая и точка-линия. Линия квадратов, выраженная в квадрате, даст единицу dist на линии вдоль ее нормали к точке. Точка и линия должны быть переведены, чтобы положить начало линии в нуле – Blindman67

+0

@rmpstmp Глядя на скрипку. Просто переместите все сегменты линии внутрь радиусом шара (вдоль нормалей линии). Используйте траекторию шара как линию и выполняйте перехват линии/линии. Найдите ближайший перехват текущего местоположения шара, и у вас есть точка перехвата, нет необходимости в перехвате сегмента линии круга. – Blindman67

Смежные вопросы