我正在尝试创建一条通过 Java 中三个给定点的曲线(我正在通过扩展 JPanel 的类绘制曲线)。我怎样才能做到?
5 回答
您应该研究类似 Catmull-Rom 样条的东西,它们基本上是通过多个控制点(在您的情况下是三个点)的曲线。
这是我在快速谷歌后找到的一个例子:http ://www.mvps.org/directx/articles/catmull/
希望这可以帮助 :)
一个圆将通过平面上的三个点。这个页面解释了几何: http: //www.mathopenref.com/const3pointcircle.html
我只是花了一些时间让它以一种健壮的方式工作。有一些支持功能,然后是用圆的三个点创建 Arc2D 的东西。出于我的目的,我有一个起点和终点,以及一个中间的“中间”点(尽管它实际上不必在中间——它的目的是告诉我我想要哪个圆弧)。
以下是源代码的直接链接:
org.six11.util.gui.shape.ShapeFactory
public static Pt getCircleCenter(Pt a, Pt b, Pt c) {
double ax = a.getX();
double ay = a.getY();
double bx = b.getX();
double by = b.getY();
double cx = c.getX();
double cy = c.getY();
double A = bx - ax;
double B = by - ay;
double C = cx - ax;
double D = cy - ay;
double E = A * (ax + bx) + B * (ay + by);
double F = C * (ax + cx) + D * (ay + cy);
double G = 2 * (A * (cy - by) - B * (cx - bx));
if (G == 0.0)
return null; // a, b, c must be collinear
double px = (D * E - B * F) / G;
double py = (A * F - C * E) / G;
return new Pt(px, py);
}
public static double makeAnglePositive(double angleDegrees) {
double ret = angleDegrees;
if (angleDegrees < 0) {
ret = 360 + angleDegrees;
}
return ret;
}
public static double getNearestAnglePhase(double limitDegrees, double sourceDegrees, int dir) {
double value = sourceDegrees;
if (dir > 0) {
while (value < limitDegrees) {
value += 360.0;
}
} else if (dir < 0) {
while (value > limitDegrees) {
value -= 360.0;
}
}
return value;
}
public static Arc2D makeArc(Pt s, Pt mid, Pt e) {
Pt c = Functions.getCircleCenter(s, mid, e);
double radius = c.distance(s);
double startAngle = Functions.makeAnglePositive(Math.toDegrees(-Math
.atan2(s.y - c.y, s.x - c.x)));
double midAngle = Functions.makeAnglePositive(Math.toDegrees(-Math.atan2(mid.y - c.y, mid.x
- c.x)));
double endAngle = Functions
.makeAnglePositive(Math.toDegrees(-Math.atan2(e.y - c.y, e.x - c.x)));
// Now compute the phase-adjusted angles begining from startAngle, moving positive and negative.
double midDecreasing = Functions.getNearestAnglePhase(startAngle, midAngle, -1);
double midIncreasing = Functions.getNearestAnglePhase(startAngle, midAngle, 1);
double endDecreasing = Functions.getNearestAnglePhase(midDecreasing, endAngle, -1);
double endIncreasing = Functions.getNearestAnglePhase(midIncreasing, endAngle, 1);
// Each path from start -> mid -> end is technically, but one will wrap around the entire
// circle, which isn't what we want. Pick the one that with the smaller angular change.
double extent = 0;
if (Math.abs(endDecreasing - startAngle) < Math.abs(endIncreasing - startAngle)) {
extent = endDecreasing - startAngle;
} else {
extent = endIncreasing - startAngle;
}
return new Arc2D.Double(c.x - radius, c.y - radius, radius * 2, radius * 2, startAngle, extent,
Arc2D.OPEN);
}
尝试在贝塞尔样条曲线上进行谷歌搜索。这可能是一个 2D 解决方案,但如果您需要它应该可以扩展到 3D。
基本上,使用这三个点作为参数,您可以获得一个适合三个点的二阶多项式.. 并且它是可扩展的,如果您有 N 个点,您将获得一个 N-1 阶多项式,该多项式参数化地生成从第一个到第一个点的所有点最后,当您“调整”一个标量参数时,通常表示为“s”。
编辑/添加:
正如所指出的(信用 CapBBeard!),贝塞尔曲线实际上并没有达到中间点。拉格朗日插值确实达到了这些点,但随着点数的增加,它变得更加丑陋。(类似于每个 N 阶的 O(n) 多项式分数)
使用由 3 个点创建的 2 条曲线
curveLine1:P1、C1、E1。
用 3 个点创建的曲线线 2:P2 = E1、C2、E2。
在 1 行中制作 C1、E1、C2。
在此处查看示例:http: //i.stack.imgur.com/I901q.png
QuadCurve2D.Double curveLine1 = new QuadCurve2D.Double(30, 75, 195, 23, 280, 143);
//QuadCurve2D.Double curveLine1 = new QuadCurve2D.Double(P1.x, P1.y, C1.x, C1.y, E1.x, E1.y);
QuadCurve2D.Double curveLine2 = new QuadCurve2D.Double(280, 143, 366, 260, 466, 193);
//QuadCurve2D.Double curveLine2 = new QuadCurve2D.Double(E1.x, E1.y, C2.x, C2.y, E2.x, E2.y);
Graphics2D g2 = (Graphics2D) g;
g2.draw(curveLine1);
g2.draw(curveLine2);