<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -4,32 +4,47 @@ require 'curves/hermite'
 
 module CelticKnot
   class Builder
-    attr_reader :graph, :knot
+    attr_reader :graph, :knot, :state
 
     def initialize(graph)
       @graph = graph
       @knot = Knot.new
-      @node_i = @edge_j = @thread_i = @connection_j = 0
-      @edge = @thread = @direction = @midpoint = nil
-      @step = 0
+      set_state(:crossings)
     end
 
     def next_step
-      @step += 1
-      if @node_i &lt; graph.nodes.length
+      case @state
+      when :crossings
         if @thread
           continue_thread
         else
           start_thread
         end
-      elsif @thread_i &lt; knot.threads.length
-        @thread = knot.threads[@thread_i]
+      when :curves
         define_thread
+      when :overlap
+        compute_overlaps
+      when :done
+        return nil
+      else
+        raise &quot;invalid state #{state.inspect}&quot;
       end
     end
         
     private
 
+      def set_state(state)
+        @state = state
+        @node_i = @edge_j = @thread_i = 0
+        @edge = @thread = @connection = @direction = @midpoint = nil
+        @stack = []
+      end
+
+      def transition_to(state)
+        set_state(state)
+        return next_step
+      end
+
       def next_edge
         @edge_j += 1
 
@@ -41,7 +56,9 @@ module CelticKnot
 
       def start_thread
         loop do
-          @node = graph.nodes[@node_i] or return nil
+          return transition_to(:curves) if @node_i &gt;= graph.nodes.length
+
+          @node = graph.nodes[@node_i]
           @edge = @node.edges[@edge_j]
 
           if @edge.nil?
@@ -55,6 +72,7 @@ module CelticKnot
           else
 puts &quot;thread ------&gt;&quot;
             @thread = knot.new_thread
+            @step = 0
 
             # direction is either &quot;clockwise&quot; or &quot;counter-clockwise&quot;,
             # and refers to the direction the cable would travel if it were to
@@ -75,6 +93,8 @@ puts &quot;thread ------&gt;&quot;
       end
 
       def continue_thread
+        @step += 1
+
         far = @edge.other(@node)
         parallel = far - @node
         vector = @edge.vector(parallel, @direction)
@@ -87,6 +107,7 @@ puts &quot;thread ------&gt;&quot;
 
         far_edge = far.nearest_edge_to(@edge, @direction)
         difference = @edge.difference(far_edge, @direction)
+        difference = 1.0 if difference == 0.0
 
 puts &quot;%d: %s %s&quot; % [@step, @node, @edge]
 puts &quot;   far edge:   %s&quot; % far_edge
@@ -95,7 +116,7 @@ puts &quot;   vector:     %s&quot; % vector
 puts &quot;   midpoint:   %s&quot; % @midpoint
 puts &quot;   direction:  %s&quot; % @direction
 puts &quot;   difference: %f&quot; % difference
-        @thread.add_connection(@midpoint, vector, 4.5 * difference)
+        @thread.add_connection(@midpoint, vector, 4.5 * difference, @edge.normal?)
 
         @edge.mark(@node, @direction)
 
@@ -117,20 +138,59 @@ puts &quot;   difference: %f&quot; % difference
       end
 
       def next_connection
-        @connection_j += 1
+        @connection = @connection[:next]
 
-        if @connection_j &gt;= @thread.connections.length
-          @connection_j = 0
+        if @connection == thread.head
           @thread_i += 1
+          @connection = thread &amp;&amp; thread.head
         end
       end
 
+      def thread
+        knot.threads[@thread_i]
+      end
+
       def define_thread
-        near = @thread.connections[@connection_j]
-        far = @thread.connections[(@connection_j + 1) % @thread.connections.length]
+        return transition_to(:overlap) if thread.nil?
+
+        @connection ||= thread.head
+
+        near = @connection
+        far = @connection[:next]
+
         near[:curve] = Curves::Hermite.new([near[:at], near[:vector] * near[:magnitude], far[:at], far[:vector] * near[:magnitude]])
