I need to be able to show moving cars using Here maps android sdk.
Is it possible in the current version? I tried to remove and add map marker object in a render like loop logic as the following;
// this is a render like loop which is called in each 1 secs
public void update(Map map, float deltaTime) {
float displacement = mCars.get(0).getVelocity() * deltaTime;
float delta = displacement / ((float) Math.sqrt(2));
MapMarker mapMarker = new MapMarker();
Image image = new Image();
image.setBitmap(mCars.get(0).getIcon().toBitmap());
mapMarker.setCoordinate(mCars.get(0).getCoordinate());
mapMarker.setIcon(image);
// remove old coordinate
map.removeMapObject(mapMarker);
mCars.get(0).addDelta(delta);
mapMarker.setCoordinate(mCars.get(0).getCoordinate());
map.addMapObject(mapMarker);
}
The removeMapObject() doesn't seem to be working. Any ideas to have a moving map object ?
The problem here was to create each marker and remove them and re-add them.
The render loop would be the following;
// this is a render like loop which is called in each 1 secs
public void update(Map map, float deltaTime) {
Timber.d("Before coordinate: %f %f", mCars.get(0).getCoordinate().getLatitude(), mCars.get(0).getCoordinate().getLongitude());
map.removeMapObject(mCars.get(0).getMarker());
mCars.get(0).addDelta(deltaTime);
map.addMapObject(mCars.get(0).getMarker());
Timber.d("Update call, delta time: %f", deltaTime);
}
The addDelta() call should update the marker coordinate and its own coordinate;
public void addDelta(float deltaTime) {
if (mDestination.equals(mCoordinate)) {
return;
}
double deltaLatitude = mDestination.getLatitude() - mCoordinate.getLatitude();
double deltaLongitude = mDestination.getLongitude() - mCoordinate.getLongitude();
double lat = getCoordinate().getLatitude() + (0.01 * deltaLatitude);
double lng = getCoordinate().getLongitude() + (0.01 * deltaLongitude);
mCoordinate.setLatitude(lat);
mCoordinate.setLongitude(lng);
updateMarker();
}
And lastly, updateMarker implementation is the following;
public void updateMarker() {
mMarker = new MapMarker();
Image image = new Image();
image.setBitmap(mIcon.toBitmap());
mMarker.setCoordinate(mCoordinate);
mMarker.setIcon(image);
}
Related
I'm working on an app, similar to uber, didi, etc. I have a problem when making the animations of the vehicles (going from point A to point B), I found this code on Internet and it works quite well:
public void animateMarker(final LatLng startPosition, final LatLng toPosition,final boolean hideMarke) {
final Handler handler = new Handler();
final Marker m = map.addMarker(new MarkerOptions()
.position(startPosition)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.bus2))
.title("Camión"));
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(m.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 5000;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ duration);
double lng = t * toPosition.longitude + (1 - t)
* startLatLng.longitude;
double lat = t * toPosition.latitude + (1 - t)
* startLatLng.latitude;
m.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
if (hideMarke) {
m.setVisible(false);
} else {
m.setVisible(true);
}
}
}
});
markers_animations.add(m);
}
I have this method that is in charge of passing it the positions of a list with all the coordinates that I require for the Polylines:
private void controlAnimaciones(List<LatLng> ruta) {
for (int i=0; i<ruta.size()-1; i++) {
if (i<ruta.size()) {
animateMarker3(ruta.get(i), ruta.get(i+1), true);
}
}
}
It does what I expected it to do, if it moves the marker from point A to point B, but, just by iterating the list, I don't know how to explain it, there are many markers that move only from one element of the list to the next and after that they stop. What I want to do is to achieve that a single marker can move through all the points of the list, I have been trying in some ways with the code I got from the internet, to try to understand it, but I have not had much success. How could I do it?
Currently in your posted code, animateMarker creates a marker for each "segment" of the polyline - it starts and stops the movement of the marker along the one segment, and it does this asynchronously. This would have the effect of a marker created (simultaneously) at the start of every segment and each one animated in parallel (nearly). Not what you want.
So you have two things to change:
Create the marker once at the start of the first segment.
Continue the animation of the second and later segments after the first (or previous leg completes.)
Easy way to do the above is to modify the animateMarker to accept the list of points rather than a single point. The list of points is your polyline (ruta).
I made some comments where the method was modified from your original.
public void animateMarker(List<LatLng> pts,final boolean hideMarker) {
// Simple check to make sure there are enough points in the list.
if (pts.size() <= 1) {
// need at least two points.
return;
}
final Handler handler = new Handler();
// Use first point in list as start.
final Marker m = mMap.addMarker(new MarkerOptions()
.position(pts.get(0))
.title("Camión"));
Projection proj = mMap.getProjection();
Point startPoint = proj.toScreenLocation(m.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 5000;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
// start at first segment
private int segment = 0;
// initial start time
long start = SystemClock.uptimeMillis();
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ duration);
// Use next point in list as destination
double lng = t * pts.get(segment+1).longitude + (1 - t)
* pts.get(segment).longitude;
double lat = t * pts.get(segment+1).latitude + (1 - t)
* pts.get(segment).latitude;
m.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
// check if to move to next segment (or done)
else if (segment < (pts.size()-2)) {
// move to next segment
segment++;
start = SystemClock.uptimeMillis();
handler.postDelayed(this,16);
} else {
if (hideMarker) {
m.setVisible(false);
} else {
m.setVisible(true);
}
}
}
});
markers_animations.add(m);
}
And to call the animation with your list just modify your
private void controlAnimaciones(List<LatLng> ruta) {
animateMarker(ruta, true);
}
And this is the result.
(Note that the "velocity" of the marker movement is dependent on the length of the segment. Since the duration is fixed per segment, longer segments would make the marker appear to move faster. You could change this to a constant velocity for any segment by changing duration as a function of the distance between the two points.)
Note that the animaterMarker supports multiple animations naturally without modification. So in this example the map onMapClickListener invokes controlAnimaciones on every map click which was done every few seconds for demonstration purposes.
Hope this helps!
What is the best way to animate markers on Google Maps using v2 API?
I am working on a map-centered game where I track locations of people and display them on the map for each other to see. As people move, I want to animate a marker from his current to his latest position. Every person has a direction, so I need to rotate the marker appropriately.
What is the best way to do it using the new Google Maps API?
Some Google engineers have provided a nice demo video with some elegant sample code about how to animate markers from a starting point to an ending point, for all various versions of Android:
The relevant code is here:
https://gist.github.com/broady/6314689
And a nice demo video of all of it in action.
http://youtu.be/WKfZsCKSXVQ
OLD DEPRECATED ANSWER BELOW
In the documentation, it is mentioned that Marker Icons cannot be changed:
Icon
A bitmap that's displayed for the marker. If the icon is left unset, a default icon is displayed. You can specify an alternative coloring of the default icon using defaultMarker(float). You can't change the icon once you've created the marker.
Google Maps API v2 Documentation
You're going to have to keep track of specific markers, perhaps using a method similar to that described here: Link a Marker to an Object, then figure out which marker you need to update. Call .remove() on the marker, then create a rotated image depending on the "direction" you want, create a new Marker with that image, and add the new Marker to the map.
You do not need to "clear" the map, simply remove the marker you want to modify, create a new one, then add it back to the map.
Unfortunately, the new Maps API is not very flexible yet. Hopefully Google continues to improve upon it.
Full Example for DiscDev's answer (Above):
LatLng fromLocation = new LatLng(38.5, -100.4); // Whatever origin coordinates
LatLng toLocation = new LatLng(37.7, -107.7); // Whatever destination coordinates
Marker marker = mMap.addMarker(new MarkerOptions().position(firstLocation));
MarkerAnimation.animateMarkerToICS(marker, toLocation, new LatLngInterpolator.Spherical());
And for those of you who uses GPS / or any position provider that receives location updates:
Marker ourGlobalMarker;
// We've got a location from some provider of ours, now we can call:
private void updateMarkerPosition(Location newLocation) {
LatLng newLatLng = new LatLng(newLocation.getLatitude(), newLocation.getLongitude());
if(ourGlobalMarker == null) { // First time adding marker to map
ourGlobalMarker = mMap.addMarker(new MarkerOptions().position(newLatLng));
}
else {
MarkerAnimation.animateMarkerToICS(ourGlobalMarker, newLatLng, new LatLngInterpolator.Spherical());
}
}
IMPORTANT:
Within 1MarkerAnimation.java If the animation duration is set to X,
and you are receiving location updates in a rate smaller then X, multiple animations will be triggered, and you might see the marker animation flickers a bit (which is not a nice user experience).
To avoid this, the animationMarkerToICS method (I took here animationMarkerToICS for example), should look something like this,
full method implementation:
private static Animator animator; // MAKING ANIMATOR GLOBAL INSTEAD OF LOCAL TO THE STATIC FUNCTION
...
// Ice Cream Sandwich compatible
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static void animateMarkerToICS(Marker marker, LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
#Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
return latLngInterpolator.interpolate(fraction, startValue, endValue);
}
};
Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");
// ADD THIS TO STOP ANIMATION IF ALREADY ANIMATING TO AN OBSOLETE LOCATION
if(animator != null && animator.isRunning()) {
animator.cancel();
animator = null;
}
animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
animator.setDuration((long) ANIMATION_DURATION);
animator.start();
}
Enjoy.
Marker has a new function added as of rev.7 of API v2. Marker.setIcon, so you can use multiple icons to show direction.
//Your code
double bearing = 0.0;
bearing = getBearing(new LatLng(
currentPosition.latitude
,currentPosition.longitude),
new LatLng(
nextPosition.latitude,
nextPosition.longitude));
bearing -= 90;
CameraPosition cameraPosition = new CameraPosition
.Builder()
.target(new LatLng(nextPosition.latitude, nextPosition.longitude))
.bearing((float) bearing)
.zoom(ZOOM_LEVEL).build();
mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000, null);
animatedMarker(currentPosition,nextPosition,busMarker);
//Method for finding bearing between two points
private float getBearing(LatLng begin, LatLng end) {
double lat = Math.abs(begin.latitude - end.latitude);
double lng = Math.abs(begin.longitude - end.longitude);
if (begin.latitude < end.latitude && begin.longitude < end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)));
else if (begin.latitude >= end.latitude && begin.longitude < end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 90);
else if (begin.latitude >= end.latitude && begin.longitude >= end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)) + 180);
else if (begin.latitude < end.latitude && begin.longitude >= end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 270);
return -1;
}
private void animatedMarker(final LatLng startPosition,final LatLng nextPosition,final Marker mMarker)
{
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final float durationInMs = 3000;
final boolean hideMarker = false;
handler.post(new Runnable() {
long elapsed;
float t;
float v;
#Override
public void run() {
// Calculate progress using interpolator
elapsed = SystemClock.uptimeMillis() - start;
t = elapsed / durationInMs;
v = interpolator.getInterpolation(t);
LatLng currentPosition = new LatLng(
startPosition.latitude * (1 - t) + nextPosition.latitude * t,
startPosition.longitude * (1 - t) + nextPosition.longitude * t);
mMarker.setPosition(currentPosition);
// Repeat till progress is complete.
if (t < 1) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
if (hideMarker) {
mMarker.setVisible(false);
} else {
mMarker.setVisible(true);
}
}
}
});
}
I am currently working on a project in Java where I need to implement a draggable and zoomable graph.
The current implementation involves a graph object having an array of predefined points to draw. And whenever the graph wants to draw a point to the screen, it asks a "graph transformer" to apply affine transformations to the point to get its location on the screen.
The graph transformer contains an X and Y offset as well as a zoom. So basically, a point P will be transformed as follows :
P_x = zoom_x * P_x + offset_x
P_y = zoom_y * P_y + offset_y
The offset is updated when the user drags the graph and the zoom is updated when the user scrolls the mouse wheel.
Everything is working just as intended. The thing is that the zoom is always applied relative to the origin (0,0) of the graph, which is normal. But what I would like to do is apply the zoom relative to the mouse position.
I've been scratching my head for quite some time now, and I am getting a bit confused between the real coordinates and the transformed coordinates.
How could I implement this feature without modifying too much my code ?
Here are my Java methods :
The method transforming points :
public Point transform(Point p) {
Point transformed = new Point();
transformed.x = Math.round(Math.round((m_zoom.x * p.x) + m_offset.x));
transformed.y = Math.round(Math.round((m_zoom.y * p.y) + m_offset.y));
return transformed;
}
The methods called when mouse events are fired :
#Override
public void mousePressed(MouseEvent e) {
m_pressed = e.getPoint();
m_lastCalculatedOffset.x = 0;
m_lastCalculatedOffset.y = 0;
}
#Override
public void mouseDragged(MouseEvent e) {
Point zoomedDragging = new Point();
zoomedDragging.x = Math.round(Math.round(e.getX() - m_pressed.x));
zoomedDragging.y = Math.round(Math.round(e.getY() - m_pressed.y));
m_offset.x += zoomedDragging.x - m_lastCalculatedOffset.x;
m_offset.y += zoomedDragging.y - m_lastCalculatedOffset.y;
m_lastCalculatedOffset.x = zoomedDragging.x;
m_lastCalculatedOffset.y = zoomedDragging.y;
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
m_zoom.x *= Math.pow(ZOOM_FACTOR, e.getWheelRotation());
m_zoom.y *= Math.pow(ZOOM_FACTOR, e.getWheelRotation());
}
Thanks in advance for your help :)
Ok so after taking a break and drawing the problem on paper user graph examples, I managed to find a solution :
If we call ZoomP the location of the mouse when the zoom occurs, once the zoom has been done, the following transformation has to be done to the offset :
newOffset_x = oldOffset_x + (1 - (newZoom_x / oldZoom_x)) * (ZoomP_x - oldOffset_x)
newOffset_y = oldOffset_y + (1 - (newZoom_y / oldZoom_y)) * (ZoomP_y - oldOffset_y)
So here is my updated mouseWheelMoved method for those interested :
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
Point mousePos = e.getPoint();
Point2D.Double newZoom = new Point2D.Double();
newZoom.x = m_zoom.x * Math.pow(ZOOM_FACTOR, e.getWheelRotation());
newZoom.y = m_zoom.y * Math.pow(ZOOM_FACTOR, e.getWheelRotation());
m_offset.x += Math.round(Math.round((1 - (newZoom.x / m_zoom.x)) * (mousePos.x - m_offset.x)));
m_offset.y += Math.round(Math.round((1 - (newZoom.y / m_zoom.y)) * (mousePos.y - m_offset.y)));
m_zoom = newZoom;
}
I don't understand how I can simply clear the screen in Java while using OpenGL. I have searched all over the internet, there is like no real good resource for OpenGL information. Basically I just want to clear the screen and re-draw a circle. Instead my code decides that it isn't going to clear the screen ever, and it most definitely isn't going to draw anything else.. I want it to clear the screen when I press "e", and then draw a new circle. I have two java files.. I will only post relevant code for the sake of any user's who can help me - but will post more code if needed.
In the beginning of my JOGLEventListener.java file I'm also declaring a global var
// Test
GLAutoDrawable test = null;
JOGLEventListener.java
#Override
public void display(GLAutoDrawable gLDrawable)
{
// Set a global variable to hold the gLDrawable
// May not need this?
test = gLDrawable;
GL2 gl = gLDrawable.getGL().getGL2();
gl.glClearColor(backrgb[0], 0, 1, 1);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
backrgb[0]+=0.0005;
if (backrgb[0]> 1) backrgb[0] = 0;
// =============================================
// Draw my circle here
//
// =============================================
// =============================================
System.out.println("Drawing Circle..");
drawCircle(5.0f, 5.0f, 10.0f);
}
// Draw Circle
void drawCircle(float x, float y, float radius)
{
System.out.println("IN DRAWCIRCLE");
int i;
GL2 gl = test.getGL().getGL2();
int lineAmount = 100; //# of triangles used to draw circle
final
//GLfloat radius = 0.8f; //radius
float twicePi = (float) (2.0f * Math.PI);
gl.glBegin(gl.GL_LINE_LOOP);
for(i = 0; i <= lineAmount;i++) {
gl.glVertex2f(
x + (radius * (float)Math.cos(i * twicePi / lineAmount)),
y + (radius* (float)Math.sin(i * twicePi / lineAmount))
);
}
gl.glEnd();
}
#Override
public void keyTyped(KeyEvent e)
{
char key= e.getKeyChar();
System.out.printf("Key typed: %c\n", key);
GL2 gl = test.getGL().getGL2();
if(key == 'e')
{
// WHY ISNT THIS WORKING
// CLEAR THE SCREEN AND DRAW ME A NEW CIRCLE
gl.glClear( gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT );
gl.glLoadIdentity();
//test
float x = 100.0f;
float y = 100.0f;
float twicePi = (float) (2.0f * Math.PI);
float radius = 100f;
System.out.println("Draw Another Circle...");
gl.glBegin(gl.GL_LINE_LOOP);
for(int i = 0; i <= 360;i++)
{
gl.glVertex2f(
x + (radius * (float)Math.cos(i * twicePi / 360)),
y + (radius* (float)Math.sin(i * twicePi / 360))
);
}
gl.glEnd();
}
1) That's deprecated OpenGL, don't use it
2) Don't save the gl object to one global value, always get it from the drawable or the GLContext
3) Use a shader program to render and a vertex buffer to hold the vertices position. But first, I'd suggest you to start a tutorial to learn the basic of OpenGL. Or if you want to get something working asap, clone this hello triangle of mine and start experiment on that
The problem is apparently that you don't swap the front and back buffers.
I'm not familiar with the OpenGL bindings for Java, but I guess that the library already does that for you after it calls the display() function. It doesn't do that after keyTyped().
The way you are supposed to do this is to always draw the scene from scratch inside the display() function based on some internal state. Then in keyTyped() you shall modify that internal state and invalidate the window, which will cause the display() to be called again and redraw the scene properly.
EDIT: Calling display() yourself won't be enough. I can't find how to invalidate the window in Java (in C this would be so much easier). As a dirty hack you can try calling temp.swapBuffers() manually in display, setting setAutoSwapBufferMode(false) and calling display from keyTyped().
I’m trying to achieve constant speed on a path using the LibGDX CatmullRomSpline and I’m having problems getting it to work. I’ve tried researching on this topic a lot including reading the LibGDX wiki, but their explanation for achieving constant speed doesn’t really make sense and I wasn’t able to get their method to work. https://github.com/libgdx/libgdx/wiki/Path-interface-&-Splines
In my case, the derivative values are very large (in the hundreds) so when dividing a number between 0-1 by the derivative the result is very small and the movement is very slow and still not constant. So I’m not sure exactly how their example works.
In my example I have a couple visual aids coinciding with the speed of the ball, the bar at the bottom of the screen increases in length as the speed increases and the color also changes from white to red as the speed increases.
In the act() method of MyPath.java I have two sections commented out starting with [1] and [2]. The first one is normal with the variable speed through the path and the second one is my failed attempt at getting the LibGDX wiki constant speed to work. So just un-comment these lines to switch between the two versions.
My idea for constant speed involves figuring out the speed based on the total length of the path (using the approxLength(1000) method on the spline), then using the derivative function to determine the actual speed at a given instant, and adjusting the percentage value sent into the spline to compensate for the speed changes in order to make the speed constant. However, I don’t quite understand what the derivative function actually represents. I posted a question about the derivative function earlier, but based a comment I received I figured it might be easier to ask about achieving constant speed instead. Here is my previous question on the derivative function:
LibGDX CatmullRomSpline Derivative Meaning?
Any ideas on how to achieve constant speed in my example (or explaining what the derivative function for the CatmullRomSpline actually represents so I could better understand how to use it) would be greatly appreciated.
For anyone who'd like to run the program, here are the two image files I created for my example (add these to the root of the assets folder):
http://dropshots.com/Tekker/date/2015-09-19
Here is my example code:
DesktopLauncher.java: (changed desktop window width and height to 1000)
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.width = 1000;
config.height = 1000;
new LwjglApplication(new TEST(), config);
}
}
TEST.java:
public class TEST extends Game {
Stage stage;
MyPath path;
#Override
public void create () {
stage = new Stage();
stage.setViewport(new ScreenViewport(stage.getViewport().getCamera()));
Gdx.input.setInputProcessor(stage);
path = new MyPath(1000, 1000);
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(){
stage.dispose();
super.dispose();
}
}
MyPath.java:
public class MyPath extends WidgetGroup {
Image start, end, path, bar1, horizontal;
float time, percent, dVal, pathLength, dMax=1000, cycle=6, maxPercent, deltaCycle;
CatmullRomSpline<Vector2> catmull;
Vector2 result = new Vector2();
Vector2 previousResult = new Vector2(50,150);
Vector2 derivative = new Vector2();
Vector2 previousDerivative = new Vector2();
Vector2[] points = {
new Vector2(50,150), new Vector2(50,150),
new Vector2(400,800), new Vector2(600,150), new Vector2(700,400),
new Vector2(860,150), new Vector2(860,150)
};
boolean print = true;
public MyPath(int width, int height){
this.setSize(width, height);
catmull = new CatmullRomSpline<Vector2>(points, false);
createPath();
createBar();
pathLength = catmull.approxLength(1000);
}
#Override
public void act(float delta){
// [1] VARIABLE SPEED
//time += delta;
//percent = (time / cycle) % 1;
// [2] CONSTANT SPEED FAIL!
//catmull.derivativeAt(previousDerivative, percent);
//time += delta;
//percent = ((time / cycle) / previousDerivative.len() ) % 1;
catmull.valueAt(result, percent);
path.setPosition(result.x, this.getHeight() - result.y);
updateSpeedVisuals();
debugPrint();
previousResult.set(result);
}
private void createPath(){
start = new Image(new Texture("dot.png"));
start.setColor(Color.GRAY);
start.setPosition(50, this.getHeight() - 150);
this.addActor(start);
end = new Image(new Texture("dot.png"));
end.setColor(Color.GRAY);
end.setPosition(860, this.getHeight() - 150);
this.addActor(end);
path = new Image(new Texture("dot.png"));
path.setColor(Color.WHITE);
this.addActor(path);
}
private void createBar(){
Texture texture = new Texture("ninepatch.png");
int crop = (int)(texture.getWidth()/2)-1;
NinePatch patch9 = new NinePatch(texture, crop, crop, crop, crop);
bar1 = new Image(patch9);
bar1.setColor(Color.GRAY);
bar1.setPosition(5, this.getHeight()-900);
this.addActor(bar1);
}
private void updateSpeedVisuals(){
catmull.derivativeAt(derivative, percent);
dVal = derivative.len() / dMax;
path.setColor(1f, 1f-dVal, 1f-dVal, 1f);
bar1.setWidth(derivative.len());
bar1.setColor(1f, 1f-dVal, 1f-dVal, 1f);
}
private void debugPrint(){
maxPercent = (percent > maxPercent) ? percent : maxPercent;
if (maxPercent > percent){
print = false;
}
if (print){
String debugPrint = "";
debugPrint = debugPrint + "pathLength=" + pathLength + "\t";
debugPrint = debugPrint + "derivative=" + derivative.len() + "\t";
System.out.println(debugPrint);
}
}
}
Since the derivative is the rate of change of the spline position it is indeed the 'speed', and when the spline is bending away from the underlying data points it has to 'speed up' to make the calculated spline reach the next data point in time, you must divide out this speed to perceive a visual constant speed.
You aren't getting a constant speed because you are still incrementing your time variable by delta instead of delta divided by the rate of change (derivative). You should be adding a variable amount to the percent variable each frame, instead you were modifying everything by the derivative of a single point along the Catmull-Rom spline.
Instead of:
catmull.derivativeAt(previousDerivative, percent);
time += delta;
percent = ((time / cycle) / previousDerivative.len() ) % 1;
You should:
catmull.derivativeAt(previousDerivative, percent);
percent += derivativeAverage / cycle * delta / previousDerivative.len();
percent %= 1;
you should use the average derivative divided by cycle now since you can't use cycle alone as a percent per second variable anymore.
Iterating over the spline to find the average value of the derivativeAverage:
int samples = 100; //the higher the more accurate, however slower
float derivativeAverage = 0;
Vector2 out = new Vector2();
for (float i=0;i<1;i+=1f/samples) {
catmull.derivativeAt(out, i);
derivativeAverage += out.len();
}
derivativeAverage /= samples;