SAT Polygon Circle Collision - разрешите пересечение в направлении скорости и определите сторону столкновения - PullRequest
8 голосов
/ 17 июня 2020

Резюме

Этот вопрос находится в JavaScript, но ответ на любом языке, псевдокоде или просто математике будет отличным!

I пытались реализовать теорему о разделительной оси , чтобы выполнить sh следующее:

  • Обнаружение пересечения между выпуклым многоугольником и кругом.
  • Поиск перевода, который может быть применен к кругу для разрешения пересечения, чтобы круг едва касался многоугольника, но больше не находился внутри.
  • Определение оси столкновения (подробности в конце вопрос).

Я успешно выполнил первый пункт, и вы можете увидеть мой код javascript в конце вопроса. У меня проблемы с другими частями.

Разрешение пересечения

В Интернете есть множество примеров того, как разрешить пересечение в направлении с наименьшим / самым коротким перекрытие круга. Вы можете видеть в моем коде в конце, что я уже рассчитал это.

Однако это не соответствует моим потребностям. Я должен разрешить столкновение в направлении, противоположном траектории круга (предположим, что у меня уже есть траектория круга и я хочу передать ее в свою функцию как единичный вектор или угол, в зависимости от того, что подходит).

Вы можете увидеть разницу между минимальным разрешением и предполагаемым разрешением на изображении ниже:

enter image description here

Как я могу рассчитать минимальный перевод вектор для разрешения пересечения внутри моей функции test_CIRCLE_POLY, но это должно применяться в определенном c направлении, противоположном траектории круга?

Мои идеи / попытки:

  • Моя первая идея заключалась в том, чтобы добавить дополнительную ось к осям, которые должны быть протестированы в алгоритме SAT, и эта ось была бы перпендикулярна траектории круга. Затем я бы решил на основе перекрытия при проецировании на эту ось. Это вроде бы сработало, но в большинстве ситуаций разрешило бы путь слишком далеко. Это не приведет к переводу минимум . Так что это не будет удовлетворительным.
  • Моя вторая идея состояла в том, чтобы продолжать использовать величину кратчайшего перекрытия, но изменить направление, чтобы оно было противоположным траектории круга. Это выглядит многообещающе, но, вероятно, есть много крайних случаев, которые я не учел. Может быть, это хорошее место для начала.

Определение стороны / оси столкновения

Я нашел способ определить, с какой стороны многоугольника круг сталкивается с. Для каждой проверенной оси многоугольника я просто проверял на перекрытие. Если есть перекрытие, эта сторона сталкивается.

Это решение снова не будет приемлемым, поскольку я хотел бы выяснить только одну сторону в зависимости от траектории круга.

Мое предполагаемое решение скажет мне, в примере изображения ниже, что ось A является осью столкновения, а не осью B. Это потому, что после разрешения пересечения ось A является осью, соответствующей стороне многоугольник, который едва касается круга.

enter image description here

Мои идеи / попытки:

  • В настоящее время я предполагаю ось столкновения перпендикулярна MTV (минимальный вектор перемещения). В настоящее время это неверно, но она должна быть правильной осью после того, как я обновлю процесс разрешения пересечения в первой половине вопроса. Так что сначала нужно заняться этой частью.

  • В качестве альтернативы я подумал о создании линии из предыдущего положения круга и его текущего положения + радиуса и проверки того, какие стороны пересекаются с этой линией. Тем не менее, двусмысленность все еще существует, потому что иногда с линией пересекается более одной стороны.

Мой код на данный момент

