HTML5 Canvas: разделение / вычисление строк - PullRequest
1 голос
/ 05 марта 2011

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

У меня есть HTML-холст, где пользователи могут рисовать линии с помощью мыши и очень простого moveTo ()и lineTo () функции.Когда пользователь закончил, я сохраняю координаты в MongoDB.Когда пользователь снова попадает на страницу позже, я хочу отобразить его рисунок, НО я не хочу загружать все изображение сразу со всеми сохраненными координатами, я хочу вернуть его в виде плиток (для повышения производительности путем кэширования каждой плитки).

Плитки имеют размер 200x200 пикселей (фиксированные смещения и ширина, начиная с 0 -> 200-> 400 -> ...).Теперь, когда пользователь рисует линию от, скажем, 50,50 (х / у) до 250,250 (х / у), в каждой ограничительной рамке (плитке) есть только одна точка.Мне нужно разделить линии и рассчитать начальную и конечную точки каждой линии в каждой ограничительной рамке (плитке).В противном случае я не могу нарисовать изображение частично (в плитках).Это становится еще сложнее, когда одна линия пересекает несколько ограничивающих рамок (плиток).Например: 100 100 (x / y) -> -1234, -300 (x / y).Линии могут идти из любой точки (+/-) в ЛЮБОЕ направление для ЛЮБОГО расстояния.

Конечно, я посмотрел на старый добрый алгоритм Брезенхэма, и он работал - частично, но он кажется самым длинным и наиболее ресурсоемким.голодное решение для меня.

Итак, причина, по которой я здесь, заключается в том, что я надеюсь, что кто-то может указать мне правильное направление с помощью (возможно) другого подхода вычисления начальных / конечных точек моих линий для каждой границыbox.

Примеры кода приветствуются в JavaScript или PHP.

Спасибо, что прочитали и подумали об этом :)

Ответы [ 2 ]

5 голосов
/ 08 марта 2011

tl; др: Используйте самолеты, математика объяснена ниже. Внизу есть пример холста.

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

Planes

Вы можете представить свою коробку как набор из четырех геометрических плоскостей . Каждая плоскость имеет нормаль или вектор длиной один, указывающий, какое направление является «фронтом» плоскости. Нормали для плоскостей, составляющих стороны вашей клетки, будут:

   top = {x:  0, y: -1};
bottom = {x:  0, y:  1};
  left = {x: -1, y:  0};
 right = {x:  1, y:  0};

Для данной точки на плоскости плоскость имеет уравнение:

distance = (normal.x * point.x) + (normal.y * point.y)

Вы можете использовать это уравнение для вычисления расстояния до плоскости. В этом случае вы знаете, что верхний левый угол вашего поля (скажем, x равен 10, а y равен 100) находится в верхней плоскости, поэтому вы можете сделать:

distance = (0 * 10) + (-1 * 100)
distance = -100

Проверка точки на плоскости

Получив расстояние, вы можете повторно использовать уравнение, чтобы проверить, где находится любая точка относительно плоскости. Для случайной точки p (где x равно -50, а y равно 90), вы можете сделать:

result = (normal.x * p.x) + (normal.y * p.y) - distance
result = (0 * -50) + (-1 * 90) - (-100)
result = 0 + (-90) - (-100)
result = -90 + 100
result = 10

Возможны два результата:

if (result >= 0) {
    // point is in front of the plane, or coplanar.
    // zero means it is coplanar, but we don't need to distinguish.
} else {
    // point is behind the plane
}

Проверка линии относительно плоскости

Вы можете проверить обе конечные точки линии от a до b следующим образом:

result1 = (normal.x * a.x) + (normal.y * a.y) - distance
result2 = (normal.x * b.x) + (normal.y * b.y) - distance

Возможны четыре результата:

if (result1 >= 0 && result2 >= 0) {
  // the line is completely in front of the plane
} else if (result1 < 0 && result2 < 0) {
  // the line is completely behind the plane
} else if (result1 >= 0 && result2 < 0) {
  // a is in front, but b is behind, line is entering the plane
} else if (result1 < 0 && result2 >= 0) {
  // a is behind, but b is in front, line is exiting the plane
}

Когда линия пересекает плоскость, вы хотите найти точку пересечения. Это помогает думать о линии в векторном выражении :

a + t * (b - a)

Если t == 0, вы находитесь в начале строки, а t == 1 - конец строки. В этом контексте вы можете рассчитать точку пересечения как:

time = result1 / (result1 - result2)

А точка пересечения как:

hit.x = a.x + (b.x - a.x) * time
hit.y = a.y + (b.y - a.y) * time

Проверка строки на поле

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

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

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

/**
 * Find the points where a line intersects a box.
 *
 * @param a Start point for the line.
 * @param b End point for the line.
 * @param tl Top left of the box.
 * @param br Bottom right of the box.
 * @return Object {nearTime, farTime, nearHit, farHit}, or false.
 */
function intersectLineBox(a, b, tl, br) {
  var nearestTime = -Infinity;
  var furthestTime = Infinity;
  var planes = [
    {nx:  0, ny: -1, dist: -tl.y},  // top
    {nx:  0, ny:  1, dist:  br.y},  // bottom
    {nx: -1, ny:  0, dist: -tl.x},  // left
    {nx:  1, ny:  0, dist:  br.x}   // right
  ];
  for (var i = 0; i < 4; ++i) {
    var plane = planes[i];
    var nearDist = (plane.nx * a.x + plane.ny * a.y) - plane.dist;
    var farDist = (plane.nx * b.x + plane.ny * b.y) - plane.dist;
    if (nearDist >= 0 && farDist >= 0) {
      // both are in front of the plane, line doesn't hit box
      return false;
    } else if (nearDist < 0 && farDist < 0) {
      // both are behind the plane
      continue;
    } else {
      var time = nearDist / (nearDist - farDist);
      if (nearDist >= farDist) {
        // entering the plane
        if (time > nearestTime) {
          nearestTime = time;
        }
      } else {
        // exiting the plane
        if (time < furthestTime) {
          furthestTime = time;
        }
      }
    }
  }
  if (furthestTime < nearestTime) {
    return false;
  }
  return {
    nearTime: nearestTime,
    farTime: furthestTime,
    nearHit: {
      x: a.x + (b.x - a.x) * nearestTime,
      y: a.y + (b.y - a.y) * nearestTime
    },
    farHit: {
      x: a.x + (b.x - a.x) * furthestTime,
      y: a.y + (b.y - a.y) * furthestTime
    }
  };
}

Если это все еще слишком медленно, вы также можете сделать широкофазный отбор, разделив мир на большие риты и назначив линии этим ритам. Если ваша линия и ячейка не совпадают, они не сталкиваются.

Я загрузил пример холста этого .

1 голос
/ 05 марта 2011

Похоже, вам нужно выяснить, в какой точке каждая линия пересекается с границами каждой плитки.

Проверьте ответ на этот вопрос: Есть ли простой способ обнаружить пересечения отрезков?

Ответы не предоставляют код, но не должно быть слишком сложно преобразовать уравнения в PHP или Javascript ...


EDIT:

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

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

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

...