可以使用整数算术和基本的加、减、乘法运算来检查一个点是否在扇形内。
要使一个点位于圆形扇区内,它必须通过以下测试:
- 它必须相对于扇形的起始“臂”逆时针定位。
- 它必须相对于扇形的结束臂顺时针定位。
它必须比扇形的半径更靠近圆心。
顺时针测试
要测试向量v2是否顺时针旋转到另一个向量v1,需执行以下步骤:
找到v1的逆时针法向量。法向量与原始向量成90度角,这可以通过简单的方法进行计算:如果v1=(x1,y1),则逆时针法向量为n1=(-y1,x1)
。
找到v2在法向量上的投影大小。这可以通过计算v2和法向量的点积来完成。
projection = v2.x*n1.x + v2.y*n1.y
如果投影是正数,则v2相对于v1逆时针定位。否则,v2顺时针定位于v1。
下面是一个逆时针旋转的例子:
这是一个顺时针旋转的例子:
这些步骤可以结合起来:
function areClockwise(v1, v2) {
return -v1.x*v2.y + v1.y*v2.x > 0;
}
半径测试
半径测试很简单。只需检查点离圆心的距离是否小于所需半径即可。为了避免计算平方根,我们可以将距离的平方与半径的平方进行比较。
function isWithinRadius(v, radiusSquared) {
return v.x*v.x + v.y*v.y <= radiusSquared;
}
组合起来
完整的部门测试大致如下:
function isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared) {
var relPoint = {
x: point.x - center.x,
y: point.y - center.y
};
return !areClockwise(sectorStart, relPoint) &&
areClockwise(sectorEnd, relPoint) &&
isWithinRadius(relPoint, radiusSquared);
}
以下示例页面在数千个点上演示了这一点。您可以在以下网址自行尝试: http://jsbin.com/oriyes/8/edit.
源代码示例
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<style>
.canvas {
position: absolute;
background: #f4f4f4;
border: 8px solid #f4f4f4;
width: 400px;
height: 400px;
}
.dot {
position: absolute;
font: 16px Arial;
}
.out { color: #ddd; }
.in { color: #00dd44; }
</style>
<script>
function isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared) {
var relPoint = {
x: point.x - center.x,
y: point.y - center.y
};
return !areClockwise(sectorStart, relPoint) &&
areClockwise(sectorEnd, relPoint) &&
isWithinRadius(relPoint, radiusSquared);
}
function areClockwise(v1, v2) {
return -v1.x*v2.y + v1.y*v2.x > 0;
}
function isWithinRadius(v, radiusSquared) {
return v.x*v.x + v.y*v.y <= radiusSquared;
}
$(function() {
var $canvas = $("#canvas");
var canvasSize = 400;
var count = 4000;
var center = { x: canvasSize / 2, y: canvasSize / 2 };
var sectorStart = { x: 4, y: 1 };
var sectorEnd = { x: 1, y: 4 };
var radiusSquared = canvasSize * canvasSize / 4;
for (var i = 0; i < count; ++i) {
var point = {
x: Math.random() * canvasSize,
y: Math.random() * canvasSize
};
var isInside = isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared);
var $point = $("<div class='dot'></div>")
.css({
left: point.x - 3,
top: canvasSize - point.y - 8 })
.html("•")
.addClass(isInside ? "in" : "out")
.appendTo($canvas);
}
});
</script>
</head>
<body>
<div id="canvas" class="canvas"></div>
</body>
</html>
注释、注意事项和限制
你必须用向量的术语来指定扇区的边界。例如,上面的截图显示一个从向量(4,1)到(1,4)延伸的扇区。
如果你的扇区是用其他术语指定的,例如角度,那么你需要先将它转换为向量,例如使用tan()
函数。幸运的是,你只需要这样做一次。
此处的逻辑适用于内角小于180度的扇区。如果你的扇区可以跨越更大的角度,则需要进行修改。
此外,该代码假定你知道扇区的边界向量中哪个是“起点”,哪个是“终点”。如果不知道,可以对它们运行areClockwise()
来找出。
请注意,虽然所有这些都可以使用整数算术完成,但半径和顺时针测试都使用了较大范围的数字,因为要平方x和y,并将它们相乘。请确保使用具有足够位数以容纳结果的整数。