function test_CIRCLE_POLY(circle, poly, circleTrajectory) {
    // circleTrajectory is currently not being used

    let axesToTest = [];
    let shortestOverlap = +Infinity;
    let shortestOverlapAxis;

    // Figure out polygon axes that must be checked

    for (let i = 0; i < poly.vertices.length; i++) {
        let vertex1 = poly.vertices[i];
        let vertex2 = poly.vertices[i + 1] || poly.vertices[0]; // neighbouring vertex
        let axis = vertex1.sub(vertex2).perp_norm();
        axesToTest.push(axis);
    }

    // Figure out circle axis that must be checked

    let closestVertex;
    let closestVertexDistSqr = +Infinity;

    for (let vertex of poly.vertices) {
        let distSqr = circle.center.sub(vertex).magSqr();

        if (distSqr < closestVertexDistSqr) {
            closestVertexDistSqr = distSqr;
            closestVertex = vertex;
        }
    }

    let axis = closestVertex.sub(circle.center).norm();
    axesToTest.push(axis);

    // Test for overlap

    for (let axis of axesToTest) {
        let circleProj = proj_CIRCLE(circle, axis);
        let polyProj = proj_POLY(poly, axis);
        let overlap = getLineOverlap(circleProj.min, circleProj.max, polyProj.min, polyProj.max);

        if (overlap === 0) {
            // guaranteed no intersection
            return { intersecting: false };
        }

        if (Math.abs(overlap) < Math.abs(shortestOverlap)) {
            shortestOverlap = overlap;
            shortestOverlapAxis = axis;
        }
    }

    return {
        intersecting: true,
        resolutionVector: shortestOverlapAxis.mul(-shortestOverlap),
        // this resolution vector is not satisfactory, I need the shortest resolution with a given direction, which would be an angle passed into this function from the trajectory of the circle
        collisionAxis: shortestOverlapAxis.perp(),
        // this axis is incorrect, I need the axis to be based on the trajectory of the circle which I would pass into this function as an angle
    };
}

function proj_POLY(poly, axis) {
    let min = +Infinity;
    let max = -Infinity;

    for (let vertex of poly.vertices) {
        let proj = vertex.projNorm_mag(axis);
        min = Math.min(proj, min);
        max = Math.max(proj, max);
    }

    return { min, max };
}

function proj_CIRCLE(circle, axis) {
    let proj = circle.center.projNorm_mag(axis);
    let min = proj - circle.radius;
    let max = proj + circle.radius;

    return { min, max };
}

// Check for overlap of two 1 dimensional lines
function getLineOverlap(min1, max1, min2, max2) {
    let min = Math.max(min1, min2);
    let max = Math.min(max1, max2);

    // if negative, no overlap
    let result = Math.max(max - min, 0);

    // add positive/negative sign depending on direction of overlap
    return result * ((min1 < min2) ? 1 : -1);
};

Ответы [ 4 ]

3 голосов
/ 20 июня 2020

Я предполагаю, что многоугольник выпуклый, а круг движется по прямой линии (по крайней мере, в течение некоторого, возможно, небольшого промежутка времени), а не следует какой-то изогнутой траектории. Если он следует по кривой траектории, все становится сложнее. В случае криволинейных траекторий основные идеи могут быть сохранены, но фактическая точка столкновения (точка разрешения столкновения для круга) может быть труднее вычислить. Тем не менее, я обрисовываю идею, которая может быть распространена и на этот случай. Кроме того, его можно использовать в качестве основного подхода для обнаружения столкновений между кругом и выпуклым многоугольником.

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

Преобразуйте в уме столкновение между кругом и многоугольником в столкновение между центром круга (точкой) и версией многоугольника, утолщенного радиусом круга r, т.е. (i) каждый край многоугольника смещен (перемещен) наружу на радиус r вдоль вектора, перпендикулярного ему и указывающего за пределы многоугольника, (ii) вершины становятся дугами окружности радиуса r с центром в точке вершины многоугольника и соединение конечных точек соответствующих соседних ребер смещения (в основном, помещаем круги радиуса r в вершинах многоугольника и берем их выпуклую оболочку).

введите описание изображения здесь