+
         next_connection
+
         return knot
       end
+
+      def compute_overlaps
+        worker = Proc.new do |start, offset|
+          c = start
+          loop do
+            if c[:intersection]
+              if c[:offset]
+                offset = c[:offset]
+              else
+                c[:offset] = offset
+              end
+
+              offset = -offset
+
+              crosses = knot.overlaps[c[:at]]
+              other = crosses[0][:thread] == thread ? crosses[1] : crosses[0]
+              worker[other, offset] unless other[:offset]
+            end
+
+            c = c[:next]
+            break if c == start
+          end
+        end
+
+        knot.threads.each { |thread| worker[thread.head, 1] }
+
+        return transition_to(:done)
+      end
   end
 end</diff>
      <filename>lib/celtic_knot/builder.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,13 +3,15 @@ require 'celtic_knot/thread'
 module CelticKnot
   class Knot
     attr_reader :threads
+    attr_reader :overlaps
 
     def initialize
       @threads = []
+      @overlaps = Hash.new { |h,k| h[k] = [] }
     end
 
     def new_thread
-      thread = Thread.new
+      thread = Thread.new(self)
       @threads &lt;&lt; thread
       return thread
     end</diff>
      <filename>lib/celtic_knot/knot.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,20 +1,50 @@
 module CelticKnot
   class Thread
-    attr_reader :connections
+    attr_reader :knot
+    attr_reader :head
+    attr_reader :tail
 
-    def initialize
-      @connections = []
+    def initialize(knot)
+      @knot = knot
+      @head = @tail = nil
     end
 
-    def add_connection(point, vector, magnitude)
-      connections &lt;&lt; { :at =&gt; point, :vector =&gt; vector, :vnorm =&gt; vector.normalize, :magnitude =&gt; magnitude }
+    def each_connection
+      c = head
+      loop do
+        yield c
+        c = c[:next]
+        break if c == head
+      end
+      self
+    end
+
+    def add_connection(point, vector, magnitude, intersection)
+      connection = { :at           =&gt; point,
+                     :vector       =&gt; vector,
+                     :vnorm        =&gt; vector.normalize,
+                     :magnitude    =&gt; magnitude,
+                     :thread       =&gt; self,
+                     :intersection =&gt; intersection }
+
+      knot.overlaps[point] &lt;&lt; connection
+
+      @head ||= connection
+      @tail ||= connection
+
+      @tail[:next] = connection
+      @head[:prev] = connection
+
+      connection[:prev] = @tail
+      connection[:next] = @head
+
+      @tail = connection
+
       self
     end
 
     def closes?(point, vector)
-      connections.first &amp;&amp;
-        connections.first[:at] == point &amp;&amp;
-        connections.first[:vnorm] == vector.normalize
+      head &amp;&amp; head[:at] == point &amp;&amp; head[:vnorm] == vector.normalize
     end
   end
 end</diff>
      <filename>lib/celtic_knot/thread.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>95db7ecc70ce48b0688610361d4598e2ae1311ba</id>
    </parent>
  </parents>
  <author>
    <name>Jamis Buck</name>
    <email>jamis@37signals.com</email>
  </author>
  <url>http://github.com/jamis/celtic_knot/commit/afa29ac19a6e6792895c627ff49c6ea75a6bb9cb</url>
  <id>afa29ac19a6e6792895c627ff49c6ea75a6bb9cb</id>
  <committed-date>2009-06-07T21:11:49-07:00</committed-date>
  <authored-date>2009-06-07T21:11:49-07:00</authored-date>
  <message>get over/under calculations working right</message>
  <tree>9059322051500d6cb708b049cefa1dec6b7950d1</tree>
  <committer>
    <name>Jamis Buck</name>
    <email>jamis@37signals.com</email>
  </committer>
</commit>
