Now you have learned how to initialize properties of a new node, and how to read out properties of existing nodes. The latter is done using a syntax with square brackets. This syntax can also be used to set new values for properties. But at first, consider the following example:
Sphere(r) ==> Sphere(r/2);
Obviously, the aim is to divide the radius of each sphere by two.
The rule implements this behaviour by replacing each sphere of
radius r
by a new sphere of radius
r/2
, using the constructor invocation
new Sphere(r/2)
. Although the rule does its
job correctly, there is a better solution: What if we keep the
existing original sphere in the graph and just divide its radius by two?
This is more efficient, because the existing sphere can be re-used
and less changes to the graph have to be done. And there is another
advantage: If the original sphere has some other properties which
differ from their default values (e.g., a shader has been set), these
properties get lost by application of the replacement rule because
the rule just uses the radius property of the original sphere. However,
if we just modify the radius of the existing sphere, all other
properties remain unchanged, so nothing is lost. Here is how the better
solution looks like:
x:Sphere ::> x[radius] /= 2;
The most apparent difference to the previous rule is the new arrow symbol
::>
: It indicates a new type of rule, namely
an execution rule. Execution rules treat their
right hand sides as statements which are to be executed for every match
of their left hand side. Think of this as a for
loop
which iterates over all matches and has the right hand side as body:
for (all matches of x:Sphere in graph)
x[radius] /= 2;
Now the statement x[radius] /= 2;
divides
x
's radius property by two as desired.
The execution rule is contained in the method grow
of
this example.
Click here to see a sphere shrinking and a cylinder growing.
You may wonder if it is also possible to write
x.setRadius(x[radius]/2);
instead of
x[radius] /= 2;
. In principle, this works. But
in this case, GroIMP is not informed about the modification of the
radius and, thus, does not redraw the 3D visualization. As a rule of thumb,
the modification of properties of nodes which are already part of
GroIMP's graph should be done using
property assignments as in
x[radius] /= 2;
. For newly created nodes which are
not yet part of GroIMP's graph, it is OK to use setter methods for
the purpose of initialization as in
Sphere(1).(setShader(RED))
.