892 lines
27 KiB
JavaScript
892 lines
27 KiB
JavaScript
OpenJsCad = function() {
|
|
};
|
|
|
|
// A viewer is a WebGL canvas that lets the user view a mesh. The user can
|
|
// tumble it around by dragging the mouse.
|
|
OpenJsCad.Viewer = function(containerelement, width, height, initialdepth) {
|
|
try
|
|
{
|
|
var gl = GL.create();
|
|
this.gl = gl;
|
|
this.angleX = 0;
|
|
this.angleY = 0;
|
|
this.viewpointX = 0;
|
|
this.viewpointY = 0;
|
|
this.viewpointZ = initialdepth;
|
|
|
|
// Draw triangle lines:
|
|
this.drawLines = false;
|
|
// Set to true so lines don't use the depth buffer
|
|
this.lineOverlay = false;
|
|
|
|
// Set up the viewport
|
|
gl.canvas.width = width;
|
|
gl.canvas.height = height;
|
|
gl.viewport(0, 0, width, height);
|
|
gl.matrixMode(gl.PROJECTION);
|
|
gl.loadIdentity();
|
|
gl.perspective(45, width / height, 0.5, 1000);
|
|
gl.matrixMode(gl.MODELVIEW);
|
|
|
|
// Set up WebGL state
|
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
gl.clearColor(0.93, 0.93, 0.93, 1);
|
|
gl.enable(gl.DEPTH_TEST);
|
|
gl.enable(gl.CULL_FACE);
|
|
gl.polygonOffset(1, 1);
|
|
|
|
// Black shader for wireframe
|
|
this.blackShader = new GL.Shader('\
|
|
void main() {\
|
|
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\
|
|
}\
|
|
', '\
|
|
void main() {\
|
|
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);\
|
|
}\
|
|
');
|
|
|
|
// Shader with diffuse and specular lighting
|
|
this.lightingShader = new GL.Shader('\
|
|
varying vec3 color;\
|
|
varying vec3 normal;\
|
|
varying vec3 light;\
|
|
void main() {\
|
|
const vec3 lightDir = vec3(1.0, 2.0, 3.0) / 3.741657386773941;\
|
|
light = lightDir;\
|
|
color = gl_Color.rgb;\
|
|
normal = gl_NormalMatrix * gl_Normal;\
|
|
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\
|
|
}\
|
|
', '\
|
|
varying vec3 color;\
|
|
varying vec3 normal;\
|
|
varying vec3 light;\
|
|
void main() {\
|
|
vec3 n = normalize(normal);\
|
|
float diffuse = max(0.0, dot(light, n));\
|
|
float specular = pow(max(0.0, -reflect(light, n).z), 10.0) * sqrt(diffuse);\
|
|
gl_FragColor = vec4(mix(color * (0.3 + 0.7 * diffuse), vec3(1.0), specular), 1.0);\
|
|
}\
|
|
');
|
|
|
|
containerelement.appendChild(gl.canvas);
|
|
|
|
var _this=this;
|
|
|
|
gl.onmousemove = function(e) {
|
|
_this.onMouseMove(e);
|
|
};
|
|
gl.ondraw = function() {
|
|
_this.onDraw();
|
|
};
|
|
this.clear();
|
|
}
|
|
catch (e) {
|
|
containerelement.innerHTML = "<b><br><br>Error: "+e.toString()+"</b><br><br>OpenJsCad currently requires Google Chrome with WebGL enabled";
|
|
}
|
|
};
|
|
|
|
OpenJsCad.Viewer.prototype = {
|
|
setCsg: function(csg) {
|
|
this.mesh = OpenJsCad.Viewer.csgToMesh(csg);
|
|
this.onDraw();
|
|
},
|
|
|
|
clear: function() {
|
|
// empty mesh:
|
|
this.mesh = new GL.Mesh();
|
|
this.onDraw();
|
|
},
|
|
|
|
supported: function() {
|
|
return !!this.gl;
|
|
},
|
|
|
|
onMouseMove: function(e) {
|
|
if (e.dragging) {
|
|
e.preventDefault();
|
|
if(e.altKey)
|
|
{
|
|
var factor = 1e-2;
|
|
this.viewpointZ *= Math.pow(2,factor * e.deltaY);
|
|
}
|
|
else if(e.shiftKey)
|
|
{
|
|
var factor = 5e-3;
|
|
this.viewpointX += factor * e.deltaX * this.viewpointZ;
|
|
this.viewpointY -= factor * e.deltaY * this.viewpointZ;
|
|
}
|
|
else
|
|
{
|
|
this.angleY += e.deltaX * 2;
|
|
this.angleX += e.deltaY * 2;
|
|
this.angleX = Math.max(-90, Math.min(90, this.angleX));
|
|
}
|
|
this.onDraw();
|
|
}
|
|
},
|
|
|
|
onDraw: function(e) {
|
|
var gl = this.gl;
|
|
gl.makeCurrent();
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
gl.loadIdentity();
|
|
gl.translate(this.viewpointX, this.viewpointY, -this.viewpointZ);
|
|
gl.rotate(this.angleX, 1, 0, 0);
|
|
gl.rotate(this.angleY, 0, 1, 0);
|
|
|
|
if (!this.lineOverlay) gl.enable(gl.POLYGON_OFFSET_FILL);
|
|
this.lightingShader.draw(this.mesh, gl.TRIANGLES);
|
|
if (!this.lineOverlay) gl.disable(gl.POLYGON_OFFSET_FILL);
|
|
|
|
if(this.drawLines)
|
|
{
|
|
if (this.lineOverlay) gl.disable(gl.DEPTH_TEST);
|
|
gl.enable(gl.BLEND);
|
|
this.blackShader.draw(this.mesh, gl.LINES);
|
|
gl.disable(gl.BLEND);
|
|
if (this.lineOverlay) gl.enable(gl.DEPTH_TEST);
|
|
}
|
|
},
|
|
}
|
|
|
|
// Convert from CSG solid to GL.Mesh object
|
|
OpenJsCad.Viewer.csgToMesh = function(csg) {
|
|
var csg = csg.canonicalized();
|
|
var mesh = new GL.Mesh({ normals: true, colors: true });
|
|
var vertexTag2Index = {};
|
|
var vertices = [];
|
|
var colors = [];
|
|
var triangles = [];
|
|
// set to true if we want to use interpolated vertex normals
|
|
// this creates nice round spheres but does not represent the shape of
|
|
// the actual model
|
|
var smoothlighting = false;
|
|
var polygons = csg.toPolygons();
|
|
var numpolygons = polygons.length;
|
|
for(var polygonindex = 0; polygonindex < numpolygons; polygonindex++)
|
|
{
|
|
var polygon = polygons[polygonindex];
|
|
var indices = polygon.vertices.map(function(vertex) {
|
|
var vertextag = vertex.getTag();
|
|
var vertexindex;
|
|
if(smoothlighting && (vertextag in vertexTag2Index))
|
|
{
|
|
vertexindex = vertexTag2Index[vertextag];
|
|
}
|
|
else
|
|
{
|
|
vertexindex = vertices.length;
|
|
vertexTag2Index[vertextag] = vertexindex;
|
|
vertices.push([vertex.pos.x, vertex.pos.y, vertex.pos.z]);
|
|
colors.push([0,0,1]);
|
|
}
|
|
return vertexindex;
|
|
});
|
|
for (var i = 2; i < indices.length; i++) {
|
|
triangles.push([indices[0], indices[i - 1], indices[i]]);
|
|
}
|
|
}
|
|
mesh.triangles = triangles;
|
|
mesh.vertices = vertices;
|
|
mesh.colors = colors;
|
|
mesh.computeWireframe();
|
|
mesh.computeNormals();
|
|
return mesh;
|
|
};
|
|
|
|
// this is a bit of a hack; doesn't properly supports urls that start with '/'
|
|
// but does handle relative urls containing ../
|
|
OpenJsCad.makeAbsoluteUrl = function(url, baseurl) {
|
|
if(!url.match(/^[a-z]+\:/i))
|
|
{
|
|
var basecomps = baseurl.split("/");
|
|
if(basecomps.length > 0)
|
|
{
|
|
basecomps.splice(basecomps.length - 1, 1);
|
|
}
|
|
var urlcomps = url.split("/");
|
|
var comps = basecomps.concat(urlcomps);
|
|
var comps2 = [];
|
|
comps.map(function(c) {
|
|
if(c == "..")
|
|
{
|
|
if(comps2.length > 0)
|
|
{
|
|
comps2.splice(comps2.length - 1, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
comps2.push(c);
|
|
}
|
|
});
|
|
url = "";
|
|
for(var i = 0; i < comps2.length; i++)
|
|
{
|
|
if(i > 0) url += "/";
|
|
url += comps2[i];
|
|
}
|
|
}
|
|
return url;
|
|
};
|
|
|
|
OpenJsCad.isChrome = function()
|
|
{
|
|
return (navigator.userAgent.search("Chrome") >= 0);
|
|
};
|
|
|
|
// This is called from within the web worker. Execute the main() function of the supplied script
|
|
// and post a message to the calling thread when finished
|
|
OpenJsCad.runMainInWorker = function(mainParameters)
|
|
{
|
|
try
|
|
{
|
|
if(typeof(main) != 'function') throw new Error('Your jscad file should contain a function main() which returns a CSG solid.');
|
|
var csg = main(mainParameters);
|
|
if( (typeof(csg) != "object") || (!(csg instanceof CSG)) )
|
|
{
|
|
throw new Error("Your main() function should return a CSG solid.");
|
|
}
|
|
self.postMessage({cmd: 'rendered', csg: csg});
|
|
}
|
|
catch(e)
|
|
{
|
|
var errtxt = e.stack;
|
|
self.postMessage({cmd: 'error', err: errtxt});
|
|
}
|
|
};
|
|
|
|
OpenJsCad.javaScriptToSolidSync = function(script, mainParameters, callback) {
|
|
var workerscript = "";
|
|
workerscript += script;
|
|
workerscript += "\n\n\n\n\n\n\n/* -------------------------------------------------------------------------\n";
|
|
workerscript += "OpenJsCad debugging\n\nAssuming you are running Chrome:\nF10 steps over an instruction\nF11 steps into an instruction\n";
|
|
workerscript += "F8 continues running\nPress the (||) button at the bottom to enable pausing whenever an error occurs\n";
|
|
workerscript += "Click on a line number to set or clear a breakpoint\n";
|
|
workerscript += "For more information see: http://code.google.com/chrome/devtools/docs/overview.html\n\n";
|
|
workerscript += "------------------------------------------------------------------------- */\n";
|
|
workerscript += "\n\n// Now press F11 twice to enter your main() function:\n\n";
|
|
workerscript += "debugger;\n";
|
|
workerscript += "return main("+JSON.stringify(mainParameters)+");";
|
|
var f = new Function(workerscript);
|
|
var csg = f();
|
|
return csg;
|
|
};
|
|
|
|
// callback: should be function(error, csg)
|
|
OpenJsCad.javaScriptToSolidASync = function(script, mainParameters, callback) {
|
|
var baselibraries = [
|
|
"csg.js",
|
|
"openjscad.js"
|
|
];
|
|
var baseurl = document.location + "";
|
|
var workerscript = "";
|
|
workerscript += script;
|
|
workerscript += "\n\n\n\n//// The following code is added by OpenJsCad:\n";
|
|
workerscript += "var _csg_libraries=" + JSON.stringify(baselibraries)+";\n";
|
|
workerscript += "var _csg_baseurl=" + JSON.stringify(baseurl)+";\n";
|
|
workerscript += "var _csg_makeAbsoluteURL=" + OpenJsCad.makeAbsoluteUrl.toString()+";\n";
|
|
// workerscript += "if(typeof(libs) == 'function') _csg_libraries = _csg_libraries.concat(libs());\n";
|
|
workerscript += "_csg_libraries = _csg_libraries.map(function(l){return _csg_makeAbsoluteURL(l,_csg_baseurl);});\n";
|
|
workerscript += "_csg_libraries.map(function(l){importScripts(l)});\n";
|
|
workerscript += "self.addEventListener('message', function(e) {if(e.data && e.data.cmd == 'render'){";
|
|
workerscript += " OpenJsCad.runMainInWorker("+JSON.stringify(mainParameters)+");";
|
|
// workerscript += " if(typeof(main) != 'function') throw new Error('Your jscad file should contain a function main() which returns a CSG solid.');\n";
|
|
// workerscript += " var csg; try {csg = main("+JSON.stringify(mainParameters)+"); self.postMessage({cmd: 'rendered', csg: csg});}";
|
|
// workerscript += " catch(e) {var errtxt = e.stack; self.postMessage({cmd: 'error', err: errtxt});}";
|
|
workerscript += "}},false);\n";
|
|
|
|
var blobURL = OpenJsCad.textToBlobUrl(workerscript);
|
|
|
|
if(!window.Worker) throw new Error("Your browser doesn't support Web Workers. Please try the Chrome browser instead.");
|
|
var worker = new Worker(blobURL);
|
|
worker.onmessage = function(e) {
|
|
if(e.data)
|
|
{
|
|
if(e.data.cmd == 'rendered')
|
|
{
|
|
var csg = CSG.fromObject(e.data.csg);
|
|
callback(null, csg);
|
|
}
|
|
else if(e.data.cmd == "error")
|
|
{
|
|
// var errtxt = "Error in line "+e.data.err.lineno+": "+e.data.err.message;
|
|
callback(e.data.err, null);
|
|
}
|
|
}
|
|
};
|
|
worker.onerror = function(e) {
|
|
var errtxt = "Error in line "+e.lineno+": "+e.message;
|
|
callback(errtxt, null);
|
|
};
|
|
worker.postMessage({
|
|
cmd: "render"
|
|
}); // Start the worker.
|
|
return worker;
|
|
};
|
|
|
|
OpenJsCad.textToBlobUrl = function(txt) {
|
|
var bb;
|
|
if(window.BlobBuilder) bb = new window.BlobBuilder()
|
|
else if(window.WebKitBlobBuilder) bb = new window.WebKitBlobBuilder()
|
|
else if(window.MozBlobBuilder) bb = new window.MozBlobBuilder()
|
|
else throw new Error("Your browser doesn't support BlobBuilder");
|
|
|
|
bb.append(txt);
|
|
var blob = bb.getBlob();
|
|
var blobURL;
|
|
if(window.URL) blobURL = window.URL.createObjectURL(blob)
|
|
else if(window.webkitURL) blobURL = window.webkitURL.createObjectURL(blob)
|
|
else throw new Error("Your browser doesn't support window.URL");
|
|
return blobURL;
|
|
};
|
|
|
|
OpenJsCad.revokeBlobUrl = function(url) {
|
|
if(window.URL) window.URL.revokeObjectURL(url)
|
|
else if(window.webkitURL) window.webkitURL.revokeObjectURL(url)
|
|
else throw new Error("Your browser doesn't support window.URL");
|
|
};
|
|
|
|
OpenJsCad.FileSystemApiErrorHandler = function(fileError, operation) {
|
|
var errormap = {
|
|
1: 'NOT_FOUND_ERR',
|
|
2: 'SECURITY_ERR',
|
|
3: 'ABORT_ERR',
|
|
4: 'NOT_READABLE_ERR',
|
|
5: 'ENCODING_ERR',
|
|
6: 'NO_MODIFICATION_ALLOWED_ERR',
|
|
7: 'INVALID_STATE_ERR',
|
|
8: 'SYNTAX_ERR',
|
|
9: 'INVALID_MODIFICATION_ERR',
|
|
10: 'QUOTA_EXCEEDED_ERR',
|
|
11: 'TYPE_MISMATCH_ERR',
|
|
12: 'PATH_EXISTS_ERR',
|
|
};
|
|
var errname;
|
|
if(fileError.code in errormap)
|
|
{
|
|
errname = errormap[fileError.code];
|
|
}
|
|
else
|
|
{
|
|
errname = "Error #"+fileError.code;
|
|
}
|
|
var errtxt = "FileSystem API error: "+operation+" returned error "+errname;
|
|
throw new Error(errtxt);
|
|
};
|
|
|
|
OpenJsCad.AlertUserOfUncaughtExceptions = function() {
|
|
window.onerror = function(message, url, line) {
|
|
message = message.replace(/^Uncaught /i, "");
|
|
alert(message+"\n\n("+url+" line "+line+")");
|
|
};
|
|
};
|
|
|
|
// parse the jscad script to get the parameter definitions
|
|
OpenJsCad.getParamDefinitions = function(script) {
|
|
var scriptisvalid = true;
|
|
try
|
|
{
|
|
// first try to execute the script itself
|
|
// this will catch any syntax errors
|
|
var f = new Function(script);
|
|
f();
|
|
}
|
|
catch(e) {
|
|
scriptisvalid = false;
|
|
}
|
|
var params = [];
|
|
if(scriptisvalid)
|
|
{
|
|
var script1 = "if(typeof(getParameterDefinitions) == 'function') {return getParameterDefinitions();} else {return [];} ";
|
|
script1 += script;
|
|
var f = new Function(script1);
|
|
params = f();
|
|
if( (typeof(params) != "object") || (typeof(params.length) != "number") )
|
|
{
|
|
throw new Error("The getParameterDefinitions() function should return an array with the parameter definitions");
|
|
}
|
|
}
|
|
return params;
|
|
};
|
|
|
|
OpenJsCad.Processor = function(containerdiv, onchange) {
|
|
this.containerdiv = containerdiv;
|
|
this.onchange = onchange;
|
|
this.viewerdiv = null;
|
|
this.viewer = null;
|
|
this.viewerwidth = 800;
|
|
this.viewerheight = 600;
|
|
this.initialViewerDistance = 50;
|
|
this.processing = false;
|
|
this.solid = null;
|
|
this.validcsg = false;
|
|
this.hasstl = false;
|
|
this.worker = null;
|
|
this.paramDefinitions = [];
|
|
this.paramControls = [];
|
|
this.script = null;
|
|
this.hasError = false;
|
|
this.debugging = false;
|
|
this.createElements();
|
|
};
|
|
|
|
OpenJsCad.Processor.prototype = {
|
|
createElements: function() {
|
|
while(this.containerdiv.children.length > 0)
|
|
{
|
|
this.containerdiv.removeChild(0);
|
|
}
|
|
if(!OpenJsCad.isChrome() )
|
|
{
|
|
var div = document.createElement("div");
|
|
div.innerHTML = "Please note: OpenJsCad currently only runs reliably on Google Chrome!";
|
|
this.containerdiv.appendChild(div);
|
|
}
|
|
var viewerdiv = document.createElement("div");
|
|
viewerdiv.className = "viewer";
|
|
viewerdiv.style.width = this.viewerwidth + "px";
|
|
viewerdiv.style.height = this.viewerheight + "px";
|
|
viewerdiv.style.backgroundColor = "rgb(200,200,200)";
|
|
this.containerdiv.appendChild(viewerdiv);
|
|
this.viewerdiv = viewerdiv;
|
|
try
|
|
{
|
|
this.viewer = new OpenJsCad.Viewer(this.viewerdiv, this.viewerwidth, this.viewerheight, this.initialViewerDistance);
|
|
} catch (e) {
|
|
this.viewerdiv.innerHTML = e.toString();
|
|
}
|
|
this.errordiv = document.createElement("div");
|
|
this.errorpre = document.createElement("pre");
|
|
this.errordiv.appendChild(this.errorpre);
|
|
this.statusdiv = document.createElement("div");
|
|
this.statusdiv.className = "statusdiv";
|
|
//this.statusdiv.style.width = this.viewerwidth + "px";
|
|
this.statusspan = document.createElement("span");
|
|
this.statusbuttons = document.createElement("div");
|
|
this.statusbuttons.style.float = "right";
|
|
this.statusdiv.appendChild(this.statusspan);
|
|
this.statusdiv.appendChild(this.statusbuttons);
|
|
this.abortbutton = document.createElement("button");
|
|
this.abortbutton.innerHTML = "Abort";
|
|
var that = this;
|
|
this.abortbutton.onclick = function(e) {
|
|
that.abort();
|
|
};
|
|
this.statusbuttons.appendChild(this.abortbutton);
|
|
this.generateStlButton = document.createElement("button");
|
|
this.generateStlButton.innerHTML = "Generate STL";
|
|
this.generateStlButton.onclick = function(e) {
|
|
that.generateStl();
|
|
};
|
|
this.statusbuttons.appendChild(this.generateStlButton);
|
|
this.downloadStlLink = document.createElement("a");
|
|
this.downloadStlLink.innerHTML = "Download STL";
|
|
this.statusbuttons.appendChild(this.downloadStlLink);
|
|
this.parametersdiv = document.createElement("div");
|
|
this.parametersdiv.className = "parametersdiv";
|
|
var headerdiv = document.createElement("div");
|
|
headerdiv.innerText = "Parameters:";
|
|
headerdiv.className = "header";
|
|
this.parametersdiv.appendChild(headerdiv);
|
|
this.parameterstable = document.createElement("table");
|
|
this.parameterstable.className = "parameterstable";
|
|
this.parametersdiv.appendChild(this.parameterstable);
|
|
var parseParametersButton = document.createElement("button");
|
|
parseParametersButton.innerHTML = "Update";
|
|
parseParametersButton.onclick = function(e) {
|
|
that.rebuildSolid();
|
|
};
|
|
this.parametersdiv.appendChild(parseParametersButton);
|
|
this.enableItems();
|
|
this.containerdiv.appendChild(this.statusdiv);
|
|
this.containerdiv.appendChild(this.errordiv);
|
|
this.containerdiv.appendChild(this.parametersdiv);
|
|
this.clearViewer();
|
|
},
|
|
|
|
clearViewer: function() {
|
|
this.clearStl();
|
|
this.solid = new CSG();
|
|
if(this.viewer)
|
|
{
|
|
this.viewer.setCsg(this.solid);
|
|
}
|
|
this.validcsg = false;
|
|
this.enableItems();
|
|
},
|
|
|
|
abort: function() {
|
|
if(this.processing)
|
|
{
|
|
//todo: abort
|
|
this.processing=false;
|
|
this.statusspan.innerHTML = "Aborted.";
|
|
this.worker.terminate();
|
|
this.enableItems();
|
|
if(this.onchange) this.onchange();
|
|
}
|
|
},
|
|
|
|
enableItems: function() {
|
|
this.abortbutton.style.display = this.processing? "inline":"none";
|
|
this.generateStlButton.style.display = ((!this.hasstl)&&(this.validcsg))? "inline":"none";
|
|
this.downloadStlLink.style.display = this.hasstl? "inline":"none";
|
|
this.parametersdiv.style.display = (this.paramControls.length > 0)? "block":"none";
|
|
this.errordiv.style.display = this.hasError? "block":"none";
|
|
this.statusdiv.style.display = this.hasError? "none":"block";
|
|
},
|
|
|
|
setError: function(txt) {
|
|
this.hasError = (txt != "");
|
|
this.errorpre.innerText = txt;
|
|
this.enableItems();
|
|
},
|
|
|
|
setDebugging: function(debugging) {
|
|
this.debugging = debugging;
|
|
},
|
|
|
|
// script: javascript code
|
|
// filename: optional, the name of the .jscad file
|
|
setJsCad: function(script, filename) {
|
|
if(!filename) filename = "openjscad.jscad";
|
|
filename = filename.replace(/\.jscad$/i, "");
|
|
this.abort();
|
|
this.clearViewer();
|
|
this.paramDefinitions = [];
|
|
this.paramControls = [];
|
|
this.script = null;
|
|
this.setError("");
|
|
var scripthaserrors = false;
|
|
try
|
|
{
|
|
this.paramDefinitions = OpenJsCad.getParamDefinitions(script);
|
|
this.createParamControls();
|
|
}
|
|
catch(e)
|
|
{
|
|
this.setError(e.toString());
|
|
this.statusspan.innerHTML = "Error.";
|
|
scripthaserrors = true;
|
|
}
|
|
if(!scripthaserrors)
|
|
{
|
|
this.script = script;
|
|
this.filename = filename;
|
|
this.rebuildSolid();
|
|
}
|
|
else
|
|
{
|
|
this.enableItems();
|
|
if(this.onchange) this.onchange();
|
|
}
|
|
},
|
|
|
|
getParamValues: function()
|
|
{
|
|
var paramValues = {};
|
|
for(var i = 0; i < this.paramDefinitions.length; i++)
|
|
{
|
|
var paramdef = this.paramDefinitions[i];
|
|
var type = "text";
|
|
if('type' in paramdef)
|
|
{
|
|
type = paramdef.type;
|
|
}
|
|
var control = this.paramControls[i];
|
|
var value;
|
|
if( (type == "text") || (type == "float") || (type == "int") )
|
|
{
|
|
value = control.value;
|
|
if( (type == "float") || (type == "int") )
|
|
{
|
|
var isnumber = !isNaN(parseFloat(value)) && isFinite(value);
|
|
if(!isnumber)
|
|
{
|
|
throw new Error("Not a number: "+value);
|
|
}
|
|
if(type == "int")
|
|
{
|
|
value = parseInt(value);
|
|
}
|
|
else
|
|
{
|
|
value = parseFloat(value);
|
|
}
|
|
}
|
|
}
|
|
else if(type == "choice")
|
|
{
|
|
value = control.options[control.selectedIndex].value;
|
|
}
|
|
paramValues[paramdef.name] = value;
|
|
}
|
|
return paramValues;
|
|
},
|
|
|
|
rebuildSolid: function()
|
|
{
|
|
this.abort();
|
|
this.setError("");
|
|
this.clearViewer();
|
|
this.processing = true;
|
|
this.statusspan.innerHTML = "Processing, please wait...";
|
|
this.enableItems();
|
|
var that = this;
|
|
var paramValues = this.getParamValues();
|
|
if(this.debugging)
|
|
{
|
|
try
|
|
{
|
|
var csg = OpenJsCad.javaScriptToSolidSync(this.script, paramValues);
|
|
that.processing = false;
|
|
that.solid = csg;
|
|
if(that.viewer) that.viewer.setCsg(csg);
|
|
that.validcsg = true;
|
|
that.statusspan.innerHTML = "Ready.";
|
|
}
|
|
catch(e)
|
|
{
|
|
that.processing = false;
|
|
that.setError(e.toString());
|
|
that.statusspan.innerHTML = "Error.";
|
|
}
|
|
that.enableItems();
|
|
if(that.onchange) that.onchange();
|
|
}
|
|
else
|
|
{
|
|
this.worker = OpenJsCad.javaScriptToSolidASync(this.script, paramValues, function(err, csg) {
|
|
that.processing = false;
|
|
that.worker = null;
|
|
if(err)
|
|
{
|
|
that.setError(err);
|
|
that.statusspan.innerHTML = "Error.";
|
|
}
|
|
else
|
|
{
|
|
that.solid = csg;
|
|
if(that.viewer) that.viewer.setCsg(csg);
|
|
that.validcsg = true;
|
|
that.statusspan.innerHTML = "Ready.";
|
|
}
|
|
that.enableItems();
|
|
if(that.onchange) that.onchange();
|
|
});
|
|
}
|
|
},
|
|
|
|
hasSolid: function() {
|
|
return this.validcsg;
|
|
},
|
|
|
|
isProcessing: function() {
|
|
return this.processing;
|
|
},
|
|
|
|
/*
|
|
clearStl1: function() {
|
|
if(this.hasstl)
|
|
{
|
|
this.hasstl = false;
|
|
OpenJsCad.revokeBlobUrl(this.stlBlobUrl);
|
|
this.stlBlobUrl = null;
|
|
this.enableItems();
|
|
if(this.onchange) this.onchange();
|
|
}
|
|
},
|
|
|
|
generateStl1: function() {
|
|
this.clearStl();
|
|
if(this.validcsg)
|
|
{
|
|
var stltxt = this.solid.toStlString();
|
|
this.stlBlobUrl = OpenJsCad.textToBlobUrl(stltxt);
|
|
this.hasstl = true;
|
|
this.downloadStlLink.href = this.stlBlobUrl;
|
|
this.enableItems();
|
|
if(this.onchange) this.onchange();
|
|
}
|
|
},
|
|
*/
|
|
|
|
clearStl: function() {
|
|
if(this.hasstl)
|
|
{
|
|
this.hasstl = false;
|
|
if(this.stlDirEntry)
|
|
{
|
|
this.stlDirEntry.removeRecursively(function(){});
|
|
this.stlDirEntry=null;
|
|
}
|
|
this.enableItems();
|
|
if(this.onchange) this.onchange();
|
|
}
|
|
},
|
|
|
|
generateStl: function() {
|
|
this.clearStl();
|
|
if(this.validcsg)
|
|
{
|
|
var stltxt = this.solid.toStlString();
|
|
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
|
|
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
|
|
if(!window.requestFileSystem)
|
|
{
|
|
throw new Error("Your browser does not support the HTML5 FileSystem API. Please try the Chrome browser instead.");
|
|
}
|
|
if(!window.BlobBuilder)
|
|
{
|
|
throw new Error("Your browser does not support the HTML5 BlobBuilder API. Please try the Chrome browser instead.");
|
|
}
|
|
// create a random directory name:
|
|
var dirname = "OpenJsCadStlOutput1_"+parseInt(Math.random()*1000000000, 10)+".stl";
|
|
var filename = this.filename+".stl";
|
|
var that = this;
|
|
window.requestFileSystem(TEMPORARY, 20*1024*1024, function(fs){
|
|
fs.root.getDirectory(dirname, {create: true, exclusive: true}, function(dirEntry) {
|
|
that.stlDirEntry = dirEntry;
|
|
dirEntry.getFile(filename, {create: true, exclusive: true}, function(fileEntry) {
|
|
fileEntry.createWriter(function(fileWriter) {
|
|
fileWriter.onwriteend = function(e) {
|
|
that.hasstl = true;
|
|
that.downloadStlLink.href = fileEntry.toURL();
|
|
that.enableItems();
|
|
if(that.onchange) that.onchange();
|
|
};
|
|
fileWriter.onerror = function(e) {
|
|
throw new Error('Write failed: ' + e.toString());
|
|
};
|
|
// Create a new Blob and write it to log.txt.
|
|
var bb = new window.BlobBuilder(); // Note: window.WebKitBlobBuilder in Chrome 12.
|
|
bb.append(stltxt);
|
|
fileWriter.write(bb.getBlob());
|
|
},
|
|
function(fileerror){OpenJsCad.FileSystemApiErrorHandler(fileerror, "createWriter");}
|
|
);
|
|
},
|
|
function(fileerror){OpenJsCad.FileSystemApiErrorHandler(fileerror, "getFile('"+filename+"')");}
|
|
);
|
|
},
|
|
function(fileerror){OpenJsCad.FileSystemApiErrorHandler(fileerror, "getDirectory('"+dirname+"')");}
|
|
);
|
|
},
|
|
function(fileerror){OpenJsCad.FileSystemApiErrorHandler(fileerror, "requestFileSystem");}
|
|
);
|
|
}
|
|
},
|
|
|
|
createParamControls: function() {
|
|
this.parameterstable.innerHTML = "";
|
|
this.paramControls = [];
|
|
var paramControls = [];
|
|
var tablerows = [];
|
|
for(var i = 0; i < this.paramDefinitions.length; i++)
|
|
{
|
|
var errorprefix = "Error in parameter definition #"+(i+1)+": ";
|
|
var paramdef = this.paramDefinitions[i];
|
|
if(!('name' in paramdef))
|
|
{
|
|
throw new Error(errorprefix + "Should include a 'name' parameter");
|
|
}
|
|
var type = "text";
|
|
if('type' in paramdef)
|
|
{
|
|
type = paramdef.type;
|
|
}
|
|
if( (type !== "text") && (type !== "int") && (type !== "float") && (type !== "choice") )
|
|
{
|
|
throw new Error(errorprefix + "Unknown parameter type '"+type+"'");
|
|
}
|
|
var control;
|
|
if( (type == "text") || (type == "int") || (type == "float") )
|
|
{
|
|
control = document.createElement("input");
|
|
control.type = "text";
|
|
if('default' in paramdef)
|
|
{
|
|
control.value = paramdef.default;
|
|
}
|
|
else
|
|
{
|
|
if( (type == "int") || (type == "float") )
|
|
{
|
|
control.value = "0";
|
|
}
|
|
else
|
|
{
|
|
control.value = "";
|
|
}
|
|
}
|
|
}
|
|
else if(type == "choice")
|
|
{
|
|
if(!('values' in paramdef))
|
|
{
|
|
throw new Error(errorprefix + "Should include a 'values' parameter");
|
|
}
|
|
control = document.createElement("select");
|
|
var values = paramdef.values;
|
|
var captions;
|
|
if('captions' in paramdef)
|
|
{
|
|
captions = paramdef.captions;
|
|
if(captions.length != values.length)
|
|
{
|
|
throw new Error(errorprefix + "'captions' and 'values' should have the same number of items");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
captions = values;
|
|
}
|
|
var selectedindex = 0;
|
|
for(var valueindex = 0; valueindex < values.length; valueindex++)
|
|
{
|
|
var option = document.createElement("option");
|
|
option.value = values[valueindex];
|
|
option.text = captions[valueindex];
|
|
control.add(option);
|
|
if('default' in paramdef)
|
|
{
|
|
if(paramdef.default == values[valueindex])
|
|
{
|
|
selectedindex = valueindex;
|
|
}
|
|
}
|
|
}
|
|
if(values.length > 0)
|
|
{
|
|
control.selectedIndex = selectedindex;
|
|
}
|
|
}
|
|
paramControls.push(control);
|
|
var tr = document.createElement("tr");
|
|
var td = document.createElement("td");
|
|
var label = paramdef.name + ":";
|
|
if('caption' in paramdef)
|
|
{
|
|
label = paramdef.caption;
|
|
}
|
|
|
|
td.innerHTML = label;
|
|
tr.appendChild(td);
|
|
td = document.createElement("td");
|
|
td.appendChild(control);
|
|
tr.appendChild(td);
|
|
tablerows.push(tr);
|
|
}
|
|
var that = this;
|
|
tablerows.map(function(tr){
|
|
that.parameterstable.appendChild(tr);
|
|
});
|
|
this.paramControls = paramControls;
|
|
},
|
|
|
|
|
|
}; |