Skip to content

Instantly share code, notes, and snippets.

@Neon22
Created January 9, 2026 21:17
Show Gist options
  • Select an option

  • Save Neon22/d2b1228b178655e806a3bb7728177ea1 to your computer and use it in GitHub Desktop.

Select an option

Save Neon22/d2b1228b178655e806a3bb7728177ea1 to your computer and use it in GitHub Desktop.
Generates a continuous yarn of interlocked knit stitches. Needs a bit more work. For pyelastica would require a series of rows with fixed ends.
#! python
# From Keenan
# - https://github.com/keenancrane/plain-knit-yarn
# modified from webGL version:
# - https://github.com/UH-AIM/yarn-fiber-generation/blob/main/yarn_fiber.py
# Has continuous rows
from math import sin, cos, sqrt, pow, pi
# Simple 3vector class
class Vector3:
def __init__(self, x=0.0, y=0.0, z=0.0):
self.x = x
self.y = y
self.z = z
def norm2(v):
return pow(v.x, 2) + pow(v.y, 2) + pow(v.z, 2)
def norm(v):
return sqrt(norm2(v))
def scale(c, v):
cv = Vector3(c * v.x, c * v.y, c * v.z)
return cv
def cross(u, v):
w = Vector3()
w.x = u.y * v.z - u.z * v.y
w.y = u.z * v.x - u.x * v.z
w.z = u.x * v.y - u.y * v.x
return w
def yarnCurve(t, a, h, d):
gamma_t = Vector3()
gamma_t.x = t + a * sin(2.0 * t)
gamma_t.y = h * cos(t)
gamma_t.z = d * cos(2.0 * t)
return gamma_t
def frenetFrame(t, a, h, d):
e1 = Vector3()
e2 = Vector3()
u_t, v_t, x_t, y_t = 0.0, 0.0, 0.0, 0.0
e1.x = 1.0 + 2.0 * a * cos(2.0 * t)
e1.y = -h * sin(t)
e1.z = -2.0 * d * sin(2.0 * t)
u_t = norm2(e1)
v_t = 2.0 * h * h * cos(t) * sin(t) + 16.0 * d * d * cos(2.0 * t) * sin(2.0 * t) - 8.0 * a * (1.0 + 2.0 * a * cos(2.0 * t)) * sin(2.0 * t)
x_t = 1.0 / sqrt(u_t)
y_t = v_t / (2.0 * pow(u_t, 3.0 / 2.0))
e2.x = y_t * (-1.0 - 2.0 * a * cos(2.0 * t)) - x_t * 4.0 * a * sin(2.0 * t)
e2.y = y_t * h * sin(t) - x_t * h * cos(t)
e2.z = y_t * 2.0 * d * sin(2.0 * t) - x_t * 4.0 * d * cos(2.0 * t)
e1 = scale(x_t, e1)
e2 = scale(1.0 / norm(e2), e2)
e3 = cross(e1, e2)
return [e1, e2, e3]
def fiberCurve(t, a, h, d, r, omega, phi):
gamma_t = yarnCurve(t, a, h, d)
[e1, e2, e3] = frenetFrame(t, a, h, d)
theta_t = t * omega - 2. * cos(t) + phi
eta_t = Vector3(0, 0, 0)
eta_t.x = gamma_t.x + r * (cos(theta_t) * e2.x + sin(theta_t) * e3.x)
eta_t.y = gamma_t.y + r * (cos(theta_t) * e2.y + sin(theta_t) * e3.y)
eta_t.z = gamma_t.z + r * (cos(theta_t) * e2.z + sin(theta_t) * e3.z)
return eta_t
def get_yarn_curves(nRows, rowOffset, nLoops, samplesPerLoop, a, h, d):
'''
Input
int nRows number of rows in wale direction
dbl rowOffset row spacing in wale direction
int nLoops number of loops in course direction
int samplesPerLoop sample points for each loop
dbl a loop roundness
dbl h loop height
dbl d loop depth
'''
verts = []
nPoints = 0
dt = 2*pi / samplesPerLoop
samples = list(range(samplesPerLoop))
samples_rev = samples[::-1]
dirn = -1
# Write vertices
for row in range(nRows):
y0 = rowOffset * row
dirn *= -1 # change dir of stitches on each row
counts = samples if dirn > 0 else samples_rev
tstart = 0 if dirn > 0 else 2*pi*(nLoops-1)
for loop in range(nLoops):
t0 = 2*pi * loop
#for sample in range(samplesPerLoop):
for sample in counts:
t = tstart + (t0 * dirn) + (dt * sample)
gamma_t = yarnCurve(t, a, h, d)
verts.append([gamma_t.x, gamma_t.y + y0, gamma_t.z])
return verts
def getFiberCurves(nRows, rowOffset, nLoops, samplesPerLoop, a, h, d, r, omega, nFibers):
'''
Input
int nRows number of rows in wale direction
dbl rowOffset row spacing in wale direction
int nLoops number of loops in course direction
int samplesPerLoop sample points for each loop
dbl a loop roundness
dbl h loop height
dbl d loop depth
dbl r yarn radius
dbl omega amount of fiber twist
int nFibers number of fibers around yarn
'''
verts = []
nPoints = 0
dt = (2.0 * pi) / samplesPerLoop
dphi = (2.0 * pi) / nFibers
# write vertices
for row in range(nRows):
y0 = rowOffset * row
for fiber in range(nFibers):
phi = dphi * fiber
rowverts = []
for loop in range(nLoops):
t0 = 2.0 * pi * loop
for sample in range(samplesPerLoop):
t = t0 + dt * sample
eta_t = fiberCurve(t, a, h, d, r, omega, phi)
rowverts.append([eta_t.x, y0 + eta_t.y, eta_t.z])
verts.append(rowverts)
return verts
def writeYarnCurves(filename, nRows, rowOffset, nLoops, samplesPerLoop, a, h, d):
'''
Input
str filename output filename
int nRows number of rows in wale direction
dbl rowOffset row spacing in wale direction
int nLoops number of loops in course direction
int samplesPerLoop sample points for each loop
dbl a loop roundness
dbl h loop height
dbl d loop depth
'''
def format_float(f):
# Format the given float to scientific notation
return "{:.8e}".format(f)
# Open the output file for writing
out = open(filename, "w")
nPoints = 0
dt = 2*pi / samplesPerLoop
# Write vertices
for row in range(nRows):
y0 = rowOffset * row
for loop in range(nLoops):
t0 = 2*pi * loop
for sample in range(samplesPerLoop):
t = t0 + dt * sample
gamma_t = yarnCurve(t, a, h, d)
out.write("v {} {} {}\n".format(
format_float(gamma_t.x),
format_float(gamma_t.y + y0),
format_float(gamma_t.z)
))
# Write polylines
for row in range(nRows):
out.write("l")
for loop in range(nLoops):
for sample in range(samplesPerLoop):
out.write(" {}".format(nPoints + 1))
nPoints += 1
out.write("\n")
# Close the output file
out.close()
def writeFiberCurves(filename, nRows, rowOffset, nLoops, samplesPerLoop, a, h, d, r, omega, nFibers):
'''
Input
str filename output filename
int nRows number of rows in wale direction
dbl rowOffset row spacing in wale direction
int nLoops number of loops in course direction
int samplesPerLoop sample points for each loop
dbl a loop roundness
dbl h loop height
dbl d loop depth
dbl r yarn radius
dbl omega amount of fiber twist
int nFibers number of fibers around yarn
'''
out = open(filename, "w")
nPoints = 0
dt = (2.0 * pi) / samplesPerLoop
dphi = (2.0 * pi) / nFibers
# write vertices
for row in range(nRows):
y0 = rowOffset * row
for fiber in range(nFibers):
phi = dphi * fiber
for loop in range(nLoops):
t0 = 2.0 * pi * loop
for sample in range(samplesPerLoop):
t = t0 + dt * sample
eta_t = fiberCurve(t, a, h, d, r, omega, phi)
out.write(f"v {eta_t.x} {y0 + eta_t.y} {eta_t.z}\n")
# write polylines
for row in range(nRows):
y0 = rowOffset * row
for fiber in range(nFibers):
out.write("l")
for loop in range(nLoops):
for sample in range(samplesPerLoop):
out.write(" {}".format(nPoints + 1))
nPoints += 1
out.write("\n")
out.close()
###
if __name__ == "__main__":
nRows = 5 # number of rows generated
nLoops = 5 # number of loops in each row
samplesPerLoop = 64 # points sampled per period
nFibers = 4 # number of twisted fibers
a = 3/2. # loop roundness
d = 1. # loop depth
h = 4. # loop height
w = h + 1/2. # row spacing
r = 1/2. # yarn radius
omega = 5. # fiber twist
phi = pi/2 # fiber offset
#writeYarnCurves("yarn.obj", nRows, w, nLoops, samplesPerLoop, a, h, d)
#writeFiberCurves("fibers.obj", nRows, w, nLoops, samplesPerLoop, a, h, d, r, omega, nFibers)
#writeYarnCurves("yarn.obj", 1, w, 1, samplesPerLoop, a, h, d)
# Just the vertices for three.js TubeGeometry
print(get_yarn_curves(1, w, 1, samplesPerLoop, a, h, d))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment