Add AMF export.

master
Mark Cooper 2014-02-10 23:43:57 +01:00 committed by Torsten Paul
parent 7809a9e916
commit cdab3e9ab8
5 changed files with 191 additions and 18 deletions

View File

@ -12,6 +12,13 @@
#include <QMutex>
#include <QSet>
enum export_type_e {
EXPORT_TYPE_UNKNOWN,
EXPORT_TYPE_STL,
EXPORT_TYPE_AMF,
EXPORT_TYPE_OFF
};
class MainWindow : public QMainWindow, public Ui::MainWindow
{
Q_OBJECT
@ -143,9 +150,10 @@ private slots:
void actionDisplayAST();
void actionDisplayCSGTree();
void actionDisplayCSGProducts();
void actionExportSTLorOFF(bool stl_mode);
void actionExport(export_type_e, const char *, const char *);
void actionExportSTL();
void actionExportOFF();
void actionExportAMF();
void actionExportDXF();
void actionExportSVG();
void actionExportCSG();

View File

@ -238,6 +238,7 @@
</property>
<addaction name="designActionExportSTL"/>
<addaction name="designActionExportOFF"/>
<addaction name="designActionExportAMF"/>
<addaction name="designActionExportDXF"/>
<addaction name="designActionExportSVG"/>
<addaction name="designActionExportCSG"/>
@ -870,6 +871,11 @@
<string>Export as SVG...</string>
</property>
</action>
<action name="designActionExportAMF">
<property name="text">
<string>Export as AMF...</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -31,12 +31,22 @@
#include "dxfdata.h"
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
#ifdef ENABLE_CGAL
#include "CGAL_Nef_polyhedron.h"
#include "cgal.h"
#include "cgalutils.h"
struct triangle {
std::string vs1;
std::string vs2;
std::string vs3;
};
void exportFile(const class Geometry *root_geom, std::ostream &output, FileFormat format)
{
if (const CGAL_Nef_polyhedron *N = dynamic_cast<const CGAL_Nef_polyhedron *>(root_geom)) {
@ -48,6 +58,9 @@ void exportFile(const class Geometry *root_geom, std::ostream &output, FileForma
case OPENSCAD_OFF:
export_off(N, output);
break;
case OPENSCAD_AMF:
export_amf(N, output);
break;
case OPENSCAD_DXF:
assert(false && "Export Nef polyhedron as DXF not supported");
break;
@ -64,6 +77,9 @@ void exportFile(const class Geometry *root_geom, std::ostream &output, FileForma
case OPENSCAD_OFF:
export_off(*ps, output);
break;
case OPENSCAD_AMF:
export_amf(*ps, output);
break;
default:
assert(false && "Unsupported file format");
}
@ -346,4 +362,127 @@ void export_svg(const Polygon2d &poly, std::ostream &output)
output << "</svg>\n";
setlocale(LC_NUMERIC, ""); // Set default locale
}
}
void export_amf(const class PolySet &ps, std::ostream &output)
{
// FIXME: Implement this without creating a Nef polyhedron
CGAL_Nef_polyhedron *N = createNefPolyhedronFromGeometry(ps);
export_amf(N, output);
delete N;
}
/*!
Saves the current 3D CGAL Nef polyhedron as AMF to the given file.
The file must be open.
*/
void export_amf(const CGAL_Nef_polyhedron *root_N, std::ostream &output)
{
CGAL::Failure_behaviour old_behaviour = CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
try {
CGAL_Polyhedron P;
root_N->p3->convert_to_Polyhedron(P);
typedef CGAL_Polyhedron::Vertex Vertex;
typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
typedef CGAL_Polyhedron::Facet_const_iterator FCI;
typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC;
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
std::vector<std::string> vertices;
std::vector<triangle> triangles;
for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) {
HFCC hc = fi->facet_begin();
HFCC hc_end = hc;
Vertex v1, v2, v3;
v1 = *VCI((hc++)->vertex());
v3 = *VCI((hc++)->vertex());
do {
v2 = v3;
v3 = *VCI((hc++)->vertex());
double x1 = CGAL::to_double(v1.point().x());
double y1 = CGAL::to_double(v1.point().y());
double z1 = CGAL::to_double(v1.point().z());
double x2 = CGAL::to_double(v2.point().x());
double y2 = CGAL::to_double(v2.point().y());
double z2 = CGAL::to_double(v2.point().z());
double x3 = CGAL::to_double(v3.point().x());
double y3 = CGAL::to_double(v3.point().y());
double z3 = CGAL::to_double(v3.point().z());
std::stringstream stream;
stream << x1 << " " << y1 << " " << z1;
std::string vs1 = stream.str();
stream.str("");
stream << x2 << " " << y2 << " " << z2;
std::string vs2 = stream.str();
stream.str("");
stream << x3 << " " << y3 << " " << z3;
std::string vs3 = stream.str();
if (std::find(vertices.begin(), vertices.end(), vs1) == vertices.end())
vertices.push_back(vs1);
if (std::find(vertices.begin(), vertices.end(), vs2) == vertices.end())
vertices.push_back(vs2);
if (std::find(vertices.begin(), vertices.end(), vs3) == vertices.end())
vertices.push_back(vs3);
if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) {
// The above condition ensures that there are 3 distinct vertices, but
// they may be collinear. If they are, the unit normal is meaningless
// so the default value of "1 0 0" can be used. If the vertices are not
// collinear then the unit normal must be calculated from the
// components.
triangle tri = {vs1, vs2, vs3};
triangles.push_back(tri);
}
} while (hc != hc_end);
}
output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
<< "<amf unit=\"millimeter\">\r\n"
<< " <metadata type=\"producer\">OpenSCAD " << QUOTED(OPENSCAD_VERSION)
#ifdef OPENSCAD_COMMIT
<< " (git " << QUOTED(OPENSCAD_COMMIT) << ")"
#endif
<< "</metadata>\r\n"
<< " <object id=\"0\">\r\n"
<< " <mesh>\r\n";
output << " <vertices>\r\n";
for (size_t i = 0; i < vertices.size(); i++) {
std::string s = vertices[i];
output << " <vertex><coordinates>\r\n";
char* chrs = new char[s.length() + 1];
strcpy(chrs, s.c_str());
std::string coords = strtok(chrs, " ");
output << " <x>" << coords << "</x>\r\n";
coords = strtok(NULL, " ");
output << " <y>" << coords << "</y>\r\n";
coords = strtok(NULL, " ");
output << " <z>" << coords << "</z>\r\n";
output << " </coordinates></vertex>\r\n";
}
output << " </vertices>\r\n";
output << " <volume>\r\n";
for (size_t i = 0; i < triangles.size(); i++) {
triangle t = triangles[i];
output << " <triangle>\r\n";
size_t index;
index = std::distance(vertices.begin(), std::find(vertices.begin(), vertices.end(), t.vs1));
output << " <v1>" << index << "</v1>\r\n";
index = std::distance(vertices.begin(), std::find(vertices.begin(), vertices.end(), t.vs2));
output << " <v2>" << index << "</v2>\r\n";
index = std::distance(vertices.begin(), std::find(vertices.begin(), vertices.end(), t.vs3));
output << " <v3>" << index << "</v3>\r\n";
output << " </triangle>\r\n";
}
output << " </volume>\r\n";
output << " </mesh>\r\n"
<< " </object>\r\n"
<< "</amf>\r\n";
} catch (CGAL::Assertion_exception e) {
PRINTB("CGAL error in CGAL_Nef_polyhedron3::convert_to_Polyhedron(): %s", e.what());
}
CGAL::set_error_behaviour(old_behaviour);
setlocale(LC_NUMERIC, ""); // Set default locale
}