Теперь текущее положение центра круга C = [ C[0], C[1] ], и он двигался по прямой линии с вектором направления V = [ V[0], V[1] ], указывающим в направлении движения (или, если хотите, подумайте V как скорость круга в момент, когда вы обнаружили столкновение). Затем есть ось (или, скажем, луч - направленная полупрямая), определяемая векторным уравнением X = C - t * V, где t >= 0 (эта ось указывает на прошлую траекторию). По сути, это полупрямая, которая проходит через центральную точку C и выровнена / параллельно вектору V. Теперь точка разрешения, то есть точка, в которую вы хотите переместить круг, - это точка, в которой ось X = C - t * V пересекает границу утолщенного многоугольника.

Итак, вы должны сначала проверить (1) пересечение осей для ребер, а затем (2) пересечение осей с дугами окружности, относящимися к вершинам исходного многоугольника.

Предположим, что многоугольник задан массивом вершин P = [ P[0], P[1], ..., P[N], P[0] ], ориентированных против часовой стрелки.

(1) Для каждого ребра P[i-1]P[i] исходного многоугольника, имеющего отношение к вашему столкновению (это могут быть два соседних ребра, встречающиеся в вершине, на основе которой обнаружено столкновение, или это может быть фактически все грани в случае круга движется с очень высокой скоростью, и вы обнаружили столкновение очень поздно, так что фактического столкновения там даже не произошло, я оставляю это на ваше усмотрение, потому что вы лучше знаете детали своей ситуации) сделайте следующее. У вас есть в качестве входных данных:

C = [ C[0], C[1] ]
V = [ V[0], V[1] ]
P[i-1] = [ P[i-1][0],  P[i-1][1] ]
P[i] = [ P[i][0],  P[i][1] ]

Do:

Normal = [ P[i-1][1] - P[i][1], P[i][0] - P[i-1][0] ];
Normal = Normal / sqrt((P[i-1][1] - P[i][1])^2 + ( P[i][0] - P[i-1][0] )^2); 
// you may have calculated these already

Q_0[0] = P[i-1][0] + r*Normal[0];
Q_0[1] = P[i-1][1] + r*Normal[1];

Q_1[0] = P[i][0]+ r*Normal[0]; 
Q_1[1] = P[i][1]+ r*Normal[1]; 

Решите для s, t линейную систему уравнений (уравнение для пересечения):

Q_0[0] + s*(Q_1[0] - Q_0[0]) = C[0] - t*V[0];
Q_0[1] + s*(Q_1[1] - Q_0[1]) = C[1] - t*V[1];

если 0<= s <= 1 и t >= 0, все готово, и ваша точка разрешения будет

R[0] = C[0] - t*V[0];
R[1] = C[1] - t*V[1];

иначе

(2) Для каждой вершины P[i] относится к вашему столкновению, выполните следующие действия: решите для t квадратное уравнение c (есть явная формула)

norm(P[i] - C + t*V )^2 = r^2

или разверните:

(V[0]^2 + V[1]^2) * t^2 + 2 * ( (P[i][0] - C[0])*V[0] + (P[i][1] - C[1])*V[1] )*t + ( P[i][0] - C[0])^2 + (P[i][1] - C[1])^2 )  - r^2 = 0

или, если вы предпочитаете код, более похожий на код:

a = V[0]^2 + V[1]^2;
b = (P[i][0] - C[0])*V[0] + (P[i][1] - C[1])*V[1];
c = (P[i][0] - C[0])^2 + (P[i][1] - C[1])^2 - r^2;
D = b^2 - a*c;

if D < 0 there is no collision with the vertex 
i.e. no intersection between the line X = C - t*V 
and the circle of radius r centered at P[i]

else
D = sqrt(D);
t1 = ( - b - D) / a;
t2 = ( - b + D) / a;  
where t2 >= t1 

Тогда ваша точка разрешения

R[0] = C[0] - t2*V[0];
R[1] = C[1] - t2*V[1];
1 голос
/ 21 июня 2020

Пересечение многоугольника окружности

Если мяч движется и если вы можете убедиться, что мяч всегда начинается за пределами многоугольника, решение довольно простое.

