gpt4 book ai didi

java - 曲线路径恒速和终点

转载 作者:行者123 更新时间:2023-12-01 10:06:44 25 4
gpt4 key购买 nike

我试图在给定的时间内以恒定的速度沿着弯曲的路径移动。我通过对曲线上各个点求导并取平均值来计算沿曲线行驶所需的平均速度。然后,我将路径的位置 (t) 乘以平均导数与曲线当前位置处的导数的比率。这种设置恒速的方法效果很好。

当多个控制点(3 个或更多)放置在同一位置时,就会出现我遇到的问题。那么此时的速度(或导数)为0,用平均速度除以0的速度显然会导致计算出现问题。

BSpline 需要在末端放置三个控制点,以使曲线真正到达起点和终点。如果我只在末端放置 1 或 2 个控制点,则路径在第一个控制点之后开始,在最后一个控制点之前结束。对于我的应用程序来说,运动到达终点很重要,因为我将把多个 BSpline 连接在一起,并且它们正确排列并且它们之间没有任何时间间隙很重要。

我尝试了几种不同的尝试来修复它,但都没有成功。

这是我的示例代码,我添加了注释来指出问题所在。

注意:我在示例中使用 CatmullRomSpline 而不是 BSpline 只是因为我发现 BSpline 的导数方法中有一个错误,该错误已修复但尚未修复但在 LibGDX 的稳定版本中。

测试.java

