mirror of https://github.com/vitalif/openscad
Add AMF export.
parent
7809a9e916
commit
cdab3e9ab8
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
141
src/export.cc
141
src/export.cc
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue