<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,32 +1,65 @@
+require 'curves/line_segment'
+
 module CelticKnot
   class KnotShape
+    DEFAULT_WIDTH = 10
+
     attr_reader :knot
     attr_reader :options
-    attr_reader :segments
+    attr_reader :polygons
 
     def initialize(knot, options={})
       @knot = knot
       @options = options
 
       @segments = {}
+      @polygons = []
 
       inflate_threads
-      #cull_overlaps
+      subtract_intersections
+      construct_polygons
+    end
+
+    def width
+      options.fetch(:width, DEFAULT_WIDTH).to_f
+    end
+
+    def separation
+      [0.0, options.fetch(:separation, 0).to_f].max
     end
 
     private
 
+      class Polygon
+        attr_reader :thread
+        attr_reader :points
+
+        def initialize(thread)
+          @thread = thread
+          @points = []
+        end
+      end
+
       class ThreadSegment
         attr_reader :left
         attr_reader :right
-        attr_reader :connection
 
-        def initialize(connection)
-          @connection = connection
+        def initialize
           @left = []
           @right = []
         end
 
+        def append(segment)
+          if left.last == segment.left.first
+            left.pop
+            right.pop
+          end
+
+          left.concat(segment.left)
+          right.concat(segment.right)
+          return self
+        end
+
         def cull_interior_overlaps!
           cull_overlap_for_list(left)
           cull_overlap_for_list(right)
@@ -43,15 +76,13 @@ module CelticKnot
           def cull_overlap_for_list(list)
             i = 0
             while i &lt; list.length-2
-              p1 = list[i]
-              p2 = list[i+1]
+              line1 = Curves::LineSegment.new(list[i], list[i+1])
 
               j = i+1
               while j &lt; list.length-1
-                p3 = list[j]
-                p4 = list[j+1]
+                line2 = Curves::LineSegment.new(list[j], list[j+1])
 
-                if (intersection = intersection_of(p1, p2, p3, p4))
+                if (intersection = line1.intersection(line2))
                   while i &lt; j
                     i += 1
                     list[i] = intersection
@@ -65,43 +96,139 @@ module CelticKnot
               i += 1
             end
           end
+      end
 
-          def intersection_of(p1, p2, p3, p4)
-            denom = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y)
-            return nil if denom == 0.0
+      def inflate_threads
+        half_width = width / 2
+
+        knot.threads.each do |thread|
+          thread.each_connection do |c|
+            @segments[c] = inflate_connection(c, half_width, 0.0, 1.0)
+          end
+        end
+      end
+
+      def inflate_connection(c, offset, t0, t1)
+        segment = ThreadSegment.new
+
+        t0.step(t1 + 0.01, 0.05) do |t|
+          point = c[:curve].evaluate(t)
+          normal = c[:curve].tangent(t).rotate_ccw.normalize
+          vector = normal * offset
+
+          segment.left &lt;&lt; point + vector
+          segment.right &lt;&lt; point - vector
+        end
+
+        segment.cull_interior_overlaps!
+        return segment
+      end
 
-            ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom
-            return nil if ua &lt;= 0.0 || ua &gt;= 1.0
+      def subtract_intersections
+        offset = width / 2 + separation
 
-            ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom
-            return nil if ub &lt;= 0.0 || ub &gt;= 1.0
+        knot.overlaps.each do |point, list|
+          next if list.length &lt; 2
+          lower, upper = list[0][:offset] &lt; list[1][:offset] ? list : [list[1], list[0]]
 
-            intersection = p1 + (p2 - p1) * ua
-            return intersection
+          bounds = inflate_connection(upper[:prev], offset, 0.5, 1.0).
+            append(inflate_connection(upper, offset, 0.0, 0.5))
+
+          left = upper[:vector].rotate_ccw
+
+          # if the normal facing left of the direction of the upper thread points in
+          # the same direction as the lower thread, then the lower thread intersects
+          # with the left side of the inflated upper thread, and lower[:prev]
+          # intersections with the right side of the inflated upper thread. If the
+          # normal faces the other direction, then the intersections are swapped.
+
+          if left.dot(lower[:vector]) &gt; 0
+            subtract_intersection_between(@segments[lower], bounds.left, :start)
+            subtract_intersection_between(@segments[lower[:prev]], bounds.right, :end)
+          else
+            subtract_intersection_between(@segments[lower[:prev]], bounds.left, :end)
+            subtract_intersection_between(@segments[lower], bounds.right, :start)
           end
