In the vector reconstruction example below the flames generation was semi-automated using PostScript and the flame shape refined in Illustrator. In the original design the flames don't have all the same shape, as expected in a handmade artwork like this. The first thought was to "easily" generate the path for all the flames in PostScript, just picking one flame from one position and trying to redraw and close the circle by rotating it by 40°÷3 at each step.
Obviously, there is a particular translation to be done after rotating each flame to make the next flame fit in the predetermined circular trajectory. Instead of calculating this exact translation one can use a near binary search trial and error approach to find the x and y components of this translation. In addition, the fact that the interior part of the pattern must fit in a predetermined circle with a certain radius serves as a feedback loop refinement to quickly find the answers.
Once the endpoint and the starting point are real close to each other this iteration can stop, but the initial point don't actually coincide with the endpoint. The black point and its coordinates (213.686, 37.8411) correspond to the path endpoint, and those in red color with coordinates (206.373, 35.5591) correspond to the initial flame point coordinates:
Observing that from the second flame forward each flame's first point actually coincides with the endpoint of the previous one by design (because we chain the rest of the path in a loop - see the PostScript program below), we conclude that we have to do the same with the initial flame's first point, that is, we simply make it equal to the endpoint. In other words, we modify its initial coordinates in the program to (213.686, 37.8411). The result is shown in the figure below:
Finally, this is the PostScript code that produces this closed path:
This a very simple way to obtain shapes that are composed by a template shape rotated until the endpoints meet. What's interesting is that we can use templates with their original absolute coordinates. These coordinates will only affect the values of the x and y components of the translation vector. The incremental angle for each rotation only affects the number of template copies, which in this case is known in advance to be 27 thanks to the original artwork.
Most of the PostScript code is just for the template (the flame). The first point set before the for loop is the same as the last point (206.373, 35.5591). The initial translation and scale is just to adjust to the page. The pop deletes the for counter. Finally:
13.333333° ≅ 360° ÷ 27
The template shape was first obtained by using one of the flames of the original artwork that was automatically vectorized. Once this shape was extracted it was tried by pasting it within the program above. It was actually interesting to see that this shape didn't look correct when it was used for all the flames. That's the reason why it was refined by hand using Illustrator.
The problem of using Illustrator in this process was that even though only few outer points were modified the resulting EPS file generated by Illustrator revealed significant differences in every point coordinate. This is because Illustrator surprisingly applies a different translation to the shape at each time it was modified. This means that all the points in the curves needed to be translated back in such a way that the first and final point would be the same as in the initial shape.
In order to avoid rounding error fixed point integer arithmetic was used instead of floating point arithmetic. This was done manually by placing the numbers in an array in the C program below:
The first line in the array is just the initial point of the template shape. The other four coordinates are just zeros, since they only make sense to represent the Bézier curves of the shape in the following lines uniformly.
The program first determines the translation vector by subtracting the original coordinates of the first point of the template shape from the coordinates of the obtained points in the EPS file generated by illustrator after modifying the shape. These modifications never alter the initial point. Therefore, since no other transformations such as scaling or translations are ever applied, both points must be always identical. That's why it's used to determine the automatic translation done by Illustrator.
Then, the program applies the translation to all the other points as shown in the code above, and prints the result, which are the following, after manually aligning them:
The initial point and the first Bézier curve are eliminated from the template since it actually corresponds to a part of the previous flame, resulting in only six following Bézier curves and the new initial point, which is the last point of the eliminated Bézier curve: (206.373, 35.5591). Even this initial point is eliminated and substituted by the endpoint of the path (213.68, 37.8411) after the entire pattern is generated with the PostScript program above as explained previously.
This example of vector reproduction starts by simply drawing an equilateral triangle. The PostScript code below shows this part defined as an independent program. It draws a triangle, although polygon function actually draws any polygon based on the array of points passed to it. Function trig pushes the triangle points on the stack based on the size passed as a parameter. '[' and ']' define an array with the points generated by trig:
Functions like polygon are of the kind "write once, use everywhere." This means that the function is not supposed to have its internal content understood or to be modified, it simply works as a function in a library that's not meant to be modified. Function trig is an intermediate type of function. This function helps thinking about polygons in a quite simple way. Here it defines the three points of a triangle starting at the origin. That's why we need the translate to be able to place it elsewhere. But we could also think in terms of an pentagon or an hexagon in the same way. In the case of a pentagon, it could be defined in this way:
With this heuristic any regular polygon with the same side size defined like this would have the same first two points, have many symmetrical points, and polygons with odd number of sides will always have the abscissa of its highest point with half of the side size.
The large gray lines inside the triangle are generated in the following part below, together with the texts. The array num contains the strings (its definition is shown at the end of this section) and indx is used to index it. inc is the increment for the x coordinates of the lines. This code is for the bottom part of the triangle. The other sides use a similar logic.
The main idea from this point on is to use PostScript as a script language. The repeat commands for example, greatly simplify loops. There was not too much concern with structure here. Simple and often repeated statements are actually what makes this and the following parts easy to write. Opportunism is clearly intentional. The addition of the text in this and the other pieces of code is proof of this concept. The use of gsave and grestore allows taking advantage of the current point set by the moveto (or lineto in other parts) commands in the loop and to use rotations without the concern of affecting what comes after the grestore. The use of a special index (indx) for the text makes it easy to use the correct string for each iteration without interfering with the repeat loop. Code is highly informal, quite improvised, you write as you go. It not only works, but one can also copy, paste and modify the code according to the needs of the other parts that follow.
The small lines making the little measures around the triangle is generated in this part below. Again, this code is for the bottom part of the triangle. The other sides use a similar logic.
We can see here an extremely straightforward code, highly inspired in the style of the previous part but with even less concern in repeating code. Actually the repetition here is even intentional to give more clarity to the code. Notice that all the code so far is heavily reliant on the triangle definition as seen in trig function. We see the same values reappearing quite often. That's the reason why some values are pre-calculated. The structure here, though, is more obvious, thanks to the repetition. The exterior repeat loop is responsible for making the measure marks at each one of the ten subdivisions. The two interior loops deal with the four lines before and after the middles lines, which are themselves drawn between these both loops.
The size of the lines are parameterized by the variable subl which is set here with a 5 pt size. The middle lines are twice as long (msub). The sub increments (iinc) are a tenth of the size of the large lines original increment, which is quite expected. The x and y coordinates of the lines starting points are stored in the stack (starting with "0 0", the origin) and at each new line the x coordinate is incremented (exch iinc add exch) and both coordinates are copied for each moveto. The lines are drawn using a relative lineto to simplify the logic, by being independent of the absolute coordinates stored in the stack.
The final part of the code is for drawing the arrows. The arr procedure was reused from
previous projects and the values are directly written in it except for the dimensions of the arrow head that are twice the original
size. Notice the rotations and translations to correctly place each arrow.
The complete code can be found here.