问题描述
我想验证一个点是否是由点p0,p1和p2定义的二次贝塞尔曲线的一部分.
这是我的函数,用于获取具有特定t的曲线中的Point:
public static final Point quadratic (Point p0, Point p1, Point p2, double t) {
double x = Math.pow(1-t, 2) * p0.x + 2 * (1-t) * t * p1.x + Math.pow(t, 2) * p2.x;
double y = Math.pow(1-t, 2) * p0.y + 2 * (1-t) * t * p1.y + Math.pow(t, 2) * p2.y;
return new Point((int)x, (int)y);
}
考虑得到二次曲线中的点B(t)如下:
我应该通过获取该点的t值并将其与使用该t参数获得的Point进行比较,来验证点P是否属于曲线,但是在Java中,变量的精度存在问题. /p>
我要验证的功能如下:
public static final boolean belongsQuadratic (Point p, Point p0, Point p1, Point p2) {
double[] tx = obtainTs(p.x, p0, p1, p2);
double[] ty = obtainTs(p.y, p0, p1, p2);
if (tx[0] >= 0) {
if ((tx[0] >= ty[0] - ERROR && tx[0] <= ty[0] + ERROR) || (tx[0] >= ty[1] - ERROR && tx[0] <= ty[1] + ERROR)) {
return true;
}
}
if (tx[1] >= 0) {
if ((tx[1] >= ty[0] - ERROR && tx[1] <= ty[0] + ERROR) || (tx[1] >= ty[1] - ERROR && tx[1] <= ty[1] + ERROR)) {
return true;
}
}
return false;
}
public static double[] obtainTs (int comp, Point p0, Point p1, Point p2) {
double a = p0.x - 2*p1.x + p2.x;
double b = 2*p1.x - 2*p0.x ;
double c = p0.x - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
因此,如果我使用以下值运行代码:
Point p0 = new Point(320, 480);
Point p1 = new Point(320, 240);
Point p2 = new Point(0, 240);
double t = 0.10f;
Point p = Bezier.quadratic(p0, p1, p2, t);
double[] ts = Bezier.obtainTs(p.x, p0, p1, p2);
我获得以下输出:
For t=0.10000000149011612, java.awt.Point[x=316,y=434]
For t1: -0.1118033988749895, java.awt.Point[x=316,y=536]
For t2: 0.1118033988749895, java.awt.Point[x=316,y=429]
java.awt.Point[x=316,y=434] belongs?: false
我应该使用BigDecimal
来执行操作吗?还有另一种方法可以验证这一点吗?谢谢
此处有错误:
double[] ty = obtainTs(p.y, p0, p1, p2);
因为obtainTs()
使用p0,p1,p2的 x 坐标来找到以下项的t参数p的 y 坐标.
如果将方法参数更改为int
(可以是该点的x或y坐标):
public static double[] obtainTs (int comp, int p0, int p1, int p2) {
double a = p0 - 2*p1 + p2;
double b = 2*p1 - 2*p0 ;
double c = p0 - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
并调用它
double[] tx = obtainTs(p.x, p0.x, p1.x, p2.x);
double[] ty = obtainTs(p.y, p0.y, p1.y, p2.y);
然后您的测试代码将返回"true"(经过ERROR = 0.02测试).
请注意,如果您写下等式
B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2
对于x和y坐标,则可以消除t ^ 2-Term并得到一个t的线性方程.这提供了以下方法,可能会稍微简单一些并且不使用平方根:
public static final boolean belongsQuadratic2 (Point p, Point p0, Point p1, Point p2) {
double ax = p0.x - 2*p1.x + p2.x;
double bx = 2*p1.x - 2*p0.x ;
double cx = p0.x - p.x;
double ay = p0.y - 2*p1.y + p2.y;
double by = 2*p1.y - 2*p0.y ;
double cy = p0.y - p.y;
// "Candidate" for t:
double t = -(cx*ay - cy*ax)/(bx*ay - by*ax);
if (t < 0 || t > 1)
return false;
// Compute the point corresponding to this candidate value ...
Point q = Bezier.quadratic(p0, p1, p2, t);
// ... and check if it is near the given point p:
return Math.abs(q.x - p.x) <= 1 && Math.abs(q.y - p.y) <= 1;
}
当然,必须检查特殊情况,例如bx*ay - by*ax == 0
.
还要注意,很难准确地确定 点是否在曲线上,因为点坐标四舍五入为整数.
I want to verify if a Point is part of a quadratic Bezier curve defined by points p0, p1 and p2..
This is my function to obtain a Point in the curve with a certain t:
public static final Point quadratic (Point p0, Point p1, Point p2, double t) {
double x = Math.pow(1-t, 2) * p0.x + 2 * (1-t) * t * p1.x + Math.pow(t, 2) * p2.x;
double y = Math.pow(1-t, 2) * p0.y + 2 * (1-t) * t * p1.y + Math.pow(t, 2) * p2.y;
return new Point((int)x, (int)y);
}
Considering that the point B(t) in a quadratic curve is obtained as follows:
I should verify if a point P belongs to a curve by getting the t value for that point and comparing it to the Point obtained using that t param, but in Java I'm having problems with the precision of the variables.
My function to verify a point is the following:
public static final boolean belongsQuadratic (Point p, Point p0, Point p1, Point p2) {
double[] tx = obtainTs(p.x, p0, p1, p2);
double[] ty = obtainTs(p.y, p0, p1, p2);
if (tx[0] >= 0) {
if ((tx[0] >= ty[0] - ERROR && tx[0] <= ty[0] + ERROR) || (tx[0] >= ty[1] - ERROR && tx[0] <= ty[1] + ERROR)) {
return true;
}
}
if (tx[1] >= 0) {
if ((tx[1] >= ty[0] - ERROR && tx[1] <= ty[0] + ERROR) || (tx[1] >= ty[1] - ERROR && tx[1] <= ty[1] + ERROR)) {
return true;
}
}
return false;
}
public static double[] obtainTs (int comp, Point p0, Point p1, Point p2) {
double a = p0.x - 2*p1.x + p2.x;
double b = 2*p1.x - 2*p0.x ;
double c = p0.x - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
So if I run the code with this values:
Point p0 = new Point(320, 480);
Point p1 = new Point(320, 240);
Point p2 = new Point(0, 240);
double t = 0.10f;
Point p = Bezier.quadratic(p0, p1, p2, t);
double[] ts = Bezier.obtainTs(p.x, p0, p1, p2);
I obtain the following output:
For t=0.10000000149011612, java.awt.Point[x=316,y=434]
For t1: -0.1118033988749895, java.awt.Point[x=316,y=536]
For t2: 0.1118033988749895, java.awt.Point[x=316,y=429]
java.awt.Point[x=316,y=434] belongs?: false
Should I use BigDecimal
to perform the operations? Is there another way to verify this? Thanks
There is an error here:
double[] ty = obtainTs(p.y, p0, p1, p2);
because obtainTs()
uses the x-coordinates of p0, p1, p2 to find the t-parameter forthe y-coordinate of p.
If you change the method parameters to int
(which can be the x- or y-coordinates of the point):
public static double[] obtainTs (int comp, int p0, int p1, int p2) {
double a = p0 - 2*p1 + p2;
double b = 2*p1 - 2*p0 ;
double c = p0 - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
and call it
double[] tx = obtainTs(p.x, p0.x, p1.x, p2.x);
double[] ty = obtainTs(p.y, p0.y, p1.y, p2.y);
then your test code will return "true" (tested with ERROR = 0.02).
Note that if you write down the equation
B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2
for both x- and y-coordinate, then you can eliminate the t^2-Term and get a singlelinear equation for t. This gives the following method, which might be slightly simplerand does not use square roots:
public static final boolean belongsQuadratic2 (Point p, Point p0, Point p1, Point p2) {
double ax = p0.x - 2*p1.x + p2.x;
double bx = 2*p1.x - 2*p0.x ;
double cx = p0.x - p.x;
double ay = p0.y - 2*p1.y + p2.y;
double by = 2*p1.y - 2*p0.y ;
double cy = p0.y - p.y;
// "Candidate" for t:
double t = -(cx*ay - cy*ax)/(bx*ay - by*ax);
if (t < 0 || t > 1)
return false;
// Compute the point corresponding to this candidate value ...
Point q = Bezier.quadratic(p0, p1, p2, t);
// ... and check if it is near the given point p:
return Math.abs(q.x - p.x) <= 1 && Math.abs(q.y - p.y) <= 1;
}
Of course, one would have to check for special cases, such as bx*ay - by*ax == 0
.
Note also that it is difficult do decide exactly if a point lies on the curve becausethe point coordinates are rounded to integers.
这篇关于验证点是否为Java二次Bezier曲线的一部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!