| CARVIEW |
Select Language
HTTP/1.1 200 OK
Date: Fri, 16 Jan 2026 09:18:18 GMT
Server: Apache/2.4.41 (Ubuntu)
Last-Modified: Sun, 21 Sep 2025 15:32:40 GMT
ETag: "11e8f-63f5169cabcc1-gzip"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 20102
Content-Type: application/rss+xml
The Universe of Discourse
https://blog.plover.com
The Universe of Discourse (Mark Dominus Blog)
en
-
Linogram circular problems
https://blog.plover.com/2007/01/22#param-circle
The problems are not related to geometric circles; the are logically
circular. <p>
<img style="margin-left: 0.5em; margin-bottom: 0.5em;" src="https://pic.blog.plover.com/linogram/param-circle/curve.jpg" align=right>
In the course of preparing my sample curve diagrams, one of which is
shown at right, I ran into several related bugs in the way that arrays
were being handled. What I really wanted to do was to define a
<tt>labeled_curve</tt> object, something like this:<p>
<pre>
define labeled_curve extends curve {
spot s[N];
constraints { s[i] = control[i]; }
}
</pre>
That is, it is just like an ordinary curve, except that it also has a
"spot" at each control point. A "spot" is a graphic element that
marks the control point, probably with a small circle or something of
the sort:<p>
<pre>
define spot extends point {
circle circ(r=0.05);
constraints {
circ.c.x = x; circ.c.y = y;
}
}
</pre>
A <tt>spot</tt> is like a <tt>point</tt>, and so it has an <i>x</i>
and a <i>y</i> coordinate. But it also has a circle, <tt>circ</tt>,
which is centered at this location. (<tt>circ.c</tt> is the center of
the circle.)<p>
When I first tried this, it didn't work because <tt>linogram</tt>
didn't understand that a <tt>labeled_curve</tt> with <i>N</i> = 4
control points would also have four instances of <tt>circ</tt>, four
of <tt>circ.c</tt>, four of <tt>circ.c.x</tt>, and so on. It did
understand that the labeled curve would have four instances of
<tt>s</tt>, but the multiplicity wasn't being propagated to the
subobjects of <tt>s</tt>. <p>
I fixed this up in pretty short order.<p>
But the same bug persisted for <tt>circ.r</tt>, and this is not
so easy to fix. The difference is that while <tt>circ.c</tt> is a
full subobject, subject to equation solving, and expected to be
unknown, <tt>circ.r</tt> is a parameter, which much be specified in
advance.<p>
<i>N</i>, the number of spots and control points, is another such
parameter. So there's a first pass through the object hierarchy to
collect the parameters, and then a later pass figures out the
subobjects. You can't figure out the subobjects without the
parameters, because until you know the value of parameters like
<i>N</i>, you don't know how many subobjects there are in arrays like
<tt>s[N]</tt>. <p>
For subobjects like <tt>S[N].circ.c.x</tt>, there is no issue. The
program gathers up the parameters, including <i>N</i>, and then
figures out the subobjects, including <tt>S[0].circ.c.x</tt> and so
on. But <tt>S[0].circ.r</tt>, is a parameter, and I can't say that
its value will be postponed until after the values of the parameters
are collected. I need to know the values of the parameters before I
can figure out what the parameters are.<p>
This is not a show-stopper. I can think of at least three ways
forward. For example, the program could do a separate pass for
<tt>param index</tt> parameters, resolving those first. Or I could do
a more sophisticated dependency analysis on the parameter values; a
lot of the code for this is already around, to handle things like
<tt>param number a = b*2, b=4, c=a+b+3, d=c*5+b</tt>. But I need to
mull over the right way to proceed.<p>
Consider this oddity in the meantime:<p>
<pre>
define snark {
param number p = 3;
}
define boojum {
param number N = s[2].p;
snark s[N];
}
</pre>
Here the program needs to know the value of <i>N</i> in order to
decide how many snarks are in a boojum. But the number N itself is
determined by examining the <tt>p</tt> parameter in snark 2, which
itself will not exist if <i>N</i> is less than 3. Should this sort of
nonsense be allowed? I'm not sure yet.<p>
When you invent a new kind of program, there is an interesting
tradeoff between what you want to allow, what you actually do allow,
and what you know how to implement. I definitely want to allow the
<tt>labeled_curve</tt> thing. But I'm quite willing to let the
snark-boojum example turn into some sort of run-time failure.<p>
-
Recent Linogram development update
https://blog.plover.com/2007/01/21#arrays-0
Lately most of my spare time (and some not-spare time) has been going
to <tt>linogram</tt>. I've been posting updates pretty regularly at
<a href="https://hop.perl.plover.com/linogram/">the main <tt>linogram</tt> page</a>. But I don't know if
anyone ever looks at that page. That got me thinking that it was not
convenient to use, even for people who are interested in
<tt>linogram</tt>, and that maybe I should have an RSS/Atom feed for
that page so that people who <i>are</i> interested do not have to keep
checking back. <p>
Then I said "duh", because I already have a syndication feed for <a
href="https://blog.plover.com/linogram/"><i>this</i> page</a>, so why not just
post the stuff here?<p>
So that is what I will do. I am about to copy a bunch of stuff from
that page to this one, backdating it to match when I posted it.<p>
The new items are:
<a href="https://blog.plover.com/linogram/update20061226.html">[1]</a>
<a href="https://blog.plover.com/linogram/update20061227.html">[2]</a>
<a href="https://blog.plover.com/linogram/update20070117.html">[3]</a>
<a href="https://blog.plover.com/linogram/update20070120.html">[4]</a>
<a href="https://blog.plover.com/linogram/bentline.html">[5]</a>.<p>
People who only want to hear about <tt>linogram</tt>, and not about
anything else, can subscribe to
<a href="https://blog.plover.com/linogram/index.rss"><img
src="https://pic.blog.plover.com/linogram/arrays-0/feed-16.jpg" border=0>this RSS feed</a>
or
<a href="https://blog.plover.com/linogram/index.atom"><img
src="https://pic.blog.plover.com/linogram/arrays-0/feed-16.jpg" border=0>this Atom feed</a>.
-
Another Linogram success story
https://blog.plover.com/2007/01/20#bentline
I've been saying for a while that a well-designed system surprises
even the designer with its power and expressiveness. Every time
<tt>linogram</tt> surprises me, I feel a flush of satisfaction because
this is evidence that I designed it well. I'm beginning to think that
<tt>linogram</tt> may be the best single piece of design I've
<i>ever</i> done.<p>
Here was today's surprise. For a long time, my demo diagram has been
a rough rendering of one of the figures from <cite>Higher-Order
Perl</cite>:<p>
<p align=center><a href="https://pic.blog.plover.com/linogram/bentline/demo-19.jpg"><img border=0
src="https://pic.blog.plover.com/linogram/bentline/demo-19-th.jpg"></a></p>
(It's big, so this is a reduced version; click to expand it.)<p>
I wanted component <i>k</i> in the middle of the diagram to be a
curved line, but since I didn't have curved lines yet, I used two
straight lines instead, as shown below:<p>
<p align=center><img src="https://pic.blog.plover.com/linogram/bentline/angle.jpg"></p>
As of today, I have working curves, so I went to replace <i>k</i> with
a curved line instead. I went into the <tt>demo.lino</tt> file, which
I wrote a couple of years ago, to see
what changes would be required. The definition of <i>k</i> was much
more complicated than I remembered. Here is the relevant extract:<p>
<pre>
define bentline {
line upper, lower;
param number depth = 0.2;
point start, end, center;
constraints {
center = upper.end = lower.start;
start = upper.start; end = lower.end;
start.x = end.x = center.x + depth;
center.y = (start.y + end.y)/2;
}
}
...
<b><font color="purple"> bentline k;</font></b>
label klbl(text="k") = k.upper.center - (0.1, 0);
...
constraints {
...
k.start = plus.sw; k.end = times.nw;
...
}
</pre>
So I had defined a thing called a <tt>bentline</tt>, which is a line
with a slight angle in it. Or more precisely, it's two
approximately-vertical lines joined end-to-end. It has three
important reference points: <tt>start</tt>, which is the top point,
<tt>end</tt>, the bottom point, which is directly under the top
point, and <tt>center</tt>, halfway in between, but displaced
leftward by <tt>depth</tt>. <p>
I now needed to replace this with a curved line. This meant
removing all the references to <tt>start</tt>, <tt>end</tt>,
<tt>upper</tt> and so forth, since curves don't have any of those
things. A significant rewrite, in other words.<p>
But then I had a happy thought. I added the following definition to
the file:<p>
<pre>
require "curve";
define bentline_curved extends bentline {
curve c(N=3);
constraints {
c.control[0] = start;
c.control[1] = center;
c.control[2] = end;
}
draw { c; }
}
</pre>
A <tt>bentline_curved</tt> is now the same as a <tt>bentline</tt>, but
with an extra curved line, called <tt>c</tt>, which has three control
points, defined to be identical with <tt>start</tt>, <tt>center</tt>,
and <tt>end</tt>. These three points inherit all the same constraints
as before, and so are constrained in the same way and positioned in
the same way. But instead of drawing the two lines, the
<tt>bentline_curved</tt> draws only the curve. <p>
I then replaced:
<pre>
bentline k;
</pre>
with:
<pre>
bentline_curved k;
</pre>
and recompiled the diagram. The result is below:<p>
<p align=center><a href="https://pic.blog.plover.com/linogram/bentline/demo-199.jpg"><img border=0
src="https://pic.blog.plover.com/linogram/bentline/demo-199-th.jpg"></a></p>
This diagram is identical, except that arc <i>k</i> has changed from a
bent line to a curve. Compare:<p>
<table align=center width="95%">
<tr><td width="50%" align=center><img src="https://pic.blog.plover.com/linogram/bentline/angle.jpg"> <td width="50%"
align=center><img src="https://pic.blog.plover.com/linogram/bentline/curve.jpg">
</table>
To make this change, I didn't have to edit or understand the
definition of <tt>bentline</tt>, except to understand a bit about its
interface: <tt>begin</tt>, <tt>end</tt>, and <tt>center</tt>. I could
build a new definition atop it that allowed the rest of the program to
use it in exactly the same way, although it was drawn in a completely
different way.<p>
I didn't foresee this when I designed the <tt>linogram</tt> language.
Sometimes when you try a new kind of program for the first time, you
keep getting unpleasant surprises. You find things you realize you
didn't think through, or that have unexpected consequences, or
features that turn out not to be as powerful as you need, or that mesh
badly with other features. Then you have to go back and revisit your
design, fix problems, try to patch up mismatches, and so forth. In
contrast, the appearance of the sort of pleasant surprise like the one
in this article is exactly the opposite sort of situation, and makes
me really happy.<p>
-
Linogram development: 20070120 Update
https://blog.plover.com/2007/01/20#update20070120
The array feature is working, pending some bug fixes. I have not yet
found all the bugs, I think. But the feature has definitely moved
from the does-not-work-at-all phase into the mostly-works phase. That
is, I am spending most of my time tracking down bugs, rather than
writing large amount of code. The test suite is expanding rapidly.<p>
The regular polygons are working pretty well, and the curves are
working pretty well. Here are some simple examples:<p>
<table align=center width="95%">
<tr><td width="50%" align=center><img src="https://pic.blog.plover.com/linogram/update20070120/curve-demo2.jpg">
<td width="50%" align=center><img src="https://pic.blog.plover.com/linogram/update20070120/curve-demo3.jpg">
</table>
<a
href="https://pic.blog.plover.com/linogram/update20070120/curve-demo.jpg">Here's
a more complicated curve demo</a>.<p>
One interesting design problem turned up that I had not foreseen. I
had planned for the <tt>curve</tt> object to be specified by 2 or more
control points. (The control points are marked by little circles in
the demo pictures above.) The first and last controlpoints would be
endpoints, and the curve would start at point 0, then head toward
point 1, veer off toward point 2, then veer off toward point 3, etc.,
until it finally ended at point <i>N</i>. You can see this in the
pictures. <p>
This is like the behavior of <tt>pic</tt>, which has good-looking
curves. You don't want to require that the curve pass <i>through</i>
all the control points, because that does not give it enough freedom
to be curvy. And this behavior is easy to get just by using a
degree-<i>N</i> Bézier curve, which was what I planned to
do.<p>
However, PostScript surprised me. I had thought that it had
degree-<i>N</i> Bézier curves, but it does not. It has only
degree-3 ("cubic") Bézier curves. So then I was left with the
puzzle of how to use PostScript's Bézier curves to get what I
wanted. Or should I just change the definition of <tt>curve</tt> in
<tt>linogram</tt> to be more like what PostScript wanted? Well, I
didn't want to do that, because <tt>linogram</tt> is supposed to be
generic, not a front-end to PostScript. Or, at least, not a front-end
<i>only</i> to PostScript.<p>
I did figure out a compromise. The curves generated by the PostScript
drawer are made of PostScript's piecewise-cubic curves, but, as you
can see from the demo pictures, they still have the behavior I want.
The four control points in the small demos above actually turn into
two PostScript cubic Bézier curves, with a total of seven control points.
If you give <tt>linogram</tt> the points
<i>A</i>,
<i>B</i>,
<i>C</i>, and
<i>D</i>, the PostScript engine draws two cubic Bézier curves, with control
points
{<i>A</i>,
<i>B</i>,
<i>B</i>,
(<i>B</i> + <i>C</i>)/2} and
{(<i>B</i> + <i>C</i>)/2,
<i>C</i>,
<i>C</i>,
<i>D</i>}, respectively. Maybe I'll write a blog article about why I
chose to do it this way.<p>
One drawback of this approach is that the curves turn rather sharply
near the control points. I may tinker with the formula later to
smooth out the curves a bit, but I think for now this part is good
enough for beta testing.<p>
-
Linogram development: 20070117 Update
https://blog.plover.com/2007/01/17#update20070117
The array feature is almost complete, perhaps entirely complete.
Fully nontrivial tests are passing. For example, here is test
<tt>polygon002</tt> from the distribution:<p>
<pre>
require "polygon";
polygon t1(N=3), t2(N=3);
constraints {
t1.v[0] = (0, 0);
t1.v[1] = (1, 1);
t1.v[2] = (2, 3);
t2.v[i] = t1.v[i-1];
}
</pre>
This defines two polygons, <i>t</i><sub>1</sub> and
<i>t</i><sub>2</sub>, each with three sides. The three vertices of
<i>t</i><sub>1</sub> are specified explicitly. Triangle
<i>t</i><sub>2</sub> is the same, but with the vertices numbered
differently: <i>t</i><sub>2</sub>.<i>v</i><sub>0</sub> =
<i>t</i><sub>1</sub>.<i>v</i><sub>2</sub>,
<i>t</i><sub>2</sub>.<i>v</i><sub>1</sub> =
<i>t</i><sub>1</sub>.<i>v</i><sub>0</sub>, and
<i>t</i><sub>2</sub>.<i>v</i><sub>2</sub> =
<i>t</i><sub>1</sub>.<i>v</i><sub>1</sub>. Each of the triangles also
has three edges, defined implicitly by the definition in
<tt>polygon.lino</tt>:<p>
<pre>
require "point";
require "line";
define polygon {
param index N;
point v[N];
line e[N];
constraints {
e[i].start = v[i];
e[i].end = v[i+1];
}
}
</pre>
All together, there are 38 values here: 2 coordinates for each of
three vertices of each of the two triangles makes 12; 2 coordinates
for each of two endpoints of each of three edges of each of the two
triangles is another 24, and the two <tt>N</tt> values themselves
makes a total of 12 + 24 + 2 = 38.<p>
All of the equations are rather trivial. All the difficulty is in
generating the equations in the first place. The program must
recognize that the variable <tt>i</tt> in the <tt>polygon</tt>
definition is a dummy iterator variable, and that it is associatated
with the parameter <tt>N</tt> in the polygon definition. It must
propagate the specification of <tt>N</tt> to the right place, and then
iterate the equations appropriately, producing something like:<p>
<blockquote>
<i>e</i><sub>0</sub>.<i>end</i> = <i>v</i><sub>0+1</sub><br>
<i>e</i><sub>1</sub>.<i>end</i> = <i>v</i><sub>1+1</sub><br>
<i>e</i><sub>2</sub>.<i>end</i> = <i>v</i><sub>2+1</sub><br>
</blockquote>
Then it must fold the constants in the subscripts and apply the
appropriate overflow semantics—in this case, 2+1=0.<p>
Open figures still don't work properly. I don't think this will take
too long to fix.<p>
The code is very messy. For example, all the <tt>Type</tt> classes
are in a file whose name is not <tt>Type.pm</tt> but
<tt>Chunk.pm</tt>. I plan to have a round of cleanup and consolidation
after the 2.0 release, which I hope will be soon.<p>
-
Linogram development: 20061227 Update
https://blog.plover.com/2006/12/27#update20061227
Defective constraints (see <a
href="https://blog.plover.com/linogram/update20061226.html">yesterday's article</a>)
are now handled. So the example <a
href="https://blog.plover.com/linogram/update20061226.html">from yesterday's
article</a> has now improved to:<p>
<pre>
define regular_polygon<font color="red"><b>[N]</b></font> <font color="green"><b>closed</b></font> {
param number radius, rotation=0;
point vertex<font color="green"><b>[N]</b></font>, center;
line edge<font color="green"><b>[N]</b></font>;
constraints {
vertex<font color="green"><b>[i]</b></font> = center + radius * cis(rotation + 360*i/N);
edge<font color="green"><b>[i]</b></font>.start = vertex<font color="green"><b>[i]</b></font>;
edge<font color="green"><b>[i]</b></font>.end = vertex<font color="green"><b>[i+1]</b></font>;
}
}
</pre>
-
Linogram development: 20061226 Update
https://blog.plover.com/2006/12/26#update20061226
I have made significant progress on the "arrays" feature. I
recognized that it was actually three features; I have two of them
implemented now. Taking the example from further down the page:<p>
<pre>
define regular_polygon<font color="red"><b>[N] closed</b></font> {
param number radius, rotation=0;
point vertex<font color="green"><b>[N]</b></font>, center;
line edge<font color="green"><b>[N]</b></font>;
constraints {
vertex<font color="green"><b>[i]</b></font> = center + radius * cis(rotation + 360*i/N);
edge<font color="green"><b>[i]</b></font>.start = vertex<font color="green"><b>[i]</b></font>;
edge<font color="green"><b>[i]</b></font>.end = vertex<font color="red"><b>[i+1]</b></font>;
}
}
</pre>
The stuff in green is working just right; the stuff in red is not.<p>
The following example works just fine:<p>
<pre>
number p[3];
number q[3];
constraints {
p[i] = q[i];
q[i] = i*2;
}
</pre>
What's still missing? Well, if you write
<pre>
number p[3];
number q[3];
constraints {
p[i] = q[i+1];
}
</pre>
This should imply three constraints on elements of <i>p</i> and
<i>q</i>:<p>
<blockquote>
<i>p</i><sub>0</sub> = <i>q</i><sub>1</sub><br>
<i>p</i><sub>1</sub> = <i>q</i><sub>2</sub><br>
<i>p</i><sub>2</sub> = <i>q</i><sub>3</sub><br>
</blockquote><p>
The third of these is defective, because there is
no <i>q</i><sub>3</sub>. If the figure is "closed" (which is the
default) the subscripts should wrap around, turning the defective
constraint into
<i>p</i><sub>2</sub> = <i>q</i><sub>0</sub> instead. If the figure is
declared "open", the defective constraint should simply be
discarded.<p>
The syntax for parameterized definitions (<tt>define
regular_polygon[N] { ... }</tt>) is still a bit up in the air; I am
now leaning toward a syntax that looks more like <tt>define
regular_polygon { param index N; ... }</tt> instead.<p>
The current work on the "arrays" feature is in the <a
href="https://hop.perl.plover.com/linogram/cvsweb.cgi/linogram/?only_with_tag=arrays">CVS
repository on the branch <tt>arrays</tt></a>; get <a
href="https://hop.perl.plover.com/linogram/cvsweb.cgi/linogram/?only_with_tag=tests-pass">the
version tagged <tt>tests-pass</tt></a> to get the most recent working
version. Most of the interesting work has been on the files
<a
href="https://hop.perl.plover.com/linogram/cvsweb.cgi/linogram/lib/Chunk.pm?only_with_tag=arrays"><tt>lib/Chunk.pm</tt></a>
and
<a
href="https://hop.perl.plover.com/linogram/cvsweb.cgi/linogram/lib/Expression.pm?only_with_tag=arrays"><tt>lib/Expression.pm</tt></a>.
<p>
-
Linogram: The EaS as a component
https://blog.plover.com/2006/11/23#sline-4
In <a href="https://blog.plover.com/linogram/sline-2.html">an earlier article</a>, I
discussed the definition of an Etch-a-Sketch picture as a component in the <tt>linogram</tt>
drawing system. I then <a
href="https://blog.plover.com/linogram/sline-3.html">digressed for a day</a> to rant
about the differences between specification-based and WYSIWYG
systems. I now return to the main topic.<p>
Having defined the <tt>EAS</tt> component, I can use it in several
diagrams. The typical diagram looks like this:
<p align=center><img border=0 src="https://pic.blog.plover.com/linogram/sline-4/eas-typical.jpg"></p>
Here's the specification for that figure:<p>
<pre>
require "eas";
number WIDTH = 2;
EAS the_eas(w = WIDTH);
gear3 gears(width=WIDTH, r1=1/4, r3=1/12);
constraints {
the_eas.left = gears.g1.c;
the_eas.right = gears.g3.c;
}
</pre>
The <tt>eas.lino</tt> file defines the <tt>EAS</tt> component and also
the <tt>gear3</tt> component, which describes what the three gears
should look like. The diagram itself just chooses a global width and
communicates it to the <tt>EAS</tt> component and the <tt>gear3</tt>
component. It also communicates two of the three selected gear sizes
to the <tt>gear3</tt>, which generates the three gears and their
axles. Finally, the specification constrains the <tt>left</tt>
reference point of the Etch-a-Sketch to lie at the center of gear 1, and the
<tt>right</tt> reference point to lie at the center of gear 3.<p>
This sort of thing was exactly what I was hoping for when I started
designing <tt>linogram</tt>. I wanted it to be easy to define a new component type,
like <tt>EAS</tt> or <tt>gear3</tt>, and to use it as if it were a
built-in type. (Indeed, as noted before, even <tt>linogram</tt>'s "built-in" types
are not actually built in! Everything from <tt>point</tt> on up is
defined by a <tt>.lino</tt> file just like the one above, and if the
user wants to redefine <tt>point</tt> or <tt>line</tt>, they are free
to do so.)<p>
(It may not be clear redefining <tt>point</tt> or <tt>line</tt> is
actually a useful thing to do. But it could be. Consider, for
example, what would happen if you were to change the definition of
<tt>point</tt> to contain a <i>z</i> coordinate, in addition to the
<i>x</i> and <i>y</i> coordinates it normally has. The <tt>line</tt>
and <tt>box</tt> definitions would inherit this change, and become
three-dimensional objects. If provided with a suitably enhanced
rendering component, <tt>linogram</tt> would then become a three-dimensional
diagram-drawing program. Eventually I will add this enhancement to <tt>linogram</tt>.)<p>
I am a long-time user of the AT&T Unix tool <tt>pic</tt>, which
wants to allow you to define and use compound objects in this way, but
gets it badly wrong, so that it's painful and impractical. Every time
I suffered through <tt>pic</tt>'s ineptitude, I would think about how
it might be done properly. I think <tt>linogram</tt> gets it right; <tt>pic</tt> was
a major inspiration. <p>
<h2>Slanty lines</h2>
Partway through drawing the Etch-a-Sketch diagrams, I had a happy idea. Since I
was describing Etch-a-Sketch configurations that would draw lines with various
slopes, why not include examples?<p>
<p align=center><img border=0 src="https://pic.blog.plover.com/linogram/sline-4/eas4-3-12.jpg"></p>
Optimally, one would like to say something like this:<p>
<pre>
line L(slope=..., start=..., ...);
</pre><p>
In general, though, this is too hard to handle. The resulting
equations are quadratic, or worse, trigonometric, and <tt>linogram</tt> does not know
how to solve those kinds of equations.<p>
But if the slope is required to be constant, then the quadratic
equations become linear, and there is no problem. And in this case, I
only needed constant slopes. Once I realized this, it was easy to
define a constant-slope line type:<p>
<pre>
require "line";
define sline extends line {
param number slope;
constraints {
end.y - start.y = slope * (end.x - start.x);
}
}
</pre>
An <tt>sline</tt> is just like a <tt>line</tt>, but it has an
additional parameter, <tt>slope</tt> (which must be constant, and
specified at compile time, not inferred from other values) and one
extra constraint that uses it. Normally, all four of <tt>start.x</tt>,
<tt>end.x</tt>, <tt>start.y</tt>, and <tt>end.y</tt> are independent.
The constraint removes some of the independence, so that any three,
plus the slope, are sufficient to determine the fourth.<p>
The diagram above was generated from this specification:<p>
<pre>
require "eas";
require "sline";
number WIDTH = 2;
EAS the_eas(w = WIDTH);
gear3 gears(width=WIDTH, r1=1/4, r3=1/12);
sline L1(slope=1/3, start=the_eas.screen.ne);
sline L2(slope=3, start=the_eas.screen.ne);
constraints {
the_eas.left = gears.g1.c;
the_eas.right = gears.g3.c;
L1.end.x = the_eas.screen.w.x;
L2.end.y = the_eas.screen.s.y;
}
</pre><P>
The additions here are the <tt>sline</tt> items, named <i>L1</i> and
<i>L2</i>. Both lines start at the northeast corner of the screen.
Line <i>L1</i> has slope 1/3, and its other endpoint is constrained to
lie somewhere on the west edge of the screen. The <i>y</i>-coordinate
of that endpoint is not specified, but is implicitly determined by
the other constraints. To locate it, <tt>linogram</tt> must solve some linear
equations. The complete set of constraints no the line is:<p>
<table align=center>
<tr><td><i>L1.start.x</i> = <i>the_eas.screen.ne.x</i>
<tr><td><i>L1.start.y</i> = <i>the_eas.screen.ne.y</i>
<tr><td><i>L1.end.x</i> = <i>the_eas.screen.w.x</i>
<tr><td><i>L1.end.y</i> - <i>L1.start.y</i> = <i>L1.slope</i> ×
(<i>L1.end.x</i> - <i>L1.start.x</i>);
</table>
The <i>L1.slope</i> is required to be specified before the equations
are solved, and in the example above, it is 1/3, so the last of these
equations becomes:<p>
<table align=center>
<tr><td><i>L1.end.y</i> - <i>L1.start.y</i> = 1/3 ×
(<i>L1.end.x</i> - <i>L1.start.x</i>);
</table>
So the
resulting system is entirely linear, and is easily solved.<p>
The other line, <i>L2</i>, with slope 3, is handled similarly; its
endpoint is constrained to lie somewhere on the south side of the
screen.<p>
<h2>Surprise features</h2>
When I first planned <tt>linogram</tt>, I was hardly thinking of slopes at all,
except to dismiss them as being intractable, along with a bunch of
other things like angles and lengths. But it turned out that they are
a little bit tractable, enough that <tt>linogram</tt> can get a bit of a handle on
them, thanks to the <tt>param</tt> feature that allows them to be
excluded from the linear equation solving.<p>
One of the signs that you have designed a system well is that it comes
out to be more powerful than you expected when you designed it, and
lends itself to unexpected uses. The <tt>sline</tt>s are an example
of that. When it occurred to me to try doing them, my first thought
was "but that won't work, will it?" But it does work.<p>
Here's another technique I hadn't specifically planned for, that is
already be supported by <tt>linogram</tt>. Suppose Fred Flooney wants to use the
<tt>eas</tt> library, but doesn't like the names of the reference
points in the <tt>EAS</tt> component. Fred is quite free to define
his own replacement, with whatever names for whatever reference
points he likes<p>
<pre>
require "eas";
define freds_EAS {
EAS it;
point middle = it.screen.c;
point bernard = (it.body.e + 2*it.body.se)/3;
line diag(start=it.body.nw, end=it.body.se);
draw { it; }
}
</pre>
The <tt>freds_EAS</tt> component is essentially the same as the
<tt>EAS</tt> component defined by <tt>eas.lino</tt>. It contains a
single Etch-a-Sketch, called <tt>it</tt>, and a few extra items that Fred is
interested in. If <tt>E</tt> is a <tt>freds_EAS</tt>, then
<tt>E.middle</tt> refers to the point at the center of <tt>E</tt>'s
screen; <tt>E.bernard</tt> is a point two-thirds of the way between
the middle and the bottom corner of its outer edge, and
<tt>E.diag</tt> is an invisible line running diagonally across the
entire body, equipped with the usual <tt>E.diag.start</tt>,
<tt>E.diag.center</tt>, and the like. All the standard items are
still available, as <tt>E.it.vknob</tt>,
<tt>E.it.screen.left.center</tt>, and so on.<p> <p>
The <tt>draw</tt> section tells <tt>linogram</tt> that only the component named
<tt>it</tt>—that is, the Etch-a-Sketch itself—should be drawn; this
suppresses the <tt>diag</tt> line. which would otherwise have been
rendered also.<p>
If Fred inherits some other diagram or component that includes an Etch-a-Sketch,
he can still use his own aliases to refer to the parts of the Etch-a-Sketch,
without modifying the diagram he inherited. For example, suppose Fred
gets handed my specification for the diagram above, and wants to
augment it or incorporate it in a larger diagram. The diagram above
is defined by a file called "eas4-3-12.lino", which in turn
<tt>require</tt>s <tt>EAS.lino</tt>. Fred does not need to modify
<tt>eas4-3-12.lino</tt>; he can do:<p>
<pre>
require "eas4-3-12.lino";
require "freds_eas";
freds_EAS freds_eas(it = the_eas);
constraints {
freds_eas.middle = ...;
freds_eas.bernard = ...;
}
...
</pre>
Fred has created one of his extended Etch-a-Sketch components, and identified
the Etch-a-Sketch part of it with <tt>the_eas</tt>, which is the Etch-a-Sketch part of my
original diagram. Fred can then apply constraints to the
<tt>middle</tt> and <tt>bernard</tt> sub-parts of his
<tt>freds_eas</tt>, and these constraints will be propagated to the
corresponding parts the <tt>the_eas</tt> in my original diagram. Fred
can now specify relations in terms of his own peronal <tt>middle</tt>
and <tt>bernard</tt> items, and they will automatically be related to
the appropriate parts of my diagram, even though I have never heard of
Fred and have no idea what <tt>bernard</tt> is supposed to represent.<p>
Why Fred wants these names for these components, I don't know;
it's just a contrived example. But the important point is that if he
does want them, he can have them, with no trouble.<p>
<h2>What next?</h2>
There are still a few things missing from <tt>linogram</tt>. The last major missing
feature is arrays. The <tt>gear3</tt> construction I used above is
clumsy. It contains a series of constraints that look like:<p>
<pre>
g1.e = g2.w;
g2.e = g3.w;
</pre>
The same file also has an analogous <tt>gear2</tt> definition, for
diagrams with only two gears. Having both <tt>gear2</tt> and
<tt>gear3</tt>, almost the same, is silly and wasteful. What I really
want is to be able to write:<p>
<pre>
define gears[n] open {
gear[n] g;
constraints {
g[i].e = g[i+1].w;
}
}
</pre>
and this would automatically imply components like <tt>gear2</tt> and
<tt>gear3</tt>, with 2 and 3 gears, but denoted <tt>gear[2]</tt> and <tt>gear[3]</tt>,
respectively; it would also imply <tt>gear[17]</tt> and
<tt>gear[253]</tt> with the analogous constraints.<p>
For <tt>gear[3]</tt>, two constraints are generated:
<i>g</i>[0].<i>e</i> = <i>g</i>[1].<i>w</i>, and
<i>g</i>[1].<i>e</i> = <i>g</i>[2].<i>w</i>. Normally, arrays are
cyclic, and a third constraint,
<i>g</i>[2].<i>e</i> = <i>g</i>[0].<i>w</i>, would be generated as
well. The <tt>open</tt> keyword suppresses this additional constraint.<p>
This feature is under development now. I originally planned it to support
splines, which can have any number of control points. But once again,
I found that the array feature was going to be useful for many other
purposes.<p>
When the array feature is finished, the next step will be to create a
<tt>spline</tt> type: <tt>spline[4]</tt> will be a spline with 4
control points; <tt>spline[7]</tt> will be a spline with 7 control
points, and so on. PostScript will take care of drawing the splines
for me, so that will be easy. I will also define a regular polygon
type at that time:<p>
<pre>
define polygon[n] closed {
param number rotation = 0;
number radius;
point v[n], center;
line e[n];
constraints {
v[i] = center + radius * cis(rotation + i * 360/n);
e[i].start = v[i];
e[i].end = v[i+1];
}
}
</pre>
<tt>polygon[3]</tt> will then be a rightward-pointing equilateral
triangle; constraining any two of its vertices will determine the
position of the third, which will be positioned automatically. Note
the <tt>closed</tt> keyword, which tells <tt>linogram</tt> to include the constraint
<i>e</i>[2].<i>end</i> =
<i>v</i>[0], which would have been omitted had <tt>open</tt> been
used instead. <p>
<div class="bookbox"><table align=right width="20%" bgcolor="#ffffdd"
border=1><tr><td align=center><A HREF="https://bookshop.org/a/93187/9781558607019"><font
size="-2">Buy</font><br><cite><font>Higher-Order Perl</font></cite><br><IMG SRC="https://images-us.bookshop.org/ingram/9781558607019.jpg?height=250&v=v2" BORDER="0"
ALIGN="center" ALT="(Higher-Order Perl cover missing)" ><br>
<font size="-2">from Bookshop.org<br>(with kickback)</a><br><a href="https://bookshop.org/a/00000/9781558607019">(without kickback)</a></font></a>
</td></tr></table></div> More complete information about <tt>linogram</tt> is available in
<a href="https://hop.perl.plover.com/chap09.html">Chapter 9</a> of <a href="https://hop.perl.plover.com/">Higher-Order
Perl</a>; complete source code is available from <a
href="https://hop.perl.plover.com/linogram/">the <tt>linogram</tt> web site</a>.<p>
-
Linogram: Declarative drawing
https://blog.plover.com/2006/11/22#sline-3
As we saw in <a href="https://blog.plover.com/linogram/sline-2.html">yesterday's
article</a>, The definition of the <tt>EAS</tt> component is twenty
lines of strange, mostly mathematical notation. I could have drawn
the Etch-a-Sketch in a WYSIWYG diagram-drawing system like xfig. It might have
been less trouble. Many people will prefer this. Why invent <tt>linogram</tt>?<p>
Some of the arguments should be familiar to you. The world is full of
operating systems with GUIs. Why use the Unix command line? The
world is full of WYSIWYG word processors. Why use TeX? <p>
Text descriptions of processes can be automatically generated, copied,
and automatically modified. Common parts can be abstracted out. This
is a powerful paradigm.<p>
Collectively, the diagrams contained 19 "gears". Partway through, I
decided that the black dot that represented the gear axle was too
small, and made it bigger. Had I been using a WYSIWYG system, I would
have had the pleasure of editing 19 black dots in 10 separate files.
Then, if I didn't like the result, I would have had the pleasure of
putting them back the way they were. With <tt>linogram</tt>, all that was required
was to change the 0.02 to an 0.05 in <tt>eas.lino</tt>:<p>
<pre>
define axle {
param number r = 0.05;
circle a(fill=1, r=r);
}
</pre><p>
<a href="https://blog.plover.com/games/etchasketch-correction.html">The Etch-a-Sketch article</a> contained seven similar diagrams with slight
differences. Each one contained a <tt>require "eas";</tt> directive
to obtain the same definition of the <tt>EAS</tt> component. Partway
through the process, I decided to alter the aspect ratio of the Etch-a-Sketch
body. Had I been drawing these with a WYSIWYG system, that would have
meant editing each of the seven diagrams in the same way. With <tt>linogram</tt>, it
meant making a single trivial change to <tt>eas.lino</tt>.<p>
A <tt>linogram</tt> diagram has a structure: it is made up of component parts with
well-defined relationships. A line in a WYSIWYG diagram might be 4.6
inches long. A line in a <tt>linogram</tt> diagram might also be 4.6 inches long,
but that is probably not all there is to it. The south edge of the
<tt>body</tt> box in my diagrams is 4.6 inches long, but only because
it has been inferred (from other relationships) to be 1.15 <i>w</i>,
and because <i>w</i> was specified to be 4 inches. Change <i>w</i>,
and everything else changes automatically to match. Each part moves
appropriately, to maintain the specified relationships. The distance
from the knob centers to the edge remains 3/40 of the distance between
the knobs. The screen remains 70% as tall as the body. A WYSIWYG
system might be able to scale everything down by 50%, but all it can
do is to scale down <i>everything</i> by 50%; it doesn't know enough
about the relationships between the elements to do any better. What
will happen if I reduce the width but not the height by 50%? The gears
are circles; will the WYSIWYG system keep them as circles? Will they
shrink appropriately? Will their widths be adjusted to fit between
the two knobs? Maybe, or maybe not. In <tt>linogram</tt>, the required relationships
are all explicit. For example, I specified the size of the black axle
dots in absolute numbers, so they do <i>not</i> grow or shrink when
the rest of the diagram is scaled. <p>
Finally, because the diagrams are mathematically specified, I can
leave the definitions of some of the components implicit in the
mathematics, and let <tt>linogram</tt> figure them out for me. For example, consider
this diagram:<p>
<p align=center><img border=0 src="https://pic.blog.plover.com/linogram/sline-3/eas4-3-12.jpg"></p>
The three gears here have radii of <i>w</i>/4, <i>w</i>/3, and
<i>w</i>/12, respectively. Here is the line in the diagram
specification that generates them:<p>
<pre>
gear3 gears(width=WIDTH, r1=1/4, r3=1/12);
</pre><p>
I specified <i>r1</i>, the radius of the left gear, and <i>r3</i>, the
radius of the right gear. Where is the middle gear? It's implicit in
the definition of the <tt>gear3</tt> type. The definition knows that
the three gears must all touch, so it calculates the radius of the
middle gear accordingly:<p>
<pre>
define gear3 {
...
number r2 = (1 - r1 - r3) / 2;
...
}
</pre><p>
<tt>linogram</tt> gives me the option of omitting <i>r2</i> and having it be
calculated for me from this formula, or of specifying <i>r2</i>
anyway, in which case <tt>linogram</tt> will check it against this formula and raise
an error if the values don't match. <p>
<div class="bookbox"><table align=right width="20%" bgcolor="#ffffdd"
border=1><tr><td align=center><A HREF="https://bookshop.org/a/93187/9781558607019"><font
size="-2">Buy</font><br><cite><font>Higher-Order Perl</font></cite><br><IMG SRC="https://images-us.bookshop.org/ingram/9781558607019.jpg?height=250&v=v2" BORDER="0"
ALIGN="center" ALT="(Higher-Order Perl cover missing)" ><br>
<font size="-2">from Bookshop.org<br>(with kickback)</a><br><a href="https://bookshop.org/a/00000/9781558607019">(without kickback)</a></font></a>
</td></tr></table></div>
Tomorrow: The Etch-a-Sketch as a component.<p>
More complete information about <tt>linogram</tt> is available in <a
href="https://hop.perl.plover.com/chap09.html">Chapter 9</a> of <a
href="https://hop.perl.plover.com/">Higher-Order Perl</a>; complete
source code is available from <a
href="https://hop.perl.plover.com/linogram/">the <tt>linogram</tt> web site</a>.<p>
-
Linogram: The EaS component
https://blog.plover.com/2006/11/21#sline-2
In <a
href="https://blog.plover.com/linogram/sline.html">yesterday's article</a> I
talked about the basic facilities provided by <tt>linogram</tt>. What about the
Etch-a-Sketch diagrams?<p>
The core of these diagrams was a specification I wrote for an Etch-a-Sketch
itself, in a file called <tt>eas.lino</tt>. The specification is
complicated, because an Etch-a-Sketch has many parts, but it is conceptually
just like the definitions above: it defines a diagram component called
an <tt>EAS</tt> that looks like an Etch-a-Sketch: <p>
<p align=center><img border=0 src="https://pic.blog.plover.com/linogram/sline-2/eas.jpg"></p>
Here is the definition, in full:<p>
<pre>
define EAS {
param number w;
number knobrad = w * 1/16;
circle hknob(r=knobrad, fill=0.25), vknob(r=knobrad, fill=0.25);
point left = hknob.c, right = vknob.c;
number margin = 3/40 * w;
box body(sw = left + (-margin, -margin), se = right + (margin, -margin),
ht = w * 1);
box screen(wd = body.wd * 0.9,
n = body.n + (0, -margin),
ht = body.ht * 0.7);
number nudge = body.ht * 0.025;
label Brand(text="Etch A Sketch") = (body.n + screen.n)/2 + (0, -nudge);
constraints { left + (w, 0) = right;
left.y = right.y = 0;
left.x = 0;
}
}
</pre><p>
I didn't, of course, write this all in one fell swoop. I built it up
a bit at a time. Each time I changed the definition in the
<tt>eas.lino</tt> file, the changes were inherited by all the files
that contained <tt>require "eas"</tt>.<p>
The two main parts of the Etch-a-Sketch are the <tt>body</tt> (large outer
rectangle) and <tt>screen</tt> (smaller inner rectangle), which are
defined to be <tt>box</tt>es:<p>
<pre>
box body(...);
box screen(...);
</pre><p>
But most of the positions are ultimately referred to the centers of
the two knobs. The knobs themselves are <tt>hknob</tt> and
<tt>vknob</tt>, and their centers, <tt>hknob.c</tt> and
<tt>vknob.c</tt>, are given the convenience names <tt>left</tt> and
<tt>right</tt>:<p>
<pre>
point left = hknob.c, right = vknob.c;
</pre><p>
Down in the <tt>constraints</tt> section is a crucial
constraint:<p>
<pre>
left + (w, 0) = right;
</pre><p>
This constrains the <tt>right</tt> point (and, by extension,
<tt>vknob.c</tt> and the circle <tt>vknob</tt> of which it is the
center, and, by further extension, anything else that depends on
<tt>vknob</tt>) to lie exactly <i>w</i> units east and 0 units south
of the <tt>left</tt> point. The number <i>w</i> ("width") is declared as a
"param", and is special: it <i>must</i> be specified by the calling context,
to tell the Etch-a-Sketch component how wide it is to be; if it is omitted,
compilation of the diagram fails.
By varying <i>w</i>,
we can have <tt>linogram</tt> draw Etch-a-Sketch components in different sizes. The diagram
above had <i>w</i>=4; this one has <i>w</i>=2:<p>
<p align=center><img border=0 src="https://pic.blog.plover.com/linogram/sline-2/eas-demo.jpg"></p>
All of the other distances are specified in terms of <i>w</i>, or
other quantities that depend on it, to
ensure proper scaling behavior. For example, the radius of the two
knobs is <i>knobrad</i>, which is constrained to be <i>w</i>/16:
<pre>
number knobrad = w * 1/16;
</pre><p>
So if you make <i>w</i> twice as big, the knobs get twice as big also.<p>
The quantity <i>margin</i> is the amount of space between knobs and
the edge of the body box, defined to be 3/40 of <i>w</i>:
<pre>
number margin = 3/40 * w;
</pre><p>
Since the margin is 0.075 <i>w</i>, and the
knobs have size 0.0625 <i>w</i>, there is a bit of extra space between
the knobs and the edge of the body. Had I wanted to state this
explicitly, I could have defined <i>margin</i> = <i>knobrad</i> * 1.15
or something of the sort. <p>
The southwest and southeast corners of the body box are defined as
offsets from the <i>left</i> and <i>right</i> reference points, which
are at the centers of the knobs:<p>
<pre>
box body(sw = left + (-margin, -margin),
se = right + (margin, -margin),
ht = w * 1);
</pre>
(The <tt>body(sw = ..., se = ..., ht = ...)</tt> notation is
equivalent to just including <tt>body.sw = ...; body.se =
...; body.ht = ...</tt> in the <tt>constraints</tt> section.)<p>
This implicitly specifies the width of the body box, since <tt>linogram</tt> can
deduce it from the positions of the two bottom corners. The height of
the body box is defined as being equal to <i>w</i>, making the height
of the body equal to the distance between the to knobs. This is not
realistic, since a real Etch-a-Sketch is not so nearly a square,
but I liked the way it looked. Earlier drafts of the diagram had
<i>ht</i> = <i>w</i> * 2/3, to make the Etch-a-Sketch more rectangular.
Changing this one number causes <tt>linogram</tt> to adjust everything else in the
entire diagram correspondingly; everything else moves around to
match.<p>
The smaller box, the <tt>screen</tt>, is defined in terms of the
larger box, the <tt>body</tt>:
<pre>
box screen(wd = body.wd * 0.9,
n = body.n + (0, -margin),
ht = body.ht * 0.7);
</pre>
It is 9/10 as wide as the body and 7/10 as tall. Its "north" point
(the middle of the top side) is due south of the north point of the
body, by a distance equal to <i>margin</i>, the distance between the
center of a knob and the bottom edge of the body. This is enough
information for <tt>linogram</tt> to figure out everything it needs to know about the
<tt>screen</tt>.<p>
The only other features are the <tt>label</tt> and the <tt>fill</tt>
property of the knobs. A <tt>label</tt> is defined by <tt>linogram</tt>'s standard
library. It is like a <tt>point</tt>, but with an associated
string:<p>
<pre>
require "point";
define label extends point {
param string text;
draw { &put_string; }
}
</pre>
Unlike an ordinary <tt>point</tt>, which is not drawn at all, a
<tt>label</tt> is drawn by placing the specified string at the
<i>x</i> and <i>y</i> coordinates of the point.
All the magic here is in the <tt>put_string()</tt> function, which is
responsible for generating the required PostScript output.
<!--
By supplying a different <tt>put_string()</tt> function, one can have
<tt>linogram</tt> generate
WTF was I going to say here?
-->
<p>
<pre>
number nudge = body.ht * 0.025;
label Brand(text="Etch A Sketch") =
(body.n + screen.n)/2 + (0, -nudge);
</pre><p>
The <tt>text="..."</tt> supplies the <tt>text</tt> parameter, which is
handed off directly to <tt>put_string()</tt>. The rest of the
constraint says that the text should be positioned halfway between the
north points of the body and the screen boxes, but nudged southwards a
bit. The <i>nudge</i> value is a fudge factor that I put in because I
haven't yet gotten the PostScript drawing component to position the
text at the exact right location. Indeed, I'm not entirely sure about
the best way to specify text positioning, so I left that part of the
program to do later, when I have more experience with text.<p>
The <tt>fill</tt> parameter of the knobs is handled similarly to the
<tt>text</tt> parameter of a label: it's
an opaque
piece of information that is passed to the drawing component for
handling:<p>
<pre>
circle hknob(r=knobrad, fill=0.25), vknob(r=knobrad, fill=0.25);
</pre><p>
The PostScript drawing component then uses this when drawing the
circles, eventually generating something like <tt>gsave 0.75 setgray
fill grestore</tt>. The default value is 0, indicating white fill;
here we use 0.25, which is light gray. (The PostScript drawing
component turns this into 0.75, which is PostScript for light gray.
PostScript has white=1 and black=0, but <tt>linogram</tt> has white=0 and black=1. I
may reverse this before the final release, or make it a per-diagram
configuration option.)<p>
<div class="bookbox"><table align=right width="20%" bgcolor="#ffffdd"
border=1><tr><td align=center><A HREF="https://bookshop.org/a/93187/9781558607019"><font
size="-2">Buy</font><br><cite><font>Higher-Order Perl</font></cite><br><IMG SRC="https://images-us.bookshop.org/ingram/9781558607019.jpg?height=250&v=v2" BORDER="0"
ALIGN="center" ALT="(Higher-Order Perl cover missing)" ><br>
<font size="-2">from Bookshop.org<br>(with kickback)</a><br><a href="https://bookshop.org/a/00000/9781558607019">(without kickback)</a></font></a>
</td></tr></table></div>
Tomorrow: Advantages of declarative drawing.<p>
More complete information about <tt>linogram</tt> is available in <a
href="https://hop.perl.plover.com/chap09.html">Chapter 9</a> of <a
href="https://hop.perl.plover.com/">Higher-Order Perl</a>; complete
source code is available from <a
href="https://hop.perl.plover.com/linogram/">the <tt>linogram</tt> web site</a>.<p>
-
A series of articles about Linogram
https://blog.plover.com/2006/11/19#sline
Every six months or so I do a little work on <tt>linogram</tt>, which for about
18 months now has been about one week of hard work short of version
1.0. Every time I try to use it, I am surprised by how pleased I am
with it, how easy I find it to use, and how flexible the (rather
spare) basic feature set is—all properties of a well-designed
system.<p>
I was planning to write a short note about this, but, as always
happens when I write about <tt>linogram</tt>, it turned into a very long note that
included a tutorial about how <tt>linogram</tt> works, a rant about why it is a good
idea, and a lot of technical examples. So I decided to break the
4,000-word article into smaller pieces, which I will serialize.<p>
Most recently I used <tt>linogram</tt> to do schematic diagrams of an Etch-a-Sketch
for some recent blog articles (<a
href="https://blog.plover.com/games/etchasketch.html">[1]</a>
<a
href="https://blog.plover.com/games/etchasketch-correction.html">[2]</a>). In case you have forgotten, here is an
example:<p>
<p align=center><img border=0 src="https://pic.blog.plover.com/linogram/sline/eas-demo.jpg"></p>
Drawing this went pretty much the way I hoped it would when I first
started designing <tt>linogram</tt>, many years ago. The program is incomplete,
in the core that provides the basic features, in the libraries, which
provide canned definitions of common elements of figures, and in the
drawing components that actually render the output. Nevertheless, it
is powerful and flexible enough that, even incomplete, I can
reasonably expect to put together simple diagrams like the one
above.<p>
<h2>Basic ideas of <tt>linogram</tt></h2>
There are two basic concepts that provide the underpinnings of <tt>linogram</tt>.
One is that most interesting features of a line drawing can be
described by giving a series of linear equations that relate the
positions of the components. For example, to say that line <i>B</i>
starts at the same place that line <i>A</i> ends, one provides the two
extremely simple linear equations:<p>
<pre>
B.start.x = A.end.x;
B.start.y = A.end.y;
</pre><p>
which one can (and normally would) abbreviate to:<p>
<pre>
B.start = A.end;
</pre><p>
The computer is very good at solving systems of linear equations, and
can figure out all the things the equations imply. For example,
consider two boxes, <i>X</i> and <i>Y</i>:<p>
<pre>
X.ht = 2; # height of box X
Y.ht = 3; # height of box Y
X.s = Y.n; # 's' is 'south'; 'n' is 'north'
</pre>
From this, the program can deduce that the south edge of box <i>Y</i>
is 5 units south of the north edge of box <i>X</i>, even though
neither was mentioned explicitly.<p>
These simple examples are not very impressive, but please bear with
me.<p>
The other fundamental idea in <tt>linogram</tt> is the idea of elements of a diagram
as components that can be composed into more complicated elements.
The <tt>linogram</tt> libraries have a definition for a box, but it is not a
primitive. In fact, <tt>linogram</tt> has one, and only one primitive type of
element: the number. A point is composed of two numbers, called
<i>x</i> and <i>y</i>, and is defined by the <tt>point.lino</tt> file
in the library:<p>
<pre>
define point {
number x, y;
}
</pre>
A line can then be defined as an object that contains two points, the
start and end points:<p>
<pre>
require "point";
define line {
point start, end;
}
</pre>
Actually the real definition of <tt>line</tt> is somewhat more
complicated, because the center point is defined as well, for the
user's convenience:<p>
<pre>
require "point";
define line {
point start, end, center;
constraints {
center = (start + end) / 2;
}
}
</pre>
The equation
<i>center</i> = (<i>start</i> + <i>end</i>) / 2 is actually shorthand
for two equations, one involving <i>x</i> and the other involving
<i>y</i>:<p>
<table align=center>
<tr><td><i>center.x</i> = (<i>start.x</i> + <i>end.x</i>) / 2
<tr><td><i>center.y</i> = (<i>start.y</i> + <i>end.y</i>) / 2
</table><p>
From the specification above, the program can deduce the location of
the center point given the two endpoints. But it can also deduce the
location of either
endpoint given the locations of the center and the other endpoint.
The treatment of equations is completely symmetrical.
In fact, the line is really, at the bottom, an agglomeration of six
numbers (<i>start.x</i>, <i>center.y</i>, and so forth) and from any
specification of two <i>x</i> coordinates and two <i>y</i>
coordinates, the program can deduce the missing values.
<p>
The <tt>linogram</tt> standard library defines <tt>hline</tt>
and <tt>vline</tt> as being like ordinary lines, but constrained to be
horizontal and vertical, and the then <tt>box.lino</tt> defines a box
as being two
<tt>hline</tt>s
and two <tt>vline</tt>s, constrained so that they line up in a box
shape:<p>
<pre>
require "hline";
require "vline";
define box {
vline left, right;
hline top, bottom;
constraints {
left.start = top.start;
right.start = top.end;
left.end = bottom.start;
right.end = bottom.end;
}
}
</pre><p>
I have abridged this definition for easy reading; the actual
definition in the file has more to it. Here it is, complete:<p>
<pre>
require "hline";
require "vline";
require "point";
define box {
vline left, right;
hline top, bottom;
point nw, n, ne, e, se, s, sw, w, c;
number ht, wd;
constraints {
nw = left.start = top.start;
ne = right.start = top.end;
sw = left.end = bottom.start;
se = right.end = bottom.end;
n = (nw + ne)/2;
s = (sw + se)/2;
w = (nw + sw)/2;
e = (ne + se)/2;
c = (n + s)/2;
ht = left.length;
wd = top.length;
}
}
</pre><p>
The additional components, like <tt>sw</tt>, make it easy to refer to
the corners and edges of the box; you can refer to the southwest
corner of a box <tt>B</tt> as <tt>B.sw</tt>. Even without this
convenience, it would not have been too hard: <tt>B.bottom.start</tt>
and <tt>B.left.end</tt> are names for the same place, as you can see
in the <tt>constraints</tt> section.<p>
<div class="bookbox"><table align=right width="20%" bgcolor="#ffffdd"
border=1><tr><td align=center><A HREF="https://bookshop.org/a/93187/9781558607019"><font
size="-2">Buy</font><br><cite><font>Higher-Order Perl</font></cite><br><IMG SRC="https://images-us.bookshop.org/ingram/9781558607019.jpg?height=250&v=v2" BORDER="0"
ALIGN="center" ALT="(Higher-Order Perl cover missing)" ><br>
<font size="-2">from Bookshop.org<br>(with kickback)</a><br><a href="https://bookshop.org/a/00000/9781558607019">(without kickback)</a></font></a>
</td></tr></table></div>
Tomorrow: The Etch-a-Sketch component.<p>
More complete information about <tt>linogram</tt> is available in <a
href="https://hop.perl.plover.com/chap09.html">Chapter 9</a> of <a
href="https://hop.perl.plover.com/">Higher-Order Perl</a>; complete
source code is available from <a
href="https://hop.perl.plover.com/linogram/">the <tt>linogram</tt> web site</a>.<p>
-
How long is the banana?
https://blog.plover.com/2006/01/08#banana
<P>
<blockquote><i>
Mark Dominus:<BR>
<blockquote><i>
One of the types of problems that al-Khwarizmi treats extensively in
his book is the problem of computing inheritances under Islamic law.
</i></blockquote>
<P>
Well, these examples are getting sillier and sillier, but
</i></blockquote>
<P>
... but maybe they're not silly enough:
<blockquote>
A rope over the top of a fence has the same length on each
side and weighs one-third of a pound per foot. On one end of
the rope hangs a monkey holding a banana, and on the other end
a weight equal to the weight of the monkey. The banana weighs
2 ounces per inch. The length of the rope in feet is the same
as the age of the monkey, and the weight of the monkey in
ounces is as much as the age of the monkey's mother. The
combined ages of the monkey and its mother is 30
years. One-half the weight of the monkey plus the weight of
the banana is one-fourth the sum of the weights of the rope
and the weight. The monkey's mother is one-half as old as the
monkey will be when it is three times as old as its mother was
when she was one-half as old as the monkey will be when it is
as old as its mother will be when she is four times as old as
the monkey was when it was twice as old as its mother was when
she was one-third as old as the monkey was when it was as old
as its mother was when she was three times as old as the
monkey was when it was one-fourth as old as its is now. How
long is the banana?
</blockquote>
<P>
(Spoiler solution using, guess what, <a href="https://hop.perl.plover.com/linogram/"><tt>Linogram</tt></a>....)
<HR>
<PRE>
define object {
param number density;
number length, weight;
constraints { density * length = weight; }
draw { &dump_all; }
}
object banana(density = 24/16), rope(density = 1/3);
number monkey_age, mother_age;
number monkey_weight, weight_weight;
constraints {
monkey_weight * 16 = mother_age;
monkey_age + mother_age = 30;
rope.length = monkey_age;
monkey_weight / 2 + banana.weight =
1/4 * (rope.weight + weight_weight);
mother_age = 1/2 * 3 * 1/2 * 4 * 2 * 1/3 * 3 * 1/4 * monkey_age;
monkey_weight = weight_weight;
}
draw { banana; }
__END__
use Data::Dumper;
sub dump_all {
my $h = shift;
print Dumper($h);
# for my $var (sort keys %$h) {
# print "$var = $h->{$var}\n";
# }
}
</PRE>
<P>
OK, seriously, how often do you get to write a program that includes
the directive <tt>draw { banana; }</tt>?
<P>
The output is:
<PRE>
$VAR1 = bless( {
'length' => '0.479166666666667',
'weight' => '0.71875',
'density' => '1.5'
}, 'Environment' );
</PRE>
<P>
So the length of the banana is 0.479166666666667 feet, which is 5.75 inches.