From b906171e448983162ef5d2785fbd096116ede4bd Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Sun, 21 Sep 2014 18:21:45 -0400 Subject: [PATCH 1/2] Add unit tests for Rect's .trace() method --- test/rect_test.dart | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/rect_test.dart b/test/rect_test.dart index 1affac9..ac130b3 100644 --- a/test/rect_test.dart +++ b/test/rect_test.dart @@ -102,6 +102,39 @@ void main() { expect(new Rect(1, 2, 3, 4).toString(), equals("(1, 2)-(3, 4)")); }); + group(".trace()", () { + + checkOutline(Rect rect, Iterable points) { + Set outline = new Set.from(points); + for (var coord in rect.trace()) { + expect(coord, isIn(outline)); + // we only want to iterate over each once + outline.remove(coord); + } + // need to hit every point + expect(outline, isEmpty); + } + + test("iterates over every point in the outline exactly once", () { + checkOutline(new Rect(1, 2, 3, 4), [ + new Vec(1, 2), new Vec(2, 2), new Vec(3, 2), + new Vec(1, 3), new Vec(3, 3), + new Vec(1, 4), new Vec(3, 4), + new Vec(1, 5), new Vec(2, 5), new Vec(3, 5) + ]); + }); + + test("doesn't iterate over points in row Rects twice", () { + Rect row = new Rect.row(1, 2, 3); + checkOutline(row, row); // make sure that the outline and interior are the same + }); + + test("doesn't iterate over points in column Rects twice", () { + Rect col = new Rect.column(1, 2, 3); + checkOutline(col, col); + }); + }); + // TODO: intersect(). // TODO: centerIn(). // TODO: center. @@ -111,5 +144,4 @@ void main() { // TODO: clamp(). // TODO: iterator. // TODO: distanceTo(). - // TODO: trace(). } From 9e1376e1c6b59912411bf55e0ea04d907cc9008e Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Sun, 21 Sep 2014 19:48:19 -0400 Subject: [PATCH 2/2] Reimplement Rect.trace() so that it uses an iterator, instead of building up a (potentially) huge list of Vecs --- lib/src/rect.dart | 105 +++++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 28 deletions(-) diff --git a/lib/src/rect.dart b/lib/src/rect.dart index fb63b33..4172575 100644 --- a/lib/src/rect.dart +++ b/lib/src/rect.dart @@ -180,34 +180,7 @@ class Rect extends IterableBase { /// Iterates over the points along the edge of the Rect. Iterable trace() { - if ((width > 1) && (height > 1)) { - // TODO(bob): Implement an iterator class here if building the list is - // slow. - // Trace all four sides. - final result = []; - - for (var x = left; x < right; x++) { - result.add(new Vec(x, top)); - result.add(new Vec(x, bottom - 1)); - } - - for (var y = top + 1; y < bottom - 1; y++) { - result.add(new Vec(left, y)); - result.add(new Vec(right - 1, y)); - } - - return result; - } else if ((width > 1) && (height == 1)) { - // A single row. - return new Rect.row(left, top, width); - } else if ((height >= 1) && (width == 1)) { - // A single column, or one unit - return new Rect.column(left, top, height); - } - - // Otherwise, the rect doesn't have a positive size, so there's nothing to - // trace. - return const []; + return new _RectTracer(this); } // TODO: Equality operator and hashCode. @@ -235,3 +208,79 @@ class RectIterator implements Iterator { return _y < _rect.bottom; } } + +class _RectTracer extends Object with IterableMixin { + final Rect _rect; + _RectTracer(this._rect); + + Iterator get iterator { + if (_rect.width.abs() > 2 && _rect.height.abs() > 2) { + // the rect has points in the middle that are not part of its outline. + return new _RectOutlineIterator(_rect); + } else { + // all the points in the rect are contained in its outline. + return _rect.iterator; + } + } +} + +class _RectOutlineIterator implements Iterator { + final Rect _rect; + // Values are produced two at a time, so we store both the current value, and + // the next (_pending) value we're going to return. + Vec _current; + Vec _pending; + // Are we moving over the rectangle horizontally or vertically? + bool _horizontal; + // Our position along the x or y axis. + int _pos; + _RectOutlineIterator(this._rect) { + assert(_rect.width.abs() > 1); + assert(_rect.height.abs() > 1); + + _pending = null; + _current = null; + _horizontal = true; + _pos = _rect.left; + } + + Vec get current => _current; + + bool moveNext() { + if (_pending != null) { + _current = _pending; + _pending = null; + return true; + } + return _horizontal ? moveNextHorz_() : moveNextVert_(); + } + + bool moveNextHorz_() { + if (_pos == _rect.right) { + // Finished iterating horizontally. + _pos = _rect.top+1; + _horizontal = false; + return moveNextVert_(); + } else { + _current = new Vec(_pos, _rect.top); + _pending = new Vec(_pos, _rect.bottom-1); + ++_pos; + } + return true; + } + + bool moveNextVert_() { + if (_pos == _rect.bottom - 1) { + // Completely done + _current = null; + return false; + } + _current = new Vec(_rect.left, _pos); + _pending = new Vec(_rect.right-1, _pos); + ++_pos; + return true; + } + +} + +