Мы назовем мяч и его движение по линии мяча. Он начинается с текущего местоположения мяча и заканчивается в том положении, в котором мяч будет в следующем кадре.

Чтобы решить, вам нужно найти ближайшую точку пересечения с началом линии мяча.

Есть два типа точки пересечения.

  • Отрезок линии (шаровая линия) с отрезком линии (край многоугольника)
  • Сегмент линии (линия шара) с кругом (круг на каждом (только выпуклом) многоугольнике) угол)

В примере кода есть объект Lines2, содержащий две соответствующие функции перехвата. Перехватчики возвращаются как Vec2, содержащее два единичных расстояния. Функции перехвата предназначены для строк (бесконечной длины), а не для линий. Если точки пересечения нет, то возврат не определен.

Для линий пересечения Line2.unitInterceptsLine(line, result = new Vec2()) единицы измерения (в result) представляют собой единичное расстояние вдоль каждой строки от начала. отрицательные значения отстают от начала.

Чтобы учесть радиус шара, каждое ребро многоугольника смещено на радиус шара по нормали. Важно, чтобы края многоугольника имели постоянное направление. В этом примере нормаль находится справа от линии, а точки многоугольника расположены по часовой стрелке.

Для отрезков отрезка / круга Line2.unitInterceptsCircle(center, radius, result = new Vec2()) единицы измерения (в result) являются единицами измерения расстояние по линии, где он пересекает круг. result.x всегда будет содержать ближайшую точку пересечения (при условии, что вы начинаете за пределами круга). Если есть перехват, то способов всегда будет два, даже если они находятся в одной точке.

Пример

Пример содержит все, что необходимо

Интересующие объекты ball и poly

  • ball определяют мяч и его движение. Также есть код для его рисования для примера

  • poly содержит точки многоугольника. Преобразует точки в линии смещения в зависимости от радиуса шара. Он оптимизирован для вычисления линий только при изменении радиуса шара.

Функция poly.movingBallIntercept - это функция, которая выполняет всю работу. Он принимает шар и необязательный вектор результатов.

Он возвращает позицию Vec2 шара, если он касается многоугольника.

Он делает это, находя наименьшее единичное расстояние к линиям смещения и точка (в виде круга) и использует это единичное расстояние для позиционирования результата.

Обратите внимание на то, что если мяч находится внутри многоугольника, пересечения с углами меняются местами. Функция Line2.unitInterceptsCircle обеспечивает расстояние в 2 единицы, где линия входит в круг и выходит из него. Однако вам нужно знать, находитесь ли вы внутри или снаружи, чтобы знать, какой из них использовать. В этом примере предполагается, что вы находитесь за пределами многоугольника.

Инструкции

  • Переместите мышь, чтобы изменить траекторию шариков.
  • Щелкните, чтобы установить начальную позицию шариков.

Math.EPSILON = 1e-6;
Math.isSmall = val => Math.abs(val) < Math.EPSILON;
Math.isUnit = u => !(u < 0 || u > 1);
Math.TAU = Math.PI * 2;


