diff --git a/src/path/Curve.js b/src/path/Curve.js
index 68524c0c..82d9ca46 100644
--- a/src/path/Curve.js
+++ b/src/path/Curve.js
@@ -730,7 +730,15 @@ statics: {
                 : v;
     },
 
-    isFlatEnough: function(v, tolerance) {
+    /**
+     * Determines if a curve is sufficiently flat, meaning it appears as a
+     * straight line and has curve-time that is enough linear, as specified by
+     * the given `flatness` parameter.
+     *
+     * @param {Number} flatness the maximum error allowed for the straight line
+     *     to deviate from the curve
+     */
+    isFlatEnough: function(v, flatness) {
         // Thanks to Kaspar Fischer and Roger Willcocks for the following:
         // http://hcklbrrfnn.files.wordpress.com/2012/08/bez.pdf
         var p1x = v[0], p1y = v[1],
@@ -742,7 +750,7 @@ statics: {
             vx = 3 * c2x - 2 * p2x - p1x,
             vy = 3 * c2y - 2 * p2y - p1y;
         return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
-                < 10 * tolerance * tolerance;
+                <= 16 * flatness * flatness;
     },
 
     getArea: function(v) {
diff --git a/src/path/Path.js b/src/path/Path.js
index 7c697024..3689c03c 100644
--- a/src/path/Path.js
+++ b/src/path/Path.js
@@ -1202,42 +1202,6 @@ var Path = PathItem.extend(/** @lends Path# */{
         return this;
     },
 
-    reverse: function() {
-        this._segments.reverse();
-        // Reverse the handles:
-        for (var i = 0, l = this._segments.length; i < l; i++) {
-            var segment = this._segments[i];
-            var handleIn = segment._handleIn;
-            segment._handleIn = segment._handleOut;
-            segment._handleOut = handleIn;
-            segment._index = i;
-        }
-        // Clear curves since it all has changed.
-        this._curves = null;
-        // Flip clockwise state if it's defined
-        if (this._clockwise !== undefined)
-            this._clockwise = !this._clockwise;
-        this._changed(/*#=*/Change.GEOMETRY);
-    },
-
-    flatten: function(maxDistance) {
-        var iterator = new PathIterator(this, 64, 0.1),
-            pos = 0,
-            // Adapt step = maxDistance so the points distribute evenly.
-            step = iterator.length / Math.ceil(iterator.length / maxDistance),
-            // Add/remove half of step to end, so imprecisions are ok too.
-            // For closed paths, remove it, because we don't want to add last
-            // segment again
-            end = iterator.length + (this._closed ? -step : step) / 2;
-        // Iterate over path and evaluate and add points at given offsets
-        var segments = [];
-        while (pos <= end) {
-            segments.push(new Segment(iterator.getPointAt(pos)));
-            pos += step;
-        }
-        this.setSegments(segments);
-    },
-
     /**
      * Reduces the path by removing curves that have a length of 0,
      * and unnecessary segments between two collinear flat curves.
@@ -1261,6 +1225,38 @@ var Path = PathItem.extend(/** @lends Path# */{
         return this;
     },
 
+    // NOTE: Documentation is in PathItem#reverse()
+    reverse: function() {
+        this._segments.reverse();
+        // Reverse the handles:
+        for (var i = 0, l = this._segments.length; i < l; i++) {
+            var segment = this._segments[i];
+            var handleIn = segment._handleIn;
+            segment._handleIn = segment._handleOut;
+            segment._handleOut = handleIn;
+            segment._index = i;
+        }
+        // Clear curves since it all has changed.
+        this._curves = null;
+        // Flip clockwise state if it's defined
+        if (this._clockwise !== undefined)
+            this._clockwise = !this._clockwise;
+        this._changed(/*#=*/Change.GEOMETRY);
+    },
+
+    // NOTE: Documentation is in PathItem#flatten()
+    flatten: function(flatness) {
+        // Use PathIterator to subdivide the curves into parts that are flat
+        // enough, as specified by `flatness` / Curve.isFlatEnough():
+        var iterator = new PathIterator(this, flatness || 0.25, 256, true),
+            parts = iterator.parts,
+            segments = [];
+        for (var i = 0, l = parts.length; i < l; i++) {
+            segments.push(new Segment(parts[i].curve.slice(0, 2)));
+        }
+        this.setSegments(segments);
+    },
+
     // NOTE: Documentation is in PathItem#simplify()
     simplify: function(tolerance) {
         var segments = new PathFitter(this).fit(tolerance || 2.5);
@@ -2183,7 +2179,7 @@ new function() { // Scope for drawing
                         // Use PathIterator to draw dashed paths:
                         if (!dontStart)
                             ctx.beginPath();
-                        var iterator = new PathIterator(this, 32, 0.25,
+                        var iterator = new PathIterator(this, 0.25, 32, false,
                                 strokeMatrix),
                             length = iterator.length,
                             from = -style.getDashOffset(), to,
diff --git a/src/path/PathItem.js b/src/path/PathItem.js
index 404b43ff..e4ace31d 100644
--- a/src/path/PathItem.js
+++ b/src/path/PathItem.js
@@ -388,14 +388,14 @@ var PathItem = Item.extend(/** @lends PathItem# */{
      */
 
     /**
-     * Converts the curves in a path to straight lines with an even distribution
-     * of points. The distance between the produced segments is as close as
-     * possible to the value specified by the `maxDistance` parameter.
+     * Flattens the curves in path items to a sequence of straight lines, by
+     * subdividing them enough times until the specified maximum error is met.
      *
      * @name PathItem#flatten
      * @function
      *
-     * @param {Number} maxDistance the maximum distance between the points
+     * @param {Number} flatness the maximum error between the flattened lines
+     *     and the original curves
      *
      * @example {@paperscript}
      * // Flattening a circle shaped path:
@@ -414,8 +414,8 @@ var PathItem = Item.extend(/** @lends PathItem# */{
      * var copy = path.clone();
      * copy.position.x += 150;
      *
-     * // Convert its curves to points, with a max distance of 20:
-     * copy.flatten(20);
+     * // Convert its curves to points, with a maximum error of 10:
+     * copy.flatten(10);
      */
 
     // TODO: Write about negative indices, and add an example for ranges.
diff --git a/src/path/PathIterator.js b/src/path/PathIterator.js
index a3424e8c..d2db26b1 100644
--- a/src/path/PathIterator.js
+++ b/src/path/PathIterator.js
@@ -19,26 +19,34 @@ var PathIterator = Base.extend({
     _class: 'PathIterator',
 
     /**
-     * Creates a path iterator for the given path.
+     * Creates a path iterator for the given path. The iterator converts curves
+     * into a sequence of straight lines by the use of curve-subdivision with an
+     * allowed maximum error to create a lookup table that maps curve-time to
+     * path offsets, and can be used for efficient iteration over the full
+     * length of the path, and getting points / tangents / normals and curvature
+     * in path offset space.
      *
-     * @param {Path} path the path to iterate over
+     * @param {Path} path the path to create the iterator for
+     * @param {Number} [flatness=0.25] the maximum error allowed for the
+     *     straight lines to deviate from the original curves
      * @param {Number} [maxRecursion=32] the maximum amount of recursion in
-     * curve subdivision when mapping offsets to curve parameters
-     * @param {Number} [tolerance=0.25] the error tolerance at which the
-     * recursion is interrupted before the maximum number of iterations is
-     * reached
+     *     curve subdivision when mapping offsets to curve parameters
+     * @param {Boolean} [ignoreStraight=false] if only interested in the result
+     *     of the sub-division (e.g. for path flattening), passing `true` will
+     *     protect straight curves from being subdivided for curve-time
+     *     translation
      * @param {Matrix} [matrix] the matrix by which to transform the path's
-     * coordinates without modifying the actual path.
+     *     coordinates without modifying the actual path.
      * @return {PathIterator} the newly created path iterator
      */
-    initialize: function(path, maxRecursion, tolerance, matrix) {
+    initialize: function(path, flatness, maxRecursion, ignoreStraight, matrix) {
         // Instead of relying on path.curves, we only use segments here and
         // get the curve values from them.
         var curves = [], // The curve values as returned by getValues()
             parts = [], // The calculated, subdivided parts of the path
             length = 0, // The total length of the path
             // By default, we're not subdividing more than 32 times.
-            minDifference = 1 / (maxRecursion || 32),
+            minSpan = 1 / (maxRecursion || 32),
             segments = path._segments,
             segment1 = segments[0],
             segment2;
@@ -51,29 +59,31 @@ var PathIterator = Base.extend({
             computeParts(curve, segment1._index, 0, 1);
         }
 
-        function computeParts(curve, index, minT, maxT) {
+        function computeParts(curve, index, t1, t2) {
             // Check if the t-span is big enough for subdivision.
-            if ((maxT - minT) > minDifference
-                    // After quite a bit of testing, a default tolerance of 0.25
+            if ((t2 - t1) > minSpan
+                    && !(ignoreStraight && Curve.isStraight(curve))
+                    // After quite a bit of testing, a default flatness of 0.25
                     // appears to offer a good trade-off between speed and
                     // precision for display purposes.
-                    && !Curve.isFlatEnough(curve, tolerance || 0.25)) {
-                var split = Curve.subdivide(curve, 0.5),
-                    halfT = (minT + maxT) / 2;
+                    && !Curve.isFlatEnough(curve, flatness || 0.25)) {
+                var halves = Curve.subdivide(curve, 0.5),
+                    tMid = (t1 + t2) / 2;
                 // Recursively subdivide and compute parts again.
-                computeParts(split[0], index, minT, halfT);
-                computeParts(split[1], index, halfT, maxT);
+                computeParts(halves[0], index, t1, tMid);
+                computeParts(halves[1], index, tMid, t2);
             } else {
-                // Calculate distance between p1 and p2
-                var x = curve[6] - curve[0],
-                    y = curve[7] - curve[1],
-                    dist = Math.sqrt(x * x + y * y);
-                if (dist > /*#=*/Numerical.TOLERANCE) {
+                // Calculate the length of the curve interpreted as a line.
+                var dx = curve[6] - curve[0],
+                    dy = curve[7] - curve[1],
+                    dist = Math.sqrt(dx * dx + dy * dy);
+                if (dist > 0) {
                     length += dist;
                     parts.push({
                         offset: length,
-                        value: maxT,
-                        index: index
+                        curve: curve,
+                        index: index,
+                        time: t2,
                     });
                 }
             }
@@ -86,16 +96,15 @@ var PathIterator = Base.extend({
         }
         if (path._closed)
             addCurve(segment2, segments[0]);
-
         this.curves = curves;
         this.parts = parts;
         this.length = length;
         // Keep a current index from the part where we last where in
-        // getTimeAt(), to optimise for iterator-like usage of iterator.
+        // _get(), to optimise for iterator-like usage of iterator.
         this.index = 0;
     },
 
-    getTimeAt: function(offset) {
+    _get: function(offset) {
         // Make sure we're not beyond the requested offset already. Search the
         // start position backwards from where to then process the loop below.
         var i, j = this.index;
@@ -116,41 +125,41 @@ var PathIterator = Base.extend({
                 var prev = this.parts[i - 1];
                 // Make sure we only use the previous parameter value if its
                 // for the same curve, by checking index. Use 0 otherwise.
-                var prevVal = prev && prev.index == part.index ? prev.value : 0,
-                    prevLen = prev ? prev.offset : 0;
+                var prevTime = prev && prev.index === part.index ? prev.time : 0,
+                    prevOffset = prev ? prev.offset : 0;
                 return {
+                    index: part.index,
                     // Interpolate
-                    value: prevVal + (part.value - prevVal)
-                        * (offset - prevLen) / (part.offset - prevLen),
-                    index: part.index
+                    time: prevTime + (part.time - prevTime)
+                        * (offset - prevOffset) / (part.offset - prevOffset)
                 };
             }
         }
-        // Return last one
+        // If we're still here, return last one
         var part = this.parts[this.parts.length - 1];
         return {
-            value: 1,
-            index: part.index
+            index: part.index,
+            time: 1
         };
     },
 
     drawPart: function(ctx, from, to) {
-        from = this.getTimeAt(from);
-        to = this.getTimeAt(to);
-        for (var i = from.index; i <= to.index; i++) {
+        var start = this._get(from),
+            end =  this._get(to);
+        for (var i = start.index, l = end.index; i <= l; i++) {
             var curve = Curve.getPart(this.curves[i],
-                    i == from.index ? from.value : 0,
-                    i == to.index ? to.value : 1);
-            if (i == from.index)
+                    i === start.index ? start.time : 0,
+                    i === end.index ? end.time : 1);
+            if (i === start.index)
                 ctx.moveTo(curve[0], curve[1]);
             ctx.bezierCurveTo.apply(ctx, curve.slice(2));
         }
     }
 }, Base.each(Curve._evaluateMethods,
     function(name) {
-        this[name + 'At'] = function(offset, weighted) {
-            var param = this.getTimeAt(offset);
-            return Curve[name](this.curves[param.index], param.value, weighted);
+        this[name + 'At'] = function(offset) {
+            var param = this._get(offset);
+            return Curve[name](this.curves[param.index], param.time);
         };
     }, {})
 );
diff --git a/test/tests/Path.js b/test/tests/Path.js
index a392a74b..30aa0a8f 100644
--- a/test/tests/Path.js
+++ b/test/tests/Path.js
@@ -370,8 +370,12 @@ test('path.curves on closed paths', function() {
 test('path.flatten(maxDistance)', function() {
     var path = new Path.Circle(new Size(80, 50), 35);
 
-    // Convert its curves to points, with a max distance of 20:
-    path.flatten(20);
+    // Convert its curves to points, with a flatness of 5:
+    path.flatten(5);
+
+    equals(function() {
+        return path.segments.length;
+    }, 8, 'Using a flatness of 10, we should end up with 8 segments.');
 
     equals(function() {
         return path.lastSegment.point.equals(path.firstSegment.point);