Tutorial #12: Aligner

This tutorial describes how to automatically align objects utilizing the `Graph::Aligner` node.

# ContainerAlignment

Independently from the `Aligner` graph node, several graph nodes (e.g. `PlaneGeometry` or `TextGeometry`) allow the adjustment of their anchor point via the `containerAlignmentX` and `containerAlignmentY` attributes. By default the anchor point is in the center.

Valid values are listed in the enums `IEnums::AlignmentX` and `IEnums::AlignmentY`:

• `UNDEFINED`
• `LEFT` (or `BOTTOM` for Y)
• `CENTER` (default)
• `RIGHT` (or `TOP` for Y)
• `NEGATIVE`
• `POSITIVE`

As an example we place three planes on the same position 0/0/0 and change merely the parameter `containerAlignmentX` and `containerAlignmentY` (as well as the color parameter).

```<!-- blue plane -->
<PlaneGeometry id="box_plane_01"
posX="0" posY="0" scaleFactor="100"
<!-- red plane -->
<PlaneGeometry id="box_plane_02"
posX="0" posY="0" scaleFactor="100"
containerAlignmentX="LEFT" containerAlignmentY="TOP"/>
<!-- green plane -->
<PlaneGeometry id="box_plane_03"
posX="0" posY="0" scaleFactor="100"
containerAlignmentX="RIGHT" containerAlignmentY="BOTTOM"/>
```

As result we get three planes which are differently positioned according to their alignment specification. The anchor world position is the same for all three planes (0/0/0) but the anchor point on the planes differ. For the blue plane the anchor point is in the center, for the red plane it is top left and for the green plane it is bottom right.

containerAlignment

# Aligner Graph Node

The `Aligner` graph node allows to automatically arrange objects along one dedicated main axis. The `Aligner` node calculates the bounding box of its child nodes (or sub graphs) and arranges them consecutively. As a first example let us arrange three `PlaneGeometry` objects along the Y axis.

```<Aligner id="aligner01"
axis="Y" posX="-200" >
<!-- blue plane -->
<!-- red plane -->
containerAlignmentX="LEFT" containerAlignmentY="TOP"/>
<!-- green plane -->
containerAlignmentX="RIGHT" containerAlignmentY="BOTTOM"/>
</Aligner>
```

We move the center of the `Aligner` node to the left at position -200/0/0. The `PlaneGeometry` objects will be arranged along the Y axis automatically. The other coordinate values (X/Z) remain unchanged.

Aligner

The attribute `objectAlignment` can be used to also adjust the other coordinate values (in our case X and Z). For example, specifying the parameter `objectAlignmentX="LEFT"` causes an object alignment along the left margin.

To illustrate that feature, we create a new `Aligner` node and specify the `objectAlignmentX` attribute. By specifying `posY="150"` and `containerAlignmentY="TOP"` the upper boundary of the `Aligner` node is placed to position 150. The parameter `order="DESCENDING"` causes a descending order.

```<Aligner id="aligner02"
axis="Y"  posX="-350" posY="150" containerAlignmentY="TOP"
objectAlignmentX="LEFT"
order="DESCENDING">
<!-- blue plane -->
<!-- red plane -->
containerAlignmentX="LEFT" containerAlignmentY="TOP"/>
<!-- green plane -->
containerAlignmentX="RIGHT" containerAlignmentY="BOTTOM"/>
</Aligner>
```
Aligner objectAlignment

The attribute `spacing` can be used to define a distance between the aligned object and the attribute `padding` defines an outer border for the `Aligner` node.

