Вопрос по math – пиксельные координаты на ромбике

10

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

diamond coordinates

Единственные координаты, которые я знаю на изображении, - это верхние углы (зеленый текст).
Когда я нажимаю на изображение, я получаю координаты этой точки, но я не могу определить, на каком алмазе я нахожусь.
Например, я нажимаю на красную точку, откуда мне знать, что x: 260, y: 179 = верхний ромб?
А синий принадлежит левому? так далее...

Большое спасибо за Вашу помощь.

РЕДАКТИРОВАТЬ:
Я наконец использовал Canvas, но я думаю, что SVG сработал бы так же, как и мне.

Извините, потому что я делаю это в jquery, но true не касается тех. Я редактировал Shadowbob
И какое это имеет отношение к Javascript и jQuery? Это просто математика. MaxArt
Ваше изображение помещает зеленую точку в нижний ромб, но координаты соответствуют правому ромбу. Кроме того, координаты синей точки размещают ее ближе к краю нижнего ромба. Markus Jarderot

Ваш Ответ

4   ответа
4

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

Вы хотите преобразование из(x,y) в "Diamond-Space". То есть система координат, где(0,0) это верхний бриллиант,(1,0) тот, что внизу справа, и(0,1) ниже слева.

A * x = y

гдеA это преобразование,x это координаты изображения, иy это ромб-координаты. Разобраться с переводом ((0,0) не будучи одной и той же точкой в обоих пространствах), вы можете добавить еще одну строку к векторам, которая всегда1.

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

[ a b dx ]   [ 225 337 113 ]   [ 0 1 0 ]
[ c d dy ] * [   2 114 114 ] = [ 0 0 1 ]
[ 0 0  1 ]   [   1   1   1 ]   [ 1 1 1 ]
                 ^   ^   ^-left  ^-^-^--- new coordinates for each point
                 |   '-right
                 '-top diamond

Чтобы найти коэффициенты в первой матрице, вам нужно разделить на вторую матрицу (или умножить на обратную).

[ a b dx ]   [ 0 1 0 ]   [ 225 337 113 ]^-1
[ c d dy ] = [ 0 0 1 ] * [   2 114 114 ]
[ 0 0  1 ]   [ 1 1 1 ]   [   1   1   1 ]

Результат:

[ a b dx ]   [  (1/224) (1/224) (-227/224) ]
[ c d dy ] = [ (-1/224) (1/224)  (223/224) ]
[ 0 0  1 ]   [   0       0          1      ]

Чтобы вставить это в программный код:

function getDiamond(x, y) {
    return [(x + y - 227) / 224, (-x + y + 223) / 224];
}

Пример:

> getDiamond(260,179); // red
[0.9464285714285714, 0.6339285714285714]
> getDiamond(250,230); // green
[1.1294642857142858, 0.90625]
> getDiamond(189,250); // blue
[0.9464285714285714, 1.2678571428571428]
> getDiamond(420,230); // yellow
[1.8883928571428572, 0.14732142857142858]

Если вы посмотрите на целочисленные части, вы увидите, какому ромбу соответствует координата. Красный на(0.94, 0.63) который находится в регионе(0,0) довольно близко к краю(1,0).

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

Если вы сделаете вычисления символически, вы получите следующее:

[ a b dx ]   [ (y2 - y0)/M  -(x2 - x0)/M  -(x0*y2 - y0*x2)/M ]
[ c d dy ] = [-(y1 - y0)/M   (x1 - x0)/M   (x0*y1 - y0*x1)/M ]
[ 0 0  1 ]   [        0             0                   1    ]

гдеM = x1*y2 - x2*y1 - y0*x1 + y0*x2 + x0*y1 - x0*y2.

Точка 0 - это положение верхнего бриллианта, точка 1 - это положение правого бриллианта, а точка 2 - это положение левого алмаза.

Вот функция для расчета этого:

function DiamondMaker(topx,topy,  leftx,lefty,  rightx,righty)
{
    var M = topx*lefty - topx*righty +
            leftx*righty - leftx*topy +
            rightx*topy - rightx*lefty;
    var a  = -(topy - righty)/M;
    var b  =  (topx - rightx)/M;
    var dx = -(topx*righty - topy*rightx)/M;
    var c  =  (topy - lefty)/M;
    var d  = -(topx - leftx)/M;
    var dy =  (topx*lefty - topy*leftx)/M;
    return function(x, y) {
        return [a * x + b * y + dx, c * x + d * y + dy];
    };
}

var getDiamond = DiamondMaker(225,2,  337,114,  113,114);
// (same example as before)
Error: User Rate Limit Exceeded Shadowbob
4

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

Один из быстрых способов сделать это - создать две линии, параллельные «осям»; из "алмазов" (но все же пересекаются друг с другом ... это тоже важно). В приведенном примере изображения это означало бы две линии, которые расположены вертикально друг к другу, но повернуты на 45 градусов. В изометрическом случае линии будут не вертикально друг к другу, а под некоторым другим углом, в зависимости от вашего вида.

