morphologica icon indicating copy to clipboard operation
morphologica copied to clipboard

How to draw 3D coordinates grid

Open asmwarrior opened this issue 1 year ago • 39 comments

Hi, I would like to create some image like below:

image

The image is from this page: https://alandefreitas.github.io/matplotplusplus/plot-types/discrete-data/stem-plot-3d/

BTW: Currently, I see the matplotplusplus use GnuPlot as it's backend, see from its homepage: (https://github.com/alandefreitas/matplotplusplus/tree/master)

This library currently include a GnuPlot backend and an experimental OpenGL backend.

Just like you statement in the page: https://abrg-models.github.io/morphologica/why

I think matplotplusplus is still big and complex for me, I would like to use more tiny and compact library like morphologica for real time 3D visualization in my application. For "real time" I mean the 3D data could change from time to time.

Thanks.

asmwarrior avatar Jan 06 '24 02:01 asmwarrior

This should be quite easy to implement. I have made 3D graphs before, but they have always been scatter plots. However, I made the axes and the plot as two different VisualModel based classes. The axes class is TriaxesVisual. You will only then need to make a stemplot class. This will be quite similar to ScatterVisual, but you will need to add some tubes to make the lines from the base to each point.

sebjameswml avatar Jan 06 '24 09:01 sebjameswml

When you want to change the points visualised in your graph, you would update the data in the stemplot and call its reinit method

sebjameswml avatar Jan 06 '24 09:01 sebjameswml

If you want circles instead of spheres for your data points, you might need a new drawing primitive to do this. The existing drawing primitive are all in VisualModel, e.g. computeSphere or computeTube. It'll be an interesting challenge to add computeToroid for a nice ring marker 🙂

I'm case it's not already clear, you would do all your model drawing in stemplot::initializeVertices which is necessary in every VisualModel derived class.

sebjameswml avatar Jan 06 '24 09:01 sebjameswml

https://github.com/sebjameswml/RetinoTectal/blob/master/sim/agent/anneal1read.cpp. This program draws 3d scatters, for reference.

sebjameswml avatar Jan 06 '24 09:01 sebjameswml

It might be worth making TriaxesVisual into a class that can itself be extended to be a 3D scatter plot or a 3D stem plot or a 3D surface plot

sebjameswml avatar Jan 06 '24 12:01 sebjameswml

Hi, thanks for the help. I'm going to learn the resources you mentioned.

BTW: Is it possible to add one screen shot for each example's cpp file? I mean if I want to find which example fit my need, I have to build the example one by one(they can have the same name, such as a.cpp for a.png as screen shot image). If there are each screen shots for each cpp, I can quick locate a cpp which I need.

Thanks.

asmwarrior avatar Jan 06 '24 13:01 asmwarrior

Good idea on the screenshots.

sebjameswml avatar Jan 06 '24 14:01 sebjameswml

To visualized some basic 3D object, I found this library is very simple, see here: glampert/debug-draw: Immediate-mode, renderer agnostic, lightweight debug drawing API. or sjb3d/imdd: Immediate Mode Debug Draw library

But this library use the immediate mode of the OpenGL function call(not the core profile OpenGL mode), and I think I need to import to morphologica.

Not sure whether it is simple, because it just need some way to draw some lines by defining the vertices.

EDIT:

Oh, I see the debug-draw has some core profile mode support, see this link: debug-draw/samples/sample_gl_core.cpp at master · glampert/debug-draw

asmwarrior avatar Jan 07 '24 02:01 asmwarrior

You shouldn't need to get into ANY gl code to draw within the morphologica framework.

If you want to implement a drawing primitive, your task is simply to define vertices and vertex indices that can be drawn as a triangle mesh. I'll try to explain it better if necessary - probably best if I do that as part of the reference website.

sebjameswml avatar Jan 07 '24 12:01 sebjameswml

You shouldn't need to get into ANY gl code to draw within the morphologica framework.

If you want to implement a drawing primitive, your task is simply to define vertices and vertex indices that can be drawn as a triangle mesh. I'll try to explain it better if necessary - probably best if I do that as part of the reference website.

OK, I can learn by examples.

But I think some of the examples are a bit complex for me to understand.

