Skip to content

Commit 4b60115

Browse files
Allow reading stl from stdin
1 parent f88c735 commit 4b60115

File tree

5 files changed

+45
-35
lines changed

5 files changed

+45
-35
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ $ stl-thumb <STL_FILE> [IMG_FILE]
5858

5959
| Option | Description |
6060
| ------------- | ------------------------------------------------------- |
61-
| <STL_FILE> | The STL file you want a picture of. |
62-
| [IMG_FILE] | The thumbnail image file that will be created. If this is omitted, the image data will be dumped to stdout. |
61+
| <STL_FILE> | The STL file you want a picture of. Use - to read from stdin instead of a file. |
62+
| <IMG_FILE> | The thumbnail image file that will be created. Use - to write to stdout instead of a file. |
6363
| -s, --size \<size\> | Specify width of the image. It will always be a square. |
6464
| -f, --format \<format\> | The format of the image file. If not specified it will be determined from the file extension, or default to PNG if there is no extension. Supported formats: PNG, JPEG, GIF, ICO, BMP |
6565
| -m, --material \<ambient\> \<diffuse\> \<specular\> | Colors for rendering the mesh using the Phong reflection model. Requires 3 colors as rgb hex values: ambient, diffuse, and specular. Defaults to blue. |

src/config.rs

