I couldn't get more interesting fields without the help of the LayZ
layout program, and I could get any data out of that without a way to save and load figures. Time for a save format.
Since a figure is hierarchical data (figure contains items contain points, with attribute values stored in each) I decided to use an HTML-like format, with nested tags.
I wanted to make it a good format, too. I might want to publish the computation tools someday, but probably not LayZ
. (I still thought of the GUI as being "the hard part", and I wanted to keep hold of it, so that nobody else would have too much advantage over me with my own tools... So I'm selfish sometimes. Although I also was getting the idea that LayZ
might never be finished enough to publish. Either way, it was important to have a good clean storage format, so that other people could integrate the computation tools into existing drawing programs.)
"HTML-like" implies XML[8] these days. I don't actually know much about XML, and I was too lazy to look it up and write a proper DTD. But I was sure I could write files that would be well-formed XML, and let other people worry about the formalities.
That was an evening of work; tedious, but not difficult. I was using only a small subset of the XML syntax, after all. A bit of code to parse the angle-brackets, tags, and associated attributes; a bunch of special-case (but not too ugly) code to read the structure of a figure object. Writing was even easier; that's just a bunch of print statements.
Since LayZ
was already capable of making line segments, I could immediately make figure files with line segments in them. (In fact, line segments joined end-to-end, if I so desired.) Time for makefield
, the tool to read a figure and generate vector fields.
My calculus had boiled down to a straightforward formula for the influence (weight) of a segment at a given point. I actually wanted makefield
to generate several fields: the total influence of every part of the figure, and then the vector, color, and density fields (which are just the influence of each item multiplied by its direction, color, and stroke-density. Color and density are independent parameters; I can set them to any value on any item).
I implemented it and then bated my breath.
Fwooosh. It worked. The influence of a single segment was a fuzzy bar. Just what I wanted. Better yet, the influence of two parallel segments joined end-to-end was exactly the influence of the segment formed by their union. So my calculus trick of breaking every curve down into a sum of "small enough" segments was likely to work.
The vector field of a single segment is a constant field, of course -- the whole universe lines up parallel with the segment -- and (similarly) the color field is a constant color. But two nonparallel segments made a smoothly varying vector field, and two items of different color made a smoothly varying color field.
These false-color images were pretty, but not what I wanted as art. I started upgrading makeps
to show strokes as strokes, not circles. For a first cut, I just made them all straight dashes of constant length. And lo:
This involved writing some linear interpolation code. The fields makefield
kicked out were of course fixed-resolution grids; but the strokes could be at any real position, not just on grid points. Linear interpolation is a very simple way to estimate the values between points. A dull detail, but I'll get back to it later --
Somewhere down the track I had added to LayZ
the ability to drag control points around. (I don't remember where, I'm afraid. I only promised asymptotic completeness....) That gave me full splines. I already had the code to break a spline down into small segments -- that was needed to draw them, since X windows has no spline primitive -- so I transplanted that to makefield
.
I'm abbreviating the story again, by the way. I didn't really transplant that breaking-down code. The underlying idea is so simple that the code itself is optimized for other concerns. Do I have to generate a list of segments, or a list of points (each assumed to connect to the last)? X windows wanted one, my makefield
code wanted the other. I wound up rewriting this trivial bit of code about eight times. Just a matter of shifting constraints and a desire to keep the source from getting too clunky. No big deal, and I won't mention it again, but it was part of the process.