For example, I try to use the class TriaxesVisual, I searched the whole git code base for the https://github.com/ABRG-Models/morphologica, and I can't find any usage of this class. I can only find some usage here:

https://github.com/sebjameswml/RetinoTectal/blob/7720d5e330f17b2ef5f72fdf04c57c3871b71c2e/sim/agent/anneal1read.cpp#L239-L249

But I really don't know what are the options of the code

    auto tav = std::make_unique<morph::TriaxesVisual<float>> (offset);
    v.bindmodel (tav);
    tav->axisstyle = morph::axisstyle::L;
    tav->input_min = range_min;
    tav->input_max = range_max;
    tav->xlabel = pnames[0];
    tav->ylabel = pnames[1];
    tav->zlabel = pnames[2];
    tav->finalize();
    v.addVisualModel (tav);

What are those options' meaning? I do not know.

So, they way is to dig into the class implementation of the TriaxesVisual.

https://github.com/ABRG-Models/morphologica/blob/b9151018edbadfb59b78e8b4f5b8a4860a6f5246/morph/TriaxesVisual.h#L23

But it still looks complex for me. It looks like there are some function calls like computeTube and computeFlatLine, from my point of view, are preparing the VBO and shaders for those objects, but I may need to dig into those functions to understand what the actual parameters I need to supply.

I think a basic tutorial is needed here. I mean one simple sample program for one image shot.

asmwarrior avatar Jan 07 '24 13:01 asmwarrior

That's fair enough that you find it difficult to understand and the class does need some documentation.

The code inside TriaxesVisual IS quite complex because drawing all the ticks and tick labels on graph axes is a lot of work!

I'll see if I can make you a simple example to play with.

sebjameswml avatar Jan 07 '24 13:01 sebjameswml

By the way - you're right - computeTube etc are all functions in morph/VisualModel.h and they are all the "graphics primitives" - the functions that build up the VBOs with vertices and vertex indices.

sebjameswml avatar Jan 07 '24 13:01 sebjameswml

I'm just making a triaxesvisual example in the issue/wxWidgets_154 branch. I have noticed that some of the TriaxesVisual.h code is slightly out of date, so I'll fix that too.

sebjameswml avatar Jan 07 '24 14:01 sebjameswml

Ok, see wx-triaxes.cpp

I also fixed a small bug which meant that the left/right mouse button presses weren't being distinguished from each other, so you can now rotate the graphs around (this didn't work on wxWidgets on Linux without commit 343f42f )

sebjameswml avatar Jan 07 '24 15:01 sebjameswml

Ok, see wx-triaxes.cpp

I also fixed a small bug which meant that the left/right mouse button presses weren't being distinguished from each other, so you can now rotate the graphs around (this didn't work on wxWidgets on Linux without commit 343f42f )

Thanks, it works, see the image shot below:

image

But there are some issues, The main issue is that the labels along the exis is NOT "billboard" style. If I rotate the scene, the text labels will rotate too, so they are not showing very well, see the above image.

To use the billboard mode, a special shader is needed, but it is still complex......

asmwarrior avatar Jan 07 '24 23:01 asmwarrior

I've actually solved that problem before. You can show the CoordAxes by pressing Ctrl-C. You'll see a red, green and blue axes in the bottom left with labels that always face the right way: image

sebjameswml avatar Jan 07 '24 23:01 sebjameswml

It works for the CoordArrows (a special VisualModel that is owned by the morph::Visual scene) because they are rotated with VisualModel::setViewRotation, which is a bit different to what happens with the view of the overall scene. It's quite complicated, I'll admit. I coded most of this several years ago, got it to a point where I was happy and haven't had to delve into it again much since then. Probably not a 5 minute job to add a 'keepFlat' option for VisualTextModels, but possible.

sebjameswml avatar Jan 07 '24 23:01 sebjameswml

image

Great, that's exact the method to show the "x", "y" and "z".

One minor issue, when I rotate the scene, the "z" label will move out of the window(see the image show above).

asmwarrior avatar Jan 07 '24 23:01 asmwarrior

I just looked at the all the images in this folder:

https://github.com/ABRG-Models/morphologica/tree/main/examples/screenshots

It looks like there are a lot of 2D graphics, but there are not many 3D graphics.

for example, I would like to see some 3D graphics like below:

