diff options
author | Don Bright <hugh.m.bright@gmail.com> | 2013-09-20 05:19:09 (GMT) |
---|---|---|
committer | Don Bright <hugh.m.bright@gmail.com> | 2013-09-20 05:19:09 (GMT) |
commit | 3eda281be13dfe9eb8fcbc9fb82e75e74d107e01 (patch) | |
tree | 724d309fa18aaa329bdf84757066e1254f842437 /src/openscad.cc | |
parent | 2e21f3deff585731d5377490cde87eeccd917445 (diff) |
allow openscad binary to be compiled with or without QT. use non-QT for tests.
Diffstat (limited to 'src/openscad.cc')
-rw-r--r-- | src/openscad.cc | 645 |
1 files changed, 344 insertions, 301 deletions
diff --git a/src/openscad.cc b/src/openscad.cc index fb5ee44..a8e71b1 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" @@ -53,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 @@ -79,6 +75,13 @@ 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; static void help(const char *progname) { @@ -119,15 +122,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; @@ -182,17 +176,250 @@ Camera get_camera( po::variables_map vm ) return camera; } -int main(int argc, char **argv) +int cmdline( const char* deps_output_file, const char* filename, Camera &camera, const char *output_file, fs::path original_path, Render::type renderer ) { - int rc = 0; + 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; + bool null_output = false; + + 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 (strcmp(output_file, "null") == 0) null_output = true; + 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 + 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); + 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); + 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 ((null_output || png_output_file) && !(renderer==Render::CGAL)) { + // null output 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 @@ -203,18 +430,101 @@ int main(int argc, char **argv) #else bool useGUI = true; #endif - QApplication app(argc, argv, useGUI); + return useGUI; +} + +int gui(const char * filename,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(); + } + + 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 + + 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,qexamplesdir); + 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,qexamplesdir); + app.connect(m, SIGNAL(destroyed()), &app, SLOT(quit())); +#endif + return app.exec(); +} +#else // OPENSCAD_QTGUI +bool QtUseGUI() { return false; } +int gui(const char * filename,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; @@ -259,6 +569,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]); @@ -306,297 +623,23 @@ 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()); - // Initialize global visitors NodeCache nodecache; NodeDumper dumper(nodecache); - 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()); - } - - 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; + rc = cmdline( deps_output_file, filename, camera, output_file, original_path, renderer ); } - else if (useGUI) + else if (QtUseGUI()) { -#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(); + rc = gui(filename,argc,argv); } else { fprintf(stderr, "Requested GUI mode but can't open display!\n"); - exit(1); + return 1; } Builtins::instance(true); |