View File

@ -10,6 +10,7 @@
enum FileFormat {
OPENSCAD_STL,
OPENSCAD_OFF,
OPENSCAD_AMF,
OPENSCAD_DXF,
OPENSCAD_SVG
};
@ -21,6 +22,8 @@ void export_stl(const class CGAL_Nef_polyhedron *root_N, std::ostream &output);
void export_stl(const class PolySet &ps, std::ostream &output);
void export_off(const CGAL_Nef_polyhedron *root_N, std::ostream &output);
void export_off(const class PolySet &ps, std::ostream &output);
void export_amf(const class CGAL_Nef_polyhedron *root_N, std::ostream &output);
void export_amf(const class PolySet &ps, std::ostream &output);
void export_dxf(const class Polygon2d &poly, std::ostream &output);
void export_svg(const class Polygon2d &poly, std::ostream &output);
void export_png(const CGAL_Nef_polyhedron *root_N, Camera &c, std::ostream &output);

View File

@ -316,6 +316,7 @@ MainWindow::MainWindow(const QString &filename)
connect(this->designActionDisplayCSGProducts, SIGNAL(triggered()), this, SLOT(actionDisplayCSGProducts()));
connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL()));
connect(this->designActionExportOFF, SIGNAL(triggered()), this, SLOT(actionExportOFF()));
connect(this->designActionExportAMF, SIGNAL(triggered()), this, SLOT(actionExportAMF()));
connect(this->designActionExportDXF, SIGNAL(triggered()), this, SLOT(actionExportDXF()));
connect(this->designActionExportSVG, SIGNAL(triggered()), this, SLOT(actionExportSVG()));
connect(this->designActionExportCSG, SIGNAL(triggered()), this, SLOT(actionExportCSG()));
@ -1626,9 +1627,9 @@ void MainWindow::actionCheckValidity() {
}
#ifdef ENABLE_CGAL
void MainWindow::actionExportSTLorOFF(bool stl_mode)
void MainWindow::actionExport(export_type_e export_type, const char *type_name, const char *suffix)
#else
void MainWindow::actionExportSTLorOFF(bool)
void MainWindow::actionExport(export_type_e, QString, QString)
#endif
{
if (GuiLocker::isLocked()) return;
@ -1655,27 +1656,38 @@ void MainWindow::actionExportSTLorOFF(bool)
return;
}
QString suffix = stl_mode ? ".stl" : ".off";
QString stl_filename = QFileDialog::getSaveFileName(this,
stl_mode ? "Export STL File" : "Export OFF File",
this->fileName.isEmpty() ? "Untitled"+suffix : QFileInfo(this->fileName).baseName()+suffix,
stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)");
if (stl_filename.isEmpty()) {
PRINTB("No filename specified. %s export aborted.", (stl_mode ? "STL" : "OFF"));
QString title = QString("Export %1 File").arg(type_name);
QString filter = QString("%1 Files (*%2)").arg(type_name, suffix);
QString filename = this->fileName.isEmpty() ? QString("Untitled") + suffix : QFileInfo(this->fileName).baseName() + suffix;
QString export_filename = QFileDialog::getSaveFileName(this, title, filename, filter);
if (export_filename.isEmpty()) {
PRINTB("No filename specified. %s export aborted.", type_name);
clearCurrentOutput();
return;
}
std::ofstream fstream(stl_filename.toUtf8());
std::ofstream fstream(export_filename.toUtf8());
if (!fstream.is_open()) {
PRINTB("Can't open file \"%s\" for export", stl_filename.toLocal8Bit().constData());
PRINTB("Can't open file \"%s\" for export", export_filename.toLocal8Bit().constData());
}
else {
if (stl_mode) exportFile(this->root_geom.get(), fstream, OPENSCAD_STL);
else exportFile(this->root_geom.get(), fstream, OPENSCAD_OFF);
switch (export_type) {
case EXPORT_TYPE_STL:
exportFile(this->root_geom.get(), fstream, OPENSCAD_STL);
break;
case EXPORT_TYPE_OFF:
exportFile(this->root_geom.get(), fstream, OPENSCAD_OFF);
break;
case EXPORT_TYPE_AMF:
exportFile(this->root_geom.get(), fstream, OPENSCAD_AMF);
break;
default:
assert(false && "Unknown export type");
break;
}
fstream.close();
PRINTB("%s export finished.", (stl_mode ? "STL" : "OFF"));
PRINTB("%s export finished.", type_name);
}
clearCurrentOutput();
@ -1684,12 +1696,17 @@ void MainWindow::actionExportSTLorOFF(bool)
void MainWindow::actionExportSTL()
{
actionExportSTLorOFF(true);
actionExport(EXPORT_TYPE_STL, "STL", ".stl");
}
void MainWindow::actionExportOFF()
{
actionExportSTLorOFF(false);
actionExport(EXPORT_TYPE_OFF, "OFF", ".off");
}
void MainWindow::actionExportAMF()
{
actionExport(EXPORT_TYPE_AMF, "AMF", ".amf");
}
QString MainWindow::get2dExportFilename(QString format, QString extension) {