summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AboutDialog.html16
-rw-r--r--src/CGALEvaluator.cc2
-rw-r--r--src/CSGTermEvaluator.cc4
-rw-r--r--src/CocoaUtils.h3
-rw-r--r--src/CocoaUtils.mm5
-rw-r--r--src/CsgInfo.h2
-rw-r--r--src/MainWindow.h2
-rw-r--r--src/MainWindow.ui11
-rw-r--r--src/ModuleCache.cc66
-rw-r--r--src/ModuleCache.h1
-rw-r--r--src/OffscreenContextWGL.cc33
-rw-r--r--src/OpenCSGRenderer.cc63
-rw-r--r--src/PlatformUtils-mac.mm7
-rw-r--r--src/PlatformUtils-posix.cc10
-rw-r--r--src/PlatformUtils-win.cc70
-rw-r--r--src/PlatformUtils.cc40
-rw-r--r--src/PlatformUtils.h14
-rw-r--r--src/PolySetCGALEvaluator.cc1
-rw-r--r--src/ProgressWidget.cc2
-rw-r--r--src/ProgressWidget.ui3
-rw-r--r--src/ThrownTogetherRenderer.cc82
-rw-r--r--src/cgaladv.cc1
-rw-r--r--src/cgalworker.cc1
-rw-r--r--src/control.cc2
-rw-r--r--src/csgops.cc1
-rw-r--r--src/csgterm.cc57
-rw-r--r--src/csgterm.h38
-rw-r--r--src/dxfdim.cc11
-rw-r--r--src/expr.cc18
-rw-r--r--src/func.cc2
-rw-r--r--src/highlighter.cc16
-rw-r--r--src/import.cc2
-rw-r--r--src/lexer.l87
-rw-r--r--src/linearextrude.cc6
-rw-r--r--src/localscope.cc8
-rw-r--r--src/mainwin.cc125
-rw-r--r--src/modcontext.cc71
-rw-r--r--src/modcontext.h3
-rw-r--r--src/module.cc92
-rw-r--r--src/module.h27
-rw-r--r--src/openscad.cc1
-rw-r--r--src/parser.y8
-rw-r--r--src/parsersettings.cc90
-rw-r--r--src/parsersettings.h6
-rw-r--r--src/primitives.cc5
-rw-r--r--src/renderer.cc73
-rw-r--r--src/renderer.h3
-rw-r--r--src/rotateextrude.cc1
-rw-r--r--src/system-gl.cc3
49 files changed, 825 insertions, 370 deletions
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();
}
contact: Jan Huwald // Impressum