├── .travis.yml ├── LICENSE ├── Readme.md ├── docs ├── annotated.html ├── bc_s.png ├── bc_sd.png ├── bdwn.png ├── classes.html ├── classstl__reader_1_1StlMesh-members.html ├── classstl__reader_1_1StlMesh.html ├── closed.png ├── doc.png ├── docd.png ├── doxygen.css ├── doxygen.svg ├── doxygen_config │ └── stl_reader.dconf ├── dynsections.js ├── files.html ├── folderclosed.png ├── folderopen.png ├── functions.html ├── functions_func.html ├── globals.html ├── globals_defs.html ├── graph_legend.dot ├── graph_legend.html ├── index.html ├── jquery.js ├── menu.js ├── menudata.js ├── nav_f.png ├── nav_fd.png ├── nav_g.png ├── nav_h.png ├── nav_hd.png ├── open.png ├── splitbar.png ├── splitbard.png ├── stl__reader_8h.html ├── stl__reader_8h_source.html ├── sync_off.png ├── sync_on.png ├── tab_a.png ├── tab_ad.png ├── tab_b.png ├── tab_bd.png ├── tab_h.png ├── tab_hd.png ├── tab_s.png ├── tab_sd.png └── tabs.css ├── run_doxygen.sh ├── stl_reader.h └── tests ├── CMakeLists.txt ├── data ├── ascii_sphere.stl └── binary_sphere.stl ├── read_stl.t.cpp ├── remove_doubles.t.cpp ├── utils.cpp └── utils.h /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: gcc 3 | dist: focal 4 | 5 | script: 6 | - mkdir -p build 7 | - cd build 8 | - cmake ../tests 9 | - make -j 10 | - ./stl_reader_tests 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2023, Sebastian Reiter (s.b.reiter@gmail.com) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # stl_reader      [![Build Status](https://app.travis-ci.com/sreiter/stl_reader.svg?branch=master)](https://app.travis-ci.com/github/sreiter/stl_reader) 2 | 3 | ## Introduction 4 | **stl_reader** is a *BSD* licensed single header C++ library providing functions to read **stl geometry files** into user provided containers. It should be easy to integrate **stl_reader** into existing code. It has no external dependencies except for the *standard template library*. 5 | 6 | The central function of **stl_reader** is `ReadStlFile(...)`. It automatically recognizes whether an *ASCII* or a *binary* file is to be read. Furthermore, it identifies matching corner coordinates of triangles with each other, so that the resulting coordinate array does not contain the same coordinate-triple multiple times. 7 | 8 | The function operates on template container types. Those containers should have a similar interface as `std::vector` and operate on `float` or `double` types (`TNumberContainer`) or on `int` or `size_t` types (`TIndexContainer`). 9 | 10 | A convenience class `StlMesh` is also provided, which makes accessing triangle corners and corresponding corner coordinates much more easy. It still provides raw access to the underlying data arrays. 11 | 12 | ## Documentation 13 | Please have a look at the [**stl_reader.h file documentation**](http://sreiter.github.io/stl_reader/stl__reader_8h.html) for a detailed documentation of the provided functions. 14 | 15 | ## Usage 16 | 17 | ### Usage example 1 (using `StlMesh`): 18 | 19 | ```{.c} 20 | try { 21 | stl_reader::StlMesh mesh ("geometry.stl"); 22 | 23 | for(size_t itri = 0; itri < mesh.num_tris(); ++itri) { 24 | std::cout << "coordinates of triangle " << itri << ": "; 25 | for(size_t icorner = 0; icorner < 3; ++icorner) { 26 | const float* c = mesh.tri_corner_coords (itri, icorner); 27 | // or alternatively: 28 | // float* c = mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner)); 29 | std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") "; 30 | } 31 | std::cout << std::endl; 32 | 33 | float* n = mesh.tri_normal (itri); 34 | std::cout << "normal of triangle " << itri << ": " 35 | << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n"; 36 | } 37 | } 38 | catch (std::exception& e) { 39 | std::cout << e.what() << std::endl; 40 | } 41 | ``` 42 | 43 | ### Usage example 2 (using `StlMesh` and *solids*) 44 | 45 | ```{.c} 46 | try { 47 | stl_reader::StlMesh mesh ("geometry.stl"); 48 | 49 | for(size_t isolid = 0; isolid < mesh.num_solids(); ++isolid) { 50 | std::cout << "solid " << isolid << std::endl; 51 | 52 | for(size_t itri = mesh.solid_tris_begin(isolid); 53 | itri < mesh.solid_tris_end(isolid); ++itri) 54 | { 55 | const float* n = mesh.tri_normal (itri); 56 | std::cout << "normal of triangle " << itri << ": " 57 | << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n"; 58 | } 59 | } 60 | } 61 | catch (std::exception& e) { 62 | std::cout << e.what() << std::endl; 63 | } 64 | ``` 65 | 66 | ### Usage example 3 (using raw data arrays) 67 | 68 | ```{.c} 69 | std::vector coords, normals; 70 | std::vector tris, solids; 71 | 72 | try { 73 | stl_reader::ReadStlFile ("geometry.stl", coords, normals, tris, solids); 74 | const size_t numTris = tris.size() / 3; 75 | for(size_t itri = 0; itri < numTris; ++itri) { 76 | std::cout << "coordinates of triangle " << itri << ": "; 77 | for(size_t icorner = 0; icorner < 3; ++icorner) { 78 | float* c = &coords[3 * tris [3 * itri + icorner]]; 79 | std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") "; 80 | } 81 | std::cout << std::endl; 82 | 83 | float* n = &normals [3 * itri]; 84 | std::cout << "normal of triangle " << itri << ": " 85 | << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n"; 86 | } 87 | } 88 | catch (std::exception& e) { 89 | std::cout << e.what() << std::endl; 90 | } 91 | ``` 92 | 93 | **Note:** If you do not want to use exceptions, you may define the macro STL_READER_NO_EXCEPTIONS before including 'stl_reader.h'. In that case, functions will return `false` if an error occurred. 94 | 95 | ## License 96 | **stl_reader** is licensed under a *2-clause BSD* license: 97 | 98 | Copyright (c) 2018-2023, Sebastian Reiter (s.b.reiter@gmail.com) 99 | All rights reserved. 100 | 101 | Redistribution and use in source and binary forms, with or without 102 | modification, are permitted provided that the following conditions are met: 103 | * Redistributions of source code must retain the above copyright 104 | notice, this list of conditions and the following disclaimer. 105 | * Redistributions in binary form must reproduce the above copyright 106 | notice, this list of conditions and the following disclaimer in the 107 | documentation and/or other materials provided with the distribution. 108 | 109 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 110 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 111 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 112 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 113 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 114 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 115 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 116 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 117 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 118 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 119 | 120 | 121 | -------------------------------------------------------------------------------- /docs/annotated.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: Class List 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |
Class List
43 |
44 |
45 |
Here are the classes, structs, unions and interfaces with brief descriptions:
46 |
[detail level 12]
47 | 48 | 49 |
 Nstl_reader
 CStlMeshConvenience mesh class which makes accessing the stl data more easy
50 |
51 |
52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /docs/bc_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/bc_s.png -------------------------------------------------------------------------------- /docs/bc_sd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/bc_sd.png -------------------------------------------------------------------------------- /docs/bdwn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/bdwn.png -------------------------------------------------------------------------------- /docs/classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: Class Index 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |
Class Index
43 |
44 |
45 | 46 |
47 |
48 |
S
49 |
StlMesh (stl_reader)
50 |
51 |
52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /docs/classstl__reader_1_1StlMesh-members.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: Member List 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 | 44 |
45 |
46 |
stl_reader::StlMesh< TNumber, TIndex > Member List
47 |
48 |
49 | 50 |

This is the complete list of members for stl_reader::StlMesh< TNumber, TIndex >, including all inherited members.

51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
num_solids() conststl_reader::StlMesh< TNumber, TIndex >inline
num_tris() conststl_reader::StlMesh< TNumber, TIndex >inline
num_vrts() conststl_reader::StlMesh< TNumber, TIndex >inline
raw_coords() conststl_reader::StlMesh< TNumber, TIndex >inline
raw_normals() conststl_reader::StlMesh< TNumber, TIndex >inline
raw_solids() conststl_reader::StlMesh< TNumber, TIndex >inline
raw_tris() conststl_reader::StlMesh< TNumber, TIndex >inline
read_file(const char *filename)stl_reader::StlMesh< TNumber, TIndex >inline
solid_tris_begin(const size_t si) conststl_reader::StlMesh< TNumber, TIndex >inline
solid_tris_end(const size_t si) conststl_reader::StlMesh< TNumber, TIndex >inline
StlMesh()stl_reader::StlMesh< TNumber, TIndex >inline
StlMesh(const char *filename)stl_reader::StlMesh< TNumber, TIndex >inline
tri_corner_coords(const size_t ti, const size_t ci) conststl_reader::StlMesh< TNumber, TIndex >inline
tri_corner_ind(const size_t ti, const size_t ci) conststl_reader::StlMesh< TNumber, TIndex >inline
tri_corner_inds(const size_t ti) conststl_reader::StlMesh< TNumber, TIndex >inline
tri_normal(const size_t ti) conststl_reader::StlMesh< TNumber, TIndex >inline
vrt_coords(const size_t vi) conststl_reader::StlMesh< TNumber, TIndex >inline
70 | 71 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/classstl__reader_1_1StlMesh.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: stl_reader::StlMesh< TNumber, TIndex > Class Template Reference 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 | 44 |
45 |
46 | 49 |
stl_reader::StlMesh< TNumber, TIndex > Class Template Reference
50 |
51 |
52 | 53 |

convenience mesh class which makes accessing the stl data more easy 54 | More...

55 | 56 |

#include <stl_reader.h>

57 | 58 | 60 | 62 | 63 | 64 | 66 | 67 | 68 | 70 | 71 | 72 | 74 | 75 | 76 | 78 | 79 | 80 | 82 | 83 | 84 | 85 | 86 | 87 | 89 | 90 | 91 | 92 | 93 | 94 | 96 | 97 | 98 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 117 | 118 | 119 | 120 | 122 | 123 | 124 |

59 | Public Member Functions

61 |  StlMesh ()
 initializes an empty mesh
 
65 | size_t num_vrts () const
 returns the number of vertices in the mesh
 
69 | const TNumber * vrt_coords (const size_t vi) const
 returns an array of 3 floating point values, one for each coordinate of the vertex
 
73 | size_t num_tris () const
 returns the number of triangles in the mesh
 
77 | const TIndex * tri_corner_inds (const size_t ti) const
 returns an array of 3 indices, one for each corner vertex of the triangle
 
81 | const TIndex tri_corner_ind (const size_t ti, const size_t ci) const
 returns the index of the corner with index 0<=ci<3 of triangle ti
 
const TNumber * tri_corner_coords (const size_t ti, const size_t ci) const
 returns an array of 3 floating point values, one for each coordinate of the specified corner of the specified tri.
 
88 | const TNumber * tri_normal (const size_t ti) const
 returns an array of 3 floating point values defining the normal of a tri
 
size_t num_solids () const
 returns the number of solids of the mesh
 
95 | TIndex solid_tris_begin (const size_t si) const
 returns the index of the first triangle in the given solid
 