/* export {Vec2, Line2} */ // this should be a module
var temp;
function Vec2(x = 0, y = (temp = x, x === 0 ? (x = 0 , 0) : (x = x.x, temp.y))) {
    this.x = x;
    this.y = y;
}
Vec2.prototype = {
    init(x, y = (temp = x, x = x.x, temp.y)) { this.x = x; this.y = y; return this }, // assumes x is a Vec2 if y is undefined
    copy() { return new Vec2(this) },
    equal(v) { return (this.x - v.x) === 0 && (this.y - v.y) === 0 },
    isUnits() { return Math.isUnit(this.x) && Math.isUnit(this.y) },
    add(v, res = this) { res.x = this.x + v.x; res.y = this.y + v.y; return res },
    sub(v, res = this) { res.x = this.x - v.x; res.y = this.y - v.y; return res },
    scale(val, res = this) { res.x = this.x * val; res.y = this.y * val; return res },
    invScale(val, res = this) { res.x = this.x / val; res.y = this.y / val; return res },
    dot(v) { return this.x * v.x + this.y * v.y },
    uDot(v, div) { return (this.x * v.x + this.y * v.y) / div },
    cross(v) { return this.x * v.y - this.y * v.x },
    uCross(v, div) { return (this.x * v.y - this.y * v.x) / div },
    get length() { return this.lengthSqr ** 0.5 },
    set length(l) { this.scale(l / this.length) },
    get lengthSqr() { return this.x * this.x + this.y * this.y },
    rot90CW(res = this) {
        const y = this.x;
        res.x = -this.y;
        res.y = y;
        return res;
    },
};
const wV1 = new Vec2(), wV2 = new Vec2(), wV3 = new Vec2(); // pre allocated work vectors used by Line2 functions
function Line2(p1 = new Vec2(), p2 = (temp = p1, p1 = p1.p1 ? p1.p1 : p1, temp.p2 ? temp.p2 : new Vec2())) {
    this.p1 = p1;
    this.p2 = p2;
}
Line2.prototype = {
    init(p1, p2 = (temp = p1, p1 = p1.p1, temp.p2)) { this.p1.init(p1); this.p2.init(p2) },
    copy() { return new Line2(this) },
    asVec(res = new Vec2()) { return this.p2.sub(this.p1, res) },
    unitDistOn(u, res = new Vec2()) { return this.p2.sub(this.p1, res).scale(u).add(this.p1) },
    translate(vec, res = this) {
        this.p1.add(vec, res.p1);
        this.p2.add(vec, res.p2);
        return res;
    },
    translateNormal(amount, res = this) {
        this.asVec(wV1).rot90CW().length = -amount;
        this.translate(wV1, res);
        return res;
    },
    unitInterceptsLine(line, res = new Vec2()) {  // segments
        this.asVec(wV1);
        line.asVec(wV2);
        const c = wV1.cross(wV2);
        if (Math.isSmall(c)) { return }
        wV3.init(this.p1).sub(line.p1);
        res.init(wV1.uCross(wV3, c), wV2.uCross(wV3, c));
        return res;
    },
    unitInterceptsCircle(point, radius, res = new Vec2()) {
        this.asVec(wV1);
        var b = -2 * this.p1.sub(point, wV2).dot(wV1);
        const c = 2 * wV1.lengthSqr;
        const d = (b * b - 2 * c * (wV2.lengthSqr - radius * radius)) ** 0.5
        if (isNaN(d)) { return }
        return res.init((b - d) / c, (b + d) / c);
    },
};

/* END of file */ // Vec2 and Line2 module 



