# Project4_writing_gcode

276

3

12

## Introduction: Project4_writing_gcode

This project was eye-opening. Coding my own Gcode made me understand a lot better my printer: its functionalities and possibilities.

Many (of what I thought were) details became suddenly very important:

• the size of the nozzle
• the thickness of each layer
• the effect of extraction/retraction on the quality of the print
• the speed of the nozzle
• the effect of gravity on the filament countered by drying time

All these components are potential features to play with and explore further in order to make interesting things.

In this assignment, I wanted to make a wearable chain. + I ended up discovering ways to create pattern.

## Supplies

Rhino + Grasshopper

Ender 3 Pro

Filament

## Step 1: Simple Square in Gcode

I started my investigation for this assignment by trying to make a simple empty square of a few layers. After some unsuccessful trials, I was finally able to trace it but it was far from smooth. The filament seemed to have a lot of difficulties to exit the nozzle, the amount of filament seemed to be too important for the straight lines I was trying to print and a weird noise was noticeable.

I realized that the first problem was due to the nozzle starting to print at Z0, so basically very close to the bed. In the next iterations, I started my first layer at 0.1mm (Z0.1) over the bed. The second problem was due to lack of computation of the required amount of material. The variable E in gcode corresponds to the amount of filament that will be extracted from the nozzle for a given command and it does not equal to the distance travelled by the nozzle by this same command since the filament has a diameter of 1.75mm while the nozzle has a diameter of 0.4mm. So I thought about it a little bit longer and realized that the volume of extruded material needed to be the same before the print and after the print of a straight line for instance.

```V1 = V2
=> π * (r_n)^2 * distance = π * (r_f)^2 * E
=> E = [π * (r_n)^2 * distance]/[π * (r_f)^2]```

where r_n is the radius of the nozzle, r_f is the radius of the filament, distance the length of the printed segment and E the amount of filament to extract.

## Step 2: Making a Base in Gcode

To make my base I first looked at the animation in Cura of the fabric Gcode provided by Jennifer. I wanted to understand how the filament was laid down before coding my own base.

Okay.. so the first base I made was a fiasco. The first layer had a really hard time to stick. I realized that I was also making my successive ellipses to close to one another, which was resulting in a really thick pattern full of bumps. The more successful bases have still some irregularities, but since it's just the base, I decided to go to the next step.

The code for the base here:

```"""Provides a scripting component.    Inputs:
x: The x script variable
y: The y script variable
Output:
a: The a output variable"""__author__ = "sambourgault"import rhinoscriptsyntax as rs
import math as math
import Rhino.Geometry as geom# MAKE THE BASE
w = width*10
l = length*10
r = w/2
thickness = 0.4
nbR = r/thickness
angle = 0
theta = 120/360 #20 deg in rad
z = 0.18
c = geom.Point3d(r,r,0)
repeat = True
globalOffset = geom.Point3d(30, 100, 0)sb = []
for k in range(0, 2):
for j in range(0, int(nbR)):
for i in range(0, int(math.ceil(2*math.pi/theta))+1):
if (angle < math.pi):
sb.append(geom.Point3d(c.X - r*math.sin(angle), c.Y - r*math.cos(angle), z)+ globalOffset)
else:
if (repeat):
print("blu")
sb.append(geom.Point3d(c.X - r*math.sin(math.pi), c.Y - r*math.cos(math.pi), z)+geom.Point3d(l-width,0,0)+globalOffset)
repeat = False
sb.append(geom.Point3d(c.X-r*math.sin(angle), c.Y - r*math.cos(angle), z)+geom.Point3d(l-width,0,0)+globalOffset)
angle+=theta
r -= thickness
repeat = True
#theta = 360/36
angle = 0
#z += 0.2
z += 0.2
r = w/2a = sb```

## Step 3: Floating Triangles on Pillars

I did't understand how the Ender32 Pro can do a pillar that has a radius of 0.4mm. So I increased the diameters of my pillar hoping that it would do something. It helped a little but then...

Then I realized that what I was fighting against was extraction/retraction. I didn't realize that the slicer was also taking care of the amount of material to retract when going to a non-continuous location. I also realized that to make my pillars I would have to retract a bit of material each time I go from one pillar to the next one and then extract the same amount!!

This realization made my life a lot easier.