99 | TIndex solid_tris_end (const size_t si) const
 returns the index of the triangle behind the last triangle in the given solid
 
const TNumber * raw_coords () const
 returns a pointer to the coordinate array, containing num_vrts()*3 entries.
 
const TNumber * raw_normals () const
 returns a pointer to the normal array, containing num_tris()*3 entries.
 
const TIndex * raw_tris () const
 returns a pointer to the triangle array, containing num_tris()*3 entries.
 
const TIndex * raw_solids () const
 returns a pointer to the solids array, containing num_solids()+1 entries.
 
116 |  StlMesh (const char *filename)
 initializes the mesh from the stl-file specified through filename
 
121 | bool read_file (const char *filename)
 fills the mesh with the contents of the specified stl-file
 
125 |

Detailed Description

126 |
template<class TNumber = float, class TIndex = unsigned int>
127 | class stl_reader::StlMesh< TNumber, TIndex >

convenience mesh class which makes accessing the stl data more easy

128 |

Member Function Documentation

129 | 130 |

◆ tri_corner_coords()

131 | 132 |
133 |
134 |
135 | template<class TNumber = float, class TIndex = unsigned int>
136 | 137 | 138 | 159 | 161 | 162 |
139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
const TNumber * stl_reader::StlMesh< TNumber, TIndex >::tri_corner_coords (const size_t ti,
const size_t ci 
) const
158 |
160 | inline
163 |
164 | 165 |

returns an array of 3 floating point values, one for each coordinate of the specified corner of the specified tri.

166 |
Note
same result as calling on a StlMesh mesh:
mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner))
167 |
168 | 169 |
170 |
171 | 172 |

◆ num_solids()

173 | 174 |
175 |
176 |
177 | template<class TNumber = float, class TIndex = unsigned int>
178 | 179 | 180 | 190 | 192 | 193 |
181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 |
size_t stl_reader::StlMesh< TNumber, TIndex >::num_solids () const
189 |
191 | inline
194 |
195 | 196 |

returns the number of solids of the mesh

197 |

solids can be seen as a partitioning of the triangles of a mesh. By iterating consecutively from the index of the first triangle of a solid si (using solid_tris_begin(si)) to the index of the last triangle of a solid (using solid_tris_end(...)-1), one visits all triangles of the solid si.

198 | 199 |
200 |
201 | 202 |

◆ raw_coords()

203 | 204 |
205 |
206 |
207 | template<class TNumber = float, class TIndex = unsigned int>
208 | 209 | 210 | 220 | 222 | 223 |
211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 |
const TNumber * stl_reader::StlMesh< TNumber, TIndex >::raw_coords () const
219 |
221 | inline
224 |
225 | 226 |

returns a pointer to the coordinate array, containing num_vrts()*3 entries.

227 |

Storage layout: x0,y0,z0,x1,y1,z1,...

Returns
pointer to a contiguous array of numbers, or NULL if no coords exist.
228 | 229 |
230 |
231 | 232 |

◆ raw_normals()

233 | 234 |
235 |
236 |
237 | template<class TNumber = float, class TIndex = unsigned int>
238 | 239 | 240 | 250 | 252 | 253 |
241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 |
const TNumber * stl_reader::StlMesh< TNumber, TIndex >::raw_normals () const
249 |
251 | inline
254 |
255 | 256 |

returns a pointer to the normal array, containing num_tris()*3 entries.

257 |

Storage layout: nx0,ny0,nz0,nx1,ny1,nz1,...

Returns
pointer to a contiguous array of numbers, or NULL if no normals exist.
258 | 259 |
260 |
261 | 262 |

◆ raw_tris()

263 | 264 |
265 |
266 |
267 | template<class TNumber = float, class TIndex = unsigned int>
268 | 269 | 270 | 280 | 282 | 283 |
271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 |
const TIndex * stl_reader::StlMesh< TNumber, TIndex >::raw_tris () const
279 |
281 | inline
284 |
285 | 286 |

returns a pointer to the triangle array, containing num_tris()*3 entries.

287 |

Storage layout: t0c0,t0c1,t0c2,t1c0,t1c1,t1c2,...

Returns
pointer to a contiguous array of indices, or NULL if no tris exist.
288 | 289 |
290 |
291 | 292 |

◆ raw_solids()

293 | 294 |
295 |
296 |
297 | template<class TNumber = float, class TIndex = unsigned int>
298 | 299 | 300 | 310 | 312 | 313 |
301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 |
const TIndex * stl_reader::StlMesh< TNumber, TIndex >::raw_solids () const
309 |
311 | inline
314 |
315 | 316 |

returns a pointer to the solids array, containing num_solids()+1 entries.

317 |

Storage layout: s0begin, s0end/s1begin, s1end/s2begin, ..., sNend