/* import {vec2, Line2} from "whateverfilename.jsm" */ // Should import vec2 and line2
const POLY_SCALE = 0.5;
const ball = {
    pos: new Vec2(-150,0),
    delta: new Vec2(10, 10),
    radius: 20,
    drawPath(ctx) {
        ctx.beginPath();
        ctx.arc(this.pos.x, this.pos.y, this.radius, 0, Math.TAU);
        ctx.stroke();
    },
}
const poly =  {
    bRadius: 0,
    lines: [],
    set ballRadius(radius) {
        const len = this.points.length
        this.bRadius = ball.radius;
        i = 0;
        while (i < len) {
            let line = this.lines[i];
            if (line) { line.init(this.points[i], this.points[(i + 1) % len]) }
            else { line = new Line2(new Vec2(this.points[i]), new Vec2(this.points[(i + 1) % len])) }
            this.lines[i++] = line.translateNormal(radius);
        }
        this.lines.length = i;
    },
    points: [
        new Vec2(-200, -150).scale(POLY_SCALE),
        new Vec2(200, -100).scale(POLY_SCALE),
        new Vec2(100, 0).scale(POLY_SCALE),
        new Vec2(200, 100).scale(POLY_SCALE),
        new Vec2(-200, 75).scale(POLY_SCALE),
        new Vec2(-150, -50).scale(POLY_SCALE),
    ],
    drawBallLines(ctx) {
        if (this.lines.length) {
            const r = this.bRadius;
            ctx.beginPath();
            for (const l of this.lines) { 
                ctx.moveTo(l.p1.x, l.p1.y);
                ctx.lineTo(l.p2.x, l.p2.y);
            }
            for (const p of this.points) { 
                ctx.moveTo(p.x + r, p.y);
                ctx.arc(p.x, p.y, r, 0, Math.TAU);
            }
            ctx.stroke()
        }
    },
    drawPath(ctx) {
        ctx.beginPath();
        for (const p of this.points) { ctx.lineTo(p.x, p.y) }
        ctx.closePath();
        ctx.stroke();
    },
    movingBallIntercept(ball, res = new Vec2()) {
        if (this.bRadius !== ball.radius) { this.ballRadius = ball.radius }
        var i = 0, nearest = Infinity, nearestGeom, units = new Vec2();
        const ballT = new Line2(ball.pos, ball.pos.add(ball.delta, new Vec2()));
        for (const p of this.points) {
            const res = ballT.unitInterceptsCircle(p, ball.radius, units);
            if (res && units.x < nearest && Math.isUnit(units.x)) { // assumes ball started outside poly so only need first point
                nearest = units.x;
                nearestGeom = ballT;
            }
        }
        for (const line of this.lines) {
            const res = line.unitInterceptsLine(ballT, units);
            if (res && units.x < nearest && units.isUnits()) { // first unit.x is for unit dist on line
                nearest = units.x;
                nearestGeom = ballT;
            }
        }
        if (nearestGeom) { return ballT.unitDistOn(nearest, res) }
        return;
    },
}



const ctx = canvas.getContext("2d");
var w = canvas.width, cw = w / 2;
var h = canvas.height, ch = h / 2
requestAnimationFrame(mainLoop);



// line and point for displaying mouse interaction. point holds the result if any
const line = new Line2(ball.pos, ball.pos.add(ball.delta, new Vec2())), point = new Vec2();
function mainLoop() {
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    if(w !== innerWidth || h !== innerHeight){
        cw = (w = canvas.width = innerWidth) / 2;
        ch = (h = canvas.height = innerHeight) / 2;
    }else{
        ctx.clearRect(0,0,w,h);
    }
    ctx.setTransform(1,0,0,1,cw,ch);  // center to canvas
    if (mouse.button) { ball.pos.init(mouse.x - cw, mouse.y - ch) }
    line.p2.init(mouse.x - cw, mouse.y - ch);
    line.p2.sub(line.p1, ball.delta);

    ctx.lineWidth = 1;
    ctx.strokeStyle = "#000"
    poly.drawPath(ctx)
    ctx.strokeStyle = "#F804"
    poly.drawBallLines(ctx);       
    
    ctx.strokeStyle = "#F00"    
    ctx.beginPath();
    ctx.arc(ball.pos.x, ball.pos.y, ball.radius, 0, Math.TAU);
    ctx.moveTo(line.p1.x, line.p1.y);
    ctx.lineTo(line.p2.x, line.p2.y);
    ctx.stroke();

    ctx.strokeStyle = "#00f"    
    ctx.lineWidth = 2;
    ctx.beginPath();
    if (poly.movingBallIntercept(ball, point)) {
       ctx.arc(point.x, point.y, ball.radius, 0, Math.TAU);
    } else {
       ctx.arc(line.p2.x, line.p2.y, ball.radius, 0, Math.TAU);
    }
    ctx.stroke();
           
    requestAnimationFrame(mainLoop);
}


