我得到了一张图片,上面有几颗钻石并排放置,如下图所示
我在图像上知道的唯一坐标是顶角(绿色文本)。
当我点击图像时,我得到了该点的坐标,但我无法得到我所在的钻石。
例如我点击红点,我怎么知道 x:260, y:179 = top diamond ?
蓝色属于左边?ETC...
非常感谢您的帮助。
编辑:
我终于使用了 Canvas,但我认为 SVG 对于我需要做的事情同样有效。
我得到了一张图片,上面有几颗钻石并排放置,如下图所示
我在图像上知道的唯一坐标是顶角(绿色文本)。
当我点击图像时,我得到了该点的坐标,但我无法得到我所在的钻石。
例如我点击红点,我怎么知道 x:260, y:179 = top diamond ?
蓝色属于左边?ETC...
非常感谢您的帮助。
编辑:
我终于使用了 Canvas,但我认为 SVG 对于我需要做的事情同样有效。
我看到了两种可能的方法:直接检查一个点是否在钻石内并使用仿射变换。我将描述两者。
要确定一个点是否在钻石内部,您必须检查它与钻石中点的偏差。您必须将 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/
在回顾展中,这对于像钻石这样简单的几何图形来说可能工作量太大了。
本质上,您所拥有的可能是 4 个瓷砖的等距视图(基于您对显示为梯形的钻石的评论)。
一种快速的方法是创建两条与“钻石”的“轴”平行的线(但仍然相互交叉......这也很重要)。在给出的示例图像中,这意味着两条线彼此垂直但旋转了 45 度。在等轴测图的情况下,这些线不会相互垂直,而是根据您的视图以其他角度。
一旦你有了这两条线,你就可以创建一个“hitTest()”函数,它将获取被点击点的坐标,并计算两条线方程。您对线方程返回的实际数字并不真正感兴趣,而只是对符号感兴趣。该标志向您显示您的点位于直线的哪一侧。
这意味着您的“钻石”将对应于这些符号对(每个线方程一个符号)[-,-], [-,+], [+,-], [+,+]。
(请注意,符号取决于线的定义方式,换句话说,对于给定的点 P,如果线被定义为“从左到右”运行,则某个线方程 (L) 的符号将有所不同或“从右到左”,或更一般地,符号将是相反方向的相反方向。)
可以从这里获得有关您需要的线方程形式的更多信息
使用矩阵,您可以推导出选择钻石的快速公式。
您想要从(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)
。
注意。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)
所有你需要的 - 只是停留什么是 roration。这是链接:http ://en.wikipedia.org/wiki/Rotation_(mathematics )
您应该旋转您的点以使正方形的边与坐标网格平行。旋转点应该是您将威胁为 0,0 钻石的钻石的 1 个角。旋转后,您可以轻松定义从 0,0 指向的 daimond 数量