Introduction: Project4_writing_gcode

About: PhD student at University of California, Santa Barbara, in the Expressive Computation Lab (https://ecl.mat.ucsb.edu/).

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/2

a = 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 layers

lastIndexP = 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.2

print("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.2

a = 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 math

def makeGcodeInfo(sb, p): sb.append("; " + datetime.now().strftime("%y%m%d_%H%M%S") + "\n") sb.append("; LayerHeight : " + p[0] + "\n") sb.append("; Diameter of pillars : " + p[1] + "\n") sb.append("; Num of Pillar : " + p[2] + "\n") sb.append("; Pitch of pillars : " + p[3] + "\n") sb.append("; Height of Pillars : " + p[4] + "\n") sb.append("; Amount of material per 1mm : " + p[5] + "\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[0].Z print(z) indexOfFirst = 0 #go to first point of layer #base sb.append("G0 X"+"{:.{}f}".format(pts[0].X,2)+" Y"+"{:.{}f}".format(pts[0].Y,2)+" Z"+"{:.{}f}".format(pts[0].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[0].X,2)+" Y"+"{:.{}f}".format(botpts[0].Y,2)+" Z"+"{:.{}f}".format(botpts[0].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[0].X,2)+" Y"+"{:.{}f}".format(midpts1[0].Y,2)+" Z"+"{:.{}f}".format(midpts1[0].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[0].X,2)+" Y"+"{:.{}f}".format(midpts2[0].Y,2)+" Z"+"{:.{}f}".format(midpts2[0].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[0].X,2)+" Y"+"{:.{}f}".format(midpts3[0].Y,2)+" Z"+"{:.{}f}".format(midpts3[0].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[0].X,2)+" Y"+"{:.{}f}".format(toppts[0].Y,2)+" Z"+"{:.{}f}".format(toppts[0].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!

A quarantine wearable chain/garland.