const mouse = {x:0, y:0, button: false};
function mouseEvents(e) {
      const bounds = canvas.getBoundingClientRect();
      mouse.x = e.pageX - bounds.left - scrollX;
      mouse.y = e.pageY - bounds.top - scrollY;
      mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["mousedown","mouseup","mousemove"].forEach(name => document.addEventListener(name,mouseEvents));
#canvas {
  position: absolute;
  top: 0px;
  left: 0px;
}
<canvas id="canvas"></canvas>
Click to position ball. Move mouse to test trajectory 

Vec2 и Line2

Чтобы упростить задачу, поможет векторная библиотека. В этом примере я написал быстрые объекты Vec2 и Line2 (обратите внимание, что были протестированы только функции, используемые в примере, Примечание: объект предназначен для повышения производительности, неопытным кодировщикам следует избегать использования этих объектов и выбирать более стандартный вектор и линейная библиотека)

1 голос
/ 20 июня 2020

Вероятно, это не то, что вы ищете, но вот способ сделать это (если вы не ищете идеальной точности):
Вы можете попробовать приблизить позицию вместо вычисляя его.

Способ настройки кода имеет большое преимущество: у вас есть последняя позиция круга перед столкновением. Благодаря этому вы можете просто «перебрать» траекторию и попытаться найти позицию, наиболее близкую к положению пересечения. Я предполагаю, что у вас уже есть функция, которая сообщает вам, пересекается ли круг с многоугольником. Код (C ++):

// What we need :

Vector startPos; // Last position of the circle before the collision
Vector currentPos; // Current, unwanted position
Vector dir; // Direction (a unit vector) of the circle's velocity
float distance = compute_distance(startPos, currentPos); // The distance from startPos to currentPos.
Polygon polygon; // The polygon
Circle circle; // The circle.
unsigned int iterations_count = 10; // The number of iterations that will be done. The higher this number, the more precise the resolution.

// The algorithm :

float currentDistance = distance / 2.f; // We start at the half of the distance.
Circle temp_copy; // A copy of the real circle to "play" with.
for (int i = 0; i < iterations_count; ++i) {
    temp_copy.pos = startPos + currentDistance * dir;
    if (checkForCollision(temp_copy, polygon)) {
        currentDistance -= currentDistance / 2.f; // We go towards startPos by the half of the current distance.
    }
    else {
        currentDistance += currentDistance / 2.f; // We go towards currentPos by the half of the current distance.
    }
}
    
// currentDistance now contains the distance between startPos and the intersection point
// And this is where you should place your circle :
Vector intersectionPoint = startPos + currentDistance * dir;

Я не тестировал этот код, поэтому надеюсь, что там нет большой ошибки. Он также не оптимизирован, и есть несколько проблем с этим подходом (точка пересечения может оказаться внутри многоугольника), поэтому его все еще нужно улучшить, но я думаю, вы поняли идею. Другая (большая, в зависимости от того, что вы делаете) проблема в том, что это приближение , а не идеальный ответ.
Надеюсь, это поможет!

0 голосов
/ 26 июня 2020

Я не уверен, правильно ли я понял сценарий, но эффективным прямым вариантом использования было бы сначала использовать только квадратную ограничивающую рамку вашего круга, вычисление пересечения этого квадрата с вашим многоугольником выполняется очень быстро, очень намного быстрее, чем при использовании круга. Как только вы обнаружите пересечение этого квадрата и многоугольника, вам нужно подумать или написать, какая точность больше всего подходит для вашего сценария. Если вам нужна лучшая точность, чем в этом состоянии, вы можете go отсюда: от угла 90 ° вашего пересечения sqare вы рисуете линию 45 °, пока она не коснется вашего круга, в этой точке, там, где он касается, вы рисуете новый квадрат, но на этот раз квадрат внедряется в круг, пусть он работает сейчас, пока этот новый квадрат не пересечет многоугольник, как только он пересечется, у вас будет гарантированное пересечение круга. В зависимости от вашей необходимой точности вы можете просто поиграть вот так. Я не уверен, в чем твоя следующая проблема? Если это должна быть только обратная траектории кругов, чем просто обратная, я действительно не уверен, что мне здесь не хватает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...