https://www.youtube.com/watch?v=NpCqjrXWzuQ
The video is also available on Vimeo.
This animation is strongly connected to LaTeX. How come? – you could ask. I’ll come to the point but first I have to confess that this nice piece of video/animation/whatever is “only” a byproduct of a search for a LaTeX related answer.
I’m a LaTeX enthusiast, and I’m trying to be active on TeX.SX, and found a question about how to draw individual glyphs with randomized paths which I could not stop thinking about. I even wanted to learn Metapost, but when I saw what was laying before me I was intimidated. So I thought I should try something easier first, namely Processing, which was very useful for some small projects in the past. I found the Geomerative library written by Ricard Marxer, and when I was looking into the examples which came with the library I found an interesting one, which used the vertices of font outlines. Then came the idea that it would be nice to try to polygonize individual glyphs with different accuracy, draw Bézier curves along/through the vertices, and stack these images on top each other. Just to see how it looks. In the end it turned out to be pretty awesome, I think.
I’m sharing the source with some notes what and how it does what it does, so others may come up with nice things too. But if you don’t want the “magic” to be debunked, this is where you should stop reading this post.
Note that the following source code comes with no warranty at all, and is under the CC BY-NC-SA 2.5 license.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
/*
Font outline animation Author: István Szántai (szantaii) License: CC BY-NC-SA 2.5 */ import geomerative.*; import processing.pdf.*; int frameCount; float backgroundColor; float strokeColor; float strokeOpacity; float fadeOutOpacity; float fadeOutOpacityStep; RShape shape; RPoint[] points; int initialPolygonizerLength; int currentPolygonizerLength; void setup() { frameCount = 0; backgroundColor = 38.25; strokeColor = 229.5; strokeOpacity = 25.5; fadeOutOpacityStep = 10.625; fadeOutOpacity = fadeOutOpacityStep; size(1280, 720, PDF, "frames.pdf"); RG.init(this); initialPolygonizerLength = 179; currentPolygonizerLength = initialPolygonizerLength; print("Drawing..."); smooth(); } void draw() { PGraphicsPDF pdf = (PGraphicsPDF) g; if (frameCount < 45) { background(backgroundColor); pdf.nextPage(); } else if (frameCount < 399) { background(backgroundColor); noFill(); strokeCap(ROUND); strokeJoin(ROUND); stroke(strokeColor, strokeOpacity); drawBezierVertices("A", width / 2 - 180, 4.95 * height / 6); drawBezierVertices("a", width / 2 + 230, 4.95 * height / 6); if (currentPolygonizerLength > 0) { --currentPolygonizerLength; } if (frameCount >= 375) { noStroke(); fill(backgroundColor, fadeOutOpacity); rect(0, 0, 1280, 720); fadeOutOpacity += fadeOutOpacityStep; } pdf.nextPage(); } else if (frameCount < 429) { background(backgroundColor); if (frameCount != 428) { pdf.nextPage(); } } else { println(" done."); exit(); } frameCount++; } void drawBezierVertices(String text, float horizontalPos, float verticalPos) { shape = RG.getText(text, "Palatino-Roman.ttf", 650, CENTER); pushMatrix(); translate(horizontalPos, verticalPos); for (int i = initialPolygonizerLength; i >= currentPolygonizerLength; i--) { RG.setPolygonizer(RG.UNIFORMLENGTH); RG.setPolygonizerLength(i); points = shape.getPoints(); if(points != null && points.length > 3) { beginShape(); for(int j = 0; j < points.length - 3; j++) { if (j == 0) { vertex(points[j].x, points[j].y); } else { bezierVertex(points[j].x, points[j].y, points[j + 1].x, points[j + 1].y, points[j + 2].x, points[j + 2].y); } } endShape(); } } popMatrix(); } |
The most important stuff is in the definition of the drawBezierVertices
function. This is how it works. It gets a piece of text, which will be “written” with a defined font (note that the chosen font should be in the project’s data folder) making a shape, but it won’t be drawn to the screen. Instead the shape will be polygonized and through the calculated vertices a bezier curve will be drawn. This is iterated a couple times (180 times in this specific example) while vertices are getting closer to each other, so at the end a the true outline of the font would be drawn (more or less).
By reading the source you can also see that every frame of the animation is rendered to pages of a PDF document. Here is why. When rendering to PDF Processing will create a vectorized output. So in the end you can get the frames from the PDF in any resolution you want without losing quality. All is left to rasterize every page of the rendered PDF with the desired resolution into individual raster images (PNG files if you ask me), and make a video from the created image sequence.
Some practical advice. I’ve used Gimp to rasterize the pages of the PDF output (with FullHD, 1920×1080 resolution), and saved them as separate files using the Export Layers plugin. After that I made the image sequence into a video from the command-line using FFmpeg.