Developing a MOOSE-based Console for arbalest: a first step to merge arbalest and qged
BRL-CAD
Daniel Rossberg, Himanshu Sekhar Nayak
This project sets out to take the initial step in merging BRL-CAD’s two in-development GUIs: “arbalest” and “qged”. The primary objective is to transfer qged’s sophisticated GED console, which works via low-level calls to BRL-CAD’s core libraries (such as libged and librt), into arbalest, while preserving the application’s distinctive clean and easy-to-scale architecture. To support this endeavor, I will also expand BRL-CAD’s new lightweight, modular, object-oriented API, known as “MOOSE”. In addition to these core tasks, I will tackle compatibility issues related to arbalest’s Qt widgets to ensure proper display across different OSs, as well as resolve various GUI-related bugs.
ObjectTree
Qt Style Sheet
Over the past few years, significant efforts have been made to create a new Graphical User Interface (GUI) for BRL-CAD, with the goal of replacing the two outdated but still-used GUIs: “mged” and “archer”.
The two most successful efforts have been “arbalest” and “qged”.
Although both are Qt-based GUIs that share the same goal, they have fundamentally different implementation philosophies:
For my Google Summer of Code 2025 project, I worked on enhancing the more successful GUI arbalest, taking the first step in merging arbalest and qged, specifically by transferring qged’s sophisticated GED (Geometry EDiting library, a CLI library for working on BRL-CAD geometry) console into arbalest, while preserving the GUI’s distinctive clean and easy-to-scale architecture.
Aside from this main objective, I will also tackle compatibility issues related to arbalest’s Qt widgets to ensure proper display across different OSs (Linux and Windows mainly), as well as resolve various GUI-related bugs.
The primary goals of the project were:
libged
’s internal functions (the library that contains all GED-related functions)QWidget
console that supports GED command execution, command completion, object names completion and command history.Qt Style Sheet
in order to make the GUI appear the same on all OSs (focusing on Windows and Linux).The proposal included an idea for adding a functionality that would have given the user the ability to create new themes (outside of the standard light/dark ones) and modify already existing ones directly from within arbalest, making it simpler to customize the GUI. This idea was abandoned to prioritize more critical goals.
What follows are details about the most important changes that I have done to arbalest during this project.
ObjectTree
The ObjectTree
is an essential class which has the task of representing the open geometric database, particularly the tree structure of the objects in a database.
Note:
It’s important to notice that a BRL-CAD database is composed of two objects: primitives (which are the actual solids) and combinations (which are groups that contain other combinations and/or primitives). BRL-CAD’s databases allow primitives and combinations to be inside many different combinations at the same time. This means that the same object, even though it’s defined only once in memory, can be a child of many combinations simultaneously, so it can appear many times inside the tree that represents the objects of a database.
Previously the ObjectTree
was mainly composed of many QHash
es that connected a unique item’s id, in which an item is a node in the database tree, to one property of the corresponding geometry object. For example:
/* fullPathMap connects an item's id to the corresponding object's path */
QHash<int, QString> fullPathMap;
/* objectIdParentObjectIdMap connects an item's id to the id of the corresponding item's parent */
QHash<int, int> objectIdParentObjectIdMap;
This architecture has the privilege of being extremely simple, but obviously has many issues:
ObjectTree
’s building speed after opening a file is pretty slow, because we need to recursively go through the database tree and fill out many QHash
es with the properties of each node.QHash
es, instead of just once.To greatly improve the ObjectTree
’s architecture I took full advantage of how BRL-CAD’s databases are structured, by defining the following two classes:
ObjectTreeItem
, which represents a node in the database tree. It contains the following properties:
ObjectTreeItem
parent,ObjectTreeItem
s children,ObjectTreeItemData
that represents the BRL-CAD’s object in the current node in the database tree,ObjectTreeItemData
, which represents an object in the database. It contains the following properties:
ObjectTreeItem
s that share this ObjectTreeItemData
,With these classes set up, it’s now possible to have only 2 QHash
es:
ObjectTreeItem
.QString
to the corresponding ObjectTreeItemData
(since in BRL-CAD’s databases all objects must have a unique name).All of this work allows us now to work with the items of the ObjectTree
in a truly object-oriented way, making the code much clearer and more maintainable.
Also, making it so that all object-specific properties are contained inside ObjectTreeItemData
, which is shared across many ObjectTreeItem
, allows us to allocate memory for the object only once, and share it across all items. This also means that it is now extremely simple to change properties of an object, without having to rewrite all corresponding items.
Finally, I also reworked the “building ObjectTree
” algorithm, making it more efficient and easier to expand if in the future more object properties are added.
The results of my work are as follows:
ObjectTree
building time is down by an average of 66.4% (97.8% in the best situation, 24.4% in the worst situation).These conclusions come from the following data table, which was created by testing the old and the new ObjectTree
on 39 standard BRL-CAD databases (that can be found here).
Database name | No. of nodes | No. of objects | No. nodes per object | Average time old [μs] | Average time new [μs] | Time decrease |
---|---|---|---|---|---|---|
sphflake.g | 835 | 834 | 1.001 | 136595.333 | 3015.467 | 97.792% |
havoc.g | 8.719 | 2.952 | 2.954 | 521648.033 | 31060.400 | 94.046% |
goliath.g | 5.284 | 217 | 24.350 | 227395.900 | 17390.867 | 92.352% |
m35.g | 4.241 | 2.080 | 2.039 | 279974.600 | 25470.000 | 90.903% |
castle.g | 823 | 125 | 6.584 | 31985.833 | 4053.567 | 87.327% |
tank_car.g | 1.301 | 645 | 2.017 | 40935.367 | 6102.100 | 85.093% |
cray.g | 404 | 60 | 6.733 | 10066.467 | 1556.033 | 84.542% |
cube.g | 546 | 61 | 8.951 | 16964.633 | 2671.067 | 84.255% |
die.g | 102 | 43 | 2.372 | 2356.700 | 395.133 | 83.234% |
xmp.g | 398 | 53 | 7.509 | 8374.900 | 1426.800 | 82.963% |
star.g | 312 | 199 | 1.568 | 6679.400 | 1497.033 | 77.587% |
bldg391.g | 754 | 383 | 1.969 | 16193.933 | 3660.800 | 77.394% |
toyjeep.g | 428 | 375 | 1.141 | 11734.767 | 2715.133 | 76.862% |
annual_gift_man.g | 271 | 148 | 1.831 | 6201.500 | 1439.600 | 76.786% |
lgt-test.g | 227 | 72 | 3.153 | 5166.800 | 1204.833 | 76.681% |
kman.g | 328 | 122 | 2.689 | 7267.067 | 1757.100 | 75.821% |
bearing.g | 48 | 39 | 1.231 | 967.500 | 245.167 | 74.660% |
galileo.g | 109 | 61 | 1.787 | 2518.833 | 646.967 | 74.315% |
radialgrid.g | 51 | 32 | 1.594 | 1097.567 | 290.800 | 73.505% |
truck.g | 435 | 366 | 1.189 | 22349.433 | 6465.400 | 71.071% |
aet.g | 133 | 57 | 2.333 | 2586.233 | 764.700 | 70.432% |
demo.g | 465 | 178 | 2.612 | 6670.700 | 1994.067 | 70.107% |
traffic_light.g | 690 | 138 | 5.000 | 95068.733 | 32956.033 | 65.335% |
shipping_container.g | 391 | 383 | 1.021 | 22419.667 | 7780.067 | 65.298% |
ktank.g | 352 | 243 | 1.449 | 7275.533 | 2609.133 | 64.138% |
axis.g | 41 | 35 | 1.171 | 1022.267 | 438.700 | 57.086% |
crod.g | 32 | 26 | 1.231 | 638.667 | 276.833 | 56.654% |
operators.g | 95 | 15 | 6.333 | 2365.900 | 1069.200 | 54.808% |
pic.g | 28 | 27 | 1.037 | 628.400 | 298.233 | 52.541% |
moss.g | 15 | 15 | 1.000 | 429.633 | 211.333 | 50.811% |
rounds.g | 25 | 20 | 1.250 | 492.767 | 245.933 | 50.091% |
woodsman.g | 26 | 24 | 1.083 | 624.833 | 323.633 | 48.205% |
terra.g | 15 | 12 | 1.250 | 414.367 | 216.733 | 47.695% |
wave.g | 25 | 25 | 1.000 | 653.667 | 372.800 | 42.968% |
cornell-kunigami.g | 21 | 21 | 1.000 | 540.900 | 348.800 | 37.061% |
cornell.g | 20 | 20 | 1.000 | 544.233 | 342.533 | 35.515% |
world.g | 18 | 18 | 1.000 | 502.733 | 350.300 | 30.321% |
boolean-ops.g | 25 | 13 | 1.923 | 458.833 | 325.700 | 29.016% |
prim.g | 8 | 8 | 1.000 | 231.867 | 175.533 | 24.296% |
The new ObjectTree
required many changes across the entire code, to adapt everything to the new ObjectTree
structure.
For details, refer to the pull requests: #66.
MOOSE required some new functionalities and modifications.
My mentor Daniel Rossberg created hooks for registering and deregistering callback functions through the underlying methods db_add_changed_clbk()
and db_rm_changed_clbk()
. These callback functions are called when an object in the database is added/removed/modified. The arguments of the callback were a reference to the object and an enum
that indicates whether said object was added/removed/modified.
While testing this new functionality, I found some issues related to having a reference to a not-fully-constructed object. In the end, we switched to passing the name of the object as an argument, instead of a reference.
Daniel Rossberg also added a way to call the registered callback if a combination’s children reference changes, using the underlying methods db_add_update_nref_clbk()
and db_rm_update_nref_clbk()
.
Finally, I also modified the Parse()
method (inside the CommandString
module), the method that handles the execution of GED commands, in order for it to return more information about the execution result. This additional information will be used by the new console to handle some specific commands.
For details, refer to the pull requests: #3, #5, #71.
The GED console is the core of my proposed project.
The actual Console
QWidget
is not very complex and is mostly based on libqtcad
’s QgConsole
(the console that is used in qged), with some modifications to simplify it and adapt it to call MOOSE’s methods.
The previously described changes to MOOSE made it easy to implement support for all commands, and with the new ObjectTree
it was easy to add callbacks for when a command caused changes in the database.
The most challenging part was creating methods that to update the ObjectTree
after a command execution. To do that I created some methods for when an object is added/removed/modified, that allowed queuing Qt signals. This way, when these queued signals actually called the connected slots, it meant that the added/removed/modified objects were fully created/modified/removed, so that updating these objects could proceed smoothly.
After this, I created an algorithm that goes through the entire database tree and, for each node, checks if anything is different in the tree structure, and if so, updates it.
Finally, I revised many components of the GUI, such as Properties
, ObjectTreeWidget
, and many more, so that they would be able to manage the new situations introduced by certain GED commands. An example of these situations are the “kill commands” (kill
, killall
, killtree
and killrefs
), which, for the first time, introduced the possibility of destroying objects in the database. This required ensuring that if an object was removed from the database, all parts of the GUI would update correctly.
For details, refer to the pull requests: #67, #68, #69, #70, #71.
New Arbalest console on Linux (Ubuntu 24.04.1 LTS):
New Arbalest console on Windows (Windows 11):
Qt Style Sheet
Many changes were made to standardize the appearance of QWidget
s on Windows and Linux, and to make the code easier to maintain.
First of all, I reverted the hacks used to create a personalized title bar that also acted as a menu bar. These hacks caused many portability issues (particularly on macOS), so I simply removed them and restored the default Qt title bar.
After that, I focused on fixing background color issues affecting many widgets on Linux (particularly the document area buttons), and standardizing the appearance of all elements (borders, margins, paddings, sizes, etc.). While working on these issues, I completely reworked the Qt Style Sheet
“arbalest_simple.qss”, making it much clearer, more concise and precise.
Finally, I added support for changing themes at runtime, without requiring the application to be restarted.
For details, refer to the pull requests: #60, #61, #62, #63, #64.
New Arbalest style with dark theme on Linux (Ubuntu 24.04.1 LTS) with theme change at runtime:
New Arbalest style with light theme on Linux (Ubuntu 24.04.1 LTS) with theme change at runtime:
New Arbalest style with dark theme on Windows (Windows 11) with theme change at runtime:
New Arbalest style with light theme on Windows (Windows 11) with theme change at runtime:
Old Arbalest style with dark theme on Linux (Ubuntu 24.04.1 LTS):
Old Arbalest style with light theme on Linux (Ubuntu 24.04.1 LTS):
Old Arbalest style with dark theme on Windows (Windows 11):
Old Arbalest style with light theme on Windows (Windows 11):
Other minor bug fixes and tweaks include:
using
directives in header files, in order to fix the error rpcndr.h: 'byte': ambiguous symbol
when building on Windows: #59.MainWindow
’s icons: #61.Date of Merge | Pull Request | Status | Description |
---|---|---|---|
June 7th, 2025 | Removed using directives in header files [BRL-CAD/arbalest/PR#59] |
![]() |
Removed using directives in header files, in order to do fix the error rpcndr.h: 'byte': ambiguous symbol when building on Windows. |
June 7th, 2025 | Reverted the hacks done to create a personalized title bar [BRL-CAD/arbalest/PR#60] | ![]() |
Reverted the hacks done to create a personalized title bar, and went back to using the default Qt title bar for portability and maintainability reasons. |
June 15th, 2025 | Removed MainWindow icon memory waste and document area buttons' icons uncertainty regarding how they appear [BRL-CAD/arbalest/PR#61] |
![]() |
Standardized how the document area buttons' icons look on different OSs, and removed some useless memory allocation. |
June 16th, 2025 | Standardized "arbalest_simple.qss" to appear the same way on Windows and Linux [BRL-CAD/arbalest/PR#62] | ![]() |
Standardized the Qt Style Sheet "arbalest_simple.qss" to appear the same way on Windows and Linux. |
June 16th, 2025 | Added dedicated method that sets UI elements icons and added more theme variables for icon colors [BRL-CAD/arbalest/PR#63] | ![]() |
Added more theme variables to define icon colors, and a dedicated method to set UI element icons. |
June 17th, 2025 | Added support for changing themes at runtime (without reopening arbalest) [BRL-CAD/arbalest/PR#64] | ![]() |
Added support for changing themes at runtime (without requiring the application to be restarted). |
August 15th, 2025 | Idea for new ObjectTree [BRL-CAD/arbalest/PR#66] |
![]() |
Replaced the old ObjectTree , which was based on QHash es that connect an id to one of the geometry object properties, with a new ObjectTree , in which a single QHash connects an id to an actual object that contains all the properties of a geometry object. |
August 10th, 2025 | New Console for arbalest (clone from libqtcad 's QgConsole , but using MOOSE) [BRL-CAD/arbalest/PR#67] |
![]() |
Created the QWidget Console , heavily based on libqtcad 's QgConsole , but without having direct connections to BRL-CAD's internal libraries, and connecting instead only to MOOSE functions. |
August 21th, 2025 | Added support to handle non-existent objects in the ObjectTreeWidget [BRL-CAD/arbalest/PR#68] |
![]() |
Modified ObjectTreeWidget in order to handle non-existent geometry objects. |
August 23th, 2025 | Allow the ObjectTree to update itself after a command execution [BRL-CAD/arbalest/PR#69] |
![]() |
Added methods to ObjectTree that allow it to update itself when a GED command that modified the geometry database was executed. |
Pending review | Fix segmentation fault when an object is selected in ObjectTreeWidget [BRL-CAD/arbalest/PR#70] |
![]() |
Heavily reworked ObjectTreeWidget to fix a segmentation fault that occurred when an object was selected (a consequence of the modifications made to ObjectTree ). |
Pending review | Added support for MOOSE's ChangeType::References [BRL-CAD/arbalest/PR#71] |
![]() |
Modified databaseChangeHandler to support the new ChangeType::References . |
August 8th, 2025 | Modified Parse() method so that it returns additional information [BRL-CAD/MOOSE/PR#3] |
![]() |
Modified the Parse() method (which handles the execution of GED commands) so that it returns additional information about the execution result. |
August 11th, 2025 | Changed callbacks argument from Object to const char* (objectName ) [BRL-CAD/MOOSE/PR#5] |
![]() |
Modified the ChangeSignalHandler callback function so that one of its arguments is the name of the object that triggered the callback, instead of a reference to the actual object. This avoids issues related to referencing a not-fully-constructed object. |
This project successfully integrated a MOOSE-based qged-like GED console into arbalest, significantly improved the ObjectTree
architecture, and enhanced cross-platform GUI consistency.
These changes will hopefully lay the groundwork for further merging of arbalest and qged, and for future arbalest specific features.
Future work that still needs to be done is the following:
I want to deeply thank Daniel Rossberg for being an outstanding mentor and developer. Without his help, I wouldn’t have achieved what I have done during this GSoC.
I also want to thank the organization administrator, Christopher Sean Morrison, for always being present and supportive throughout the entire summer.
And obviously I also want to thank everyone else in the great BRL-CAD community who helped me during this project.