+        end
       end
 
-      def inflate_threads
-        width = options.fetch(:width, 10).to_f
-        half_width = width / 2
+      def subtract_intersection_between(segment, boundary, from)
+        operation = from == :start ? :shift : :pop
+        reverseop = from == :start ? :unshift : :push
 
-        knot.threads.each do |thread|
-          thread.each_connection do |c|
-            segment = ThreadSegment.new(c)
+        lp1 = segment.left.send(operation)
+        rp1 = segment.right.send(operation)
+
+        found_left = found_right = false
 
-            0.step(1.01, 0.05) do |t|
-              point = c[:curve].evaluate(t)
-              normal = c[:curve].tangent(t).rotate_ccw.normalize
-              offset = normal * half_width
+        until found_left &amp;&amp; found_right
+          unless found_left
+            lp2 = segment.left.send(operation)
+            lline = Curves::LineSegment.new(lp1, lp2)
+          end
 
-              segment.left &lt;&lt; point + offset
-              segment.right &lt;&lt; point - offset
+          unless found_right
+            rp2 = segment.right.send(operation)
+            rline = Curves::LineSegment.new(rp1, rp2)
+          end
+          
+          p3 = boundary[0]
+          1.upto(boundary.length-1) do |j|
+            p4 = boundary[j]
+            boundary_line = Curves::LineSegment.new(p3, p4)
+
+            if !found_left &amp;&amp; (left_intersection = lline.intersection(boundary_line))
+              found_left = j
+              segment.left.send(reverseop, lp2)
+              segment.left.send(reverseop, left_intersection)
             end
 
-            segment.cull_interior_overlaps!
-            segments[c] = segment
+            if !found_right &amp;&amp; (right_intersection = rline.intersection(boundary_line))
+              found_right = j
+              segment.right.send(reverseop, rp2)
+              segment.right.send(reverseop, right_intersection)
+            end
+
+            break if found_left &amp;&amp; found_right
+            p3 = p4
+          end
+
+          lp1, rp1 = lp2, rp2
+        end
+
+        if found_left &lt; found_right
+          lo, hi = found_left, found_right
+          collection = segment.left
+        else
+          lo, hi = found_right, found_left
+          collection = segment.right
+        end
+
+        lo.upto(hi-1) { |j| collection.send(reverseop, boundary[j]) }
+      end
+
+      def construct_polygons
+        knot.threads.each do |thread|
+          thread.each_connection do |c|
+            next unless c[:intersection] &amp;&amp; c[:offset] &lt; 0
+            polygons &lt;&lt; build_polygon_for_segments_from(c)
           end
         end
       end
+
+      def build_polygon_for_segments_from(c)
+        segment = ThreadSegment.new
+        loop do
+          segment.append(@segments[c])
+          c = c[:next]
+          break if c[:offset] &amp;&amp; c[:offset] &lt; 0
+        end
+
+        polygon = Polygon.new(c[:thread])
+        segment.left.each { |pt| polygon.points &lt;&lt; pt }
+        segment.right.reverse.each { |pt| polygon.points &lt;&lt; pt }
+
+        return polygon
+      end
   end
 end</diff>
      <filename>lib/celtic_knot/knot_shape.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>6bf97cee0d907842f5dc1fc849bc1e1d8c5863cd</id>
    </parent>
  </parents>
  <author>
    <name>Jamis Buck</name>
    <email>jamis@37signals.com</email>
  </author>
  <url>http://github.com/jamis/celtic_knot/commit/f9e72a9b238f40f3218762273d5f2db969abb3d9</url>
  <id>f9e72a9b238f40f3218762273d5f2db969abb3d9</id>
  <committed-date>2009-06-13T21:28:48-07:00</committed-date>
  <authored-date>2009-06-13T21:28:48-07:00</authored-date>
  <message>convert thread segments into polygons

Side-effect: more intersection rendering options. I can now
draw intersections where the &quot;under&quot; thread stops before it
intersects the &quot;over&quot; thread.</message>
  <tree>f803829a30adaa3cd838deca6828074fd0a79fd7</tree>
  <committer>
    <name>Jamis Buck</name>
    <email>jamis@37signals.com</email>
  </committer>
</commit>
