最近完整一个图像处理工具的第二期,其中将原来的模块的连接直线,升级为贝赛尔曲线(关键是看起来比较上档次)。随之问题就来了,如果检测鼠标在点击时,是否选中它了呢。直线好算,利用中学知识就行,那贝赛尔曲线呢?
如下图,是采用三阶贝赛尔曲线,利用GDI+直接画的:
什么是贝赛尔曲线,我就不解释了,关于它资料很多
这是三阶贝赛尔方程式:
inline double BezierFormula(int p0, int p1, int p2, int p3, double t){
double r = 1.0 - t;
return p0*r*r*r + 3*p1*t*r*r + 3*p2*t*t*r + p3*t*t*t;
}
检测算法如下:
//
// check point is where on specified bezier curve. half-find
// @param p0 start poing
// @param p1 contrl point
// @param p2 contrl point
// @param p3 end point
// @NOTE:
// p0(1-t)³+3p1t(1-t)²+3p2t²(1-t)+p3t³ = p
// where t in range of [0.0, 1.0]
// reference below:
// http://baike.baidu.com/link?url=n7iJBnCBzhkLtffffPQO1Vsx0JFuKN3qRWi4WDXpSR4VJ_y1XN4F6G0xjcpOUF_X
//
// @return
bool IsPointOnBezier(const Point& p, const Point& p0, const Point& p1,
const Point& p2, const Point& p3){
double left = 0.0;
double right = 1.0;
const int kTorlance = 3;
const double kGranularity = 0.000001;
int y = INT_MAX;
Rect bounds(MakeRect(p0, p3));
bounds.Inflate(kTorlance, kTorlance);
if(!bounds.Contains(p)){
return false;
}
while(abs(left - right) > kGranularity){
double middle = (left + right) * 0.5;
double x = BezierFormula(p0.X, p1.X, p2.X, p3.X, middle);
if(abs(x - p.X) < 0.5){
y = static_cast<int>(BezierFormula(p0.Y, p1.Y, p2.Y, p3.Y, middle) + 0.5f);
break;
}
double lr = BezierFormula(p0.X, p1.X, p2.X, p3.X, left) - p.X;
double mr = BezierFormula(p0.X, p1.X, p2.X, p3.X, middle) - p.X;
if(lr * mr < 0)
right = middle;
else
left = middle;
}
return abs(y - p.Y) <= kTorlance;
}
在找查区间的选择上,要通过符号来判断,不然无处理结束点在起如点左边的情形。
算法