Skip to content

Commit

Permalink
documentation: Add 3. tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
Makman2 committed Feb 2, 2016
1 parent 698b10f commit 67957a7
Show file tree
Hide file tree
Showing 2 changed files with 278 additions and 0 deletions.
118 changes: 118 additions & 0 deletions CE3D2/doc/GettingStarted-Transformations-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Getting Started - Part 3 - Example code

This is the full code file from the third tutorial dealing with transformations.

## Build

You can build it via `g++` with

```
g++ -std=c++11 -lCE3D2 example_transform.cpp -o example_transform
```

and execute it with `./example_transform`.

## example_transform.cpp

```cpp
#include <iostream>
#include <memory>
#include <vector>

#include <CE3D2/models/Model.h>
#include <CE3D2/transformation/Scale.h>
#include <CE3D2/transformation/OrthogonalProjection.h>
#include <CE3D2/render/TextRenderer.h>
#include <CE3D2/render/TextSurface.h>
#include <CE3D2/Vector.h>

using namespace CE3D2;

// Let's create a little helper function for instantiating vectors, since
// initializer lists are not supported from CE3D2::Vector. The issue is already
// addressed in https://github.com/Makman2/CE3D2/issues/52.
Vector create_3d_vector(float x, float y, float z)
{
Vector v(3);
v[0] = x;
v[1] = y;
v[2] = z;
return v;
}

// Creates a cube of given size and specific equidistant number of vectors on
// the edges.
std::vector<Vector> make_cube(float size = 1.0f, unsigned int edge_density = 0)
{
std::vector<Vector> vectors;

float step = size / (1 + edge_density);
for (float x = 0.0f; x <= size; x += step)
{
vectors.push_back(create_3d_vector(x, 0.0f, 0.0f));
vectors.push_back(create_3d_vector(x, size, 0.0f));
vectors.push_back(create_3d_vector(x, size, size));
vectors.push_back(create_3d_vector(x, 0.0f, size));
}
for (float y = step; y <= size - step; y += step)
{
vectors.push_back(create_3d_vector(0.0f, y, 0.0f));
vectors.push_back(create_3d_vector(size, y, 0.0f));
vectors.push_back(create_3d_vector(size, y, size));
vectors.push_back(create_3d_vector(0.0f, y, size));
}
for (float z = step; z <= size - step; z += step)
{
vectors.push_back(create_3d_vector(0.0f, 0.0f, z));
vectors.push_back(create_3d_vector(size, 0.0f, z));
vectors.push_back(create_3d_vector(size, size, z));
vectors.push_back(create_3d_vector(0.0f, size, z));
}

return vectors;
}

// TextSurface does not contain a function that prints out the contents to
// console. Already addressed in https://github.com/Makman2/CE3D2/issues/51.
void print_textsurface(Render::TextSurface const& surf)
{
for (unsigned int y = 0; y < surf.height(); y++)
{
for (unsigned int x = 0; x < surf.width(); x++)
{
std::cout << surf(x, y);
}
std::cout << std::endl;
}
}

int main()
{
auto cube_vectors = make_cube(10, 10);
auto cube = std::make_shared<Models::Model>();
cube->vectors().assign(cube_vectors.begin(), cube_vectors.end());

Render::TextRenderer renderer;
renderer.set_target(std::make_shared<Render::TextSurface>(40, 20));
renderer.models().push_back(cube);

Transformation::OrthogonalProjection ortho_projection;
std::vector<Vector> projection_vecs;
projection_vecs.push_back(create_3d_vector(0.7f, 0.3f, 0.0f));
projection_vecs.push_back(create_3d_vector(0.0f, 0.3f, 0.7f));
ortho_projection.set_projection_vectors(projection_vecs);

Vector scaling_vector(2);
scaling_vector[0] = 2.0f;
scaling_vector[1] = 1.0f;
Transformation::Scale scale(scaling_vector);

ortho_projection.transform(cube->vectors());
scale.transform(cube->vectors());

renderer.render();
print_textsurface(*renderer.get_target());

return 0;
}
```
160 changes: 160 additions & 0 deletions CE3D2/doc/GettingStarted-Transformations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Getting Started - Part 3

## Transformations

