diff options
Diffstat (limited to 'src/openscad.cc')
-rw-r--r-- | src/openscad.cc | 680 |
1 files changed, 368 insertions, 312 deletions
diff --git a/src/openscad.cc b/src/openscad.cc index ccaeb91..a8bb24a 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -25,7 +25,6 @@ */ #include "openscad.h" -#include "MainWindow.h" #include "node.h" #include "module.h" #include "modcontext.h" @@ -37,6 +36,7 @@ #include "parsersettings.h" #include "rendersettings.h" #include "PlatformUtils.h" +#include "nodedumper.h" #include <string> #include <vector> @@ -52,12 +52,9 @@ #include "CSGTermEvaluator.h" #include "CsgInfo.h" -#include <QApplication> -#include <QString> -#include <QDir> #include <sstream> -#ifdef Q_WS_MAC +#ifdef __APPLE__ #include "EventFilter.h" #include "AppleEvents.h" #ifdef OPENSCAD_DEPLOY @@ -78,6 +75,28 @@ namespace po = boost::program_options; namespace fs = boost::filesystem; +namespace Render { enum type { CGAL, OPENCSG, THROWNTOGETHER }; }; +std::string commandline_commands; +std::string currentdir; +using std::string; +using std::vector; +using boost::lexical_cast; +using boost::is_any_of; + +class Echostream : public std::ofstream +{ +public: + Echostream( const char * filename ) : std::ofstream( filename ) { + set_output_handler( &Echostream::output, this ); + } + static void output( const std::string &msg, void *userdata ) { + Echostream *thisp = static_cast<Echostream*>(userdata); + *thisp << msg << "\n"; + } + ~Echostream() { + this->close(); + } +}; static void help(const char *progname) { @@ -118,15 +137,6 @@ static void info() exit(0); } -std::string commandline_commands; -std::string currentdir; -QString examplesdir; - -using std::string; -using std::vector; -using boost::lexical_cast; -using boost::is_any_of; - Camera get_camera( po::variables_map vm ) { Camera camera; @@ -181,17 +191,254 @@ Camera get_camera( po::variables_map vm ) return camera; } -int main(int argc, char **argv) +int cmdline(const char *deps_output_file, const std::string &filename, Camera &camera, const char *output_file, const fs::path &original_path, Render::type renderer, char ** argv ) { - int rc = 0; + parser_init(boosty::stringy(boost::filesystem::path( argv[0] ).parent_path())); + Tree tree; +#ifdef ENABLE_CGAL + CGALEvaluator cgalevaluator(tree); + PolySetCGALEvaluator psevaluator(cgalevaluator); +#endif + const char *stl_output_file = NULL; + const char *off_output_file = NULL; + const char *dxf_output_file = NULL; + const char *csg_output_file = NULL; + const char *png_output_file = NULL; + const char *ast_output_file = NULL; + const char *term_output_file = NULL; + const char *echo_output_file = NULL; + + std::string suffix = boosty::extension_str( output_file ); + boost::algorithm::to_lower( suffix ); + + if (suffix == ".stl") stl_output_file = output_file; + else if (suffix == ".off") off_output_file = output_file; + else if (suffix == ".dxf") dxf_output_file = output_file; + else if (suffix == ".csg") csg_output_file = output_file; + else if (suffix == ".png") png_output_file = output_file; + else if (suffix == ".ast") ast_output_file = output_file; + else if (suffix == ".term") term_output_file = output_file; + else if (suffix == ".echo") echo_output_file = output_file; + else { + fprintf(stderr, "Unknown suffix for output file %s\n", output_file); + return 1; + } + // Top context - this context only holds builtins + ModuleContext top_ctx; + top_ctx.registerBuiltin(); +#if 0 && DEBUG + top_ctx.dump(NULL, NULL); +#endif + shared_ptr<Echostream> echostream; + if (echo_output_file) + echostream.reset( new Echostream( echo_output_file ) ); + + FileModule *root_module; + ModuleInstantiation root_inst("group"); + AbstractNode *root_node; + AbstractNode *absolute_root_node; + CGAL_Nef_polyhedron root_N; + + handle_dep(filename.c_str()); + + std::ifstream ifs(filename.c_str()); + if (!ifs.is_open()) { + fprintf(stderr, "Can't open input file '%s'!\n", filename.c_str()); + return 1; + } + std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); + text += "\n" + commandline_commands; + fs::path abspath = boosty::absolute(filename); + std::string parentpath = boosty::stringy(abspath.parent_path()); + root_module = parse(text.c_str(), parentpath.c_str(), false); + if (!root_module) { + fprintf(stderr, "Can't parse file '%s'!\n", filename.c_str()); + return 1; + } + root_module->handleDependencies(); + + fs::path fpath = boosty::absolute(fs::path(filename)); + fs::path fparent = fpath.parent_path(); + fs::current_path(fparent); + top_ctx.setDocumentPath(fparent.string()); + + AbstractNode::resetIndexCounter(); + absolute_root_node = root_module->instantiate(&top_ctx, &root_inst, NULL); + + // Do we have an explicit root node (! modifier)? + if (!(root_node = find_root_tag(absolute_root_node))) + root_node = absolute_root_node; + + tree.setRoot(root_node); + + if (csg_output_file) { + fs::current_path(original_path); + std::ofstream fstream(csg_output_file); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", csg_output_file); + } + else { + fs::current_path(fparent); // Force exported filenames to be relative to document path + fstream << tree.getString(*root_node) << "\n"; + fstream.close(); + } + } + else if (ast_output_file) { + fs::current_path(original_path); + std::ofstream fstream(ast_output_file); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", ast_output_file); + } + else { + fs::current_path(fparent); // Force exported filenames to be relative to document path + fstream << root_module->dump("", "") << "\n"; + fstream.close(); + } + } + else if (term_output_file) { + std::vector<shared_ptr<CSGTerm> > highlight_terms; + std::vector<shared_ptr<CSGTerm> > background_terms; + + CSGTermEvaluator csgRenderer(tree, &psevaluator); + shared_ptr<CSGTerm> root_raw_term = csgRenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms); + + fs::current_path(original_path); + std::ofstream fstream(term_output_file); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", term_output_file); + } + else { + if (!root_raw_term) + fstream << "No top-level CSG object\n"; + else { + fstream << root_raw_term->dump() << "\n"; + } + fstream.close(); + } + } + else { #ifdef ENABLE_CGAL - // Causes CGAL errors to abort directly instead of throwing exceptions - // (which we don't catch). This gives us stack traces without rerunning in gdb. - CGAL::set_error_behaviour(CGAL::ABORT); + if ((echo_output_file || png_output_file) && !(renderer==Render::CGAL)) { + // echo or OpenCSG png -> don't necessarily need CGALMesh evaluation + } else { + root_N = cgalevaluator.evaluateCGALMesh(*tree.root()); + } + + fs::current_path(original_path); + + if (deps_output_file) { + std::string deps_out( deps_output_file ); + std::string geom_out; + if ( stl_output_file ) geom_out = std::string(stl_output_file); + else if ( off_output_file ) geom_out = std::string(off_output_file); + else if ( dxf_output_file ) geom_out = std::string(dxf_output_file); + else if ( png_output_file ) geom_out = std::string(png_output_file); + else { + PRINTB("Output file:%s\n",output_file); + PRINT("Sorry, don't know how to write deps for that file type. Exiting\n"); + return 1; + } + int result = write_deps( deps_out, geom_out ); + if ( !result ) { + PRINT("error writing deps"); + return 1; + } + } + + if (stl_output_file) { + if (root_N.dim != 3) { + fprintf(stderr, "Current top level object is not a 3D object.\n"); + return 1; + } + if (!root_N.p3->is_simple()) { + fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n"); + return 1; + } + std::ofstream fstream(stl_output_file); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", stl_output_file); + } + else { + export_stl(&root_N, fstream); + fstream.close(); + } + } + + if (off_output_file) { + if (root_N.dim != 3) { + fprintf(stderr, "Current top level object is not a 3D object.\n"); + return 1; + } + if (!root_N.p3->is_simple()) { + fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n"); + return 1; + } + std::ofstream fstream(off_output_file); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", off_output_file); + } + else { + export_off(&root_N, fstream); + fstream.close(); + } + } + + if (dxf_output_file) { + if (root_N.dim != 2) { + fprintf(stderr, "Current top level object is not a 2D object.\n"); + return 1; + } + std::ofstream fstream(dxf_output_file); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", dxf_output_file); + } + else { + export_dxf(&root_N, fstream); + fstream.close(); + } + } + + if (png_output_file) { + std::ofstream fstream(png_output_file,std::ios::out|std::ios::binary); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", png_output_file); + } + else { + if (renderer==Render::CGAL) { + export_png_with_cgal(&root_N, camera, fstream); + } else if (renderer==Render::THROWNTOGETHER) { + export_png_with_throwntogether(tree, camera, fstream); + } else { + export_png_with_opencsg(tree, camera, fstream); + } + fstream.close(); + } + } +#else + fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n"); + return 1; #endif - Builtins::instance()->initialize(); + } + delete root_node; + return 0; +} + +#ifdef OPENSCAD_TESTING +#undef OPENSCAD_QTGUI +#else +#define OPENSCAD_QTGUI 1 +#endif + +#ifdef OPENSCAD_QTGUI +#include "MainWindow.h" +#include <QApplication> +#include <QString> +#include <QDir> + +bool QtUseGUI() +{ #ifdef Q_WS_X11 // see <http://qt.nokia.com/doc/4.5/qapplication.html#QApplication-2>: // On X11, the window system is initialized if GUIenabled is true. If GUIenabled @@ -202,18 +449,95 @@ int main(int argc, char **argv) #else bool useGUI = true; #endif - QApplication app(argc, argv, useGUI); + return useGUI; +} + +int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, char ** argv) +{ + QApplication app(argc, argv, true); //useGUI); #ifdef Q_WS_MAC app.installEventFilter(new EventFilter(&app)); #endif - fs::path original_path = fs::current_path(); - // set up groups for QSettings QCoreApplication::setOrganizationName("OpenSCAD"); QCoreApplication::setOrganizationDomain("openscad.org"); QCoreApplication::setApplicationName("OpenSCAD"); QCoreApplication::setApplicationVersion(TOSTRING(OPENSCAD_VERSION)); + QDir exdir(QApplication::instance()->applicationDirPath()); + QString qexamplesdir; +#ifdef Q_WS_MAC + exdir.cd("../Resources"); // Examples can be bundled + if (!exdir.exists("examples")) exdir.cd("../../.."); +#elif defined(Q_OS_UNIX) + if (exdir.cd("../share/openscad/examples")) { + qexamplesdir = exdir.path(); + } else + if (exdir.cd("../../share/openscad/examples")) { + qexamplesdir = exdir.path(); + } else + if (exdir.cd("../../examples")) { + qexamplesdir = exdir.path(); + } else +#endif + if (exdir.cd("examples")) { + qexamplesdir = exdir.path(); + } + MainWindow::setExamplesDir(qexamplesdir); + + parser_init(QApplication::instance()->applicationDirPath().toLocal8Bit().constData()); + +#ifdef Q_WS_MAC + installAppleEventHandlers(); +#endif + +#if defined(OPENSCAD_DEPLOY) && defined(Q_WS_MAC) + AutoUpdater *updater = new SparkleAutoUpdater; + AutoUpdater::setUpdater(updater); + if (updater->automaticallyChecksForUpdates()) updater->checkForUpdates(); +#endif + +#if 0 /*** disabled by clifford wolf: adds rendering artefacts with OpenCSG ***/ + // turn on anti-aliasing + QGLFormat f; + f.setSampleBuffers(true); + f.setSamples(4); + QGLFormat::setDefaultFormat(f); +#endif + if (!inputFiles.size()) inputFiles.push_back(""); +#ifdef ENABLE_MDI + BOOST_FOREACH(const string &infile, inputFiles) { + new MainWindow(QString::fromLocal8Bit(boosty::stringy(original_path / infile).c_str())); + } + app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); +#else + MainWindow *m = new MainWindow(QString::fromLocal8Bit(boosty::stringy(original_path / inputFiles[0]).c_str())); + app.connect(m, SIGNAL(destroyed()), &app, SLOT(quit())); +#endif + return app.exec(); +} +#else // OPENSCAD_QTGUI +bool QtUseGUI() { return false; } +int gui(const vector<string> &inputFiles, const fs::path &original_path, int argc, char ** argv) +{ + fprintf(stderr,"Error: compiled without QT, but trying to run GUI\n"); + return 1; +} +#endif // OPENSCAD_QTGUI + +int main(int argc, char **argv) +{ + int rc = 0; + +#ifdef ENABLE_CGAL + // Causes CGAL errors to abort directly instead of throwing exceptions + // (which we don't catch). This gives us stack traces without rerunning in gdb. + CGAL::set_error_behaviour(CGAL::ABORT); +#endif + Builtins::instance()->initialize(); + + fs::path original_path = fs::current_path(); + const char *filename = NULL; const char *output_file = NULL; const char *deps_output_file = NULL; @@ -258,6 +582,13 @@ int main(int argc, char **argv) if (vm.count("version")) version(); if (vm.count("info")) info(); + Render::type renderer = Render::OPENCSG; + if (vm.count("render")) + renderer = Render::CGAL; + if (vm.count("preview")) + if (vm["preview"].as<string>() == "throwntogether") + renderer = Render::THROWNTOGETHER; + if (vm.count("o")) { // FIXME: Allow for multiple output files? if (output_file) help(argv[0]); @@ -290,13 +621,12 @@ int main(int argc, char **argv) commandline_commands += ";\n"; } } - - if (vm.count("input-file")) { - filename = vm["input-file"].as< vector<string> >().begin()->c_str(); + vector<string> inputFiles; + if (vm.count("input-file")) { + inputFiles = vm["input-file"].as<vector<string> >(); } - #ifndef ENABLE_MDI - if (vm.count("input-file") > 1) { + if (inputFiles.size() > 1) { help(argv[0]); } #endif @@ -305,294 +635,20 @@ int main(int argc, char **argv) Camera camera = get_camera( vm ); - QDir exdir(QApplication::instance()->applicationDirPath()); -#ifdef Q_WS_MAC - exdir.cd("../Resources"); // Examples can be bundled - if (!exdir.exists("examples")) exdir.cd("../../.."); -#elif defined(Q_OS_UNIX) - if (exdir.cd("../share/openscad/examples")) { - examplesdir = exdir.path(); - } else - if (exdir.cd("../../share/openscad/examples")) { - examplesdir = exdir.path(); - } else - if (exdir.cd("../../examples")) { - examplesdir = exdir.path(); - } else -#endif - if (exdir.cd("examples")) { - examplesdir = exdir.path(); - } - - parser_init(QApplication::instance()->applicationDirPath().toLocal8Bit().constData()); - - Tree tree; -#ifdef ENABLE_CGAL - CGALEvaluator cgalevaluator(tree); - PolySetCGALEvaluator psevaluator(cgalevaluator); -#endif - - if (output_file) - { - const char *stl_output_file = NULL; - const char *off_output_file = NULL; - const char *dxf_output_file = NULL; - const char *csg_output_file = NULL; - const char *png_output_file = NULL; - const char *ast_output_file = NULL; - const char *term_output_file = NULL; - bool null_output = false; - - QString suffix = QFileInfo(output_file).suffix().toLower(); - if (suffix == "stl") stl_output_file = output_file; - else if (suffix == "off") off_output_file = output_file; - else if (suffix == "dxf") dxf_output_file = output_file; - else if (suffix == "csg") csg_output_file = output_file; - else if (suffix == "png") png_output_file = output_file; - else if (suffix == "ast") ast_output_file = output_file; - else if (suffix == "term") term_output_file = output_file; - else if (strcmp(output_file, "null") == 0) null_output = true; - else { - fprintf(stderr, "Unknown suffix for output file %s\n", output_file); - exit(1); - } - - if (!filename) help(argv[0]); - - // Top context - this context only holds builtins - ModuleContext top_ctx; - top_ctx.registerBuiltin(); -#if 0 && DEBUG - top_ctx.dump(NULL, NULL); -#endif - - FileModule *root_module; - ModuleInstantiation root_inst("group"); - AbstractNode *root_node; - AbstractNode *absolute_root_node; - CGAL_Nef_polyhedron root_N; - - handle_dep(filename); - - std::ifstream ifs(filename); - if (!ifs.is_open()) { - fprintf(stderr, "Can't open input file '%s'!\n", filename); - exit(1); - } - std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); - text += "\n" + commandline_commands; - fs::path abspath = boosty::absolute(filename); - std::string parentpath = boosty::stringy(abspath.parent_path()); - root_module = parse(text.c_str(), parentpath.c_str(), false); - if (!root_module) exit(1); - root_module->handleDependencies(); - - fs::path fpath = boosty::absolute(fs::path(filename)); - fs::path fparent = fpath.parent_path(); - fs::current_path(fparent); - top_ctx.setDocumentPath(fparent.string()); - - AbstractNode::resetIndexCounter(); - absolute_root_node = root_module->instantiate(&top_ctx, &root_inst, NULL); - - // Do we have an explicit root node (! modifier)? - if (!(root_node = find_root_tag(absolute_root_node))) - root_node = absolute_root_node; - - tree.setRoot(root_node); - - if (csg_output_file) { - fs::current_path(original_path); - std::ofstream fstream(csg_output_file); - if (!fstream.is_open()) { - PRINTB("Can't open file \"%s\" for export", csg_output_file); - } - else { - fs::current_path(fparent); // Force exported filenames to be relative to document path - fstream << tree.getString(*root_node) << "\n"; - fstream.close(); - } - } - else if (ast_output_file) { - fs::current_path(original_path); - std::ofstream fstream(ast_output_file); - if (!fstream.is_open()) { - PRINTB("Can't open file \"%s\" for export", ast_output_file); - } - else { - fs::current_path(fparent); // Force exported filenames to be relative to document path - fstream << root_module->dump("", "") << "\n"; - fstream.close(); - } - } - else if (term_output_file) { - std::vector<shared_ptr<CSGTerm> > highlight_terms; - std::vector<shared_ptr<CSGTerm> > background_terms; - - CSGTermEvaluator csgrenderer(tree, &psevaluator); - shared_ptr<CSGTerm> root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms); - - fs::current_path(original_path); - std::ofstream fstream(term_output_file); - if (!fstream.is_open()) { - PRINTB("Can't open file \"%s\" for export", term_output_file); - } - else { - if (!root_raw_term) - fstream << "No top-level CSG object\n"; - else { - fstream << root_raw_term->dump() << "\n"; - } - fstream.close(); - } - } - else { -#ifdef ENABLE_CGAL - if ((null_output || png_output_file) && !vm.count("render")) { - // null output or OpenCSG png -> don't necessarily need CGALMesh evaluation - } else { - root_N = cgalevaluator.evaluateCGALMesh(*tree.root()); - } + // Initialize global visitors + NodeCache nodecache; + NodeDumper dumper(nodecache); - fs::current_path(original_path); - - if (deps_output_file) { - std::string deps_out( deps_output_file ); - std::string geom_out; - if ( stl_output_file ) geom_out = std::string(stl_output_file); - else if ( off_output_file ) geom_out = std::string(off_output_file); - else if ( dxf_output_file ) geom_out = std::string(dxf_output_file); - else if ( png_output_file ) geom_out = std::string(png_output_file); - else { - PRINTB("Output file:%s\n",output_file); - PRINT("Sorry, don't know how to write deps for that file type. Exiting\n"); - exit(1); - } - int result = write_deps( deps_out, geom_out ); - if ( !result ) { - PRINT("error writing deps"); - exit(1); - } - } - - if (stl_output_file) { - if (root_N.dim != 3) { - fprintf(stderr, "Current top level object is not a 3D object.\n"); - exit(1); - } - if (!root_N.p3->is_simple()) { - fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n"); - exit(1); - } - std::ofstream fstream(stl_output_file); - if (!fstream.is_open()) { - PRINTB("Can't open file \"%s\" for export", stl_output_file); - } - else { - export_stl(&root_N, fstream); - fstream.close(); - } - } - - if (off_output_file) { - if (root_N.dim != 3) { - fprintf(stderr, "Current top level object is not a 3D object.\n"); - exit(1); - } - if (!root_N.p3->is_simple()) { - fprintf(stderr, "Object isn't a valid 2-manifold! Modify your design.\n"); - exit(1); - } - std::ofstream fstream(off_output_file); - if (!fstream.is_open()) { - PRINTB("Can't open file \"%s\" for export", off_output_file); - } - else { - export_off(&root_N, fstream); - fstream.close(); - } - } - - if (dxf_output_file) { - if (root_N.dim != 2) { - fprintf(stderr, "Current top level object is not a 2D object.\n"); - exit(1); - } - std::ofstream fstream(dxf_output_file); - if (!fstream.is_open()) { - PRINTB("Can't open file \"%s\" for export", dxf_output_file); - } - else { - export_dxf(&root_N, fstream); - fstream.close(); - } - } - - if (png_output_file) { - std::ofstream fstream(png_output_file,std::ios::out|std::ios::binary); - if (!fstream.is_open()) { - PRINTB("Can't open file \"%s\" for export", png_output_file); - } - else { - if (vm.count("render")) { - export_png_with_cgal(&root_N, camera, fstream); - } else if (vm.count("preview") && vm["preview"].as<string>() == "throwntogether" ) { - export_png_with_throwntogether(tree, camera, fstream); - } else { - export_png_with_opencsg(tree, camera, fstream); - } - fstream.close(); - } - } -#else - fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n"); - exit(1); -#endif - } - delete root_node; + if (output_file) { + if (!inputFiles.size()) help(argv[0]); + rc = cmdline(deps_output_file, inputFiles[0], camera, output_file, original_path, renderer, argv); } - else if (useGUI) - { -#ifdef Q_WS_MAC - installAppleEventHandlers(); -#endif - -#if defined(OPENSCAD_DEPLOY) && defined(Q_WS_MAC) - AutoUpdater *updater = new SparkleAutoUpdater; - AutoUpdater::setUpdater(updater); - if (updater->automaticallyChecksForUpdates()) updater->checkForUpdates(); -#endif - - QString qfilename; - if (filename) qfilename = QString::fromLocal8Bit(boosty::stringy(boosty::absolute(filename)).c_str()); - -#if 0 /*** disabled by clifford wolf: adds rendering artefacts with OpenCSG ***/ - // turn on anti-aliasing - QGLFormat f; - f.setSampleBuffers(true); - f.setSamples(4); - QGLFormat::setDefaultFormat(f); -#endif -#ifdef ENABLE_MDI - new MainWindow(qfilename); - vector<string> inputFiles; - if (vm.count("input-file")) { - inputFiles = vm["input-file"].as<vector<string> >(); - for (vector<string>::const_iterator infile = inputFiles.begin()+1; infile != inputFiles.end(); infile++) { - new MainWindow(QString::fromLocal8Bit(boosty::stringy(original_path / *infile).c_str())); - } - } - app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); -#else - MainWindow *m = new MainWindow(qfilename); - app.connect(m, SIGNAL(destroyed()), &app, SLOT(quit())); -#endif - rc = app.exec(); + else if (QtUseGUI()) { + rc = gui(inputFiles, original_path, argc, argv); } - else - { + else { fprintf(stderr, "Requested GUI mode but can't open display!\n"); - exit(1); + help(argv[0]); } Builtins::instance(true); |