Google Summer of Code 2025
Lorenzo Pegorari

Google Summer of Code 2025 - Final project report

GSoC_BRLCAD_arbalest_logo


Read as a GitHub Gist!

Project

Developing a MOOSE-based Console for arbalest: a first step to merge arbalest and qged

Organization

BRL-CAD

Mentors

Daniel Rossberg, Himanshu Sekhar Nayak

Abstract

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.


Table of Contents

  1. Introduction
    • Goals
    • Abandoned Goals
  2. Implementation Details
    • Reworking the ObjectTree
    • Changes Made to MOOSE
    • The GED Console
      • How the Console Looks
    • Improving the Qt Style Sheet
      • New GUI
      • Old GUI
  3. Minor Miscellaneous Improvements
  4. Pull Requests
  5. Conclusions
  6. Future Work
  7. Acknowledgments
  8. References

Introduction

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.

Goals

The primary goals of the project were:

Abandoned Goals

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.

Implementation Details

What follows are details about the most important changes that I have done to arbalest during this project.

Reworking the 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 QHashes 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:

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:

With these classes set up, it’s now possible to have only 2 QHashes:

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:

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.

Changes Made to MOOSE

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

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.

How the Console Looks

New Arbalest console on Linux (Ubuntu 24.04.1 LTS):

New Arbalest console on Windows (Windows 11):

Improving the Qt Style Sheet

Many changes were made to standardize the appearance of QWidgets 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 Sheetarbalest_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 GUI

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 GUI

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):

Minor Miscellaneous Improvements

Other minor bug fixes and tweaks include:

Pull Requests

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 QHashes 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.

Conclusions

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

Future work that still needs to be done is the following:

Acknowledgements

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.

References