Fornjot Versions Save

Early-stage b-rep CAD kernel, written in the Rust programming language.

v0.49.0

1 month ago

This release announcement is also available on the Fornjot website

It's about time for a new release of Fornjot! This one is a bit of a transitionary one, with lots of work that has been started but isn't finished yet. But none the less, there are a few goodies in here.

Let's take a look at the highlights first. A full (curated) changelog is available below.

fj::Instance

fj::Instance serves as the new entry point to the Fornjot API, and you can now easily create an instance of Fornjot by calling fj::Instance::new().

fj-core, the most substantial of Fornjot's libraries, also has a new entry point, fj_core::Core, which is available as a field on fj::Instance.

The following sections have some examples that show off the improved ease of use.

Create a shell from vertices and indices

It's now possible to create a shell by providing a bunch of vertices and indices:

use fj::core::{objects::Shell, operations::build::BuildShell};

let mut fj = fj::Instance::new();

let tetrahedron = Shell::from_vertices_and_indices(
    [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
    [[2, 1, 0], [0, 1, 3], [1, 2, 3], [2, 0, 3]],
    &mut fj.core,
);

Here's the result:

A tetrahedron

This is not the most convenient way to create a shape, nor the most powerful one, but it does allow you to specify any arbitrary polyhedron. Where the shape is too complex for less general methods, but not yet too complex to get unwieldy, there's a sweet spot for this approach.

Layers

The core of Fornjot's data structure is the object graph, which describes shapes as a graph of interrelated objects (like faces, edges, vertices, and more). Traditionally, this graph has contained all the data necessary to describe a shape. But in this release, I've started to introduce the concept of dedicated layers.

I've already finished extraction color information to a separate presentation layer. This makes it possible to update the color of an object, without having to create a new, modified object.

let mut fj = fj::Instance::new();

let size = 1.;
let cuboid = cuboid::model([size, size, size], &mut fj.core);

cuboid
  .shells().only()
  .faces().first()
  .region()
  .set_color([0., 1., 0.], &mut fj.core);

Here's the result:

A mostly red cube with one green face

This was mostly a test run, and the main effort is still ongoing: Extracting all geometric information from the object graph into a new geometry layer.

The goal is to leave the object graph as a purely topological data structure, while geometry is defined separately from that, referencing the object graph and enriching it with geometric data.

Once this work is finished, I'm hoping to get some nice advantages from that:

  • Simplified object graph: So far, every simplification of the object graph that I've managed has been a huge win in maintainability throughout the whole project. This will hopefully have the same effect.
  • Easier and more efficient updates of geometry: If geometry is part of the object graph, it's necessary to create a new object with modified geometry to update it. This is not practical, if you're using Fornjot in a GUI app, your user is dragging an edge over the screen, and you need to re-create most of the object graph every pixel to show a preview.
  • Easier to experiment with geometry: I have big plans for how to make the geometry representation in Fornjot more flexible, and thus much more capable. Having geometry not all tied up in the object graph should help a lot with making those changes.

This is currently the main focus of my ongoing work, and I hope to have more progress to show soon.

Improved validation infrastructure

Validation is a critical piece of Fornjot, which checks your shapes and points out any problems. In this release, I've started to create a new and improved validation infrastructure. This work is still ongoing, and most validation checks still run on the old infrastructure.

I'm taking this as an opportunity to clean up and document all the validation checks, which is a big win in itself. But the new validation infrastructure also has some inherent advantages over the old one.

1. Validation checks are now types

Whereas before, a validation check was a combination of a method and an error variant, hidden away in a private module, each validation check is now a dedicated type. This makes validation checks more discoverable, and provides a place to document them.

Here's an example from Fornjot's API reference:

Screenshot from Fornjot's API reference, showing the documentation of a validation check

2. Validation checks implement a trait

There already was a validation trait, but it was implemented per-object. With the new trait being implemented per-check, but referencing the object, it shows up in the object's documentation, further aiding discoverability.

Here's how that looks:

Screenshot from Fornjot's API reference, showing a validation check referenced from an object's documentation

3. Easier code sharing

With validation checks no longer organized strictly per-object, it's now easier to share code between similar checks, or even implement the same validation check for multiple types of object.

More flexible exporting

Fornjot can currently export shapes to .3mf, .obj, and .stl files. Before, you had to export the shape to an actual file, but now you can pass any impl io::Write + io::Seek, providing you better flexibility in where the exported data actually goes.

In addition, you can now explicitly choose which format to export to via the respective methods in the fj-export crate, whereas before, the format was chosen automatically based on the file extension.

This work was driven and implemented by contributor @IamTheCarl. Thank you!

Cleanups, fixes, improvements to documentation

In addition to the highlights already mentioned, we have the usual avalanche of small improvements, fixes, and documentation updates. Check out the pull requests linked below, for a full view of what happened!

I'd like to thank all the contributors who helped out with this release. Thank you @nathan-folsom, @brungardtdb, @watana318, @IamTheCarl, and @emka! You're awesome!

Sponsors

Fornjot is supported by @MitchellHansen, @webtrax-oz, @seanjensengrey, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, @sucaba, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

Library improvements

Improvements to Fornjot libraries.

fj

  • Add fj::Instance (#2217)

fj-core

fj-export

fj-interop

  • Clean up crate (#2165)
  • Add various conversions to Color (#2166)

fj-viewer

  • Upgrade to wgpu 0.19 (#2182)

Other changes

Improvements that are not associated with a specific Fornjot library.

v0.48.0

5 months ago

This release announcement is also available on the Fornjot website

Shutting down the weekly release schedule was necessary. But waiting 6 months for putting out another release? Not quite as I thought it would go 😄

Time passed quickly, as it usually does, and now the year is basically over. But don't worry, that time was filled with lots of work on Fornjot. Let's take a look at some of the highlights!

Build/update operations and ObjectSet

Fornjot has APIs, called operations, to create and modify shapes. Two of the most important types of operations are build operations, which build shapes, and update operations, for low-level modifications of shapes.

These operations have been expanded and refined since the last release. Here's an example:

Sketch::empty()
    .add_region(
        Region::polygon(
            [
                [-x / 2., -y / 2.],
                [x / 2., -y / 2.],
                [x / 2., y / 2.],
                [-x / 2., y / 2.],
            ],
            services,
        )
        .insert(services),
    )

This example combines both build operations, like Sketch::empty and Region::polygon, with update operations, like add_region, to create a rectangular sketch.

There's also the new ObjectSet data structure, which is used by all objects that reference multiple other objects of the same type. Combining it with update operations makes it easier to select the specific objects you want to modify:

solid
    .update_shell(solid.shells().only(), |shell| {
        shell
            .update_face(shell.faces().first(), |face| {
                // Update the face here!
                todo!()
            })
            .insert(services)
    })

Here, we express that we want to update the only shell of solid (which will helpfully panic, if it has multiple shells, to tell us about our wrong assumption), and the first face of shell.

This is still a very basic way to select objects, and it becomes very tedious, or even impossible, to use in non-trivial scenarios. This is an area where further improvement is required.

Split and sweep

Sweeping is an operation that "sweeps" a 2D shape through space, to create a 3D shape (you can also sweep a 1D shape into a 2D shape, but that's more of an implementation detail). We've had this feature for a long time!

In this release, the sweep code has been cleaned up significantly, and this cleanup has enabled an important new capability: Where previously, the main use case was to sweep a sketch into a new shell, you can now take an existing shell and sweep one of its faces to extend the shell.

Combined with the new split operation, which can split a face into two, this can create interesting shapes that haven't been possible before. Here's an example:

cube
    .update_shell(cube.shells().only(), |shell| {
        let face = shell.faces().first();
        let cycle = face.region().exterior();

        let line = [
            (cycle.half_edges().nth(0).unwrap(), [split_pos]),
            (cycle.half_edges().nth(2).unwrap(), [split_pos]),
        ];

        let (shell, [face, _]) = shell.split_face(face, line, services);

        shell
            .sweep_face_of_shell(face, [0., 0., -size / 2.], services)
            .insert(services)
    })

And here's the result: A cube, which had one of its faces split, then one of the resulting smaller faces swept.

This is a big step forward in capability, but there's also much left to do:

  • The new split operation is clunky and limited. Creating the line along which the face is split is awkward, and it can only be a line.
  • What we actually want here, is to apply a sketch to a face, then extrude that sketch. This will happen, but it requires smarter operations, and better infrastructure to support them.
  • Actually using the split operation suffers from the limited ways to select objects (as mentioned above).

Splitting faces is only a first step, a proof of concept. Right now, it enables models that haven't been possible before, and it paves the way for more advanced operations to be implemented in the future.

Holes

Cleaning up the sweep operations enabled another new feature: You can now create holes!

A cuboid with two holes: a blind hole on the left, and a through hole on the right

Here's an example that creates a blind hole in the bottom face of a shell:

shell.add_blind_hole(
    HoleLocation {
        face: bottom_face,
        position: [-offset, Scalar::ZERO].into(),
    },
    radius,
    [Scalar::ZERO, Scalar::ZERO, depth],
    services,
)

And this one creates a through hole, from a shell's bottom face to its top face:

shell
    .add_through_hole(
        [
            HoleLocation {
                face: bottom_face,
                position: [offset, Scalar::ZERO].into(),
            },
            HoleLocation {
                face: top_face,
                position: [offset, Scalar::ZERO].into(),
            },
        ],
        radius,
        services,
    )

While this is pretty neat, it is still quite limited:

  • The new hole operations are not very smart. You have to explicitly specify whether to create a blind or through hole.
  • The hole entry and exit each need to be contained within a face. You can't create a hole where two faces meet, and you can't create a groove using a hole that only partially overlaps the shell.
  • And I'm sure there are more bugs and limitations that haven't been discovered yet.

Again, like the face splitting, this is just a first step. Future iterations will be more powerful, robust, and flexible.

And much more!

This is only the tip of the iceberg! There are many more improvements. Some user-visible, but smaller than the ones presented above. Others under the hood, where they support the user-visible features.

Check out the list below, for a more complete overview.

What's next?

I've been focused on new features for a while, and this release is the culmination of that. But now it's time to turn inward. To lift some of the limitations of those features, and to add new and better features, we need better infrastructure.

The planning process for this has started. We'll have to see where it takes us, but #2116, #2117, and #2118, are probably what will keep me busy for a while.

However, this is not all there is to do! There are open issues and a feature wishlist with many more work items, and help is always appreciated. So if you see anything there that appeals to you, or have an idea of your own, please feel free to jump in and help out!

Sponsors

Fornjot is supported by @MitchellHansen, @webtrax-oz, @seanjensengrey, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, and my other awesome sponsors. Thank you!

Additional thanks go to @jonnedelm, @refarb, and @Retraze, who also supported this release with their financial contribution!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

Sorry, not enough room left for the full changelog! Check out CHANGELOG.md or the release announcement on the website for more information!

v0.47.0

10 months ago

This release announcement is also available on the Fornjot website

It's the first release since the end of the weekly release schedule!

The big-ticket item this time is the execution of the recent change in focus. This involved moving now deprecated components out of the repository, replace some of them with simpler versions, and improve anything else as required. The biggest chunk of work here was restoring the examples (which were based on the app and other now-removed components).

As a result, the API of fj-core (formerly fj-kernel) has become a lot more convenient and powerful. All the new examples in the repository are using it directly now, instead of through some intermediate high-level API. Improving the fj-core API further is an ongoing process.

Other than that, I've been settling into my new schedule, working on Fornjot in a reduced capacity. I've made sure to stay consistent, still putting a bit of work in every week, and that's working well so far.

Not sure what's next, specifically (figuring that out is actually the next item on my task list), but I'm sure it will mainly involve improving the fj-core API, to make existing models more convenient to write, and enable more powerful models in the future.

Sponsors

Fornjot is supported by @MitchellHansen, @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

Library improvements

Improvements to Fornjot libraries.

fj

  • Add new fj crate as all-in-one interface to the whole kernel (#1853)
  • Add standardized CLI for models (#1860)
  • Compute tolerance manually, if not provided via CLI (#1872)

fj-core

fj-interop

  • Clean up model-related code in fj-interop (#1863, #1864)

fj-math

  • Expand Aabb API (#1870)
  • Add Vector::from_component and Circle::aabb (#1887)

fj-viewer

  • Remove GUI code (#1829)
  • Remove vestigial debug rendering code (#1862)
  • Make more cleanups (#1893, #1898)

fj-window

  • Re-add fj-window crate (#1837)
  • Work around crash when opening window (#1849)
  • Rename window::run to display (#1861)
  • Expect Model in fj_window::display (#1865)

Other changes

Improvements that are not associated with a specific Fornjot library.

v0.46.0

1 year ago

This release announcement is also available on the Fornjot website

Last week saw some solid, but boring progress on the operations API (#1713). The new API is becoming ever more capable, and has now fully replaced the previous builder API. I also managed to make some design decisions that I had struggled with the week before.

This is going to be the last weekly release for the time being. Development has slowed down recently, and there's simply no need for a weekly release schedule anymore. For now, I'll play it by ear and just put out a new release whenever I think that's warranted.

I'll be publishing an article on the blog soon, about what has changed, and what's going on with the project in general.

Sponsors

Fornjot is supported by @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, @rozgo, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

End-user improvements

Improvements to Fornjot and its documentation that are visible to end users.

None this week! Busy working on the kernel.

Ecosystem improvements

Improvements to Fornjot components that are relevant to developers building on top of those. These have an indirect effect on end users, through fixed bugs and improved robustness.

fj-kernel

  • Derive Eq/Ord for Object (#1810)
  • Clean up validation service (#1811)
  • Replace remaining parts of obsolete builder API with operations API (#1812, #1813)

Internal Improvements

Improvements that are relevant to developers working on Fornjot itself.

None this week.

v0.45.0

1 year ago

This release announcement is also available on the Fornjot website

What's this, another Tuesday release? Yeah, I'm sorry. This time, a public holiday was to blame. I happened to be out hiking for most of Monday, so no time to publish a release, really 😁

Not much else to say this week. #1713 is inching along. No great problems, nor any great breakthroughs. You, you know, work.

Meanwhile, @A-Walrus found and fixed an issue in the documentation, which is always very welcome!

Sponsors

Fornjot is supported by @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, @rozgo, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

End-user improvements

Improvements to Fornjot and its documentation that are visible to end users.

Ecosystem improvements

Improvements to Fornjot components that are relevant to developers building on top of those. These have an indirect effect on end users, through fixed bugs and improved robustness.

fj-kernel

  • Clean up and expand operations API (#1794, #1797)
  • Clean up in-kernel services API (#1795)
  • Add missing information to error messages (#1796)

Internal Improvements

Improvements that are relevant to developers working on Fornjot itself.

  • Fix warning; make sure warnings can't slip through CI again (#1793)
  • Update list of sponsors in README (#1798)
  • Update dependencies (#1808)

v0.44.0

1 year ago

This release announcement is also available on the Fornjot website

Yet again, sorry for being a day late with this release! I've been experimenting with some changes to how I organize my work day, and not everything is going smoothly 😁

It's been a while since we had a proper release (due to my vacation), so I'd like to take this opportunity to summarize where we've been, where we stand now, and where we're going.

For many months, my development efforts where dominated by cleanup. A lot of complexity had crept into the kernel over time, and it became more and more clear that this was getting untenable. Fortunately, I also developed more and more ideas on how to address it.

#1589 (and the issues it links to) provide a good overview. The oldest issue linked there dates back to October, and I declared victory in March. This wasn't the only thing I worked on during that time, but still, it was a major effort.

With the complexity addressed and the code in a much better position, it became time to work on advancing the roadmap again! The next milestone is largely defined by constructive solid geometry, and we need boolean operations (#42, #43, #44) for that. However, this is a bit too much to bite off all at once. We need better APIs to construct geometry from the primitives that the Fornjot kernel already supports.

And that's what I'm working on right now. I've come to call it the operations API, as it's made up of a number of orthogonal operations on geometry ("build this, update part of it like that, join it to this other thing"). I've identified a smaller use case, writing test cases for some validation checks (#1713), that is suited to experiment with the new API.

I've made some good progress there, and I'm reasonably happy with how it's going. Still lots of questions to answer along the way of course, but that's always going to be the case. Once the new API is powerful enough to handle those test cases, I'll be looking for another use case to improve the API with, until eventually, I'm ready to tackle boolean operations directly.

This is an exciting time in the development of Fornjot (at least for me), as for the first time in a while, I'm dealing primarily with developing new things. Not just struggling to get the mess I've already made under control. Let's see where that leads over the next months!

Sponsors

Fornjot is supported by @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, @rozgo, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

End-user improvements

Improvements to Fornjot and its documentation that are visible to end users.

None this week. Busy working on the kernel!

Ecosystem improvements

Improvements to Fornjot components that are relevant to developers building on top of those. These have an indirect effect on end users, through fixed bugs and improved robustness.

fj-kernel

Internal Improvements

Improvements that are relevant to developers working on Fornjot itself.

v0.43.0

1 year ago

This release announcement is also available on the Fornjot website

Hey folks, sorry for being a day late with this release! It took me an extra day to get myself sorted after coming back from vacation.

Speaking of vacation, this is going to be a short one! Not a lot happened while I was away, and the week before I left was also quite slow.

The main attraction is definitely support for exporting .obj files, added by @replicadse!

Sponsors

Fornjot is supported by @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, @rozgo, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

End-user improvements

Improvements to Fornjot and its documentation that are visible to end users.

Ecosystem improvements

Improvements to Fornjot components that are relevant to developers building on top of those. These have an indirect effect on end users, through fixed bugs and improved robustness.

fj-kernel

  • Make minor cleanups in Solid validation code (#1737)
  • Expand operations API; replace HalfEdgeBuilder (#1738)

Internal Improvements

Improvements that are relevant to developers working on Fornjot itself.

v0.42.0

1 year ago

This release announcement is also available on the Fornjot website

That new API for create/updating geometry is coming along. I chose to tackle tests for the new Solid/Shell validation checks as a first use case, and I've made some good progress there. I wrote an update on what's left to do here.

Once that use case is addressed, a possible next step is to rewrite the sweep algorithm on top of the new API. This would require further expanding and solidifying the new API, but I'm not sure yet if that's the next logical step. We'll see!

Meanwhile, @A-Walrus submitted another fix for the Nix flake and added more validation checks, while @IamTheCarl removed and unnecessary dependency and improved the error message for panics in the model code.

Sponsors

Fornjot is supported by @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, @rozgo, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

End-user improvements

Improvements to Fornjot and its documentation that are visible to end users.

Ecosystem improvements

Improvements to Fornjot components that are relevant to developers building on top of those. These have an indirect effect on end users, through fixed bugs and improved robustness.

fj-host

fj-kernel

  • Add validation checks for Shell and Solid (#1695; thank you, @A-Walrus!)
  • Fix winding algorithm not going back to start (#1709)
  • Create new API for creating/updating geometry (#1711, #1714, #1717, #1718, #1719)
  • Clean up objects service code (#1715)

Internal Improvements

Improvements that are relevant to developers working on Fornjot itself.

v0.41.0

1 year ago

This release announcement is also available on the Fornjot website

The big kernel cleanup (#1589) is finished! The last big item was unification of partial and full objects (#1570), and I wrapped that up last week. There's still more cleanup to do, of course (I opened #1691, for example), but that's always going to be the case.

The important thing is, we're in a much better place now, and ready for the next challenges! As I wrote in #1589, the kernel has reached a level of simplicity it hasn't had in many months, and back then it had fewer features and more bugs. A clear win!

Next, I'm going to restart my work on the union operation (#42), but with a different approach: The union algorithm is going to need both intersection tests and an API to build/modify geometry. Previously, I focused on the intersection tests, but now I'm going to focus on the builder side first. I already wrote about my reasoning, in case you're interested.

Meanwhile, @A-Walrus has sent a whole avalanche of pull requests, updating the Nix flake, expanding validation, and improving test tooling.

Sponsors

Fornjot is supported by @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @martindederer, @bollian, @rozgo, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

End-user improvements

Improvements to Fornjot and its documentation that are visible to end users.

None this week, busy working on the kernel!

Ecosystem improvements

Improvements to Fornjot components that are relevant to developers building on top of those. These have an indirect effect on end users, through fixed bugs and improved robustness.

fj-kernel

Internal Improvements

Improvements that are relevant to developers working on Fornjot itself.

v0.40.0

1 year ago

This release announcement is also available on the Fornjot website

When you've just had one of the more productive weeks of your life, it's hard to live up to that anyway. Add to that a cold and the resulting low energy, and you end up with something as thoroughly mediocre as last week.

It was fine. I made some progress on the big kernel cleanup (#1589), or more specifically the unification of full and partial objects (#1570). That went easier than expected so far, but now I'm hitting some open questions. I'll attempt to answer those this week!

Sponsors

Fornjot is supported by @webtrax-oz, @reivilibre, @lthiery, @ahdinosaur, @Yatekii, @martindederer, @bollian, @rozgo, and my other awesome sponsors. Thank you!

If you want Fornjot to be sustainable long-term, please consider supporting me too.

End-user improvements

Improvements to Fornjot and its documentation that are visible to end users.

None this week. Busy working on the kernel!

Ecosystem improvements

Improvements to Fornjot components that are relevant to developers building on top of those. These have an indirect effect on end users, through fixed bugs and improved robustness.

fj-kernel

Internal Improvements

Improvements that are relevant to developers working on Fornjot itself.