diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index d79a0261..d0f4eb01 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -50,7 +50,7 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&module) bool valid = (stat(filename.c_str(), &st) == 0); // If file isn't there, just return and let the cache retain the old module - if (!valid) return NULL; + if (!valid) return false; // If the file is present, we'll always cache some result std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size); @@ -96,7 +96,7 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&module) std::ifstream ifs(filename.c_str()); if (!ifs.is_open()) { PRINTB("WARNING: Can't open library file '%s'\n", filename); - return NULL; + return false; } textbuf << ifs.rdbuf(); } diff --git a/src/PlatformUtils-mac.mm b/src/PlatformUtils-mac.mm index 1e2ba438..d7edb647 100644 --- a/src/PlatformUtils-mac.mm +++ b/src/PlatformUtils-mac.mm @@ -1,6 +1,11 @@ #include "PlatformUtils.h" #import +std::string PlatformUtils::pathSeparatorChar() +{ + return ":"; +} + std::string PlatformUtils::documentsPath() { return std::string([[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] UTF8String]); diff --git a/src/PlatformUtils-posix.cc b/src/PlatformUtils-posix.cc index d2b87927..8a68b32e 100644 --- a/src/PlatformUtils-posix.cc +++ b/src/PlatformUtils-posix.cc @@ -1,6 +1,11 @@ #include "PlatformUtils.h" #include "boosty.h" +std::string PlatformUtils::pathSeparatorChar() +{ + return ":"; +} + std::string PlatformUtils::documentsPath() { const char *home = getenv("HOME"); diff --git a/src/PlatformUtils-win.cc b/src/PlatformUtils-win.cc index a58a3460..6240c55b 100644 --- a/src/PlatformUtils-win.cc +++ b/src/PlatformUtils-win.cc @@ -6,6 +6,11 @@ #endif #include +std::string PlatformUtils::pathSeparatorChar() +{ + return ";"; +} + // convert from windows api w_char strings (usually utf16) to utf8 std::string std::string winapi_wstr_to_utf8( std::wstring wstr ) { diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc index c2076512..5b768e1e 100644 --- a/src/PlatformUtils.cc +++ b/src/PlatformUtils.cc @@ -1,7 +1,9 @@ +#include + #include "PlatformUtils.h" #include "boosty.h" -#include +extern std::vector librarypath; bool PlatformUtils::createLibraryPath() { @@ -142,6 +144,8 @@ std::string PlatformUtils::info() std::string cgal_2d_kernelEx = ""; #endif // ENABLE_CGAL + const char *env_path = getenv("OPENSCADPATH"); + s << "OpenSCAD Version: " << TOSTRING(OPENSCAD_VERSION) << "\nCompiler, build date: " << compiler_info << ", " << __DATE__ << "\nBoost version: " << BOOST_LIB_VERSION @@ -151,7 +155,12 @@ std::string PlatformUtils::info() << "\nQt version: " << qtVersion << "\nMingW build: " << mingwstatus << "\nGLib version: " << GLIB_MAJOR_VERSION << "." << GLIB_MINOR_VERSION << "." << GLIB_MICRO_VERSION - << "\nOPENSCADPATH: " << getenv("OPENSCADPATH") << "\n" - ; + << "\nOPENSCADPATH: " << (env_path == NULL ? "" : env_path) + << "\nOpenSCAD library path:\n"; + + for (std::vector::iterator it = librarypath.begin();it != librarypath.end();it++) { + s << " " << *it << "\n"; + } + return s.str(); } diff --git a/src/PlatformUtils.h b/src/PlatformUtils.h index 57ee4684..964dedb8 100644 --- a/src/PlatformUtils.h +++ b/src/PlatformUtils.h @@ -11,6 +11,15 @@ namespace PlatformUtils { std::string backupPath(); bool createBackupPath(); std::string info(); + + /** + * Single character separating path specifications in a list + * (e.g. OPENSCADPATH). On Windows that's ';' and on most other + * systems ':'. + * + * @return the path separator + */ + std::string pathSeparatorChar(); } #endif diff --git a/src/func.cc b/src/func.cc index 878bc1c6..6001475f 100644 --- a/src/func.cc +++ b/src/func.cc @@ -36,6 +36,10 @@ #include "printutils.h" #include +#include +using boost::math::isnan; +using boost::math::isinf; + /* Random numbers @@ -598,6 +602,71 @@ Value builtin_parent_module(const Context *, const EvalContext *evalctx) return Value(Module::stack_element(s - 1 - n)); } +Value builtin_norm(const Context *, const EvalContext *evalctx) +{ + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::VECTOR) { + double sum = 0; + Value::VectorType v = evalctx->getArgValue(0).toVector(); + for (size_t i = 0; i < v.size(); i++) + if (v[i].type() == Value::NUMBER) + sum += pow(v[i].toDouble(),2); + else { + PRINT(" WARNING: Incorrect arguments to norm()"); + return Value(); + } + return Value(sqrt(sum)); + } + return Value(); +} + +Value builtin_cross(const Context *, const EvalContext *evalctx) +{ + if (evalctx->numArgs() != 2) { + PRINT("WARNING: Invalid number of parameters for cross()"); + return Value(); + } + + Value arg0 = evalctx->getArgValue(0); + Value arg1 = evalctx->getArgValue(1); + if ((arg0.type() != Value::VECTOR) || (arg1.type() != Value::VECTOR)) { + PRINT("WARNING: Invalid type of parameters for cross()"); + return Value(); + } + + Value::VectorType v0 = arg0.toVector(); + Value::VectorType v1 = arg1.toVector(); + if ((v0.size() != 3) || (v1.size() != 3)) { + PRINT("WARNING: Invalid vector size of parameter for cross()"); + return Value(); + } + for (unsigned int a = 0;a < 3;a++) { + if ((v0[a].type() != Value::NUMBER) || (v1[a].type() != Value::NUMBER)) { + PRINT("WARNING: Invalid value in parameter vector for cross()"); + return Value(); + } + double d0 = v0[a].toDouble(); + double d1 = v1[a].toDouble(); + if (isnan(d0) || isnan(d1)) { + PRINT("WARNING: Invalid value (NaN) in parameter vector for cross()"); + return Value(); + } + if (isinf(d0) || isinf(d1)) { + PRINT("WARNING: Invalid value (INF) in parameter vector for cross()"); + return Value(); + } + } + + double x = v0[1].toDouble() * v1[2].toDouble() - v0[2].toDouble() * v1[1].toDouble(); + double y = v0[2].toDouble() * v1[0].toDouble() - v0[0].toDouble() * v1[2].toDouble(); + double z = v0[0].toDouble() * v1[1].toDouble() - v0[1].toDouble() * v1[0].toDouble(); + + Value::VectorType result; + result.push_back(Value(x)); + result.push_back(Value(y)); + result.push_back(Value(z)); + return Value(result); +} + void register_builtin_functions() { Builtins::init("abs", new BuiltinFunction(&builtin_abs)); @@ -627,5 +696,7 @@ void register_builtin_functions() Builtins::init("search", new BuiltinFunction(&builtin_search)); Builtins::init("version", new BuiltinFunction(&builtin_version)); Builtins::init("version_num", new BuiltinFunction(&builtin_version_num)); + Builtins::init("norm", new BuiltinFunction(&builtin_norm)); + Builtins::init("cross", new BuiltinFunction(&builtin_cross)); Builtins::init("parent_module", new BuiltinFunction(&builtin_parent_module)); } diff --git a/src/openscad.cc b/src/openscad.cc index 12e22ce8..ad6cbf92 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -453,16 +453,12 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c #include #include #include -#include // Only if "fileName" is not absolute, prepend the "absoluteBase". -static QString assemblePath(const fs::path& absoluteBaseDir, +static QString assemblePath(const fs::path& absoluteBase, const string& fileName) { - if (fileName.empty()) return ""; - QString qsDir( boosty::stringy( absoluteBaseDir ).c_str() ); - QString qsFile( fileName.c_str() ); - QFileInfo info( qsDir, qsFile ); // if qsfile is absolute, dir is ignored. - return info.absoluteFilePath(); + return fileName.empty() ? "" : QDir(QString::fromStdString((const string&) absoluteBase)) + .absoluteFilePath(QString::fromStdString(fileName)); } bool QtUseGUI() diff --git a/src/parsersettings.cc b/src/parsersettings.cc index ba4a2236..e7b5ff89 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -90,17 +90,15 @@ fs::path find_valid_path(const fs::path &sourcepath, void parser_init(const std::string &applicationpath) { - // Add paths from OPENSCADPATH before adding built-in paths + // Add paths from OPENSCADPATH before adding built-in paths const char *openscadpaths = getenv("OPENSCADPATH"); if (openscadpaths) { std::string paths(openscadpaths); - typedef boost::split_iterator string_split_iterator; - for (string_split_iterator it = - make_split_iterator(paths, first_finder(":", boost::is_iequal())); - it != string_split_iterator(); - ++it) { - add_librarydir(boosty::absolute(fs::path(boost::copy_range(*it))).string()); - } + std::string sep = PlatformUtils::pathSeparatorChar(); + typedef boost::split_iterator string_split_iterator; + for (string_split_iterator it = boost::make_split_iterator(paths, boost::first_finder(sep, boost::is_iequal())); it != string_split_iterator(); ++it) { + add_librarydir(boosty::absolute(fs::path(boost::copy_range(*it))).string()); + } } // This is the built-in user-writable library path diff --git a/src/winconsole.c b/src/winconsole.c index 11a03090..7182ea8f 100644 --- a/src/winconsole.c +++ b/src/winconsole.c @@ -28,6 +28,15 @@ #include #include #include + +// want to use system definitions instead like #include +#ifndef WIFEXITED +#define WIFEXITED(S) (((S) & 0xff) == 0) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(S) (((S) >> 8) & 0xff) +#endif + #define MAXCMDLEN 64000 #define BUFFSIZE 42 @@ -36,57 +45,64 @@ int main( int argc, char * argv[] ) FILE *cmd_stdout; char cmd[MAXCMDLEN]; char buffer[BUFFSIZE]; - char *fgets_result; - int eof = 0; int pclose_result; int i; - const char * argchar; int result = 0; - int quotify_arg = 0; + unsigned n; // total number of characters in cmd + static const char exe_str[] = "openscad.exe"; + static const char redirect_str[] = " 2>&1"; // capture stderr and stdout - strcat( cmd, "\0" ); - strcat( cmd, "openscad.exe" ); + memcpy(cmd, exe_str, (n = sizeof(exe_str)-1)); // without \0 for ( i = 1 ; i < argc ; ++i ) { - quotify_arg = 0; - for ( argchar = argv[i]; *argchar!=0; argchar++ ) { - if ((char)(*argchar)==' ') quotify_arg = 1; + register char *s; + /*bool*/ int quote; + + cmd[n++] = ' '; + // MS Windows special characters need quotation + // See issues #440, #441 & #479 + quote = NULL != strpbrk((s = argv[i]), " \"&'<>^|\t"); + if (quote) cmd[n++] = '"'; + while (*s) { // copy & check + if ('"' == *s) cmd[n++] = *s; // duplicate it + cmd[n++] = *s++; + if (n >= MAXCMDLEN-sizeof(redirect_str)) { + fprintf(stderr, "Command line length exceeds limit of %d\n", MAXCMDLEN); + return 1; + } } - strcat( cmd, " " ); - if (quotify_arg) strcat( cmd, "\""); - strcat( cmd, argv[i] ); - if (quotify_arg) strcat( cmd, "\""); + if (quote) cmd[n++] = '"'; } - strcat( cmd, " 2>&1"); // capture stderr and stdout - printf("openscad.com: running command: %s\n", cmd ); + memcpy(&cmd[n], redirect_str, sizeof(redirect_str)); // including \0 cmd_stdout = _popen( cmd, "rt" ); if ( cmd_stdout == NULL ) { - printf( "Error opening _popen for command: %s", cmd ); - perror( "Error message:" ); + fprintf(stderr, "Error opening _popen for command: %s", cmd ); + perror( "Error message" ); return 1; } - while ( !eof ) - { - fgets_result = fgets( buffer, BUFFSIZE, cmd_stdout ); - if ( fgets_result == NULL ) { + for(;;) { + if (NULL == fgets(buffer, BUFFSIZE, cmd_stdout)) { if ( ferror( cmd_stdout ) ) { - printf("Error reading from stdout of %s\n", cmd); + fprintf(stderr, "Error reading from stdout of %s\n", cmd); result = 1; } if ( feof( cmd_stdout ) ) { - eof = 1; + break; } } else { - fprintf( stdout, "%s", buffer ); + fputs(buffer, stdout); } } pclose_result = _pclose( cmd_stdout ); - if ( pclose_result < 0 ) { - perror("Error while closing stdout for command:"); + // perror() applicable with return value of -1 only! + // Avoid stupid "Error: No Error" message + if (pclose_result == -1) { + perror("Error while closing stdout for command"); result = 1; + } else if (!result) { + result = WIFEXITED(pclose_result) ? WEXITSTATUS(pclose_result) : 1; } - return result; } diff --git a/testdata/scad/functions/cross-tests.scad b/testdata/scad/functions/cross-tests.scad new file mode 100644 index 00000000..5dda5a01 --- /dev/null +++ b/testdata/scad/functions/cross-tests.scad @@ -0,0 +1,17 @@ +echo(cross([2, 3, 4], [5, 6, 7])); +echo(cross([2, 1, -3], [0, 4, 5])); +echo(cross([2, 1, -3], cross([2, 3, 4], [5, 6, 7]))); + +echo(cross([2, 0/0, -3], [0, 4, 5])); +echo(cross([2, 1/0, -3], [0, 4, 5])); +echo(cross([2, -1/0, -3], [0, 4, 5])); + +echo(cross(0)); +echo(cross(0, 1)); +echo(cross(0, "a")); +echo(cross(0, 1, 3)); +echo(cross([0], [1])); +echo(cross([1, 2, 3], [1, 2])); +echo(cross([1, 2, 3], [1, 2, "a"])); +echo(cross([1, 2, 3], [1, 2, [0]])); +echo(cross([1, 2, 3], [1, 2, [1:2]])); diff --git a/testdata/scad/functions/norm-tests.scad b/testdata/scad/functions/norm-tests.scad new file mode 100644 index 00000000..dfa54418 --- /dev/null +++ b/testdata/scad/functions/norm-tests.scad @@ -0,0 +1,27 @@ +u=undef; + +echo(norm([])); +echo(norm([1])); +echo(norm([1,2])); +echo(norm([1,2,3])); +echo(norm([1,2,3,4])); +echo(norm()); + +echo(norm([1,2,0/0])); +echo(norm([1,2,1/0])); +echo(norm([1,2,-1/0])); + +echo(norm("")); +echo(norm("abcd")); +echo(norm(true)); +echo(norm([1:4])); + +echo(norm([1, 2, "a"])); +echo(norm([1, 2, []])); +echo(norm([1, 2, [1]])); +echo(norm([1, 2, [1:3]])); +echo(norm([[1,2,3,4],[1,2,3],[1,2],[1]])); + +echo(norm(u)); +echo(norm(u, u)); +echo(norm(a, a)); diff --git a/testdata/scad/misc/override.scad b/testdata/scad/misc/override.scad new file mode 100644 index 00000000..97aeeda3 --- /dev/null +++ b/testdata/scad/misc/override.scad @@ -0,0 +1,4 @@ +// Used to test variable override with the -D parameter +a = 1; +b = 2; +echo(a,b); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 32455322..83d1a942 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -540,6 +540,8 @@ set(CORE_SOURCES ../src/fileutils.cc ../src/progress.cc ../src/boost-utils.cc + ../src/PlatformUtils.cc + ../src/${PLATFORMUTILS_SOURCE} ${FLEX_OpenSCADlexer_OUTPUTS} ${BISON_OpenSCADparser_OUTPUTS}) @@ -586,13 +588,11 @@ set(OFFSCREEN_SOURCES ../src/ThrownTogetherRenderer.cc ../src/renderer.cc ../src/render.cc - ../src/PlatformUtils.cc - ../src/${PLATFORMUTILS_SOURCE} ../src/OpenCSGRenderer.cc) add_library(tests-core STATIC ${CORE_SOURCES}) target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ) -set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} ) +set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${Boost_LIBRARIES} ${COCOA_LIBRARY}) add_library(tests-common STATIC ${COMMON_SOURCES}) target_link_libraries(tests-common tests-core) @@ -625,14 +625,14 @@ target_link_libraries(csgtexttest tests-nocgal ${TESTS-NOCGAL-LIBRARIES}) # add_executable(cgalcachetest cgalcachetest.cc) set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") -target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY}) +target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY}) # # openscad no-qt # add_executable(openscad_nogui ../src/openscad.cc) set_target_properties(openscad_nogui PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -DEIGEN_DONT_ALIGN -DENABLE_CGAL -DENABLE_OPENCSG ${CGAL_CXX_FLAGS_INIT}") -target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${OPENCSG_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY}) +target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${OPENCSG_LIBRARY} ${APP_SERVICES_LIBRARY}) # # GUI binary tests @@ -1010,7 +1010,16 @@ add_cmdline_test(openscad-nonascii EXE ${OPENSCAD_BINPATH} ARGS -o FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/sfære.scad) -# Image output +# Variable override (-D arg) + +# Override simple variable +add_cmdline_test(openscad-override EXE ${OPENSCAD_BINPATH} + ARGS -D a=3$ -o + SUFFIX echo + FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/override.scad) + + +# Image output parameters add_cmdline_test(openscad-imgsize EXE ${OPENSCAD_BINPATH} ARGS --imgsize 100,100 -o SUFFIX png diff --git a/tests/regression/echotest/cross-tests-expected.echo b/tests/regression/echotest/cross-tests-expected.echo new file mode 100644 index 00000000..ad80dacf --- /dev/null +++ b/tests/regression/echotest/cross-tests-expected.echo @@ -0,0 +1,27 @@ +ECHO: [-3, 6, -3] +ECHO: [17, -10, 8] +ECHO: [15, 15, 15] +WARNING: Invalid value (NaN) in parameter vector for cross() +ECHO: undef +WARNING: Invalid value (INF) in parameter vector for cross() +ECHO: undef +WARNING: Invalid value (INF) in parameter vector for cross() +ECHO: undef +WARNING: Invalid number of parameters for cross() +ECHO: undef +WARNING: Invalid type of parameters for cross() +ECHO: undef +WARNING: Invalid type of parameters for cross() +ECHO: undef +WARNING: Invalid number of parameters for cross() +ECHO: undef +WARNING: Invalid vector size of parameter for cross() +ECHO: undef +WARNING: Invalid vector size of parameter for cross() +ECHO: undef +WARNING: Invalid value in parameter vector for cross() +ECHO: undef +WARNING: Invalid value in parameter vector for cross() +ECHO: undef +WARNING: Invalid value in parameter vector for cross() +ECHO: undef diff --git a/tests/regression/echotest/norm-tests-expected.echo b/tests/regression/echotest/norm-tests-expected.echo new file mode 100644 index 00000000..cfa2e572 --- /dev/null +++ b/tests/regression/echotest/norm-tests-expected.echo @@ -0,0 +1,26 @@ +ECHO: 0 +ECHO: 1 +ECHO: 2.2360679775 +ECHO: 3.74165738677 +ECHO: 5.47722557505 +ECHO: undef +ECHO: nan +ECHO: inf +ECHO: inf +ECHO: undef +ECHO: undef +ECHO: undef +ECHO: undef + WARNING: Incorrect arguments to norm() +ECHO: undef + WARNING: Incorrect arguments to norm() +ECHO: undef + WARNING: Incorrect arguments to norm() +ECHO: undef + WARNING: Incorrect arguments to norm() +ECHO: undef + WARNING: Incorrect arguments to norm() +ECHO: undef +ECHO: undef +ECHO: undef +ECHO: undef diff --git a/tests/regression/openscad-override/override-expected.echo b/tests/regression/openscad-override/override-expected.echo new file mode 100644 index 00000000..99b37a25 --- /dev/null +++ b/tests/regression/openscad-override/override-expected.echo @@ -0,0 +1 @@ +ECHO: 3, 2