1 /*
2  *  OpenSCAD (www.openscad.org)
3  *  Copyright (C) 2009-2016 Clifford Wolf <clifford@clifford.at> and
4  *                          Marius Kintel <marius@kintel.net>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  As a special exception, you have permission to link this program
12  *  with the CGAL library and distribute executables, as long as you
13  *  follow the requirements of the GNU GPL in regard to all of the
14  *  software in the executable aside from CGAL.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  */
26 
27 #include "importnode.h"
28 
29 #include "polyset.h"
30 #include "Geometry.h"
31 #include "printutils.h"
32 #include "version_helper.h"
33 #include "AST.h"
34 #include "boost-utils.h"
35 
36 #ifdef ENABLE_LIB3MF
37 #ifndef LIB3MF_API_2
38 #include <Model/COM/NMR_DLLInterfaces.h>
39 #undef BOOL
40 using namespace NMR;
41 
42 /*
43  * Provided here for reference in LibraryInfo.cc which can't include
44  * both Qt and lib3mf headers due to some conflicting definitions of
45  * windows types when compiling with MinGW.
46  */
get_lib3mf_version()47 const std::string get_lib3mf_version() {
48 	DWORD major, minor, micro;
49 	NMR::lib3mf_getinterfaceversion(&major, &minor, &micro);
50 
51 	const OpenSCAD::library_version_number header_version{NMR_APIVERSION_INTERFACE_MAJOR, NMR_APIVERSION_INTERFACE_MINOR, NMR_APIVERSION_INTERFACE_MICRO};
52 	const OpenSCAD::library_version_number runtime_version{major, minor, micro};
53 	return OpenSCAD::get_version_string(header_version, runtime_version);
54 }
55 
56 #ifdef ENABLE_CGAL
57 #include "cgalutils.h"
58 #endif
59 
60 typedef std::list<std::shared_ptr<PolySet>> polysets_t;
61 
import_3mf_error(PLib3MFModel * model=nullptr,PLib3MFModelResourceIterator * object_it=nullptr,PolySet * mesh=nullptr,PolySet * mesh2=nullptr)62 static Geometry * import_3mf_error(PLib3MFModel *model = nullptr, PLib3MFModelResourceIterator *object_it = nullptr, PolySet *mesh = nullptr, PolySet *mesh2 = nullptr)
63 {
64 	if (model) {
65 		lib3mf_release(model);
66 	}
67 	if (object_it) {
68 		lib3mf_release(object_it);
69 	}
70 	if (mesh) {
71 		delete mesh;
72 	}
73 	if (mesh2) {
74 		delete mesh2;
75 	}
76 
77 	return new PolySet(3);
78 }
79 
import_3mf(const std::string & filename,const Location & loc)80 Geometry * import_3mf(const std::string &filename, const Location &loc)
81 {
82 	DWORD interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro;
83 	HRESULT result = lib3mf_getinterfaceversion(&interfaceVersionMajor, &interfaceVersionMinor, &interfaceVersionMicro);
84 	if (result != LIB3MF_OK) {
85 		LOG(message_group::Error,Location::NONE,"","Error reading 3MF library version");
86 		return new PolySet(3);
87 	}
88 
89 	if ((interfaceVersionMajor != NMR_APIVERSION_INTERFACE_MAJOR)) {
90 		LOG(message_group::Error,Location::NONE,"","Invalid 3MF library major version %1$d.%2$d.%3$d, expected %4$d.%5$d.%6$d",
91                         interfaceVersionMajor,interfaceVersionMinor,interfaceVersionMicro,
92                         NMR_APIVERSION_INTERFACE_MAJOR,NMR_APIVERSION_INTERFACE_MINOR,NMR_APIVERSION_INTERFACE_MICRO);
93 		return new PolySet(3);
94 	}
95 
96 	PLib3MFModel *model;
97 	result = lib3mf_createmodel(&model);
98 	if (result != LIB3MF_OK) {
99 		LOG(message_group::Error,Location::NONE,"","Could not create model: %1$08lx",result);
100 		return import_3mf_error();
101 	}
102 
103 	PLib3MFModelReader *reader;
104 	result = lib3mf_model_queryreader(model, "3mf", &reader);
105 	if (result != LIB3MF_OK) {
106 		LOG(message_group::Error,Location::NONE,"","Could not create 3MF reader: %1$08lx",result);
107 		return import_3mf_error(model);
108 	}
109 
110 	result = lib3mf_reader_readfromfileutf8(reader, filename.c_str());
111 	lib3mf_release(reader);
112 	if (result != LIB3MF_OK) {
113 		LOG(message_group::Warning,Location::NONE,"","Could not read file '%1$s', import() at line %2$d",filename.c_str(),loc.firstLine());
114 		return import_3mf_error(model);
115 	}
116 
117 	PLib3MFModelResourceIterator *object_it;
118 	result = lib3mf_model_getmeshobjects(model, &object_it);
119 	if (result != LIB3MF_OK) {
120 		return import_3mf_error(model);
121 	}
122 
123 	PolySet *first_mesh = 0;
124 	polysets_t meshes;
125 	unsigned int mesh_idx = 0;
126 	while (true) {
127 		int has_next;
128 		result = lib3mf_resourceiterator_movenext(object_it, &has_next);
129 		if (result != LIB3MF_OK) {
130 			return import_3mf_error(model, object_it, first_mesh);
131 		}
132 		if (!has_next) {
133 			break;
134 		}
135 
136 		PLib3MFModelResource *object;
137 		result = lib3mf_resourceiterator_getcurrent(object_it, &object);
138 		if (result != LIB3MF_OK) {
139 			return import_3mf_error(model, object_it, first_mesh);
140 		}
141 
142 		DWORD vertex_count;
143 		result = lib3mf_meshobject_getvertexcount(object, &vertex_count);
144 		if (result != LIB3MF_OK) {
145 			return import_3mf_error(model, object_it, first_mesh);
146 		}
147 		DWORD triangle_count;
148 		result = lib3mf_meshobject_gettrianglecount(object, &triangle_count);
149 		if (result != LIB3MF_OK) {
150 			return import_3mf_error(model, object_it, first_mesh);
151 		}
152 
153 		PRINTDB("%s: mesh %d, vertex count: %lu, triangle count: %lu", filename.c_str() % mesh_idx % vertex_count % triangle_count);
154 
155 		PolySet *p = new PolySet(3);
156 		for (DWORD idx = 0; idx < triangle_count; ++idx) {
157 			MODELMESHTRIANGLE triangle;
158 			if (lib3mf_meshobject_gettriangle(object, idx, &triangle) != LIB3MF_OK) {
159 				return import_3mf_error(model, object_it, first_mesh, p);
160 			}
161 
162 			MODELMESHVERTEX vertex1, vertex2, vertex3;
163 			if (lib3mf_meshobject_getvertex(object, triangle.m_nIndices[0], &vertex1) != LIB3MF_OK) {
164 				return import_3mf_error(model, object_it, first_mesh, p);
165 			}
166 			if (lib3mf_meshobject_getvertex(object, triangle.m_nIndices[1], &vertex2) != LIB3MF_OK) {
167 				return import_3mf_error(model, object_it, first_mesh, p);
168 			}
169 			if (lib3mf_meshobject_getvertex(object, triangle.m_nIndices[2], &vertex3) != LIB3MF_OK) {
170 				return import_3mf_error(model, object_it, first_mesh, p);
171 			}
172 
173 			p->append_poly();
174 			p->append_vertex(vertex1.m_fPosition[0], vertex1.m_fPosition[1], vertex1.m_fPosition[2]);
175 			p->append_vertex(vertex2.m_fPosition[0], vertex2.m_fPosition[1], vertex2.m_fPosition[2]);
176 			p->append_vertex(vertex3.m_fPosition[0], vertex3.m_fPosition[1], vertex3.m_fPosition[2]);
177 		}
178 
179 		if (first_mesh) {
180 			meshes.push_back(std::shared_ptr<PolySet>(p));
181 		} else {
182 			first_mesh = p;
183 		}
184 		mesh_idx++;
185 	}
186 
187 	lib3mf_release(object_it);
188 	lib3mf_release(model);
189 
190 	if (first_mesh == 0) {
191 		return new PolySet(3);
192 	} else if (meshes.empty()) {
193 		return first_mesh;
194 	} else {
195 		PolySet *p = new PolySet(3);
196 #ifdef ENABLE_CGAL
197 		Geometry::Geometries children;
198 		children.push_back(std::make_pair((const AbstractNode*)NULL,  shared_ptr<const Geometry>(first_mesh)));
199 		for (polysets_t::iterator it = meshes.begin(); it != meshes.end(); ++it) {
200 			children.push_back(std::make_pair((const AbstractNode*)NULL,  shared_ptr<const Geometry>(*it)));
201 		}
202 		CGAL_Nef_polyhedron *N = CGALUtils::applyUnion3D(children.begin(), children.end());
203 
204 		CGALUtils::createPolySetFromNefPolyhedron3(*N->p3, *p);
205 		delete N;
206 #endif
207 		return p;
208 	}
209 }
210 
211 #else // LIB3MF_API_2
212 
213 #include "lib3mf_implicit.hpp"
214 
215 /*
216  * Provided here for reference in LibraryInfo.cc which can't include
217  * both Qt and lib3mf headers due to some conflicting definitions of
218  * windows types when compiling with MinGW.
219  */
get_lib3mf_version()220 const std::string get_lib3mf_version() {
221 	Lib3MF_uint32 interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro;
222 	Lib3MF::PWrapper wrapper;
223 
224 	try {
225 		wrapper = Lib3MF::CWrapper::loadLibrary();
226 		wrapper->GetLibraryVersion(interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro);
227 	} catch (Lib3MF::ELib3MFException &e) {
228 		LOG(message_group::Export_Error,Location::NONE,"",e.what());
229 	}
230 
231 	const OpenSCAD::library_version_number header_version{LIB3MF_VERSION_MAJOR, LIB3MF_VERSION_MINOR, LIB3MF_VERSION_MICRO};
232 	const OpenSCAD::library_version_number runtime_version{interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro};
233 	return OpenSCAD::get_version_string(header_version, runtime_version);
234 }
235 
236 #ifdef ENABLE_CGAL
237 #include "cgalutils.h"
238 #endif
239 
240 typedef std::list<std::shared_ptr<PolySet>> polysets_t;
241 
import_3mf_error(PolySet * mesh=nullptr,PolySet * mesh2=nullptr)242 static Geometry * import_3mf_error(PolySet *mesh = nullptr, PolySet *mesh2 = nullptr)
243 {
244 	if (mesh) {
245 		delete mesh;
246 	}
247 	if (mesh2) {
248 		delete mesh2;
249 	}
250 	return new PolySet(3);
251 }
252 
import_3mf(const std::string & filename,const Location & loc)253 Geometry *import_3mf(const std::string &filename, const Location &loc)
254 {
255 	Lib3MF_uint32 interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro;
256 	Lib3MF::PWrapper wrapper;
257 
258 	try {
259 		wrapper = Lib3MF::CWrapper::loadLibrary();
260 		wrapper->GetLibraryVersion(interfaceVersionMajor, interfaceVersionMinor, interfaceVersionMicro);
261 		if (interfaceVersionMajor != LIB3MF_VERSION_MAJOR) {
262 			LOG(message_group::Error,Location::NONE,"","Invalid 3MF library major version %1$d.%2$d.%3$d, expected %4$d.%5$d.%6$d",
263 	                        interfaceVersionMajor,interfaceVersionMinor,interfaceVersionMicro,
264 	                        LIB3MF_VERSION_MAJOR,LIB3MF_VERSION_MINOR,LIB3MF_VERSION_MICRO);
265 			return new PolySet(3);
266 		}
267 	} catch (Lib3MF::ELib3MFException &e) {
268 		LOG(message_group::Export_Error,Location::NONE,"",e.what());
269 		return new PolySet(3);
270 	}
271 
272 	Lib3MF::PModel model;
273 	try {
274 		model = wrapper->CreateModel();
275 		if (!model) {
276 			LOG(message_group::Error,Location::NONE,"","Could not create model");
277 			return new PolySet(3);
278 		}
279 	} catch (Lib3MF::ELib3MFException &e) {
280 		LOG(message_group::Export_Error,Location::NONE,"",e.what());
281 		return new PolySet(3);
282 	}
283 
284 	Lib3MF::PReader reader;
285 	try {
286 		reader = model->QueryReader("3mf");
287 		if (!reader) {
288 			LOG(message_group::Error,Location::NONE,"","Could not create 3MF reader");
289 			return new PolySet(3);
290 		}
291 	} catch (Lib3MF::ELib3MFException &e) {
292 		LOG(message_group::Export_Error,Location::NONE,"",e.what());
293 		return new PolySet(3);
294 	}
295 
296 	bool read_error = false;
297 	try {
298 		reader->ReadFromFile(filename);
299 	} catch (Lib3MF::ELib3MFException &e) {
300 		read_error = true;
301 	}
302 	if (!reader || read_error) {
303 		LOG(message_group::Warning,Location::NONE,"","Could not read file '%1$s', import() at line %2$d",filename.c_str(),loc.firstLine());
304 		return new PolySet(3);
305 	}
306 
307 	Lib3MF::PMeshObjectIterator object_it;
308 	object_it = model->GetMeshObjects();
309 	if (!object_it) {
310 		return new PolySet(3);
311 	}
312 
313 	PolySet *first_mesh = 0;
314 	polysets_t meshes;
315 	unsigned int mesh_idx = 0;
316 	bool has_next = object_it->MoveNext();
317 	while (has_next) {
318 		Lib3MF::PMeshObject object;
319 		try {
320 			object = object_it->GetCurrentMeshObject();
321 			if (!object) {
322 				return import_3mf_error(first_mesh);
323 			}
324 		} catch (Lib3MF::ELib3MFException &e) {
325 			LOG(message_group::Error,Location::NONE,"",e.what());
326 			return import_3mf_error(first_mesh);
327 		}
328 
329 		Lib3MF_uint64 vertex_count = object->GetVertexCount();
330 		if (!vertex_count) {
331 			return import_3mf_error(first_mesh);
332 		}
333 		Lib3MF_uint64 triangle_count = object->GetTriangleCount();
334 		if (!triangle_count) {
335 			return import_3mf_error(first_mesh);
336 		}
337 
338 		PRINTDB("%s: mesh %d, vertex count: %lu, triangle count: %lu", filename.c_str() % mesh_idx % vertex_count % triangle_count);
339 
340 		PolySet *p = new PolySet(3);
341 		for (Lib3MF_uint64 idx = 0; idx < triangle_count; ++idx) {
342 			Lib3MF::sTriangle triangle = object->GetTriangle(idx);
343 			Lib3MF::sPosition vertex1, vertex2, vertex3;
344 
345 			vertex1 = object->GetVertex(triangle.m_Indices[0]);
346 			vertex2 = object->GetVertex(triangle.m_Indices[1]);
347 			vertex3 = object->GetVertex(triangle.m_Indices[2]);
348 
349 			p->append_poly();
350 			p->append_vertex(vertex1.m_Coordinates[0], vertex1.m_Coordinates[1], vertex1.m_Coordinates[2]);
351 			p->append_vertex(vertex2.m_Coordinates[0], vertex2.m_Coordinates[1], vertex2.m_Coordinates[2]);
352 			p->append_vertex(vertex3.m_Coordinates[0], vertex3.m_Coordinates[1], vertex3.m_Coordinates[2]);
353 		}
354 
355 		if (first_mesh) {
356 			meshes.push_back(std::shared_ptr<PolySet>(p));
357 		} else {
358 			first_mesh = p;
359 		}
360 		mesh_idx++;
361 		has_next = object_it->MoveNext();
362 	}
363 
364 	if (first_mesh == 0) {
365 		return new PolySet(3);
366 	} else if (meshes.empty()) {
367 		return first_mesh;
368 	} else {
369 		PolySet *p = new PolySet(3);
370 #ifdef ENABLE_CGAL
371 		Geometry::Geometries children;
372 		children.push_back(std::make_pair((const AbstractNode*)NULL,  shared_ptr<const Geometry>(first_mesh)));
373 		for (polysets_t::iterator it = meshes.begin(); it != meshes.end(); ++it) {
374 			children.push_back(std::make_pair((const AbstractNode*)NULL,  shared_ptr<const Geometry>(*it)));
375 		}
376 		CGAL_Nef_polyhedron *N = CGALUtils::applyUnion3D(children.begin(), children.end());
377 
378 		CGALUtils::createPolySetFromNefPolyhedron3(*N->p3, *p);
379 		delete N;
380 #endif
381 		return p;
382 	}
383 }
384 
385 #endif // LIB3MF_API_2
386 
387 #else // ENABLE_LIB3MF
388 
get_lib3mf_version()389 const std::string get_lib3mf_version() {
390 	const std::string lib3mf_version = "(not enabled)";
391 	return lib3mf_version;
392 }
393 
import_3mf(const std::string &,const Location & loc)394 Geometry * import_3mf(const std::string &, const Location &loc)
395 {
396 	LOG(message_group::Warning,Location::NONE,"","Import from 3MF format was not enabled when building the application, import() at line %1$d",loc.firstLine());
397 	return new PolySet(3);
398 }
399 
400 #endif // ENABLE_LIB3MF
401