I change my code a little bit and made the amount of retraction 1 mm, which created a very interesting pattern (that I will explore in one of the following steps). But to obtain the desired design I had to set my retraction amount to 6 mm.

## Step 4: Chain of (three) Triangles

After a full day of work, I was able to print a chain of three triangles. The hardest part was to code the chain links in terms of layers. The horizontal triangles are therefore not printed the same way the vertical triangle is, which generate some disparities between them...but 'hey' good enough for now!

My code was a mess (still is..), but the small chain of three triangles actually worked!! Three triangle links are connected to each other and hang happily.

However my code was not well parametrized, there is a lot of "I'll put this number here and see what happens...". The next step required better parametrization.

The code to make the 3d Points for chain is divided in five parts:

```"""Provides a scripting component.    Inputs:
x: The x script variable
y: The y script variable
Output:
a: The a output variable"""__author__ = "sambourgault"import rhinoscriptsyntax as rs
import math
import Rhino.Geometry as geom bw = baseWidth*10
bl = baseLength*10
s = sideLength
pts = []
innerpts = []
botpts = []
midpts1 = []
midpts2 = []
midpts3 = []
toppts = []
angle = 0
theta = 2*math.pi/6
# note that the first layer of the base is at 0.05, the second at 2.25
z = 0.58
r = 0.2
offset = geom.Point3d(30,100,0)+geom.Point3d(bw/3, bw/3,0)#pillars
direction = True
distance = 0.9
#determine the amount of layerslastIndexP = 0;
""" BOTTOM PART """
for k in range(0, int(nbLayer/4)):
#determine the amount of triangle
for i in range(0, nbCol):
#determine the position of the pillar
for n in range (0, 3):
#draw circle
for j in range (0, 6):
if (direction):
if (n == 0):
c = geom.Point3d(distance*s*i, 0, z)+offset
botpts.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
elif (n == 1):
c2 = geom.Point3d(distance*s*i+s, 0, z)+offset
botpts.append(geom.Point3d(c2.X+r*math.cos(angle), c2.Y+r*math.sin(angle), z))
elif (n == 2):
c3 = geom.Point3d(distance*s*i+s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2)), z)+offset
botpts.append(geom.Point3d(c3.X+r*math.cos(angle), c3.Y+r*math.sin(angle), z))
else:
if (n == 0):
c = geom.Point3d(distance*s*(i) +s/2, 0-s/10, z)+offset
botpts.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
elif (n == 1):
c2 = geom.Point3d(distance*s*(i)+s/2 - s +s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset
botpts.append(geom.Point3d(c2.X+r*math.cos(angle), c2.Y+r*math.sin(angle), z))
elif (n == 2):
c3 = geom.Point3d(distance*s*(i)+s/2 +s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset
botpts.append(geom.Point3d(c3.X+r*math.cos(angle), c3.Y+r*math.sin(angle), z))

angle+=theta
angle = 0
direction = not direction

#*math.tan(30/180*math.pi)

# 2 pillars for horizontal triangles
for f in range(0, 2*nbCol):
for j in range (0, 6):
if (f % 2 == 0):
c = geom.Point3d(s/2+(f/2*distance*s), s/4, z) + offset
botpts.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
angle+=theta
else:
c = geom.Point3d(s/2+distance*s+1.2+(f-1)/2*distance*s, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/4+0.6, z) + offset
botpts.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
angle+=theta

direction = True
z += 0.2""" Mid Part 1"""
for k in range(int(nbLayer/2), int(nbLayer/2)+2):
#determine the amount of triangle
for i in range(0, nbCol):
#determine the position of the pillar
for n in range (0, 3):
#draw circle
for j in range (0, 6):
if (direction):
if (n == 0):
c = geom.Point3d(distance*s*i, 0, z)+offset
midpts1.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
elif (n == 1):
c2 = geom.Point3d(distance*s*i+s, 0, z)+offset
midpts1.append(geom.Point3d(c2.X+r*math.cos(angle), c2.Y+r*math.sin(angle), z))
elif (n == 2):
c3 = geom.Point3d(distance*s*i+s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2)), z)+offset
midpts1.append(geom.Point3d(c3.X+r*math.cos(angle), c3.Y+r*math.sin(angle), z))
else:
if (n == 0):
c = geom.Point3d(distance*s*(i) +s/2, 0-s/10, z)+offset
midpts1.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
elif (n == 1):
c2 = geom.Point3d(distance*s*(i)+s/2 - s +s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset
midpts1.append(geom.Point3d(c2.X+r*math.cos(angle), c2.Y+r*math.sin(angle), z))
elif (n == 2):
c3 = geom.Point3d(distance*s*(i)+s/2 +s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset
midpts1.append(geom.Point3d(c3.X+r*math.cos(angle), c3.Y+r*math.sin(angle), z))

angle+=theta

#pts.append(geom.Point3d(s*2*i, 0, z)+offset)
#pts.append(geom.Point3d(s*2*i+s, 0, z)+offset)
#pts.append(geom.Point3d(s*2*i+s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2)), z)+offset)
#z+=0.2
angle = 0
direction = not direction

# 2 pillars for horizontal triangles
print("after pils")
print(len(midpts1))

beta = math.tan((s/3)/(distance*s))

for f in range(0, nbCol):
x = (s/2 + (s/4)*(k-(nbLayer/2))/(nbLayer/2))
p2 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.4, z) + offset
midpts1.append(p2)
p3 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.8, z) + offset
midpts1.append(p3)

x = s/2 + distance*s - (s/4)*(k-(nbLayer/2))/(nbLayer/2)
p2 = geom.Point3d(x+1.2+(f*distance*s), x*math.tan(beta)+0.9, z) + offset
midpts1.append(p2)
p3 = geom.Point3d(x+1.2+(f*distance*s), x*math.tan(beta)+1.3, z) + offset
midpts1.append(p3)

direction = True
z += 0.2
print("mid1")
print(len(midpts1))# Mid Part 2 for k in range(int(nbLayer/2)+2, int(nbLayer/2)+9):
#determine the amount of triangle
for i in range(0, nbCol):
#determine the position of the pillar
for n in range (0, 3):
#draw circle
for j in range (0, 6):
if (direction):
if (n == 0):
c = geom.Point3d(distance*s*i, 0, z)+offset
midpts2.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
elif (n == 1):
c2 = geom.Point3d(distance*s*i+s, 0, z)+offset
midpts2.append(geom.Point3d(c2.X+r*math.cos(angle), c2.Y+r*math.sin(angle), z))
elif (n == 2):
c3 = geom.Point3d(distance*s*i+s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2)), z)+offset
midpts2.append(geom.Point3d(c3.X+r*math.cos(angle), c3.Y+r*math.sin(angle), z))
else:
if (n == 0):
c = geom.Point3d(distance*s*(i) +s/2, 0-s/10, z)+offset
midpts2.append(geom.Point3d(c.X+r*math.cos(angle), c.Y+r*math.sin(angle), z))
elif (n == 1):
c2 = geom.Point3d(distance*s*(i)+s/2 - s +s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset
midpts2.append(geom.Point3d(c2.X+r*math.cos(angle), c2.Y+r*math.sin(angle), z))
elif (n == 2):
c3 = geom.Point3d(distance*s*(i)+s/2 +s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset
midpts2.append(geom.Point3d(c3.X+r*math.cos(angle), c3.Y+r*math.sin(angle), z))

angle+=theta

#pts.append(geom.Point3d(s*2*i, 0, z)+offset)
#pts.append(geom.Point3d(s*2*i+s, 0, z)+offset)
#pts.append(geom.Point3d(s*2*i+s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2)), z)+offset)
#z+=0.2
angle = 0
direction = not direction
#*math.tan(30/180*math.pi)

beta = math.tan((s/3)/(distance*s))

for f in range(0, nbCol):
x = s/2 + (s/4)*(k-(nbLayer/2))/(nbLayer/2)
p2 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.4, z) + offset
midpts2.append(p2)
p3 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.8, z) + offset
midpts2.append(p3)

x = s/2 + s - (s/4)*(k-(nbLayer/2))/(nbLayer/2)
p2 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.4, z) + offset
midpts2.append(p2)
p3 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.8, z) + offset
midpts2.append(p3)

direction = True
z += 0.2print("mid2")
print(len(midpts2))# floating triangle
# Mid Part 3
tpts = []
direction = True
for j in range(int(nbLayer/2)+9, int(nbLayer/2)+12):
for i in range(0,nbCol):
if (direction):
midpts3.append(geom.Point3d(distance*s*i, 0, z)+offset)
midpts3.append(geom.Point3d(distance*s*i+s, 0, z)+offset)
midpts3.append(geom.Point3d(distance*s*i+s/2, math.sqrt(math.pow(s,2)-math.pow(s/2,2)), z)+offset)
else:
midpts3.append(geom.Point3d(distance*s*(i) +s/2, 0-s/10, z)+offset)
midpts3.append(geom.Point3d(distance*s*(i), math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset)
midpts3.append(geom.Point3d(distance*s*(i)+s, math.sqrt(math.pow(s,2)-math.pow(s/2,2))-s/10, z)+offset)
direction = not direction

beta = math.tan((s/3)/(distance*s))

for f in range(0, nbCol):
x = s/2 + s/4 + (s/4)*(j-(nbLayer))/(nbLayer/2)
p2 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.4, z) + offset
midpts3.append(p2)
p3 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.8, z) + offset
midpts3.append(p3)

x = s/4 + s - (s/4)*(j-(nbLayer))/(nbLayer/2)
p2 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.4, z) + offset
midpts3.append(p2)
p3 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.8, z) + offset
midpts3.append(p3)

direction = True
z += 0.2# Top Part for i in range(int(nbLayer/2)+12, int(1.57*nbLayer)):
beta = math.tan((s/3)/(distance*s))
for f in range(0, nbCol):
x = s/2 + s/4 + (s/4)*(i-nbLayer)/(nbLayer/2)
p2 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.4, z) + offset
toppts.append(p2)
p3 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.8, z) + offset
toppts.append(p3)

x = s/4 + s - (s/4)*(i-nbLayer)/(nbLayer/2)
p2 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.4, z) + offset
toppts.append(p2)
p3 = geom.Point3d(x+(f*distance*s), x*math.tan(beta)+0.8, z) + offset
toppts.append(p3)

z += 0.2a = botpts
b = midpts1
c = midpts2
d = midpts3
e = toppts```