public class Test extends Game {
private Stage stage;
private MyPath path;

@Override
public void create () {
Gdx.graphics.setDisplayMode(1000, 1000, false);
stage = new Stage();
stage.setViewport(new ScreenViewport(stage.getViewport().getCamera()));
Gdx.input.setInputProcessor(stage);
path = new MyPath(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
stage.addActor(path);
}
@Override
public void render () {
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
@Override
public void dispose(){
path.dispose();
stage.dispose();
super.dispose();
}
}

MyPath.java

public class MyPath extends WidgetGroup implements Disposable {
private Path<Vector2> path;
private Vector2 result=new Vector2(), derivative=new Vector2();
private float time, t, tPrev, dt, tConst, tConstPrev, derivativeAverage;
private Array<Texture> textures = new Array<Texture>(Texture.class);
private Array<Image> points = new Array<Image>(Image.class);
private Image dot;

private final float CYCLE = 4; // path cycle time (in seconds)

private Vector2[] pointsData = {
new Vector2(100, 100),
new Vector2(100, 100),
// new Vector2(100, 100), // << UN-COMMENT TO PRODUCE BUG

new Vector2(350, 800),
new Vector2(550, 200),
new Vector2(650, 400),
new Vector2(900, 100),
new Vector2(900, 100)
};

public MyPath(int width, int height){
this.setSize(width, height);
path = new CatmullRomSpline<Vector2>(pointsData, false);
// create and add images
createImages();
for (int i=0; i<points.size; i++){
points.items[i].setPosition(pointsData[i].x - points.items[i].getWidth()/2, pointsData[i].y - points.items[i].getHeight()/2);
addActor(points.items[i]);
}
addActor(dot);

// calculate derivative average
derivativeAverage();
}

@Override
public void act(float delta){
result = getValue(delta);
dot.setPosition(result.x - dot.getWidth()/2, result.y - dot.getHeight()/2);
}
private Vector2 getValue(float delta){
// set t in the range [0,1] for path
time += delta;
if (time > CYCLE){
time = tPrev = dt = tConst = tConstPrev = 0;
}
t = time / CYCLE;
dt = t - tPrev;
tPrev = t;

// constant speed (tConst)
path.derivativeAt(derivative, tConstPrev);
tConst += dt * (derivativeAverage / derivative.len()); // << ERROR when derivative.len() is 0
tConstPrev = tConst;

path.valueAt(result, tConst);

return result;
}

private void derivativeAverage(){
float segmentCount = 20000;
derivativeAverage = 0;
for (float i=0; i<=1; i+=1.0/segmentCount) {
path.derivativeAt(result, i);
derivativeAverage += result.len();
}
derivativeAverage /= segmentCount;
if (derivativeAverage==0){ throw new GdxRuntimeException("ERROR: derivative average is zero"); }
}

private void createImages(){
dot = getImage(Color.GREEN, true);
for (int i=0; i<pointsData.length; i++){
points.add(getImage(Color.WHITE, false));
}
}
private Image getImage(Color color, boolean fillCircle){
Pixmap pixmap = new Pixmap(50, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
if (fillCircle){
pixmap.fillCircle(pixmap.getWidth()/2, pixmap.getHeight()/2, pixmap.getWidth()/2-1);
} else {
pixmap.drawCircle(pixmap.getWidth()/2, pixmap.getHeight()/2, pixmap.getWidth()/2-1);
}
textures.add(new Texture(pixmap));
pixmap.dispose();
return new Image(textures.peek());
}
@Override
public void dispose(){
while (textures.size > 0){
textures.pop().dispose();
}
}
}

================================================== ===================

编辑

================================================== ===================

这是我最近尝试增加 t 直到点移动。

此方法有时确实适用于某些帧(平滑地移动到零导数)。但其他时候,当点达到零导数时,点会在曲线的起点处重新开始,或者延伸到曲线的终点之外,移动不同的方向,或者完全消失(因为位置被设置为负值)。/p>

所以看起来这个方法非常接近,因为它偶尔会在某些帧上工作,但它会出现故障并在其他帧上做奇怪的事情。

Vector2 lastPoint = new Vector2();
float minSpeed = 1;
float minDerivative = 1;
float temp;

...

private Vector2 getValue(float delta){
// set t in the range [0,1] for path
time += delta;
if (time > CYCLE){
time = tPrev = dt = tConst = tConstPrev = 0;
}
t = time / CYCLE;

// CONSTANT SPEED
dt = t - tPrev;
path.derivativeAt(derivative, tConstPrev);
temp = dt * (derivativeAverage / derivative.len());
path.valueAt(result, tConst + temp);

//**************************************
// FIX FOR ZERO SPEED
// increase t in loop until speed > 0
//**************************************
while (result.dst(lastPoint)<minSpeed || derivative.len()<minDerivative){
// set t in the range [0,1] for path
time += delta;
if (time > CYCLE){
time = tPrev = dt = tConst = tConstPrev = 0;
lastPoint.set(0,0);
}
t = time / CYCLE;

// CONSTANT SPEED
dt = t - tPrev;
// new derivative
path.valueAt(derivative, t);
derivative.sub(lastPoint);

temp = dt * (speedAverage / derivative.len());
path.valueAt(result, tConst + temp);
}

tConst += temp;

lastPoint.set(result);
tPrev = t;
tConstPrev = tConst;

return result;
}

在计算平均速度时我也做了类似的事情,以防止零导数影响它。我还尝试在计算平均值时使用带有“addedSegmentCount”变量的注释掉的部分,但这实际上由于某种原因导致了更多的故障......尽管理论上这似乎是计算平均值的“正确”方法,因为某些段不如果距离太小,则不会添加。

private void pathLength_SpeedAverage(){
float segmentCount = 20000;
// float addedSegmentCount = 0;
pathLength = 0;

path.valueAt(lastPoint, 0);
for (float i=0; i<=1; i+=1.0/segmentCount){
path.valueAt(result, i);
if (result.dst(lastPoint) >= minSpeed){
path.derivativeAt(result, i);
if (result.len() >= minDerivative){
pathLength += result.len();

lastPoint.set(result);
// ++addedSegmentCount;
}
}
}
speedAverage = pathLength / segmentCount;
// speedAverage = pathLength / addedSegmentCount;
lastPoint.set(0,0);
}

最佳答案

如果控制点可能重合,则无法完全避免零一阶导数。所以,我的建议是根本不要使用一阶导数。你的目的是以恒定速度遍历路径,这相当于沿着路径上等弧长的采样点。解决这个问题的理论方法涉及微积分以数值方式计算弧长,但我们可以采用如下近似方法:

假设你想用 N 步遍历这条路径,
1) 在参数域(即 t=0.0, 0.1, 0.2, 0.3, ....)中沿路径均匀采样 M 个点,其中 M 最好大于 N。将这些点表示为 P0、P1、P2, ....
2) 计算P0P1, P1P2, P2P3,....之间的距离
3) 编译一个查找表,将参数 t(i) 映射到累积弦长 |P0P1|+|P1P2|+.....+|P(i-1)P(i)|。最后,您还将获得路径的总长度,记为L。
4) 现在,对于 kL/N 的每个值(其中 k=0 到 N),您可以通过对两个参数值进行线性插值,从查找表中计算相应的 t 值,其中 kL/N 落在。

关于java - 曲线路径恒速和终点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36392634/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com