Git Product home page Git Product logo

fornjot's Introduction

Fornjot

Blog | Community | Contribution Guide

About

Fornjot is an early-stage CAD kernel, using boundary representation (b-rep), written in the Rust programming language.

As a CAD kernel, the project's main goal is to provide a solid foundation for developers to build on top of, whether for special purpose tooling, third-party libraries for extending Fornjot's feature set, or full-featured CAD applications.

In doing so, Fornjot follows these principles:

  • Focus on mechanical CAD applications, like 3D printing, machining, woodworking; over other use cases such as architecture or electronics.
  • Favor reliability over features. Anything you can do should either work as expected, or result in a clear and actionable error.
  • Maintain a friendly API for directly defining models in Rust. This means code-first CAD modeling (or Code-CAD) is natively supported.
  • Support code-first CAD modeling in other languages, by enabling third-party APIs.

Fornjot is still in development and doesn't always live up to these ambitions. None the less, these are the priorities the project follows.

For more information, please check out the website.

Sponsors

Fornjot is supported by @MitchellHansen, @reivilibre, @krl, @seanjensengrey, @lthiery, @ahdinosaur, @martindederer, @bollian, @sucaba, @thestig4242, @Rahix, @nullstyle, @HalfVoxel, @jessebraham, @MattOslin, @jminer, @U007D, @guillaumechauvat, @mayfieldiv, @bglw, @hansihe, @romixlab, @justinmimbs, @yikesable, and my other awesome sponsors. Thank you!

Please consider supporting me too, to help make Fornjot sustainable long-term.

Table of Contents

Status

Fornjot is usable for simple models (see examples), but currently lacks the features for anything more advanced. Work to change that is underway.

Overview

Fornjot features a modular architecture, allowing you to pick and choose which parts of it you want to use. It is made up of the following libraries:

  • fj: All-in-one API that re-exports all of the following crates.
  • fj-math: Math primitives used by the rest of Fornjot.
  • fj-interop: Basic types that allow other crates to interoperate, without depending on each other.
  • fj-core: Core primitives and code operating on those primitives.
  • fj-export: Exports Fornjot models to external data formats.
  • fj-viewer: Displays Fornjot models.
  • fj-window: Simple windowing abstraction for use with fj-viewer.

Usage

Fornjot is a set of Rust libraries (see list above). The definitive documentation on how to use those is their reference documentation. The crates.io pages of each library (see list above) link to those.

If you want to use Fornjot to create a specific model in Rust, the best starting point are the example models in this repository:

  • To display a model, run cargo run -p cuboid (replace cuboid with name of model you want to display).
  • To export a model, run cargo run -p cuboid -- --export model.3mf (replace cuboid with name of model you want to export; optionally replace 3mf with another supported file format).
  • To see full set of CLI options, run cargo run -p cuboid -- --help (all models have the same CLI interface, so this shouldn't differ much between them).

Community

If you are interested in Fornjot, please consider joining the community. We'd love to have you!

Please check out the community page on the website for information on where to find us!

Get Involved

If you are interested in helping out, just fork one of the GitHub repositories and submit a pull request:

If you don't know what to work on, check out the good first issues. If you need some more guidance, check out the contribution guide, or just ask!

License

This project is open source, licensed under the terms of the Zero Clause BSD License (0BSD, for short). This basically means you can do anything with it, without any restrictions, but you can't hold the authors liable for problems.

See LICENSE.md for full details.

fornjot's People

Contributors

a-walrus avatar antonok-edm avatar bandsberg avatar brungardtdb avatar chrisprice avatar connor-lennox avatar danieleades avatar dependabot[bot] avatar devanlooches avatar emka avatar erenoku avatar gabsi26 avatar hannobraun avatar hendrikmaus avatar iamthecarl avatar jeevcat avatar jzow avatar kamirr avatar kopackiw avatar martinkavik avatar michael-f-bryan avatar mxdamien avatar nathan-folsom avatar obiwanrohan avatar payload avatar philipp-m avatar t0mstone avatar therealprof avatar tmayoff avatar zthompson47 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fornjot's Issues

Build models in repository as part of CI build

The CI build doesn't currently build the models in the models/ directory, which means that errors can creep in there unnoticed.

Maybe it could be as simple as creating a Cargo workspace, and adding the models to it. If not, adding a shell script (or ideally something more portable) that goes through everything in models/ and runs cargo build would work.

Labeling as https://github.com/hannobraun/Fornjot/labels/good%20first%20issue, since this is purely a Cargo thing that doesn't require any knowledge of Fornjot.

Implement intersection operation

It should be possible to create the intersection of two shapes, using constructive solid geometry (CSG). This is currently not supported.

Implementing this requires more solid infrastructure in the host application for storing and querying shapes. Also see #42 and #43.

Develop concept for configuration

Right now, we don't have any persistent configuration. There are command-line parameters and some stuff in the UI, but no means to store configuration persistently.

Some thoughts:

  • At the heart of it should be a configuration file. I prefer TOML.
  • It might be useful to override configuration settings from the command line. Whether to do that manually, for selected settings, or whether there should be a CLI parameter that can systematically override any config setting, I don't know.
  • Where should that configuration file live? Something in .config on Linux (and the equivalent on other platforms) might be appropriate, but maybe users would also want to override user-level configuration on a per-model basis, using a local file. For a start, it might be best to just stick with a local file and see if there's need for something more advanced.
  • It would be great to be able to change settings from the GUI, but if there are multiple locations for configuration files, that poses the question how to GUI would handle that.
  • Maybe there is already a crate that handles that kind of stuff.

I wanted to open this issue to write down my thoughts (because the topic came up in #37), but it's probably best to wait until we have real use cases (marking as status: blocked until then). We can collect thoughts here in the meantime.

Loading model libraries doesn't work on platforms other than Linux

Right now, the host application expects the dynamic library to end with .so, which is correct on Linux. It should either fall back to other file endings to support more platforms, or there might be a way to override the file name outright, and set it to something like .model.

Improve plugin system

Convenience

The extern "C" stuff required for the dynamic library isn't great. And the way parameters are passed to the model has caused further pain. Ideally, creating a model should require little or no ceremony to satisfy the plugin system. Maybe something like this would be good:

/// Defines the model parameters
///
/// The `derive` makes the parameter parsing just work, so the user doesn't have
/// to think about it. Preferably in a way that moves the bulk of the work to
/// the host, so compile times for the model stay low.
#[derive(fj::Parameters)]
pub struct Spacer {
    pub outer: f64,
    pub inner: f64,
    pub height: f64,
}

impl fj::Model for Spacer {
    /// Defines the model itself
    /// 
    /// Has access to the parameters through `self`. Method is defined in a
    /// trait, to provide type safety.
    fn model(&self) -> fj::Shape {
        // define the model
    }
}

This is just a sketch, not to be taken too seriously. Maybe it's possible, or maybe a #[no_mangle] or something will still be required.

Technology

Aside from the convenience of defining the model, the ideal technology is also a bit of an open question. I've decided to go with dynamic linking initially, as that seemed like a good way to get started (which turned out to be correct). But WebAssembly seems like a stronger candidate long-term:

  • It provides sandboxing, which could alleviate security concerns. At some point, people might want to look at models from untrusted sources.
  • It provides a path for running on the web. I'd like to be able to integrate Fornjot models into websites, in an interactive way that allows people to change model parameters. I'm not sure how the implementation would look in detail, but if models are WASM modules, that seems like a step in the right direction.
  • On non-web platforms, something like Wasmtime or Wasmer could be used.

Enforce formatting in CI build

I, like many others, use cargo fmt to format my code. To prevent the formatting from drifting, which might cause unrelated formatting changes when someone with formatting enabled edits differently-formatted code again, we should enforce this in the CI build.

Instead of adding a build step for each platform-specific build, I think it would make sense to add a build script and just call that from each build. That reduces duplication and has the advantage of letting developers run the CI build locally, which takes out a lot of guesswork. This build script could then be extended to check formatting.

A shell script won't do, we need something portable. cargo-make looks like it would work.

Labeling as https://github.com/hannobraun/Fornjot/labels/good%20first%20issue, since no Fornjot knowledge is required. This is just general build system work.

Display model structure

Right now, only the end result is shown. The primitives and operations applied to them, that make up the model, aren't. This can make it hard to debug your model.

There should be some kind of tree structure visible on the screen, that shows all those primitives and operations which make up the model. Each element in that tree should be selectable. When selected, only that element should be shown.

Blocked on #116.

Evaluate options for improving GUI infrastructure

I can use some better debugging/visualization infrastructure soon, and I see the need for some more advanced GUI stuff in the future (like some kind of selectable model tree to show different aspects of the model; like sketches, not just the final result). Both of these cases will benefit from some more solid UI infrastructure.

I don't want to write it myself, and egui is looking really good. I think it would be great to port the current UI (sparse as it is) over, then look into expanding the UI.

Provide Linux/Mac/Windows binaries

To make Fornjot more accessible, and testing easier, we should compile executables for Linux, Mac, and Windows. Since we're already using GitHub Actions for the CI build, it makes sense to use it for this too. This should probably be a separate action that is triggered whenever a normal build on the main branch finished. It shouldn't block pull requests.

In the past, I've used the Releases feature to host those binaries. I'd rather reserve Releases for real releases though (as to not confuse users with a million different release, when they click there), so either those binaries should be downloadable through GitHub Actions, or there should be a single nightly/in-progress/WIP release that gets overwritten every time. I don't know if either of those are possible.

Labeling this as good first issue, as this only requires knowledge of Cargo and GitHub, not really of anything Fornjot-specific.

Feature: Support for GD&T

Would you be interested in supporting GD&T?

I would love to help implementing this if it has interest. I am not an expert, but we use it (ISO GPS to be specific) in the company that I work at.

Application freezes sometimes after starting

Sometimes (rarely, but often enough that it happens quite a lot during development) the application freezes after being started. The model is rendered, everything looks normal, but no input is processed. The window can't even be closed, and the application has to be shut down via CTRL-C on the command-line.

This points to the problem being relatively low-level. Probably something is going wrong when processing winit events.

Make sure Fornjot can be used outside this repository

I always just use cargo run during development. To make Fornjot accessible to end-users, we need to make sure that a Fornjot binary can be used outside this repository.

The one issue I'm aware of right now is that Fornjot makes assumptions about where to find models (see #9). I don't know what hidden dependencies there are. Maybe there are none.

I'm labeling this as https://github.com/hannobraun/Fornjot/labels/good%20first%20issue, since it's pretty straight-forward: Download a binary from the Compile Binaries action run it outside of this repository, open issues for any problems that are encountered.

Fornjot application can crash, if specific graphics features are not supported on target

As of this writing, the graphics code uses one feature that might not be available everywhere: wgpu::Features::POLYGON_MODE_LINE

The code just requests that feature. If that features isn't available on the target (where the host application is executed), the host application would either fail with an error message, or even panic. This is not optimal, especially since POLYGON_MODE_LINE is only used for an optional drawing mode (rendering the lines of the triangle mesh).

Although I haven't seen or heard of any problems regarding this, it would be better to handle these optional features in a more robust way:

  1. Check which of features we want to use are actually supported.
  2. Only request those features that are actually available. Store that information somewhere.
  3. Don't let the user enable anything that requires features which are not available.

Ideally, there would be a nice error message in the UI, to let the user know what the problem is. But for now, it would already be an improvement, if the host application didn't crash if such a feature wasn't available.

Add option to switch off automatic model reloading

Automatic reloading of models doesn't work perfectly and might work differently between platforms. It would be good to have the option to disable it, if need be.

I'm not sure how best to do that though:

  • A command-line parameter is an option, but that would require the user to always specify it.
  • A UI element could be added (in which case this issue should be blocked on #4). That might be more convenient than a command-line parameter, in some cases, but any change to this setting would not be persist after Fornjot is restarted.
  • Specifying it in a configuration file might be best, but we don't have that yet. (Also see #38.)

I'm not sure if any of that is necessary right now, or if we should just wait until we have a more comprehensive configuration system. I don't have the need to disable reloading myself (and if I had, I'd rather fix whatever my issue is), so I don't know. Feedback would be very appreciated!

Render edges

Right now, only faces are rendered. And they are shaded according to their angle towards the camera.

This works very well, mostly, but if you're looking at the edge between two faces, such that both faces have exactly the same angle towards the camera, you can no longer make out the edge, and both faces look like one face.

It would be better to just render edges as a black line.

Provide basic user-facing documentation

There should be some basic user-facing documentation. It can be hosted on the website.

Eventually, this should be a comprehensive resource, but for a start, the following information should be enough:

  • What is Fornjot?
  • How to install it
  • How to get started
  • Further reading
    • API documentation of fj crate
    • Models in repository

Maybe an example of using it together with common tools, like VS Code and Git, would also be good.

Document vision for this project

It would be great for contributors and other interested parties to learn a bit more about the vision I have for this project. I've had lots of thoughts on that over the years, many of which have changed as I gained experience with the various prototypes, but haven't written that up in one place yet.

I'd like to collect notes in this issue over time. Once those notes are comprehensive, I'll flesh them out a bit and add that to the README, or possibly a new document.

So here my notes so far. Please note that a lot of that might be quite far off, or might change as things develop:

  • UI
    • Models defined as code: This is already the case, just making clear that this is not just a temporary step, but core part of the vision. I wrote an article about the advantages of Code-CAD, in case someone's interested in more context.
    • Comprehensive GUI for inspecting models: Other Code-CAD software I've dealt with is pretty basic. You only see the finished model, which can make it hard to change things about the model (you end up changing and commenting out code to see an early sketch, for example). See #13.
    • Hybrid UI for modifying models: Code-CAD is great, but a GUI is just better suited for some tasks. Creating sketches is one example. The code will be the source of truth, and editing it by hand will always be an option. But I'd like to provide GUI tools for specific editing tasks. Ideally, these GUI tools would modify the source code directly, but maybe having them modify specialized files that are then referenced in the source code is more practical. We'll see.
  • CAD kernel
    • I've experimented with function representation (also known as F-Rep, signed distance functions, signed distance fields, explicit functions) before, and have come to the conclusion that it isn't a good approach for this project (context). Right now, I'm experimenting with more traditional approaches based on boundary representation (B-Rep). There's still a lot to implement (and thick books to read), so it's possible this isn't the last word.
    • Constructive Solid Geometry: CSG is convenient for many models, so I'd like to support it. It seems reasonable to assume that it will be an initial target, to provide a full set of basic modeling capability.
    • Referencing existing geometry: CSG by itself is too limited. I think the ability to reference existing geometry, for example by drawing a sketch on an existing face to create a new feature there, is essential for many models.
    • Constraint-based modeling: I think supporting constraints in sketches is pretty much essential. It would be nice to also be able to constrain 3D geometry.
  • Platform support:
    • Desktop: I use Linux, but I'm happy to support any desktop platform where this is reasonably possible. This is going to depend on contributors doing the testing and maintenance for platforms I don't have access to (at this point, I only have access to Linux).
    • Web: I would absolutely like to support browsers as a platform. My own use case is to embed CAD models into websites, in a way that allows viewing, configuring, and exporting them. If anyone is willing to put in the work to support the full feature set (so including editing) on the web, I'm happy to support that.
    • Mobile: I don't have any plans to work on support for mobile platforms (except indirectly, via support for the web), but I'm happy to support such work.
  • Software Architecture
    • Modularity: My focus is to create an application I can use to create CAD models, and I probably won't put much work into making all of the code modular and reusable. If anyone has a use case that requires splitting the code into smaller, reusable libraries, I'm happy to accommodate that though.
    • WebAssembly: For web support, it's probably necessary to switch to WebAssembly for the plugin mechanism that is used to load models. WebAssembly also provides sandboxing, which is good for security. See #7.
    • Language support: Using WASM for the plugin system allows us to support other modeling languages. I have no plans to work on that, but would be happy to accommodate such work.
    • Custom modeling language: One thing I'm thinking about is to support a custom modeling language. It could be purpose-built for expressing CAD geometry, and be integrated with any GUI modeling features. I think this would require too much work to be practical any time soon though. Also see Kari, which I've created for an earlier iteration of Fornjot.

Please let me know, if anything here is unclear, of if there's an area you think I should cover.

Validate sketches

Right now, users can just feed any points they want to sketches, valid or not. This might then result in crashes or other weird issues.

Sketches should be validated before use. To keep the fj library light, this should probably be done in the host application. For now, it's fine if that results in a panic with an informative error message. At a later stage, it would be nice to display that error to the user in a friendlier way.

Specifically, validation should include the following checks:

  • Sketches must not have duplicate points.
  • Sketches must not be self-overlapping.
  • Sketches must have counter-clockwise winding.

Make `--model` parameter of host application more flexible

Currently, if you run fj-host -m my-model, it tries to find a model in models/my-model. This is convenient for use in this repository, but it is less convenient for other use cases.

Here's how I could see that being fixed:

  1. If no -m argument is passed and there is a Cargo package in the current directory, load that.
  2. If no -m arugment is passed and there is no Cargo package in the current directory, fall back to current behavior.
  3. If the parameter passed to -m is an existing path, attempt to load a model from there.

Labeling https://github.com/hannobraun/Fornjot/labels/good%20first%20issue, as this is a rather small change that doesn't require detailed knowledge of Fornjot. Some notes on the implementation:

I think it would make sense to add the new logic in model.rs.

Show focus point

The focus point is the point on the model (in model coordinates) that the mouse cursor points at. This point is used for input; for example when rotating the model, it is rotated around the focus point.

It would be nice to visualize this point, to show the user that it exists. Otherwise, rotation behavior might be confusing. Making the focus point visible could also help with debugging.

I don't know what approach would work best. Some ideas:

  1. Just draw a shape (maybe a circle or triangle) where the focus point is. This would likely not be the best way to visualize it, but it would be easy and help with debugging.
  2. Draw a shape, as in 1., but with circle size defined in the model coordinate system. That would help clarify that the shape represents something in the 3D world.
  3. Draw a shape, as in 2., but oriented such that its surface normal is parallel to the surface normal of the model at the focus point. This might help clarify that the shape relates to a point on the model.

Improve zooming/rotating/moving

The current input code doesn't work great. Rotating the model works fine, but zooming and moving are basically unusable, unless the model happens to fall into the right size range. Plus, the current zooming code is a bit of a failed experiment.

Furthermore, this kind of thing is a pain point in all of the open source CAD applications I've tried. Quite often, you want to see a feature you modeled up close, but maneuvering the camera where you need it is borderline impossible.

I've come up with the following concept, that I'd like to try out.

Basic ideas:

  • The camera is stationary. You're moving the model.
  • Zooming/rotating/moving are all influenced by where the mouse pointer touches the model when the operation begins. I'll call that the focus point from here on.

Zooming:

  • There is no 1:1 relation between mouse wheel or touch pad input and changes in zoom. Instead, zoom speed is dependent on input frequency.
  • If you turn the mouse wheel once, you get slow movement for a short time. Turn it faster, you get much faster movement. Not sure what the function mapping from frequency to speed should be, but maybe it shouldn't be linear. Something that allows precise control at low speeds, but also very high speeds.
  • The speed zooming in is limited by the distance to the focus point. Speed zooming out is not limited.

Moving:

  • Moving only takes place, if there is a focus point. If the mouse pointer doesn't point to the model, it isn't moved.
  • The focus point stays directly under the mouse pointer, regardless of zoom level.

Rotating:

  • The center of the rotation is always the focus point. This won't matter much when looking at the model from afar. But it will allow for very precise inspection of features up close, as you won't accidentally turn your point of interest far away while zoomed in.

Thoughts on implementation:

  • To find the focus point, we can shoot a ray from the camera and make an intersection test.
  • Could be a ray-triangle intersection, as we already have triangulation anyway, and don't need perfect accuracy for this.

Experiment with displaying background

The metaphor I'm going for with the input is that there is a stationary camera, in front of which the model is moved and rotated. If this is not understood, the controls might be confusing. People might assume that the camera is moving, and try to use the controls accordingly.

Displaying a stationary background far off in the distance could help make the metaphor obvious. I'm not sure if it's a good idea, and if so, how it should be implemented in detail, but maybe there's something to it. I image some far-off walls, as if we were in some huge room. Maybe that'll work.

Handle errors in a user-friendly way

Right now, errors in the CAD model, whether legitimate errors due to user input, internal errors due to unimplemented functionality, or bugs, most likely lead to a panic. This crashes the host application which is not user-friendly:

  • The user might want to fix the error with a simple change to the model, and now has to restart the host application, instead of the model reloading automatically.
  • Unless the user starts the host application from a terminal, they might never even see the error message, just a silent crash.

Here's what I think we should do:

  • Any CAD kernel code should handle all errors correctly by returning Results.
  • At the top level, those results should be turned into friendly error messages and shown to the user in the UI.

Issue #69 is related. This is blocked on #116.

Plane transformation test fails on GitHub Action's Windows runner

See test run. I've checked the error, and it looks like everything's working correctly, except that the epsilon value used to compare the actual and expected plane is too small. Not sure why, maybe something to do with Windows, maybe some weird Windows-specific behavior of approx, or the Windows CI runner runs different hardware that acts differently.

Whatever it is, it's probably just an issue of setting a different epsilon value in the test.

Implement union operation

It should be possible to create the union of two shapes, using constructive solid geometry (CSG). This is currently not supported, except for the simplest case, disjoint bodies, which is supported in the form of fj::Group.

Implementing this requires more solid infrastructure in the host application for storing and querying shapes. Also see #43 and #44.

Zooming is way too slow for large models

Zooming is way too slow with large models. With the enclosure model, zooming basically does nothing.

Right now, there's a given standard zoom speed that is limited by how close the model is, to make viewing details up-close practical. I think this should probably be changed to make zoom speed dependent on distance to the model. Basically, however far away you are, zoom speed should work comfortably, never being too fast or too slow.

If anyone wants to look into this, basically all the relevant code is in zoom.rs. Specifically the speed limiting code is interesting, and would have to be replaced when fixing this.

Model modifications happening during `fj-host` initialization could be missed

The initialization of the host application roughly looks like this:

  1. Load the model.
  2. Do a bunch of other stuff.
  3. Set up the mechanism that watches the model for changes.

If the model were to be modified during step 2., that change would be missed. I don't think this is very likely, and have certainly never seen it in real use, but none the less, there's a race condition here.

I haven't looked into this in a while, but last time I did, I got the impression that it isn't easy to fix, and probably a rework of that aspect of the architecture would be required.

In case anyone wants to take a look, here's where the model is loaded:

fornjot/src/main.rs

Lines 53 to 59 in 416df5f

// Since we're loading the model before setting up the watcher below,
// there's a race condition, and a modification could be missed between
// those two events.
//
// This can't be addressed with the current structure, since the watcher
// closure takes ownership of the model.
let shape = model.load(&parameters)?;

And here's where the watcher is set up:

fornjot/src/main.rs

Lines 123 to 163 in 416df5f

let watch_path = model.src_path();
let mut watcher = notify::recommended_watcher(
move |event: notify::Result<notify::Event>| {
// TASK: Figure out when this error can happen, find a better way to
// handle it.
let event = event.expect("Error handling watch event");
//Various acceptable ModifyKind kinds. Varies across platforms (e.g. MacOs vs. Windows10)
if let notify::EventKind::Modify(notify::event::ModifyKind::Any)
| notify::EventKind::Modify(notify::event::ModifyKind::Data(
notify::event::DataChange::Any,
))
| notify::EventKind::Modify(notify::event::ModifyKind::Data(
notify::event::DataChange::Content,
)) = event.kind
{
let shape = match model.load(&parameters) {
Ok(shape) => shape,
Err(model::Error::Compile) => {
// It would be better to display an error in the UI,
// where the user can actually see it. Issue:
// https://github.com/hannobraun/fornjot/issues/30
println!("Error compiling model");
return;
}
Err(err) => {
panic!("Error reloading model: {:?}", err);
}
};
// This will panic, if the other end is disconnected, which is
// probably the result of a panic on that thread, or the
// application is being shut down.
//
// Either way, not much we can do about it here, except maybe to
// provide a better error message in the future.
watcher_tx.send(shape).unwrap();
}
},
)?;
watcher.watch(&watch_path, notify::RecursiveMode::Recursive)?;

Begin unit testing

To be able to systematically narrow down the issue behind #12 I have found that I need start writing unit tests.
However if it is to early in development to begin doing tests, e.g. most or all will be obsolete in a short period of time, I will focus my energy on other issues for the time being.
@hannobraun are you comfortable with me beginning to include tests?

Display model parameters as modifiable UI elements

If a model specifies parameters, it should be possible to directly modify these parameters from the UI.

This issue is currently blocked on the following issues:

  • #116 - It would be better to figure out the future of our GUI first, before adding too many UI elements.
  • #72 - Right now, querying a model's parameters is not supported, so at least that much would have to be added.

Marching Directly

Have you considered rendering the model by sphere marching rather than building a mesh and using traditional rendering techniques? Doing such a thing could push much of the computation to the gpu speeding up the process and preserve the desirable aspects of signed distance field "iso surfaces" such as domain/boolean operations.

I stumbled upon your project while contemplating building a version of my own in order to model some things for 3d printing. I really like jscad but found it to be way too unstable I think in part because the boolean operations are all done on the mesh rather than some implicit function. In the past I've done rendering similar to Inigo Quilez' signed distance field ray tracing https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm and figured a similar approach could be used to make 3d prints so long as a good meshing algorithm could be used. Which led me here.

I'd be interested if you could elaborate on what is pushing you away from using distance fields in favor of "simpler" algorithms. They seem very appealing at first glance especially when compared to other existing cad approaches.

Changing the model file triggers multiple rebuilds

Changing the model file while the host is running can trigger multiple rebuilds. I'm seeing two on Linux, and don't know about other platforms.

This is only a minor annoyance, so it's not critical to fix this. It would be nicer though if every change triggered exactly one rebuild, to save unnecessary work. This might be possibly by choosing the events we listen for more carefully, or maybe some kind of de-duplicating feature is required.

This problem has been introduced in #28.

Consider turning camera towards mouse cursor when zooming

When zooming, the camera could turn towards the mouse cursor. Maybe immediately, maybe at a limited turning speed, to make it feel less jarring.

I think this could make zooming (and camera navigation in general) a bit more intuitive.

Figure out how to handle experimental features

Fornjot is still very experimental, and it's quite easy for a model to crash the host application. This is not a great user experience, obviously, but trying to be too strict at this early stage (by only accepting really solid code) would stifle progress.

I'd like to carve out a stable subset of features and make any experimental features opt-in somehow, so users know what they're getting into. I'm not yet sure how to do that.

  • One option is to add an experimental Cargo feature to the fj crate. This is not ideal, as models can depend on other models (they are just Cargo packages, after all). One component deep in the dependency tree could enable the experimental feature for everyone, making it easy to use experimental features accidentally.
  • Maybe it would be better to mark experimental features with an attribute that works somewhat like #[deprecated]. Then users would be warned when using experimental features, or could even add configuration to cause a compiler error in that case. Maybe it's possible to build something like this with a procedural macro. If not, it might be possible to misuse #[deprecated] for this purpose.
  • Another option is to add some kind of setting to the host application, that will result in models using experimental features to be rejected. I don't think this is viable though. A component might use experimental features in a controlled way that doesn't trigger any misbehavior. A model author might want to use such a component, without enabling use of experimental features in their own code.
  • Whether a model is allowed to use experimental code or not could be specified through a flag in the data structures models can return. Then use of experimental features could be checked in the host application. When a model uses a component from another package, the data structure for the component could still be present within the data structure for the model as a whole. This would give the host application enough information to allow/disallow use of experimental features in a fine-grained way.

If anyone has any ideas, I'd love to hear them! I'm adding the https://github.com/hannobraun/Fornjot/labels/help%20wanted to further encourage feedback.

If this issue doesn't result in a great plan, just adding an experimental feature to fj should do for a start.

Model is clipped when near to or far from the camera

When zooming in closely or far out, the the model is clipped. This happens because the near and far planes are at a constant distance from the camera. It would be better to set near and far plane distances so that the model is always fully visible.

I initially thought we could do this based on the bounding volume, but I realized this is not true. If the model is concave, the camera might be within the bounding volume, so we can't use the bounding volume to make decisions about the near plane in this case.

Maybe any of those are better solutions:

  • Use distance to the focus point.
  • Shoot one or more representative rays at the model.
  • Do collision detection between model and camera cone.

Publish host application on crates.io

Currently, only the fj crate is published on crates.io. But it would be convenient to be able to cargo install the host.

It would be even better, if all of those lived in the same crate, so you could cargo install fj to get the host (the command would just be called fj). However, I'm not sure how practical that is right now. The host contains a lot of code that the fj library shouldn't pick up, so it stays a light dependency.

Add integration test for triangle mesh quality

To prevent any regressions to the quality of the generated triangle meshes, it would be great to have some kind of test suite that builds a triangle mesh for each of the models in this repository, and checks that mesh for quality. Either by using parts of Fornjot as a library and checking internal data structures directly, or by exporting a 3MF/STL/... file and checking that.

This test suite wouldn't check if the generated mesh was correct for the specific model, but would just check the mesh itself for correctness:

  • Is the mesh manifold?
  • Are triangle normals consistent?
  • Are there duplicate or overlapping triangles?

And whatever other ways a mesh can be wrong (please comment, if you can think of another one).

Maybe there are libraries in the Rust ecosystem we could use for this, or maybe there's some suitable command-line tool. (I do know that slicers routinely do this kind of check, so maybe it's possible to use one from the command-line for this purpose. But a Rust library would be nicer, all else being equal.)

Triangulation algorithm can eliminate segments that are part of the face's boundary

The triangulation algorithm first runs a Delaunay triangulation, then filters out triangles that are not part of the face. It does this by running a point-in-polygon check of triangle edges, but it should never run this check for triangle edges that were generated directly from the face's edges.

triangulation-failure

This is debug output from a 2D version of the star model. It shows checks for face edges (marked blue), as well as one where there shouldn't even be a triangle edge (marked yellow).

I'm looking into it.

Computation of focus point is not fully correct

The focus point is the point on the model (in model coordinates) that the mouse cursor points at. This point is used for input, for example when rotating the model, it is rotated around the focus point.

This works mostly, but not fully. The focus point is not detected correctly when the cursor still points at the model, but just barely. Maybe the graphics and the focus point computations use slightly different perspectives, leading to some distortion. This also seems dependent on window size (or at least aspect ratio), as the effect is less pronounced when the window is square.

It might help to implement #17 first, to show what's going on.

Set up CI build

Set up a CI build, probably using GitHub Actions, that runs on all pushes to main and all pull requests.

Sanely handle no parameters being passed

If no parameters are passed, Fornjot it will try to load a default model from a models/ subdirectory. This is convenient during development, but makes no sense for end users.

The purpose of this issue is to solve this problem in a low-effort way that works for users that execute the binary without a terminal (as I'd like to have a solution within a finite time frame, and improvements can always come later). That probably means that just printing a message to stdout is out of the question, as that would just never be shown to the user on Linux, if they're not using a terminal in the first place (not sure about other platforms).

Ideas:

  • Open regular Fornjot UI without a model, display message that refers to the --model command-line argument. Not ideal, but at least puts users on the right track.
  • Open "load file" dialog before going into the Fornjot UI. If that can be done easily in a platform-independent way (using some library), then that is an option. See #117.
  • Integrate a "load file" dialog into the Fornjot UI. See #117.

Labeling as https://github.com/hannobraun/Fornjot/labels/help%20wanted, as I don't have a lot of desktop GUI expertise. So if someone has any other ideas, or can comment on how easy my ideas would be to implement, given the current state of the art in Rust GUIs, I'd appreciate it!

Reloading the model doesn't work on macOS

I've gotten a report that reloading the model doesn't work on MacOS. I'm just using Notify, so maybe it's an issue in that library.

I don't have a Mac, and no recent experience with MacOS, so any help here would be appreciated. As of this writing, the code for that lives in main.rs, line 56 and following, but it'll probably move somewhere else at some point (likely either model.rs or a dedicated module).

Hide platform-specific code behind a single API

There is some platform-specific code in model.rs. There's also the watch event handling code in main.rs. This is fine for now, but following this approach everywhere will get unwieldy.

I think the following concept would be better:

  • Create a new module, platforms, that contains a Platform trait.
  • This trait should define a minimal interface, such that all platform-specific code lives in implementations of that trait.
  • Otherwise those trait implementations should contain as little code as possible, to prevent duplication.
  • Avoid the use of #[cfg(...)] as much as practical. All code should be compiled on all platforms, to prevent errors from creeping in.
  • Use of cfg!(...) is fine, of course.

Implement difference operation

It should be possible to create the difference of two shapes, using constructive solid geometry (CSG). There is limited support for 2D differences, which is not directly related to this issue.

Implementing this requires more solid infrastructure for storing and querying shapes in the host application. Also see #42 and #44.

Remove `TASK`s from source code

Since starting work on this iteration of Fornjot, I've liberally sprinkled TASKs all over the source code, which is basically my version of TODO.

That was fine while I was working on Fornjot alone, as a hobby, but as things get more professional around here, I don't want to have a code base that is full of TODOs.

I have the following in mind for how to handle them:

  • Replace actual problems with an issue. Open an issue, convert the TASK into a regular comment referencing the issue.
  • If there's no clear problem, just convert them into regular comments.
  • If they add no value, for example next to a todo! (which is just as searchable), remove them.

Add UI element that displays orientation of model

Right now, after rotating the model for a bit, it can be hard to judge from where you're looking at it (unless the model has no symmetries, then you can judge from the model itself, of course).

At the minimum, we should have something like OpenSCAD: A simple representation of the x/y/z axes. Something like FreeCAD has, a cube with clearly marked "Front", "Right", "Top", etc. faces would be even better.

Once this is done, we can add interactivity, to allow resetting to specific perspectives. But just having the display would already be nice.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.