From f1cc913b729e524e3a657cc803efae3dec31d817 Mon Sep 17 00:00:00 2001 From: Seb James Date: Sun, 19 Nov 2023 20:55:30 +0000 Subject: [PATCH] More content --- docs/_posts/2023-11-18-A_Post.md | 5 +- docs/about.md | 6 +- docs/core_classes/colourmap.md | 3 + docs/core_classes/range.md | 5 +- docs/core_classes/scale.md | 3 + docs/core_classes/vec.md | 6 +- docs/core_classes/vvec.md | 3 + docs/starting.md | 10 ++ docs/visual/visual.md | 185 +++++++++++++++++++++++++++++++ docs/visual/viz.md | 10 +- docs/who.md | 3 + docs/why.md | 8 +- 12 files changed, 232 insertions(+), 15 deletions(-) create mode 100644 docs/starting.md create mode 100644 docs/visual/visual.md diff --git a/docs/_posts/2023-11-18-A_Post.md b/docs/_posts/2023-11-18-A_Post.md index 25d126b8..10a0e8a6 100644 --- a/docs/_posts/2023-11-18-A_Post.md +++ b/docs/_posts/2023-11-18-A_Post.md @@ -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. \ No newline at end of file +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. \ No newline at end of file diff --git a/docs/about.md b/docs/about.md index 8b6f33b8..7611315b 100644 --- a/docs/about.md +++ b/docs/about.md @@ -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. diff --git a/docs/core_classes/colourmap.md b/docs/core_classes/colourmap.md index 623e4480..5950193a 100644 --- a/docs/core_classes/colourmap.md +++ b/docs/core_classes/colourmap.md @@ -3,4 +3,7 @@ title: morph::ColourMap layout: page permalink: /core/colourmap --- +```c++ +#include +``` morph::ColourMap provides a set of colour maps to indicate numerical values with graded colours. \ No newline at end of file diff --git a/docs/core_classes/range.md b/docs/core_classes/range.md index e61951f0..448e59e1 100644 --- a/docs/core_classes/range.md +++ b/docs/core_classes/range.md @@ -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. \ No newline at end of file +```c++ +#include +``` +morph::range is a useful little class for specifying a range of values. diff --git a/docs/core_classes/scale.md b/docs/core_classes/scale.md index 461f6289..96e991ef 100644 --- a/docs/core_classes/scale.md +++ b/docs/core_classes/scale.md @@ -3,4 +3,7 @@ title: morph::Scale layout: page permalink: /core/scale --- +```c++ +#include +``` morph::Scale is for scaling numbers. \ No newline at end of file diff --git a/docs/core_classes/vec.md b/docs/core_classes/vec.md index d1417f8e..5819845e 100644 --- a/docs/core_classes/vec.md +++ b/docs/core_classes/vec.md @@ -3,12 +3,12 @@ layout: page title: morph::vec permalink: /core/vec/ --- - -A fixed-size mathematical vector class. Derives from `std::array`. - ```c++ #include ``` + +A fixed-size mathematical vector class. Derives from `std::array`. + Create like an `std::array`: ```c++ diff --git a/docs/core_classes/vvec.md b/docs/core_classes/vvec.md index a420f743..007e4e79 100644 --- a/docs/core_classes/vvec.md +++ b/docs/core_classes/vvec.md @@ -3,5 +3,8 @@ layout: page title: morph::vvec permalink: /core/vvec/ --- +```c++ +#include +``` This could be a page about the variable vector class vvec. diff --git a/docs/starting.md b/docs/starting.md new file mode 100644 index 00000000..af6953a0 --- /dev/null +++ b/docs/starting.md @@ -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. \ No newline at end of file diff --git a/docs/visual/visual.md b/docs/visual/visual.md new file mode 100644 index 00000000..9844c280 --- /dev/null +++ b/docs/visual/visual.md @@ -0,0 +1,185 @@ +--- +title: morph::Visual +layout: page +permalink: /visual/ +--- +```c++ +#include +``` +[`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 +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 +``` +Then before v.keepOpen() you can add a simple VisualModel: + +```c++ +morph::vec pos = { 0.0, 0.0, 0.0 }; // model position in scene +morph::vec start = { 0, 0, 0 }; // start coordinate of rod (model frame of reference) +morph::vec end = { 0.25, 0, 0 }; // end coordinate of rod +morph::vec colour1 = { 1.0, 0.0, 0.0 }; // Colour for the rod +// This will return a std::unique_ptr into rvm: +auto rvm = std::make_unique (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({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({-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({-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 (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 (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 +``` \ No newline at end of file diff --git a/docs/visual/viz.md b/docs/visual/viz.md index 7d5746eb..8250fd4c 100644 --- a/docs/visual/viz.md +++ b/docs/visual/viz.md @@ -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`. \ No newline at end of file +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. \ No newline at end of file diff --git a/docs/who.md b/docs/who.md index aa3cad3c..1d2964ca 100644 --- a/docs/who.md +++ b/docs/who.md @@ -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. diff --git a/docs/why.md b/docs/why.md index a6f6ad32..abea6055 100644 --- a/docs/why.md +++ b/docs/why.md @@ -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. \ No newline at end of file +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! \ No newline at end of file