Screen shot showing a Canvas GUI dialog with a custom MathGL element rendering a simple sine (proof-of-concept) - the point being, while simple stuff like this can certainly be renderered using our existing Canvas.Path element, more complex/mathematical expressions and functions are better handled by a dedicated plotting library, that is threaded and hardware accelerated (OpenGL+ GLSL).
MathGL is a Mathematical Graphics Library (MathGL), a collection of classes and routines for scientific plotting. [1]. MathGL is ...
- a library for making high-quality scientific graphics under Linux and Windows
- a library for the fast data plotting and handling of large data arrays
- a library for working in window and console modes and for easy embedding into other programs
- a library with large and growing set of graphics
To learn more about key concepts, see: http://mathgl.sourceforge.net/doc_en/General-concepts.html
For details, see the reference manual: https://kumisystems.dl.sourceforge.net/project/mathgl/mathgl/mathgl%202.4.4/mathgl-2.4.4.eng.pdf
For testing purposes, use any of the examples from: http://mathgl.sourceforge.net/doc_en/All-samples.html#All-samples
To integrate MathGL as a custom Canvas drawing primitive (element), the steps are basically:
- downloading/installing MathGL locally
- modifying $SG_SRC/CMakeLists.txt to add MathGL detection (includes & linker)
- adding a new Canvas element to $SG_SRC/elements, e.g. named "MathGL"
- registering the new element via the sc::Group<> registry
- updating $FG_DATA/Nasal/canvas/api.nas to add the new element there
- adding one of the MathGL examples to the new Canvas element
- obtaining the raw image data from MathGL by drawing in-memory:
- updating the osg::Image directly via osg::Image::setImage()
- exposing useful MathGL APIs in the form of dedicated element-specific properties
- some APIs are not easily mapped to a SGPropertyNode based structure, so that dedicated Nasal/CppBind bindings would make more sense, and be less work
This Canvas element merely sub-classes sc::Image to update the osg::Image in memory by using MathGL's GetRGBA() API to obtain the raw RGBA image and update the corresponding osg::Image/Texture in memory respectively by calling osg::Image::setImage()
Screen shot showing a Canvas GUI dialog with a custom MathGL drawing element populated via the Nasal console.
Screen shot showing a Canvas GUI dialog with a custom MathGL drawing element updated by the property browser.
This is the same custom MathGL Canvas element rendering a more complex 3D mesh/function, fully hardware accelerated using OpenGL and GLSL, including support for threaded number crunching in pthread-enabled MathGL builds
MathGL running in FlightGear
simple MathGL based 2D plotting
Complex multi-instance MathGL based plotting
You will want to add a new Canvas::Element subclass whenever you want to add support for features which cannot be currently expressed easily (or efficiently) using existing means/canvas drawing primitives (i.e. via existing elements and scripting space frameworks).
For example, this may involve projects requiring camera support, i.e. rendering scenery views to a texture, rendering 3D models to a texture or doing a complete moving map with terrain elevations/height maps (even though the latter could be implemented by sub-classing Canvas::Image to some degree).
Another good example for implementing new elements is rendering file formats like PDF, 3d models or ESRI shape files.
To create a new element, you need to create a new child class which inherits from Canvas::Element base class (or any of its child-classes, e.g. Canvas::Image) and implement the interface of the parent class by providing/overriding the correspond virtual methods.
To add a new element, these are the main steps:
- Set up a working build environment (including simgear): Building FlightGear
- update/pull simgear,flightgear and fgdata
- check out a new set of topic branches for each repo: git checkout -b topic/canvas-MathGL
- Navigate to $SG_SRC/canvas/elements
- Create a new set of files MathGL.cxx/.hxx (as per Adding a new Canvas element)
- add them to $SG_SRC/canvas/elements/CMakeLists.txt (as per Developing using CMake)
- edit $SG_SRC/canvas/elements/CanvasGroup.cxx to register your new element (header and staticInit)
- begin replacing the stubs with your own C++ code
- map the corresponding OSG/library APIs to properties/events understood by the Canvas element (see the valueChanged() and update() methods)
- alternatively, consider using dedicated Nasal/CppBind bindings
Below, you can find patches illustrating how to approach each of these steps using boilerplate code, which you will need to customize/replace accordingly:
Caution This custom Canvas element requires a 3rd party library which is not currently used by SimGear/FlightGear, so that the top-level CMakeLists.txt file in $SG_SRC needs to be modified to add a corresponding findPackage() call and you also need to download/install the corresponding library for building sg/fg. In addition, the CMake module itself may need to be placed in $SG_SRC/CMakeModules:
We need to edit the top-level cmake file to check for our new dependency: MathGL2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e3b905..711547b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -204,6 +204,7 @@ else()
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
+ # put FindMathGL2.cmake from (check google) into $SG_SRC/CMakeModules
+ find_package(MathGL2)
endif(SIMGEAR_HEADLESS)
find_package(ZLIB REQUIRED)
|
Step #1 is editing $SG_SRC/canvas/elements/CMakeLists.txt to add our set of new files
diff --git a/simgear/canvas/elements/CMakeLists.txt b/simgear/canvas/elements/CMakeLists.txt
index 2b537c0..08f9857 100644
--- a/simgear/canvas/elements/CMakeLists.txt
+++ b/simgear/canvas/elements/CMakeLists.txt
@@ -7,6 +7,7 @@ set(HEADERS
CanvasMap.hxx
CanvasPath.hxx
CanvasText.hxx
+ CanvasMathGL.hxx
)
set(DETAIL_HEADERS
@@ -20,6 +21,7 @@ set(SOURCES
CanvasMap.cxx
CanvasPath.cxx
CanvasText.cxx
+ CanvasMathGL.cxx
)
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
@@ -28,4 +30,4 @@ simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEA
add_boost_test(canvas_element
SOURCES canvas_element_test.cpp
LIBRARIES ${TEST_LIBS}
-)
\ No newline at end of file
+)
MathGL: Canvas MathGL Support (minimal C++ changes)
Step #2 is creating the new files for our C++ code
diff --git a/simgear/canvas/elements/CanvasMathGL.cxx b/simgear/canvas/elements/CanvasMathGL.cxx
new file mode 100644
index 0000000..8004245
--- /dev/null
+++ b/simgear/canvas/elements/CanvasMathGL.cxx
@@ -0,0 +1,87 @@
+// MathGL.cxx: Stub for exposing MathGL as a dedicated Canvas element
+//
+// Copyright (C) 2015 your name
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "CanvasMathGL.hxx"
+
+
+namespace simgear
+{
+namespace canvas
+{
+ const std::string MathGL::TYPE_NAME = "mathgl";
+ //----------------------------------------------------------------------------
+ void MathGL::staticInit()
+ {
+ Image::staticInit();
+
+ if( isInit<MathGL>() )
+ return;
+
+ // Do some initialization if needed...
+ }
+
+ //----------------------------------------------------------------------------
+ MathGL::MathGL( const CanvasWeakPtr& canvas,
+ const SGPropertyNode_ptr& node,
+ const Style& parent_style,
+ ElementWeakPtr parent ):
+ Image(canvas, node, parent_style, parent)
+ {
+ SG_LOG(SG_GENERAL, SG_ALERT, "New MathGL element added!");
+ }
+
+ //----------------------------------------------------------------------------
+ MathGL::~MathGL()
+ {
+ SG_LOG(SG_GENERAL, SG_ALERT, "New MathGL element removed!");
+ }
+
+ //----------------------------------------------------------------------------
+ void MathGL::update(double dt)
+ {
+ Image::update(dt);
+ }
+
+ //----------------------------------------------------------------------------
+ void MathGL::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
+ {
+ return Image::childAdded(parent, child);
+ }
+
+ //----------------------------------------------------------------------------
+ void MathGL::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
+ {
+ return Image::childRemoved(parent, child);
+ }
+
+ //----------------------------------------------------------------------------
+ void MathGL::valueChanged(SGPropertyNode* child)
+ {
+ return Image::valueChanged(child);
+ }
+
+ //----------------------------------------------------------------------------
+ void MathGL::childChanged(SGPropertyNode* child)
+ {
+ }
+
+ //----------------------------------------------------------------------------
+
+} // namespace canvas
+} // namespace simgear
+
diff --git a/simgear/canvas/elements/CanvasMathGL.hxx b/simgear/canvas/elements/CanvasMathGL.hxx
new file mode 100644
index 0000000..1cd4062
--- /dev/null
+++ b/simgear/canvas/elements/CanvasMathGL.hxx
@@ -0,0 +1,61 @@
+// CanvasMathGL.hxx: Stub for exposing MathGL as a dedicated Canvas element
+//
+//
+// Copyright (C) 2015 your name
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef CANVAS_MATHGL_HXX_
+#define CANVAS_MATHGL_HXX_
+
+#include "CanvasImage.hxx"
+
+#include <boost/shared_ptr.hpp>
+
+namespace simgear
+{
+namespace canvas
+{
+ class MathGL:
+ public Image
+ {
+ public:
+ static const std::string TYPE_NAME;
+ static void staticInit();
+
+ MathGL( const CanvasWeakPtr& canvas,
+ const SGPropertyNode_ptr& node,
+ const Style& parent_style,
+ ElementWeakPtr parent = 0 );
+ virtual ~MathGL();
+
+ virtual void update(double dt);
+
+ virtual void childAdded( SGPropertyNode * parent,
+ SGPropertyNode * child );
+ virtual void childRemoved( SGPropertyNode * parent,
+ SGPropertyNode * child );
+ virtual void valueChanged(SGPropertyNode * child);
+
+ protected:
+
+ virtual void childChanged(SGPropertyNode * child);
+
+ };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_MATHGL_HXX_ */
MathGL: Canvas MathGL Support (registering the new element)
Step #3 is modifying CanvasGroup.cxx to register our new element
diff --git a/simgear/canvas/elements/CanvasGroup.cxx b/simgear/canvas/elements/CanvasGroup.cxx
index de289a8..49fa2c1 100644
--- a/simgear/canvas/elements/CanvasGroup.cxx
+++ b/simgear/canvas/elements/CanvasGroup.cxx
@@ -21,6 +21,8 @@
#include "CanvasMap.hxx"
#include "CanvasPath.hxx"
#include "CanvasText.hxx"
+#include "CanvasMathGL.hxx"
+
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
@@ -64,6 +66,8 @@ namespace canvas
add<Map >(_child_factories);
add<Path >(_child_factories);
add<Text >(_child_factories);
+ // add MathGL
+ add<MathGL>(_child_factories);
}
//----------------------------------------------------------------------------
MathGL: Canvas MathGL Support $FG_ROOT/Nasal/canvas/api.nas changes
Step #4 is extending api.nas to provide Nasal wrappers for the new element (note, the corresponding fgdata structure has been updated recently)
diff --git a/Nasal/canvas/api.nas b/Nasal/canvas/api.nas
index 6d39d03..f98fdd9 100644
--- a/Nasal/canvas/api.nas
+++ b/Nasal/canvas/api.nas
@@ -1099,13 +1099,21 @@ var Image = {
}
};
+var MathGL = {
+ new: func(ghost)
+ {
+ return {parents: [MathGL, Element.new(ghost)]};
+ },
+};
+
# Element factories used by #Group elements to create children
Group._element_factories = {
"group": Group.new,
"map": Map.new,
"text": Text.new,
"path": Path.new,
- "image": Image.new
+ "image": Image.new,
+ "mathgl": MathGL.new,
};
MathGL: Canvas MathGL Support (testing the new element using the
Nasal Console)
Step #5
var (width,height) = (640,480);
var title = 'Canvas Element test: MathGL';
# create a new window, dimensions are WIDTH x HEIGHT, using the dialog decoration (i.e. titlebar)
var window = canvas.Window.new([width,height],"dialog").set('title',title);
# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();
var child = root.createChild("mathgl");
var updateFunction = func canvas.InputDialog.getText("MathGL Demo", "Please enter a function to plot", func(btn,value) {
if(!value) return;
# set up some params for the graph:
child.set("clear", 1);
child.set("title", "Click to change function");
child.set("function", value);
child.set("finish", 1);
}, "sin(pi*x)");
child.addEventListener("click", func(e) {
updateFunction();
});
updateFunction();
Screen shot showing how the function to be plotted by MathGL can be interactively changed using a standard Canvas GUI dialog.