September 25, 2011

Given distance and friction, calculate the initial velocity to stop at the distance target

While simulating iOS scroll views in javascript, I needed to find the initial velocity to send the scroll content back into position if you drag it out of bounds. Since the in-bounds scroll view inertia was already working with a given friction, I wanted to use this to run the calculation. After some brute-force experimenting, I came up with the equation to calculate this velocity:
// set up vars
var curPos = 0;
var distance = 2000;
var friction = 0.8;
var velocity = distance / ( friction * ( 1 / ( 1 - friction ) ) );

// in timer:
setInterval(function(){
    velocity *= friction;
    curPos += velocity;
},30);
The curPos value will ease into the target distance, no matter what you set as the friction and distance. Friction must be a decimal between zero and one.

September 13, 2011

Xcode error: "The argument is invalid"

If you get this error while publishing from Xcode, you may have a rogue symlink in your project. As of Xcode 4.1, symlinks will give you the awesome, helpful error: "The argument is invalid"
Check your project for symlinks, and remove them. If you're symlinking another directory into your project, this would work in the iOS Simulator, but not on a device, so you'd probably want to come up with another strategy for including files from another project. To help with finding symlinks, run the following command from the root of your project directory. It will list any symlinks and their original location.
find ./ -type l -exec ls -l {} \;

September 1, 2011

Google Maps API: Polyline encoding/decoding issue

I'm using google's Geometry Library to encode large numbers of GPS location points into a compressed format for storage. You can add this capability to your .js by adding it to the quersystring in the javascript reference:
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?libraries=geometry&sensor=true"></script>
The library provides a very easy way to display a path, add points realtime, store this path as an encoded & compressed string, and feed that string back into a new map when you're ready to display it later. You can see a live example here.

Encoding works great, but I ran into an issue where some of my encoded paths, generated by google.maps.geometry.encoding.encodePath() from my google.maps.Polyline object, would have major errors when using the google.maps.geometry.encoding.decodePath() method. My Polyline would have random right-angle turns that effectively ruined my path. I played around with the encoded string, trying to figure out what was causing the issue, to no avail. I found another implementation of the polyline encoding algorithm, and found an explanation of what was causing the issue.

It turns out that you need to escape the backslashes that may appear in the output string from google.maps.geometry.encoding.encodePath() (and the other library linked to above). So if you're storing the string for later, you want to do something like this:
var encodedPath = google.maps.geometry.encoding.encodePath( _poly.getPath() ).replace(/\\/g,'\\\\');
You can then feed that encoded string into a new Map's Polyline instance like so:
_poly.setPath( google.maps.geometry.encoding.decodePath( encodedPath ) );
It seems like an oversight that this double-backslash issue isn't mentioned in the Google documentation. I spent hours before trying to figure out the problem in my mobile app before coming across the fix.

Finally, after you set a decoded path as the data for a Polyline, use the following code to fit the Map to the bounds of your path:
var bounds = new google.maps.LatLngBounds();
var path = _poly.getPath();
path.forEach(function( latlng ) {
	bounds.extend( latlng );
});
_map.fitBounds( bounds );