```<Aligner id="aligner03"
axis="Y" posX="250" posY="250" containerAlignmentY="TOP"
containerSizeX="200" order="DESCENDING" spacing="20">
<TextGeometry
materialSlot="4" systemFontName="SansBold"
fontSize="20"
textColor="255i, 255i, 255i, 255i"
backgroundColor="0i, 0i, 0i, 0i"
text="Line 1 centered"
enableWordWrapping="yes"
containerSizeX="200"
textAlignmentX="CENTER"
/>
<TextGeometry
materialSlot="4" systemFontName="SansBold"
fontSize="20"
textColor="255i, 255i, 255i, 255i"
backgroundColor="0i, 0i, 0i, 0i"
text="A auto wrapped text line"
enableWordWrapping="yes"
containerSizeX="200"
textAlignmentX="CENTER"
/>
<TextGeometry
materialSlot="4" systemFontName="SansBold"
fontSize="20"
textColor="255i, 255i, 255i, 255i"
backgroundColor="0i, 0i, 0i, 0i"
text="A manually&#10;wrapped line"
enableWordWrapping="yes"
containerSizeX="200"
textAlignmentX="CENTER"
/>
<TextGeometry
materialSlot="4" systemFontName="SansBold"
fontSize="20"
textColor="255i, 255i, 255i, 255i"
backgroundColor="0i, 0i, 0i, 0i"
text="Left aligned text"
enableWordWrapping="yes"
containerSizeX="200"
textAlignmentX="LEFT"
/>
<TextGeometry
materialSlot="4" systemFontName="SansBold"
fontSize="20"
textColor="255i, 255i, 255i, 255i"
backgroundColor="0i, 0i, 0i, 0i"
text="Right aligned text"
enableWordWrapping="yes"
containerSizeX="200"
textAlignmentX="RIGHT"
/>
<Node>
containerAlignmentX="LEFT" />
<PlaneGeometry posX="25" posY="-25" scaleFactor="100" parametersSlot="2" materialSlot="2"
containerAlignmentX="RIGHT" containerAlignmentY="BOTTOM"/>
</Node>
</Aligner>
```
Aligner spacing

The final size of the `Aligner` can be queried with the method `GetBoundingVolume()`. The request should be done in the `OnFinishTick()` method and not in the `OnProcessTick()` method because the automatic alignment of the `Aligner` objects is performed after the method `OnProcessTick()` has been executed. Hence a request in the `OnProcessTick()` method would return the values of the previous frame.

```void App::AlignerLogic::OnFinishTick(const Logic::IState* state)
{
// Size
if (mPrintBoundingValues)
{
const Graph::IBoundingVolume* boundingVolume = mAligner->GetBoundingVolume();
const Graph::Box& box = boundingVolume->GetInnerLocalBox();
const Graph::Vector& min = box.GetMinimum();
const Graph::Vector& max = box.GetMaximum();
Debug::Trace("From OnFinishTick:");
Debug::Trace("  Bounding X %f / %f", min.x, max.x);
Debug::Trace("  Bounding Y %f / %f", min.y, max.y);
Debug::Trace("  Bounding Z %f / %f", min.z, max.z);
mPrintBoundingValues = false;
}
}
```

The result for the `Aligner` `aligner03` and the first frame is:

From OnProcessTick:
Bounding X 0.000000 / 0.000000
Bounding Y 0.000000 / 0.000000
Bounding Z 0.000000 / 0.000000
From OnFinishTick:
Bounding X -100.000000 / 100.000000
Bounding Y -450.000000 / 0.000000
Bounding Z 0.000000 / 0.000000

For all further frames the call in the method `OnProcessTick()` and the call in the method `OnFinishTick()` return the same values. This is because the child nodes of the `Aligner` do not change and therefore the `IBoundingVolume` stays the same. The `IBoundingVolume` for another frame can be printed out by pressing the key P:

From OnProcessTick:
Bounding X -100.000000 / 100.000000
Bounding Y -450.000000 / 0.000000
Bounding Z 0.000000 / 0.000000
From OnFinishTick:
Bounding X -100.000000 / 100.000000
Bounding Y -450.000000 / 0.000000
Bounding Z 0.000000 / 0.000000

We can optically verify the calculated `IBoundingVolume` with a `PlaneGeometry` node:

```<PlaneGeometry posX="250" posY="250" containerAlignmentY="TOP"
scaleFactorX="200" scaleFactorY="450"