+8-12
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub enum AAMethod {
2020
#[derive(Clone)]
2121
pub struct Config {
2222
pub stl_filename: String,
23-
pub img_filename: Option<String>,
23+
pub img_filename: String,
2424
pub format: ImageOutputFormat,
2525
pub width: u32,
2626
pub height: u32,
@@ -35,7 +35,7 @@ impl Default for Config {
3535
fn default() -> Self {
3636
Config {
3737
stl_filename: "".to_string(),
38-
img_filename: None,
38+
img_filename: "".to_string(),
3939
format: ImageOutputFormat::Png,
4040
width: 1024,
4141
height: 768,
@@ -60,13 +60,14 @@ impl Config {
6060
.author(env!("CARGO_PKG_AUTHORS"))
6161
.arg(
6262
clap::Arg::new("STL_FILE")
63-
.help("STL file")
63+
.help("STL file. Use - to read from stdin instead of a file.")
6464
.required(true)
6565
.index(1),
6666
)
6767
.arg(
6868
clap::Arg::new("IMG_FILE")
69-
.help("Thumbnail image file. If this is omitted, the image data will be dumped to stdout.")
69+
.help("Thumbnail image file. Use - to write to stdout instead of a file.")
70+
.required(true)
7071
.index(2),
7172
)
7273
.arg(
@@ -125,16 +126,11 @@ impl Config {
125126
};
126127

127128
c.stl_filename = matches.value_of("STL_FILE").unwrap().to_string();
128-
matches
129-
.value_of("IMG_FILE")
130-
.map(|x| c.img_filename = Some(x.to_string()));
129+
c.img_filename = matches.value_of("IMG_FILE").unwrap().to_string();
131130
match matches.value_of("format") {
132131
Some(x) => c.format = match_format(x),
133-
None => match &c.img_filename {
134-
Some(x) => match Path::new(x).extension() {
135-
Some(ext) => c.format = match_format(ext.to_str().unwrap()),
136-
_ => (),
137-
},
132+
None => match Path::new(&c.img_filename).extension() {
133+
Some(ext) => c.format = match_format(ext.to_str().unwrap()),
138134
_ => (),
139135
},
140136
};

src/lib.rs

+7-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use libc::c_char;
2121
use mesh::Mesh;
2222
use std::error::Error;
2323
use std::ffi::CStr;
24-
use std::fs::File;
2524
use std::{io, panic, slice, thread, time};
2625

2726
#[cfg(target_os = "linux")]
@@ -268,9 +267,8 @@ where
268267

269268
pub fn render_to_window(config: Config) -> Result<(), Box<dyn Error>> {
270269
// Get geometry from STL file
271-
// =========================
272-
let stl_file = File::open(&config.stl_filename)?;
273-
let mesh = Mesh::from_stl(stl_file)?;
270+
// ==========================
271+
let mesh = Mesh::load(&config.stl_filename)?;
274272

275273
// Create GL context
276274
// =================
@@ -329,10 +327,7 @@ pub fn render_to_window(config: Config) -> Result<(), Box<dyn Error>> {
329327
pub fn render_to_image(config: &Config) -> Result<image::DynamicImage, Box<dyn Error>> {
330328
// Get geometry from STL file
331329
// =========================
332-
// TODO: Add support for URIs instead of plain file names
333-
// https://developer.gnome.org/integration-guide/stable/thumbnailer.html.en
334-
let stl_file = File::open(&config.stl_filename)?;
335-
let mesh = Mesh::from_stl(stl_file)?;
330+
let mesh = Mesh::load(&config.stl_filename)?;
336331

337332
let img: image::DynamicImage;
338333

@@ -382,9 +377,9 @@ pub fn render_to_file(config: &Config) -> Result<(), Box<dyn Error>> {
382377

383378
// Choose output
384379
// Write to stdout if user did not specify a file
385-
let mut output: Box<dyn io::Write> = match config.img_filename {
386-
Some(ref x) => Box::new(std::fs::File::create(&x).unwrap()),
387-
None => Box::new(io::stdout()),
380+
let mut output: Box<dyn io::Write> = match config.img_filename.as_str() {
381+
"-" => Box::new(io::stdout()),
382+
_ => Box::new(std::fs::File::create(&config.img_filename).unwrap()),
388383
};
389384

390385
// write_to() requires a seekable writer for performance reasons.
@@ -503,7 +498,7 @@ mod tests {
503498
let img_filename = "cube.png".to_string();
504499
let config = Config {
505500
stl_filename: "test_data/cube.stl".to_string(),
506-
img_filename: Some(img_filename.clone()),
501+
img_filename: img_filename.clone(),
507502
format: image::ImageOutputFormat::Png,
508503
..Default::default()
509504
};

src/main.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ fn main() {
2626
.unwrap();
2727

2828
info!("STL File: {}", config.stl_filename);
29-
match config.img_filename {
30-
Some(ref name) => info!("Thumbnail File: {}\n", &name),
31-
None => info!("Output: stdout\n"),
32-
};
29+
info!("IMG File: {}", config.img_filename);
3330

3431
if config.visible {
3532
if let Err(e) = stl_thumb::render_to_window(config) {

src/mesh.rs

+27-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ extern crate cgmath;
22
extern crate stl_io;
33

44
use std::error::Error;
5-
use std::fmt;
65
use std::fs::File;
6+
use std::io::{Cursor, Read, Seek};
7+
use std::{fmt, io};
78

89
#[derive(Copy, Clone)]
910
pub struct Vertex {
@@ -14,7 +15,7 @@ pub struct Vertex {
1415
implement_vertex!(Vertex, position);
1516
//implement_vertex!(Vertex, position, texcoords);
1617

17-
#[derive(Copy, Clone)]
18+
#[derive(Debug, Copy, Clone)]
1819
pub struct Normal {
1920
normal: [f32; 3],
2021
}
@@ -96,9 +97,30 @@ pub struct Mesh {
9697
}
9798

9899
impl Mesh {
99-
pub fn from_stl(mut stl_file: File) -> Result<Mesh, Box<dyn Error>> {
100-
//let stl = stl_io::read_stl(&mut stl_file)?;
101-
//debug!("{:?}", stl);
100+
// Load mesh data from file (if provided) or stdin
101+
pub fn load(stl_filename: &String) -> Result<Mesh, Box<dyn Error>> {
102+
// TODO: Add support for URIs instead of plain file names
103+
// https://developer.gnome.org/integration-guide/stable/thumbnailer.html.en
104+
match stl_filename.as_str() {
105+
"-" => {
106+
// create_stl_reader requires Seek, so we must read the entire stream into memory before proceeding.
107+
// So I guess this can just consume all RAM if it gets bad input. Hmmm....
108+
let mut input_buffer = Vec::new();
109+
io::stdin().read_to_end(&mut input_buffer)?;
110+
Mesh::from_stl(Cursor::new(input_buffer))
111+
}
112+
_ => {
113+
// TODO: Try BufReader and see if it's faster
114+
let stl_file = File::open(&stl_filename)?;
115+
Mesh::from_stl(stl_file)
116+
}
117+
}
118+
}
119+
120+
pub fn from_stl<R>(mut stl_file: R) -> Result<Mesh, Box<dyn Error>>
121+
where
122+
R: Read + Seek,
123+
{
102124
let mut stl_iter = stl_io::create_stl_reader(&mut stl_file)?;
103125

104126
// Get starting point for finding bounding box

0 commit comments

Comments
 (0)