1 背景描述
在一给定画布场景中,判断线段所经过网格。下图所示分别为线性递增和线性递减时,直线经过的网格路径。
octave:1> plot([1.5,8.7],[0.4,9.4]);
octave:2>grid on;
octave:3> plot([1.5,8.7],[9.4,0.4]);
octave:4>grid on;
2 方案详解
2.1 前述
在计算机图形学,生成直线的常见算法包括:数值微分法(DDA)、Bresenham法和中点画线法。乍眼一看,计算直线经过的网格区域类似反向算法实现,通过实验发现,对线段起始点坐标为[(2,4),(8,9)]、[(2.1,4),(8.9,9)]和[(2,0.4),(8,9.4)]进行计算较为有效,但当起始点横纵坐标不为整数,计算网格存在遗漏。
2.2 分析
回归问题本身,直线经过网格,就必然会与网格线产生交点,因此,可考虑此种策略:①先分别遍历直线与网格横纵交点(需考虑沿X轴和Y轴递增递减性质);②根据直线与网格横纵交点求出交点位置跨越的网格序列号,且存在以下规律:
1)当交点在横轴
{
p
1
u
p
=
{
p
1
c
o
l
=
c
e
i
l
(
p
1
x
)
p
1
r
o
w
=
p
1
y
+
1
上侧网格
p
1
d
o
w
n
=
{
p
1
c
o
l
=
c
e
i
l
(
p
1
x
)
p
1
r
o
w
=
p
1
y
下侧网格
\begin{cases} p1_{up} = \begin{cases} p1_{col} = ceil(p1_x) \\ p1_{row} = p1_y + 1 \end{cases} & \quad \text{上侧网格}\\ p1_{down} = \begin{cases} p1_{col} = ceil(p1_x) \\ p1_{row} = p1_y \end{cases} & \quad \text{下侧网格} \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧p1up={p1col=ceil(p1x)p1row=p1y+1p1down={p1col=ceil(p1x)p1row=p1y上侧网格下侧网格
2)当交点在纵轴
{
p
2
l
e
f
t
=
{
p
2
c
o
l
=
p
2
x
p
2
r
o
w
=
c
e
i
l
(
p
2
y
)
左侧网格
p
2
r
i
g
h
t
=
{
p
2
c
o
l
=
p
2
x
+
1
p
2
r
o
w
=
c
e
i
l
(
p
2
y
)
右侧网格
\begin{cases} p2_{left} = \begin{cases} p2_{col} = p2_x \\ p2_{row} = ceil(p2_y) \end{cases} & \quad \text{左侧网格}\\ p2_{right} = \begin{cases} p2_{col} = p2_x + 1 \\ p2_{row} = ceil(p2_y) \end{cases} & \quad \text{右侧网格} \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧p2left={p2col=p2xp2row=ceil(p2y)p2right={p2col=p2x+1p2row=ceil(p2y)左侧网格右侧网格
3 具体实现
/**
* @description: 计算线段与网格线的交点
* @param {type}
* @return:
*/
function intersectionCal(startPoint, endPoint) {
var dx = endPoint.x - startPoint.x;
var dy = endPoint.y - startPoint.y;
// 设置数据列表空间
var temList = new Array();
var gridList = new Array();
// 设置增量
var increX = dx/Math.abs(dx);
var increY = dy/Math.abs(dy);
// 直线方程:斜截式
var k = dy/dx;
var b = -k * startPoint.x + startPoint.y;
// 开始计算交点
for (var m=0; m<=Math.abs(dx)-1; m++) {
// 沿横轴方向遍历,既查询与网格轴轴的交点
var temPoint1 = new b2Vec2();
temPoint1.x = Math.ceil(startPoint.x) + increX * m;
temPoint1.y = k * temPoint1.x + b;
temList.push(temPoint1);
}
// 沿纵轴方向遍历,既查询与网格横轴的交点
for (var n=0; n<=Math.abs(dy); n++) {
var temPoint2 = new b2Vec2();
temPoint2.y = Math.ceil(startPoint.y) + increY * n;
temPoint2.x = (temPoint2.y - startPoint.y) / k + startPoint.x;
temList.push(temPoint2);
}
return temList;
}
/**
* @description: 计算线段经过网格行列号
* @param {type}
* @return:
*/
function lineGridCal(temList) {
// 存储网格路径
for (var j=0; j<(temList.length);j++) {
var temGridUp = new b2Vec2();
var temGridDown = new b2Vec2();
var temGridLeft = new b2Vec2();
var temGridRight = new b2Vec2();
// 横轴交点
if((temList[j].x !=Math.ceil(temList[j].x)) &&
(temList[j].y ==Math.ceil(temList[j].y))){
temGridUp.x = Math.ceil(temList[j].x);
temGridUp.y = Math.ceil(temList[j].y)+1;
temGridDown.x = Math.ceil(temList[j].x);
temGridDown.y = Math.ceil(temList[j].y);
gridList.push(temGridUp, temGridDown);
}
// 纵轴交点
else if((temList[j].y !=Math.ceil(temList[j].y)) &&
(temList[j].x ==Math.ceil(temList[j].x))) {
temGridLeft.x = Math.ceil(temList[j].x);
temGridLeft.y = Math.ceil(temList[j].y);
temGridRight.x = Math.ceil(temList[j].x)+1;
temGridRight.y = Math.ceil(temList[j].y);
gridList.push(temGridLeft, temGridRight);
}
};
return gridList;
}
}