Как выполнить обнаружение столкновений между штрихами

Вот упрощенная форма моего намерения: Эскиз.

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

Как выполнить обнаружение столкновений между штрихами

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

Как выполнить обнаружение столкновений между штрихами

Как правильно обнаруживать столкновения?

Если я правильно понял, вы хотите выполнить обнаружение пересечения на путях расширенный (расширенный / смещенный) с расширением, равным количеству strokeWidth. По этому изложено здесь ведутся активные обсуждения.

nicholaswmin 27.10.2018 23:26

Верный. Я использую эту функциональность для чего-то вроде этого: youtube.com/watch?v=kzro0Jc70xI Посмотрите, как маршруты трассировки толкают друг друга.

ceremcem 27.10.2018 23:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
343
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать Path.getIntersections() или Path.intersects().

getIntersections() не может получить пересечения на границах Path (не учитывает strokeWidth). Также неясно, как мы можем вычислить точку столкновения, см. FIXME Sketch в моем отредактированном вопросе.
ceremcem 27.10.2018 15:21

Ссылка скрыта в истории, см. Заголовок Редактировать: stackoverflow.com/revisions/53017518/2

ceremcem 30.10.2018 12:41
Ответ принят как подходящий

Я придумал подход "проб и ошибок": Полный пример

// Returns item.position at the moment of collision
// and the collided object.
function collisionTest(item, curr){
    var prev = item.position;
    var point = curr.clone();
    var hit = null;
    var tolerance = 45;
    var _error = 0.01;
    var firstPass = true;
    var _stopSearching = false;
    var _step = ((curr - prev) / 2).clone();
    var _acceptable_iter_count = 16;
    var _max_iter_count = 100;

    var i; 
    for (i = 0; i < _max_iter_count; i++){
        var _hit = project.hitTest(point, {
            fill: true, 
            stroke: true, 
            segments: true, 
            tolerance: tolerance,
            match: function(hit){
                if (hit.item && hit.item.data && hit.item.data.type === "pointer"){
                    return false
                }
                return true
            }
        })

        if (_hit){
            hit = _hit;
            // hit has happened between prev and curr points 
            // step half way backward
            point -= _step 
            if (_step.length < _error){
                // step is too small, stop trials as soon 
                // as no hit can be found
                _stopSearching = true;
            }
        } else {
            if (firstPass || _stopSearching){
                break;
            } else  {
                // not hit found, but we should 
                // step forward to search for a more 
                // accurate collision point 
                point += _step  
            }
        }
        firstPass = false;
        if (_step.length >= _error * 2 ) {
            _step /= 2
        } else {
            // minimum step length must be error/2
            // in order to save loop count 
            _step = _step.normalize(_error * 0.8)
        }
    }
    if (i > _acceptable_iter_count){
        console.info("found at " + i + ". iteration, step: ", _step.length)
    }
    return {point, hit}
}

Алгоритм

  1. Выполните хит-тест.
  2. Если обнаружено совпадение, учтите, что фактическое столкновение может произойти где-нибудь между предыдущей позицией и текущей позицией (на delta).
  3. Сделайте шаг назад с помощью delta = delta / 2 и выполните проверку нажатия.
  4. Если все еще попадает, повторите шаг 3.
  5. Если попадание не может быть найдено, сделайте шаг вперед на delta = delta / 2.
  6. Если по-прежнему не попадает, повторите шаг 5.
  7. В случае попадания повторите шаг 3, если шаг 8 позволяет это сделать.
  8. Если размер шага (delta) слишком мал или процесс превышает максимальный предел итераций, разбейте и верните наилучшую возможную точку столкновения (точку для элемента, которая обеспечивает лучшую точку столкновения)

Дальнейшие улучшения

Это решение основано на следующей проблеме: обнаружение попадания - недостаточно, потому что при обнаружении попадания может быть слишком поздно, а фактическое столкновение может произойти где-нибудь между предыдущей и текущей точками (на delta) из-за интервалов обнаружения попаданий.

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

Мы можем рассчитать 1 (фактическую точку столкновения) за один выстрел, используя 2. Похоже, это ближайшая точка 3 к 4:

Если расчет верен, для обнаружения точки столкновения будет достаточно только двух циклов: 1-й предназначен для обнаружения попадания (как показано #2), а 2-й - для проверки. Если это неверно, то все возьмет на себя метод проб и ошибок.

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

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

Хотя есть загвоздка: свойства strokeJoin и strokeCap вашего пути должны быть установлен на round, так как это приводит к симметричной ширине штриха вокруг углы и окончания, поэтому избегайте ложноотрицательных результатов.

Вот пример:

var path = new Path({
    segments: [
        new Point(100, 200),
        new Point(200, 100),
        new Point(300, 300)
    ],
    strokeWidth: 100,
    // This is important.
    strokeJoin: 'round',
    // This is important.
    strokeCap: 'round',
    strokeColor: 'red',
    selected: true,
    position: view.center
})

var dot = new Path.Circle({
    center: view.center,
    radius: 10,
    fillColor: 'red'
})

function onMouseMove(event) {
    dot.position = event.point

    var nearestPoint = path.getNearestPoint(dot.position)
    var distance = dot.position.subtract(nearestPoint)

    if (distance.length < (dot.bounds.width / 2) + (path.strokeWidth / 2)) {
        path.strokeColor = 'blue'
    } else {
        path.strokeColor = 'red'
    }
}

и Эскиз.


При этом лучший способ сделать это - дождаться (или реализовать самостоятельно) Расширение / смещение пути и вместо этого найти пересечение на расширенном пути, поскольку это позволит вам использовать любые настройки strokeJoin / strokeCap.

В идеале расширение / смещение должно выполняться с теми же свойствами strokeJoin / strokeCap, что и Path.

Есть Библиотека JS, которая надежно выполняет смещение полигонов, который может вам пригодиться.

Смещение пути кажется многообещающим в этой теме. На самом деле я пытаюсь точно сместить путь и получить пересечения. С другой стороны, определение попадания - это только половина решения, потому что может быть слишком поздно, когда мы обнаружим попадание (точная точка попадания может быть где угодно на event.delta). Вот почему нам нужна функция, которая вычисляет точную точку столкновения после hit.

ceremcem 28.10.2018 10:34

Другие вопросы по теме