diff options
author | Marius Kintel <marius@kintel.net> | 2013-06-18 05:46:48 (GMT) |
---|---|---|
committer | Marius Kintel <marius@kintel.net> | 2013-06-18 05:46:48 (GMT) |
commit | 6d91540e4cc3f9fe0caaea63ac64518a5626d28b (patch) | |
tree | ca7034453a1f55124e4b7378aaab22985aff6a21 | |
parent | 95947a877b8e88521a7f00348d56c89e9b7c2a79 (diff) | |
parent | 6c7d386a3338039416ced323bf1aa75edbb43d19 (diff) |
Merge branch 'master' into epec-kernel
156 files changed, 3916 insertions, 660 deletions
diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 56b92d9..455515c 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,31 +1,54 @@ -OpenSCAD 2013.XX +OpenSCAD 2013.06 ================ -Features: -o Recursive modules and functions is now supported (including cascading child() operations) +Language Features: +o linear_extrude now takes a scale parameter: + linear_extrude(height=a, slices=b, twist=c, scale=[x,y]) +o Recursive use of modules is now supported (including cascading child() operations): + https://github.com/openscad/openscad/blob/master/examples/example024.scad o Parameter list values can now depend on earlier values, e.g. for (i=[0:2], j=[0:i]) .. -o Console output is now enabled on Windows through the openscad.com executable +o value assignments in parameters can now depend on already declared parameters +o Added resize() module: + http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations#resize + +Program Features: o Added basic syntax highlighting in the editor +o There is now a built-in library path in user-space: + http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Libraries#Library_Locations +o Commandline output to PNG, with various camera and rendering settings. + Run openscad -h to see usage info or see the OpenSCAD wiki user manual. +o Attempting to open dxf, off or stl files in the GUI will now create an import statement. +o The preview operator (%) will now preserve any manually set color +o The highlight operator (#) will now color the object in transparent red o Mac: Added document icon o Mac: Added auto-update check -o Commandline output to PNG, with various camera and rendering settings -o resize() command introduced -o Regression test now creates single monolithic .html file for easier uploading -o value reassignment is now less strict -o value assignments in parameters can now depend on already declared parameters -o Attempting to open dxf, off or stl files in the GUI will now create an import statement. +o Windows: Better cmd-line support using the openscad.com executable Bugfixes: o Importing files is now always relative to the importing script, also for libraries -o OpenCSG rendering sometimes crashed when rendering large models o We didn't always print a warning when CSG normalization created too many elements o Binary STLs can now be read on big endian architectures o Some binary STLs couldn't be read o Fixed some issues related to ARM builds -o Changed multmatrix floating-point output to improve dumptest portability -o Regression test auto-starts & stops Xvfb / Xvnc if on headless unix machine o CGAL triangulation more lenient- enables partial rendering of 'bad' DXF data -o Fixes problem where local changes are overwritten on automatic reload when included files has changed. +o The Automatic Reload feature is now more robust +o If a file couldn't be saved it no longer fails silently +o Fixed a number of crashes related to CGAL and OpenCSG rendering or complex models +o The lookup() function had bad boundary condition behavior +o The surface() module failed when the .dat file lacked a trailing newline +o The hull() module could crash if any of the children were empty objects +o Some problems using unicode filenames have been fixed + +Misc: +o Build scripts have been further improved +o Regression test now creates single monolithic .html file for easier uploading +o Regression test auto-starts & stops Xvfb / Xvnc if on headless unix machine +o The backend is finally independent of Qt +o Windows: We now have a 64-bit version + +Known Bugs: +o Linux: command-line png rendering on Gallium is flaky. + Workaround: use CGAL --render or hardware rendering. OpenSCAD 2013.01 ================ @@ -3,7 +3,7 @@ bison.input = BISONSOURCES bison.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.cpp bison.commands = bison -d -p ${QMAKE_FILE_BASE} -o ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.cpp ${QMAKE_FILE_IN} - bison.commands += && mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h + bison.commands += && if [ -e ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ]; then mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h ; fi bison.CONFIG += target_predeps bison.variable_out = GENERATED_SOURCES silent:bison.commands = @echo Bison ${QMAKE_FILE_IN} && $$bison.commands @@ -11,7 +11,7 @@ bison_header.input = BISONSOURCES bison_header.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h bison_header.commands = bison -d -p ${QMAKE_FILE_BASE} -o ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.cpp ${QMAKE_FILE_IN} - bison_header.commands += && mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h + bison_header.commands += && if [ -e ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ]; then mv ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.hpp ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.h ; fi bison_header.CONFIG += target_predeps no_link silent:bison_header.commands = @echo Bison ${QMAKE_FILE_IN} && $$bison.commands QMAKE_EXTRA_COMPILERS += bison_header @@ -6,7 +6,7 @@ boost { !isEmpty(BOOST_DIR) { QMAKE_INCDIR += $$BOOST_DIR message("boost location: $$BOOST_DIR") - win32: QMAKE_LIBDIR += -L$$BOOST_DIR/lib + win*: QMAKE_LIBDIR += -L$$BOOST_DIR/lib } CONFIG(mingw-cross-env) { @@ -16,7 +16,7 @@ boost { BOOST_LINK_FLAGS = -lboost_thread_win32-mt -lboost_program_options-mt -lboost_filesystem-mt -lboost_system-mt -lboost_regex-mt -lboost_chrono-mt } - isEmpty(BOOST_LINK_FLAGS):win32 { + isEmpty(BOOST_LINK_FLAGS):win* { BOOST_LINK_FLAGS = -llibboost_thread-vc90-mt-s-1_46_1 -llibboost_program_options-vc90-mt-s-1_46_1 -llibboost_filesystem-vc90-mt-s-1_46_1 -llibboost_system-vc90-mt-s-1_46_1 -llibboost_regex-vc90-mt-s-1_46_1 } @@ -6,7 +6,7 @@ cgal { CGAL_DIR = $$(CGALDIR) !isEmpty(CGAL_DIR) { QMAKE_INCDIR += $$CGAL_DIR/include - win32: QMAKE_INCDIR += $$CGAL_DIR/auxiliary/gmp/include + win*: QMAKE_INCDIR += $$CGAL_DIR/auxiliary/gmp/include QMAKE_LIBDIR += $$CGAL_DIR/lib message("CGAL location: $$CGAL_DIR") } @@ -15,7 +15,7 @@ cgal { LIBS += -lgmp -lmpfr -lCGAL QMAKE_CXXFLAGS += -frounding-math } else { - win32 { + win* { *-g++* { QMAKE_CXXFLAGS += -frounding-math } @@ -3,7 +3,7 @@ MOC_DIR = objects UI_DIR = objects RCC_DIR = objects -include(win32.pri) +include(win.pri) include(flex.pri) include(bison.pri) include(cgal.pri) diff --git a/contrib/scad-mode.el b/contrib/scad-mode.el index 5961b08..68a47e6 100644 --- a/contrib/scad-mode.el +++ b/contrib/scad-mode.el @@ -66,7 +66,7 @@ "round" "ceil" "floor" "pow" "sqrt" "exp" "log" "ln" "str" - "lookup" "version" "version_num" + "lookup" "version" "version_num" "len" "search" "dxf_dim" "dxf_cross" ;;dxfdim.cc ) "SCAD functions." @@ -74,7 +74,7 @@ :group 'scad-font-lock) (defcustom scad-modules - '("child" "echo" "assign" "for" "intersection_for" "if" ;;control.cc + '("child" "echo" "assign" "for" "intersection_for" "if" "else" ;;control.cc "cube" "sphere" "cylinder" "polyhedron" "square" "circle" "polygon" ;;primitives.cc "scale" "rotate" "translate" "mirror" "multmatrix" ;;transform.cc "union" "difference" "intersection" ;;csgops.cc @@ -86,7 +86,7 @@ "import_stl" "import_off" "import_dxf" "import" ;;import.cc "group" ;;builtin.cc "projection" ;;projection.cc - "minkowski" "glide" "subdiv" "hull" ;;cgaladv.cc + "minkowski" "glide" "subdiv" "hull" "resize" ;;cgaladv.cc ) "SCAD modules." :type 'list diff --git a/doc/TODO.txt b/doc/TODO.txt index d05df2c..e56dbd7 100644 --- a/doc/TODO.txt +++ b/doc/TODO.txt @@ -227,7 +227,7 @@ DOCUMENTATION ------------- o Auto-generate API documentation instead of, in addition to or combined with, the wikibooks docs. o Write checklists for typical extension work (add new module, add new function) - -> make sure new test files are added + -> syntax highlighter, test files, examples, documentation (external editor modes) o Clarify include/use better in the wikibook docs (e.g. that use'd modules have to be self-contained) TESTING diff --git a/doc/release-checklist.txt b/doc/release-checklist.txt index f5bb759..d239278 100644 --- a/doc/release-checklist.txt +++ b/doc/release-checklist.txt @@ -1,6 +1,19 @@ OpenSCAD Release Checklist -------------------------- +o Pre-release preparations + - Merge MCAD + o In MCAD clone: + $ git fetch upstream + $ git merge upstream/master + $ git push + o In OpenSCAD: + $ cd libraries/MCAD + $ git pull + $ cd ../.. + $ git commit -m "Updated MCAD" + $ git push + (See bottom of this file for how to build release binaries) o Set VERSION and VERSIONDATE environment variable @@ -31,19 +44,24 @@ o git push --tags o Upload Source package $ ./scripts/googlecode_upload.py -s 'Source Code' -p openscad -l Featured,Type-Source openscad-$VERSION.src.tar.gz + $ scp openscad-$VERSION.src.tar.gz openscad@files.openscad.org:www o Remove VERSION environment variable $ unset VERSION +o Write release email/blog entry o Update web page -o Write email to mailing list + - news.html + - inc/src_release_links.js o Update external resources: - http://en.wikipedia.org/wiki/OpenSCAD +o Write to mailing list +o Tweet o Notify package managers - Ubuntu: https://launchpad.net/~chrysn - Fedora: Miro Hrončok <miro@hroncok.cz> or <mhroncok@redhat.com> - - MacPorts: - + - OpenSUSE: Pavol Rusnak <prusnak@opensuse.org> + - MacPorts: Frank Schima <macports2000@gmail.com> Build and Upload Release Binaries --------------------------------- @@ -66,10 +84,6 @@ Linux: $ ./scripts/googlecode_upload.py -s 'Linux Binaries' -p openscad openscad-$VERSION.x86-ARCH.tar.gz -l Featured,OpSys-Linux,Type-Archive openscad-$VERSION.x86-ARCH.tar.gz o Update web page with download links -Windows mingw cross-build: FIXME 32 vs. 64 bit +Windows mingw cross-build: - $ ./scripts/publish-mingw-x.sh -> mingw32/Openscad-$VERSION.zip - -> mingw32/Openscad-$VERSION-Installer.exe - $ ./scripts/googlecode_upload.py -s 'Windows Binaries' -p openscad OpenSCAD-$VERSION.zip -l Featured,OpSys-Windows,Type-Archive OpenSCAD-$VERSION.zip - $ ./scripts/googlecode_upload.py -s 'Windows Installer' -p openscad OpenSCAD-$VERSION-Installer.exe -l Featured,OpSys-Windows,Type-Installer OpenSCAD-$VERSION-Installer.exe - o Update web page with download links +FIXME: Adapt scripts/builder.sh to build release binaries diff --git a/examples/example017.scad b/examples/example017.scad index 9013d4e..4279546 100644 --- a/examples/example017.scad +++ b/examples/example017.scad @@ -1,6 +1,6 @@ // To render the DXF file from the command line: -// openscad -x example017.dxf -D'mode="parts"' example017.scad +// openscad -o example017.dxf -D'mode="parts"' example017.scad // mode = "parts"; // mode = "exploded"; diff --git a/libraries/MCAD b/libraries/MCAD -Subproject 9af89906fa87e2c7fa21f914f00f3fe3879c69f +Subproject 9a958fd11b0a6b5f8becd37c4f8a42f585abfcd diff --git a/mingw-cross-env.pri b/mingw-cross-env.pri index e696b56..07a0fc1 100644 --- a/mingw-cross-env.pri +++ b/mingw-cross-env.pri @@ -13,6 +13,9 @@ CONFIG(mingw-cross-env) { LIBS += mingw-cross-env/lib/libgmp.a LIBS += mingw-cross-env/lib/libCGAL.a QMAKE_CXXFLAGS += -fpermissive + WINSTACKSIZE = 8388608 # 8MB # github issue 116 + QMAKE_CXXFLAGS += -Wl,--stack,$$WINSTACKSIZE + LIBS += -Wl,--stack,$$WINSTACKSIZE QMAKE_DEL_FILE = rm -f QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedefs #eigen3 } diff --git a/openscad.pro b/openscad.pro index 542bcc3..fd9f494 100644 --- a/openscad.pro +++ b/openscad.pro @@ -31,7 +31,7 @@ isEmpty(QT_VERSION) { include(version.pri) # for debugging link problems (use nmake -f Makefile.Release > log.txt) -win32 { +win* { # QMAKE_LFLAGS += -VERBOSE } debug: DEFINES += DEBUG @@ -88,7 +88,7 @@ else { TARGET = openscad } -win32 { +win* { RC_FILE = openscad_win32.rc } @@ -107,6 +107,7 @@ netbsd* { QMAKE_LFLAGS += -L/usr/X11R7/lib QMAKE_LFLAGS += -Wl,-R/usr/X11R7/lib QMAKE_LFLAGS += -Wl,-R/usr/pkg/lib + !clang: { QMAKE_CXXFLAGS += -std=c++0x } !isEmpty(OPENSCAD_LIBDIR) { QMAKE_CFLAGS = -I$$OPENSCAD_LIBDIR/include $$QMAKE_CFLAGS QMAKE_CXXFLAGS = -I$$OPENSCAD_LIBDIR/include $$QMAKE_CXXFLAGS @@ -171,7 +172,7 @@ CONFIG(mingw-cross-env) { include(mingw-cross-env.pri) } -win32 { +win* { FLEXSOURCES = src/lexer.l BISONSOURCES = src/parser.y } else { @@ -302,6 +303,7 @@ SOURCES += src/version_check.cc \ src/parsersettings.cc \ src/stl-utils.cc \ src/boost-utils.cc \ + src/PlatformUtils.cc \ \ src/nodedumper.cc \ src/traverser.cc \ @@ -347,7 +349,7 @@ macx { SOURCES += src/imageutils-macosx.cc OBJECTIVE_SOURCES += src/OffscreenContextCGL.mm } -win32* { +win* { SOURCES += src/imageutils-lodepng.cc SOURCES += src/OffscreenContextWGL.cc } @@ -384,7 +386,14 @@ macx { src/EventFilter.h \ src/CocoaUtils.h SOURCES += src/AppleEvents.cc - OBJECTIVE_SOURCES += src/CocoaUtils.mm + OBJECTIVE_SOURCES += src/CocoaUtils.mm \ + src/PlatformUtils-mac.mm +} +unix:!macx { + SOURCES += src/PlatformUtils-posix.cc +} +win* { + SOURCES += src/PlatformUtils-win.cc } isEmpty(PREFIX):PREFIX = /usr/local diff --git a/scripts/builder.sh b/scripts/builder.sh index 6a143e3..b00919f 100755 --- a/scripts/builder.sh +++ b/scripts/builder.sh @@ -11,6 +11,9 @@ # todo - make linux work # # todo - detect failure and stop +# +# todo - generalize to build release binaries as well +# init_variables() { @@ -94,8 +97,11 @@ upload_win_generic() if [ $DRYRUN ]; then echo dry run, not uploading to googlecode echo cmd - python ./scripts/googlecode_upload.py -s '"'$summary'"' $opts + echo dry run, not uploading to files.openscad.org + echo scp -v $filename openscad@files.openscad.org:www/ else python ./scripts/googlecode_upload.py -s "$summary" $opts + scp -v $filename openscad@files.openscad.org:www/ fi } @@ -173,22 +179,23 @@ update_win_www_download_links() cd inc echo `pwd` BASEURL='https://openscad.googlecode.com/files/' + # BASEURL='http://files.openscad.org' DATECODE=`date +"%Y.%m.%d"` rm win_snapshot_links.js - echo "snapinfo['WIN64_SNAPSHOT1_URL'] = '$BASEURL$WIN64_PACKAGEFILE1'" >> win_snapshot_links.js - echo "snapinfo['WIN64_SNAPSHOT2_URL'] = '$BASEURL$WIN64_PACKAGEFILE2'" >> win_snapshot_links.js - echo "snapinfo['WIN64_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js - echo "snapinfo['WIN64_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js - echo "snapinfo['WIN64_SNAPSHOT1_SIZE'] = '$WIN64_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js - echo "snapinfo['WIN64_SNAPSHOT2_SIZE'] = '$WIN64_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js - - echo "snapinfo['WIN32_SNAPSHOT1_URL'] = '$BASEURL$WIN32_PACKAGEFILE1'" >> win_snapshot_links.js - echo "snapinfo['WIN32_SNAPSHOT2_URL'] = '$BASEURL$WIN32_PACKAGEFILE2'" >> win_snapshot_links.js - echo "snapinfo['WIN32_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js - echo "snapinfo['WIN32_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js - echo "snapinfo['WIN32_SNAPSHOT1_SIZE'] = '$WIN32_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js - echo "snapinfo['WIN32_SNAPSHOT2_SIZE'] = '$WIN32_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js + echo "fileinfo['WIN64_SNAPSHOT1_URL'] = '$BASEURL$WIN64_PACKAGEFILE1'" >> win_snapshot_links.js + echo "fileinfo['WIN64_SNAPSHOT2_URL'] = '$BASEURL$WIN64_PACKAGEFILE2'" >> win_snapshot_links.js + echo "fileinfo['WIN64_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js + echo "fileinfo['WIN64_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js + echo "fileinfo['WIN64_SNAPSHOT1_SIZE'] = '$WIN64_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js + echo "fileinfo['WIN64_SNAPSHOT2_SIZE'] = '$WIN64_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js + + echo "fileinfo['WIN32_SNAPSHOT1_URL'] = '$BASEURL$WIN32_PACKAGEFILE1'" >> win_snapshot_links.js + echo "fileinfo['WIN32_SNAPSHOT2_URL'] = '$BASEURL$WIN32_PACKAGEFILE2'" >> win_snapshot_links.js + echo "fileinfo['WIN32_SNAPSHOT1_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js + echo "fileinfo['WIN32_SNAPSHOT2_NAME'] = 'OpenSCAD $DATECODE'" >> win_snapshot_links.js + echo "fileinfo['WIN32_SNAPSHOT1_SIZE'] = '$WIN32_PACKAGEFILE1_SIZE'" >> win_snapshot_links.js + echo "fileinfo['WIN32_SNAPSHOT2_SIZE'] = '$WIN32_PACKAGEFILE2_SIZE'" >> win_snapshot_links.js echo 'modified win_snapshot_links.js' PAGER=cat git diff @@ -200,6 +207,9 @@ update_win_www_download_links() fi } +# FIXME: We might be running this locally and not need an ssh agent. +# Before checking $SSH_AUTH_SOCK, try 'ssh -T git@github.com' to verify that we +# can access github over ssh check_ssh_agent() { if [ $DRYRUN ]; then echo 'skipping ssh, dry run'; return; fi diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh index ef8c904..5fddb13 100755 --- a/scripts/check-dependencies.sh +++ b/scripts/check-dependencies.sh @@ -87,11 +87,16 @@ mpfr_sysver() gmp_sysver() { # on some systems you have VERSION in gmp-$arch.h not gmp.h. use gmp*.h - if [ ! -e $1/include ]; then return; fi - gmppaths=`ls $1/include | grep ^gmp` + if [ -e $1/include/multiarch-x86_64-linux ]; then + subdir=include/multiarch-x86_64-linux + else + subdir=include + fi + if [ ! -e $1/$subdir ]; then return; fi + gmppaths=`ls $1/$subdir | grep ^gmp` if [ ! "$gmppaths" ]; then return; fi for gmpfile in $gmppaths; do - gmppath=$1/include/$gmpfile + gmppath=$1/$subdir/$gmpfile if [ "`grep __GNU_MP_VERSION $gmppath`" ]; then gmpmaj=`grep "define *__GNU_MP_VERSION *[0-9]*" $gmppath | awk '{print $3}'` gmpmin=`grep "define *__GNU_MP_VERSION_MINOR *[0-9]*" $gmppath | awk '{print $3}'` @@ -155,8 +160,15 @@ flex_sysver() bison_sysver() { + # bison (GNU Bison) 2.7.12-4996 if [ ! -x $1/bin/bison ]; then return ; fi - bison_sysver_result=`$1/bin/bison --version | grep bison | sed s/"[^0-9.]"/" "/g` + bison_sver=`$1/bin/bison --version | grep bison` + debug bison_sver1: $bison_sver + bison_sver=`echo $bison_sver | awk -F ")" ' { print $2 } '` + debug bison_sver2: $bison_sver + bison_sver=`echo $bison_sver | awk -F "-" ' { print $1 } '` + debug bison_sver3: $bison_sver + bison_sysver_result=$bison_sver } gcc_sysver() @@ -425,7 +437,7 @@ find_installed_version() debug $depname"_sysver" $syspath eval $depname"_sysver" $syspath fsv_tmp=`eval echo "$"$depname"_sysver_result"` - if [ $fsv_tmp ]; then break; fi + if [ $fsv_tmp ]; then break; fi fi done fi @@ -511,6 +523,7 @@ main() deps="qt4 cgal gmp mpfr boost opencsg glew eigen gcc bison flex make" #deps="$deps curl git" # not technically necessary for build #deps="$deps python cmake imagemagick" # only needed for tests + #deps="cgal" pretty_print title for depname in $deps; do debug "processing $dep" diff --git a/scripts/git-archive-all.py b/scripts/git-archive-all.py index ccfb08a..0088e1a 100755 --- a/scripts/git-archive-all.py +++ b/scripts/git-archive-all.py @@ -1,171 +1,464 @@ #! /usr/bin/env python +# coding=utf-8 + +from __future__ import print_function +from __future__ import unicode_literals + +__version__ = "1.7" import sys -from os import path, chdir -from subprocess import Popen, PIPE -from sys import argv, stdout -from fnmatch import fnmatch +from os import path, extsep +from subprocess import Popen, PIPE, CalledProcessError class GitArchiver(object): """ GitArchiver - + Scan a git repository and export all tracked files, and submodules. Checks for .gitattributes files in each directory and uses 'export-ignore' pattern entries for ignore files in the archive. - - Automatically detects output format extension: zip, tar, bz2, or gz + + Automatically detects output format extension: zip, tar, bz2, or gz. """ - - def __init__(self, prefix='', verbose=False, exclude=True, extra=[]): - self.prefix = prefix - self.verbose = verbose - self.exclude = exclude - self.extra = extra - - self._excludes = [] + def __init__(self, prefix='', verbose=False, exclude=True, force_sub=False, extra=None, main_repo_abspath=None): + """ + @type prefix: string + @param prefix: Prefix used to prepend all paths in the resulting archive. + + @type verbose: bool + @param verbose: Determines verbosity of the output (stdout). + + @type exclude: bool + @param exclude: Determines whether archiver should follow rules specified in .gitattributes files. + Defaults to True. + + @type force_sub: bool + @param force_sub: Determines whether submodules are initialized and updated before archiving. + Defaults to False - def create(self, output_file): + @type extra: list + @param extra: List of extra paths to include in the resulting archive. + + @type main_repo_abspath: string + @param main_repo_abspath: Absolute path to the main repository (or one of subdirectories). + If None, current cwd is used. + If given path is path to a subdirectory (but not a submodule directory!) + it will be replaced with abspath to toplevel directory of the repository. """ - create(str output_file) -> None - - Creates the archive, written to the given output_file - Filetype may be one of: gz, zip, bz2, tar + if extra is None: + extra = [] + + if main_repo_abspath is None: + main_repo_abspath = path.abspath('') + elif not path.isabs(main_repo_abspath): + raise ValueError("You MUST pass absolute path to the main git repository.") + + # Raises an exception if there is no repo under main_repo_abspath. + try: + self.run_shell("[ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1", main_repo_abspath) + except Exception as e: + raise ValueError("Not a git repository (or any of the parent directories).".format(path=main_repo_abspath)) + + # Detect toplevel directory of the repo. + main_repo_abspath = path.abspath(self.read_git_shell('git rev-parse --show-toplevel', main_repo_abspath).rstrip()) + + self.prefix = prefix + self.verbose = verbose + self.exclude = exclude + self.extra = extra + self.force_sub = force_sub + self.main_repo_abspath = main_repo_abspath + + def create(self, output_path, dry_run=False, output_format=None): """ - # - # determine the format - # - _, _, format = output_file.rpartition(".") + Creates the archive, written to the given output_file_path + + Type of the archive is determined either by extension of output_file_path or by the format argument. + Supported formats are: gz, zip, bz2, tar, tgz + + @type output_path: string + @param output_path: Output file path. - if format.lower() == 'zip': + @type dry_run: bool + @param dry_run: Determines whether create should do nothing but print what it would archive. + + @type output_format: string + @param output_format: Determines format of the output archive. + If None, format is determined from extension of output_file_path. + """ + if output_format is None: + file_name, file_ext = path.splitext(output_path) + output_format = file_ext[len(extsep):].lower() + + if output_format == 'zip': from zipfile import ZipFile, ZIP_DEFLATED - output_archive = ZipFile(path.abspath(output_file), 'w') - add = lambda name, arcname: output_archive.write(name, self.prefix + arcname, ZIP_DEFLATED) - - elif format.lower() in ['tar', 'bz2', 'gz']: + + if not dry_run: + archive = ZipFile(path.abspath(output_path), 'w') + add = lambda file_path, file_name: archive.write(file_path, path.join(self.prefix, file_name), ZIP_DEFLATED) + elif output_format in ['tar', 'bz2', 'gz', 'tgz']: import tarfile - t_mode = ('w:%s' % format) if format != 'tar' else 'w' - - output_archive = tarfile.open(path.abspath(output_file), t_mode) - add = lambda name, arcname: output_archive.add(name, self.prefix + arcname) + + if output_format == 'tar': + t_mode = 'w' + elif output_format == 'tgz': + t_mode = 'w:gz' + else: + t_mode = 'w:{f}'.format(f=output_format) + + if not dry_run: + archive = tarfile.open(path.abspath(output_path), t_mode) + add = lambda file_path, file_name: archive.add(file_path, path.join(self.prefix, file_name)) + else: + raise RuntimeError("Unknown format: {f}".format(f=output_format)) + + for file_path in self.extra: + if not dry_run: + if self.verbose: + print("Compressing {f} => {a}...".format(f=file_path, + a=path.join(self.prefix, file_path))) + add(file_path, file_path) + else: + print("{f} => {a}".format(f=file_path, + a=path.join(self.prefix, file_path))) + + for file_path in self.list_files(): + if not dry_run: + if self.verbose: + print("Compressing {f} => {a}...".format(f=path.join(self.main_repo_abspath, file_path), + a=path.join(self.prefix, file_path))) + add(path.join(self.main_repo_abspath, file_path), file_path) + else: + print("{f} => {a}".format(f=path.join(self.main_repo_abspath, file_path), + a=path.join(self.prefix, file_path))) + + if not dry_run: + archive.close() + + def get_path_components(self, repo_abspath, abspath): + """ + Splits given abspath into components until repo_abspath is reached. + + E.g. if repo_abspath is '/Documents/Hobby/ParaView/' and abspath is + '/Documents/Hobby/ParaView/Catalyst/Editions/Base/', function will return: + ['.', 'Catalyst', 'Editions', 'Base'] + + First element is always '.' (concrete symbol depends on OS). + + @type repo_abspath: string + @param repo_abspath: Absolute path to the git repository. + + @type abspath: string + @param abspath: Absolute path to within repo_abspath. + + @rtype: list + @return: List of path components. + """ + components = [] + + while not path.samefile(abspath, repo_abspath): + abspath, tail = path.split(abspath) + + if len(tail): + components.insert(0, tail) + + components.insert(0, path.relpath(repo_abspath, repo_abspath)) + return components + + def get_exclude_patterns(self, repo_abspath, repo_file_paths): + """ + Returns exclude patterns for a given repo. It looks for .gitattributes files in repo_file_paths. + + Resulting dictionary will contain exclude patterns per path (relative to the repo_abspath). + E.g. {('.', 'Catalyst', 'Editions', 'Base'), ['Foo*', '*Bar']} + + @type repo_abspath: string + @param repo_abspath: Absolute path to the git repository. + + @type repo_file_paths: list + @param repo_file_paths: List of paths relative to the repo_abspath that are under git control. + + @rtype: dict + @return: Dictionary representing exclude patterns. + Keys are tuples of strings. Values are lists of strings. + Returns None if self.exclude is not set. + """ + if not self.exclude: + return None + + def read_attributes(attributes_abspath): + patterns = [] + if path.isfile(attributes_abspath): + attributes = open(attributes_abspath, 'r').readlines() + patterns = [] + for line in attributes: + tokens = line.strip().split() + if "export-ignore" in tokens[1:]: + patterns.append(tokens[0]) + return patterns + + exclude_patterns = {(): []} + + # There may be no gitattributes. + try: + global_attributes_abspath = self.read_shell("git config --get core.attributesfile", repo_abspath).rstrip() + exclude_patterns[()] = read_attributes(global_attributes_abspath) + except: + # And valid to not have them. + pass + + for attributes_abspath in [path.join(repo_abspath, f) for f in repo_file_paths if f.endswith(".gitattributes")]: + # Each .gitattributes affects only files within its directory. + key = tuple(self.get_path_components(repo_abspath, path.dirname(attributes_abspath))) + exclude_patterns[key] = read_attributes(attributes_abspath) + + local_attributes_abspath = path.join(repo_abspath, ".git", "info", "attributes") + key = tuple(self.get_path_components(repo_abspath, repo_abspath)) + + if key in exclude_patterns: + exclude_patterns[key].extend(read_attributes(local_attributes_abspath)) else: - raise RuntimeError("Unknown format: '%s'" % format) - - # - # compress - # - - # extra files first (we may change folder later) - for name in self.extra: - if self.verbose: - toPath = '=> %s%s' % (self.prefix, name) if self.prefix else "" - print 'Compressing %s %s ...' % (name, toPath) - add(name, name) - - self._excludes = [] - - for name, arcname in self.listFiles(path.abspath('')): - if self.verbose: - toPath = '=> %s%s' % (self.prefix, arcname) if self.prefix else "" - print 'Compressing %s %s ...' % (arcname, toPath) - add(name, arcname) - - output_archive.close() - - - def listFiles(self, git_repositary_path, baselevel=''): - """ - listFiles(str git_repository_path, str baselevel='') -> iterator - - An iterator method that yields a tuple(filepath, fullpath) + exclude_patterns[key] = read_attributes(local_attributes_abspath) + + return exclude_patterns + + def is_file_excluded(self, repo_abspath, repo_file_path, exclude_patterns): + """ + Checks whether file at a given path is excluded. + + @type repo_abspath: string + @param repo_abspath: Absolute path to the git repository. + + @type repo_file_path: string + @param repo_file_path: Path to a file within repo_abspath. + + @type exclude_patterns: dict + @param exclude_patterns: Exclude patterns with format specified for get_exclude_patterns. + + @rtype: bool + @return: True if file should be excluded. Otherwise False. + """ + if exclude_patterns is None or not len(exclude_patterns): + return False + + from fnmatch import fnmatch + + file_name = path.basename(repo_file_path) + components = self.get_path_components(repo_abspath, path.join(repo_abspath, path.dirname(repo_file_path))) + + is_excluded = False + # We should check all patterns specified in intermediate directories to the given file. + # At the end we should also check for the global patterns (key '()' or empty tuple). + while not is_excluded: + key = tuple(components) + if key in exclude_patterns: + patterns = exclude_patterns[key] + for p in patterns: + if fnmatch(file_name, p) or fnmatch(repo_file_path, p): + if self.verbose: + print("Exclude pattern matched {pattern}: {path}".format(pattern=p, path=repo_file_path)) + is_excluded = True + + if not len(components): + break + + components.pop() + + return is_excluded + + def list_files(self, repo_path=''): + """ + An iterator method that yields a file path relative to main_repo_abspath for each file that should be included in the archive. Skips those that match the exclusion patterns found in any discovered .gitattributes files along the way. - - Recurses into submodules as well. - """ - for filepath in self.runShell('git ls-files --cached --full-name --no-empty-directory'): - fullpath = path.join(baselevel, filepath) - filename = path.basename(filepath) - - if self.exclude and filename == '.gitattributes': - self._excludes = [] - fh = open(filepath, 'r') - for line in fh: - if not line: break - tokens = line.strip().split() - if 'export-ignore' in tokens[1:]: - self._excludes.append(tokens[0]) - fh.close() - - if not filename.startswith('.git') and not path.isdir(filepath): - - # check the patterns first - ignore = False - for pattern in self._excludes: - if fnmatch(fullpath, pattern) or fnmatch(filename, pattern): - if self.verbose: print 'Exclude pattern matched (%s): %s' % (pattern, fullpath) - ignore = True - break - if ignore: - continue - - # baselevel is needed to tell the arhiver where it have to extract file - yield filepath, fullpath - - # get paths for every submodule - for submodule in self.runShell("git submodule --quiet foreach 'pwd'"): - chdir(submodule) - # in order to get output path we need to exclude repository path from the submodule path - submodule = submodule[len(git_repositary_path)+1:] - # recursion allows us to process repositories with more than one level of submodules - for git_file in self.listFiles(git_repositary_path, submodule): - yield git_file - - - + + Recurs into submodules as well. + + @type repo_path: string + @param repo_path: Path to the git submodule repository within the main git repository. + + @rtype: iterator + @return: Iterator to traverse files under git control relative to main_repo_abspath. + """ + repo_abspath = path.join(self.main_repo_abspath, repo_path) + repo_file_paths = self.read_git_shell("git ls-files --cached --full-name --no-empty-directory", repo_abspath).splitlines() + exclude_patterns = self.get_exclude_patterns(repo_abspath, repo_file_paths) + + for repo_file_path in repo_file_paths: + # Git puts path in quotes if file path has unicode characters. + repo_file_path = repo_file_path.strip('"') # file path relative to current repo + file_name = path.basename(repo_file_path) + + # Only list symlinks and files that don't start with git. + if file_name.startswith(".git") or (not path.islink(repo_file_path) and path.isdir(repo_file_path)): + continue + + main_repo_file_path = path.join(repo_path, repo_file_path) # file path relative to the main repo + + if self.is_file_excluded(repo_abspath, repo_file_path, exclude_patterns): + continue + + # Yield both repo_file_path and main_repo_file_path to preserve structure of the repo. + yield main_repo_file_path + + if self.force_sub: + self.run_shell("git submodule init", repo_abspath) + self.run_shell("git submodule update", repo_abspath) + + # List files of every submodule. + for submodule_path in self.read_shell("git submodule --quiet foreach 'pwd'", repo_abspath).splitlines(): + # In order to get output path we need to exclude repository path from submodule_path. + submodule_path = path.relpath(submodule_path, self.main_repo_abspath) + for file_path in self.list_files(submodule_path): + yield file_path + @staticmethod - def runShell(cmd): - return Popen(cmd, shell=True, stdout=PIPE).stdout.read().splitlines() - - - -if __name__ == "__main__": + def run_shell(cmd, cwd=None): + """ + Runs shell command. + + @type cmd: string + @param cmd: Command to be executed. + + @type cwd: string + @param cwd: Working directory. + + @rtype: int + @return: Return code of the command. + + @raise CalledProcessError: Raises exception if return code of the command is non-zero. + """ + p = Popen(cmd, shell=True, cwd=cwd) + p.wait() + + if p.returncode: + raise CalledProcessError(returncode=p.returncode, cmd=cmd) + + return p.returncode + + @staticmethod + def read_shell(cmd, cwd=None, encoding='utf-8'): + """ + Runs shell command and reads output. + + @type cmd: string + @param cmd: Command to be executed. + + @type cwd: string + @param cwd: Working directory. + + @type encoding: string + @param encoding: Encoding used to decode bytes returned by Popen into string. + + @rtype: string + @return: Output of the command. + + @raise CalledProcessError: Raises exception if return code of the command is non-zero. + """ + p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd) + output, _ = p.communicate() + output = output.decode(encoding) + + if p.returncode: + raise CalledProcessError(returncode=p.returncode, cmd=cmd, output=output) + + return output + + @staticmethod + def read_git_shell(cmd, cwd=None): + """ + Runs git shell command, reads output and decodes it into unicode string + + @type cmd: string + @param cmd: Command to be executed. + + @type cwd: string + @param cwd: Working directory. + + @rtype: string + @return: Output of the command. + + @raise CalledProcessError: Raises exception if return code of the command is non-zero. + """ + p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd) + output, _ = p.communicate() + output = output.decode('unicode_escape').encode('raw_unicode_escape').decode('utf-8') + + if p.returncode: + raise CalledProcessError(returncode=p.returncode, cmd=cmd, output=output) + + return output + + +if __name__ == '__main__': from optparse import OptionParser - parser = OptionParser(usage="usage: %prog [-v] [--prefix PREFIX] [--no-exclude] OUTPUT_FILE", version="%prog 1.0") - - parser.add_option('--prefix', type='string', dest='prefix', - default='', help="prepend PREFIX to each filename in the archive") - - parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='enable verbose mode') - - parser.add_option('--no-exclude', action='store_false', dest='exclude', - default=True, help="Dont read .gitattributes files for patterns containing export-ignore attrib") - - parser.add_option('--extra', action='append', dest='extra', default=[], + parser = OptionParser(usage="usage: %prog [-v] [--prefix PREFIX] [--no-exclude] [--force-submodules] [--dry-run] OUTPUT_FILE", + version="%prog {version}".format(version=__version__)) + + parser.add_option('--prefix', + type='string', + dest='prefix', + default='', + help="Prepend PREFIX to each filename in the archive. OUTPUT_FILE name is used by default to avoid tarbomb.") + + parser.add_option('-v', '--verbose', + action='store_true', + dest='verbose', + help='Enable verbose mode.') + + parser.add_option('--no-exclude', + action='store_false', + dest='exclude', + default=True, + help="Don't read .gitattributes files for patterns containing export-ignore attrib.") + + parser.add_option('--force-submodules', + action='store_true', + dest='force_sub', + help="Force a git submodule init && git submodule update at each level before iterating submodules.") + + parser.add_option('--extra', + action='append', + dest='extra', + default=[], help="Any additional files to include in the archive.") + parser.add_option('--dry-run', + action='store_true', + dest='dry_run', + help="Don't actually archive anything, just show what would be done.") options, args = parser.parse_args() - + if len(args) != 1: - parser.error('You must specify exactly one output file') - - outFile = args[0] - - if path.isdir(outFile): - parser.error('You cannot use directory as output') - - archiver = GitArchiver(options.prefix, - options.verbose, - options.exclude, - options.extra) - + parser.error("You must specify exactly one output file") + + output_file_path = args[0] + + if path.isdir(output_file_path): + parser.error("You cannot use directory as output") + + # avoid tarbomb + if options.prefix: + options.prefix = path.join(options.prefix, '') + else: + import re + + output_name = path.basename(output_file_path) + output_name = re.sub('(\.zip|\.tar|\.tgz|\.gz|\.bz2|\.tar\.gz|\.tar\.bz2)$', '', output_name) or "Archive" + options.prefix = path.join(output_name, '') + try: - archiver.create(outFile) - except Exception, e: - parser.exit(2, "%s\n" % e) - + archiver = GitArchiver(options.prefix, + options.verbose, + options.exclude, + options.force_sub, + options.extra) + archiver.create(output_file_path, options.dry_run) + except Exception as e: + parser.exit(2, "{exception}\n".format(exception=e)) + sys.exit(0) diff --git a/scripts/installer.nsi b/scripts/installer.nsi index 2cbd6d3..fea6563 100644 --- a/scripts/installer.nsi +++ b/scripts/installer.nsi @@ -17,6 +17,7 @@ CreateShortCut $SMPROGRAMS\OpenSCAD.lnk $INSTDIR\openscad.exe WriteUninstaller $INSTDIR\Uninstall.exe WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenSCAD" "DisplayName" "OpenSCAD (remove only)" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenSCAD" "UninstallString" "$INSTDIR\Uninstall.exe" +WriteRegStr HKCR ".scad" "PerceivedType" "text" SectionEnd Section "Uninstall" ${unregisterExtension} ".scad" "OpenSCAD_File" @@ -30,5 +31,6 @@ Delete $INSTDIR\libraries\boxes.scad Delete $INSTDIR\libraries\shapes.scad RMDir $INSTDIR\libraries Delete $INSTDIR\openscad.exe +Delete $INSTDIR\openscad.com RMDir $INSTDIR SectionEnd diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh index 088d8b4..03f8598 100755 --- a/scripts/macosx-build-dependencies.sh +++ b/scripts/macosx-build-dependencies.sh @@ -238,8 +238,9 @@ build_cgal() cd $BASEDIR/src rm -rf CGAL-$version if [ ! -f CGAL-$version.tar.gz ]; then - # 4.1 - curl -O https://gforge.inria.fr/frs/download.php/31641/CGAL-$version.tar.gz + # 4.2 + curl -O https://gforge.inria.fr/frs/download.php/32359/CGAL-$version.tar.gz + # 4.1 curl -O https://gforge.inria.fr/frs/download.php/31641/CGAL-$version.tar.gz # 4.1-beta1 curl -O https://gforge.inria.fr/frs/download.php/31348/CGAL-$version.tar.gz # 4.0.2 curl -O https://gforge.inria.fr/frs/download.php/31175/CGAL-$version.tar.gz # 4.0 curl -O https://gforge.inria.fr/frs/download.php/30387/CGAL-$version.tar.gz @@ -306,7 +307,9 @@ build_eigen() EIGENDIR="none" if [ $version = "2.0.17" ]; then EIGENDIR=eigen-eigen-b23437e61a07; fi - if [ $version = "3.1.2" ]; then EIGENDIR=eigen-eigen-5097c01bcdc4; fi + if [ $version = "3.1.2" ]; then EIGENDIR=eigen-eigen-5097c01bcdc4; + elif [ $version = "3.1.3" ]; then EIGENDIR=eigen-eigen-2249f9c22fe8; fi + if [ $EIGENDIR = "none" ]; then echo Unknown eigen version. Please edit script. exit 1 @@ -435,12 +438,13 @@ fi echo "Using basedir:" $BASEDIR mkdir -p $SRCDIR $DEPLOYDIR build_qt 4.8.4 -build_eigen 3.1.2 -build_gmp 5.1.1 +# NB! For eigen, also update the path in the function +build_eigen 3.1.3 +build_gmp 5.1.2 build_mpfr 3.1.2 build_boost 1.53.0 # NB! For CGAL, also update the actual download URL in the function -build_cgal 4.1 +build_cgal 4.2 build_glew 1.9.0 build_opencsg 1.3.2 if $OPTION_DEPLOY; then diff --git a/scripts/publish-macosx.sh b/scripts/publish-macosx.sh index a3b0090..3617570 100755 --- a/scripts/publish-macosx.sh +++ b/scripts/publish-macosx.sh @@ -26,9 +26,9 @@ update_www_download_links() if [ -f $webdir/$incfile ]; then cd $webdir - echo "snapinfo['MAC_SNAPSHOT_URL'] = '$BASEURL$packagefile'" > $incfile - echo "snapinfo['MAC_SNAPSHOT_NAME'] = 'OpenSCAD $version'" >> $incfile - echo "snapinfo['MAC_SNAPSHOT_SIZE'] = '$filesize'" >> $incfile + echo "fileinfo['MAC_SNAPSHOT_URL'] = '$BASEURL$packagefile'" > $incfile + echo "fileinfo['MAC_SNAPSHOT_NAME'] = 'OpenSCAD $version'" >> $incfile + echo "fileinfo['MAC_SNAPSHOT_SIZE'] = '$filesize'" >> $incfile echo 'modified mac_snapshot_links.js' git --no-pager diff @@ -67,7 +67,7 @@ if [[ $? != 0 ]]; then exit 1 fi -SIGNATURE=$(openssl dgst -sha1 -binary < OpenSCAD-$VERSION.dmg | openssl dgst -dss1 -sign dsa_priv.pem | openssl enc -base64) +SIGNATURE=$(openssl dgst -sha1 -binary < OpenSCAD-$VERSION.dmg | openssl dgst -dss1 -sign $HOME/.ssh/openscad-appcast.pem | openssl enc -base64) if [[ $VERSION == $VERSIONDATE ]]; then APPCASTFILE=appcast-snapshots.xml @@ -90,5 +90,10 @@ if [[ $? != 0 ]]; then exit 1 fi +scp OpenSCAD-$VERSION.dmg openscad@files.openscad.org:www +if [[ $? != 0 ]]; then + exit 1 +fi + # Update snapshot filename on web page update_www_download_links version=$VERSION packagefile=OpenSCAD-$VERSION.dmg filesize=$FILESIZE diff --git a/scripts/release-common.sh b/scripts/release-common.sh index 7d36907..8a1ed7c 100755 --- a/scripts/release-common.sh +++ b/scripts/release-common.sh @@ -52,13 +52,13 @@ elif [[ $OSTYPE == "linux-gnu" ]]; then fi if [ "`echo $* | grep mingw32`" ]; then - OS=LINXWIN + OS=UNIX_CROSS_WIN ARCH=32 echo Mingw-cross build using ARCH=32 fi if [ "`echo $* | grep mingw64`" ]; then - OS=LINXWIN + OS=UNIX_CROSS_WIN ARCH=64 echo Mingw-cross build using ARCH=64 fi @@ -90,7 +90,7 @@ fi echo "Checking pre-requisites..." case $OS in - LINXWIN) + UNIX_CROSS_WIN) MAKENSIS= if [ "`command -v makensis`" ]; then MAKENSIS=makensis @@ -120,10 +120,9 @@ echo "Building openscad-$VERSION ($VERSIONDATE) $CONFIGURATION..." if [ ! $NUMCPU ]; then echo "note: you can 'export NUMCPU=x' for multi-core compiles (x=number)"; - NUMCPU=2 -else - echo "NUMCPU: " $NUMCPU + NUMCPU=1 fi +echo "NUMCPU: " $NUMCPU CONFIG=deploy case $OS in @@ -138,7 +137,7 @@ case $OS in ZIPARGS="a -tzip" TARGET=release ;; - LINXWIN) + UNIX_CROSS_WIN) . ./scripts/setenv-mingw-xbuild.sh $ARCH TARGET=release ZIP="zip" @@ -148,7 +147,7 @@ esac case $OS in - LINXWIN) + UNIX_CROSS_WIN) cd $DEPLOYDIR && qmake VERSION=$VERSION OPENSCAD_COMMIT=$OPENSCAD_COMMIT CONFIG+=$CONFIG CONFIG+=mingw-cross-env CONFIG-=debug ../openscad.pro cd $OPENSCADDIR ;; @@ -158,7 +157,7 @@ case $OS in esac case $OS in - LINXWIN) + UNIX_CROSS_WIN) cd $DEPLOYDIR make clean ## comment out for test-run cd $OPENSCADDIR @@ -176,10 +175,17 @@ case $OS in #if the following files are missing their tried removal stops the build process on msys touch -t 200012121010 parser_yacc.h parser_yacc.cpp lexer_lex.cpp ;; + UNIX_CROSS_WIN) + # kludge to enable paralell make + touch -t 200012121010 $OPENSCADDIR/src/parser_yacc.h + touch -t 200012121010 $OPENSCADDIR/src/parser_yacc.cpp + touch -t 200012121010 $OPENSCADDIR/src/parser_yacc.hpp + touch -t 200012121010 $OPENSCADDIR/src/lexer_lex.cpp + ;; esac case $OS in - LINXWIN) + UNIX_CROSS_WIN) # make main openscad.exe cd $DEPLOYDIR make $TARGET -j$NUMCPU ## comment 4 test @@ -214,12 +220,12 @@ case $OS in EXAMPLESDIR=OpenSCAD.app/Contents/Resources/examples LIBRARYDIR=OpenSCAD.app/Contents/Resources/libraries ;; - LINXWIN) + UNIX_CROSS_WIN) EXAMPLESDIR=$DEPLOYDIR/openscad-$VERSION/examples/ LIBRARYDIR=$DEPLOYDIR/openscad-$VERSION/libraries/ rm -rf $DEPLOYDIR/openscad-$VERSION mkdir $DEPLOYDIR/openscad-$VERSION - ;; + ;; *) EXAMPLESDIR=openscad-$VERSION/examples/ LIBRARYDIR=openscad-$VERSION/libraries/ @@ -267,7 +273,7 @@ case $OS in rm -rf openscad-$VERSION echo "Binary created: openscad-$VERSION.zip" ;; - LINXWIN) + UNIX_CROSS_WIN) BINFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH.zip INSTFILE=$DEPLOYDIR/OpenSCAD-$VERSION-x86-$ARCH-Installer.exe diff --git a/scripts/setenv-mingw-xbuild.sh b/scripts/setenv-mingw-xbuild.sh index d3a014c..a88b752 100644 --- a/scripts/setenv-mingw-xbuild.sh +++ b/scripts/setenv-mingw-xbuild.sh @@ -6,6 +6,7 @@ # # source ./scripts/setenv-mingw-xbuild.sh # 32 bit build # source ./scripts/setenv-mingw-xbuild.sh 64 # 64 bit build +# source ./scripts/setenv-mingw-xbuild.sh clean # Clean up exported variables # # Prerequisites: # diff --git a/scripts/setenv-unibuild.sh b/scripts/setenv-unibuild.sh index 980fa7b..cb0b0a0 100644 --- a/scripts/setenv-unibuild.sh +++ b/scripts/setenv-unibuild.sh @@ -54,7 +54,9 @@ setenv_netbsd() QMAKESPEC=netbsd-g++ QTDIR=/usr/pkg/qt4 PATH=/usr/pkg/qt4/bin:$PATH - LD_LIBRARY_PATH=/usr/pkg/qt4/lib:/usr/X11R7/lib:$LD_LIBRARY_PATH + LD_LIBRARY_PATH=/usr/pkg/qt4/lib:$LD_LIBRARY_PATH + LD_LIBRARY_PATH=/usr/X11R7/lib:$LD_LIBRARY_PATH + LD_LIBRARY_PATH=/usr/pkg/lib:$LD_LIBRARY_PATH export QMAKESPEC export QTDIR diff --git a/scripts/uni-build-dependencies.sh b/scripts/uni-build-dependencies.sh index 60dbb74..6596c8a 100755 --- a/scripts/uni-build-dependencies.sh +++ b/scripts/uni-build-dependencies.sh @@ -290,11 +290,26 @@ build_cgal() ver3_7="curl --insecure -O https://gforge.inria.fr/frs/download.php/27641/CGAL-3.7.tar.gz" vernull="echo already downloaded..skipping" download_cmd=ver`echo $version | sed s/"\."/"_"/` - if [ -e CGAL-$version.tar.gz ]; then download_cmd=vernull; fi - if [ -e CGAL-$version.tar.bz2 ]; then download_cmd=vernull; fi + + if [ -e CGAL-$version.tar.gz ]; then + download_cmd=vernull; + fi + if [ -e CGAL-$version.tar.bz2 ]; then + download_cmd=vernull; + fi + `eval echo "$"$download_cmd` - if [ -e CGAL-$version.tar.gz ]; then tar xf CGAL-$version.tar.gz; fi - if [ -e CGAL-$version.tar.bz2 ]; then tar xf CGAL-$version.tar.bz2; fi + + zipper=gzip + suffix=gz + if [ -e CGAL-$version.tar.bz2 ]; then + zipper=bzip2 + suffix=bz2 + fi + + $zipper -f -d CGAL-$version.tar.$suffix; + tar xf CGAL-$version.tar + cd CGAL-$version # older cmakes have buggy FindBoost that can result in diff --git a/scripts/uni-get-dependencies.sh b/scripts/uni-get-dependencies.sh index e2fdaa7..31337c8 100755 --- a/scripts/uni-get-dependencies.sh +++ b/scripts/uni-get-dependencies.sh @@ -7,8 +7,9 @@ get_fedora_deps() { sudo yum install qt-devel bison flex eigen2-devel python-paramiko \ - boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc pkgconfig \ - git libXmu-devel curl imagemagick + boost-devel mpfr-devel gmp-devel glew-devel CGAL-devel gcc gcc-c++ pkgconfig \ + opencsg-devel git libXmu-devel curl imagemagick ImageMagick make \ + xorg-x11-server-Xvfb } get_qomo_deps() @@ -34,7 +35,8 @@ get_freebsd_deps() get_netbsd_deps() { sudo pkgin install bison boost cmake git bash eigen flex gmake gmp mpfr \ - qt4 glew cgal opencsg modular-xorg python27 py27-paramiko curl imagemagick + qt4 glew cgal opencsg modular-xorg python27 py27-paramiko curl \ + imagemagick ImageMagick } get_opensuse_deps() @@ -48,7 +50,7 @@ get_mageia_deps() sudo urpmi ctags sudo urpmi task-c-devel task-c++-devel libqt4-devel libgmp-devel \ libmpfr-devel libboost-devel eigen3-devel libglew-devel bison flex \ - cmake imagemagick python curl git + cmake imagemagick python curl git x11-server-xvfb } get_debian_deps() @@ -74,6 +76,8 @@ if [ -e /etc/issue ]; then get_debian_deps elif [ "`grep -i debian /etc/issue`" ]; then get_debian_deps + elif [ "`grep -i mint /etc/issue`" ]; then + get_debian_deps elif [ "`grep -i suse /etc/issue`" ]; then get_opensuse_deps elif [ "`grep -i fedora /etc/issue`" ]; then diff --git a/src/AboutDialog.html b/src/AboutDialog.html index 005f61f..b5a5d7c 100644 --- a/src/AboutDialog.html +++ b/src/AboutDialog.html @@ -7,7 +7,8 @@ make to do your testing. --> <head> - <meta name="qrichtext" content="1" /> + <meta charset="UTF-8"/> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> </head> @@ -74,18 +75,23 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li <b>OpenSCAD Github Project members (public)</b> </p> -<lu> +<ul> <li><a href="https://github.com/kintel">Marius Kintel </a> <li><a href="http://clifford.at">Clifford Wolf</a> <li><a href="http://www.github.com/GilesBathgate">Giles Bathgate</a> <li><a href="https://github.com/brad">Brad Pitcher</a> <li><a href="https://github.com/donbright">Don Bright</a> -</lu> +</ul> <p> -<b><a href="http://www.debian.org">Debian</a> maintainer:</b> - <a href="http://christian.amsuess.com/">chrysn</a> +<b>Package Maintainers</b> </p> +<ul> +<li><a href="http://www.debian.org">Debian</a> maintainer:</b> + <a href="http://christian.amsuess.com/">chrysn</a></lu> +<li><a href="http://fedoraproject.org/">Fedora</a> maintainer:</b> + <a href="http://hroncok.cz/">Miro Hrončok</a></lu> +</ul> <p> <b>Patches</b> diff --git a/src/CGALEvaluator.cc b/src/CGALEvaluator.cc index 8417636..8e1b5eb 100644 --- a/src/CGALEvaluator.cc +++ b/src/CGALEvaluator.cc @@ -280,7 +280,7 @@ Response CGALEvaluator::visit(State &state, const CsgNode &node) if (state.isPostfix()) { CGAL_Nef_polyhedron N; if (!isCached(node)) { - CGALEvaluator::CsgOp op; + CGALEvaluator::CsgOp op = CGE_UNION; switch (node.type) { case CSG_TYPE_UNION: op = CGE_UNION; diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc index 4624d4c..a6b654c 100644 --- a/src/CSGTermEvaluator.cc +++ b/src/CSGTermEvaluator.cc @@ -57,6 +57,7 @@ void CSGTermEvaluator::applyToChildren(const AbstractNode &node, CSGTermEvaluato } } if (t1 && node.modinst->isHighlight()) { + t1->flag = CSGTerm::FLAG_HIGHLIGHT; this->highlights.push_back(t1); } if (t1 && node.modinst->isBackground()) { @@ -95,6 +96,7 @@ static shared_ptr<CSGTerm> evaluate_csg_term_from_ps(const State &state, stream << node.name() << node.index(); shared_ptr<CSGTerm> t(new CSGTerm(ps, state.matrix(), state.color(), stream.str())); if (modinst->isHighlight()) { + t->flag = CSGTerm::FLAG_HIGHLIGHT; highlights.push_back(t); } if (modinst->isBackground()) { @@ -124,7 +126,7 @@ Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node) Response CSGTermEvaluator::visit(State &state, const CsgNode &node) { if (state.isPostfix()) { - CsgOp op; + CsgOp op = CSGT_UNION; switch (node.type) { case CSG_TYPE_UNION: op = CSGT_UNION; diff --git a/src/CocoaUtils.h b/src/CocoaUtils.h index ad5518b..8543d84 100644 --- a/src/CocoaUtils.h +++ b/src/CocoaUtils.h @@ -1,13 +1,10 @@ #ifndef COCOAUTILS_H_ #define COCOAUTILS_H_ -#include <string> - class CocoaUtils { public: static void endApplication(); - static std::string documentsPath(); }; #endif diff --git a/src/CocoaUtils.mm b/src/CocoaUtils.mm index 295ceb9..b72583c 100644 --- a/src/CocoaUtils.mm +++ b/src/CocoaUtils.mm @@ -1,6 +1,5 @@ #include "CocoaUtils.h" #import <Foundation/Foundation.h> -#include <stdio.h> void CocoaUtils::endApplication() { @@ -9,7 +8,3 @@ void CocoaUtils::endApplication() object:nil]; } -std::string CocoaUtils::documentsPath() -{ - return std::string([[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] UTF8String]); -} diff --git a/src/CsgInfo.h b/src/CsgInfo.h index fe953b5..774325b 100644 --- a/src/CsgInfo.h +++ b/src/CsgInfo.h @@ -57,7 +57,7 @@ public: if (this->root_norm_term) { this->root_chain = new CSGChain(); this->root_chain->import(this->root_norm_term); - PRINTB("Normalized CSG tree has %d elements", int(this->root_chain->polysets.size())); + PRINTB("Normalized CSG tree has %d elements", int(this->root_chain->objects.size())); } else { this->root_chain = NULL; diff --git a/src/MainWindow.h b/src/MainWindow.h index 378705e..1dcffeb 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -76,7 +76,6 @@ private: void refreshDocument(); void updateTemporalVariables(); bool fileChangedOnDisk(); - bool includesChanged(); bool compileTopLevelDocument(bool reload); bool compile(bool reload, bool procevents); void compileCSG(bool procevents); @@ -101,6 +100,7 @@ private slots: void actionSave(); void actionSaveAs(); void actionReload(); + void actionShowLibraryFolder(); private slots: void pasteViewportTranslation(); diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 8e995cd..68bc064 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -137,6 +137,7 @@ <addaction name="fileActionOpen"/> <addaction name="menuOpenRecent"/> <addaction name="menuExamples"/> + <addaction name="fileShowLibraryFolder"/> <addaction name="separator"/> <addaction name="fileActionClose"/> <addaction name="fileActionSave"/> @@ -176,14 +177,17 @@ <addaction name="designActionReloadAndCompile"/> <addaction name="designActionCompile"/> <addaction name="designActionCompileAndRender"/> + <addaction name="separator"/> <addaction name="designActionDisplayAST"/> <addaction name="designActionDisplayCSGTree"/> <addaction name="designActionDisplayCSGProducts"/> + <addaction name="separator"/> <addaction name="designActionExportSTL"/> <addaction name="designActionExportOFF"/> <addaction name="designActionExportDXF"/> <addaction name="designActionExportCSG"/> <addaction name="designActionExportImage"/> + <addaction name="separator"/> <addaction name="designActionFlushCaches"/> </widget> <widget class="QMenu" name="menu_View"> @@ -621,7 +625,7 @@ </action> <action name="helpActionManual"> <property name="text"> - <string>OpenSCAD Manual</string> + <string>Documentation</string> </property> </action> <action name="fileActionClearRecent"> @@ -688,6 +692,11 @@ <string>Check for Update..</string> </property> </action> + <action name="fileShowLibraryFolder"> + <property name="text"> + <string>Show Library Folder...</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index 4944495..99c0b20 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -14,50 +14,58 @@ #include <time.h> #include <sys/stat.h> +//#include "parsersettings.h" /*! FIXME: Implement an LRU scheme to avoid having an ever-growing module cache */ ModuleCache *ModuleCache::inst = NULL; -static bool is_modified(const std::string &filename, const time_t &mtime) -{ - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.c_str(), &st); - return (st.st_mtime > mtime); -} +/*! + Reevaluate the given file and recompile if necessary. + Returns NULL on any error (e.g. compile error or file not found) + If the given filename is relative, it means that the module hasn't been + previously located. +*/ FileModule *ModuleCache::evaluate(const std::string &filename) { - FileModule *lib_mod = NULL; + FileModule *lib_mod = (this->entries.find(filename) != this->entries.end()) ? + &(*this->entries[filename].module) : NULL; + + // Don't try to recursively evaluate - if the file changes + // during evaluation, that would be really bad. + if (lib_mod && lib_mod->isHandlingDependencies()) return lib_mod; + + bool shouldCompile = true; - // Create cache ID + // Create cache ID struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(filename.c_str(), &st); + 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; + std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size); - // Lookup in cache - if (this->entries.find(filename) != this->entries.end() && - this->entries[filename].cache_id == cache_id) { -#ifdef DEBUG -// Causes too much debug output -// PRINTB("Using cached library: %s (%s)", filename % cache_id); -#endif - lib_mod = &(*this->entries[filename].module); + // Lookup in cache + if (lib_mod) { + if (this->entries[filename].cache_id == cache_id) { + shouldCompile = false; - BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, lib_mod->includes) { - if (is_modified(item.first, item.second)) { + if (lib_mod->includesChanged()) { lib_mod = NULL; - break; + shouldCompile = true; } } } + else { + shouldCompile = valid; + } - // If cache lookup failed (non-existing or old timestamp), compile module - if (!lib_mod) { + // If cache lookup failed (non-existing or old timestamp), compile module + if (shouldCompile) { #ifdef DEBUG if (this->entries.find(filename) != this->entries.end()) { PRINTB("Recompiling cached library: %s (%s)", filename % cache_id); @@ -91,7 +99,7 @@ FileModule *ModuleCache::evaluate(const std::string &filename) if (lib_mod) { // We defer deletion so we can ensure that the new module won't - // have the same address as the old + // have the same address as the old delete oldmodule; this->entries[filename].module = lib_mod; } else { @@ -101,7 +109,9 @@ FileModule *ModuleCache::evaluate(const std::string &filename) print_messages_pop(); } - if (lib_mod) lib_mod->handleDependencies(); + if (lib_mod) { + lib_mod->handleDependencies(); + } return lib_mod; } @@ -111,3 +121,9 @@ void ModuleCache::clear() this->entries.clear(); } +FileModule *ModuleCache::lookup(const std::string &filename) +{ + return (this->entries.find(filename) != this->entries.end()) ? + &(*this->entries[filename].module) : NULL; +} + diff --git a/src/ModuleCache.h b/src/ModuleCache.h index b8ded38..7795ab7 100644 --- a/src/ModuleCache.h +++ b/src/ModuleCache.h @@ -9,6 +9,7 @@ class ModuleCache public: static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; } class FileModule *evaluate(const std::string &filename); + class FileModule *lookup(const std::string &filename); size_t size() { return this->entries.size(); } void clear(); diff --git a/src/OffscreenContextWGL.cc b/src/OffscreenContextWGL.cc index 0ccd68d..6e657fe 100644 --- a/src/OffscreenContextWGL.cc +++ b/src/OffscreenContextWGL.cc @@ -108,15 +108,33 @@ bool create_wgl_dummy_context(OffscreenContext &ctx) wc.style = CS_OWNDC; wc.lpfnWndProc = WndProc; wc.hInstance = inst; - wc.lpszClassName = (LPCWSTR)"OpenSCAD"; - RegisterClass( &wc ); + wc.lpszClassName = L"OpenSCAD"; + ATOM class_atom = RegisterClassW( &wc ); - HWND window = CreateWindow( (LPCWSTR)"OpenSCAD", (LPCWSTR)"OpenSCAD", - WS_CAPTION | WS_POPUPWINDOW, //| WS_VISIBLE, - 0, 0, ctx.width, ctx.height, NULL, NULL, inst, NULL ); + if ( class_atom == 0 ) { + cerr << "MS GDI - RegisterClass failed\n"; + cerr << "last-error code: " << GetLastError() << "\n"; + return false; + } + + LPCTSTR lpClassName = L"OpenSCAD"; + LPCTSTR lpWindowName = L"OpenSCAD"; + DWORD dwStyle = WS_CAPTION | WS_POPUPWINDOW; // | WS_VISIBLE + int x = 0; + int y = 0; + int nWidth = ctx.width; + int nHeight = ctx.height; + HWND hWndParent = NULL; + HMENU hMenu = NULL; + HINSTANCE hInstance = inst; + LPVOID lpParam = NULL; + + HWND window = CreateWindowW( lpClassName, lpWindowName, dwStyle, x, y, + nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam ); if ( window==NULL ) { cerr << "MS GDI - CreateWindow failed\n"; + cerr << "last-error code: " << GetLastError() << "\n"; return false; } @@ -127,6 +145,7 @@ bool create_wgl_dummy_context(OffscreenContext &ctx) HDC dev_context = GetDC( window ); if ( dev_context == NULL ) { cerr << "MS GDI - GetDC failed\n"; + cerr << "last-error code: " << GetLastError() << "\n"; return false; } @@ -145,18 +164,21 @@ bool create_wgl_dummy_context(OffscreenContext &ctx) chosenformat = ChoosePixelFormat( dev_context, &pixformat ); if (chosenformat==0) { cerr << "MS GDI - ChoosePixelFormat failed\n"; + cerr << "last-error code: " << GetLastError() << "\n"; return false; } bool spfok = SetPixelFormat( dev_context, chosenformat, &pixformat ); if (!spfok) { cerr << "MS GDI - SetPixelFormat failed\n"; + cerr << "last-error code: " << GetLastError() << "\n"; return false; } HGLRC gl_render_context = wglCreateContext( dev_context ); if ( gl_render_context == NULL ) { cerr << "MS WGL - wglCreateContext failed\n"; + cerr << "last-error code: " << GetLastError() << "\n"; ReleaseDC( ctx.window, ctx.dev_context ); return false; } @@ -164,6 +186,7 @@ bool create_wgl_dummy_context(OffscreenContext &ctx) bool mcok = wglMakeCurrent( dev_context, gl_render_context ); if (!mcok) { cerr << "MS WGL - wglMakeCurrent failed\n"; + cerr << "last-error code: " << GetLastError() << "\n"; return false; } diff --git a/src/OpenCSGRenderer.cc b/src/OpenCSGRenderer.cc index eb66687..b4acf82 100644 --- a/src/OpenCSGRenderer.cc +++ b/src/OpenCSGRenderer.cc @@ -77,35 +77,50 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, std::vector<OpenCSG::Primitive*> primitives; size_t j = 0; for (size_t i = 0;; i++) { - bool last = i == chain->polysets.size(); - if (last || chain->types[i] == CSGTerm::TYPE_UNION) { + const CSGChainObject &i_obj = chain->objects[i]; + bool last = i == chain->objects.size(); + if (last || i_obj.type == CSGTerm::TYPE_UNION) { if (j+1 != i) { OpenCSG::render(primitives); glDepthFunc(GL_EQUAL); } if (shaderinfo) glUseProgram(shaderinfo[0]); for (; j < i; j++) { - const Transform3d &m = chain->matrices[j]; - const Color4f &c = chain->colors[j]; + const CSGChainObject &j_obj = chain->objects[j]; + const Color4f &c = j_obj.color; glPushMatrix(); - glMultMatrixd(m.data()); - PolySet::csgmode_e csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; - if (highlight) { - setColor(COLORMODE_HIGHLIGHT, shaderinfo); - csgmode = PolySet::csgmode_e(csgmode + 20); - } - else if (background) { - setColor(COLORMODE_BACKGROUND, shaderinfo); + glMultMatrixd(j_obj.matrix.data()); + PolySet::csgmode_e csgmode = j_obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + ColorMode colormode = COLORMODE_NONE; + if (background) { + if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) { + colormode = COLORMODE_HIGHLIGHT; + } + else { + colormode = COLORMODE_BACKGROUND; + } csgmode = PolySet::csgmode_e(csgmode + 10); - } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) { - // User-defined color or alpha from source - setColor(c.data(), shaderinfo); - } else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) { - setColor(COLORMODE_CUTOUT, shaderinfo); + } else if (j_obj.type == CSGTerm::TYPE_DIFFERENCE) { + if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) { + colormode = COLORMODE_HIGHLIGHT; + csgmode = PolySet::csgmode_e(csgmode + 20); + } + else { + colormode = COLORMODE_CUTOUT; + } } else { - setColor(COLORMODE_MATERIAL, shaderinfo); + if (j_obj.flag & CSGTerm::FLAG_HIGHLIGHT) { + colormode = COLORMODE_HIGHLIGHT; + csgmode = PolySet::csgmode_e(csgmode + 20); + } + else { + colormode = COLORMODE_MATERIAL; + } } - chain->polysets[j]->render_surface(csgmode, m, shaderinfo); + + setColor(colormode, c.data(), shaderinfo); + + j_obj.polyset->render_surface(csgmode, j_obj.matrix, shaderinfo); glPopMatrix(); } if (shaderinfo) glUseProgram(0); @@ -118,11 +133,11 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo, if (last) break; - OpenCSGPrim *prim = new OpenCSGPrim(chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? - OpenCSG::Subtraction : OpenCSG::Intersection, chain->polysets[i]->convexity); - prim->ps = chain->polysets[i]; - prim->m = chain->matrices[i]; - prim->csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + OpenCSGPrim *prim = new OpenCSGPrim(i_obj.type == CSGTerm::TYPE_DIFFERENCE ? + OpenCSG::Subtraction : OpenCSG::Intersection, i_obj.polyset->convexity); + prim->ps = i_obj.polyset; + prim->m = i_obj.matrix; + prim->csgmode = i_obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; if (highlight) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 20); else if (background) prim->csgmode = PolySet::csgmode_e(prim->csgmode + 10); primitives.push_back(prim); diff --git a/src/PlatformUtils-mac.mm b/src/PlatformUtils-mac.mm new file mode 100644 index 0000000..1e2ba43 --- /dev/null +++ b/src/PlatformUtils-mac.mm @@ -0,0 +1,7 @@ +#include "PlatformUtils.h" +#import <Foundation/Foundation.h> + +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 new file mode 100644 index 0000000..d7b7b6d --- /dev/null +++ b/src/PlatformUtils-posix.cc @@ -0,0 +1,10 @@ +#include "PlatformUtils.h" +#include "boosty.h" + +std::string PlatformUtils::documentsPath() +{ + fs::path docpath(getenv("HOME")); + docpath = docpath / ".local" / "share"; + + return boosty::stringy(docpath); +} diff --git a/src/PlatformUtils-win.cc b/src/PlatformUtils-win.cc new file mode 100644 index 0000000..a58a346 --- /dev/null +++ b/src/PlatformUtils-win.cc @@ -0,0 +1,70 @@ +#include "PlatformUtils.h" +#include "printutils.h" +#include <windows.h> +#ifndef _WIN32_IE +#define _WIN32_IE 0x0501 // SHGFP_TYPE_CURRENT +#endif +#include <shlobj.h> + +// convert from windows api w_char strings (usually utf16) to utf8 std::string +std::string winapi_wstr_to_utf8( std::wstring wstr ) +{ + UINT CodePage = CP_UTF8; + DWORD dwFlags = 0; + LPCWSTR lpWideCharStr = &wstr[0]; + int cchWideChar = (int)wstr.size(); + LPSTR lpMultiByteStr = NULL; + int cbMultiByte = 0; + LPCSTR lpDefaultChar = NULL; + LPBOOL lpUsedDefaultChar = NULL; + + int numbytes = WideCharToMultiByte( CodePage, dwFlags, lpWideCharStr, + cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar ); + + //PRINTB("utf16 to utf8 conversion: numbytes %i",numbytes); + + std::string utf8_str(numbytes,0); + lpMultiByteStr = &utf8_str[0]; + cbMultiByte = numbytes; + + int result = WideCharToMultiByte( CodePage, dwFlags, lpWideCharStr, + cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar ); + + if (result != numbytes) { + PRINT("ERROR: error converting w_char str to utf8 string"); + PRINTB("ERROR: error code %i",GetLastError()); + } + + return utf8_str; +} + + +// retrieve the path to 'My Documents' for the current user under windows +// In XP this is 'c:\documents and settings\username\my documents' +// In Vista, 7, 8+ this is 'c:\users\username\documents' +// This code may have problems with unusual dir types in Vista because +// Mingw does not provide access to the updated SHGetKnownFolderPath +std::string PlatformUtils::documentsPath() +{ + std::string retval; + std::wstring path(MAX_PATH,0); + + HWND hwndOwner = 0; + int nFolder = CSIDL_PERSONAL; + HANDLE hToken = NULL; + DWORD dwFlags = SHGFP_TYPE_CURRENT; + LPTSTR pszPath = &path[0]; + + int result = SHGetFolderPathW( hwndOwner, nFolder, hToken, dwFlags, pszPath ); + + if (result == S_OK) { + path = std::wstring( path.c_str() ); // stip extra NULLs + //std::wcerr << "wchar path:" << "\n"; + retval = winapi_wstr_to_utf8( path ); + //PRINTB("Path found: %s",retval); + } else { + PRINT("ERROR: Could not find My Documents location"); + retval = ""; + } + return retval; +} diff --git a/src/PlatformUtils.cc b/src/PlatformUtils.cc new file mode 100644 index 0000000..5dd007d --- /dev/null +++ b/src/PlatformUtils.cc @@ -0,0 +1,40 @@ +#include "PlatformUtils.h" +#include "boosty.h" + +bool PlatformUtils::createLibraryPath() +{ + std::string path = PlatformUtils::libraryPath(); + bool OK = false; + try { + if (!fs::exists(fs::path(path))) { + //PRINTB("Creating library folder %s", path ); + OK = fs::create_directories( path ); + } + if (!OK) { + PRINTB("ERROR: Cannot create %s", path ); + } + } catch (const fs::filesystem_error& ex) { + PRINTB("ERROR: %s",ex.what()); + } + return OK; +} + +std::string PlatformUtils::libraryPath() +{ + fs::path path; + try { + std::string pathstr = PlatformUtils::documentsPath(); + if (pathstr=="") return ""; + path = boosty::canonical(fs::path( pathstr )); + //PRINTB("path size %i",boosty::stringy(path).size()); + //PRINTB("lib path found: [%s]", path ); + if (path.empty()) return ""; + path /= "OpenSCAD"; + path /= "libraries"; + //PRINTB("Appended path %s", path ); + //PRINTB("Exists: %i", fs::exists(path) ); + } catch (const fs::filesystem_error& ex) { + PRINTB("ERROR: %s",ex.what()); + } + return boosty::stringy( path ); +} diff --git a/src/PlatformUtils.h b/src/PlatformUtils.h new file mode 100644 index 0000000..089b3ca --- /dev/null +++ b/src/PlatformUtils.h @@ -0,0 +1,14 @@ +#ifndef PLATFORMUTILS_H_ +#define PLATFORMUTILS_H_ + +#include <string> + +namespace PlatformUtils { + + std::string documentsPath(); + std::string libraryPath(); + bool createLibraryPath(); + +} + +#endif diff --git a/src/PolySetCGALEvaluator.cc b/src/PolySetCGALEvaluator.cc index f0c274f..bc9206f 100644 --- a/src/PolySetCGALEvaluator.cc +++ b/src/PolySetCGALEvaluator.cc @@ -324,6 +324,7 @@ PolySet *PolySetCGALEvaluator::extrudeDxfData(const LinearExtrudeNode &node, Dxf { PolySet *ps = new PolySet(); ps->convexity = node.convexity; + if (node.height <= 0) return ps; double h1, h2; diff --git a/src/ProgressWidget.cc b/src/ProgressWidget.cc index ce66405..7aa6a4a 100644 --- a/src/ProgressWidget.cc +++ b/src/ProgressWidget.cc @@ -5,7 +5,7 @@ ProgressWidget::ProgressWidget(QWidget *parent) :QWidget(parent) { setupUi(this); - setRange(0, 100); + setRange(0, 1000); setValue(0); this->wascanceled = false; this->starttime.start(); diff --git a/src/ProgressWidget.ui b/src/ProgressWidget.ui index 895586c..24aefdb 100644 --- a/src/ProgressWidget.ui +++ b/src/ProgressWidget.ui @@ -22,6 +22,9 @@ </property> <item> <widget class="QProgressBar" name="progressBar"> + <property name="maximum"> + <number>1000</number> + </property> <property name="value"> <number>24</number> </property> diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc index 146d2e1..6be30dc 100644 --- a/src/ThrownTogetherRenderer.cc +++ b/src/ThrownTogetherRenderer.cc @@ -31,6 +31,7 @@ #include "system-gl.h" #include <boost/unordered_map.hpp> +#include <boost/foreach.hpp> ThrownTogetherRenderer::ThrownTogetherRenderer(CSGChain *root_chain, CSGChain *highlights_chain, @@ -52,9 +53,9 @@ void ThrownTogetherRenderer::draw(bool /*showfaces*/, bool showedges) const glDisable(GL_CULL_FACE); } if (this->background_chain) - renderCSGChain(this->background_chain, false, true, showedges, false); + renderCSGChain(this->background_chain, false, true, showedges, false); if (this->highlights_chain) - renderCSGChain(this->highlights_chain, true, false, showedges, false); + renderCSGChain(this->highlights_chain, true, false, showedges, false); } void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, @@ -62,58 +63,63 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, bool fberror) const { glDepthFunc(GL_LEQUAL); - boost::unordered_map<std::pair<PolySet*,Transform3d*>,int> polySetVisitMark; - for (size_t i = 0; i < chain->polysets.size(); i++) { - if (polySetVisitMark[std::make_pair(chain->polysets[i].get(), &chain->matrices[i])]++ > 0) + boost::unordered_map<std::pair<PolySet*,const Transform3d*>,int> polySetVisitMark; + BOOST_FOREACH(const CSGChainObject &obj, chain->objects) { + if (polySetVisitMark[std::make_pair(obj.polyset.get(), &obj.matrix)]++ > 0) continue; - const Transform3d &m = chain->matrices[i]; - const Color4f &c = chain->colors[i]; + const Transform3d &m = obj.matrix; + const Color4f &c = obj.color; glPushMatrix(); glMultMatrixd(m.data()); - PolySet::csgmode_e csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + PolySet::csgmode_e csgmode = obj.type == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL; + ColorMode colormode = COLORMODE_NONE; + ColorMode edge_colormode = COLORMODE_NONE; + if (highlight) { csgmode = PolySet::csgmode_e(csgmode + 20); - setColor(COLORMODE_HIGHLIGHT); - chain->polysets[i]->render_surface(csgmode, m); - if (showedges) { - setColor(COLORMODE_HIGHLIGHT_EDGES); - chain->polysets[i]->render_edges(csgmode); - } + colormode = COLORMODE_HIGHLIGHT; + edge_colormode = COLORMODE_HIGHLIGHT_EDGES; } else if (background) { - csgmode = PolySet::csgmode_e(csgmode + 10); - setColor(COLORMODE_BACKGROUND); - chain->polysets[i]->render_surface(csgmode, m); - if (showedges) { - setColor(COLORMODE_BACKGROUND_EDGES); - chain->polysets[i]->render_edges(csgmode); + if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) { + colormode = COLORMODE_HIGHLIGHT; + } + else { + colormode = COLORMODE_BACKGROUND; } + csgmode = PolySet::csgmode_e(csgmode + 10); + edge_colormode = COLORMODE_BACKGROUND_EDGES; } else if (fberror) { if (highlight) csgmode = PolySet::csgmode_e(csgmode + 20); else if (background) csgmode = PolySet::csgmode_e(csgmode + 10); else csgmode = PolySet::csgmode_e(csgmode); - chain->polysets[i]->render_surface(csgmode, m); - } else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) { - setColor(c.data()); - chain->polysets[i]->render_surface(csgmode, m); - if (showedges) { - glColor4f((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); - chain->polysets[i]->render_edges(csgmode); + } else if (obj.type == CSGTerm::TYPE_DIFFERENCE) { + if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) { + colormode = COLORMODE_HIGHLIGHT; + csgmode = PolySet::csgmode_e(csgmode + 20); } - } else if (chain->types[i] == CSGTerm::TYPE_DIFFERENCE) { - setColor(COLORMODE_CUTOUT); - chain->polysets[i]->render_surface(csgmode, m); - if (showedges) { - setColor(COLORMODE_CUTOUT_EDGES); - chain->polysets[i]->render_edges(csgmode); + else { + colormode = COLORMODE_CUTOUT; } + edge_colormode = COLORMODE_CUTOUT_EDGES; } else { - setColor(COLORMODE_MATERIAL); - chain->polysets[i]->render_surface(csgmode, m); - if (showedges) { - setColor(COLORMODE_MATERIAL_EDGES); - chain->polysets[i]->render_edges(csgmode); + if (obj.flag & CSGTerm::FLAG_HIGHLIGHT) { + colormode = COLORMODE_HIGHLIGHT; + csgmode = PolySet::csgmode_e(csgmode + 20); + } + else { + colormode = COLORMODE_MATERIAL; } + edge_colormode = COLORMODE_MATERIAL_EDGES; } + + setColor(colormode, c.data()); + obj.polyset->render_surface(csgmode, m); + if (showedges) { + // FIXME? glColor4f((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); + setColor(edge_colormode); + obj.polyset->render_edges(csgmode); + } + glPopMatrix(); } } diff --git a/src/cgaladv.cc b/src/cgaladv.cc index 70590f7..ee3d657 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -142,6 +142,7 @@ std::string CgaladvNode::name() const default: assert(false); } + return "internal_error"; } std::string CgaladvNode::toString() const diff --git a/src/cgalworker.cc b/src/cgalworker.cc index 96fead9..f011262 100644 --- a/src/cgalworker.cc +++ b/src/cgalworker.cc @@ -9,6 +9,7 @@ CGALWorker::CGALWorker() { this->thread = new QThread(); + if (this->thread->stackSize() < 1024*1024) this->thread->setStackSize(1024*1024); connect(this->thread, SIGNAL(started()), this, SLOT(work())); moveToThread(this->thread); } diff --git a/src/control.cc b/src/control.cc index 7786e36..c5ad09b 100644 --- a/src/control.cc +++ b/src/control.cc @@ -114,7 +114,7 @@ AbstractNode *ControlModule::instantiate(const Context *ctx, const ModuleInstant // assert(filectx->evalctx); if (filectx->evalctx) { - if (n < filectx->evalctx->numChildren()) { + if (n < (int)filectx->evalctx->numChildren()) { node = filectx->evalctx->getChild(n)->evaluate(filectx->evalctx); } else { diff --git a/src/csgops.cc b/src/csgops.cc index 92b97e7..8ac1d4f 100644 --- a/src/csgops.cc +++ b/src/csgops.cc @@ -69,6 +69,7 @@ std::string CsgNode::name() const default: assert(false); } + return "internal_error"; } void register_builtin_csgops() diff --git a/src/csgterm.cc b/src/csgterm.cc index aed97b2..a0379e7 100644 --- a/src/csgterm.cc +++ b/src/csgterm.cc @@ -28,6 +28,7 @@ #include "polyset.h" #include "linalg.h" #include <sstream> +#include <boost/foreach.hpp> /*! \class CSGTerm @@ -103,19 +104,19 @@ shared_ptr<CSGTerm> CSGTerm::createCSGTerm(type_e type, CSGTerm *left, CSGTerm * } CSGTerm::CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const Color4f &color, const std::string &label) - : type(TYPE_PRIMITIVE), polyset(polyset), label(label), m(matrix), color(color) + : type(TYPE_PRIMITIVE), polyset(polyset), label(label), flag(CSGTerm::FLAG_NONE), m(matrix), color(color) { initBoundingBox(); } CSGTerm::CSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right) - : type(type), left(left), right(right), m(Transform3d::Identity()) + : type(type), left(left), right(right), flag(CSGTerm::FLAG_NONE), m(Transform3d::Identity()) { initBoundingBox(); } CSGTerm::CSGTerm(type_e type, CSGTerm *left, CSGTerm *right) - : type(type), left(left), right(right), m(Transform3d::Identity()) + : type(type), left(left), right(right), flag(CSGTerm::FLAG_NONE), m(Transform3d::Identity()) { initBoundingBox(); } @@ -181,26 +182,15 @@ std::string CSGTerm::dump() return dump.str(); } -CSGChain::CSGChain() -{ -} - -void CSGChain::add(const shared_ptr<PolySet> &polyset, const Transform3d &m, const Color4f &color, CSGTerm::type_e type, std::string label) -{ - polysets.push_back(polyset); - matrices.push_back(m); - colors.push_back(color); - types.push_back(type); - labels.push_back(label); -} - -void CSGChain::import(shared_ptr<CSGTerm> term, CSGTerm::type_e type) +void CSGChain::import(shared_ptr<CSGTerm> term, CSGTerm::type_e type, CSGTerm::Flag flag) { + CSGTerm::Flag newflag = (CSGTerm::Flag)(term->flag | flag); if (term->type == CSGTerm::TYPE_PRIMITIVE) { - add(term->polyset, term->m, term->color, type, term->label); + this->objects.push_back(CSGChainObject(term->polyset, term->m, term->color, type, term->label, newflag)); } else { - import(term->left, type); - import(term->right, term->type); + assert(term->left && term->right); + import(term->left, type, newflag); + import(term->right, term->type, newflag); } } @@ -208,21 +198,20 @@ std::string CSGChain::dump(bool full) { std::stringstream dump; - for (size_t i = 0; i < types.size(); i++) - { - if (types[i] == CSGTerm::TYPE_UNION) { - if (i != 0) dump << "\n"; + BOOST_FOREACH(const CSGChainObject &obj, this->objects) { + if (obj.type == CSGTerm::TYPE_UNION) { + if (&obj != &this->objects.front()) dump << "\n"; dump << "+"; } - else if (types[i] == CSGTerm::TYPE_DIFFERENCE) + else if (obj.type == CSGTerm::TYPE_DIFFERENCE) dump << " -"; - else if (types[i] == CSGTerm::TYPE_INTERSECTION) + else if (obj.type == CSGTerm::TYPE_INTERSECTION) dump << " *"; - dump << labels[i]; + dump << obj.label; if (full) { - dump << " polyset: \n" << polysets[i]->dump() << "\n"; - dump << " matrix: \n" << matrices[i].matrix() << "\n"; - dump << " color: \n" << colors[i] << "\n"; + dump << " polyset: \n" << obj.polyset->dump() << "\n"; + dump << " matrix: \n" << obj.matrix.matrix() << "\n"; + dump << " color: \n" << obj.color << "\n"; } } dump << "\n"; @@ -232,11 +221,11 @@ std::string CSGChain::dump(bool full) BoundingBox CSGChain::getBoundingBox() const { BoundingBox bbox; - for (size_t i=0;i<polysets.size();i++) { - if (types[i] != CSGTerm::TYPE_DIFFERENCE) { - BoundingBox psbox = polysets[i]->getBoundingBox(); + BOOST_FOREACH(const CSGChainObject &obj, this->objects) { + if (obj.type != CSGTerm::TYPE_DIFFERENCE) { + BoundingBox psbox = obj.polyset->getBoundingBox(); if (!psbox.isNull()) { - bbox.extend(matrices[i] * psbox); + bbox.extend(obj.matrix * psbox); } } } diff --git a/src/csgterm.h b/src/csgterm.h index 566ebc3..94878e5 100644 --- a/src/csgterm.h +++ b/src/csgterm.h @@ -18,6 +18,12 @@ public: TYPE_DIFFERENCE }; + enum Flag { + FLAG_NONE = 0x00, + FLAG_BACKGROUND = 0x01, + FLAG_HIGHLIGHT = 0x03 + }; + static shared_ptr<CSGTerm> createCSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right); static shared_ptr<CSGTerm> createCSGTerm(type_e type, CSGTerm *left, CSGTerm *right); @@ -27,6 +33,7 @@ public: shared_ptr<CSGTerm> left; shared_ptr<CSGTerm> right; BoundingBox bbox; + Flag flag; CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const Color4f &color, const std::string &label); ~CSGTerm(); @@ -46,19 +53,34 @@ private: friend class CSGChain; }; +class CSGChainObject +{ +public: + CSGChainObject(shared_ptr<PolySet> polyset, + const Transform3d &matrix, + const Color4f &color, + CSGTerm::type_e type, + const std::string &label, + CSGTerm::Flag flag = CSGTerm::FLAG_NONE) + : polyset(polyset), matrix(matrix), color(color), type(type), label(label), flag(flag) {} + + shared_ptr<PolySet> polyset; + Transform3d matrix; + Color4f color; + CSGTerm::type_e type; + std::string label; + CSGTerm::Flag flag; +}; + class CSGChain { public: - std::vector<shared_ptr<PolySet> > polysets; - std::vector<Transform3d> matrices; - std::vector<Color4f> colors; - std::vector<CSGTerm::type_e> types; - std::vector<std::string> labels; + std::vector<CSGChainObject> objects; - CSGChain(); + CSGChain() {}; - void add(const shared_ptr<PolySet> &polyset, const Transform3d &m, const Color4f &color, CSGTerm::type_e type, std::string label); - void import(shared_ptr<CSGTerm> term, CSGTerm::type_e type = CSGTerm::TYPE_UNION); + void import(shared_ptr<CSGTerm> term, CSGTerm::type_e type = CSGTerm::TYPE_UNION, + CSGTerm::Flag flag = CSGTerm::FLAG_NONE); std::string dump(bool full = false); BoundingBox getBoundingBox() const; diff --git a/src/dxfdim.cc b/src/dxfdim.cc index 66842d2..a241b87 100644 --- a/src/dxfdim.cc +++ b/src/dxfdim.cc @@ -160,9 +160,16 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) } std::stringstream keystream; + fs::path filepath(filename); + uintmax_t filesize = -1; + time_t lastwritetime = -1; + if (fs::exists(filepath) && fs::is_regular_file(filepath)) { + filesize = fs::file_size(filepath); + lastwritetime = fs::last_write_time(filepath); + } keystream << filename << "|" << layername << "|" << xorigin << "|" << yorigin - << "|" << scale << "|" << fs::last_write_time(filename) - << "|" << fs::file_size(filename); + << "|" << scale << "|" << lastwritetime + << "|" << filesize; std::string key = keystream.str(); if (dxf_cross_cache.find(key) != dxf_cross_cache.end()) diff --git a/src/expr.cc b/src/expr.cc index 4355400..2e069f0 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -69,7 +69,7 @@ public: expr.recursioncount++; } ~FuncRecursionGuard() { expr.recursioncount--; } - bool recursion_detected() const { return (expr.recursioncount > 100); } + bool recursion_detected() const { return (expr.recursioncount > 1000); } private: const Expression &expr; }; @@ -172,23 +172,27 @@ std::string Expression::toString() const if (this->type == "*" || this->type == "/" || this->type == "%" || this->type == "+" || this->type == "-" || this->type == "<" || this->type == "<=" || this->type == "==" || - this->type == "!=" || this->type == ">=" || this->type == ">") { + this->type == "!=" || this->type == ">=" || this->type == ">" || + this->type == "&&" || this->type == "||") { stream << "(" << *this->children[0] << " " << this->type << " " << *this->children[1] << ")"; } else if (this->type == "?:") { - stream << "(" << *this->children[0] << " ? " << this->type << " : " << *this->children[1] << ")"; + stream << "(" << *this->children[0] << " ? " << *this->children[1] << " : " << *this->children[2] << ")"; } else if (this->type == "[]") { - stream << "(" << *this->children[0] << "[" << *this->children[1] << "])"; + stream << *this->children[0] << "[" << *this->children[1] << "]"; } else if (this->type == "I") { - stream << "(-" << *this->children[0] << ")"; + stream << "-" << *this->children[0]; + } + else if (this->type == "!") { + stream << "!" << *this->children[0]; } else if (this->type == "C") { stream << this->const_value; } else if (this->type == "R") { - stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << this->children[2] << "]"; + stream << "[" << *this->children[0] << " : " << *this->children[1] << " : " << *this->children[2] << "]"; } else if (this->type == "V") { stream << "["; @@ -202,7 +206,7 @@ std::string Expression::toString() const stream << this->var_name; } else if (this->type == "N") { - stream << "(" << *this->children[0] << "." << this->var_name << ")"; + stream << *this->children[0] << "." << this->var_name; } else if (this->type == "F") { stream << this->call_funcname << "("; diff --git a/src/func.cc b/src/func.cc index 18884b8..eaaae74 100644 --- a/src/func.cc +++ b/src/func.cc @@ -64,6 +64,7 @@ AbstractFunction::~AbstractFunction() Value AbstractFunction::evaluate(const Context*, const EvalContext *evalctx) const { + (void)evalctx; // unusued parameter return Value(); } @@ -500,6 +501,7 @@ Value builtin_search(const Context *, const EvalContext *evalctx) Value builtin_version(const Context *, const EvalContext *evalctx) { + (void)evalctx; // unusued parameter Value::VectorType val; val.push_back(Value(double(OPENSCAD_YEAR))); val.push_back(Value(double(OPENSCAD_MONTH))); diff --git a/src/highlighter.cc b/src/highlighter.cc index bf80bb4..4b4aa30 100644 --- a/src/highlighter.cc +++ b/src/highlighter.cc @@ -132,11 +132,14 @@ Highlighter::Highlighter(QTextDocument *parent) tokentypes["operator"] << "=" << "!" << "&&" << "||" << "+" << "-" << "*" << "/" << "%" << "!" << "#" << ";"; typeformats["operator"].setForeground(Qt::blue); - tokentypes["keyword"] << "module" << "function" << "for" << "intersection_for" << "if" << "assign"; + tokentypes["math"] << "abs" << "sign" << "acos" << "asin" << "atan" << "atan2" << "sin" << "cos" << "floor" << "round" << "ceil" << "ln" << "log" << "lookup" << "min" << "max" << "pow" << "sqrt" << "exp" << "rands"; + typeformats["math"].setForeground(Qt::green); + + tokentypes["keyword"] << "module" << "function" << "for" << "intersection_for" << "if" << "assign" << "echo"<< "search" << "str"; typeformats["keyword"].setForeground(QColor("Green")); typeformats["keyword"].setToolTip("Keyword"); - tokentypes["transform"] << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection" << "hull" << "resize"; + tokentypes["transform"] << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection" << "hull" << "resize" << "mirror" << "minkowski"; typeformats["transform"].setForeground(QColor("Indigo")); tokentypes["csgop"] << "union" << "intersection" << "difference" << "render"; @@ -148,7 +151,7 @@ Highlighter::Highlighter(QTextDocument *parent) tokentypes["prim2d"] << "square" << "polygon" << "circle"; typeformats["prim2d"].setForeground(QColor("MidnightBlue")); - tokentypes["import"] << "include" << "use" << "import_stl" << "import" << "import_dxf" << "dxf_dim" << "dxf_cross"; + tokentypes["import"] << "include" << "use" << "import_stl" << "import" << "import_dxf" << "dxf_dim" << "dxf_cross" << "surface"; typeformats["import"].setForeground(Qt::darkYellow); tokentypes["special"] << "$children" << "child" << "$fn" << "$fa" << "$fs" << "$t" << "$vpt" << "$vpr"; @@ -285,6 +288,7 @@ void Highlighter::highlightBlock(const QString &text) // Quoting and Comments. state_e state = (state_e) previousBlockState(); + int quote_esc_state = 0; for (int n = 0; n < text.size(); ++n){ if (state == NORMAL){ if (text[n] == '"'){ @@ -301,7 +305,11 @@ void Highlighter::highlightBlock(const QString &text) } } else if (state == QUOTE){ setFormat(n,1,quoteFormat); - if (text[n] == '"' && n-1 >=0 && text[n-1] != '\\') + if (quote_esc_state > 0) + quote_esc_state = 0; + else if (text[n] == '\\') + quote_esc_state = 1; + else if (text[n] == '"') state = NORMAL; } else if (state == COMMENT){ setFormat(n,1,commentFormat); diff --git a/src/import.cc b/src/import.cc index 2180684..b5d67d2 100644 --- a/src/import.cc +++ b/src/import.cc @@ -204,7 +204,7 @@ PolySet *ImportNode::evaluate_polyset(class PolySetEvaluator *) const boost::regex ex_vertices("\\s*vertex\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)"); bool binary = false; - int file_size = f.tellg(); + std::streampos file_size = f.tellg(); f.seekg(80); if (!f.eof()) { uint32_t facenum = 0; diff --git a/src/lexer.l b/src/lexer.l index 6dfe9bc..0b8048f 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -100,7 +100,7 @@ E [Ee][+-]?{D}+ %% -include[ \t\r\n>]*"<" { BEGIN(cond_include); } +include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = "";} <cond_include>{ [^\t\r\n>]*"/" { filepath = yytext; } [^\t\r\n>/]+ { filename = yytext; } @@ -113,29 +113,16 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); } [^\t\r\n>]+ { filename = yytext; } ">" { BEGIN(INITIAL); - fs::path usepath; - if (boosty::is_absolute(fs::path(filename))) { - usepath = filename; - } - else { - usepath = sourcepath() / filename; - if (!fs::exists(usepath)) { - usepath = locate_file(filename); - } - } - /* Only accept regular files which exists */ - if (usepath.has_parent_path() && fs::exists(usepath) && !fs::is_directory(usepath)) { - handle_dep(usepath.string()); - parserlval.text = strdup(usepath.string().c_str()); - return TOK_USE; - } else { - PRINTB("WARNING: Can't open 'use' file '%s'.", filename); - if ( filename.size() == 0 ) - PRINT("WARNING: 'use' filename is blank"); - else if ( fs::is_directory( usepath ) ) - PRINTB("WARNING: 'use' file points to a directory: %s",filename); - } - } + fs::path fullpath = find_valid_path(sourcepath(), fs::path(filename), &openfilenames); + if (fullpath.empty()) { + PRINTB("WARNING: Can't open library '%s'.", filename); + parserlval.text = strdup(filename.c_str()); + } else { + handle_dep(fullpath.string()); + parserlval.text = strdup(fullpath.string().c_str()); + } + return TOK_USE; + } } <<EOF>> { @@ -206,55 +193,37 @@ fs::path sourcepath() Rules for include <path/file> 1) include <sourcepath/path/file> 2) include <librarydir/path/file> + + Globals used: filepath, sourcepath, filename */ void includefile() { - if (filename.empty()) return; - - fs::path dirinfo = sourcepath(); - if (!filepath.empty()) { - if (boosty::is_absolute(fs::path(filepath))) { - dirinfo = filepath; - } - else { - dirinfo /= filepath; - } - } - - fs::path finfo = dirinfo / filename; - if (!exists(finfo)) { - finfo = locate_file((fs::path(filepath) / filename).string()); - } - - if (!exists(finfo) || finfo.empty()) { - // deal with some unusual situations with is_absolute() and Wine - fs::path fnp( fs::path(filepath) / filename ); - if (fs::exists( fnp ) && !fs::is_directory( fnp )) { - finfo = fnp; - } - } - - if (finfo.empty()) { - PRINTB("WARNING: Can't find 'include' file '%s'.", filename); + fs::path localpath = fs::path(filepath) / filename; + fs::path fullpath = find_valid_path(sourcepath(), localpath, &openfilenames); + if (!fullpath.empty()) { + rootmodule->registerInclude(boosty::stringy(localpath), boosty::stringy(fullpath)); } + else { + rootmodule->registerInclude(boosty::stringy(localpath), boosty::stringy(localpath)); + PRINTB("WARNING: Can't open include file '%s'.", boosty::stringy(localpath)); + if (path_stack.size() > 0) path_stack.pop_back(); + return; + }; - std::string fullname = boosty::absolute(finfo).string(); - // Detect circular includes - BOOST_FOREACH(std::string &s, openfilenames) { - if (s == fullname) return; - } + std::string fullname = boosty::stringy(fullpath); filepath.clear(); - path_stack.push_back(finfo.parent_path()); + path_stack.push_back(fullpath.parent_path()); handle_dep(fullname); - rootmodule->registerInclude(fullname); + yyin = fopen(fullname.c_str(), "r"); if (!yyin) { - PRINTB("WARNING: Can't open 'include' file '%s'.", filename); + PRINTB("WARNING: Can't open include file '%s'.", boosty::stringy(localpath)); path_stack.pop_back(); return; } + openfiles.push_back(yyin); openfilenames.push_back(fullname); filename.clear(); diff --git a/src/linearextrude.cc b/src/linearextrude.cc index 9a7365b..c5d4529 100644 --- a/src/linearextrude.cc +++ b/src/linearextrude.cc @@ -88,7 +88,8 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI } node->layername = layer.isUndefined() ? "" : layer.toString(); - node->height = height.toDouble(); + node->height = 100; + height.getDouble(node->height); node->convexity = (int)convexity.toDouble(); origin.getVec2(node->origin_x, node->origin_y); node->scale_x = node->scale_y = 1; @@ -99,8 +100,7 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI if (center.type() == Value::BOOL) node->center = center.toBool(); - if (node->height <= 0) - node->height = 100; + if (node->height <= 0) node->height = 0; if (node->convexity <= 0) node->convexity = 1; diff --git a/src/localscope.cc b/src/localscope.cc index eecff91..dd40563 100644 --- a/src/localscope.cc +++ b/src/localscope.cc @@ -55,9 +55,11 @@ std::vector<AbstractNode*> LocalScope::instantiateChildren(const Context *evalct // c->functions_p = &this->functions; // c->modules_p = &this->modules; - BOOST_FOREACH (const Assignment &ass, this->assignments) { - c->set_variable(ass.first, ass.second->evaluate(c)); - } + // Uncommenting the following would allow assignments in local scopes, + // but would cause duplicate evaluation of module scopes + // BOOST_FOREACH (const Assignment &ass, this->assignments) { + // c->set_variable(ass.first, ass.second->evaluate(c)); + // } } std::vector<AbstractNode*> childnodes; diff --git a/src/mainwin.cc b/src/mainwin.cc index da3501d..95f7242 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -53,6 +53,7 @@ #ifdef Q_OS_MAC #include "CocoaUtils.h" #endif +#include "PlatformUtils.h" #include <QMenu> #include <QTime> @@ -104,6 +105,8 @@ #define OPENCSG_VERSION_STRING "unknown, <1.3.2" #endif +#include "boosty.h" + extern QString examplesdir; // Global application state @@ -119,7 +122,7 @@ static char helptitle[] = #endif "\nhttp://www.openscad.org\n\n"; static char copyrighttext[] = - "Copyright (C) 2009-2013 Marius Kintel <marius@kintel.net> and Clifford Wolf <clifford@clifford.at>\n" + "Copyright (C) 2009-2013 The OpenSCAD Developers\n" "\n" "This program is free software; you can redistribute it and/or modify " "it under the terms of the GNU General Public License as published by " @@ -227,6 +230,7 @@ MainWindow::MainWindow(const QString &filename) connect(this->fileActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs())); connect(this->fileActionReload, SIGNAL(triggered()), this, SLOT(actionReload())); connect(this->fileActionQuit, SIGNAL(triggered()), this, SLOT(quit())); + connect(this->fileShowLibraryFolder, SIGNAL(triggered()), this, SLOT(actionShowLibraryFolder())); #ifndef __APPLE__ QList<QKeySequence> shortcuts = this->fileActionSave->shortcuts(); shortcuts.push_back(QKeySequence(Qt::Key_F2)); @@ -458,12 +462,11 @@ void MainWindow::showProgress() void MainWindow::report_func(const class AbstractNode*, void *vp, int mark) { MainWindow *thisp = static_cast<MainWindow*>(vp); - int v = (int)((mark*100.0) / progress_report_count); - int percent = v < 100 ? v : 99; - - if (percent > thisp->progresswidget->value()) { + int v = (int)((mark*1000.0) / progress_report_count); + int permille = v < 1000 ? v : 999; + if (permille > thisp->progresswidget->value()) { QMetaObject::invokeMethod(thisp->progresswidget, "setValue", Qt::QueuedConnection, - Q_ARG(int, percent)); + Q_ARG(int, permille)); QApplication::processEvents(); } @@ -489,12 +492,12 @@ void MainWindow::openFile(const QString &new_filename) { QString actual_filename = new_filename; + QFileInfo fi(new_filename); + if (fi.suffix().toLower().contains(QRegExp("^(stl|off|dxf)$"))) { + actual_filename = QString(); + } #ifdef ENABLE_MDI if (!editor->toPlainText().isEmpty()) { - QFileInfo fi(new_filename); - if (fi.suffix().toLower().contains(QRegExp("^(stl|off|dxf)$"))) { - actual_filename = QString(); - } new MainWindow(actual_filename); clearCurrentOutput(); return; @@ -502,6 +505,7 @@ MainWindow::openFile(const QString &new_filename) #endif setFileName(actual_filename); + fileChangedOnDisk(); // force cached autoReloadId to update refreshDocument(); updateRecentFiles(); if (actual_filename.isEmpty()) { @@ -651,7 +655,11 @@ bool MainWindow::compile(bool reload, bool procevents) if (procevents) QApplication::processEvents(); AbstractNode::resetIndexCounter(); - this->root_inst = ModuleInstantiation("group"); + + // split these two lines - gcc 4.7 bug + ModuleInstantiation mi = ModuleInstantiation( "group" ); + this->root_inst = mi; + this->absolute_root_node = this->root_module->instantiate(&top_ctx, &this->root_inst, NULL); if (this->absolute_root_node) { @@ -687,8 +695,7 @@ void MainWindow::compileCSG(bool procevents) { assert(this->root_node); PRINT("Compiling design (CSG Products generation)..."); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); // Main CSG evaluation QTime t; @@ -706,16 +713,16 @@ void MainWindow::compileCSG(bool procevents) PolySetEvaluator psevaluator(this->tree); #endif CSGTermEvaluator csgrenderer(this->tree, &psevaluator); + if (procevents) QApplication::processEvents(); this->root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms); if (!root_raw_term) { PRINT("ERROR: CSG generation failed! (no top level object found)"); - if (procevents) - QApplication::processEvents(); } PolySetCache::instance()->print(); #ifdef ENABLE_CGAL CGALCache::instance()->print(); #endif + if (procevents) QApplication::processEvents(); } catch (const ProgressCancelException &e) { PRINT("CSG generation cancelled."); @@ -727,8 +734,7 @@ void MainWindow::compileCSG(bool procevents) if (root_raw_term) { PRINT("Compiling design (CSG Products normalization)..."); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); size_t normalizelimit = 2 * Preferences::inst()->getValue("advanced/openCSGLimit").toUInt(); CSGTermNormalizer normalizer(normalizelimit); @@ -740,15 +746,13 @@ void MainWindow::compileCSG(bool procevents) else { this->root_chain = NULL; PRINT("WARNING: CSG normalization resulted in an empty tree"); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); } if (highlight_terms.size() > 0) { PRINTB("Compiling highlights (%d CSG Trees)...", highlight_terms.size()); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); highlights_chain = new CSGChain(); for (unsigned int i = 0; i < highlight_terms.size(); i++) { @@ -760,8 +764,7 @@ void MainWindow::compileCSG(bool procevents) if (background_terms.size() > 0) { PRINTB("Compiling background (%d CSG Trees)...", background_terms.size()); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); background_chain = new CSGChain(); for (unsigned int i = 0; i < background_terms.size(); i++) { @@ -771,14 +774,14 @@ void MainWindow::compileCSG(bool procevents) } if (this->root_chain && - (this->root_chain->polysets.size() > + (this->root_chain->objects.size() > Preferences::inst()->getValue("advanced/openCSGLimit").toUInt())) { - PRINTB("WARNING: Normalized tree has %d elements!", this->root_chain->polysets.size()); + PRINTB("WARNING: Normalized tree has %d elements!", this->root_chain->objects.size()); PRINT("WARNING: OpenCSG rendering has been disabled."); } else { PRINTB("Normalized CSG tree has %d elements", - (this->root_chain ? this->root_chain->polysets.size() : 0)); + (this->root_chain ? this->root_chain->objects.size() : 0)); this->opencsgRenderer = new OpenCSGRenderer(this->root_chain, this->highlights_chain, this->background_chain, @@ -790,8 +793,7 @@ void MainWindow::compileCSG(bool procevents) PRINT("CSG generation finished."); int s = t.elapsed() / 1000; PRINTB("Total rendering time: %d hours, %d minutes, %d seconds", (s / (60*60)) % ((s / 60) % 60) % (s % 60)); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); } } @@ -913,6 +915,8 @@ void MainWindow::actionSave() QFile file(this->fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { PRINTB("Failed to open file for writing: %s (%s)", this->fileName.toLocal8Bit().constData() % file.errorString().toLocal8Bit().constData()); + QMessageBox::warning(this, windowTitle(), tr("Failed to open file for writing:\n %1 (%2)") + .arg(this->fileName).arg(file.errorString())); } else { QTextStream writer(&file); @@ -951,9 +955,26 @@ void MainWindow::actionSaveAs() } } +void MainWindow::actionShowLibraryFolder() +{ + std::string path = PlatformUtils::libraryPath(); + if (!fs::exists(path)) { + PRINTB("WARNING: Library path %s doesnt exist. Creating", path); + if (!PlatformUtils::createLibraryPath()) { + PRINTB("ERROR: Cannot create library path: %s",path); + } + } + QString url = QString::fromStdString( path ); + //PRINTB("Opening file browser for %s", url.toStdString() ); + QDesktopServices::openUrl(QUrl::fromLocalFile( url )); +} + void MainWindow::actionReload() { - if (checkEditorModified()) refreshDocument(); + if (checkEditorModified()) { + fileChangedOnDisk(); // force cached autoReloadId to update + refreshDocument(); + } } void MainWindow::hideEditor() @@ -1007,7 +1028,10 @@ bool MainWindow::fileChangedOnDisk() if (!this->fileName.isEmpty()) { struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(this->fileName.toLocal8Bit(), &st); + bool valid = (stat(this->fileName.toLocal8Bit(), &st) == 0); + // If file isn't there, just return and use current editor text + if (!valid) return false; + std::string newid = str(boost::format("%x.%x") % st.st_mtime % st.st_size); if (newid != this->autoReloadId) { @@ -1018,25 +1042,6 @@ bool MainWindow::fileChangedOnDisk() return false; } -// FIXME: The following two methods are duplicated in ModuleCache.cc - refactor -static bool is_modified(const std::string &filename, const time_t &mtime) -{ - struct stat st; - memset(&st, 0, sizeof(struct stat)); - stat(filename.c_str(), &st); - return (st.st_mtime > mtime); -} - -bool MainWindow::includesChanged() -{ - if (this->root_module) { - BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->root_module->includes) { - if (is_modified(item.first, item.second)) return true; - } - } - return false; -} - /*! If reload is true, does a timestamp check on the document and tries to reload it. Otherwise, just reparses the current document and any dependencies, updates the @@ -1048,11 +1053,22 @@ bool MainWindow::compileTopLevelDocument(bool reload) { bool shouldcompiletoplevel = !reload; - if (includesChanged()) shouldcompiletoplevel = true; - - if (reload && fileChangedOnDisk() && checkEditorModified()) { + if (this->root_module && this->root_module->includesChanged()) { shouldcompiletoplevel = true; - refreshDocument(); + } + + if (reload) { + // Refresh file if it has changed on disk + if (fileChangedOnDisk() && checkEditorModified()) { + shouldcompiletoplevel = true; + refreshDocument(); + } + // If the file hasn't changed, we might still need to compile it + // if we haven't yet compiled the current text. + else if (!editor->isContentModified()) { + QString current_doc = editor->toPlainText(); + if (current_doc != last_compiled_doc) shouldcompiletoplevel = true; + } } if (shouldcompiletoplevel) { @@ -1105,7 +1121,6 @@ void MainWindow::autoReloadSet(bool on) QSettings settings; settings.setValue("design/autoReload",designActionAutoReload->isChecked()); if (on) { - autoReloadId = ""; autoReloadTimer->start(200); } else { autoReloadTimer->stop(); @@ -1746,7 +1761,7 @@ MainWindow::helpHomepage() void MainWindow::helpManual() { - QDesktopServices::openUrl(QUrl("http://en.wikibooks.org/wiki/OpenSCAD_User_Manual")); + QDesktopServices::openUrl(QUrl("http://www.openscad.org/documentation.html")); } #define STRINGIFY(x) #x diff --git a/src/modcontext.cc b/src/modcontext.cc index 3879811..5b48009 100644 --- a/src/modcontext.cc +++ b/src/modcontext.cc @@ -4,6 +4,7 @@ #include "function.h" #include "printutils.h" #include "builtin.h" +#include "ModuleCache.h" #include <boost/foreach.hpp> @@ -16,6 +17,51 @@ ModuleContext::~ModuleContext() { } +// Experimental code. See issue #399 +#if 0 +void ModuleContext::evaluateAssignments(const AssignmentList &assignments) +{ + // First, assign all simple variables + std::list<std::string> undefined_vars; + BOOST_FOREACH(const Assignment &ass, assignments) { + Value tmpval = ass.second->evaluate(this); + if (tmpval.isUndefined()) undefined_vars.push_back(ass.first); + else this->set_variable(ass.first, tmpval); + } + + // Variables which couldn't be evaluated in the first pass is attempted again, + // to allow for initialization out of order + + boost::unordered_map<std::string, Expression *> tmpass; + BOOST_FOREACH (const Assignment &ass, assignments) { + tmpass[ass.first] = ass.second; + } + + bool changed = true; + while (changed) { + changed = false; + std::list<std::string>::iterator iter = undefined_vars.begin(); + while (iter != undefined_vars.end()) { + std::list<std::string>::iterator curr = iter++; + boost::unordered_map<std::string, Expression *>::iterator found = tmpass.find(*curr); + if (found != tmpass.end()) { + const Expression *expr = found->second; + Value tmpval = expr->evaluate(this); + // FIXME: it's not enough to check for undefined; + // we need to check for any undefined variable in the subexpression + // For now, ignore this and revisit the validity and order of variable + // assignments later + if (!tmpval.isUndefined()) { + changed = true; + this->set_variable(*curr, tmpval); + undefined_vars.erase(curr); + } + } + } + } +} +#endif + void ModuleContext::initializeModule(const class Module &module) { this->setVariables(module.definition_arguments, evalctx); @@ -25,6 +71,8 @@ void ModuleContext::initializeModule(const class Module &module) BOOST_FOREACH(const Assignment &ass, module.scope.assignments) { this->set_variable(ass.first, ass.second->evaluate(this)); } +// Experimental code. See issue #399 +// evaluateAssignments(module.scope.assignments); } /*! @@ -125,15 +173,18 @@ Value FileContext::evaluate_function(const std::string &name, const EvalContext if (foundf) return foundf->evaluate(this, evalctx); BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) { - if (m.second->scope.functions.find(name) != m.second->scope.functions.end()) { - FileContext ctx(*m.second, this->parent); - ctx.initializeModule(*m.second); + // usedmod is NULL if the library wasn't be compiled (error or file-not-found) + FileModule *usedmod = ModuleCache::instance()->lookup(m); + if (usedmod && + usedmod->scope.functions.find(name) != usedmod->scope.functions.end()) { + FileContext ctx(*usedmod, this->parent); + ctx.initializeModule(*usedmod); // FIXME: Set document path #if 0 && DEBUG PRINTB("New lib Context for %s func:", name); ctx.dump(NULL, NULL); #endif - return m.second->scope.functions[name]->evaluate(&ctx, evalctx); + return usedmod->scope.functions[name]->evaluate(&ctx, evalctx); } } @@ -146,16 +197,18 @@ AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, c if (foundm) return foundm->instantiate(this, &inst, evalctx); BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) { - assert(m.second); - if (m.second->scope.modules.find(inst.name()) != m.second->scope.modules.end()) { - FileContext ctx(*m.second, this->parent); - ctx.initializeModule(*m.second); + FileModule *usedmod = ModuleCache::instance()->lookup(m); + // usedmod is NULL if the library wasn't be compiled (error or file-not-found) + if (usedmod && + usedmod->scope.modules.find(inst.name()) != usedmod->scope.modules.end()) { + FileContext ctx(*usedmod, this->parent); + ctx.initializeModule(*usedmod); // FIXME: Set document path #if 0 && DEBUG PRINT("New file Context:"); ctx.dump(NULL, &inst); #endif - return m.second->scope.modules[inst.name()]->instantiate(&ctx, &inst, evalctx); + return usedmod->scope.modules[inst.name()]->instantiate(&ctx, &inst, evalctx); } } diff --git a/src/modcontext.h b/src/modcontext.h index 0b3e48c..3a05a0c 100644 --- a/src/modcontext.h +++ b/src/modcontext.h @@ -36,6 +36,9 @@ public: #ifdef DEBUG virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst); #endif +private: +// Experimental code. See issue #399 +// void evaluateAssignments(const AssignmentList &assignments); }; class FileContext : public ModuleContext diff --git a/src/module.cc b/src/module.cc index e853457..8fb8506 100644 --- a/src/module.cc +++ b/src/module.cc @@ -32,6 +32,7 @@ #include "expression.h" #include "function.h" #include "printutils.h" +#include "parsersettings.h" #include <boost/filesystem.hpp> namespace fs = boost::filesystem; @@ -46,6 +47,8 @@ AbstractModule::~AbstractModule() AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { + (void)ctx; // avoid unusued parameter warning + AbstractNode *node = new AbstractNode(inst); node->children = inst->instantiateChildren(evalctx); @@ -99,16 +102,35 @@ std::string ModuleInstantiation::dump(const std::string &indent) const if (scope.numElements() == 0) { dump << ");\n"; } else if (scope.numElements() == 1) { - dump << ")\n"; - dump << scope.dump(indent + "\t"); + dump << ") "; + dump << scope.dump(""); } else { dump << ") {\n"; - scope.dump(indent + "\t"); + dump << scope.dump(indent + "\t"); dump << indent << "}\n"; } return dump.str(); } +std::string IfElseModuleInstantiation::dump(const std::string &indent) const +{ + std::stringstream dump; + dump << ModuleInstantiation::dump(indent); + dump << indent; + if (else_scope.numElements() > 0) { + dump << indent << "else "; + if (else_scope.numElements() == 1) { + dump << else_scope.dump(""); + } + else { + dump << "{\n"; + dump << else_scope.dump(indent + "\t"); + dump << indent << "}\n"; + } + } + return dump.str(); +} + AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const { EvalContext c(ctx, this->arguments, &this->scope); @@ -144,7 +166,7 @@ public: ~ModRecursionGuard() { inst.recursioncount--; } - bool recursion_detected() const { return (inst.recursioncount > 100); } + bool recursion_detected() const { return (inst.recursioncount > 1000); } private: const ModuleInstantiation &inst; }; @@ -194,12 +216,36 @@ std::string Module::dump(const std::string &indent, const std::string &name) con return dump.str(); } -void FileModule::registerInclude(const std::string &filename) +void FileModule::registerInclude(const std::string &localpath, + const std::string &fullpath) { struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(filename.c_str(), &st); - this->includes[filename] = st.st_mtime; + bool valid = stat(fullpath.c_str(), &st) == 0; + IncludeFile inc = {fullpath, valid, st.st_mtime}; + this->includes[localpath] = inc; +} + +bool FileModule::includesChanged() const +{ + BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->includes) { + if (include_modified(item.second)) return true; + } + return false; +} + +bool FileModule::include_modified(const IncludeFile &inc) const +{ + struct stat st; + memset(&st, 0, sizeof(struct stat)); + + fs::path fullpath = find_valid_path(this->path, inc.filename); + bool valid = !fullpath.empty() ? (stat(boosty::stringy(fullpath).c_str(), &st) == 0) : false; + + if (valid && !inc.valid) return true; // Detect appearance of file but not removal + if (valid && st.st_mtime > inc.mtime) return true; + + return false; } /*! @@ -212,21 +258,39 @@ bool FileModule::handleDependencies() this->is_handling_dependencies = true; bool changed = false; + + // If a lib in usedlibs was previously missing, we need to relocate it + // by searching the applicable paths. We can identify a previously missing module + // as it will have a relative path. + // Iterating manually since we want to modify the container while iterating FileModule::ModuleContainer::iterator iter = this->usedlibs.begin(); while (iter != this->usedlibs.end()) { FileModule::ModuleContainer::iterator curr = iter++; - FileModule *oldmodule = curr->second; - curr->second = ModuleCache::instance()->evaluate(curr->first); - if (curr->second != oldmodule) { + + bool wasmissing = false; + // Get an absolute filename for the module + std::string filename = *curr; + if (!boosty::is_absolute(filename)) { + wasmissing = true; + fs::path fullpath = find_valid_path(this->path, filename); + if (!fullpath.empty()) filename = boosty::stringy(fullpath); + } + + FileModule *oldmodule = ModuleCache::instance()->lookup(filename); + FileModule *newmodule = ModuleCache::instance()->evaluate(filename); + // Detect appearance but not removal of files + if (newmodule && oldmodule != newmodule) { changed = true; #ifdef DEBUG - PRINTB_NOCACHE(" %s: %p", curr->first % curr->second); + PRINTB_NOCACHE(" %s: %p -> %p", filename % oldmodule % newmodule); #endif } - if (!curr->second) { - PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", curr->first); - this->usedlibs.erase(curr); + if (!newmodule) { + // Only print warning if we're not part of an automatic reload + if (!oldmodule && !wasmissing) { + PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename); + } } } diff --git a/src/module.h b/src/module.h index 9f46d37..6027fe6 100644 --- a/src/module.h +++ b/src/module.h @@ -5,6 +5,10 @@ #include <vector> #include <list> #include <boost/unordered_map.hpp> +#include <boost/unordered_set.hpp> +#include <time.h> +#include <sys/stat.h> + #include "value.h" #include "typedefs.h" #include "localscope.h" @@ -16,7 +20,7 @@ public: : tag_root(false), tag_highlight(false), tag_background(false), recursioncount(0), modname(name) { } virtual ~ModuleInstantiation(); - std::string dump(const std::string &indent) const; + virtual std::string dump(const std::string &indent) const; class AbstractNode *evaluate(const class Context *ctx) const; std::vector<AbstractNode*> instantiateChildren(const Context *evalctx) const; @@ -48,6 +52,7 @@ public: IfElseModuleInstantiation() : ModuleInstantiation("if") { } virtual ~IfElseModuleInstantiation(); std::vector<AbstractNode*> instantiateElseChildren(const Context *evalctx) const; + virtual std::string dump(const std::string &indent) const; LocalScope else_scope; }; @@ -84,16 +89,26 @@ public: void setModulePath(const std::string &path) { this->path = path; } const std::string &modulePath() const { return this->path; } - void registerInclude(const std::string &filename); + void registerInclude(const std::string &localpath, const std::string &fullpath); + bool includesChanged() const; bool handleDependencies(); virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; + bool hasIncludes() const { return !this->includes.empty(); } + bool usesLibraries() const { return !this->usedlibs.empty(); } + bool isHandlingDependencies() const { return this->is_handling_dependencies; } - typedef boost::unordered_map<std::string, class FileModule*> ModuleContainer; + typedef boost::unordered_set<std::string> ModuleContainer; ModuleContainer usedlibs; - typedef boost::unordered_map<std::string, time_t> IncludeContainer; - IncludeContainer includes; - private: + struct IncludeFile { + std::string filename; + bool valid; + time_t mtime; + }; + + bool include_modified(const IncludeFile &inc) const; + typedef boost::unordered_map<std::string, struct IncludeFile> IncludeContainer; + IncludeContainer includes; bool is_handling_dependencies; std::string path; }; diff --git a/src/openscad.cc b/src/openscad.cc index 7c54762..bcde5e2 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -330,7 +330,6 @@ int main(int argc, char **argv) // Top context - this context only holds builtins ModuleContext top_ctx; top_ctx.registerBuiltin(); - PRINT("Root Context:"); #if 0 && DEBUG top_ctx.dump(NULL, NULL); #endif diff --git a/src/parser.y b/src/parser.y index 2b07f14..5b3d019 100644 --- a/src/parser.y +++ b/src/parser.y @@ -127,7 +127,7 @@ input: /* empty */ | -TOK_USE { rootmodule->usedlibs[$1] = NULL; } input | +TOK_USE { rootmodule->usedlibs.insert($1); } input | statement input ; inner_input: @@ -136,15 +136,17 @@ statement inner_input ; assignment: TOK_ID '=' expr ';' { + bool found = false; for (AssignmentList::iterator iter = scope_stack.top()->assignments.begin(); iter != scope_stack.top()->assignments.end(); iter++) { if (iter->first == $1) { - scope_stack.top()->assignments.erase(iter); + iter->second = $3; + found = true; break; } } - scope_stack.top()->assignments.push_back(Assignment($1, $3)); + if (!found) scope_stack.top()->assignments.push_back(Assignment($1, $3)); } ; statement: diff --git a/src/parsersettings.cc b/src/parsersettings.cc index 8d82744..ab93b78 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -3,9 +3,7 @@ #include <boost/foreach.hpp> #include "boosty.h" #include <boost/algorithm/string.hpp> -#ifdef __APPLE__ -#include "CocoaUtils.h" -#endif +#include "PlatformUtils.h" namespace fs = boost::filesystem; @@ -20,13 +18,74 @@ void add_librarydir(const std::string &libdir) Searces for the given file in library paths and returns the full path if found. Returns an empty path if file cannot be found or filename is a directory. */ -std::string locate_file(const std::string &filename) +fs::path search_libs(const fs::path &localpath) { BOOST_FOREACH(const std::string &dir, librarypath) { - fs::path usepath = fs::path(dir) / filename; - if (fs::exists(usepath) && !fs::is_directory(usepath)) return usepath.string(); + fs::path usepath = fs::path(dir) / localpath; + if (fs::exists(usepath) && !fs::is_directory(usepath)) { + return usepath.string(); + } + } + return fs::path(); +} + +// files must be 'ordinary' - they must exist and be non-directories +// FIXME: We cannot print any output here since these function is called periodically +// from "Automatic reload and compile" +static bool check_valid(const fs::path &p, const std::vector<std::string> *openfilenames) +{ + if (p.empty()) { +// PRINTB("WARNING: File path is blank: %s",p); + return false; + } + if (!p.has_parent_path()) { +// PRINTB("WARNING: No parent path: %s",p); + return false; + } + if (!fs::exists(p)) { +// PRINTB("WARNING: File not found: %s",p); + return false; + } + if (fs::is_directory(p)) { +// PRINTB("WARNING: %s invalid - points to a directory",p); + return false; + } + std::string fullname = boosty::stringy(p); + // Detect circular includes + if (openfilenames) { + BOOST_FOREACH(const std::string &s, *openfilenames) { + if (s == fullname) { +// PRINTB("WARNING: circular include file %s", fullname); + return false; + } + } + } + return true; +} + +/*! + Check if the given filename is valid. + + If the given filename is absolute, do a simple check. + If not, search the applicable paths for a valid file. + + Returns the absolute path to a valid file, or an empty path if no + valid files could be found. +*/ +fs::path find_valid_path(const fs::path &sourcepath, + const fs::path &localpath, + const std::vector<std::string> *openfilenames) +{ + if (boosty::is_absolute(localpath)) { + if (check_valid(localpath, openfilenames)) return boosty::absolute(localpath); + } + else { + fs::path fpath = sourcepath / localpath; + if (check_valid(fpath, openfilenames)) return fpath; + fpath = search_libs(localpath); + if (!fpath.empty() && check_valid(fpath, openfilenames)) return fpath; } - return std::string(); + return fs::path(); } void parser_init(const std::string &applicationpath) @@ -44,12 +103,15 @@ void parser_init(const std::string &applicationpath) } } - // FIXME: Add ~/.openscad/libraries -#if defined(__APPLE__) && !defined(OPENSCAD_TESTING) - fs::path docdir(CocoaUtils::documentsPath()); + // This is the built-in user-writable library path +#ifndef OPENSCAD_TESTING + // This will resolve to ~/Documents on Mac, "My Documents" on Windows and + // ~/.local/share on Linux + fs::path docdir(PlatformUtils::documentsPath()); add_librarydir(boosty::stringy(docdir / "OpenSCAD" / "libraries")); #endif + // This is the built-in read-only library path std::string librarydir; fs::path libdir(applicationpath); fs::path tmpdir; @@ -58,15 +120,15 @@ void parser_init(const std::string &applicationpath) if (!is_directory(libdir / "libraries")) libdir /= "../../.."; #elif !defined(WIN32) if (is_directory(tmpdir = libdir / "../share/openscad/libraries")) { - librarydir = boosty::stringy( tmpdir ); + librarydir = boosty::stringy(tmpdir); } else if (is_directory(tmpdir = libdir / "../../share/openscad/libraries")) { - librarydir = boosty::stringy( tmpdir ); + librarydir = boosty::stringy(tmpdir); } else if (is_directory(tmpdir = libdir / "../../libraries")) { - librarydir = boosty::stringy( tmpdir ); + librarydir = boosty::stringy(tmpdir); } else #endif if (is_directory(tmpdir = libdir / "libraries")) { - librarydir = boosty::stringy( tmpdir ); + librarydir = boosty::stringy(tmpdir); } if (!librarydir.empty()) add_librarydir(librarydir); } diff --git a/src/parsersettings.h b/src/parsersettings.h index 007aa9c..52172b6 100644 --- a/src/parsersettings.h +++ b/src/parsersettings.h @@ -2,11 +2,15 @@ #define PARSERSETTINGS_H_ #include <string> +#include "boosty.h" extern int parser_error_pos; void parser_init(const std::string &applicationpath); void add_librarydir(const std::string &libdir); -std::string locate_file(const std::string &filename); +fs::path search_libs(const fs::path &localpath); +fs::path find_valid_path(const fs::path &sourcepath, + const fs::path &localpath, + const std::vector<std::string> *openfilenames = NULL); #endif diff --git a/src/primitives.cc b/src/primitives.cc index 9b755ef..89c1573 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -242,9 +242,8 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta */ int get_fragments_from_r(double r, double fn, double fs, double fa) { - if (r < GRID_FINE) return 0; - if (fn > 0.0) - return (int)fn; + if (r < GRID_FINE) return 3; + if (fn > 0.0) return (int)(fn >= 3 ? fn : 3); return (int)ceil(fmax(fmin(360.0 / fa, r*2*M_PI / fs), 5)); } diff --git a/src/renderer.cc b/src/renderer.cc index 985b460..7c4f8d7 100644 --- a/src/renderer.cc +++ b/src/renderer.cc @@ -1,28 +1,11 @@ #include "renderer.h" #include "rendersettings.h" -#include "linalg.h" -void Renderer::setColor(const float color[4], GLint *shaderinfo) const -{ - Color4f col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); - float c[4] = {color[0], color[1], color[2], color[3]}; - if (c[0] < 0) c[0] = col[0]; - if (c[1] < 0) c[1] = col[1]; - if (c[2] < 0) c[2] = col[2]; - if (c[3] < 0) c[3] = col[3]; - glColor4fv(c); - if (shaderinfo) { - glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]); - glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); - } -} - -void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const +bool Renderer::getColor(Renderer::ColorMode colormode, Color4f &col) const { - Color4f col; switch (colormode) { case COLORMODE_NONE: - return; + return false; break; case COLORMODE_MATERIAL: col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); @@ -31,7 +14,7 @@ void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_BACK_COLOR); break; case COLORMODE_HIGHLIGHT: - col.setRgb(255, 157, 81, 128); + col.setRgb(255, 81, 81, 128); break; case COLORMODE_BACKGROUND: col.setRgb(180, 180, 180, 128); @@ -49,19 +32,51 @@ void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const col.setRgb(150, 150, 150, 128); break; default: - return; + return false; break; } - float rgba[4]; - rgba[0] = col[0]; - rgba[1] = col[1]; - rgba[2] = col[2]; - rgba[3] = col[3]; - glColor4fv(rgba); + return true; +} + +void Renderer::setColor(const float color[4], GLint *shaderinfo) const +{ + Color4f col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); + float c[4] = {color[0], color[1], color[2], color[3]}; + if (c[0] < 0) c[0] = col[0]; + if (c[1] < 0) c[1] = col[1]; + if (c[2] < 0) c[2] = col[2]; + if (c[3] < 0) c[3] = col[3]; + glColor4fv(c); #ifdef ENABLE_OPENCSG if (shaderinfo) { - glUniform4f(shaderinfo[1], col[0], col[1], col[2], 1.0f); - glUniform4f(shaderinfo[2], (col[0]+1)/2, (col[1]+1)/2, (col[2]+1)/2, 1.0f); + glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]); + glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0); } #endif } + +void Renderer::setColor(ColorMode colormode, const float color[4], GLint *shaderinfo) const +{ + Color4f basecol; + if (getColor(colormode, basecol)) { + if (colormode == COLORMODE_BACKGROUND) { + basecol = Color4f(color[0] >= 0 ? color[0] : basecol[0], + color[1] >= 0 ? color[1] : basecol[1], + color[2] >= 0 ? color[2] : basecol[2], + color[3] >= 0 ? color[3] : basecol[3]); + } + else if (colormode != COLORMODE_HIGHLIGHT) { + basecol = Color4f(color[0] >= 0 ? color[0] : basecol[0], + color[1] >= 0 ? color[1] : basecol[1], + color[2] >= 0 ? color[2] : basecol[2], + color[3] >= 0 ? color[3] : basecol[3]); + } + setColor(basecol.data(), shaderinfo); + } +} + +void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const +{ + float c[4] = {-1,-1,-1,-1}; + setColor(colormode, c, shaderinfo); +} diff --git a/src/renderer.h b/src/renderer.h index 2bc482d..f70b4e1 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -2,6 +2,7 @@ #define RENDERER_H_ #include "system-gl.h" +#include "linalg.h" #ifdef _MSC_VER // NULL #include <cstdlib> @@ -25,8 +26,10 @@ public: COLORMODE_BACKGROUND_EDGES }; + virtual bool getColor(ColorMode colormode, Color4f &col) const; virtual void setColor(const float color[4], GLint *shaderinfo = NULL) const; virtual void setColor(ColorMode colormode, GLint *shaderinfo = NULL) const; + virtual void setColor(ColorMode colormode, const float color[4], GLint *shaderinfo = NULL) const; }; #endif // RENDERER_H diff --git a/src/rotateextrude.cc b/src/rotateextrude.cc index e073a69..a79b92e 100644 --- a/src/rotateextrude.cc +++ b/src/rotateextrude.cc @@ -33,7 +33,6 @@ #include "polyset.h" #include "visitor.h" #include "PolySetEvaluator.h" -#include "openscad.h" // get_fragments_from_r() #include <sstream> #include <boost/assign/std/vector.hpp> diff --git a/src/system-gl.cc b/src/system-gl.cc index 0c436e5..098dcb2 100644 --- a/src/system-gl.cc +++ b/src/system-gl.cc @@ -46,7 +46,8 @@ string glew_extensions_dump() sort( extensions.begin(), extensions.end() ); stringstream out; out << "GL Extensions:"; - for ( int i=0;i<extensions.size();i++ ) out << extensions[i] << "\n"; + for ( unsigned int i=0;i<extensions.size();i++ ) + out << extensions[i] << "\n"; return out.str(); } diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt index 277cff8..214acc5 100644 --- a/testdata/modulecache-tests/README.txt +++ b/testdata/modulecache-tests/README.txt @@ -26,13 +26,6 @@ o Open use-mcad.scad o Compile (F5) o Check that you get a rounded box -Test4: USE Non-existing file ------- - -o Open usenonexsistingfile.scad -o Compile (F5) -o Verify that you get: WARNING: Can't open 'use' file 'nofile.scad'. - Test5: Overload USEd module ------ @@ -72,10 +65,62 @@ o Compile (F5) - Verify that you get a circular disc o Edit radius.scad: Change RADIUS o Compile (F5) - Verify that the disc changed size -Test9: Circular include +Test10: Circular include ------ o Open circularincludemain.scad o Compile (F5) o Verify that OpenSCAD won't hang or crash +Test11: Missing include file appears +------ +o rm missing.scad +o Open includemissing.scad +o Compile (F5) +o Verify that you get: WARNING: Can't open include file 'missing.scad'. +o echo "module missing() { sphere(10); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere appeared +o rm missing.scad +o Reload and Compile (F4) - verify that the sphere is still there +o echo "module missing() { sphere(20); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere increased in size + +Test12: Missing include file in subpath appears +------ +o rm subdir/missingsub.scad +o Open includemissingsub.scad +o Compile (F5) +o Verify that you get: WARNING: Can't open include file 'subdir/missingsub.scad'. +o echo "module missingsub() { sphere(10); }" > subdir/missingsub.scad +o Reload and Compile (F4) - verify that the sphere appeared +o rm subdir/missingsub.scad +o Reload and Compile (F4) - verify that the sphere is still there +o echo "module missingsub() { sphere(20); }" > subdir/missingsub.scad +o Reload and Compile (F4) - verify that the sphere increased in size + +Test13: Missing library file appears +------- +o rm missing.scad +o Open usemissing.scad +o Compile (F5) +o Verify that you get: WARNING: Can't open library file 'missing.scad'. +o echo "module missing() { sphere(10); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere appeared +o rm missing.scad +o Reload and Compile (F4) - verify that the sphere is still there +o echo "module missing() { sphere(20); }" > missing.scad +o Reload and Compile (F4) - verify that the sphere increased in size + +Test14: Automatic reload of cascading changes +------- + +o ./cascade.sh +o Open cascadetest.scad +o Turn on Automatic Reload and Compile +o Verify that the 4 objects render correctly +o rm cascadetest.scad +o Verify that no rerendering was triggered (the 4 objects are still there) +o rm cascade*.scad +o Verify that no rerendering was triggered (the 4 objects are still there) +o ./cascade2.sh +o Verify that everything reloads at once without flickering diff --git a/testdata/modulecache-tests/cascade.sh b/testdata/modulecache-tests/cascade.sh new file mode 100755 index 0000000..3193d06 --- /dev/null +++ b/testdata/modulecache-tests/cascade.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +rm -f cascade*.scad +echo "include <cascade-A.scad> include <cascade-B.scad> use <cascade-C.scad> use <cascade-D.scad> A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D();" > cascadetest.scad +sleep 0.05 +echo "module A() { sphere(5); }" > cascade-A.scad +sleep 0.05 +echo "module B() { cube([8,8,8], center=true); }" > cascade-B.scad +sleep 0.05 +echo "module C() { cylinder(h=8, r=5, center=true); }" > cascade-C.scad +sleep 0.05 +echo "module D() { cylinder(h=10, r1=5, r2=0, center=true); }" > cascade-D.scad diff --git a/testdata/modulecache-tests/cascade2.sh b/testdata/modulecache-tests/cascade2.sh new file mode 100755 index 0000000..4969e6f --- /dev/null +++ b/testdata/modulecache-tests/cascade2.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +rm -f cascade-*.scad +echo "include <cascade-A.scad> include <cascade-B.scad> use <cascade-C.scad> use <cascade-D.scad> A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D();" > cascadetest.scad +sleep 0.1 +echo "module A() { sphere(6); }" > cascade-A.scad +sleep 0.1 +echo "module B() { cube([10,10,10], center=true); }" > cascade-B.scad +sleep 0.1 +echo "module C() { cylinder(h=10, r=6, center=true); }" > cascade-C.scad +sleep 0.1 +echo "module D() { cylinder(h=12, r1=6, r2=0, center=true); }" > cascade-D.scad diff --git a/testdata/modulecache-tests/includemissing.scad b/testdata/modulecache-tests/includemissing.scad new file mode 100644 index 0000000..d8a165f --- /dev/null +++ b/testdata/modulecache-tests/includemissing.scad @@ -0,0 +1,2 @@ +include <missing.scad> +missing(); diff --git a/testdata/modulecache-tests/includemissingsub.scad b/testdata/modulecache-tests/includemissingsub.scad new file mode 100644 index 0000000..50acffe --- /dev/null +++ b/testdata/modulecache-tests/includemissingsub.scad @@ -0,0 +1,2 @@ +include <subdir/missingsub.scad> +missingsub(); diff --git a/testdata/modulecache-tests/usemissing.scad b/testdata/modulecache-tests/usemissing.scad new file mode 100644 index 0000000..9f80b8b --- /dev/null +++ b/testdata/modulecache-tests/usemissing.scad @@ -0,0 +1,2 @@ +use <missing.scad> +missing(); diff --git a/testdata/scad/dxf/arc.scad b/testdata/scad/dxf/arc.scad index fff70a3..f53b7bd 100644 --- a/testdata/scad/dxf/arc.scad +++ b/testdata/scad/dxf/arc.scad @@ -1 +1,2 @@ import("../../dxf/arc.dxf"); +translate([110,0]) import("../../dxf/arc.dxf", $fn=0.1); diff --git a/testdata/scad/dxf/circle.scad b/testdata/scad/dxf/circle.scad index 8b5d132..edb9f77 100644 --- a/testdata/scad/dxf/circle.scad +++ b/testdata/scad/dxf/circle.scad @@ -1 +1,2 @@ import("../../dxf/circle.dxf"); +translate([210,0,0]) import("../../dxf/circle.dxf", $fn=0.1); diff --git a/testdata/scad/features/background-modifier.scad b/testdata/scad/features/background-modifier.scad index 5430472..d67270e 100644 --- a/testdata/scad/features/background-modifier.scad +++ b/testdata/scad/features/background-modifier.scad @@ -3,3 +3,8 @@ difference() { %cylinder(h=30, r=6, center=true); } %if (true) cube([25,6,3], center=true); + +%translate([0,-9,0]) difference() { + color("green") cube([10,4,10], center=true); + color("red") translate([0,-2,0]) sphere(3); +} diff --git a/testdata/scad/features/circle-tests.scad b/testdata/scad/features/circle-tests.scad index 6b54d55..90cd9f6 100644 --- a/testdata/scad/features/circle-tests.scad +++ b/testdata/scad/features/circle-tests.scad @@ -8,3 +8,4 @@ translate([6,-3,0]) circle(1, $fn=12); translate([0,-6,0]) circle(1, $fa=20, $fs=0.3); translate([3,-6,0]) circle(1, $fa=30, $fs=0.3); translate([6,-6,0]) circle(1, $fa=40, $fs=0.3); +translate([3,-9,0]) circle(1, $fn=0.1); diff --git a/testdata/scad/features/cylinder-tests.scad b/testdata/scad/features/cylinder-tests.scad index 71f43a6..75b06f2 100644 --- a/testdata/scad/features/cylinder-tests.scad +++ b/testdata/scad/features/cylinder-tests.scad @@ -16,5 +16,5 @@ translate([22,11,0]) cylinder(h=15, r=5, r2=5); // tend to "abuse" this for captured nut slots translate([-10,0,0]) cylinder(h=2, r=3, $fn=6); - -// FIXME: We could test $fs, $fa, $fn as well +// Test that we clamp number of sections to a minimum of 3 +translate([-10, -10, 0]) cylinder(r=3.5356, h=7.0711, $fn=0.1, center=true); diff --git a/testdata/scad/features/highlight-and-background-modifier.scad b/testdata/scad/features/highlight-and-background-modifier.scad index 5dca703..d97408b 100644 --- a/testdata/scad/features/highlight-and-background-modifier.scad +++ b/testdata/scad/features/highlight-and-background-modifier.scad @@ -1,10 +1,31 @@ difference() { sphere(r=10); %#cylinder(h=30, r=6, center=true); - %#if (true) cube([6,25,3], center=true); + %#if (true) cube([25,6,3], center=true); } -translate([13,0,0]) difference() { - sphere(r=10); - #%cylinder(h=30, r=6, center=true); - #%if (true) cube([6,25,3], center=true); +%#translate([0,-9,0]) difference() { + color("green") cube([10,4,10], center=true); + color("red") translate([0,-2,0]) sphere(3); +} +%translate([13,0,0]){ + difference() { + sphere(r=10); + cylinder(h=30, r=6, center=true); + if (true) cube([25,6,3], center=true); + } + #translate([0,-9,0]) difference() { + color("green") cube([10,4,10], center=true); + color("red") translate([0,-2,0]) sphere(3); + } +} +#translate([26,0,0]){ + difference() { + sphere(r=10); + cylinder(h=30, r=6, center=true); + if (true) cube([25,6,3], center=true); + } + %translate([0,-9,0]) difference() { + color("green") cube([10,4,10], center=true); + color("red") translate([0,-2,0]) sphere(3); + } } diff --git a/testdata/scad/features/highlight-modifier.scad b/testdata/scad/features/highlight-modifier.scad index f228d08..2141f58 100644 --- a/testdata/scad/features/highlight-modifier.scad +++ b/testdata/scad/features/highlight-modifier.scad @@ -3,3 +3,8 @@ difference() { #cylinder(h=30, r=6, center=true); } #if (true) cube([25,6,3], center=true); + +#translate([0,-9,0]) difference() { + color("green") cube([10,4,10], center=true); + color("red") translate([0,-2,0]) sphere(3); +} diff --git a/testdata/scad/features/ifelse-tests.scad b/testdata/scad/features/ifelse-tests.scad index d8c777b..3c72d55 100644 --- a/testdata/scad/features/ifelse-tests.scad +++ b/testdata/scad/features/ifelse-tests.scad @@ -1,5 +1,11 @@ -if (true) cube(2, true); -else cylinder(r=1,h=2); +if (true) { + cube(2, true); + translate([-3,0,0]) cube(2, true); +} +else { + cylinder(r=1,h=2); + translate([-3,0,0]) cylinder(r=1,h=2); +} translate([3,0,0]) if (false) cylinder(r=1,h=2); diff --git a/testdata/scad/features/linear_extrude-tests.scad b/testdata/scad/features/linear_extrude-tests.scad index 528eea2..680bf53 100644 --- a/testdata/scad/features/linear_extrude-tests.scad +++ b/testdata/scad/features/linear_extrude-tests.scad @@ -22,3 +22,6 @@ translate([-15,20,0]) linear_extrude(height=20, scale=[4,5,6]) square(10); translate([-10,5,0]) linear_extrude(height=15, scale=-2) square(10, center=true); // scale given as undefined translate([-15,-15,0]) linear_extrude(height=10, scale=var_undef) square(10); + +// height is negative +translate([0,-25,0]) linear_extrude(-1) square(10, center=true); diff --git a/testdata/scad/features/rotate_extrude-tests.scad b/testdata/scad/features/rotate_extrude-tests.scad index 347bc78..010b7d2 100644 --- a/testdata/scad/features/rotate_extrude-tests.scad +++ b/testdata/scad/features/rotate_extrude-tests.scad @@ -28,3 +28,7 @@ translate([50,50,0]) { translate([-50,0,0]) cube([100,100,100], center=true); } } + +// Minimal $fn +translate([0,-60,0]) rotate_extrude($fn=1) translate([20,0,0]) circle(r=10,$fn=1); + diff --git a/testdata/scad/features/sphere-tests.scad b/testdata/scad/features/sphere-tests.scad index e666c1b..cc80738 100644 --- a/testdata/scad/features/sphere-tests.scad +++ b/testdata/scad/features/sphere-tests.scad @@ -8,3 +8,4 @@ translate([11,11,0]) sphere(5, $fn=15); translate([22,-11, 0]) sphere(5, $fa=20, $fs=0.3); translate([22, 0, 0]) sphere(5, $fa=30, $fs=0.3); translate([22, 11, 0]) sphere(5, $fa=40, $fs=0.3); +translate([11, 22, 0]) sphere(5, $fn=0.1); diff --git a/testdata/scad/minimal/allexpressions.scad b/testdata/scad/minimal/allexpressions.scad new file mode 100644 index 0000000..f618c4b --- /dev/null +++ b/testdata/scad/minimal/allexpressions.scad @@ -0,0 +1,30 @@ +a = true; +b = false; +c = undef; +d = a; +e = $fn; +f1 = [1,,]; +f2 = [1,2,3]; +g = f2.x + f2.y + f2.z; +h1 = [2:5]; +h2 = [1:2:10]; +i = h2.begin - h2.step - h2.end; +j = "test"; +k = 1.23e-2; +l = a * b; +m = a / b; +n = a % b; +o = c < d; +p = c <= d; +q = c == d; +r = c != d; +s = c >= d; +t = c > d; +u = e && g; +v = e || g; +w = +i; +x = -i; +y = !i; +z = (j); +aa = k ? l : m; +bb = n[o]; diff --git a/testdata/scad/minimal/allfunctions.scad b/testdata/scad/minimal/allfunctions.scad index 2aebe54..b97f121 100644 --- a/testdata/scad/minimal/allfunctions.scad +++ b/testdata/scad/minimal/allfunctions.scad @@ -1,27 +1,28 @@ a = abs(); -a = sign(); -a = rands(); -a = min(); -a = max(); -a = sin(); -a = cos(); -a = asin(); -a = acos(); -a = tan(); -a = atan(); -a = atan2(); -a = round(); -a = ceil(); -a = floor(); -a = pow(); -a = sqrt(); -a = exp(); -a = log(); -a = ln(); -a = str(); -a = lookup(); -a = dxf_dim(); -a = dxf_cross(); -a = version(); -a = version_num(); -a = len(); +b = sign(); +c = rands(); +d = min(); +e = max(); +f = sin(); +g = cos(); +h = asin(); +i = acos(); +j = tan(); +k = atan(); +l = atan2(); +m = round(); +n = ceil(); +o = floor(); +p = pow(); +q = sqrt(); +r = exp(); +s = log(); +t = ln(); +u = str(); +v = lookup(); +w = dxf_dim(); +x = dxf_cross(); +y = version(); +z = version_num(); +aa = len(); +bb = search(); diff --git a/testdata/scad/minimal/allmodules.scad b/testdata/scad/minimal/allmodules.scad index 4395fd7..2e38d8f 100644 --- a/testdata/scad/minimal/allmodules.scad +++ b/testdata/scad/minimal/allmodules.scad @@ -2,12 +2,13 @@ minkowski(); glide(); subdiv(); hull(); +resize(); child(); echo(); assign(); for(); intersection_for(); -if(true) { } +if(false) { cube(); } else { sphere(); } union(); difference(); intersection(); diff --git a/testdata/scad/misc/value-reassignment-tests.scad b/testdata/scad/misc/value-reassignment-tests.scad index 475f78f..26afa03 100644 --- a/testdata/scad/misc/value-reassignment-tests.scad +++ b/testdata/scad/misc/value-reassignment-tests.scad @@ -1,4 +1,9 @@ +// Test reassignment which depends on a previously assigned variable, +// as this could be messed up if order of assignment evaluation +// changes + myval = 2; i = 2; -myval = i * 2; -echo(myval); +myval = i * 2; // This is not (yet) allowed as it will be evaluates in place of the first assignment +echo(myval, i); // Should output undef, 2 + diff --git a/testdata/scad/misc/value-reassignment-tests2.scad b/testdata/scad/misc/value-reassignment-tests2.scad new file mode 100644 index 0000000..29a2fb7 --- /dev/null +++ b/testdata/scad/misc/value-reassignment-tests2.scad @@ -0,0 +1,16 @@ +// Test reassignment where another variable has used the previous +// value before the reassignment. This could get messed up if order of +// assignment evaluation changes + +myval = 2; +i = myval; +myval = 3; +echo(myval, i); // Should output 3, 3 + +// NB! This feels wrong, but it's a simulation of what happens +// when overriding a variable on the cmd-line: openscad -Dmyval=3 myfile.scad +// Since the intention is to override a top-level variable, the evaluation of the +// new expression must be done in the same place as the old. +// This is currently solved by appending the text given to the -D parameter to the end +// of the main file. + diff --git a/testdata/scad/misc/variable-scope-tests.scad b/testdata/scad/misc/variable-scope-tests.scad index 8426fbb..104d1a4 100644 --- a/testdata/scad/misc/variable-scope-tests.scad +++ b/testdata/scad/misc/variable-scope-tests.scad @@ -49,5 +49,16 @@ echo("undeclared variable can still be passed and used"); module undeclared_var() { echo(d); } - undeclared_var(d=6); + +echo("attempt to assign from a not-yet-defined variable which also exists globally"); + +globalval = 1; +// Test that b = a turns into b = 1, heeding the order of the assignments +// See issue #399 for more discussion +module global_lookup() { + b = globalval; // Should be assigned 1 since the local one isn't yet defined + globalval = 5; // Overrides the value for the local scope only + echo(globalval,b); // Should output 5, 1 +} +global_lookup(); diff --git a/tests/.gitignore b/tests/.gitignore index 2f5abd7..5033bd2 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -21,5 +21,8 @@ out.png /throwntogethertest /cgalstlsanitytest /cgalcachetest +/modulecachetest +/moduledumptest +/test_pretty_print /sysinfo.txt /CTestCustom.cmake
\ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 39dc341..7509ed1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -333,6 +333,9 @@ endif() if(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD") include_directories( /usr/pkg/include /usr/X11R7/include ) set(FLEX_EXECUTABLE /usr/pkg/bin/flex) + if(NOT ${CMAKE_CXX_COMPILER} MATCHES ".*clang.*") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + endif() endif() find_package(FLEX REQUIRED) @@ -501,8 +504,8 @@ set(OFFSCREEN_SOURCES ../src/system-gl.cc) add_library(tests-core STATIC ${CORE_SOURCES}) -target_link_libraries(tests-core ${OPENGL_LIBRARY}) -set(TESTS-CORE-LIBRARIES ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${Boost_LIBRARIES}) +target_link_libraries(tests-core ${OPENGL_LIBRARIES}) +set(TESTS-CORE-LIBRARIES ${OPENGL_LIBRARIES} ${Boost_LIBRARIES}) add_library(tests-common STATIC ${COMMON_SOURCES}) target_link_libraries(tests-common tests-core) @@ -531,6 +534,12 @@ add_executable(dumptest dumptest.cc) target_link_libraries(dumptest tests-nocgal ${TESTS-NOCGAL-LIBRARIES}) # +# moduledumptest +# +add_executable(moduledumptest moduledumptest.cc) +target_link_libraries(moduledumptest tests-nocgal ${TESTS-NOCGAL-LIBRARIES}) + +# # modulecachetest # add_executable(modulecachetest modulecachetest.cc) @@ -770,6 +779,7 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES} ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/variable-scope-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/lookup-tests.scad) @@ -834,8 +844,6 @@ disable_tests(cgalpngtest_child-background openscad-cgalpng_child-background openscad-cgalpng_highlight-and-background-modifier openscad-cgalpng_testcolornames - throwntogethertest_child-background - throwntogethertest_highlight-and-background-modifier throwntogethertest_testcolornames) # Test config handling @@ -922,6 +930,10 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake ${TMP}) add_cmdline_test(echotest SUFFIX txt FILES ${ECHO_FILES}) add_cmdline_test(dumptest SUFFIX txt FILES ${DUMPTEST_FILES}) +add_cmdline_test(moduledumptest SUFFIX txt FILES + ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/allmodules.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/allfunctions.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/minimal/allexpressions.scad) add_cmdline_test(csgtexttest SUFFIX txt FILES ${MINIMAL_FILES}) add_cmdline_test(csgtermtest SUFFIX txt FILES ${MINIMAL_FILES}) add_cmdline_test(cgalpngtest SUFFIX png FILES ${CGALPNGTEST_FILES}) diff --git a/tests/moduledumptest.cc b/tests/moduledumptest.cc new file mode 100644 index 0000000..5925d03 --- /dev/null +++ b/tests/moduledumptest.cc @@ -0,0 +1,118 @@ +/* + * OpenSCAD (www.openscad.org) + * Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and + * Marius Kintel <marius@kintel.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * As a special exception, you have permission to link this program + * with the CGAL library and distribute executables, as long as you + * follow the requirements of the GNU GPL in regard to all of the + * software in the executable aside from CGAL. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "tests-common.h" +#include "openscad.h" +#include "parsersettings.h" +#include "node.h" +#include "module.h" +#include "modcontext.h" +#include "value.h" +#include "export.h" +#include "builtin.h" +#include "Tree.h" + +#ifndef _MSC_VER +#include <getopt.h> +#endif +#include <assert.h> +#include <iostream> +#include <sstream> +#include <fstream> + +#include <boost/filesystem.hpp> +namespace fs = boost::filesystem; +#include "boosty.h" + +std::string commandline_commands; +std::string currentdir; + +using std::string; + +string dumptree(const Tree &tree, const AbstractNode &node) +{ + std::stringstream str; + const std::vector<AbstractNode*> &children = node.getChildren(); + for (std::vector<AbstractNode*>::const_iterator iter = children.begin(); iter != children.end(); iter++) { + str << tree.getString(**iter) << "\n"; + } + return str.str(); +} + +int main(int argc, char **argv) +{ +#ifdef _MSC_VER + _set_output_format(_TWO_DIGIT_EXPONENT); +#endif + if (argc != 3) { + fprintf(stderr, "Usage: %s <file.scad> <output.txt>\n", argv[0]); + exit(1); + } + + const char *filename = argv[1]; + const char *outfilename = argv[2]; + int rc = 0; + + Builtins::instance()->initialize(); + + fs::path original_path = fs::current_path(); + + currentdir = boosty::stringy(fs::current_path()); + + parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); + add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); + + ModuleContext top_ctx; + top_ctx.registerBuiltin(); + + FileModule *root_module; + ModuleInstantiation root_inst("group"); + + root_module = parsefile(filename); + if (!root_module) { + exit(1); + } + + string dumpstdstr = root_module->dump("", ""); + + fs::current_path(original_path); + std::ofstream outfile; + outfile.open(outfilename); + if (!outfile.is_open()) { + fprintf(stderr, "Error: Unable to open output file %s\n", outfilename); + exit(1); + } + std::cout << "Opened " << outfilename << "\n"; + outfile << dumpstdstr << "\n"; + outfile.close(); + if (outfile.fail()) fprintf(stderr, "Failed to close file\n"); + + delete root_module; + + Builtins::instance(true); + + return rc; +} diff --git a/tests/regression/cgalpngtest/arc-expected.png b/tests/regression/cgalpngtest/arc-expected.png Binary files differindex 2f555c4..23a67c7 100644 --- a/tests/regression/cgalpngtest/arc-expected.png +++ b/tests/regression/cgalpngtest/arc-expected.png diff --git a/tests/regression/cgalpngtest/circle-expected.png b/tests/regression/cgalpngtest/circle-expected.png Binary files differindex aacf12d..89a7fd0 100644 --- a/tests/regression/cgalpngtest/circle-expected.png +++ b/tests/regression/cgalpngtest/circle-expected.png diff --git a/tests/regression/cgalpngtest/circle-tests-expected.png b/tests/regression/cgalpngtest/circle-tests-expected.png Binary files differindex 0736af5..d640042 100644 --- a/tests/regression/cgalpngtest/circle-tests-expected.png +++ b/tests/regression/cgalpngtest/circle-tests-expected.png diff --git a/tests/regression/cgalpngtest/cylinder-tests-expected.png b/tests/regression/cgalpngtest/cylinder-tests-expected.png Binary files differindex 843d70f..5d63548 100644 --- a/tests/regression/cgalpngtest/cylinder-tests-expected.png +++ b/tests/regression/cgalpngtest/cylinder-tests-expected.png diff --git a/tests/regression/cgalpngtest/example024-expected.png b/tests/regression/cgalpngtest/example024-expected.png Binary files differnew file mode 100644 index 0000000..8e69808 --- /dev/null +++ b/tests/regression/cgalpngtest/example024-expected.png diff --git a/tests/regression/cgalpngtest/highlight-modifier-expected.png b/tests/regression/cgalpngtest/highlight-modifier-expected.png Binary files differindex e220aa1..2fc7678 100644 --- a/tests/regression/cgalpngtest/highlight-modifier-expected.png +++ b/tests/regression/cgalpngtest/highlight-modifier-expected.png diff --git a/tests/regression/cgalpngtest/ifelse-tests-expected.png b/tests/regression/cgalpngtest/ifelse-tests-expected.png Binary files differindex fcda7bc..6680498 100644 --- a/tests/regression/cgalpngtest/ifelse-tests-expected.png +++ b/tests/regression/cgalpngtest/ifelse-tests-expected.png diff --git a/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png b/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png Binary files differindex ee60a72..1488c85 100644 --- a/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png +++ b/tests/regression/cgalpngtest/rotate_extrude-tests-expected.png diff --git a/tests/regression/cgalpngtest/sphere-tests-expected.png b/tests/regression/cgalpngtest/sphere-tests-expected.png Binary files differindex f2a11f3..781d8a9 100644 --- a/tests/regression/cgalpngtest/sphere-tests-expected.png +++ b/tests/regression/cgalpngtest/sphere-tests-expected.png diff --git a/tests/regression/csgtermtest/allexpressions-expected.txt b/tests/regression/csgtermtest/allexpressions-expected.txt new file mode 100644 index 0000000..a40cf60 --- /dev/null +++ b/tests/regression/csgtermtest/allexpressions-expected.txt @@ -0,0 +1 @@ +No top-level CSG object diff --git a/tests/regression/csgtermtest/allmodules-expected.txt b/tests/regression/csgtermtest/allmodules-expected.txt index f544c01..b802c81 100644 --- a/tests/regression/csgtermtest/allmodules-expected.txt +++ b/tests/regression/csgtermtest/allmodules-expected.txt @@ -1 +1 @@ -((((((cube23 + sphere24) + cylinder25) + polyhedron26) + square27) + circle28) + polygon29) +(((((((sphere12 + cube25) + sphere26) + cylinder27) + polyhedron28) + square29) + circle30) + polygon31) diff --git a/tests/regression/csgtexttest/allexpressions-expected.txt b/tests/regression/csgtexttest/allexpressions-expected.txt new file mode 100644 index 0000000..331822f --- /dev/null +++ b/tests/regression/csgtexttest/allexpressions-expected.txt @@ -0,0 +1 @@ +group1 diff --git a/tests/regression/csgtexttest/allmodules-expected.txt b/tests/regression/csgtexttest/allmodules-expected.txt index 49171ba..95e0029 100644 --- a/tests/regression/csgtexttest/allmodules-expected.txt +++ b/tests/regression/csgtexttest/allmodules-expected.txt @@ -1 +1 @@ -group1(minkowski2+glide3+subdiv4+hull5+group6+group6+group6+intersection9+group6+union11+difference12+intersection9+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group6+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render31+surface+transform33+transform33+transform35+transform33+transform33+color38) +group1(minkowski2+glide3+subdiv4+hull5+resize6+group7+group7+group7+intersection10+group11(sphere)+union13+difference14+intersection10+linear_extrude+linear_extrude+rotate_extrude+rotate_extrude+import+import+import+import+group7+cube+sphere+cylinder+polyhedron+square+circle+polygon+projection+render33+surface+transform35+transform35+transform37+transform35+transform35+color40) diff --git a/tests/regression/dumptest/allexpressions-expected.txt b/tests/regression/dumptest/allexpressions-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/regression/dumptest/allexpressions-expected.txt @@ -0,0 +1 @@ + diff --git a/tests/regression/dumptest/allmodules-expected.txt b/tests/regression/dumptest/allmodules-expected.txt index e6bd498..74bf191 100644 --- a/tests/regression/dumptest/allmodules-expected.txt +++ b/tests/regression/dumptest/allmodules-expected.txt @@ -2,11 +2,14 @@ glide(path = undef, convexity = 0); subdiv(level = 1, convexity = 0); hull(); + resize(newsize = [0,0,0], auto = [0,0,0]); group(); group(); group(); intersection(); - group(); + group() { + sphere($fn = 0, $fa = 12, $fs = 2, r = 1); + } union(); difference(); intersection(); diff --git a/tests/regression/dumptest/background-modifier-expected.txt b/tests/regression/dumptest/background-modifier-expected.txt index ed769b3..5861bef 100644 --- a/tests/regression/dumptest/background-modifier-expected.txt +++ b/tests/regression/dumptest/background-modifier-expected.txt @@ -5,4 +5,16 @@ %group() { cube(size = [25, 6, 3], center = true); } + %multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + color([0, 0.501961, 0, 1]) { + cube(size = [10, 4, 10], center = true); + } + color([1, 0, 0, 1]) { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 0, $fa = 12, $fs = 2, r = 3); + } + } + } + } diff --git a/tests/regression/dumptest/circle-tests-expected.txt b/tests/regression/dumptest/circle-tests-expected.txt index 910b375..28359d8 100644 --- a/tests/regression/dumptest/circle-tests-expected.txt +++ b/tests/regression/dumptest/circle-tests-expected.txt @@ -26,4 +26,7 @@ multmatrix([[1, 0, 0, 6], [0, 1, 0, -6], [0, 0, 1, 0], [0, 0, 0, 1]]) { circle($fn = 0, $fa = 40, $fs = 0.3, r = 1); } + multmatrix([[1, 0, 0, 3], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) { + circle($fn = 0.1, $fa = 12, $fs = 2, r = 1); + } diff --git a/tests/regression/dumptest/cylinder-tests-expected.txt b/tests/regression/dumptest/cylinder-tests-expected.txt index b1e8b6e..c47ffe1 100644 --- a/tests/regression/dumptest/cylinder-tests-expected.txt +++ b/tests/regression/dumptest/cylinder-tests-expected.txt @@ -32,4 +32,7 @@ multmatrix([[1, 0, 0, -10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { cylinder($fn = 6, $fa = 12, $fs = 2, h = 2, r1 = 3, r2 = 3, center = false); } + multmatrix([[1, 0, 0, -10], [0, 1, 0, -10], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cylinder($fn = 0.1, $fa = 12, $fs = 2, h = 7.0711, r1 = 3.5356, r2 = 3.5356, center = true); + } diff --git a/tests/regression/dumptest/example024-expected.txt b/tests/regression/dumptest/example024-expected.txt new file mode 100644 index 0000000..e7d6e8b --- /dev/null +++ b/tests/regression/dumptest/example024-expected.txt @@ -0,0 +1,1869 @@ + difference() { + multmatrix([[0.81649658092, 0.40824829046, 0.40824829046, 0], [0, 0.70710678118, -0.70710678118, 0], [-0.57735026919, 0.57735026919, 0.57735026919, 0], [0, 0, 0, 1]]) { + group() { + difference() { + cube(size = [100, 100, 100], center = true); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 33.3333, 33.3333], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + } + } + } + } + multmatrix([[0, -1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 33.3333, 33.3333], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + } + } + } + } + multmatrix([[0, 0, 1, 0], [0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 33.3333, 33.3333], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, -33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 33.33333333333], [0, 0, 1, 33.33333333333], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 11.1111, 11.1111], center = true); + group() { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group(); + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, -11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 11.11111111111], [0, 0, 1, 11.11111111111], [0, 0, 0, 1]]) { + group() { + cube(size = [110, 3.7037, 3.7037], center = true); + group(); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -100], [0, 0, 0, 1]]) { + cube(size = [200, 200, 200], center = true); + } + } + diff --git a/tests/regression/dumptest/highlight-and-background-modifier-expected.txt b/tests/regression/dumptest/highlight-and-background-modifier-expected.txt index 20c82cc..eb8931c 100644 --- a/tests/regression/dumptest/highlight-and-background-modifier-expected.txt +++ b/tests/regression/dumptest/highlight-and-background-modifier-expected.txt @@ -2,15 +2,60 @@ sphere($fn = 0, $fa = 12, $fs = 2, r = 10); %cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true); %group() { - cube(size = [6, 25, 3], center = true); + cube(size = [25, 6, 3], center = true); } } - multmatrix([[1, 0, 0, 13], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + %multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + color([0, 0.501961, 0, 1]) { + cube(size = [10, 4, 10], center = true); + } + color([1, 0, 0, 1]) { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 0, $fa = 12, $fs = 2, r = 3); + } + } + } + } + %multmatrix([[1, 0, 0, 13], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + sphere($fn = 0, $fa = 12, $fs = 2, r = 10); + cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true); + group() { + cube(size = [25, 6, 3], center = true); + } + } + multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + color([0, 0.501961, 0, 1]) { + cube(size = [10, 4, 10], center = true); + } + color([1, 0, 0, 1]) { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 0, $fa = 12, $fs = 2, r = 3); + } + } + } + } + } + multmatrix([[1, 0, 0, 26], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { difference() { sphere($fn = 0, $fa = 12, $fs = 2, r = 10); - %cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true); - %group() { - cube(size = [6, 25, 3], center = true); + cylinder($fn = 0, $fa = 12, $fs = 2, h = 30, r1 = 6, r2 = 6, center = true); + group() { + cube(size = [25, 6, 3], center = true); + } + } + %multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + color([0, 0.501961, 0, 1]) { + cube(size = [10, 4, 10], center = true); + } + color([1, 0, 0, 1]) { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 0, $fa = 12, $fs = 2, r = 3); + } + } } } } diff --git a/tests/regression/dumptest/highlight-modifier-expected.txt b/tests/regression/dumptest/highlight-modifier-expected.txt index c6204bd..c0a29ad 100644 --- a/tests/regression/dumptest/highlight-modifier-expected.txt +++ b/tests/regression/dumptest/highlight-modifier-expected.txt @@ -5,4 +5,16 @@ group() { cube(size = [25, 6, 3], center = true); } + multmatrix([[1, 0, 0, 0], [0, 1, 0, -9], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + color([0, 0.501961, 0, 1]) { + cube(size = [10, 4, 10], center = true); + } + color([1, 0, 0, 1]) { + multmatrix([[1, 0, 0, 0], [0, 1, 0, -2], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 0, $fa = 12, $fs = 2, r = 3); + } + } + } + } diff --git a/tests/regression/dumptest/ifelse-tests-expected.txt b/tests/regression/dumptest/ifelse-tests-expected.txt index 1b60002..e2d3fce 100644 --- a/tests/regression/dumptest/ifelse-tests-expected.txt +++ b/tests/regression/dumptest/ifelse-tests-expected.txt @@ -1,5 +1,8 @@ group() { cube(size = [2, 2, 2], center = true); + multmatrix([[1, 0, 0, -3], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [2, 2, 2], center = true); + } } multmatrix([[1, 0, 0, 3], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { group() { diff --git a/tests/regression/dumptest/linear_extrude-tests-expected.txt b/tests/regression/dumptest/linear_extrude-tests-expected.txt index c867388..face5e2 100644 --- a/tests/regression/dumptest/linear_extrude-tests-expected.txt +++ b/tests/regression/dumptest/linear_extrude-tests-expected.txt @@ -54,4 +54,9 @@ square(size = [10, 10], center = false); } } + multmatrix([[1, 0, 0, 0], [0, 1, 0, -25], [0, 0, 1, 0], [0, 0, 0, 1]]) { + linear_extrude(height = 0, center = false, convexity = 1, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) { + square(size = [10, 10], center = true); + } + } diff --git a/tests/regression/dumptest/rotate_extrude-tests-expected.txt b/tests/regression/dumptest/rotate_extrude-tests-expected.txt index 42faff2..023d78d 100644 --- a/tests/regression/dumptest/rotate_extrude-tests-expected.txt +++ b/tests/regression/dumptest/rotate_extrude-tests-expected.txt @@ -42,4 +42,11 @@ } } } + multmatrix([[1, 0, 0, 0], [0, 1, 0, -60], [0, 0, 1, 0], [0, 0, 0, 1]]) { + rotate_extrude(convexity = 1, $fn = 1, $fa = 12, $fs = 2) { + multmatrix([[1, 0, 0, 20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + circle($fn = 1, $fa = 12, $fs = 2, r = 10); + } + } + } diff --git a/tests/regression/dumptest/sphere-tests-expected.txt b/tests/regression/dumptest/sphere-tests-expected.txt index 84f8c23..461e946 100644 --- a/tests/regression/dumptest/sphere-tests-expected.txt +++ b/tests/regression/dumptest/sphere-tests-expected.txt @@ -26,4 +26,7 @@ multmatrix([[1, 0, 0, 22], [0, 1, 0, 11], [0, 0, 1, 0], [0, 0, 0, 1]]) { sphere($fn = 0, $fa = 40, $fs = 0.3, r = 5); } + multmatrix([[1, 0, 0, 11], [0, 1, 0, 22], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 0.1, $fa = 12, $fs = 2, r = 5); + } diff --git a/tests/regression/echotest/value-reassignment-tests-expected.txt b/tests/regression/echotest/value-reassignment-tests-expected.txt index 05a6741..344f7ab 100644 --- a/tests/regression/echotest/value-reassignment-tests-expected.txt +++ b/tests/regression/echotest/value-reassignment-tests-expected.txt @@ -1 +1,2 @@ -ECHO: 4 +WARNING: Ignoring unknown variable 'i'. +ECHO: undef, 2 diff --git a/tests/regression/echotest/value-reassignment-tests2-expected.txt b/tests/regression/echotest/value-reassignment-tests2-expected.txt new file mode 100644 index 0000000..efb1be7 --- /dev/null +++ b/tests/regression/echotest/value-reassignment-tests2-expected.txt @@ -0,0 +1 @@ +ECHO: 3, 3 diff --git a/tests/regression/echotest/variable-scope-tests-expected.txt b/tests/regression/echotest/variable-scope-tests-expected.txt index 92db05d..5994778 100644 --- a/tests/regression/echotest/variable-scope-tests-expected.txt +++ b/tests/regression/echotest/variable-scope-tests-expected.txt @@ -14,3 +14,5 @@ ECHO: undef ECHO: 5 ECHO: "undeclared variable can still be passed and used" ECHO: 6 +ECHO: "attempt to assign from a not-yet-defined variable which also exists globally" +ECHO: 5, 1 diff --git a/tests/regression/moduledumptest/allexpressions-expected.txt b/tests/regression/moduledumptest/allexpressions-expected.txt new file mode 100644 index 0000000..6d9de40 --- /dev/null +++ b/tests/regression/moduledumptest/allexpressions-expected.txt @@ -0,0 +1,31 @@ +a = true; +b = false; +c = undef; +d = a; +e = $fn; +f1 = [1]; +f2 = [1, 2, 3]; +g = ((f2.x + f2.y) + f2.z); +h1 = [2 : 1 : 5]; +h2 = [1 : 2 : 10]; +i = ((h2.begin - h2.step) - h2.end); +j = "test"; +k = 0.0123; +l = (a * b); +m = (a / b); +n = (a % b); +o = (c < d); +p = (c <= d); +q = (c == d); +r = (c != d); +s = (c >= d); +t = (c > d); +u = (e && g); +v = (e || g); +w = i; +x = -i; +y = !i; +z = j; +aa = (k ? l : m); +bb = n[o]; + diff --git a/tests/regression/moduledumptest/allfunctions-expected.txt b/tests/regression/moduledumptest/allfunctions-expected.txt new file mode 100644 index 0000000..a8a0fea --- /dev/null +++ b/tests/regression/moduledumptest/allfunctions-expected.txt @@ -0,0 +1,29 @@ +a = abs(); +b = sign(); +c = rands(); +d = min(); +e = max(); +f = sin(); +g = cos(); +h = asin(); +i = acos(); +j = tan(); +k = atan(); +l = atan2(); +m = round(); +n = ceil(); +o = floor(); +p = pow(); +q = sqrt(); +r = exp(); +s = log(); +t = ln(); +u = str(); +v = lookup(); +w = dxf_dim(); +x = dxf_cross(); +y = version(); +z = version_num(); +aa = len(); +bb = search(); + diff --git a/tests/regression/moduledumptest/allmodules-expected.txt b/tests/regression/moduledumptest/allmodules-expected.txt new file mode 100644 index 0000000..0e3fc32 --- /dev/null +++ b/tests/regression/moduledumptest/allmodules-expected.txt @@ -0,0 +1,41 @@ +minkowski(); +glide(); +subdiv(); +hull(); +resize(); +child(); +echo(); +assign(); +for(); +intersection_for(); +if(false) cube(); +else sphere(); +union(); +difference(); +intersection(); +dxf_linear_extrude(); +linear_extrude(); +dxf_rotate_extrude(); +rotate_extrude(); +import(); +import_stl(); +import_off(); +import_dxf(); +group(); +cube(); +sphere(); +cylinder(); +polyhedron(); +square(); +circle(); +polygon(); +projection(); +render(); +surface(); +scale(); +rotate(); +mirror(); +translate(); +multmatrix(); +color(); + diff --git a/tests/regression/opencsgtest/arc-expected.png b/tests/regression/opencsgtest/arc-expected.png Binary files differindex a930c0e..7361853 100644 --- a/tests/regression/opencsgtest/arc-expected.png +++ b/tests/regression/opencsgtest/arc-expected.png diff --git a/tests/regression/opencsgtest/background-modifier-expected.png b/tests/regression/opencsgtest/background-modifier-expected.png Binary files differindex 2505331..9cf7db0 100644 --- a/tests/regression/opencsgtest/background-modifier-expected.png +++ b/tests/regression/opencsgtest/background-modifier-expected.png diff --git a/tests/regression/opencsgtest/circle-expected.png b/tests/regression/opencsgtest/circle-expected.png Binary files differindex 7bc63b1..14fce98 100644 --- a/tests/regression/opencsgtest/circle-expected.png +++ b/tests/regression/opencsgtest/circle-expected.png diff --git a/tests/regression/opencsgtest/circle-tests-expected.png b/tests/regression/opencsgtest/circle-tests-expected.png Binary files differindex 2e5b314..06f7d9c 100644 --- a/tests/regression/opencsgtest/circle-tests-expected.png +++ b/tests/regression/opencsgtest/circle-tests-expected.png diff --git a/tests/regression/opencsgtest/cylinder-tests-expected.png b/tests/regression/opencsgtest/cylinder-tests-expected.png Binary files differindex 4c7ab79..d0c607c 100644 --- a/tests/regression/opencsgtest/cylinder-tests-expected.png +++ b/tests/regression/opencsgtest/cylinder-tests-expected.png diff --git a/tests/regression/opencsgtest/difference-tests-expected.png b/tests/regression/opencsgtest/difference-tests-expected.png Binary files differindex a6d863a..8db2742 100644 --- a/tests/regression/opencsgtest/difference-tests-expected.png +++ b/tests/regression/opencsgtest/difference-tests-expected.png diff --git a/tests/regression/opencsgtest/example024-expected.png b/tests/regression/opencsgtest/example024-expected.png Binary files differnew file mode 100644 index 0000000..dfbe847 --- /dev/null +++ b/tests/regression/opencsgtest/example024-expected.png diff --git a/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png b/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png Binary files differindex 8febe76..c1c7313 100644 --- a/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png +++ b/tests/regression/opencsgtest/highlight-and-background-modifier-expected.png diff --git a/tests/regression/opencsgtest/highlight-modifier-expected.png b/tests/regression/opencsgtest/highlight-modifier-expected.png Binary files differindex af01e5b..1021c22 100644 --- a/tests/regression/opencsgtest/highlight-modifier-expected.png +++ b/tests/regression/opencsgtest/highlight-modifier-expected.png diff --git a/tests/regression/opencsgtest/ifelse-tests-expected.png b/tests/regression/opencsgtest/ifelse-tests-expected.png Binary files differindex 6dae4df..84e18e3 100644 --- a/tests/regression/opencsgtest/ifelse-tests-expected.png +++ b/tests/regression/opencsgtest/ifelse-tests-expected.png diff --git a/tests/regression/opencsgtest/rotate_extrude-tests-expected.png b/tests/regression/opencsgtest/rotate_extrude-tests-expected.png Binary files differindex 96452e1..861f6ab 100644 --- a/tests/regression/opencsgtest/rotate_extrude-tests-expected.png +++ b/tests/regression/opencsgtest/rotate_extrude-tests-expected.png diff --git a/tests/regression/opencsgtest/sphere-tests-expected.png b/tests/regression/opencsgtest/sphere-tests-expected.png Binary files differindex d11e3bf..d1b4845 100644 --- a/tests/regression/opencsgtest/sphere-tests-expected.png +++ b/tests/regression/opencsgtest/sphere-tests-expected.png diff --git a/tests/regression/throwntogethertest/arc-expected.png b/tests/regression/throwntogethertest/arc-expected.png Binary files differindex 8783806..7361853 100644 --- a/tests/regression/throwntogethertest/arc-expected.png +++ b/tests/regression/throwntogethertest/arc-expected.png diff --git a/tests/regression/throwntogethertest/background-modifier-expected.png b/tests/regression/throwntogethertest/background-modifier-expected.png Binary files differindex 2505331..499b92f 100644 --- a/tests/regression/throwntogethertest/background-modifier-expected.png +++ b/tests/regression/throwntogethertest/background-modifier-expected.png diff --git a/tests/regression/throwntogethertest/child-background-expected.png b/tests/regression/throwntogethertest/child-background-expected.png Binary files differnew file mode 100644 index 0000000..7540ee6 --- /dev/null +++ b/tests/regression/throwntogethertest/child-background-expected.png diff --git a/tests/regression/throwntogethertest/circle-expected.png b/tests/regression/throwntogethertest/circle-expected.png Binary files differindex c001955..14fce98 100644 --- a/tests/regression/throwntogethertest/circle-expected.png +++ b/tests/regression/throwntogethertest/circle-expected.png diff --git a/tests/regression/throwntogethertest/circle-tests-expected.png b/tests/regression/throwntogethertest/circle-tests-expected.png Binary files differindex 2e5b314..06f7d9c 100644 --- a/tests/regression/throwntogethertest/circle-tests-expected.png +++ b/tests/regression/throwntogethertest/circle-tests-expected.png diff --git a/tests/regression/throwntogethertest/cylinder-tests-expected.png b/tests/regression/throwntogethertest/cylinder-tests-expected.png Binary files differindex 4c7ab79..d0c607c 100644 --- a/tests/regression/throwntogethertest/cylinder-tests-expected.png +++ b/tests/regression/throwntogethertest/cylinder-tests-expected.png diff --git a/tests/regression/throwntogethertest/difference-tests-expected.png b/tests/regression/throwntogethertest/difference-tests-expected.png Binary files differindex 0a27c90..7a61f42 100644 --- a/tests/regression/throwntogethertest/difference-tests-expected.png +++ b/tests/regression/throwntogethertest/difference-tests-expected.png diff --git a/tests/regression/throwntogethertest/example024-expected.png b/tests/regression/throwntogethertest/example024-expected.png Binary files differnew file mode 100644 index 0000000..ffa2360 --- /dev/null +++ b/tests/regression/throwntogethertest/example024-expected.png diff --git a/tests/regression/throwntogethertest/highlight-and-background-modifier-expected.png b/tests/regression/throwntogethertest/highlight-and-background-modifier-expected.png Binary files differnew file mode 100644 index 0000000..e95f763 --- /dev/null +++ b/tests/regression/throwntogethertest/highlight-and-background-modifier-expected.png diff --git a/tests/regression/throwntogethertest/highlight-modifier-expected.png b/tests/regression/throwntogethertest/highlight-modifier-expected.png Binary files differindex 7973d82..e8d4e62 100644 --- a/tests/regression/throwntogethertest/highlight-modifier-expected.png +++ b/tests/regression/throwntogethertest/highlight-modifier-expected.png diff --git a/tests/regression/throwntogethertest/ifelse-tests-expected.png b/tests/regression/throwntogethertest/ifelse-tests-expected.png Binary files differindex 7f6374c..5cb1c4c 100644 --- a/tests/regression/throwntogethertest/ifelse-tests-expected.png +++ b/tests/regression/throwntogethertest/ifelse-tests-expected.png diff --git a/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png b/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png Binary files differindex 94b0d9c..8956be2 100644 --- a/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png +++ b/tests/regression/throwntogethertest/rotate_extrude-tests-expected.png diff --git a/tests/regression/throwntogethertest/sphere-tests-expected.png b/tests/regression/throwntogethertest/sphere-tests-expected.png Binary files differindex d11e3bf..d1b4845 100644 --- a/tests/regression/throwntogethertest/sphere-tests-expected.png +++ b/tests/regression/throwntogethertest/sphere-tests-expected.png diff --git a/tests/virtualfb.sh b/tests/virtualfb.sh index 186b389..c5c9668 100755 --- a/tests/virtualfb.sh +++ b/tests/virtualfb.sh @@ -24,7 +24,11 @@ start() fi VFB_DISPLAY=`echo | awk 'BEGIN{srand();} {printf ":%.0f", rand()*1000+100};'` - $VFB_BINARY $VFB_DISPLAY -screen 0 800x600x24 &> ./virtualfb.log & + if [ $debug ]; then + echo debug VFB_DISPLAY $VFB_DISPLAY + echo debug VFB_BINARY $VFB_BINARY + fi + $VFB_BINARY $VFB_DISPLAY -screen 0 800x600x24 > ./virtualfb1.log 2> ./virtualfb2.log & # on some systems $! gives us VFB_BINARY's PID, on others we have to subtract 1 VFB_PID_MINUS0=$! VFB_PID_MINUS1=$(($VFB_PID_MINUS0 - 1)) |