Modeling Crochet as a Programmer

During my batch at the Recurse Center, I’ve worked on both larger projects (like my BitTorrent client) and smaller projects (like my Moniac JS simulation and twitterbot). One source of small projects I’ve gone back to over and over is crochet. Lately, I’m working on a tool that attempts to make lace patterns from images, with scaling and dithering. It’s nowhere near finished. This is essentially my notes on the process of representing crochet with a program in general.

source

Crochet is an interesting craft to me, as it is relatively easy and forgiving. You can drop a crochet project and probably not lose any stitches, unlike with a knitting project. And you can make a mistake and often improvise around it later. I also like the aesthetic of crocheted objects, and like how it works well with cheap cotton yarn.

A crochet pattern describes a series of steps to create an object made of stitches. Patterns sometimes use repetition and custom stitch notations for more complicated patterns. In that way, they remind me of loops and macros in programs.

From Wikipedia

A crocheted object gets its shape from the connection between stitches. To model it mathematically, I’ve found a spring-mass network or force-directed graph (itself a kind of spring network) works well for objects that are either flat or three dimensional, but a simple mesh is good enough for flat crochet objects.

Further, crochet can be worked either flat or in the round. Flat crochet is easy to describe in rectangular coordinates. Polar coordinates come in handy for crochet done in the round, as you’re describing either a series of circular rows or one spiral. If it’s not done with a constant amount of increases (which creates a flat circle like a doily), it can create a cylinder, or a tube, or all kinds of variations on 3d forms.

If you’re unfamiliar with polar coordinates, it’s an alternative to rectangular coordinates where you have r, a unit describing the distance from the origin (radius), and theta, a unit describing the rotation from the origin. It’s very easy to convert points to rectangular coordinates (x = r cos(theta), y = r sin(theta)), and back. (r = sqrt(x*x + y*y), theta = atan2(y, x))

With either a static mesh or spring network, you model each stitch as a vertex, and add edges to connect those vertices. Edges can be stored in an “adjacency list,” a list of pairs of vertices for each edge.

Fabrics in general model well with a spring-mass network, where each stitch is a mass, and each thread between those stitches is a spring. After all, most cloth can stretch a little, or be crumpled up, and then return back to its “normal” shape. According to Hooke’s law, a spring has a relaxed length where its displacement, X, is zero, and a “spring constant,” k, which models how much force a spring will apply if deformed from its relaxed length. The equation is F = kX. It makes sense to apply damping or friction to this force, as otherwise the system is prone to oscillation.

In the spring-mass network model, each point (or stitch) is a movable object with mass, and three vectors (position, velocity, and acceleration). Each iteration of the game loop, any forces are added to acceleration, acceleration is added to velocity, velocity is added to position, and acceleration is set to 0. Or a different integration is used to transfer force to acceleration to velocity to position, but that’s outside the scope here. Here’s the pseudocode of a phsyics object in 2d.

Class Mover {
Mover(x,y,m){
float this.mass = m;
Vector this.position = Vector(x,y);
Vector this.velocity = Vector(0,0);
Vector this.acceleration = Vector(0,0);
}
//add force vector from the game world
void addForce(f){
this.acceleration.add(f.div(m));
}
void update(){
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
}
}

So, write a function that determines how far apart two points are, and subtract it from the “correct” distance, and multiply that difference by K. Then apply that force to each object, rotated at the other object.

const DAMPING = 0.9;
Vector spring(pos1, pos2, rel_len, K){
float x= rel_len - pos1.dist(pos2);
float mag = K*x;
//get unit vector towards position 2, and scale it to magnitude
return pos1.dir(pos2).mult(mag).mult(damping);
}

Of course, not every edge will find its relaxed length, but with a good K value and a little damping, the network will find a relatively relaxed position.

All of this assumes the network will be made of one type of stitch. I tend to model single crochet as it’s easy, but other stitches could probably be modeled with some variant of chains between the top and bottom of the stitch.

I’m also curious how crochet could be modeled in Box2d or another “serious” physics library, but I have yet to try that. I imagine it could cut back on some of my repeated code and less-than-ideal integration.