Skip to content

Commit

Permalink
More content
Browse files Browse the repository at this point in the history
  • Loading branch information
sebjameswml committed Nov 19, 2023
1 parent b11ed9a commit f1cc913
Show file tree
Hide file tree
Showing 12 changed files with 232 additions and 15 deletions.
5 changes: 4 additions & 1 deletion docs/_posts/2023-11-18-A_Post.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ title: "Morphologica needs a website!"

# Welcome

Not enough people know about morphologica, even though it is amazing. A website with tutorial content on how to use the different classes should help.
Not enough people know about morphologica, even though it is amazing.
A website with tutorial content on how to use the different classes
should help. I'll fill this with some reference and tutorial material
in regular web pages and then I can use blog posts to discuss ideas.
6 changes: 2 additions & 4 deletions docs/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ permalink: /about
order: 1
---

morphologica is a library of header-only C++ code.

If you're a C++ developer and you want to draw runtime graphs, surface plots or scatter plots that are **fast**, you are a member of the target demographic.
morphologica is a library of header-only C++ code for data visualization.

morphologica provides **simulation support facilities** for simulations of dynamical systems, agent-based models or, in fact, any program that needs dynamic, runtime visualization.

It helps with:

* **OpenGL Visualizations of your program while it runs**. A modern OpenGL visualization
scheme called **[morph::Visual](https://github.com/ABRG-Models/morphologica/blob/main/morph/Visual.h)**
scheme called [morph::Visual](/visual/)
provides the ability to visualise 2D and 3D graphs
of surfaces, lines, bars, scatter plots and quiver plots with minimal
processing overhead.
Expand Down
3 changes: 3 additions & 0 deletions docs/core_classes/colourmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ title: morph::ColourMap
layout: page
permalink: /core/colourmap
---
```c++
#include <morph/ColourMap.h>
```
morph::ColourMap provides a set of colour maps to indicate numerical values with graded colours.
5 changes: 4 additions & 1 deletion docs/core_classes/range.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ title: morph::range
layout: page
permalink: /core/range
---
morph::range is a useful little class for specifying a range of values.
```c++
#include <morph/range.h>
```
morph::range is a useful little class for specifying a range of values.
3 changes: 3 additions & 0 deletions docs/core_classes/scale.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ title: morph::Scale
layout: page
permalink: /core/scale
---
```c++
#include <morph/Scale.h>
```
morph::Scale is for scaling numbers.
6 changes: 3 additions & 3 deletions docs/core_classes/vec.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ layout: page
title: morph::vec
permalink: /core/vec/
---

A fixed-size mathematical vector class. Derives from `std::array`.

```c++
#include <morph/vec.h>
```

A fixed-size mathematical vector class. Derives from `std::array`.

Create like an `std::array`:

```c++
Expand Down
3 changes: 3 additions & 0 deletions docs/core_classes/vvec.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ layout: page
title: morph::vvec
permalink: /core/vvec/
---
```c++
#include <morph/vvec.h>
```

This could be a page about the variable vector class vvec.
10 changes: 10 additions & 0 deletions docs/starting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: Getting Started
layout: page
permalink: /starting/
---
This page will walk you through the process of setting up a new project to build morphologica code so that you can try the example code in the tutorials.

Because morphologica is header-only, it's very easy to work with. All you have to do is make sure your compiler knows where to find morphologica files, set a few flags in the compiler and link to a few essential libraries. The best way to coordinate all this is to use CMake from kitware, a widely used build management system.

Include the cmake readme I already wrote here.
185 changes: 185 additions & 0 deletions docs/visual/visual.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
title: morph::Visual
layout: page
permalink: /visual/
---
```c++
#include <morph/Visual.h>
```
[`morph::Visual`](https://github.com/ABRG-Models/morphologica/blob/main/morph/Visual.h) is the graphics scene class. It bundles up all the complexities of OpenGL rendering, including that of managing fonts for text into a single header-only include. It
allows you to pan and zoom the environment to view 3D
visualisations from any angle. It bundles fonts and text-handling code
to allow you to render anti-aliased text in your models. It provides
shaders that give you lighting and alpha-blend effects. It also
provides the facilities to save an image of the scene, or save the
graphical portions of the scene into a
[glTF](https://github.com/KhronosGroup/glTF) file (so that you could
open them in Blender). If you want to make a custom visualisation for
a simulation, the only job you have to do is to describe what shapes
will make up the graphical model. And because it's *modern* OpenGL,
the graphics rendering is really fast, so you can visualise your
simulations in realtime.

## Creating a scene

Here's a "Helloworld" example that creates a `morph::Visual` object, adds a label containing some text at coordinates (0,0,0) in the scene, and renders it in a window.

```c++
#include <morph/Visual.h>
int main()
{
morph::Visual v(600, 400, "Hello World!");
v.addLabel ("Hello World!", {0,0,0});
v.keepOpen(); // Renders the scene and polls for mouse/keyboard events
return 0;
}
```

This program is in morphologica's examples, so you can
compile and run it with:

```bash
cd morphologica
mkdir build
cd build
cmake ..
make helloworld
./examples/helloworld
```
It's an empty Visual scene with a text label and nothing
else. However, try pressing 'Ctrl-c' in the window, and you'll see the
3D coordinate system arrows appear. Press 'Ctrl-q' to exit.

## Adding a visual model to the scene

A Visual scene is nothing without some objects inside it. We'll use an example VisualModel called RodVisual to add an object. Add this include to the helloworld program:
```c++
#include <morph/RodVisual.h>
```
Then before v.keepOpen() you can add a simple VisualModel:

```c++
morph::vec<float, 3> pos = { 0.0, 0.0, 0.0 }; // model position in scene
morph::vec<float, 3> start = { 0, 0, 0 }; // start coordinate of rod (model frame of reference)
morph::vec<float, 3> end = { 0.25, 0, 0 }; // end coordinate of rod
morph::vec<float, 3> colour1 = { 1.0, 0.0, 0.0 }; // Colour for the rod
// This will return a std::unique_ptr<morph::VisualModel> into rvm:
auto rvm = std::make_unique<morph::RodVisual> (pos, start, end, 0.1f, colour1, colour1);
v.bindmodel (rvm); // boilerplate for all VisualModels
// Any model specific settings, such as rvm->setWidth(0.4f); would go here
rvm->finalize(); // Required. Causes the OpenGL vertices to be computed
auto rvm_ptr = v.addVisualModel (rvm); // Transfer ownership of the model into the morph::Visual
```
The pattern is to:
* create a `std::unique_ptr` to the VisualModel with `std::make_unique`
* call Visual's `bindmodel()` method, passing in the VisualModel unique_ptr. This sets some runtime callbacks
* set any features of the model (colour, dimensions, associated data etc)
* `finalize()` the model, which causes the vertices to be computed,
* add it to the `morph::Visual` with `addVisualModel()`, transferring ownership of the unique_ptr.
Note that `addVisualModel` returns a non-owning pointer to the model, which can be used to interact with the model after it has been added to the Visual.
## Render the scene and poll for events
The `Visual::render` method calls render() for each object in the scene and re-draws the entire window. You can call render() as often as is necessary in your program, but be sure to include a call to waitevents() so that your keyboard/mouse input can be processed. A common pattern would be:
```c++
MyModel model;
while (!v.readyToFinish) {
// Do computations on your model object
model.step(); // This may have nothing to do with morphologica
if ((model.stepnum % 100) == 0) { // Render every 100 steps
// Calls to update VisualModels and then...
v.render();
v.waitevents (0.001); // A very short wait
}
}
```
There is also `v.poll()` if you want to poll without any timeout.

If you don't need to change the scene you're viewing, then
```c++
v.keepOpen();
```
is equivalent to `render()` followed by `waitevents (0.018)`.


### OpenGL Context

It is essential that the correct OpenGL context is current when rendering a morph::Visual. This is especially true when rendering to two Visual windows in one program, or when rendering in one morph::Visual window, while another does GL-compute operations.

In these situations, before calling `Visual::render` for a Visual object, call its setContext() method:

```c++
v.setContext();
v.render();
```
If you only have one window, you won't need to setContext().

## The scene view
The scene will render VisualModel objects at different coordinates. You may add several graphs on a grid, and when you've completed the scene, you will want the new window to appear with a view that places all the graphs within a appropriately sized window. You can set the window size in the Visual constructor, but to move the 'view' of the scene, you need `Visual::setSceneTrans`. This translates the entire scene by a 3D vector. By moving the scene in the z coordinate, you can zoom.
```c++
v.setSceneTrans (morph::vec<float,3>({0.0f, 0.0f, -5.0f}));
```
To get the right numbers for the offset, Visual has a neat trick which you can try with any of the example programs. With the mouse, pan and zoom the content until it looks right. Press 'Ctrl-z' and then look in the stdout for the program which should look something like:
```
Scenetrans setup code:
v.setSceneTrans (morph::vec<float,3>({-0.411793f, -0.397834f, -5.0f}));
scene rotation is Quaternion[wxyz]=(0.999999,0.00121815,0,0)
Writing scene trans/rotation into /tmp/Visual.json... Success.
```
It will give the setSceneTrans() call you need. Add it after the Visual constructor:
```c++
morph::Visual v(600, 400, "Hello World!");
v.setSceneTrans (morph::vec<float,3>({-0.411793f, -0.397834f, -5.0f}));
```

## Background colour and lighting

You can set the background colour to anything by changing Visual::bgcolour which is of type std::array<float, 4> (RGBA). To set white or black backgrounds, it's just
```c++
v.backgroundWhite();
v.backgroundBlack();
```

Often in a visualization you don't want any kind of lighting effects, because an exact colour conveys information, so any kind of lighting effect distorts the information perceived by the viewer. However, to add a simple diffuse light source (which switches morph::Visual to use a slightly different set of shaders) you can call
```c++
v.lightingEffects (true);
```

## Perspective

The default is to render with perspective. However, it's possible to choose an orthgraphic projection by changing the `Visual::ptype` attribute
```c++
// Change from default of morph::perspective_type::perspective;
v.ptype = morph::perspective_type::orthographic;
```

## Saving

You can save a PNG image of the view with
```c++
v.saveImage ("file_path.png");
```
or a glTF file that describes the graphical elements of your scene (excluding text) with

```c++
v.savegltf ("file_path.gltf");
```

## Callbacks

morph::Visual contains a scheme for setting mouse and keyboard callbacks so that you can add custom key press events to your programs. See [Custom callbacks in extended Visual classes](/custom_callbacks) for more details.

## Removing a VisualModel

You may need to remove a VisualModel from your scene. Use `removeVisualModel`:

```c++
auto rvm = std::make_unique<morph::RodVisual> (pos, start, end, 0.1f, colour1, colour1);
auto rvm_ptr = v.addVisualModel (rvm);
// Stuff happens
v.removeVisualModel (rvm_ptr);
rvm_ptr = nullptr; // Don't use it again until it points to something valid
```
10 changes: 8 additions & 2 deletions docs/visual/viz.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ title: Visualization with OpenGL
layout: page
permalink: /viz/
---
morphologica uses OpenGL to draw graphics and text. In a way it's like a game engine for data visualization. Unlike a game engine, it uses very simple shaders and very simple graphics. Where a game would take artist-generated 3D models and render and animate these models, morphologica draws very primitive models out of triangles, which are generated at runtime. For example, to draw a hexagonal grid, it simply creates vertices at the corners of each hexagon, and a vertex in the centre of each hex, then renders lots of triangles. Each group of vertices forms an 'OpenGL model'.
morphologica uses OpenGL to draw graphics and text.
In a way it's like a game engine for data visualization because it provides a 3D world into which models are drawn and you can 'fly around' the models viewing them from different angles.
Unlike a game engine though, it uses very simple shaders and very simple graphics.
Where a game would take artist-generated 3D models and render and animate these models, morphologica draws very primitive models with triangles, which are generated at runtime.
For example, to draw a hexagonal grid, it simply creates vertices at the corners of each hexagon, and a vertex in the centre of each hex, then renders lots of triangles. Each group of vertices forms an 'OpenGL model'.

Before it can draw OpenGL models, morphologica needs an OpenGL context to have been set up. morphologica has a class called `morph::Visual` which sets up a graphical 'scene' and deals with the OpenGL context. By default, morph::Visual uses the library GLFW3 to create a Window and an associated OpenGL context for all the drawing (if you want two windows in your program, you'll need two morph::Visuals).

Within your scene, you will then create one or more instances of a `morph::VisualModel`. This is the base class for a number of different classes that draw different kinds of objects. If you have a custom visualization need, you can derive your own class from `morph::VisualModel`.
Within your scene, you will then create one or more instances of a `morph::VisualModel`. This is the base class for a number of different classes that draw different kinds of objects. VisualModel provides a number of drawing primitives that can be used to draw the objects that form the model. Visualization models commonly consist of discs, rods, spheres and so on. Each primitive specifies the locations of OpenGL vertices that make up the rod, sphere or disc.

If you have a custom visualization need, you can derive your own class from `morph::VisualModel`, and use the built-in primitives to draw your objects, or you can create your own.
3 changes: 3 additions & 0 deletions docs/who.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ layout: page
permalink: /who
order: 2
---
If you want to write mathematical programs in C++ and you need to see the maths, then you need a system like morphologica.

If you're a C++ developer and you want to draw runtime graphs, surface plots or scatter plots that are **fast**, you are a member of the target demographic.
8 changes: 4 additions & 4 deletions docs/why.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ Why expend the time writing an OpenGL visualization system when there are altern

I think the the answers are 'opportunity', 'preference' and 'benefit'.

'Opportunity' because I started a project with Stuart Wilson at the University of Sheffield's Department of Psychology in which we were going to solve reaction-diffusion equations and there was a requirement to plot the results of those computations. I had a free hand in how to go about the work and plenty of time on a 4 year project.
'Opportunity' because I started a project in 2018 with Stuart Wilson at the University of Sheffield's Department of Psychology in which we were going to solve reaction-diffusion equations and there was a requirement to plot the results of those computations. I had a free hand in how to go about the work and plenty of time on a 4 year project.

'Preference', because I just don't really like Python very much. I dislike its lack of scope identifiers, which make it awkward to copy blocks of code around and I find the management of Python packages a headache. On the other hand, I do really like C++.

If the resulting visualization system had not had any promise of showing a benefit over matplotlib or MATLAB, I'd still not have started it and would have lived with the least bad alternative. But vizualization based on computer gaming technology had the promise of being lightning fast with minimal use of computational resources. I really wrote morphologica so that I could have insightful graphics while still getting the most I possilbly could out of the CPU hardware I had access to.
If the resulting visualization system had not had any promise of showing a benefit over matplotlib or MATLAB, I'd still not have started it and would have lived with the least bad alternative. But visualization based on computer gaming technology had the promise of being lightning fast with minimal use of computational resources. I really wrote morphologica so that I could have insightful graphics while still getting the most I possilbly could out of the CPU hardware I had access to.

Could I have used an alternative C++ plotting system? Possibly. Alternatives include CERN's [Root](https://root.cern/), which looked old-fashioned and byzantine, [VTK](https://vtk.org/) from Kitware, which seemed complex to get started with and [matplot++](https://alandefreitas.github.io/matplotplusplus/), which copies the Python matplotlib API which I never got on with!
Could I have used an alternative C++ plotting system? Possibly. Alternatives include CERN's [Root](https://root.cern/), (ugly graphs, too complex), [VTK](https://vtk.org/) from Kitware, (too big, too complex) and [matplot++](https://alandefreitas.github.io/matplotplusplus/), which copies the Python matplotlib API which I never got on with!

What I have ended up with is a library of code which is very easy to incorporate into projects and which can animate 3D plots with very low CPU overhead. It was worth the effort.
What I have ended up with is a library of code which is lightweight, very easy to incorporate into projects and which can animate 3D plots with very low CPU overhead. It was worth the effort!

0 comments on commit f1cc913

Please sign in to comment.