Simulating freehand drawing with Cairo
Have a look at this image. You might think I scrawled it on a napkin and scanned it in. Wrong! It was completely automatically generated by an upcoming release of www.websequencediagrams.com, with the new "napkin" style. Getting it to render this way was easy, simply with a tiny bit of math and a change to my line drawing function. The handwriting font FG Virgil.
Here's the same diagram in a different style:
Here's the C code that I use for my line drawing function, using the cairo API.
void crazyLine( cairo_t* ctx, double fromX, double fromY, double toX, double toY) { // Crazyline. By Steve Hanov, 2008 // Released to the public domain. // The idea is to draw a curve, setting two control points at random // close to each side of the line. The longer the line, the sloppier it's drawn. double control1x, control1y; double control2x, control2y; // calculate the length of the line. double length = sqrt( (toX-fromX)*(toX-fromX) + (toY-fromY)*(toY-fromY)); // This offset determines how sloppy the line is drawn. It depends on the // length, but maxes out at 20. double offset = length/20; if ( offset > 20 ) offset = 20; // Overshoot the destination a little, as one might if drawing with a pen. toX += ((double)rand()/RAND_MAX)*offset/4; toY += ((double)rand()/RAND_MAX)*offset/4; double t1X = fromX, t1Y = fromY; double t2X = toX, t2Y = toY; // t1 and t2 are coordinates of a line shifted under or to the right of // our original. t1X += offset; t2X += offset; t1Y += offset; t2Y += offset; // create a control point at random along our shifted line. double r = (double)rand()/RAND_MAX; control1X = t1Y + r * (t2X-t1X); control1Y = t1Y + r * (t2Y-t1Y); // now make t1 and t2 the coordinates of our line shifted above // and to the left of the original. t1X = fromX - offset; t2X = toX - offset; t1Y = fromY - offset; t2Y = toY - offset; // create a second control point at random along the shifted line. r = (double)rand()/RAND_MAX; control2X = t1X + r * (t2X-t1X); control2Y = t1Y + r * (t2Y-t1Y); // draw the line! cairo_move_to( _ctx, fromX, fromY ); cairo_curve_to( _ctx, control1X, control1Y, control2X, control2Y, toX, toY ); }
Nice work! I used your code to create a CairoContext in Python. It makes for really cool diagrams.
You can find it on GitHub: /amolenaar/gaphas/blob/freehand/gaphas/freehand.py
control1X = t1Y + r * (t2X-t1X);
should be:
control1X = t1X + r * (t2X-t1X);