Gasket Weaving - draw a Sierpiński knot
Ruby, 1168 932
Corrected a mistake from last night, more golfing to do after clarifying.
This is (currently) a full program that accepts a number from stdin and outputs an svg file to stdout. I chose svg because I knew it was possible to meet all the requirements of the question, but it did have some issues. in particular SVG only supports arcs of circles as part of the path object, and does not define them in terms of their centre but rather the two endpoints.
Code
n=gets.to_i
r=64*w=0.75**0.5
m=1&lt;&lt;n-2
z=128*m/w
a=(s=&quot;&lt;path style='fill:none;stroke:black;stroke-width:3.464102' transform='translate(%f %f)'
&quot;)%[0,r-r*m*8/3]+&quot;d='M18.11943,-2A#{b=r-6*w-32} #{b} 0 0,0 #{-b} 0#{k='A%f %f 0 0 '%([58*w]*2)}0 0,38.71692
M28.58980,1.968882#{l='A%f %f 0 0 '%([70*w]*2)}0 #{c=r+6*w-32} 0A#{c} #{c} 0 0,0 #{-c} 0#{l}0 -9 44.65423'/&gt;&quot;
p=2
m.times{|i|(i*2+1).times{|j|(p&gt;&gt;j)%8%3==2&amp;&amp;a&lt;&lt;s%[128*(j-i),r*3+r*i*4-r*m*8/3]+
&quot;d='M-55,44.65423#{k}0 11.5,25.11473#{l}1 35.41020,1.968882
M-64,51.48786#{l}0 20.5,30.31089#{k}1 36.82830,13.17993
M-82.17170,-2.408529#{l}1 -11.5,25.11473#{k}0 0,38.71692
M-81.52984 8.35435#{k}1 -20.5,30.31089#{l}0 -9,44.65423
M9,44.65423#{k}0 81.52984,8.35435
M0,51.48786#{l}0 91.17169,13.17993'/&gt;&quot;}
p^=p*4}
puts &quot;&lt;svg xmlns='http://www.w3.org/2000/svg' viewBox='#{-z} #{-z} #{e=2*z+1} #{e}' width='#{e}px' height='#{e}px'&gt;&quot;+
&quot;&lt;g transform='rotate(%d)'&gt;#{a}&lt;/g&gt;&quot;*3%[0,120,240]+&quot;&lt;/svg&gt;&quot;

Output N=4
rescaled by Stack exchange. Looks much better as original.

Explanation
At first I considered something like http://euler.nmt.edu/~jstarret/sierpinski.html where the triangle is broken down into three differently coloured strands, each of which forms a path from one corner to another. The incomplete circles are shown as incomplete hexagons there. inscribing circles inside the hexagons shows that the circle radius should be sqrt(3)/2 times the sidelength. The strands could be built up recursively as shown, but there is an added complication because the corners need to be rounded and it is difficult to know in which direction to curve, so I did not use this approach.
What I did was the following.
In the image below you can see there are horizontal twists belonging to the N=2 units (in green) arranged in a sierpinski triangle and additional bridging twists (in blue.)
It is common knowledge that the odd numbers on Pascal's triangle form a sierpinski triangle. A sierpinski triangle of binary digits can be obtained in an analogous manner by starting with the number p=1 and iteratively xoring it with p&lt;&lt;1.
I modified this approach, starting at p=2 and iteratively xoring with p*4. This gives a sierpinski triangle alternating with columns of zeros.
Now we can rightshift p and inspect the last three bits using %8. If they are 010 we need to draw a green twist belonging to an N=2 unit. if they are 101 we need to draw a blue, bridging twist. To test for both of these numbers together we find the modulo %3 and if this is 2 we need to draw the twist.
Finally, in additional to the horizontal twists, we make two copies rotated through 120 and 240 degrees to draw the diagonal twists and complete the picture. All that remains is to add the corners.
Commented code
n=gets.to_i

#r=vertical distance between rows 
r=64*w=0.75**0.5

#m=number of rows of horizontal twists
m=1&lt;&lt;n-2

#z=half the size of the viewport
z=128*m/w

#s=SVG common to all paths
s=&quot;&lt;path style='fill:none;stroke:black;stroke-width:3.464102' transform='translate(%f %f)'
&quot;

#initialize a with SVG to draw top corner loop. Set k and l to the SVG common to all arcs of 58*w and 70*w radius 
a=s%[0,r-r*m*8/3]+
&quot;d='M18.11943,-2A#{b=r-6*w-32} #{b} 0 0,0 #{-b} 0#{k='A%f %f 0 0 '%([58*w]*2)}0 0,38.71692
M28.58980,1.968882#{l='A%f %f 0 0 '%([70*w]*2)}0 #{c=r+6*w-32} 0A#{c} #{c} 0 0,0 #{-c} 0#{l}0 -9 44.65423'/&gt;&quot;

#p is the pattern variable, top row of twists has one twist so set to binary 00000010
p=2

#loop vertically and horizontally
m.times{|i|
 (i*2+1).times{|j|

   #leftshift p. if 3 digits inspected are 010 or 101 
   (p&gt;&gt;j)%8%3==2&amp;&amp;

   #append to a, the common parts of a path...
   a&lt;&lt;s%[128*(j-i),r*3+r*i*4-r*m*8/3]+

   #...and the SVG for the front strand and left and right parts of the back strand (each strand has 2 borders)
&quot;d='M-55,44.65423#{k}0 11.5,25.11473#{l}1 35.41020,1.968882
M-64,51.48786#{l}0 20.5,30.31089#{k}1 36.82830,13.17993
M-82.17170,-2.408529#{l}1 -11.5,25.11473#{k}0 0,38.71692
M-81.52984 8.35435#{k}1 -20.5,30.31089#{l}0 -9,44.65423
M9,44.65423#{k}0 81.52984,8.35435
M0,51.48786#{l}0 91.17169,13.17993'/&gt;&quot;}

#modify the pattern by xoring with 4 times itself for the next row
p^=p*4}

#output complete SVG of correct size with three copies of the finished pattern rotated through 0,120,240 degrees.
puts &quot;&lt;svg xmlns='http://www.w3.org/2000/svg' viewBox='#{-z} #{-z} #{e=2*z+1} #{e}' width='#{e}px' height='#{e}px'&gt;&quot;+
&quot;&lt;g transform='rotate(%d)'&gt;#{a}&lt;/g&gt;&quot;*3%[0,120,240]+&quot;&lt;/svg&gt;&quot;