This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Mjolnir Release 0.4.0

     After another month of work, the next release of Mjolnir is now available! This release brings a lot of comfort changes to improve the workflow of part design, and also includes many oft-requested features such as a dark mode.

Full list of changes include:

  • Added a config tab
    • Colors, theme, font Size, initial scale, key binds, mouse sensitivity, and and like a gorillion other things can be configured
    • Stored in your native config directory under mjolnir
    • Themes can be separately saved + loaded for easy sharing
    • There are now two built-in themes to choose from: Light and Dark
  • Added expressions and variables
    • Expressions support basic operations and referencing other expressions
    • You can use expressions in any place a number would be used, note that a unit is required for distance and etc.
    • If you forget to provide a unit for a field that requires a distance, the value will automatically be wrapped in parenthesis and it will be appended with mm
    • All part variables can be accessed and edited in the “vars” tab
      • Variables cannot be deleted just yet but I’ll be working on that later this week, honestly I just forgot until I started typing this.
  • Added ability to reorder operations by dragging and dropping
  • Added context menu for timelines
  • Added “isolate” shortcut to isolate a solid by itself visually
  • Added distance and centerpoint constraints for the origin point
  • Added distance constraints for the radius of arcs + circles,
  • Fixed bug where the solver would freeze if you managed to select the same point twice via a point-point distance constraint

Also, here are some currently known bugs:

  • Subtraction operations involving circles are still a little slow. I am currently re-working some code that will fix this issue.

Expressions and Variables

     When making a parametric model it is imperative you use various variables and equations/expressions to make your part. This is what makes it parametric modeling parametric, after all. To add these to Mjolnir I had to overcome a couple hurtles: make an expression parser/processor, and integrate them to where they are ergonomic and easy to use.

     Making the parser and processor was the first on my to-do. It was going to be a little tricky, but I have also been working on a C compiler in my free time so I already had a general idea of where to start. I designed it so that when the user provides an expression it is first parsed into computer- digestible tokens, and then those tokens are stored into memory where they can be processed later when needed. Variables referenced in expressions are also stored via their UUID instead of their alias, so that when/if you want to change the variable name you don’t need to go through each expression and update it; it is automatically updated for you.

     Expressions also needed to handle units properly and universally; if the end user is insistent on mixing inches and meters they should have the ability to, even at the expense of the sanity of everyone around them. The internal tokens(which are stored as enums wrapping structs) retain all info about their intended unit and will relay that information to any operation or constraint the expression is being used for. Also, to improve ergonomics, a unit will be appended to expressions that are unitless if the operation requires it, like extrusion or distance constraints.

     To make it ergonomic was my next challenge. A peeve of mine in other CAD programs I’ve used is that creating/modifying variables can be annoying or difficult to do when making a sketch or editing geometry. FreeCAD makes it the most convoluted process ever, and OnShape places variables in the timeline making you have to drag every variable to the top. I just wanted a tab with all the part’s variables in one table so that as I’m working on a sketch or whatever I can just change the tab, type in a variable, and get back to what I was doing.

     I was more or less able to accomplish that, though I will say the GUI framework I’m using does NOT have a good native table. If I just left it as is, there would only be one line running down the middle and none separating the variables. I had to do some hackery to add different separators and a border around the edge of the table to make it look right.

