Вот упрощенная форма моего намерения: Эскиз.
Я хочу, чтобы окно предупреждения стало красным только тогда, когда оранжевый круг достаточно близко (когда серый круг касается) линии:
Однако в настоящее время он может выдавать ложные срабатывания, т.е. прежде, чем достаточно близко:
Как правильно обнаруживать столкновения?
Верный. Я использую эту функциональность для чего-то вроде этого: youtube.com/watch?v=kzro0Jc70xI Посмотрите, как маршруты трассировки толкают друг друга.
Вы можете использовать Path.getIntersections()
или Path.intersects()
.
getIntersections()
не может получить пересечения на границах Path
(не учитывает strokeWidth
). Также неясно, как мы можем вычислить точку столкновения, см. FIXME Sketch
в моем отредактированном вопросе.
Ссылка скрыта в истории, см. Заголовок Редактировать: stackoverflow.com/revisions/53017518/2
Я придумал подход "проб и ошибок": Полный пример
// 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}
}
delta
).delta = delta / 2
и выполните проверку нажатия.delta = delta / 2
.delta
) слишком мал или процесс превышает максимальный предел итераций, разбейте и верните наилучшую возможную точку столкновения (точку для элемента, которая обеспечивает лучшую точку столкновения)Это решение основано на следующей проблеме: обнаружение попадания - недостаточно, потому что при обнаружении попадания может быть слишком поздно, а фактическое столкновение может произойти где-нибудь между предыдущей и текущей точками (на delta
) из-за интервалов обнаружения попаданий.
Этот метод проб и ошибок можно улучшить, начав попытки очень точного «предположения», основанного на правильном вычислении после обнаружения первого попадания.
Мы можем рассчитать 1
(фактическую точку столкновения) за один выстрел, используя 2
. Похоже, это ближайшая точка 3
к 4
:
Если расчет верен, для обнаружения точки столкновения будет достаточно только двух циклов: 1-й предназначен для обнаружения попадания (как показано #2
), а 2-й - для проверки. Если это неверно, то все возьмет на себя метод проб и ошибок.
Вместо того, чтобы пытаться найти прямые пересечения, вы можете вместо этого найти ближайшая точка на пути с позиции движущейся точки.
Если расстояние между точкой и этой ближайшей точкой падает ниже порогового значения (равного strokeWidth
Path
и радиусу точки), вы можете считать это действительным попаданием.
Хотя есть загвоздка: свойства 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.
Если я правильно понял, вы хотите выполнить обнаружение пересечения на путях расширенный (расширенный / смещенный) с расширением, равным количеству
strokeWidth
. По этому изложено здесь ведутся активные обсуждения.