Пиксельные координаты на алмазе
Я получил изображение с несколькими алмазами, расположенными рядом друг с другом, как на изображении ниже
![diamond coordinates]()
Единственными координатами, которые я знаю на изображении, являются верхние углы (зеленый текст).
Когда я нажимаю на изображение, я получаю координаты этой точки, но я не могу получить тот алмаз, на котором я нахожусь.
Например, я нажимаю на красную точку, откуда я знаю, что x: 260, y: 179 = верхний бриллиант?
И синий принадлежит слева? и т.д.
Большое спасибо за вашу помощь.
EDIT:
Я, наконец, использовал Canvas, но я думаю, что SVG работал бы так же хорошо, как мне было нужно.
Ответы
Ответ 1
Я вижу два возможных подхода: прямо проверяем, находится ли точка внутри алмаза и использует аффинные преобразования. Я опишу оба.
Проверка положения прямой точки
Чтобы определить, находится ли точка внутри алмаза, вы должны проверить его отклонение от средней точки алмаза. Вы должны отложить отклонения 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/
Аффинные преобразования
Используя аффинные преобразования, вы можете изменить угловые координаты вашего верхнего алмаза на (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/
В ретроспективе это была, вероятно, слишком большая работа для простой геометрической фигуры, такой как алмаз.
Ответ 2
По существу, у вас есть, возможно, изометрический вид из 4 плиток (на основе вашего комментария о бриллиантах, появляющихся в виде трапеций).
Одним из быстрых способов сделать это является создание двух линий, которые параллельны "осям" "бриллиантов" (но все же пересекаются друг с другом... это тоже важно). В приведенном примере изображение будет означать две линии, которые вертикальны друг к другу, но повернуты на 45 градусов. В изометрическом случае линии не будут вертикальными друг к другу, а под другим углом в зависимости от вашего вида.
Как только у вас есть эти две строки, вы можете создать функцию "hitTest()", которая будет принимать координаты точки, которая была нажата, и будет оценивать два уравнения линии. Вас действительно не интересует фактическое число, возвращаемое линейными уравнениями, а только знаки. Знак показывает, на какой стороне линии находится ваша точка.
Это означает, что ваши "бриллианты" будут соответствовать этим знаковым парам (один знак для каждого линейного уравнения) [-, -], [-, +], [+, -], [+, +].
(Обратите внимание, что знак зависит от того, как была определена линия, другими словами, для данной точки P знак из некоторого линейного уравнения (L) будет отличаться, если строка определена как "слева" справа "или" справа налево ", или, в более общем случае, знак будет обратным для обратных направлений.)
Немного больше информации о форме нужного уравнения линии можно получить отсюда
Ответ 3
Используя матрицы, вы можете получить быструю формулу, для которой выбран алмаз.
Вы хотите преобразование из (x,y)
в "алмазное пространство". То есть, система координат, где (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)
Ответ 4
Все, что вам нужно - просто stady, что такое roration. Вот ссылка: http://en.wikipedia.org/wiki/Rotation_(mathematics)
Вы должны повернуть свою точку, чтобы сделать стороны квадратов в parrallel с координатной сеткой. Точка ротации должна составлять 1 угол диммеров, который будет угрожать как 0,0 алмаз. После rotaion вы можете легко определить, сколько daimond вы указываете от 0,0