https://asymptote.sourceforge.io/gallery/3Dgraphs/helix.html

Also, there are many other 3D graphics in the page: https://asymptote.sourceforge.io/gallery/3Dgraphs/

The first step is 3D lines, the next step is 3D surfaces.

I looked at the file:

https://github.com/ABRG-Models/morphologica/blob/main/morph/vec.h

Is it possible to use this class to draw a 3D space lines?

I don't see any example code can do that, thanks.

asmwarrior avatar Jan 22 '24 01:01 asmwarrior

I try to copy the code to set the model in the source file examples/wx/wx-triaxes.cpp to the examples/wx/wx-graph1.cpp. And I got a crash when runs to the line tav->finalize();, not sure why, I am under the git branch abrg/dev/remove_morph_wx_frame. Can you have a look? Thanks.

asmwarrior avatar Jan 22 '24 08:01 asmwarrior

I try to copy the code to set the model in the source file examples/wx/wx-triaxes.cpp to the examples/wx/wx-graph1.cpp. And I got a crash when runs to the line tav->finalize();, not sure why, I am under the git branch abrg/dev/remove_morph_wx_frame. Can you have a look? Thanks.

It crash at this line:

https://github.com/ABRG-Models/morphologica/blob/685759aca8fdd7d2379050477b7ce9b911a25d15/morph/TriaxesVisual.h#L57

asmwarrior avatar Jan 22 '24 09:01 asmwarrior

[snip]

It looks like there are a lot of 2D graphics, but there are not many 3D graphics. [snip] The first step is 3D lines, the next step is 3D surfaces.

Yes, it would be nice to show more 3D examples. The examples that you see reflect the way I used the library myself - 3D surface plots and 2D graphs, mostly, along with customised visuals for stuff like virtual agents.

I looked at the file:

https://github.com/ABRG-Models/morphologica/blob/main/morph/vec.h

Is it possible to use this class to draw a 3D space lines?

I don't see any example code can do that, thanks.

vec.h is one of the core maths classes in morphologica. It's used to do some of the maths for the graphics.

You could certainly use it as a data type within a VisualModel for plotting 3D space lines.

Have a look at how QuiverVisual works - that's a visual which plots lots of lines.

optseb avatar Jan 22 '24 13:01 optseb

I try to copy the code to set the model in the source file examples/wx/wx-triaxes.cpp to the examples/wx/wx-graph1.cpp. And I got a crash when runs to the line tav->finalize();, not sure why, I am under the git branch abrg/dev/remove_morph_wx_frame. Can you have a look? Thanks.

It crash at this line:

https://github.com/ABRG-Models/morphologica/blob/685759aca8fdd7d2379050477b7ce9b911a25d15/morph/TriaxesVisual.h#L57

Ok, on that branch I ONLY updated wx-graph1 to work with the architecture changes we discussed a few days ago. wx-triaxes and wx-graph6 both need some extra work.

optseb avatar Jan 22 '24 13:01 optseb

I try to copy the code to set the model in the source file examples/wx/wx-triaxes.cpp to the examples/wx/wx-graph1.cpp. And I got a crash when runs to the line tav->finalize();, not sure why, I am under the git branch abrg/dev/remove_morph_wx_frame. Can you have a look? Thanks.

It crash at this line: https://github.com/ABRG-Models/morphologica/blob/685759aca8fdd7d2379050477b7ce9b911a25d15/morph/TriaxesVisual.h#L57

Ok, on that branch I ONLY updated wx-graph1 to work with the architecture changes we discussed a few days ago. wx-triaxes and wx-graph6 both need some extra work.

I try to debug it myself, but GDB just show the application get crashed at the line:

                auto lbl = std::make_unique<morph::VisualTextModel<glver>> (this->parentVis, this->get_tprog(this->parentVis), this->font, this->fontsize, this->fontres);

I really don't know how to track the actual error.

asmwarrior avatar Jan 22 '24 14:01 asmwarrior

[snip]

It looks like there are a lot of 2D graphics, but there are not many 3D graphics. [snip] The first step is 3D lines, the next step is 3D surfaces.

Yes, it would be nice to show more 3D examples. The examples that you see reflect the way I used the library myself - 3D surface plots and 2D graphs, mostly, along with customised visuals for stuff like virtual agents.