In the last part of the tutorial we applied an orthogonal projection
transformation onto a cube geometry. This chapter deals with the transformation
system of CE3D2.

### A quantum of theory

Transformations are something quite general, mathematically defined a
transformation maps a vector onto another vector in CE3D2.

In fact any function that operates on vectors and returns one is a suitable
transformation and can be implemented as such. More interesting is now which
ones are useful for us, since there are plenty of mathematical functions one
could think of.

CE3D2 implements common transformations like
`CE3D2::Transformation::OrthogonalProjection` (that we used before) or
`CE3D2::Transformation::Rotation`. Important to know is that transformations
apply their changes *in-place*. This allows to chain them easily with just
applying them sequentially:

```cpp
CE3D2::Models::Model geometry;

// ...

scale_transform.transform(geometry.vectors());
orthogonal_projection.transform(geometry.vectors());
```

An important derivative of the `CE3D2::Transformation::Transformation` class
is the `CE3D2::Transformation::LinearTransformation` which allows to implement
linear mappings.

A linear transformation can be described in the most general matter as a matrix
(I believe). Means for linear transformations you only need to provide a matrix
that defines the linear behaviour of the transformation. Imagine you apply many
transformations in a row and assume they are all linear, means they consist of
matrices. Let's write this down mathematically:

`output = f(g(input))`
`=> output = A * (B * input)`

Where `f` and `g` are our linear transformations of the form `M * input`, where
`M` describes an arbitrary matrix.

That's what happens when we apply transformations in a row like above. Matrices
have a very nice property: Associativity!

`output = A * (B * input)`
`=> output = A * B * input`
`=> output = (A * B) * input`

Nice! This means we can premultiply all matrices before applying them onto a
vector!

This behaviour yields significant performance boosts if you have many
transformations and many vectors. Since now you don't need to multiply every
single matrix with a vector again and again until every transformation is
worked through, we multiply the matrices together a single time, and then we
need to multiply with a vector also a single time!

So that's about the concept of transformations, let's do something meaningful
with our example.

### Squarify our cube

The last time our example served us this:

```
...... ....
.. . ..
. . . ...
. . .. .... .
. ... . .
. . . .
.
. . .
. . .
. .
. .... .
..... . .
. . . .
. . . .
. . .
... ......
..... .
```

We defined a cube, but this doesn't look like a cube, more like a cuboid. This
happens due to the size of a single character: It's more height than width.

Let's compensate this using a scale transformation. We assume that a char needs
twice as much height than width.

```cpp
#include <CE3D2/transformation/Scale.h>

// ...

int main()
{
auto cube_vectors = make_cube(10, 10);
auto cube = std::make_shared<Models::Model>();
cube->vectors().assign(cube_vectors.begin(), cube_vectors.end());

Render::TextRenderer renderer;
renderer.set_target(std::make_shared<Render::TextSurface>(40, 20));
renderer.models().push_back(cube);

Transformation::OrthogonalProjection ortho_projection;
std::vector<Vector> projection_vecs;
projection_vecs.push_back(create_3d_vector(0.7f, 0.3f, 0.0f));
projection_vecs.push_back(create_3d_vector(0.0f, 0.3f, 0.7f));
ortho_projection.set_projection_vectors(projection_vecs);

// Instantiate a scale transformation.
Vector scaling_vector(2);
scaling_vector[0] = 2.0f; // Only the x-component of the screen needs a bit
// stretching.
scaling_vector[1] = 1.0f;
Transformation::Scale scale(scaling_vector);

ortho_projection.transform(cube->vectors());
// Apply scale.
scale.transform(cube->vectors());

renderer.render();
print_textsurface(*renderer.get_target());

return 0;
}
```

Running the modified example prints out this:

```
... . . . . . ....
. .. . ..
. .. . . ...
.. . . . . . . .
. . . . .
. . .
.
. . .
. . .
. .
. . . . .. .
. ... . . .. .
. . .. .
. . .. .
.. . ..
.. . . . . . . .
... . . . .
```

Yeah! That's actually a cube!

1 comment on commit 67957a7

@Makman2
Copy link
Owner Author

@Makman2 Makman2 commented on 67957a7 Feb 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack

Please sign in to comment.