Configuration

     Configuration is an important yet often neglected part of many CAD suites. It always feels like there’s so much to change, yet also what you can change never feels like enough. One of Mjolnir’s primary goals is to have a clean and enjoyable experience, and in order to enable that the config tab is there to allow end users to truly mold their CAD program to fit their feel, needs and aesthetic.

     I didn’t want Mjolnir to seem like a dime-a-dozen off-brand desktop app, so I already knew I didn’t want configuration to be in a little claustrophobic gray box with like 18 different tabs where you can’t find anything(and it looks ugly). I wanted it to be laid out in a responsive page that feels like a modern app.

     First I started on the keybinds section. I wanted to avoid tables since it would clash with the feel I had in my head, but also if I just had a bunch of labels it would be hard to navigate and discern which button belongs to which key. To add visibility, I made it so each label highlights when the corresponding button is hovered over, allowing the user to quickly see what keybind they are modifying without sacrificing the aesthetic.

     Implementation wasn’t too hard, though it was a little tricky to lay everything out due to the fact I am using egui, which is an immediate mode renderer. I have to go through every element at least once and record the maximum width to get the appropriate size and padding for each config entry(this applies to almost every text field you see in the program to be fair). This does limit the flexibility of layout quite a bit, and also has a noticeable performance impact, but Mjolnir accepts these limitations for now and I’ve been able to make some fairly serviceable UIs with it.

     Once I was able to configure key binds I then went to implementing the config file format. It’s a little bass ackwards to do it this way, but I program best when I can get tangible results quickly so I really wanted to get the format figured out before I implemented too much else. Config is stored in the user’s native config directory in mjolnir/config.toml, along with themes in mjolnir/themes/ (more on that in a second). As can be inferred, I chose to use TOML for configuration since it is simple to understand and used commonly naively in Rust. This is in contrast to the project files which are Compressed JSON files, but that is primarily due to the fact that the projects need to deal with substantially more complex types that TOML can’t handle and it really doesn’t need to be human readable. Perhaps I’ll go into the Project file format one day.

     After the config file format was laid out and I verified configurations stuck (the file is saved each time you edit a field), I moved on to colors and themes. I first had to figure out what colors configured what in egui. At first I thought this was going to be easy, but I ended up having trouble finding out what colors actually mapped to what. This was due partially to the fact that egui doesn’t have the tightest enforcement on color mapping standards, and also the docs not being entirely clear as to what belongs to what. But also I’ll admit I was kinda oblivious and missed a bunch of things initially, like it didn’t click in my head that widget styles were in the widget field of the style struct. I probably just needed more coffee or sumn, idk.

     Besides the egui theme colors I also needed to map colors like icons, lines, points and etc. Most of it was relatively easy: icons used set colors in their shader and the colors could be changed with one write to the uniform, lines and points were generated per rebuild and could use any rgb color for their vertices. The only things I couldn’t do this update were the colors of the planes and the navcube, primarily because I want to rewrite both in the future and the way they’re implemented now isn’t too flexible.

     Once I had the colors mapped I added a drop-down for built in themes, along with a save+load button in case the user wishes to make their own. While it may not be a major thing nowadays, I have a fond nostalgia of making my own color themes in apps like Notepad++ and LxTerminal when I was a teen, and while it may be more practical to have just a plain dark or light theme I do think it is nice to be able to have fun in your program every once in a while and tweak it to be your own. I wish I could add fonts too this update (I would choose Comic Sans) but alas I didn’t have the time as changing fonts would likely mess with spacing. I did however add the ability to change font size though since the default font size for egui is microscopic, at least for me.

     After I finished the colors and themes section I went on to add various misc. settings that the user can configure; mouse sensitivity, render tolerance, MSAA on/off (I can’t tell the difference with it on tbh), tolerance. There are two settings I feel need further elaboration: cursor assist and solver iterations. Cursor assist is fairly straight forward; basically it increases the ‘hitbox’ for things like points and lines so that you don’t need to split pixels to be able to select them. Solver iterations is also straight forward but needs some more explaining. Mjolnir uses a solver derived from Solvespace’s, which basically just keeps making iterative educated guesses as to what numbers best fit the requirements/constraints of a sketch, and stopping once the values fall under our tolerance. Since the solver rarely knows the correct values on the first pass, we need to let it get progressively better tolerances over multiple iterations. You can’t set this too high, otherwise things can get slow and there are some constraint setups that can’t be solved no matter how hard the solver tries. However if the value is too low you’ll get errors on clearly solvable geometry and you’ll have to finesse your sketch to make the solver happy. I have it set to 1000 iterations since this works the best in my testing, but you can set it to whatever works best for you.

Re-ordering operations

     Some things are unusually difficult in programming, and some things are pleasantly easy. Drag and drop re-ordering was pleasantly easy.

     When making a part sometimes you need to re-order things in the timeline, and the easiest way for the user to do that would be to allow them to drag the operation to a new location in the timeline. Surprisingly egui already had a handy drag and drop system to deal with this; all I had to do was provide the index of an operation when it is detected to be dragged, and when it is dropped I can sense it on the draw call for the destination operation. Then all I need to do is tell the OpEngine to move the operation at the index of the dragged operation to the index of the recipient operation (a little bit winded after typing that ngl). Either way maybe took like 20-50 lines total, it was so easy that I decided to go ahead and add a transparent ghost of the operation that follows the cursor and that also took like maybe another 20 or so lines. Experiences like these are a breath of fresh air in programming.

Future Outlook & Goals

     So far I am quite happy with the progress I’ve made. Since 0.1.0 last December I have been able to keep on a fairly regular schedule and I’ve been able to add plenty of CAD features that other 3d suites simply don’t have, like truly parametric modeling without TNP and a robust constraint solver. Next update I want to finally push this program from a proof of concept to something I can use for basic projects, by implementing sweep, revolve and loft tools. After I implement those I will start modeling something with Mjolnir to truly test it, most likely a model rocket first since I’ve been wanting to do one for a while.

     I also want to stabilize the file format next update. The past updates have resulted in breaking changes to the save file format, and with the introduction of these new features I want people to actually be able to make parts with Mjolnir without having to worry about their files not being able to be opened in newer versions.

     I am also still working on acquiring a non-linux machine to do testing on; I have only really been able to find windows 7 and 8 junk PCs which don’t have windows 10 drivers, and Mjolnir requires at least OpenGL 3.3(which was release in 2010 so you’d think it’d be supported by everything but no). I also need an ARM Mac but I’m too broke for that at the moment.

Special Thanks To

  • caseusdeus for illustrating the icons and the new logo
  • The Mjolnir Discord for beta-testing my program

Open Source Dependencies

Mjolnir would not be possible without these projects!