I looked at the file: https://github.com/ABRG-Models/morphologica/blob/main/morph/vec.h Is it possible to use this class to draw a 3D space lines? I don't see any example code can do that, thanks.

vec.h is one of the core maths classes in morphologica. It's used to do some of the maths for the graphics.

You could certainly use it as a data type within a VisualModel for plotting 3D space lines.

Have a look at how QuiverVisual works - that's a visual which plots lots of lines.

        morph::vec<float, 3> offset = { 0.0, 0.0, 0.0 };

        vector<morph::vec<float, 3>> coords;
        coords.push_back ({0, 0,   0});
        coords.push_back ({1, 1,   0});
        coords.push_back ({2, 0,   0});
        coords.push_back ({1, 0.8, 0});
        coords.push_back ({2, 0.5, 0});

        vector<morph::vec<float, 3>> quivs;
        quivs.push_back ({0.3,   0.4,  0});
        quivs.push_back ({0.1,   0.2,  0.1});
        quivs.push_back ({-0.1,  0,    0});
        quivs.push_back ({-0.04, 0.05, -.2});
        quivs.push_back ({0.3,  -0.1,  0});

        auto qvp = std::make_unique<morph::QuiverVisual<float>> (&coords, offset, &quivs, morph::ColourMapType::Cividis);
        v.bindmodel (qvp);
        qvp->finalize();
        unsigned int visId = v.addVisualModelId (qvp);
        cout << "Added Visual with visId " << visId << endl;

This code is from the file testVisRemoveModel.cpp, but I think it is far more complex than a general 3D lines. Because a Quiver need many other properties than a single 3D line segment. I even don't need to use a 'cone' or 'cylinder', instead a pure single 3D line is enough.

asmwarrior avatar Jan 23 '24 03:01 asmwarrior

I debugged a while through the code, to be honest, it is hard to debug those template related code.

And I found a line here:

https://github.com/ABRG-Models/morphologica/blob/9d55bdd02d0b6b5a5de1caf9282ea92594fa937e/morph/GraphVisual.h#L562

The question is: Why this is needed? Which looks like the data can only be x and y, because the z value is set to 0.

My guess is that to use pure 3D lines, the z value should be set by the user.

asmwarrior avatar Jan 23 '24 03:01 asmwarrior

While reading the code, I see some variables named ad and sd, from the comments, I see they are "abscissa and ordinate". But Why not to use a more common name such as x and y, this will let the user to understand the code more quickly, and also if you want to add the z coordinates of a point.

asmwarrior avatar Jan 23 '24 03:01 asmwarrior

OK, it looks like this class does not allow a z value for the point, see the function here:

https://github.com/ABRG-Models/morphologica/blob/9d55bdd02d0b6b5a5de1caf9282ea92594fa937e/morph/GraphVisual.h#L370-L411

The z value is always set the 0, in the function call (*this->graphDataCoords[data_idx])[i][2] = Flt{0};.

asmwarrior avatar Jan 23 '24 03:01 asmwarrior

Oh, sorry, it looks like from the comment, it said this class is for 2D

    /*!
     * A VisualModel for showing a 2D graph.
     */
    template <typename Flt, int glver = morph::gl::version_4_1>
    class GraphVisual : public VisualModel<glver>

asmwarrior avatar Jan 23 '24 05:01 asmwarrior

I just search all the opengl's glDrawXXXX related function calls in this library, and I found that it only exits in two files:

morph/VisualModel.h and morph/VisualTextModel.h.

The bad thing I see is that everything it would like to draw is a type of GL_TRIANGLES. I mean whether it is a surface, a line or a cube, it all draws like a triangle types.

But sometimes, I do need some other kinds of glDraw types. For example, in my own personal opengl project, I use glDrawArrays(GL_LINES, xxx, xxxx); which is for 3D lines, and glDrawArrays(GL_POINTS, xxx, xxxx); for drawing points.

So, I don't want to simply use the function like computeLine or computeFlatLine to convert the pure 3D lines to a surface. There are a lot of performance issues.

Any ideas to solve this issue?

It looks like different kinds of types(lines, points, surfaces) may need different shaders.

asmwarrior avatar Jan 23 '24 06:01 asmwarrior