Sometimes it's necessary just to work with numbers rather than with geometric figures. For example, if you want to make a circle that has 1.72 the radius of another, you may be able to figure out a way to do it geometrically, but it's a heck of a lot easier if you can just use numbers. For example, here's a chunk of Geometer code that will make a circle centered at v1 and passing through v2 and will, in addition, draw a circle centered at v3 having a radius of π times the radius of the first circle:
c1 = .c.vv(v1, v2); rad = .f.vv(v1, v2); newrad = .f.rpn(rad, 3.14159265, .mul); c2 = .c.vf(v3, newrad);
The first line is standard, but the second line says to make a number called "rad" whose value is the distance between the points v1 and v2. The third line says to take the value of rad, as a number, and to multiply it by 3.14159265, and to store the value in newrad. Finally, the last line says to produce a circle of radius newrad centered at the point v3. The "f" in the commands above is short for "flt" which you can think of as a floating point number.
There is a whole series of commands that obtain, manipulate, and use floating point numbers. The only access to them is via the editor interface—i.e. there is no way to use them via the graphical user interface.
These numbers are usually used as coordinates, or as sizes in transformations, so before you can use them, you have to have some idea of Geometer's coordinate system. Earlier, we said that it goes roughly from -1.0 to 1.0 in both dimensions. This is exactly true for a square display area, but if the display area is a little longer than wide or wider than tall, the images would appear distorted with these coordinates. So what Geometer does is to determine the minimum of the width and the height, and makes that minimum length run from -1 to 1. Thus if the display window size were 500 pixels wide and 400 high, the x coordinates would run from -1.25 to 1.25, and the y coordinates from -1.0 to 1.0.
The numbers can also be used to describe angles, like 90°. Most people measure angles in degrees, mathematicians and computer programmers almost always measure them in radians, where 2π radians is equal to 360°. Geometer goes both ways, but if you do nothing, it assumes you're working with degrees. You can add a line to your file like this:
.radianmode;and if that line appears, all angles in your file will be expressed in radians. The default setting is .degreemode;, but since it's the default, it need never appear in your file. In some of the examples that follow, I will include ".degreemode" explicitly just to remind you that angles are measured that way. There's no need to include it in a normal diagram (although it is legal to include it, and Geometer will cheerfully eliminate it from any output file it creates).
Here is a list of the all the commands involving flts that do not involve transformations. Transformations are covered in the next section. In the command names, the appearance of an "f" is used to indicate that a number (a "f"loating point number) is used or produced. In the parameter list, a flt is represented by [F], [P] is a polygon, and [RPN] stands for "rpn-expression", which we'll talk about later:
[P] = .v.ff([F], [F]); [C] = .c.vf([P], [F]); [F] = .f.vvvratio([P], [P], [P]); [P] = .v.vvf([P], [P], [F]); [F] = .f.vv([P], [P]); [F] = .f.vxcoord([P]); [F] = .f.vycoord([P]); [A] = .a.f([F]); [F] = .f.area([P]); [F] = .f.rpn([RPN]);
Most of these are fairly straight-forward. .v.ff makes a point using the two numbers as its x and y coordinates. .c.vf produces a circle centered at the point, and having a radius given by the number.
.f.vvvratio makes a number that is the ratio of the distances between the three points. Typically, the points are on a line, and if they are called A, B, and C, then the number produced is the ratio AB/AC. The points don't have to be on a line, however.
Use .v.vvf to do the opposite -- to produce a point with the given ratio of distances relative to two other points. This command will produce the new point on the line connecting the other two.
.f.vv produces a number that's the distance between the two points, and .f.vxcoord and .f.vycoord put the x and y coordinates into a number.
.a.f produces an angle of the given magnitude. The magnitude depends on whether you're in degree mode or radian mode (see above), so
.degreemode; ang = .a.f(180);and
.radianmode; ang = .a.f(3.14159265);have exactly the same net effect.
Finally, .f.area calculates the area of the polygon and puts it in the number. If the polygon's edges cross each other, you'll get consistent results, but perhaps difficult to interpret. Geometer simply treats the polygon as a series of triangles and adds those together with signed areas.
So far we've just made and used numbers, but have no way to calculate with them. And exactly what is it that you can put in place of an "f" in the examples above?
In every case, you can use a defined number, or a literal number. For example, if you wanted to have a point whose x coordinate was locked at 0.1, but whose y coordinate was the same as the distance between two other points, the following code would work:
v1 = .free(0.372188, 0.255624, "A"); v2 = .free(0.609407, 0.247444, "B"); ycoord = .f.vv(v1, v2); v3 = .v.ff(0.100000, ycoord);
Geometer can also do more or less arbitrary calculations with these numbers, and all of these are achieved by means of the .f.rpn command. The "rpn" refers to "reverse Polish notation", and within that command, Geometer supports a tiny PostScript-like or Fourth-like language. The instructions consist of a series of command tokens, number variables, angles, and number literals. They are evaluated from left to right, as follows:
A number literal, a number variable, or an angle will simply have its value pushed on the execution stack. If it's an angle, the actual number pushed on the stack will depend on whether Geometer is in degree mode or in radian mode.
Every other possible entry is a Geometer reserved word, and each has some effect on the contents of the stack. After all the entries are evaluated, the result is whatever is left on the top of the stack.
Here's a list of all the Geometer stack operators, and what each one does to the contents of the stack:
The above doesn't seem like much if you've never worked with reverse Polish notation, but you can do just about any calculation you like using it. For example, here's a little Geometer program that will draw a graph of the function sin x + cos 2x:
.geometry "version 0.2"; v1 = .free(-1.00204, -0.398773, "A"); v2 = .free(0.130879, -0.366053, "B"); xval = .f.vv(v1, v2); yval = .f.rpn(xval, .sin, xval, 2.000000, .mul, .cos, .add); v3 = .v.ff(xval, yval, .smear);
It isn't very interesting, because the values mostly lie outside the drawing window, and need to be better scaled to make a reasonable diagram, but it does behave as advertised. The x coordinate is simply taken to be the distance between the points v1 and v2. The y coordinate is calculated using the rpn expression. Follow along to see what happens. First the xval is placed on the stack and its sine is taken. Another copy of it is put on the top of the stack (above sin x), then a 2 is added to the stack, the top two elements are multiplied, yielding 2x on top, whose cosine is taken, and that result is added to sin x below, yielding sin x + cos 2x. Those values are then used as the coordinates of v3, which has a .smear color, so as you drag around v1 or v2, the point v3 will leave a smeared track along the curve y = sin x + cos 2x.
After each .f.rpn expression is evaluated, the stack is cleared, so you can't pass information on to other commands, although this may occur in the future, so it's a good idea to leave only the final result you're interested in on the stack when you're done, for compatibility with future versions of Geometer.