And here the code to make the G-code:

```"""Provides a scripting component.    Inputs:
x: The x script variable
y: The y script variable
Output:
a: The a output variable"""__author__ = "sambourgault"import rhinoscriptsyntax as rs
from datetime import datetime
import math as mathdef makeGcodeInfo(sb, p):
sb.append("; " + datetime.now().strftime("%y%m%d_%H%M%S") + "\n")
sb.append("; LayerHeight : " + p + "\n")
sb.append("; Diameter of pillars : " + p + "\n")
sb.append("; Num of Pillar : " + p + "\n")
sb.append("; Pitch of pillars : " + p + "\n")
sb.append("; Height of Pillars : " + p + "\n")
sb.append("; Amount of material per 1mm : " + p + "\n")#
def makeGcodeHeatSettings(sb):
sb.append("M140 S" + str(BedTemp) +" ; Set Bed Temperature\n")
sb.append("M105\n")
sb.append("M190 S" + str(BedTemp) +" ; Wait for Bed Temperature\n")
sb.append("M104 S" + str(NozzleTemp) +" ; Set Nozzle Temperature\n")
sb.append("M105\n")
sb.append("M109 S" + str(NozzleTemp) +" ; Wait for Nozzle Temperature\n")

#
def makeGcodeStartupSettings(sb):
#unit mm
sb.append("G21\n")
#autohome
sb.append("G28\n")
#set E to relative positioning
sb.append("M83\n")
#set interpreter to absolute positioning
sb.append("G90\n")
# liner move: straight line on the planner
# feedrate: 300 to all following moves
# then move in Z: 0.4 mm
sb.append("G1 F300 Z0.4\n")
# move 50mm in x, extrude 8mm at a feedrate of 800mm/min
sb.append("G1 X50 E8 F800\n")
def WriteFile(str, path):
# open file and write to it
f = open(path, "w")
f.write(str)
f.close()

def makeRetraction(amount, speed, sign):
mes = "";
if(sign == -1):
mes = " ; Retraction"
else:
mes = " ; Extraction"
#move with feedrate F, extract E finlament
return "G1 F" + str(speed) + " E" + str(sign * amount) + mes + "\n"def makeGcode(to):
return "G0 F9000 X" + "{:.{}f}".format(to.X,2) + " Y" + "{:.{}f}".format(to.Y,2) + "\n" def makeE(previous, next):
dist = (next - previous).Length
e = (math.pow(_NozzleWidth/2, 2) * dist) / math.pow(1.75/2, 2)
#e = (_NozzleWidth * _LayerHeight * dist) / math.pow(1.75/2, 2)
return e#components
_LayerHeight = LayerHeight;
_NozzleWidth = 0.4;
_RetAmount = 10.0;
_RetSpeed = 1200;
PrintSpeed = 500;
PrintSpeedHigh = 1000; if(Apply == True):
sb = []
#makeGcodeInfo(sb, ParameterInfo)
makeGcodeHeatSettings(sb)
makeGcodeStartupSettings(sb)
sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))

CurrentHeight = 0.0;
i = 0

### instructions
sb.append("G0 Z20\n")
sb.append("G0 X100 Y100\n")
sb.append("G0 Z0.05\n")
sb.append("G1 F800\n")

z = pts.Z
print(z)
indexOfFirst = 0
#go to first point of layer
#base
sb.append("G0 X"+"{:.{}f}".format(pts.X,2)+" Y"+"{:.{}f}".format(pts.Y,2)+" Z"+"{:.{}f}".format(pts.Z,2)+ "\n")

for i in range(0, len(pts)-1):
if (pts[i+1].Z == z):
e = float(makeE(pts[i+1], pts[i]))
sb.append("G1 X"+"{:.{}f}".format(pts[i+1].X,2)+" Y"+"{:.{}f}".format(pts[i+1].Y,2)+" Z"+"{:.{}f}".format(pts[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
else:
e = float(makeE(pts[indexOfFirst], pts[i]))
sb.append("G1 X"+"{:.{}f}".format(pts[indexOfFirst].X,2)+" Y"+"{:.{}f}".format(pts[indexOfFirst].Y,2)+" Z"+"{:.{}f}".format(pts[indexOfFirst].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
z = pts[i+1].Z

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(pts[i+1].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(pts[i+1].X,2)+" Y"+"{:.{}f}".format(pts[i+1].Y,2)+ "\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))

#slow down for the pillars
#go to first point of layer
sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G0 X"+"{:.{}f}".format(botpts.X,2)+" Y"+"{:.{}f}".format(botpts.Y,2)+" Z"+"{:.{}f}".format(botpts.Z,2)+ "\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
sb.append("G1 F300\n")

for i in range(0, len(botpts)-1):
if ((i+1)%6 != 0):
e = float(makeE(botpts[i+1], botpts[i]))
sb.append("G1 X"+"{:.{}f}".format(botpts[i+1].X,2)+" Y"+"{:.{}f}".format(botpts[i+1].Y,2)+" Z"+"{:.{}f}".format(botpts[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
else:
e = float(makeE(botpts[i-5], botpts[i]))
sb.append("G1 X"+"{:.{}f}".format(botpts[i-5].X,2)+" Y"+"{:.{}f}".format(botpts[i-5].Y,2)+" Z"+"{:.{}f}".format(botpts[i-5].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(botpts[i+1].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(botpts[i+1].X,2)+" Y"+"{:.{}f}".format(botpts[i+1].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G0 X"+"{:.{}f}".format(midpts1.X,2)+" Y"+"{:.{}f}".format(midpts1.Y,2)+" Z"+"{:.{}f}".format(midpts1.Z,2)+ "\n")
sb.append("G1 F300\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))

i = 0
# Mid Part 1
print("yoo")
print(len(midpts1)/2)
for k in range(0, 2):
for j in range(0, int(len(midpts1)/2)):
i = j + k*int(len(midpts1)/2)
#pilars
if (j < 6*3*nbCol):
if ((j+1)%6 != 0):
e = float(makeE(midpts1[i+1], midpts1[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts1[i+1].X,2)+" Y"+"{:.{}f}".format(midpts1[i+1].Y,2)+" Z"+"{:.{}f}".format(midpts1[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
else:
e = float(makeE(midpts1[i-5], midpts1[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts1[i-5].X,2)+" Y"+"{:.{}f}".format(midpts1[i-5].Y,2)+" Z"+"{:.{}f}".format(midpts1[i-5].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(midpts1[i+1].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(midpts1[i+1].X,2)+" Y"+"{:.{}f}".format(midpts1[i+1].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
#basis of elevated triangles
elif (j == 6*3*nbCol):
for f in range (0, nbCol):
e = float(makeE(midpts1[i+1], midpts1[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts1[i+1].X,2)+" Y"+"{:.{}f}".format(midpts1[i+1].Y,2)+" Z"+"{:.{}f}".format(midpts1[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
e = float(makeE(midpts1[i+3], midpts1[i+1]))
sb.append("G1 X"+"{:.{}f}".format(midpts1[i+3].X,2)+" Y"+"{:.{}f}".format(midpts1[i+3].Y,2)+" Z"+"{:.{}f}".format(midpts1[i+3].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
e = float(makeE(midpts1[i+2], midpts1[i+3]))
sb.append("G1 X"+"{:.{}f}".format(midpts1[i+2].X,2)+" Y"+"{:.{}f}".format(midpts1[i+2].Y,2)+" Z"+"{:.{}f}".format(midpts1[i+2].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
e = float(makeE(midpts1[i], midpts1[i+2]))
sb.append("G1 X"+"{:.{}f}".format(midpts1[i].X,2)+" Y"+"{:.{}f}".format(midpts1[i].Y,2)+" Z"+"{:.{}f}".format(midpts1[i].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

if(i != len(midpts1)-4):
sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(midpts1[i+4].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(midpts1[i+4].X,2)+" Y"+"{:.{}f}".format(midpts1[i+4].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
i += 4

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G0 X"+"{:.{}f}".format(midpts2.X,2)+" Y"+"{:.{}f}".format(midpts2.Y,2)+" Z"+"{:.{}f}".format(midpts2.Z,2)+ "\n")
sb.append("G1 F300\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
_RetAmount = 6.0;

i = 0
# MID PART 2
nb = (int(nbLayer/2)+9) - (int(nbLayer/2)+2)
for k in range(0, int(nb)):
for j in range(0, int(len(midpts2)/nb)):
i = j + k*int(len(midpts2)/nb)

#pilars
if (j < 6*3*nbCol):
if ((j+1)%6 != 0):
e = float(makeE(midpts2[i+1], midpts2[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts2[i+1].X,2)+" Y"+"{:.{}f}".format(midpts2[i+1].Y,2)+" Z"+"{:.{}f}".format(midpts2[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
else:
e = float(makeE(midpts2[i-5], midpts2[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts2[i-5].X,2)+" Y"+"{:.{}f}".format(midpts2[i-5].Y,2)+" Z"+"{:.{}f}".format(midpts2[i-5].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(midpts2[i+1].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(midpts2[i+1].X,2)+" Y"+"{:.{}f}".format(midpts2[i+1].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
#basis of elevated triangles
elif (j == 6*3*nbCol):
for f in range (0, nbCol):
e = float(makeE(midpts2[i+1], midpts2[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts2[i+1].X,2)+" Y"+"{:.{}f}".format(midpts2[i+1].Y,2)+" Z"+"{:.{}f}".format(midpts2[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 X"+"{:.{}f}".format(midpts2[i+2].X,2)+" Y"+"{:.{}f}".format(midpts2[i+2].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))                        e = float(makeE(midpts2[i+2], midpts2[i+3]))
sb.append("G1 X"+"{:.{}f}".format(midpts2[i+3].X,2)+" Y"+"{:.{}f}".format(midpts2[i+3].Y,2)+" Z"+"{:.{}f}".format(midpts2[i+3].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

if(i != len(midpts2)-4):
sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(midpts2[i+4].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(midpts2[i+4].X,2)+" Y"+"{:.{}f}".format(midpts2[i+4].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
i += 4

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G0 X"+"{:.{}f}".format(midpts3.X,2)+" Y"+"{:.{}f}".format(midpts3.Y,2)+" Z"+"{:.{}f}".format(midpts3.Z,2)+ "\n")
sb.append("G1 F300\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))

# MID PART 3
print("mid3")
print(len(midpts3))
for k in range(0, 3):
for j in range(0, int(len(midpts3)/3)):
i = j + k*int(len(midpts3)/3)
if (j < 3*nbCol):
if ((j+1)%3 != 0):
e = float(makeE(midpts3[i+1], midpts3[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts3[i+1].X,2)+" Y"+"{:.{}f}".format(midpts3[i+1].Y,2)+" Z"+"{:.{}f}".format(midpts3[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")
else:
e = float(makeE(midpts3[i-2], midpts3[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts3[i-2].X,2)+" Y"+"{:.{}f}".format(midpts3[i-2].Y,2)+" Z"+"{:.{}f}".format(midpts3[i-2].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

#move to the next triangle or the next layer
sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 X"+"{:.{}f}".format(midpts3[i+1].X,2)+" Y"+"{:.{}f}".format(midpts3[i+1].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))

elif(j == 3*nbCol):

for f in range (0, nbCol):
e = float(makeE(midpts3[i+1], midpts3[i]))
sb.append("G1 X"+"{:.{}f}".format(midpts3[i+1].X,2)+" Y"+"{:.{}f}".format(midpts3[i+1].Y,2)+" Z"+"{:.{}f}".format(midpts3[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 X"+"{:.{}f}".format(midpts3[i+2].X,2)+" Y"+"{:.{}f}".format(midpts3[i+2].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))                        e = float(makeE(midpts3[i+2], midpts3[i+3]))
sb.append("G1 X"+"{:.{}f}".format(midpts3[i+3].X,2)+" Y"+"{:.{}f}".format(midpts3[i+3].Y,2)+" Z"+"{:.{}f}".format(midpts3[i+3].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

if(i != len(midpts3)-4):
sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(midpts3[i+4].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(midpts3[i+4].X,2)+" Y"+"{:.{}f}".format(midpts3[i+4].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
i += 4

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G0 X"+"{:.{}f}".format(toppts.X,2)+" Y"+"{:.{}f}".format(toppts.Y,2)+" Z"+"{:.{}f}".format(toppts.Z,2)+ "\n")
sb.append("G1 F300\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))

nb = int(1.57*nbLayer) - int((nbLayer/2)+12)
for k in range(0, int(nb)):
for j in range(0, int(len(toppts)/nb)):
i = j + k*int(len(toppts)/nb)
for f in range (0, nbCol):
if (j == 0):

e = float(makeE(toppts[i+1], toppts[i]))
sb.append("G1 X"+"{:.{}f}".format(toppts[i+1].X,2)+" Y"+"{:.{}f}".format(toppts[i+1].Y,2)+" Z"+"{:.{}f}".format(toppts[i+1].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 X"+"{:.{}f}".format(toppts[i+2].X,2)+" Y"+"{:.{}f}".format(toppts[i+2].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))                        e = float(makeE(toppts[i+2], toppts[i+3]))
sb.append("G1 X"+"{:.{}f}".format(toppts[i+3].X,2)+" Y"+"{:.{}f}".format(toppts[i+3].Y,2)+" Z"+"{:.{}f}".format(toppts[i+3].Z,2)+" E"+"{:.{}f}".format(e, 2) + "\n")

if(i != len(toppts)-4):
sb.append(makeRetraction(_RetAmount, _RetSpeed, -1))
sb.append("G1 F9000")
sb.append("G1 Z"+"{:.{}f}".format(toppts[i+4].Z,2)+"\n")
sb.append("G1 X"+"{:.{}f}".format(toppts[i+4].X,2)+" Y"+"{:.{}f}".format(toppts[i+4].Y,2)+"\n")
sb.append(makeRetraction(_RetAmount, _RetSpeed, 1))
i+=4

sb.append(makeRetraction(_RetAmount, _RetSpeed, -1));
CurrentHeight += 10.0;
sb.append("G1 Z70 \n");    # disable steppers
sb.append("M84");
strsb = ''.join(sb)
WriteFile(strsb, FilePath);

a = strsb```

## Step 5: Chain of Triangle Links

I increased the number of links to 24. And I had an adhesion problem of the base.

The first print was not great, there was a lot of oozing, the base didn't stick to the bed properly. So I modified a few things to make it sharper:

• I increase the retraction amount from 6 mm to 10 mm and it reduced the oozing dramatically.
• I noticed that I have a better adhesion in the middle of the bed, where it's hotter.
• I re-leveled the bed so it's a bit closer to the nozzle.

I still have some spider-weby filament but globally it's a LOT better.

## Step 6: Discovering Patterns in Mistakes

Embracing the "mistake" made in step 2, I realized that modifying the retraction/extraction amount could generate interesting pattern. I therefore redid my pillars from step 2 so they go higher and could observe the emergence of a pattern, which cast a beautiful organic shadow in the sunlight.

I would like to investigate this emergence further, there might be ways to understand/control the pattern it better..

## Step 7: Wearable

All that for this!