Returns
pointer to a contiguous array of indices, or NULL if no solids exist.
318 | 319 |
320 |
321 |
The documentation for this class was generated from the following file: 324 |
325 | 326 | 329 | 330 | 331 | -------------------------------------------------------------------------------- /docs/closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/closed.png -------------------------------------------------------------------------------- /docs/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/doc.png -------------------------------------------------------------------------------- /docs/docd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/docd.png -------------------------------------------------------------------------------- /docs/doxygen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/dynsections.js: -------------------------------------------------------------------------------- 1 | /* 2 | @licstart The following is the entire license notice for the JavaScript code in this file. 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (C) 1997-2020 by Dimitri van Heesch 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 9 | and associated documentation files (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 11 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all copies or 15 | substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 18 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | @licend The above is the entire license notice for the JavaScript code in this file 24 | */ 25 | function toggleVisibility(linkObj) 26 | { 27 | var base = $(linkObj).attr('id'); 28 | var summary = $('#'+base+'-summary'); 29 | var content = $('#'+base+'-content'); 30 | var trigger = $('#'+base+'-trigger'); 31 | var src=$(trigger).attr('src'); 32 | if (content.is(':visible')===true) { 33 | content.hide(); 34 | summary.show(); 35 | $(linkObj).addClass('closed').removeClass('opened'); 36 | $(trigger).attr('src',src.substring(0,src.length-8)+'closed.png'); 37 | } else { 38 | content.show(); 39 | summary.hide(); 40 | $(linkObj).removeClass('closed').addClass('opened'); 41 | $(trigger).attr('src',src.substring(0,src.length-10)+'open.png'); 42 | } 43 | return false; 44 | } 45 | 46 | function updateStripes() 47 | { 48 | $('table.directory tr'). 49 | removeClass('even').filter(':visible:even').addClass('even'); 50 | $('table.directory tr'). 51 | removeClass('odd').filter(':visible:odd').addClass('odd'); 52 | } 53 | 54 | function toggleLevel(level) 55 | { 56 | $('table.directory tr').each(function() { 57 | var l = this.id.split('_').length-1; 58 | var i = $('#img'+this.id.substring(3)); 59 | var a = $('#arr'+this.id.substring(3)); 60 | if (l 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: File List 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |
File List
43 |
44 |
45 |
Here is a list of all documented files with brief descriptions:
46 | 47 | 48 |
 stl_reader.hProvides functions to read stl files into user provided arrays
49 |
50 |
51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /docs/folderclosed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/folderclosed.png -------------------------------------------------------------------------------- /docs/folderopen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/folderopen.png -------------------------------------------------------------------------------- /docs/functions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: Class Members 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |
Here is a list of all documented class members with links to the class documentation for each member:
60 |
61 | 62 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/functions_func.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: Class Members - Functions 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 | 61 | 62 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: File Members 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |
Here is a list of all documented file members with links to the documentation:
46 |
47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/globals_defs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: File Members 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |   46 |
47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/graph_legend.dot: -------------------------------------------------------------------------------- 1 | digraph "Graph Legend" 2 | { 3 | // LATEX_PDF_SIZE 4 | bgcolor="transparent"; 5 | edge [fontname=Helvetica,fontsize=10,labelfontname=Helvetica,labelfontsize=10]; 6 | node [fontname=Helvetica,fontsize=10,shape=box,height=0.2,width=0.4]; 7 | Node9 [label="Inherited",height=0.2,width=0.4,color="gray40", fillcolor="grey60", style="filled", fontcolor="black",tooltip=" "]; 8 | Node10 -> Node9 [dir="back",color="steelblue1",style="solid"]; 9 | Node10 [label="PublicBase",height=0.2,width=0.4,color="grey40", fillcolor="white", style="filled",URL="url.html",tooltip=" "]; 10 | Node11 -> Node10 [dir="back",color="steelblue1",style="solid"]; 11 | Node11 [label="Truncated",height=0.2,width=0.4,color="red", fillcolor="#FFF0F0", style="filled",URL="url.html",tooltip=" "]; 12 | Node13 -> Node9 [dir="back",color="darkgreen",style="solid"]; 13 | Node13 [label="ProtectedBase",color="gray40",fillcolor="white",style="filled"]; 14 | Node14 -> Node9 [dir="back",color="firebrick4",style="solid"]; 15 | Node14 [label="PrivateBase",color="gray40",fillcolor="white",style="filled"]; 16 | Node15 -> Node9 [dir="back",color="steelblue1",style="solid"]; 17 | Node15 [label="Undocumented",height=0.2,width=0.4,color="grey60", fillcolor="#E0E0E0", style="filled",tooltip=" "]; 18 | Node16 -> Node9 [dir="back",color="steelblue1",style="solid"]; 19 | Node16 [label="Templ\< int \>",color="gray40",fillcolor="white",style="filled"]; 20 | Node17 -> Node16 [dir="back",color="orange",style="dashed",label="< int >",]; 21 | Node17 [label="Templ\< T \>",color="gray40",fillcolor="white",style="filled"]; 22 | Node18 -> Node9 [dir="back",color="darkorchid3",style="dashed",label="m_usedClass",]; 23 | Node18 [label="Used",color="gray40",fillcolor="white",style="filled"]; 24 | } 25 | -------------------------------------------------------------------------------- /docs/graph_legend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: Graph Legend 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |
Graph Legend
43 |
44 |
45 |

This page explains how to interpret the graphs that are generated by doxygen.

46 |

Consider the following example:

/*! Invisible class because of truncation */
47 |
class Invisible { };
48 |
49 |
/*! Truncated class, inheritance relation is hidden */
50 |
class Truncated : public Invisible { };
51 |
52 |
/* Class not documented with doxygen comments */
53 |
class Undocumented { };
54 |
55 |
/*! Class that is inherited using public inheritance */
56 |
class PublicBase : public Truncated { };
57 |
58 |
/*! A template class */
59 |
template<class T> class Templ { };
60 |
61 |
/*! Class that is inherited using protected inheritance */
62 |
class ProtectedBase { };
63 |
64 |
/*! Class that is inherited using private inheritance */
65 |
class PrivateBase { };
66 |
67 |
/*! Class that is used by the Inherited class */
68 |
class Used { };
69 |
70 |
/*! Super class that inherits a number of other classes */
71 |
class Inherited : public PublicBase,
72 |
protected ProtectedBase,
73 |
private PrivateBase,
74 |
public Undocumented,
75 |
public Templ<int>
76 |
{
77 |
private:
78 |
Used *m_usedClass;
79 |
};
80 |

This will result in the following graph:

81 |

The boxes in the above graph have the following meaning:

82 |
    83 |
  • 84 | A filled gray box represents the struct or class for which the graph is generated.
  • 85 |
  • 86 | A box with a black border denotes a documented struct or class.
  • 87 |
  • 88 | A box with a gray border denotes an undocumented struct or class.
  • 89 |
  • 90 | A box with a red border denotes a documented struct or class forwhich not all inheritance/containment relations are shown. A graph is truncated if it does not fit within the specified boundaries.
  • 91 |
92 |

The arrows have the following meaning:

93 |
    94 |
  • 95 | A blue arrow is used to visualize a public inheritance relation between two classes.
  • 96 |
  • 97 | A dark green arrow is used for protected inheritance.
  • 98 |
  • 99 | A dark red arrow is used for private inheritance.
  • 100 |
  • 101 | A purple dashed arrow is used if a class is contained or used by another class. The arrow is labelled with the variable(s) through which the pointed class or struct is accessible.
  • 102 |
  • 103 | A yellow dashed arrow denotes a relation between a template instance and the template class it was instantiated from. The arrow is labelled with the template parameters of the instance.
  • 104 |
105 |
106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: Introduction 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 24 | 25 | 26 |
21 |
stl_reader 22 |
23 |
27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 |
41 |
42 |
Introduction
43 |
44 |
45 |

stl_reader is a BSD licensed single header C++ library providing functions to read stl geometry files into user provided containers. It should be easy to integrate stl_reader into existing code. It has no external dependencies except for the standard template library.

46 |

The central function of stl_reader is ReadStlFile(...). It automatically recognizes whether an ASCII or a binary file is to be read. Furthermore, it identifies matching corner coordinates of triangles with each other, so that the resulting coordinate array does not contain the same coordinate-triple multiple times.

47 |

The function operates on template container types. Those containers should have a similar interface as std::vector and operate on float or double types (TNumberContainer) or on int or size_t types (TIndexContainer).

48 |

A convenience class StlMesh is also provided, which makes accessing triangle corners and corresponding corner coordinates much more easy. It still provides raw access to the underlying data arrays.

49 |

50 | Documentation

51 |

Please have a look at the stl_reader.h file documentation for a detailed documentation of the provided functions.

52 |

53 | Usage

54 |

55 | Usage example 1 (using <tt>StlMesh</tt>):

56 |
try {
57 |
stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
58 |
59 |
for(size_t itri = 0; itri < mesh.num_tris(); ++itri) {
60 |
std::cout << "coordinates of triangle " << itri << ": ";
61 |
for(size_t icorner = 0; icorner < 3; ++icorner) {
62 |
const float* c = mesh.tri_corner_coords (itri, icorner);
63 |
// or alternatively:
64 |
// float* c = mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner));
65 |
std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
66 |
}
67 |
std::cout << std::endl;
68 |
69 |
float* n = mesh.tri_normal (itri);
70 |
std::cout << "normal of triangle " << itri << ": "
71 |
<< "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
72 |
}
73 |
}
74 |
catch (std::exception& e) {
75 |
std::cout << e.what() << std::endl;
76 |
}
77 |

78 | Usage example 2 (using <tt>StlMesh</tt> and <em>solids</em>)

79 |
try {
80 |
stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
81 |
82 |
for(size_t isolid = 0; isolid < mesh.num_solids(); ++isolid) {
83 |
std::cout << "solid " << isolid << std::endl;
84 |
85 |
for(size_t itri = mesh.solid_tris_begin(isolid);
86 |
itri < mesh.solid_tris_end(isolid); ++itri)
87 |
{
88 |
const float* n = mesh.tri_normal (itri);
89 |
std::cout << "normal of triangle " << itri << ": "
90 |
<< "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
91 |
}
92 |
}
93 |
}
94 |
catch (std::exception& e) {
95 |
std::cout << e.what() << std::endl;
96 |
}
97 |

98 | Usage example 3 (using raw data arrays)

99 |
std::vector<float> coords, normals;
100 |
std::vector<unsigned int> tris, solids;
101 |
102 |
try {
103 |
stl_reader::ReadStlFile ("geometry.stl", coords, normals, tris, solids);
104 |
const size_t numTris = tris.size() / 3;
105 |
for(size_t itri = 0; itri < numTris; ++itri) {
106 |
std::cout << "coordinates of triangle " << itri << ": ";
107 |
for(size_t icorner = 0; icorner < 3; ++icorner) {
108 |
float* c = &coords[3 * tris [3 * itri + icorner]];
109 |
std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
110 |
}
111 |
std::cout << std::endl;
112 |
113 |
float* n = &normals [3 * itri];
114 |
std::cout << "normal of triangle " << itri << ": "
115 |
<< "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
116 |
}
117 |
}
118 |
catch (std::exception& e) {
119 |
std::cout << e.what() << std::endl;
120 |
}
121 |

Note: If you do not want to use exceptions, you may define the macro STL_READER_NO_EXCEPTIONS before including 'stl_reader.h'. In that case, functions will return false if an error occurred.

122 |

123 | License

124 |

stl_reader is licensed under a 2-clause BSD license:

Copyright (c) 2018-2023, Sebastian Reiter (s.b.reiter@gmail.com)
125 | All rights reserved.
126 | 
127 | Redistribution and use in source and binary forms, with or without
128 | modification, are permitted provided that the following conditions are met:
129 |     * Redistributions of source code must retain the above copyright
130 |       notice, this list of conditions and the following disclaimer.
131 |     * Redistributions in binary form must reproduce the above copyright
132 |       notice, this list of conditions and the following disclaimer in the
133 |       documentation and/or other materials provided with the distribution.
134 | 
135 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
136 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
137 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
138 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
139 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
140 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
141 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
142 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
143 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
144 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
145 | 
146 |
147 | 148 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /docs/menu.js: -------------------------------------------------------------------------------- 1 | /* 2 | @licstart The following is the entire license notice for the JavaScript code in this file. 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (C) 1997-2020 by Dimitri van Heesch 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 9 | and associated documentation files (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 11 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all copies or 15 | substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 18 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | @licend The above is the entire license notice for the JavaScript code in this file 24 | */ 25 | function initMenu(relPath,searchEnabled,serverSide,searchPage,search) { 26 | function makeTree(data,relPath) { 27 | var result=''; 28 | if ('children' in data) { 29 | result+='
    '; 30 | for (var i in data.children) { 31 | var url; 32 | var link; 33 | link = data.children[i].url; 34 | if (link.substring(0,1)=='^') { 35 | url = link.substring(1); 36 | } else { 37 | url = relPath+link; 38 | } 39 | result+='
  • '+ 40 | data.children[i].text+''+ 41 | makeTree(data.children[i],relPath)+'
  • '; 42 | } 43 | result+='
'; 44 | } 45 | return result; 46 | } 47 | var searchBoxHtml; 48 | if (searchEnabled) { 49 | if (serverSide) { 50 | searchBoxHtml='
'+ 51 | '
'+ 52 | '
 '+ 54 | ''+ 57 | '
'+ 58 | '
'+ 59 | '
'+ 60 | '
'; 61 | } else { 62 | searchBoxHtml='
'+ 63 | ''+ 64 | ' '+ 66 | ''+ 70 | ''+ 71 | ''+ 73 | ''+ 75 | ''+ 76 | '
'; 77 | } 78 | } 79 | 80 | $('#main-nav').before('
'+ 81 | ''+ 84 | ''+ 85 | '
'); 86 | $('#main-nav').append(makeTree(menudata,relPath)); 87 | $('#main-nav').children(':first').addClass('sm sm-dox').attr('id','main-menu'); 88 | if (searchBoxHtml) { 89 | $('#main-menu').append('
  • '); 90 | } 91 | var $mainMenuState = $('#main-menu-state'); 92 | var prevWidth = 0; 93 | if ($mainMenuState.length) { 94 | function initResizableIfExists() { 95 | if (typeof initResizable==='function') initResizable(); 96 | } 97 | // animate mobile menu 98 | $mainMenuState.change(function(e) { 99 | var $menu = $('#main-menu'); 100 | var options = { duration: 250, step: initResizableIfExists }; 101 | if (this.checked) { 102 | options['complete'] = function() { $menu.css('display', 'block') }; 103 | $menu.hide().slideDown(options); 104 | } else { 105 | options['complete'] = function() { $menu.css('display', 'none') }; 106 | $menu.show().slideUp(options); 107 | } 108 | }); 109 | // set default menu visibility 110 | function resetState() { 111 | var $menu = $('#main-menu'); 112 | var $mainMenuState = $('#main-menu-state'); 113 | var newWidth = $(window).outerWidth(); 114 | if (newWidth!=prevWidth) { 115 | if ($(window).outerWidth()<768) { 116 | $mainMenuState.prop('checked',false); $menu.hide(); 117 | $('#searchBoxPos1').html(searchBoxHtml); 118 | $('#searchBoxPos2').hide(); 119 | } else { 120 | $menu.show(); 121 | $('#searchBoxPos1').empty(); 122 | $('#searchBoxPos2').html(searchBoxHtml); 123 | $('#searchBoxPos2').show(); 124 | } 125 | if (typeof searchBox!=='undefined') { 126 | searchBox.CloseResultsWindow(); 127 | } 128 | prevWidth = newWidth; 129 | } 130 | } 131 | $(window).ready(function() { resetState(); initResizableIfExists(); }); 132 | $(window).resize(resetState); 133 | } 134 | $('#main-menu').smartmenus(); 135 | } 136 | /* @license-end */ 137 | -------------------------------------------------------------------------------- /docs/menudata.js: -------------------------------------------------------------------------------- 1 | /* 2 | @licstart The following is the entire license notice for the JavaScript code in this file. 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (C) 1997-2020 by Dimitri van Heesch 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 9 | and associated documentation files (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 11 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all copies or 15 | substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 18 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | @licend The above is the entire license notice for the JavaScript code in this file 24 | */ 25 | var menudata={children:[ 26 | {text:"Main Page",url:"index.html"}, 27 | {text:"Classes",url:"annotated.html",children:[ 28 | {text:"Class List",url:"annotated.html"}, 29 | {text:"Class Members",url:"functions.html",children:[ 30 | {text:"All",url:"functions.html"}, 31 | {text:"Functions",url:"functions_func.html"}]}]}, 32 | {text:"Files",url:"files.html",children:[ 33 | {text:"File List",url:"files.html"}, 34 | {text:"File Members",url:"globals.html",children:[ 35 | {text:"All",url:"globals.html"}, 36 | {text:"Macros",url:"globals_defs.html"}]}]}]} 37 | -------------------------------------------------------------------------------- /docs/nav_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/nav_f.png -------------------------------------------------------------------------------- /docs/nav_fd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/nav_fd.png -------------------------------------------------------------------------------- /docs/nav_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/nav_g.png -------------------------------------------------------------------------------- /docs/nav_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/nav_h.png -------------------------------------------------------------------------------- /docs/nav_hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/nav_hd.png -------------------------------------------------------------------------------- /docs/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/open.png -------------------------------------------------------------------------------- /docs/splitbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/splitbar.png -------------------------------------------------------------------------------- /docs/splitbard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/splitbard.png -------------------------------------------------------------------------------- /docs/stl__reader_8h.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | stl_reader: stl_reader.h File Reference 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 | 18 | 19 | 20 | 24 | 25 | 26 |
    21 |
    stl_reader 22 |
    23 |
    27 |
    28 | 29 | 30 | 31 | 32 | 39 | 40 |
    41 |
    42 |
    43 | Classes | 44 | Macros | 45 | Functions
    46 |
    stl_reader.h File Reference
    47 |
    48 |
    49 | 50 |

    Provides functions to read stl files into user provided arrays. 51 | More...

    52 | 53 |

    Go to the source code of this file.

    54 | 55 | 57 | 58 | 59 | 60 |

    56 | Classes

    class  stl_reader::StlMesh< TNumber, TIndex >
     convenience mesh class which makes accessing the stl data more easy More...
     
    61 | 63 | 65 | 66 | 67 | 69 | 70 | 71 |

    62 | Macros

    64 | #define STL_READER_THROW(msg)   {std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
     Throws an std::runtime_error with the given message.
     
    68 | #define STL_READER_COND_THROW(cond, msg)   if(cond){std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
     Throws an std::runtime_error with the given message, if the given condition evaluates to true.
     
    72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |

    73 | Functions

    template<class TNumberContainer1 , class TNumberContainer2 , class TIndexContainer1 , class TIndexContainer2 >
    bool stl_reader::ReadStlFile (const char *filename, TNumberContainer1 &coordsOut, TNumberContainer2 &normalsOut, TIndexContainer1 &trisOut, TIndexContainer2 &solidRangesOut)
     Reads an ASCII or binary stl file into several arrays.
     
    template<class TNumberContainer1 , class TNumberContainer2 , class TIndexContainer1 , class TIndexContainer2 >
    bool stl_reader::ReadStlFile_ASCII (const char *filename, TNumberContainer1 &coordsOut, TNumberContainer2 &normalsOut, TIndexContainer1 &trisOut, TIndexContainer2 &solidRangesOut)
     Reads an ASCII stl file into several arrays.
     
    template<class TNumberContainer1 , class TNumberContainer2 , class TIndexContainer1 , class TIndexContainer2 >
    bool stl_reader::ReadStlFile_BINARY (const char *filename, TNumberContainer1 &coordsOut, TNumberContainer2 &normalsOut, TIndexContainer1 &trisOut, TIndexContainer2 &solidRangesOut)
     Reads a binary stl file into several arrays.
     
    bool stl_reader::StlFileHasASCIIFormat (const char *filename)
     Determines whether a stl file has ASCII format.
     
    90 |

    Detailed Description

    91 |

    Provides functions to read stl files into user provided arrays.

    92 |

    The central function of this file is ReadStlFile(...). It automatically recognizes whether an ASCII or a Binary file is to be read. It identifies matching corner coordinates of triangles with each other, so that the resulting coordinate array does not contain the same coordinate-triple multiple times.

    93 |

    The function operates on template container types. Those containers should have similar interfaces as std::vector and operate on float or double types (TNumberContainer) or on int or size_t types (TIndexContainer).

    94 |

    A conveniance class StlMesh is also provided, which makes accessing triangle corners and corresponding corner coordinates much more easy. It still provides raw access to the underlying data arrays.

    95 |

    96 | Usage example 1 (using <tt>StlMesh</tt>):

    97 |
    try {
    98 |
    stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
    99 |
    100 |
    for(size_t itri = 0; itri < mesh.num_tris(); ++itri) {
    101 |
    std::cout << "coordinates of triangle " << itri << ": ";
    102 |
    for(size_t icorner = 0; icorner < 3; ++icorner) {
    103 |
    const float* c = mesh.tri_corner_coords (itri, icorner);
    104 |
    // or alternatively:
    105 |
    // float* c = mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner));
    106 |
    std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
    107 |
    }
    108 |
    std::cout << std::endl;
    109 |
    110 |
    float* n = mesh.tri_normal (itri);
    111 |
    std::cout << "normal of triangle " << itri << ": "
    112 |
    << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
    113 |
    }
    114 |
    }
    115 |
    catch (std::exception& e) {
    116 |
    std::cout << e.what() << std::endl;
    117 |
    }
    118 |

    119 | Usage example 2 (using <tt>StlMesh</tt> and <em>solids</em>)

    120 |
    try {
    121 |
    stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
    122 |
    123 |
    for(size_t isolid = 0; isolid < mesh.num_solids(); ++isolid) {
    124 |
    std::cout << "solid " << isolid << std::endl;
    125 |
    126 |
    for(size_t itri = mesh.solid_tris_begin(isolid);
    127 |
    itri < mesh.solid_tris_end(isolid); ++itri)
    128 |
    {
    129 |
    const float* n = mesh.tri_normal (itri);
    130 |
    std::cout << "normal of triangle " << itri << ": "
    131 |
    << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
    132 |
    }
    133 |
    }
    134 |
    }
    135 |
    catch (std::exception& e) {
    136 |
    std::cout << e.what() << std::endl;
    137 |
    }
    138 |

    139 | Usage example 3 (using raw data arrays)

    140 |
    std::vector<float> coords, normals;
    141 |
    std::vector<unsigned int> tris, solids;
    142 |
    143 |
    try {
    144 |
    stl_reader::ReadStlFile ("geometry.stl", coords, normals, tris, solids);
    145 |
    const size_t numTris = tris.size() / 3;
    146 |
    for(size_t itri = 0; itri < numTris; ++itri) {
    147 |
    std::cout << "coordinates of triangle " << itri << ": ";
    148 |
    for(size_t icorner = 0; icorner < 3; ++icorner) {
    149 |
    float* c = &coords[3 * tris [3 * itri + icorner]];
    150 |
    std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
    151 |
    }
    152 |
    std::cout << std::endl;
    153 |
    154 |
    float* n = &normals [3 * itri];
    155 |
    std::cout << "normal of triangle " << itri << ": "
    156 |
    << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
    157 |
    }
    158 |
    }
    159 |
    catch (std::exception& e) {
    160 |
    std::cout << e.what() << std::endl;
    161 |
    }
    162 |

    If you do not want to use exceptions, you may define the macro STL_READER_NO_EXCEPTIONS before including 'stl_reader.h'. In that case, functions will return false if an error occurred.

    163 |

    Function Documentation

    164 | 165 |

    ◆ ReadStlFile()

    166 | 167 |
    168 |
    169 |
    170 | template<class TNumberContainer1 , class TNumberContainer2 , class TIndexContainer1 , class TIndexContainer2 >
    171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 |
    bool stl_reader::ReadStlFile (const char * filename,
    TNumberContainer1 & coordsOut,
    TNumberContainer2 & normalsOut,
    TIndexContainer1 & trisOut,
    TIndexContainer2 & solidRangesOut 
    )
    208 |
    209 | 210 |

    Reads an ASCII or binary stl file into several arrays.

    211 |

    Reads a stl file and writes its coordinates, normals and triangle-corner-indices to the provided containers. It also fills a container solidRangesOut, which provides the triangle ranges for individual solids.

    212 |

    Double vertex entries are removed on the fly, so that triangle corners with equal coordinates are represented by a single coordinate entry in coordsOut.

    213 |
    Parameters
    214 | 215 | 216 | 217 | 218 | 219 | 221 |
    filename[in] The name of the file which shall be read
    coordsOut[out] Coordinates are written to this container. On termination, it has size numVertices * 3. Each triple of entries forms a 3d coordinate. The type TNumberContainer should have the same interface as std::vector<float>.
    normalsOut[out] Face normals are written to this container. On termination, it has size numFaces * 3. Each triple of entries forms a 3d normal. The type TNumberContainer should have the same interface as std::vector<float>.
    trisOut[out] Triangle corner indices are written to this container. On termination, it has size numFaces * 3. Each triple of entries defines a triangle. The type TIndexContainer should have the same interface as std::vector<size_t>. Multiply corner indices from trisOut by 3 to obtain the index of the first coordinate of that corner in coordsOut.
    solidRangesOut[out] On termination, it holds the ranges of triangle indices for each solid. It has the size numSolids + 1. Each entry can be interpreted as a end/begin triangle index for the previous/next solid. E.g., if there are 3 solids, the returned array would look like this:
    {sol1Begin, sol1End/sol2Begin, sol2End/sol3Begin, sol3End}.
    220 |
    The type TIndexContainer should have the same interface as std::vector<size_t>.
    222 |
    223 |
    224 |
    Returns
    true if the file was successfully read into the provided container.
    225 | 226 |
    227 |
    228 | 229 |

    ◆ ReadStlFile_ASCII()

    230 | 231 |
    232 |
    233 |
    234 | template<class TNumberContainer1 , class TNumberContainer2 , class TIndexContainer1 , class TIndexContainer2 >
    235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 |
    bool stl_reader::ReadStlFile_ASCII (const char * filename,
    TNumberContainer1 & coordsOut,
    TNumberContainer2 & normalsOut,
    TIndexContainer1 & trisOut,
    TIndexContainer2 & solidRangesOut 
    )
    272 |
    273 | 274 |

    Reads an ASCII stl file into several arrays.

    275 |
    See also
    ReadStlFile, ReadStlFile_ASCII
    276 | 277 |
    278 |
    279 | 280 |

    ◆ ReadStlFile_BINARY()

    281 | 282 |
    283 |
    284 |
    285 | template<class TNumberContainer1 , class TNumberContainer2 , class TIndexContainer1 , class TIndexContainer2 >
    286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 |
    bool stl_reader::ReadStlFile_BINARY (const char * filename,
    TNumberContainer1 & coordsOut,
    TNumberContainer2 & normalsOut,
    TIndexContainer1 & trisOut,
    TIndexContainer2 & solidRangesOut 
    )
    323 |
    324 | 325 |

    Reads a binary stl file into several arrays.

    326 |
    See also
    ReadStlFile, ReadStlFile_BINARY
    327 | 328 |
    329 |
    330 | 331 |

    ◆ StlFileHasASCIIFormat()

    332 | 333 |
    334 |
    335 | 336 | 337 | 348 | 350 | 351 |
    338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 |
    bool stl_reader::StlFileHasASCIIFormat (const char * filename)
    347 |
    349 | inline
    352 |
    353 | 354 |

    Determines whether a stl file has ASCII format.

    355 |

    The underlying mechanism is simply checks whether the provided file starts with the keyword solid. This should work for many stl files, but may fail, of course.

    356 | 357 |
    358 |
    359 |
    360 | 361 | 364 | 365 | 366 | -------------------------------------------------------------------------------- /docs/sync_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/sync_off.png -------------------------------------------------------------------------------- /docs/sync_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/sync_on.png -------------------------------------------------------------------------------- /docs/tab_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_a.png -------------------------------------------------------------------------------- /docs/tab_ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_ad.png -------------------------------------------------------------------------------- /docs/tab_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_b.png -------------------------------------------------------------------------------- /docs/tab_bd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_bd.png -------------------------------------------------------------------------------- /docs/tab_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_h.png -------------------------------------------------------------------------------- /docs/tab_hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_hd.png -------------------------------------------------------------------------------- /docs/tab_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_s.png -------------------------------------------------------------------------------- /docs/tab_sd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/docs/tab_sd.png -------------------------------------------------------------------------------- /docs/tabs.css: -------------------------------------------------------------------------------- 1 | .sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.main-menu-btn{position:relative;display:inline-block;width:36px;height:36px;text-indent:36px;margin-left:8px;white-space:nowrap;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.main-menu-btn-icon,.main-menu-btn-icon:before,.main-menu-btn-icon:after{position:absolute;top:50%;left:2px;height:2px;width:24px;background:var(--nav-menu-button-color);-webkit-transition:all .25s;transition:all .25s}.main-menu-btn-icon:before{content:'';top:-7px;left:0}.main-menu-btn-icon:after{content:'';top:7px;left:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon{height:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:before{top:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:after{top:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}#main-menu-state{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(1px,1px,1px,1px)}#main-menu-state:not(:checked) ~ #main-menu{display:none}#main-menu-state:checked ~ #main-menu{display:block}@media(min-width:768px){.main-menu-btn{position:absolute;top:-99999px}#main-menu-state:not(:checked) ~ #main-menu{display:block}}.sm-dox{background-image:var(--nav-gradient-image)}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0 12px;padding-right:43px;font-family:var(--font-family-nav);font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:var(--nav-text-normal-shadow);color:var(--nav-text-normal-color);outline:0}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a.current{color:#d23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:var(--nav-menu-toggle-color);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox a span.sub-arrow:before{display:block;content:'+'}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px;border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0;border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox ul{background:var(--nav-menu-background-color)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:var(--nav-menu-background-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:0 1px 1px black}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media(min-width:768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:var(--nav-gradient-image);line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:var(--nav-text-normal-color) transparent transparent transparent;background:transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0 12px;background-image:var(--nav-separator-image);background-repeat:no-repeat;background-position:right;-moz-border-radius:0 !important;-webkit-border-radius:0;border-radius:0 !important}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a:hover span.sub-arrow{border-color:var(--nav-text-hover-color) transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent var(--nav-menu-background-color) transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:var(--nav-menu-background-color);-moz-border-radius:5px !important;-webkit-border-radius:5px;border-radius:5px !important;-moz-box-shadow:0 5px 9px rgba(0,0,0,0.2);-webkit-box-shadow:0 5px 9px rgba(0,0,0,0.2);box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent var(--nav-menu-foreground-color);border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:var(--nav-menu-foreground-color);background-image:none;border:0 !important;color:var(--nav-menu-foreground-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent var(--nav-text-hover-color)}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:var(--nav-menu-background-color);height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #d23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#d23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent var(--nav-menu-foreground-color) transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:var(--nav-menu-foreground-color) transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:var(--nav-gradient-image)}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:var(--nav-menu-background-color)}} -------------------------------------------------------------------------------- /run_doxygen.sh: -------------------------------------------------------------------------------- 1 | doxygen docs/doxygen_config/stl_reader.dconf 2 | -------------------------------------------------------------------------------- /stl_reader.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018-2023, Sebastian Reiter (s.b.reiter@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | 26 | /** \file 27 | * \brief Provides functions to read **stl files** into user provided arrays 28 | * 29 | * The central function of this file is `ReadStlFile(...)`. It automatically recognizes 30 | * whether an *ASCII* or a *Binary* file is to be read. It identifies matching corner 31 | * coordinates of triangles with each other, so that the resulting coordinate 32 | * array does not contain the same coordinate-triple multiple times. 33 | * 34 | * The function operates on template container types. Those containers should 35 | * have similar interfaces as `std::vector` and operate on `float` or `double` types 36 | * (`TNumberContainer`) or on `int` or `size_t` types (`TIndexContainer`). 37 | * 38 | * 39 | * A conveniance class `StlMesh` is also provided, which makes accessing triangle 40 | * corners and corresponding corner coordinates much more easy. It still provides 41 | * raw access to the underlying data arrays. 42 | * 43 | * 44 | * ### Usage example 1 (using `StlMesh`): 45 | * 46 | * \code 47 | * try { 48 | * stl_reader::StlMesh mesh ("geometry.stl"); 49 | * 50 | * for(size_t itri = 0; itri < mesh.num_tris(); ++itri) { 51 | * std::cout << "coordinates of triangle " << itri << ": "; 52 | * for(size_t icorner = 0; icorner < 3; ++icorner) { 53 | * const float* c = mesh.tri_corner_coords (itri, icorner); 54 | * // or alternatively: 55 | * // float* c = mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner)); 56 | * std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") "; 57 | * } 58 | * std::cout << std::endl; 59 | * 60 | * float* n = mesh.tri_normal (itri); 61 | * std::cout << "normal of triangle " << itri << ": " 62 | * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n"; 63 | * } 64 | * } 65 | * catch (std::exception& e) { 66 | * std::cout << e.what() << std::endl; 67 | * } 68 | * \endcode 69 | * 70 | * 71 | * ### Usage example 2 (using `StlMesh` and *solids*) 72 | * 73 | * \code 74 | * try { 75 | * stl_reader::StlMesh mesh ("geometry.stl"); 76 | * 77 | * for(size_t isolid = 0; isolid < mesh.num_solids(); ++isolid) { 78 | * std::cout << "solid " << isolid << std::endl; 79 | * 80 | * for(size_t itri = mesh.solid_tris_begin(isolid); 81 | * itri < mesh.solid_tris_end(isolid); ++itri) 82 | * { 83 | * const float* n = mesh.tri_normal (itri); 84 | * std::cout << "normal of triangle " << itri << ": " 85 | * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n"; 86 | * } 87 | * } 88 | * } 89 | * catch (std::exception& e) { 90 | * std::cout << e.what() << std::endl; 91 | * } 92 | * \endcode 93 | * 94 | * 95 | * ### Usage example 3 (using raw data arrays) 96 | * 97 | * \code 98 | * std::vector coords, normals; 99 | * std::vector tris, solids; 100 | * 101 | * try { 102 | * stl_reader::ReadStlFile ("geometry.stl", coords, normals, tris, solids); 103 | * const size_t numTris = tris.size() / 3; 104 | * for(size_t itri = 0; itri < numTris; ++itri) { 105 | * std::cout << "coordinates of triangle " << itri << ": "; 106 | * for(size_t icorner = 0; icorner < 3; ++icorner) { 107 | * float* c = &coords[3 * tris [3 * itri + icorner]]; 108 | * std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") "; 109 | * } 110 | * std::cout << std::endl; 111 | * 112 | * float* n = &normals [3 * itri]; 113 | * std::cout << "normal of triangle " << itri << ": " 114 | * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n"; 115 | * } 116 | * } 117 | * catch (std::exception& e) { 118 | * std::cout << e.what() << std::endl; 119 | * } 120 | * \endcode 121 | * 122 | * If you do not want to use exceptions, you may define the macro 123 | * STL_READER_NO_EXCEPTIONS before including 'stl_reader.h'. In that case, 124 | * functions will return `false` if an error occurred. 125 | */ 126 | 127 | #ifndef __H__STL_READER 128 | #define __H__STL_READER 129 | 130 | #include 131 | #include 132 | #include 133 | #include 134 | #include 135 | 136 | #ifdef STL_READER_NO_EXCEPTIONS 137 | #define STL_READER_THROW(msg) return false; 138 | #define STL_READER_COND_THROW(cond, msg) if(cond) return false; 139 | #else 140 | /// Throws an std::runtime_error with the given message. 141 | #define STL_READER_THROW(msg) {std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));} 142 | 143 | /// Throws an std::runtime_error with the given message, if the given condition evaluates to true. 144 | #define STL_READER_COND_THROW(cond, msg) if(cond){std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));} 145 | #endif 146 | 147 | 148 | namespace stl_reader { 149 | 150 | /// Reads an ASCII or binary stl file into several arrays 151 | /** Reads a stl file and writes its coordinates, normals and triangle-corner-indices 152 | * to the provided containers. It also fills a container solidRangesOut, which 153 | * provides the triangle ranges for individual solids. 154 | * 155 | * Double vertex entries are removed on the fly, so that triangle corners with 156 | * equal coordinates are represented by a single coordinate entry in coordsOut. 157 | * 158 | * 159 | * \param filename [in] The name of the file which shall be read 160 | * 161 | * \param coordsOut [out] Coordinates are written to this container. On termination, 162 | * it has size numVertices * 3. Each triple of entries forms a 163 | * 3d coordinate. The type TNumberContainer should have the same 164 | * interface as std::vector. 165 | * 166 | * \param normalsOut [out] Face normals are written to this container. On termination, 167 | * it has size numFaces * 3. Each triple of entries forms a 168 | * 3d normal. The type TNumberContainer should have the same 169 | * interface as std::vector. 170 | * 171 | * \param trisOut [out] Triangle corner indices are written to this container. 172 | * On termination, it has size numFaces * 3. Each triple of 173 | * entries defines a triangle. The type TIndexContainer should 174 | * have the same interface as std::vector. 175 | * Multiply corner indices from trisOut by 3 to obtain the index 176 | * of the first coordinate of that corner in coordsOut. 177 | * 178 | * \param solidRangesOut [out] On termination, it holds the ranges of triangle indices 179 | * for each solid. It has the size numSolids + 1. Each entry 180 | * can be interpreted as a end/begin triangle index for the 181 | * previous/next solid. E.g., if there are 3 solids, the 182 | * returned array would look like this: 183 | * \code 184 | * {sol1Begin, sol1End/sol2Begin, sol2End/sol3Begin, sol3End}. 185 | * \endcode 186 | * The type TIndexContainer should have the same interface 187 | * as std::vector. 188 | * 189 | * \returns true if the file was successfully read into the provided container. 190 | */ 191 | template 193 | bool ReadStlFile(const char* filename, 194 | TNumberContainer1& coordsOut, 195 | TNumberContainer2& normalsOut, 196 | TIndexContainer1& trisOut, 197 | TIndexContainer2& solidRangesOut); 198 | 199 | 200 | /// Reads an ASCII stl file into several arrays 201 | /** \copydetails ReadStlFile 202 | * \sa ReadStlFile, ReadStlFile_ASCII 203 | */ 204 | template 206 | bool ReadStlFile_ASCII(const char* filename, 207 | TNumberContainer1& coordsOut, 208 | TNumberContainer2& normalsOut, 209 | TIndexContainer1& trisOut, 210 | TIndexContainer2& solidRangesOut); 211 | 212 | /// Reads a binary stl file into several arrays 213 | /** \copydetails ReadStlFile 214 | * \todo support systems with big endianess 215 | * \sa ReadStlFile, ReadStlFile_BINARY 216 | */ 217 | template 219 | bool ReadStlFile_BINARY(const char* filename, 220 | TNumberContainer1& coordsOut, 221 | TNumberContainer2& normalsOut, 222 | TIndexContainer1& trisOut, 223 | TIndexContainer2& solidRangesOut); 224 | 225 | /// Determines whether a stl file has ASCII format 226 | /** The underlying mechanism is simply checks whether the provided file starts 227 | * with the keyword solid. This should work for many stl files, but may 228 | * fail, of course. 229 | */ 230 | inline bool StlFileHasASCIIFormat(const char* filename); 231 | 232 | 233 | /// convenience mesh class which makes accessing the stl data more easy 234 | template 235 | class StlMesh { 236 | public: 237 | /// initializes an empty mesh 238 | StlMesh () 239 | { 240 | solids.resize (2, 0); 241 | } 242 | 243 | /// initializes the mesh from the stl-file specified through filename 244 | /** \{ */ 245 | StlMesh (const char* filename) 246 | { 247 | read_file (filename); 248 | } 249 | 250 | StlMesh (const std::string& filename) 251 | { 252 | read_file (filename); 253 | } 254 | /** \} */ 255 | 256 | /// fills the mesh with the contents of the specified stl-file 257 | /** \{ */ 258 | bool read_file (const char* filename) 259 | { 260 | bool res = false; 261 | 262 | #ifndef STL_READER_NO_EXCEPTIONS 263 | try { 264 | #endif 265 | 266 | res = ReadStlFile (filename, coords, normals, tris, solids); 267 | 268 | #ifndef STL_READER_NO_EXCEPTIONS 269 | } catch (std::exception& e) { 270 | #else 271 | if (!res) { 272 | #endif 273 | 274 | coords.clear (); 275 | normals.clear (); 276 | tris.clear (); 277 | solids.clear (); 278 | STL_READER_THROW (e.what()); 279 | } 280 | 281 | return res; 282 | } 283 | 284 | bool read_file (const std::string& filename) 285 | { 286 | return read_file (filename.c_str()); 287 | } 288 | /** \} */ 289 | 290 | /// returns the number of vertices in the mesh 291 | size_t num_vrts () const 292 | { 293 | return coords.size() / 3; 294 | } 295 | 296 | /// returns an array of 3 floating point values, one for each coordinate of the vertex 297 | const TNumber* vrt_coords (const size_t vi) const 298 | { 299 | return &coords[vi * 3]; 300 | } 301 | 302 | /// returns the number of triangles in the mesh 303 | size_t num_tris () const 304 | { 305 | return tris.size() / 3; 306 | } 307 | 308 | /// returns an array of 3 indices, one for each corner vertex of the triangle 309 | const TIndex* tri_corner_inds (const size_t ti) const 310 | { 311 | return &tris [ti * 3]; 312 | } 313 | 314 | /// returns the index of the corner with index `0<=ci<3` of triangle ti 315 | const TIndex tri_corner_ind (const size_t ti, const size_t ci) const 316 | { 317 | return tris [ti * 3 + ci]; 318 | } 319 | 320 | /** \brief returns an array of 3 floating point values, one for each 321 | * coordinate of the specified corner of the specified tri. 322 | * \note same result as calling on a `StlMesh mesh`: 323 | * \code 324 | * mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner)) 325 | * \endcode 326 | */ 327 | const TNumber* tri_corner_coords (const size_t ti, const size_t ci) const 328 | { 329 | return &coords[tri_corner_ind(ti, ci) * 3]; 330 | } 331 | 332 | /// returns an array of 3 floating point values defining the normal of a tri 333 | const TNumber* tri_normal (const size_t ti) const 334 | { 335 | return &normals [ti * 3]; 336 | } 337 | 338 | /// returns the number of solids of the mesh 339 | /** solids can be seen as a partitioning of the triangles of a mesh. 340 | * By iterating consecutively from the index of the first triangle of a 341 | * solid `si` (using `solid_tris_begin(si)`) to the index of the last 342 | * triangle of a solid (using `solid_tris_end(...)-1`), one visits all 343 | * triangles of the solid `si`.*/ 344 | size_t num_solids () const 345 | { 346 | if(solids.empty ()) 347 | return 0; 348 | return solids.size () - 1; 349 | } 350 | 351 | /// returns the index of the first triangle in the given solid 352 | TIndex solid_tris_begin (const size_t si) const 353 | { 354 | return solids [si]; 355 | } 356 | 357 | /// returns the index of the triangle behind the last triangle in the given solid 358 | TIndex solid_tris_end (const size_t si) const 359 | { 360 | return solids [si + 1]; 361 | } 362 | 363 | /// returns a pointer to the coordinate array, containing `num_vrts()*3` entries. 364 | /** Storage layout: `x0,y0,z0,x1,y1,z1,...` 365 | * \returns pointer to a contiguous array of numbers, or `NULL` if no coords exist.*/ 366 | const TNumber* raw_coords () const 367 | { 368 | if(coords.empty()) 369 | return NULL; 370 | return &coords[0]; 371 | } 372 | 373 | /// returns a pointer to the normal array, containing `num_tris()*3` entries. 374 | /** Storage layout: `nx0,ny0,nz0,nx1,ny1,nz1,...` 375 | * \returns pointer to a contiguous array of numbers, or `NULL` if no normals exist.*/ 376 | const TNumber* raw_normals () const 377 | { 378 | if(normals.empty()) 379 | return NULL; 380 | return &normals[0]; 381 | } 382 | 383 | /// returns a pointer to the triangle array, containing `num_tris()*3` entries. 384 | /** Storage layout: `t0c0,t0c1,t0c2,t1c0,t1c1,t1c2,...` 385 | * \returns pointer to a contiguous array of indices, or `NULL` if no tris exist.*/ 386 | const TIndex* raw_tris () const 387 | { 388 | if(tris.empty()) 389 | return NULL; 390 | return &tris[0]; 391 | } 392 | 393 | /// returns a pointer to the solids array, containing `num_solids()+1` entries. 394 | /** Storage layout: `s0begin, s0end/s1begin, s1end/s2begin, ..., sNend` 395 | * \returns pointer to a contiguous array of indices, or `NULL` if no solids exist.*/ 396 | const TIndex* raw_solids () const 397 | { 398 | if(solids.empty()) 399 | return NULL; 400 | return &solids[0]; 401 | } 402 | 403 | private: 404 | std::vector coords; 405 | std::vector normals; 406 | std::vector tris; 407 | std::vector solids; 408 | }; 409 | 410 | 411 | //////////////////////////////////////////////////////////////////////////////// 412 | // IMPLEMENTATION 413 | //////////////////////////////////////////////////////////////////////////////// 414 | 415 | 416 | namespace stl_reader_impl { 417 | 418 | // a coordinate triple with an additional index. The index is required 419 | // for RemoveDoubles, so that triangles can be reindexed properly. 420 | template 421 | struct CoordWithIndex { 422 | number_t data[3]; 423 | index_t index; 424 | 425 | bool operator == (const CoordWithIndex& c) const 426 | { 427 | return (c[0] == data[0]) && (c[1] == data[1]) && (c[2] == data[2]); 428 | } 429 | 430 | bool operator != (const CoordWithIndex& c) const 431 | { 432 | return (c[0] != data[0]) || (c[1] != data[1]) || (c[2] != data[2]); 433 | } 434 | 435 | bool operator < (const CoordWithIndex& c) const 436 | { 437 | return (data[0] < c[0]) 438 | || (data[0] == c[0] && data[1] < c[1]) 439 | || (data[0] == c[0] && data[1] == c[1] && data[2] < c[2]); 440 | } 441 | 442 | inline number_t& operator [] (const size_t i) {return data[i];} 443 | inline number_t operator [] (const size_t i) const {return data[i];} 444 | }; 445 | 446 | // sorts the array coordsWithIndexInOut and copies unique indices to coordsOut. 447 | // Triangle-corners are re-indexed on the fly and degenerated triangles are removed. 448 | template 450 | void RemoveDoubles (TNumberContainer1& uniqueCoordsOut, 451 | TIndexContainer1& trisInOut, 452 | TNumberContainer2& normalsInOut, 453 | TIndexContainer2& solidsInOut, 454 | std::vector > 457 | &coordsWithIndexInOut) 458 | { 459 | using namespace std; 460 | 461 | typedef typename TNumberContainer1::value_type number_t; 462 | typedef typename TIndexContainer1::value_type index_t; 463 | 464 | sort (coordsWithIndexInOut.begin(), coordsWithIndexInOut.end()); 465 | 466 | // first count unique indices 467 | index_t numUnique = 1; 468 | for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){ 469 | if(coordsWithIndexInOut[i] != coordsWithIndexInOut[i - 1]) 470 | ++numUnique; 471 | } 472 | 473 | uniqueCoordsOut.resize (numUnique * 3); 474 | vector newIndex (coordsWithIndexInOut.size()); 475 | 476 | TIndexContainer2 newSolids; 477 | 478 | // copy unique coordinates to 'uniqueCoordsOut' and create an index-map 479 | // 'newIndex', which allows to re-index triangles later on. 480 | index_t curInd = 0; 481 | newIndex[coordsWithIndexInOut[0].index] = 0; 482 | for(index_t i = 0; i < 3; ++i) 483 | uniqueCoordsOut[i] = coordsWithIndexInOut[0][i]; 484 | 485 | for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){ 486 | const CoordWithIndex c = coordsWithIndexInOut[i]; 487 | if(c != coordsWithIndexInOut[i - 1]){ 488 | ++curInd; 489 | for(index_t j = 0; j < 3; ++j) 490 | uniqueCoordsOut[curInd * 3 + j] = coordsWithIndexInOut[i][j]; 491 | } 492 | 493 | newIndex[c.index] = static_cast (curInd); 494 | } 495 | 496 | // re-index triangles, so that they refer to 'uniqueCoordsOut' 497 | // make sure to only add triangles which refer to three different indices 498 | index_t numUniqueTriInds = 0; 499 | for(index_t i = 0; i < trisInOut.size(); i+=3){ 500 | 501 | const index_t triInd = i / 3; 502 | const index_t newTriInd = numUniqueTriInds / 3; 503 | if (newSolids.size () < solidsInOut.size () && 504 | solidsInOut [newSolids.size ()] <= triInd) 505 | { 506 | newSolids.push_back (newTriInd); 507 | } 508 | 509 | index_t ni[3]; 510 | for(index_t j = 0; j < 3; ++j) 511 | ni[j] = newIndex[trisInOut[i+j]]; 512 | 513 | if((ni[0] != ni[1]) && (ni[0] != ni[2]) && (ni[1] != ni[2])){ 514 | for(index_t j = 0; j < 3; ++j) 515 | { 516 | trisInOut[numUniqueTriInds + j] = ni[j]; 517 | normalsInOut[numUniqueTriInds + j] = normalsInOut [i + j]; 518 | } 519 | numUniqueTriInds += 3; 520 | } 521 | } 522 | 523 | if(numUniqueTriInds < trisInOut.size()) 524 | { 525 | trisInOut.resize (numUniqueTriInds); 526 | normalsInOut.resize (numUniqueTriInds); 527 | } 528 | 529 | if (!newSolids.empty ()) 530 | newSolids.push_back (numUniqueTriInds / 3); 531 | 532 | using std::swap; 533 | swap (solidsInOut, newSolids); 534 | } 535 | }// end of namespace stl_reader_impl 536 | 537 | 538 | template 540 | bool ReadStlFile(const char* filename, 541 | TNumberContainer1& coordsOut, 542 | TNumberContainer2& normalsOut, 543 | TIndexContainer1& trisOut, 544 | TIndexContainer2& solidRangesOut) 545 | { 546 | if(StlFileHasASCIIFormat(filename)) 547 | return ReadStlFile_ASCII(filename, coordsOut, normalsOut, trisOut, solidRangesOut); 548 | else 549 | return ReadStlFile_BINARY(filename, coordsOut, normalsOut, trisOut, solidRangesOut); 550 | } 551 | 552 | 553 | template 555 | bool ReadStlFile_ASCII(const char* filename, 556 | TNumberContainer1& coordsOut, 557 | TNumberContainer2& normalsOut, 558 | TIndexContainer1& trisOut, 559 | TIndexContainer2& solidRangesOut) 560 | { 561 | using namespace std; 562 | using namespace stl_reader_impl; 563 | 564 | typedef typename TNumberContainer1::value_type number_t; 565 | typedef typename TIndexContainer1::value_type index_t; 566 | 567 | coordsOut.clear(); 568 | normalsOut.clear(); 569 | trisOut.clear(); 570 | solidRangesOut.clear(); 571 | 572 | ifstream in(filename); 573 | STL_READER_COND_THROW(!in, "Couldn't open file " << filename); 574 | 575 | vector > coordsWithIndex; 576 | 577 | string buffer; 578 | vector tokens; 579 | int lineCount = 1; 580 | int maxNumTokens = 0; 581 | size_t numFaceVrts = 0; 582 | 583 | while(!(in.eof() || in.fail())) 584 | { 585 | // read the line and tokenize. 586 | // In order to reuse memory in between lines, 'tokens' won't be cleared. 587 | // Instead we count the number of tokens using 'tokenCount'. 588 | getline(in, buffer); 589 | 590 | istringstream line(buffer); 591 | int tokenCount = 0; 592 | while(!(line.eof() || line.fail())){ 593 | if(tokenCount >= maxNumTokens){ 594 | maxNumTokens = tokenCount + 1; 595 | tokens.resize(maxNumTokens); 596 | } 597 | line >> tokens[tokenCount]; 598 | ++tokenCount; 599 | } 600 | 601 | if(tokenCount > 0) 602 | { 603 | string& tok = tokens[0]; 604 | if(tok.compare("vertex") == 0){ 605 | if(tokenCount < 4){ 606 | STL_READER_THROW("ERROR while reading from " << filename << 607 | ": vertex not specified correctly in line " << lineCount); 608 | } 609 | 610 | // read the position 611 | CoordWithIndex c; 612 | for(size_t i = 0; i < 3; ++i) 613 | c[i] = static_cast (atof(tokens[i+1].c_str())); 614 | c.index = static_cast(coordsWithIndex.size()); 615 | coordsWithIndex.push_back(c); 616 | ++numFaceVrts; 617 | } 618 | else if(tok.compare("facet") == 0) 619 | { 620 | STL_READER_COND_THROW(tokenCount < 5, 621 | "ERROR while reading from " << filename << 622 | ": triangle not specified correctly in line " << lineCount); 623 | 624 | STL_READER_COND_THROW(tokens[1].compare("normal") != 0, 625 | "ERROR while reading from " << filename << 626 | ": Missing normal specifier in line " << lineCount); 627 | 628 | // read the normal 629 | for(size_t i = 0; i < 3; ++i) 630 | normalsOut.push_back (static_cast (atof(tokens[i+2].c_str()))); 631 | 632 | numFaceVrts = 0; 633 | } 634 | else if(tok.compare("outer") == 0){ 635 | STL_READER_COND_THROW ((tokenCount < 2) || (tokens[1].compare("loop") != 0), 636 | "ERROR while reading from " << filename << 637 | ": expecting outer loop in line " << lineCount); 638 | } 639 | else if(tok.compare("endfacet") == 0){ 640 | STL_READER_COND_THROW(numFaceVrts != 3, 641 | "ERROR while reading from " << filename << 642 | ": bad number of vertices specified for face in line " << lineCount); 643 | 644 | trisOut.push_back(static_cast (coordsWithIndex.size() - 3)); 645 | trisOut.push_back(static_cast (coordsWithIndex.size() - 2)); 646 | trisOut.push_back(static_cast (coordsWithIndex.size() - 1)); 647 | } 648 | else if(tok.compare("solid") == 0){ 649 | solidRangesOut.push_back(static_cast (trisOut.size() / 3)); 650 | } 651 | } 652 | lineCount++; 653 | } 654 | 655 | solidRangesOut.push_back(static_cast (trisOut.size() / 3)); 656 | 657 | RemoveDoubles (coordsOut, trisOut, normalsOut, solidRangesOut, coordsWithIndex); 658 | 659 | return true; 660 | } 661 | 662 | 663 | template 665 | bool ReadStlFile_BINARY(const char* filename, 666 | TNumberContainer1& coordsOut, 667 | TNumberContainer2& normalsOut, 668 | TIndexContainer1& trisOut, 669 | TIndexContainer2& solidRangesOut) 670 | { 671 | using namespace std; 672 | using namespace stl_reader_impl; 673 | 674 | typedef typename TNumberContainer1::value_type number_t; 675 | typedef typename TIndexContainer1::value_type index_t; 676 | 677 | coordsOut.clear(); 678 | normalsOut.clear(); 679 | trisOut.clear(); 680 | solidRangesOut.clear(); 681 | 682 | ifstream in(filename, ios::binary); 683 | STL_READER_COND_THROW(!in, "Couldnt open file " << filename); 684 | 685 | char stl_header[80]; 686 | in.read(stl_header, 80); 687 | STL_READER_COND_THROW(!in, "Error while parsing binary stl header in file " << filename); 688 | 689 | unsigned int numTris = 0; 690 | in.read((char*)&numTris, 4); 691 | STL_READER_COND_THROW(!in, "Couldnt determine number of triangles in binary stl file " << filename); 692 | 693 | vector > coordsWithIndex; 694 | 695 | for(unsigned int tri = 0; tri < numTris; ++tri){ 696 | float d[12]; 697 | in.read((char*)d, 12 * 4); 698 | STL_READER_COND_THROW(!in, "Error while parsing trianlge in binary stl file " << filename); 699 | 700 | for(int i = 0; i < 3; ++i) 701 | normalsOut.push_back (d[i]); 702 | 703 | for(size_t ivrt = 1; ivrt < 4; ++ivrt){ 704 | CoordWithIndex c; 705 | for(size_t i = 0; i < 3; ++i) 706 | c[i] = d[ivrt * 3 + i]; 707 | c.index = static_cast(coordsWithIndex.size()); 708 | coordsWithIndex.push_back(c); 709 | } 710 | 711 | trisOut.push_back(static_cast (coordsWithIndex.size() - 3)); 712 | trisOut.push_back(static_cast (coordsWithIndex.size() - 2)); 713 | trisOut.push_back(static_cast (coordsWithIndex.size() - 1)); 714 | 715 | char addData[2]; 716 | in.read(addData, 2); 717 | STL_READER_COND_THROW(!in, "Error while parsing additional triangle data in binary stl file " << filename); 718 | } 719 | 720 | solidRangesOut.push_back(0); 721 | solidRangesOut.push_back(static_cast (trisOut.size() / 3)); 722 | 723 | RemoveDoubles (coordsOut, trisOut, normalsOut, solidRangesOut, coordsWithIndex); 724 | 725 | return true; 726 | } 727 | 728 | 729 | inline bool StlFileHasASCIIFormat(const char* filename) 730 | { 731 | using namespace std; 732 | ifstream in(filename); 733 | STL_READER_COND_THROW(!in, "Couldnt open file " << filename); 734 | 735 | char chars [256]; 736 | in.read (chars, 256); 737 | string buffer (chars, in.gcount()); 738 | transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); 739 | return buffer.find ("solid") != string::npos && 740 | buffer.find ("\n") != string::npos && 741 | buffer.find ("facet") != string::npos && 742 | buffer.find ("normal") != string::npos; 743 | } 744 | 745 | } // end of namespace stl_reader 746 | 747 | #endif //__H__STL_READER 748 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.15) 2 | project(stl_reader_tests) 3 | 4 | add_executable ( 5 | stl_reader_tests 6 | read_stl.t.cpp 7 | remove_doubles.t.cpp 8 | utils.cpp) 9 | 10 | include (FetchContent) 11 | FetchContent_Declare ( 12 | googletest 13 | GIT_REPOSITORY https://github.com/google/googletest 14 | GIT_TAG release-1.12.1 15 | GIT_SHALLOW ON) 16 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 17 | FetchContent_MakeAvailable (googletest) 18 | 19 | target_link_libraries (stl_reader_tests gtest_main) 20 | add_custom_target (copyResources ALL COMMAND cmake -E copy_directory 21 | ${CMAKE_CURRENT_SOURCE_DIR}/data data) 22 | -------------------------------------------------------------------------------- /tests/data/ascii_sphere.stl: -------------------------------------------------------------------------------- 1 | solid solidA 2 | facet normal -0 0.934172 0.356822 3 | outer loop 4 | vertex -0.525731 0.850651 0 5 | vertex 0 0.525731 0.850651 6 | vertex 0.525731 0.850651 0 7 | endloop 8 | endfacet 9 | facet normal 0 0.934172 -0.356822 10 | outer loop 11 | vertex -0.525731 0.850651 0 12 | vertex 0.525731 0.850651 0 13 | vertex 0 0.525731 -0.850651 14 | endloop 15 | endfacet 16 | endsolid solidA 17 | solid solidB 18 | facet normal -0.57735 0.57735 -0.57735 19 | outer loop 20 | vertex 0 0.525731 -0.850651 21 | vertex -0.850651 0 -0.525731 22 | vertex -0.525731 0.850651 0 23 | endloop 24 | endfacet 25 | facet normal -0.934172 0.356822 0 26 | outer loop 27 | vertex -0.850651 0 -0.525731 28 | vertex -0.850651 0 0.525731 29 | vertex -0.525731 0.850651 0 30 | endloop 31 | endfacet 32 | facet normal -0.57735 0.57735 0.57735 33 | outer loop 34 | vertex -0.525731 0.850651 0 35 | vertex -0.850651 0 0.525731 36 | vertex 0 0.525731 0.850651 37 | endloop 38 | endfacet 39 | facet normal 0.57735 0.57735 0.57735 40 | outer loop 41 | vertex 0 0.525731 0.850651 42 | vertex 0.850651 0 0.525731 43 | vertex 0.525731 0.850651 0 44 | endloop 45 | endfacet 46 | facet normal 0.934172 0.356822 0 47 | outer loop 48 | vertex 0.850651 0 0.525731 49 | vertex 0.850651 0 -0.525731 50 | vertex 0.525731 0.850651 0 51 | endloop 52 | endfacet 53 | facet normal 0.57735 0.57735 -0.57735 54 | outer loop 55 | vertex 0.525731 0.850651 0 56 | vertex 0.850651 0 -0.525731 57 | vertex 0 0.525731 -0.850651 58 | endloop 59 | endfacet 60 | facet normal -0.356822 0 0.934172 61 | outer loop 62 | vertex -0.850651 0 0.525731 63 | vertex 0 -0.525731 0.850651 64 | vertex 0 0.525731 0.850651 65 | endloop 66 | endfacet 67 | facet normal 0.356822 0 0.934172 68 | outer loop 69 | vertex 0 0.525731 0.850651 70 | vertex 0 -0.525731 0.850651 71 | vertex 0.850651 0 0.525731 72 | endloop 73 | endfacet 74 | facet normal -0.356822 0 -0.934172 75 | outer loop 76 | vertex -0.850651 0 -0.525731 77 | vertex 0 0.525731 -0.850651 78 | vertex 0 -0.525731 -0.850651 79 | endloop 80 | endfacet 81 | facet normal 0.356822 0 -0.934172 82 | outer loop 83 | vertex 0 0.525731 -0.850651 84 | vertex 0.850651 0 -0.525731 85 | vertex 0 -0.525731 -0.850651 86 | endloop 87 | endfacet 88 | facet normal -0.57735 -0.57735 -0.57735 89 | outer loop 90 | vertex 0 -0.525731 -0.850651 91 | vertex -0.525731 -0.850651 0 92 | vertex -0.850651 0 -0.525731 93 | endloop 94 | endfacet 95 | facet normal -0.934172 -0.356822 0 96 | outer loop 97 | vertex -0.850651 0 -0.525731 98 | vertex -0.525731 -0.850651 0 99 | vertex -0.850651 0 0.525731 100 | endloop 101 | endfacet 102 | facet normal -0.57735 -0.57735 0.57735 103 | outer loop 104 | vertex -0.525731 -0.850651 0 105 | vertex 0 -0.525731 0.850651 106 | vertex -0.850651 0 0.525731 107 | endloop 108 | endfacet 109 | facet normal 0.57735 -0.57735 0.57735 110 | outer loop 111 | vertex 0 -0.525731 0.850651 112 | vertex 0.525731 -0.850651 0 113 | vertex 0.850651 0 0.525731 114 | endloop 115 | endfacet 116 | facet normal 0.934172 -0.356822 0 117 | outer loop 118 | vertex 0.525731 -0.850651 0 119 | vertex 0.850651 0 -0.525731 120 | vertex 0.850651 0 0.525731 121 | endloop 122 | endfacet 123 | facet normal 0.57735 -0.57735 -0.57735 124 | outer loop 125 | vertex 0.525731 -0.850651 0 126 | vertex 0 -0.525731 -0.850651 127 | vertex 0.850651 0 -0.525731 128 | endloop 129 | endfacet 130 | facet normal 0 -0.934172 0.356822 131 | outer loop 132 | vertex -0.525731 -0.850651 0 133 | vertex 0.525731 -0.850651 0 134 | vertex 0 -0.525731 0.850651 135 | endloop 136 | endfacet 137 | facet normal 0 -0.934172 -0.356822 138 | outer loop 139 | vertex -0.525731 -0.850651 0 140 | vertex 0 -0.525731 -0.850651 141 | vertex 0.525731 -0.850651 0 142 | endloop 143 | endfacet 144 | endsolid solidB 145 | -------------------------------------------------------------------------------- /tests/data/binary_sphere.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sreiter/stl_reader/6c7615759269a2e6958d04d4d6a4982b6b0da902/tests/data/binary_sphere.stl -------------------------------------------------------------------------------- /tests/read_stl.t.cpp: -------------------------------------------------------------------------------- 1 | #include "../stl_reader.h" 2 | #include 3 | 4 | TEST (readSTL, asciiSphere) 5 | { 6 | stl_reader::StlMesh<> mesh; 7 | mesh.read_file ("data/ascii_sphere.stl"); 8 | 9 | EXPECT_EQ (mesh.num_vrts (), 12); 10 | EXPECT_EQ (mesh.num_tris (), 20); 11 | 12 | EXPECT_EQ (mesh.num_solids (), 2); 13 | EXPECT_EQ (mesh.solid_tris_begin (0), 0); 14 | EXPECT_EQ (mesh.solid_tris_begin (1), 2); 15 | EXPECT_EQ (mesh.solid_tris_end (1), 20); 16 | } 17 | 18 | TEST (readSTL, binarySphere) 19 | { 20 | stl_reader::StlMesh<> mesh; 21 | mesh.read_file ("data/binary_sphere.stl"); 22 | 23 | EXPECT_EQ (mesh.num_vrts (), 12); 24 | EXPECT_EQ (mesh.num_tris (), 20); 25 | EXPECT_EQ (mesh.num_solids (), 1); 26 | EXPECT_EQ (mesh.solid_tris_begin (0), 0); 27 | EXPECT_EQ (mesh.solid_tris_end (0), 20); 28 | } 29 | -------------------------------------------------------------------------------- /tests/remove_doubles.t.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | 4 | namespace 5 | { 6 | using namespace stl_reader; 7 | using std::vector; 8 | 9 | Coords const sourceCoordinatesWithIndex { 10 | {{0, 1, 0}, 0}, 11 | {{1, 0, 0}, 1}, 12 | {{1, 1, 0}, 2}, 13 | {{1, 0, 0}, 3}, 14 | {{0, 0, 0}, 4},}; 15 | 16 | Indices const sourceTriangles { 17 | 2, 3, 4, 18 | 1, 2, 3, // degenerate triangle 19 | 2, 1, 0}; 20 | 21 | vector const sourceNormals { 22 | 0, 0, 1, 23 | 0, 1, -1, 24 | 1, 1, 0}; 25 | 26 | void testRemoveDoubles (vector& solidRanges) 27 | { 28 | auto newTris = sourceTriangles; 29 | auto reorderedCoordinatesWithIndex = sourceCoordinatesWithIndex; 30 | vector newCoords; 31 | vector newNormals = sourceNormals; 32 | stl_reader_impl::RemoveDoubles (newCoords, newTris, newNormals, solidRanges, reorderedCoordinatesWithIndex); 33 | 34 | EXPECT_EQ (newCoords.size (), 12); 35 | EXPECT_EQ (newTris.size (), 6); 36 | EXPECT_EQ (newNormals.size (), 6); 37 | 38 | EXPECT_TRUE (compareTriangleCoords (newCoords, newTris, 0, sourceCoordinatesWithIndex, sourceTriangles, 0)); 39 | EXPECT_TRUE (compareTriangleCoords (newCoords, newTris, 1, sourceCoordinatesWithIndex, sourceTriangles, 2)); 40 | EXPECT_EQ (toVec3 (newNormals, 0), toVec3 (sourceNormals, 0)); 41 | EXPECT_EQ (toVec3 (newNormals, 1), toVec3 (sourceNormals, 2)); 42 | } 43 | } 44 | 45 | TEST (removeDoubles, removeOneVertexAndOneTriangleWithOneSolid) 46 | { 47 | vector solidRanges {0, 3}; 48 | testRemoveDoubles (solidRanges); 49 | EXPECT_EQ (solidRanges.size (), 2); 50 | EXPECT_EQ (solidRanges [0], 0); 51 | EXPECT_EQ (solidRanges [1], 2); 52 | } 53 | 54 | TEST (removeDoubles, removeOneVertexAndOneTriangleWithBigAndSmallSolid) 55 | { 56 | vector solidRanges {0, 2, 3}; 57 | testRemoveDoubles (solidRanges); 58 | EXPECT_EQ (solidRanges.size (), 3); 59 | EXPECT_EQ (solidRanges [0], 0); 60 | EXPECT_EQ (solidRanges [1], 1); 61 | EXPECT_EQ (solidRanges [2], 2); 62 | } 63 | 64 | TEST (removeDoubles, removeOneVertexAndOneTriangleWithSmallAndBigSolid) 65 | { 66 | vector solidRanges {0, 1, 3}; 67 | testRemoveDoubles (solidRanges); 68 | EXPECT_EQ (solidRanges.size (), 3); 69 | EXPECT_EQ (solidRanges [0], 0); 70 | EXPECT_EQ (solidRanges [1], 1); 71 | EXPECT_EQ (solidRanges [2], 2); 72 | } 73 | 74 | TEST (removeDoubles, removeOneVertexAndOneTriangleWithThreeSolids) 75 | { 76 | vector solidRanges {0, 1, 2, 3}; 77 | testRemoveDoubles (solidRanges); 78 | EXPECT_EQ (solidRanges.size (), 4); 79 | EXPECT_EQ (solidRanges [0], 0); 80 | EXPECT_EQ (solidRanges [1], 1); 81 | EXPECT_EQ (solidRanges [2], 1); 82 | EXPECT_EQ (solidRanges [3], 2); 83 | } 84 | -------------------------------------------------------------------------------- /tests/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | 4 | auto operator << (std::ostream& out, vec3 const& v) -> std::ostream& 5 | { 6 | out << "(" << v [0] << ", " << v [1] << ", " << v [2] << ")"; 7 | return out; 8 | } 9 | 10 | auto toVec3 (Coords const& coords, int iCoord) -> vec3 11 | { 12 | assert (iCoord >= 0 && iCoord < coords.size ()); 13 | auto const& c = coords [iCoord].data; 14 | return {c [0], c [1], c [2]}; 15 | } 16 | 17 | auto toVec3 (RawCoords const& coords, int iCoord) -> vec3 18 | { 19 | assert (iCoord >= 0 && iCoord * 3 + 2 < coords.size ()); 20 | auto const c = coords.data () + iCoord * 3; 21 | return {c [0], c [1], c [2]}; 22 | } 23 | 24 | void printTriangleIndices (Indices const& indices) 25 | { 26 | for (size_t iTri = 0; iTri * 3 + 2 < indices.size (); ++iTri) 27 | { 28 | for (int i = 0; i < 3; ++i) 29 | std::cout << indices [iTri * 3 + i] << ", "; 30 | std::cout << std::endl; 31 | } 32 | } 33 | 34 | bool compareTriangleCoords ( 35 | std::vector const& coordsA, 36 | Indices const& trisA, 37 | int triIndexA, 38 | Coords const& coordsB, 39 | Indices const& trisB, 40 | int triIndexB) 41 | { 42 | for (int i = 0; i < 3; ++i) 43 | { 44 | int iCoordA = trisA.at (triIndexA * 3 + i); 45 | int iCoordB = trisB.at (triIndexB * 3 + i); 46 | if (toVec3 (coordsA, iCoordA) != toVec3 (coordsB, iCoordB)) 47 | return false; 48 | } 49 | return true; 50 | } 51 | -------------------------------------------------------------------------------- /tests/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../stl_reader.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using Coord = stl_reader::stl_reader_impl::CoordWithIndex; 10 | using Coords = std::vector; 11 | using RawCoords = std::vector; 12 | using Indices = std::vector; 13 | using vec3 = std::array; 14 | 15 | auto operator << (std::ostream& out, vec3 const& v) -> std::ostream&; 16 | auto toVec3 (Coords const& coords, int iCoord) -> vec3; 17 | auto toVec3 (RawCoords const& coords, int iCoord) -> vec3; 18 | 19 | template 20 | void printTriangleCoordinates (std::vector const& coords, Indices const& indices) 21 | { 22 | for (size_t iTri = 0; iTri * 3 + 2 < indices.size (); ++iTri) 23 | { 24 | for (int i = 0; i < 3; ++i) 25 | std::cout << toVec3 (coords, indices [iTri * 3 + i]) << ", "; 26 | std::cout << std::endl; 27 | } 28 | } 29 | 30 | void printTriangleIndices (Indices const& indices); 31 | 32 | bool compareTriangleCoords ( 33 | std::vector const& coordsA, 34 | Indices const& trisA, 35 | int triIndexA, 36 | Coords const& coordsB, 37 | Indices const& trisB, 38 | int triIndexB); 39 | --------------------------------------------------------------------------------