Macros

Imagine that you want to make a whole bunch of triangles connecting various sets of three points. Without macros, the only way to proceed would be to put down a long series of three points followed by the three lines that connect them. Each triangle would thus require 3 lines of code. With macros, you can reduce this to 4 lines per triangle, and with more complicated drawings, the savings can be far more.

Here's how to do the triangle example using a triangle macro:

.geometry "version 0.2";
.macro triangle(.vertex v1, .vertex v2, .vertex v3)
{
l1 = .l.vv(v1, v2);
l2 = .l.vv(v2, v3);
l3 = .l.vv(v3, v1);
}
v1 = .free(-0.764826, 0.554192, "1");
v2 = .free(-0.515337, 0.766871, "2");
v3 = .free(-0.384458, 0.300613, "3");
v4 = .free(0.265849, 0.476483, "4");
v5 = .free(0.703476, 0.595092, "5");
v6 = .free(0.658487, -0.0429448, "6");
triangle(v1, v2, v3);
triangle(v4, v5, v6);

The macro triangle takes three parameters, all of which are points, and from them it constructs three lines. Each time it is called, it generates a new set of three lines from the given points.

At present, the macro facility is rather simple-minded -- parameters can be any of the usual types: .vertex, .line, .circle, .flt, .conic, et cetera.

If a name within the macro is either a parameter name or is a name of an item produced within the macro, that local version is used. If it is not a parameter name or locally generated item, Geometer assumes that it is a global value.

For example, the following macro will draw a line from the point that's a parameter "v" to the fixed point v0:

v0 = .pinned(0, 0);
.macro linetozero(.vertex v)
{
    l1 = .l.vv(v0, v);
}

Macros can return a single value. For example, here is the syntax for a macro that takes two points and returns a circle having the two points as a diameter:

.macro .circle diamcircle(.vertex v1, .vertex v2)
{
    vmid = .v.vvmid(v1, v2, .in);
    .return c = .c.vv(vmid, v1);
}
v1 = .free(-0.2975, -0.12);
v2 = .free(0.1975, -0.1825);
v3 = .free(0.045, 0.3225);
c1 = diamcircle(v1, v2, .red);
c2 = diamcircle(v1, v3, .green);

Note that the point "vmid" is colored invisible so that it doesn't show up every time you make a new circle. Note also that the macro calls that produce c1 and c2 can have properties. In this case, the two different expansions of the macro will draw a red and a green circle. If you want, you can then use c1 or c2 in future constructions.

There is a known bug in the Geometer macro package that does not allow you to pass .flt parameters that are constant. For example, the following code will not work to generate a point with coordinates (.3, .3):

.macro .vertex v(.flt f)
{
    .return vv = .v.ff(f, f);
}
v1 = v(.3);

You can get around the bug with this minor modification to the code, since as long as the .flt parameter is not constant, the macro code works fine:

.macro .vertex v(.flt f)
{
    .return vv = .v.ff(f, f);
}
f3 = .f.rpn(.3);
v1 = v(f3);