mirror of https://github.com/vitalif/openscad
Add AMF export.
parent
7809a9e916
commit
cdab3e9ab8
|
@ -12,6 +12,13 @@
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QSet>
|
#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
|
class MainWindow : public QMainWindow, public Ui::MainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -143,9 +150,10 @@ private slots:
|
||||||
void actionDisplayAST();
|
void actionDisplayAST();
|
||||||
void actionDisplayCSGTree();
|
void actionDisplayCSGTree();
|
||||||
void actionDisplayCSGProducts();
|
void actionDisplayCSGProducts();
|
||||||
void actionExportSTLorOFF(bool stl_mode);
|
void actionExport(export_type_e, const char *, const char *);
|
||||||
void actionExportSTL();
|
void actionExportSTL();
|
||||||
void actionExportOFF();
|
void actionExportOFF();
|
||||||
|
void actionExportAMF();
|
||||||
void actionExportDXF();
|
void actionExportDXF();
|
||||||
void actionExportSVG();
|
void actionExportSVG();
|
||||||
void actionExportCSG();
|
void actionExportCSG();
|
||||||
|
|
|
@ -238,6 +238,7 @@
|
||||||
</property>
|
</property>
|
||||||
<addaction name="designActionExportSTL"/>
|
<addaction name="designActionExportSTL"/>
|
||||||
<addaction name="designActionExportOFF"/>
|
<addaction name="designActionExportOFF"/>
|
||||||
|
<addaction name="designActionExportAMF"/>
|
||||||
<addaction name="designActionExportDXF"/>
|
<addaction name="designActionExportDXF"/>
|
||||||
<addaction name="designActionExportSVG"/>
|
<addaction name="designActionExportSVG"/>
|
||||||
<addaction name="designActionExportCSG"/>
|
<addaction name="designActionExportCSG"/>
|
||||||
|
@ -870,6 +871,11 @@
|
||||||
<string>Export as SVG...</string>
|
<string>Export as SVG...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="designActionExportAMF">
|
||||||
|
<property name="text">
|
||||||
|
<string>Export as AMF...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|
141
src/export.cc
141
src/export.cc
|
@ -31,12 +31,22 @@
|
||||||
#include "dxfdata.h"
|
#include "dxfdata.h"
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
#define QUOTE(x__) # x__
|
||||||
|
#define QUOTED(x__) QUOTE(x__)
|
||||||
|
|
||||||
#ifdef ENABLE_CGAL
|
#ifdef ENABLE_CGAL
|
||||||
#include "CGAL_Nef_polyhedron.h"
|
#include "CGAL_Nef_polyhedron.h"
|
||||||
#include "cgal.h"
|
#include "cgal.h"
|
||||||
#include "cgalutils.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)
|
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)) {
|
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:
|
case OPENSCAD_OFF:
|
||||||
export_off(N, output);
|
export_off(N, output);
|
||||||
break;
|
break;
|
||||||
|
case OPENSCAD_AMF:
|
||||||
|
export_amf(N, output);
|
||||||
|
break;
|
||||||
case OPENSCAD_DXF:
|
case OPENSCAD_DXF:
|
||||||
assert(false && "Export Nef polyhedron as DXF not supported");
|
assert(false && "Export Nef polyhedron as DXF not supported");
|
||||||
break;
|
break;
|
||||||
|
@ -64,6 +77,9 @@ void exportFile(const class Geometry *root_geom, std::ostream &output, FileForma
|
||||||
case OPENSCAD_OFF:
|
case OPENSCAD_OFF:
|
||||||
export_off(*ps, output);
|
export_off(*ps, output);
|
||||||
break;
|
break;
|
||||||
|
case OPENSCAD_AMF:
|
||||||
|
export_amf(*ps, output);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false && "Unsupported file format");
|
assert(false && "Unsupported file format");
|
||||||
}
|
}
|
||||||
|
@ -346,4 +362,127 @@ void export_svg(const Polygon2d &poly, std::ostream &output)
|
||||||
output << "</svg>\n";
|
output << "</svg>\n";
|
||||||
|
|
||||||
setlocale(LC_NUMERIC, ""); // Set default locale
|
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 {
|
enum FileFormat {
|
||||||
OPENSCAD_STL,
|
OPENSCAD_STL,
|
||||||
OPENSCAD_OFF,
|
OPENSCAD_OFF,
|
||||||
|
OPENSCAD_AMF,
|
||||||
OPENSCAD_DXF,
|
OPENSCAD_DXF,
|
||||||
OPENSCAD_SVG
|
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_stl(const class PolySet &ps, std::ostream &output);
|
||||||
void export_off(const CGAL_Nef_polyhedron *root_N, 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_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_dxf(const class Polygon2d &poly, std::ostream &output);
|
||||||
void export_svg(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);
|
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->designActionDisplayCSGProducts, SIGNAL(triggered()), this, SLOT(actionDisplayCSGProducts()));
|
||||||
connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL()));
|
connect(this->designActionExportSTL, SIGNAL(triggered()), this, SLOT(actionExportSTL()));
|
||||||
connect(this->designActionExportOFF, SIGNAL(triggered()), this, SLOT(actionExportOFF()));
|
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->designActionExportDXF, SIGNAL(triggered()), this, SLOT(actionExportDXF()));
|
||||||
connect(this->designActionExportSVG, SIGNAL(triggered()), this, SLOT(actionExportSVG()));
|
connect(this->designActionExportSVG, SIGNAL(triggered()), this, SLOT(actionExportSVG()));
|
||||||
connect(this->designActionExportCSG, SIGNAL(triggered()), this, SLOT(actionExportCSG()));
|
connect(this->designActionExportCSG, SIGNAL(triggered()), this, SLOT(actionExportCSG()));
|
||||||
|
@ -1626,9 +1627,9 @@ void MainWindow::actionCheckValidity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_CGAL
|
#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
|
#else
|
||||||
void MainWindow::actionExportSTLorOFF(bool)
|
void MainWindow::actionExport(export_type_e, QString, QString)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (GuiLocker::isLocked()) return;
|
if (GuiLocker::isLocked()) return;
|
||||||
|
@ -1655,27 +1656,38 @@ void MainWindow::actionExportSTLorOFF(bool)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString suffix = stl_mode ? ".stl" : ".off";
|
QString title = QString("Export %1 File").arg(type_name);
|
||||||
QString stl_filename = QFileDialog::getSaveFileName(this,
|
QString filter = QString("%1 Files (*%2)").arg(type_name, suffix);
|
||||||
stl_mode ? "Export STL File" : "Export OFF File",
|
QString filename = this->fileName.isEmpty() ? QString("Untitled") + suffix : QFileInfo(this->fileName).baseName() + suffix;
|
||||||
this->fileName.isEmpty() ? "Untitled"+suffix : QFileInfo(this->fileName).baseName()+suffix,
|
QString export_filename = QFileDialog::getSaveFileName(this, title, filename, filter);
|
||||||
stl_mode ? "STL Files (*.stl)" : "OFF Files (*.off)");
|
if (export_filename.isEmpty()) {
|
||||||
if (stl_filename.isEmpty()) {
|
PRINTB("No filename specified. %s export aborted.", type_name);
|
||||||
PRINTB("No filename specified. %s export aborted.", (stl_mode ? "STL" : "OFF"));
|
|
||||||
clearCurrentOutput();
|
clearCurrentOutput();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream fstream(stl_filename.toUtf8());
|
std::ofstream fstream(export_filename.toUtf8());
|
||||||
if (!fstream.is_open()) {
|
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 {
|
else {
|
||||||
if (stl_mode) exportFile(this->root_geom.get(), fstream, OPENSCAD_STL);
|
switch (export_type) {
|
||||||
else exportFile(this->root_geom.get(), fstream, OPENSCAD_OFF);
|
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();
|
fstream.close();
|
||||||
|
|
||||||
PRINTB("%s export finished.", (stl_mode ? "STL" : "OFF"));
|
PRINTB("%s export finished.", type_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCurrentOutput();
|
clearCurrentOutput();
|
||||||
|
@ -1684,12 +1696,17 @@ void MainWindow::actionExportSTLorOFF(bool)
|
||||||
|
|
||||||
void MainWindow::actionExportSTL()
|
void MainWindow::actionExportSTL()
|
||||||
{
|
{
|
||||||
actionExportSTLorOFF(true);
|
actionExport(EXPORT_TYPE_STL, "STL", ".stl");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::actionExportOFF()
|
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) {
|
QString MainWindow::get2dExportFilename(QString format, QString extension) {
|
||||||
|
|
Loading…
Reference in New Issue