Skip to content

Commit

Permalink
Merge pull request #594 from chrisprice/support-emf
Browse files Browse the repository at this point in the history
Support emf
  • Loading branch information
hannobraun authored May 17, 2022
2 parents 4d1a69d + 5d23fb9 commit 2aecf7c
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 12 deletions.
21 changes: 19 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ As of this writing, Fornjot runs on Linux, Windows, and macOS. The project is pr

Short- to mid-term, the plan is to add support for the web platform, so Fornjot can run in browsers. Long-term, the plan is to additionally support the major mobile platforms.

### Export to 3MF
### Export to 3MF & STL

Exporting models to the [3D Manufacturing Format](https://en.wikipedia.org/wiki/3D_Manufacturing_Format) (3MF), which is used in 3D printing, is supported.
Exporting models to both the [3D Manufacturing Format](https://en.wikipedia.org/wiki/3D_Manufacturing_Format) (3MF), which is used in 3D printing, and STL is supported.


## Usage
Expand Down Expand Up @@ -126,12 +126,14 @@ So far, the host application is not published on [crates.io](https://crates.io/)

### Exporting models

To export a model to a 3MF file, run:
To export a model to a file, run:

``` sh
cargo run -- -m spacer --export spacer.3mf
```

The file type is based on the supplied extension. Both 3MF and STL are supported.

### Model parameters

Some models have parameters that can be overridden. For example, to override the inner and outer radii of the spacer model:
Expand Down
2 changes: 2 additions & 0 deletions crates/fj-export/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ categories = ["encoding", "mathematics", "rendering"]


[dependencies]
anyhow = "1.0.57"
threemf = "0.3.0"
stl = "0.2.1"

[dependencies.fj-interop]
version = "0.6.0"
Expand Down
86 changes: 79 additions & 7 deletions crates/fj-export/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,35 @@
#![deny(missing_docs)]

use std::path::Path;
use std::{fs::File, path::Path};

use anyhow::{anyhow, Result};

use fj_interop::mesh::Mesh;
use fj_math::Point;
use fj_math::{Point, Triangle, Vector};

/// Export the provided mesh to the file at the given path
/// Export the provided mesh to the file at the given path.
///
/// This function will create a file if it does not exist, and will truncate it if it does.
///
/// Currently only 3MF is supported as an export format. The file extension of
/// the provided path is ignored.
pub fn export(mesh: &Mesh<Point<3>>, path: &Path) -> Result<(), Error> {
/// Currently 3MF & STL file types are supported. The case insensitive file extension of
/// the provided path is used to switch between supported types.
pub fn export(mesh: &Mesh<Point<3>>, path: &Path) -> Result<()> {
match path.extension() {
Some(extension) if extension.to_ascii_uppercase() == "3MF" => {
export_3mf(mesh, path)
}
Some(extension) if extension.to_ascii_uppercase() == "STL" => {
export_stl(mesh, path)
}
Some(extension) => {
Err(anyhow!("Extension not recognised, got {:?}", extension))
}
None => Err(anyhow!("No extension specified")),
}
}

fn export_3mf(mesh: &Mesh<Point<3>>, path: &Path) -> Result<()> {
let vertices = mesh.vertices().map(|vertex| vertex.into()).collect();

let indices: Vec<_> = mesh.indices().collect();
Expand All @@ -48,4 +67,57 @@ pub fn export(mesh: &Mesh<Point<3>>, path: &Path) -> Result<(), Error> {
Ok(())
}

pub use threemf::Error;
fn export_stl(mesh: &Mesh<Point<3>>, path: &Path) -> Result<()> {
let points = mesh
.triangles()
.map(|triangle| triangle.points)
.collect::<Vec<_>>();

let vertices = points.iter().map(|points| {
points.map(|point| {
[point.x.into_f32(), point.y.into_f32(), point.z.into_f32()]
})
});

let normals = points
.iter()
.map(|&points| points.into())
.map(|triangle: Triangle<3>| triangle.to_parry().normal())
.collect::<Option<Vec<_>>>()
.ok_or_else(|| anyhow!("Unable to compute normal"))?;

let normals = normals.iter().map(|vector| vector.into_inner().into()).map(
|vector: Vector<3>| {
[
vector.x.into_f32(),
vector.y.into_f32(),
vector.z.into_f32(),
]
},
);

let triangles = vertices
.zip(normals)
.map(|([v1, v2, v3], normal)| stl::Triangle {
normal,
v1,
v2,
v3,
attr_byte_count: 0,
})
.collect::<Vec<_>>();

let mut file = File::create(path)?;

let binary_stl_file = stl::BinaryStlFile {
header: stl::BinaryStlHeader {
header: [0u8; 80],
num_triangles: triangles.len().try_into()?,
},
triangles,
};

stl::write_stl(&mut file, &binary_stl_file)?;

Ok(())
}

0 comments on commit 2aecf7c

Please sign in to comment.