Если у вас есть эти две строки, вы можете создать & quot; hitTest () & quot; функция, которая будет принимать координаты точки, по которой щелкнули, и будет оценивать два линейных уравнения. Вы на самом деле не интересуетесь фактическим числом, возвращаемым линейными уравнениями, а только знаками. Знак показывает, на какой стороне линии находится ваша точка.

Это означает, что ваши "алмазы" будет соответствовать этим парам знаков (по одному знаку для каждого уравнения строки) [-, -], [-, +], [+, -], [+, +].

(Обратите внимание, что знак зависит от того, как была определена линия, другими словами, для данной точки P знак из некоторого линейного уравнения (L) будет отличаться, если линия была определена как бегущая "слева направо" или "справа налево" или, в более общем смысле, знак будет обратным для взаимных указаний.)

Немного больше информации о форме необходимого уравнения линии можно получитьотсюда

1

http://en.wikipedia.org/wiki/Rotation_(mathematics)

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

Error: User Rate Limit Exceeded Shadowbob
Error: User Rate Limit Exceeded
9

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

Direct point position check

Чтобы определить, находится ли точка внутри алмаза, вы должны проверить ее отклонение от средней точки алмаза. Вы должны поместить отклонения X и Y пропорционально экстентам X и Y алмаза, вы получите два фактора. Для всех точек внутри ромба сумма значений по модулю для этих факторов меньше или равна 1. В коде это выглядит так:

var dx = Math.abs(coords[0] - middle[0]);
var dy = Math.abs(coords[1] - middle[1]);
if (dx / size[0] + dy / size[1] <= 1)
  alert("Inside diamond");
else
  alert("Outside diamond");

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

Рабочий пример:http://jsfiddle.net/z98hr/

Affine transformations

С помощьюаффинные преобразования Вы можете изменить угловые координаты вашего верхнего ромба на (0,0), (1,0), (0,1) и (1,1). Если затем применить то же преобразование к точке, которую нужно проверить, определить, к какому алмазу он принадлежит, становится тривиальным.

Сначала вам понадобится вектор перевода, чтобы переместить точку (225,2) в начало координат. Допустим, у вас есть четыре координаты, определяющие ваш верхний ромб (координаты слева и справа, координаты сверху и снизу):

var topDiamond = [[113, 2], [337, 227]];

Тогдавектор перевода чтобы переместить верхнюю точку ромба в нулевую координату было бы:

var translationVector = [-(topDiamond[0][0] + topDiamond[1][0]) / 2,
                         -topDiamond[0][1]];

Вы можете применить его к исходным координатам следующим образом:

function add(vector1, vector2)
{
  return [vector1[0] + vector2[0], vector1[1] + vector2[1]];
}
topDiamond = [add(topDiamond[0], translationVector),
              add(topDiamond[1], translationVector)];

Тогда вам понадобитсяматрица вращения:

var angle = -Math.atan2(topDiamond[1][1] - topDiamond[0][1],
                        topDiamond[1][0] - topDiamond[0][0]);
var rotMatrix = [[Math.cos(angle), -Math.sin(angle)],
                 [Math.sin(angle), Math.cos(angle)]];

После умножения с этой матрицей точки (225,2) и (337,114,5) выравниваются по оси X. Но то, что у вас сейчас есть трапеция, теперь вам нужнопреобразование горизонтального сдвига чтобы выровнять другую сторону ромба по оси Y:

function multiply(matrix, vector)
{
  return [matrix[0][0] * vector[0] + matrix[0][1] * vector[1],
          matrix[1][0] * vector[0] + matrix[1][1] * vector[1]];
}
var point = [topDiamond[0][0], (topDiamond[0][1] + topDiamond[1][1]) / 2];
point = multiply(rotMatrix, point);
var shearMatrix = [[1, -point[0] / point[1]], [0, 1]];

После умножения на эту матрицу у вас есть прямоугольник. Теперь вам нужно толькомасштабная матрица чтобы убедиться, что координаты X и Y углов имеют значения 0 и 1:

point = multiply(shearMatrix, point);
var point2 = [topDiamond[1][0], (topDiamond[0][1] + topDiamond[1][1]) / 2];
point2 = multiply(rotMatrix, point2);
point2 = multiply(shearMatrix, point2);
var scaleMatrix = [[1/point2[0], 0], [0, 1/point[1]]];

И вот, теперь вы можете применить эти преобразования к любой точке:

alert(
  multiply(scaleMatrix,
    multiply(shearMatrix,
      multiply(rotMatrix,
        add(translationVector, [260, 179])
      )
    )
  )
);

Это дает вам0.94,0.63 - оба значения в(0..1) диапазон означает, что это верхний бриллиант. С[420,230] в качестве ввода вы получаете1.88,0.14 - X в(1..2) диапазон и Y в0..1 Диапазон означает правильный бриллиант. И так далее.

Рабочий пример:http://jsfiddle.net/FzWHe/

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

Error: User Rate Limit Exceeded Shadowbob
Error: User Rate Limit Exceeded Shadowbob

Похожие вопросы