Table of Contents
XL queries and operators
Queries
Graph queries are used to analyse the actual structure (graph), which can be for example a plant structure.
In XL, queries are enclosed in asterixed parentheses (* *)
.
The elements in a query are given in their expected order, e.g. (* A A B *)
searches for a subgraph which consists of a sequence of nodes of the types A
A
B
, connected by successor edges.
To try out how queries work, we first create a simple plant model.
Test model
module Bud extends Sphere(0.1).(setShader(GREEN)); module Internode(int age, super.length) extends Cylinder(length, 0.1).(setShader(YELLOW)); module Leaf(int age, super.length, double width) extends Parallelogram(length, width).(setShader(GREEN)) { double area; { setArea(); } void setArea() { area = length*width; } } protected void init() [ Axiom ==> Bud; ] public void run() [ Bud ==> Internode(1, 1) [RL(60) Leaf(1, 1, 1)] RH(180) Bud ; i:Internode ::> { i[age]++; i[length] += 1; } l:Leaf ::> { l[age]++; l[length] += 1; l.setArea(); } ]
We create a new method called analyse()
. The method will appear as a new button in the RGG toolbar. Before running the analyse()
method, press the Reset
button in the RGG Toolbar, to reset the graph structure. Inside this method, we first call the run()
method for 3 steps. The result is a simple plant made of 3 internodes and 3 leaves.
public void analyse() { for (apply(3)) { run(); } println("add here a query example"); }
The underlying 2d graph of the generated structure given below:
Beginning from RGGRoot, we have the sequence of three Internodes where to each is a Leaf linked by a branching edge. The last internode, additionally, has a Bud node successor.
Query examples
Replace the text (in red) inside the println()
one by one by the queries below.
Below each query, there is a result of the print command, for illustration. Note, that the ids will differ with different model execution.
- Find all the internodes in the structure:
(* Internode *)
The query will return ids of all the internodes in the plant structure.
Notice, that when you click with a mouse on an internode in the 3D View window, information about the selected internode will be displayed in the Attribute Editor. In the Attribute Editor, you can check the internode id (e.g. Model$Internode [Internode.1756]) and compare it with the one that was printed in the XL Console. Another option is to look at the node ids in the 2D Graph (Panels → 2D → Graph, in the graph window: View → Redraw).
- Find all newly created internode(s), with age == 1:
(* i:Internode, (i[age] == 1) *)
- Find the internode below the bud:
(* Bud < RH < Internode *)
<
represents a successor edge in the indicated direction
- Find all leaves connected to an internode:
(* Internode +> RL > Leaf *)
This is just to illustrate how to define different edge types inside a query. +>
represents a branch edge.
Aggregators
Aggregate operations are used to collect multiple values when traversing the graph structure and return a single value.
Standard aggregate operations are: count
, sum
, empty
, exist
, forall
, first
, last
, max
, min
, mean
, selectRandomly
, selectWhereMin
, selectWhereMax
, …
- Count all leaves:
count((* Leaf *))
- Sum up the area of all leaves:
sum((* Leaf *)[area])
- Sum up the length of internodes:
sum((* Internode *)[length])
In this particular example with internodes forming (only) the main stem, the query will return stem length.
- Select the leaf with the highest leaf area:
selectWhereMax((* l:Leaf *), (l[area]))
- Count internodes that are older than 2 (time units):
count((* i:Internode, (i[age] >= 2) *))