Created
January 9, 2026 21:17
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! 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