anchor.diff:        r = new Room.temp(name);
         var lStream = querySelector('canvas').onMouseMove.listen((ev) {
             r.tmpPoint = r.lock(new Point.fromPoint(ev.page));
  +
  +          // can't have overlapping lines
  +          if (r.points.length < 2)
  +            return;
  +
  +          // don't allow overlapping lines
  +          var last = r.points.last;
  +
  +          bool tmpIsVert = (r.tmpPoint.x == r.points.last.x);
  +          r.tmpInvalid = (r.isVertEdge(r.points.length - 2) == tmpIsVert);
  +
  +          // don't allow the new line to go the same direction as the last
  +          if (r.tmpInvalid)
  +            return;
  +
  +          // check all edges up-to, but not including, the last edge
  +          for (int i = 0; i < r.points.length - 2; i++) {
  +            Point a = r.points[i];
  +            Point b = r.points[i + 1];
  +
  +            bool isVert = r.isVertEdge(i);
  +            if (r.isVertEdge(i)) {
  +              if (tmpIsVert) {
  +                // line overlaps another vertical line
  +                if (r.tmpPoint.x == r.points[i].x && r.tmpPoint.y <= max(a.y, b.y) && r.tmpPoint.y >= min(a.y, b.y)) {
  +                  r.tmpInvalid = true;
  +                  break;
  +                }
  +              } else if ((r.tmpPoint.x < r.points[i].x) == (last.x > r.points[i].x)) {
  +                r.tmpInvalid = true;
  +                break;
  +              }
  +            } else if (r.isHorizEdge(i)) {
  +              if (!tmpIsVert) {
  +                // line overlaps another horizontal line
  +                if (r.tmpPoint.y == r.points[i].y && r.tmpPoint.x <= max(a.x, b.x) && r.tmpPoint.x >= min(a.x, b.x)) {
  +                  r.tmpInvalid = true;
  +                  break;
  +                }
  +              } else if ((r.tmpPoint.y < r.points[i].y) == (last.y > r.points[i].y)) {
  +                r.tmpInvalid = true;
  +                break;
  +              }
  +            } else {
  +              // TODO: allow diagonal lines
  +              print("Invalid edge");
  +            }
  +          }
           });
         querySelector('canvas').onMouseDown
           .takeWhile((ev) => r.points.isEmpty || r.points.first.distanceTo(ev.page) > tolerance)
  -        .listen((ev) => r.points.add(r.tmpPoint), onDone: () {
  +        .listen((ev) { if (!r.tmpInvalid) r.points.add(r.tmpPoint); }, onDone: () {
               lStream.cancel();
               r.fixup();
               newRoom();
  
positive.code:   void offset(int x, int y, [bool lock]) => this.points.forEach((p) => p.offset(x, y));
  
    void offsetEdge(int edge, int x, int y, [bool lock]) {
      int next = (edge + 1) % points.length;
      Point a = points[edge];
      Point b = points[next];
  
      if (edge != this._lockEdge && this._lockEdge != null) {
        // unlock old points
  
negative.code:   int onEdge(Point p) {
      int best;
      num bestDistance = double.INFINITY;
      for (int i = 0; i < this.points.length; i++) {
        int second = (i + 1) % this.points.length;
  
        Point a = this.points[i];
        Point b = this.points[second];
  
        num dist;
        if (a.x == b.x) {
  