User editable parameters; debugging support; gears demo
This commit is contained in:
parent
1b89514039
commit
3544b92a1a
13
csg.js
13
csg.js
|
@ -144,6 +144,15 @@ CSG.prototype = {
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Like union, but when we know that the two solids are not intersecting
|
||||||
|
// Do not use if you are not completely sure that the solids do not intersect!
|
||||||
|
unionForNonIntersecting: function(csg) {
|
||||||
|
var newpolygons = this.polygons.concat(csg.polygons);
|
||||||
|
var result = CSG.fromPolygons(newpolygons);
|
||||||
|
result.properties = this.properties._merge(csg.properties);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
// Return a new CSG solid representing space in this solid but not in the
|
// Return a new CSG solid representing space in this solid but not in the
|
||||||
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
||||||
//
|
//
|
||||||
|
@ -2258,6 +2267,10 @@ CSG.Vector2D = function(x, y) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CSG.Vector2D.fromAngle = function(radians) {
|
||||||
|
return new CSG.Vector2D(Math.cos(radians), Math.sin(radians));
|
||||||
|
};
|
||||||
|
|
||||||
CSG.Vector2D.prototype = {
|
CSG.Vector2D.prototype = {
|
||||||
// extend to a 3D vector by adding a z coordinate:
|
// extend to a 3D vector by adding a z coordinate:
|
||||||
toVector3D: function(z) {
|
toVector3D: function(z) {
|
||||||
|
|
187
gearsdemo.html
Normal file
187
gearsdemo.html
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html><head>
|
||||||
|
<script src="lightgl.js"></script>
|
||||||
|
<script src="csg.js"></script>
|
||||||
|
<script src="openjscad.js"></script>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 14px/20px 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
max-width: 820px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, code, textarea {
|
||||||
|
font: 12px/20px Monaco, monospace;
|
||||||
|
border: 1px solid #CCC;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #F9F9F9;
|
||||||
|
padding: 0 3px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
pre, textarea {
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas { cursor: move; }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="openjscad.css" type="text/css">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var gProcessor=null;
|
||||||
|
|
||||||
|
// Show all exceptions to the user:
|
||||||
|
OpenJsCad.AlertUserOfUncaughtExceptions();
|
||||||
|
|
||||||
|
function onload()
|
||||||
|
{
|
||||||
|
gProcessor = new OpenJsCad.Processor(document.getElementById("viewer"));
|
||||||
|
updateSolid();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSolid()
|
||||||
|
{
|
||||||
|
gProcessor.setJsCad(document.getElementById('code').value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<title>OpenJsCad demo: involute gears</title>
|
||||||
|
</head>
|
||||||
|
<body onload="onload()">
|
||||||
|
<h1>OpenJsCad demo: involute gears</h1>
|
||||||
|
<div id="viewer"></div>
|
||||||
|
<h2>Source code</h2>
|
||||||
|
Below is the OpenJsCad script for this demo. To build your own models, create a .jscad script
|
||||||
|
and use the <a href="processfile.html"><b>OpenJsCad parser</b></a>. For more information see the
|
||||||
|
<a href="index.html">OpenJsCad documentation</a>.
|
||||||
|
<br><br>
|
||||||
|
<textarea id="code">
|
||||||
|
// Here we define the user editable parameters:
|
||||||
|
function getParameterDefinitions() {
|
||||||
|
return [
|
||||||
|
{ name: 'numTeeth', caption: 'Number of teeth:', type: 'int', default: 10 },
|
||||||
|
{ name: 'circularPitch', caption: 'Circular pitch:', type: 'float', default: 5 },
|
||||||
|
{ name: 'pressureAngle', caption: 'Pressure angle:', type: 'float', default: 20 },
|
||||||
|
{ name: 'clearance', caption: 'Clearance:', type: 'float', default: 0 },
|
||||||
|
{ name: 'thickness', caption: 'Thickness:', type: 'float', default: 5 },
|
||||||
|
{ name: 'centerholeradius', caption: 'Radius of center hole (0 for no hole):', type: 'float', default: 2 },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entry point; here we construct our solid:
|
||||||
|
function main(params)
|
||||||
|
{
|
||||||
|
var gear = involuteGear(
|
||||||
|
params.numTeeth,
|
||||||
|
params.circularPitch,
|
||||||
|
params.pressureAngle,
|
||||||
|
params.clearance,
|
||||||
|
params.thickness
|
||||||
|
);
|
||||||
|
if(params.centerholeradius > 0)
|
||||||
|
{
|
||||||
|
var centerhole = CSG.cylinder({start: [0,0,-params.thickness], end: [0,0,params.thickness], radius: params.centerholeradius, resolution: 16});
|
||||||
|
gear = gear.subtract(centerhole);
|
||||||
|
}
|
||||||
|
return gear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
For gear terminology see:
|
||||||
|
http://www.astronomiainumbria.org/advanced_internet_files/meccanica/easyweb.easynet.co.uk/_chrish/geardata.htm
|
||||||
|
Algorithm based on:
|
||||||
|
http://www.cartertools.com/involute.html
|
||||||
|
|
||||||
|
circularPitch: The distance between adjacent teeth measured at the pitch circle
|
||||||
|
*/
|
||||||
|
function involuteGear(numTeeth, circularPitch, pressureAngle, clearance, thickness)
|
||||||
|
{
|
||||||
|
// default values:
|
||||||
|
if(arguments.length < 3) pressureAngle = 20;
|
||||||
|
if(arguments.length < 4) clearance = 0;
|
||||||
|
if(arguments.length < 4) thickness = 1;
|
||||||
|
|
||||||
|
var addendum = circularPitch / Math.PI;
|
||||||
|
var dedendum = addendum + clearance;
|
||||||
|
|
||||||
|
// radiuses of the 4 circles:
|
||||||
|
var pitchRadius = numTeeth * circularPitch / (2 * Math.PI);
|
||||||
|
var baseRadius = pitchRadius * Math.cos(Math.PI * pressureAngle / 180);
|
||||||
|
var outerRadius = pitchRadius + addendum;
|
||||||
|
var rootRadius = pitchRadius - dedendum;
|
||||||
|
|
||||||
|
var maxtanlength = Math.sqrt(outerRadius*outerRadius - baseRadius*baseRadius);
|
||||||
|
var maxangle = maxtanlength / baseRadius;
|
||||||
|
|
||||||
|
var tl_at_pitchcircle = Math.sqrt(pitchRadius*pitchRadius - baseRadius*baseRadius);
|
||||||
|
var angle_at_pitchcircle = tl_at_pitchcircle / baseRadius;
|
||||||
|
var diffangle = angle_at_pitchcircle - Math.atan(angle_at_pitchcircle);
|
||||||
|
var angularToothWidthAtBase = Math.PI / numTeeth + 2*diffangle;
|
||||||
|
|
||||||
|
// build a single 2d tooth in the 'points' array:
|
||||||
|
var resolution = 5;
|
||||||
|
var points = [new CSG.Vector2D(0,0)];
|
||||||
|
for(var i = 0; i <= resolution; i++)
|
||||||
|
{
|
||||||
|
// first side of the tooth:
|
||||||
|
var angle = maxangle * i / resolution;
|
||||||
|
var tanlength = angle * baseRadius;
|
||||||
|
var radvector = CSG.Vector2D.fromAngle(angle);
|
||||||
|
var tanvector = radvector.normal();
|
||||||
|
var p = radvector.times(baseRadius).plus(tanvector.times(tanlength));
|
||||||
|
points[i+1] = p;
|
||||||
|
|
||||||
|
// opposite side of the tooth:
|
||||||
|
radvector = CSG.Vector2D.fromAngle(angularToothWidthAtBase - angle);
|
||||||
|
tanvector = radvector.normal().negated();
|
||||||
|
p = radvector.times(baseRadius).plus(tanvector.times(tanlength));
|
||||||
|
points[2 * resolution + 2 - i] = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the polygon and extrude into 3D:
|
||||||
|
var tooth3d = new CSG.Polygon2D(points).extrude({offset: [0, 0, thickness]});
|
||||||
|
|
||||||
|
var allteeth = new CSG();
|
||||||
|
for(var i = 0; i < numTeeth; i++)
|
||||||
|
{
|
||||||
|
var angle = i*360/numTeeth;
|
||||||
|
var rotatedtooth = tooth3d.rotateZ(angle);
|
||||||
|
allteeth = allteeth.unionForNonIntersecting(rotatedtooth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the root circle:
|
||||||
|
points = [];
|
||||||
|
var toothAngle = 2 * Math.PI / numTeeth;
|
||||||
|
var toothCenterAngle = 0.5 * angularToothWidthAtBase;
|
||||||
|
for(var i = 0; i < numTeeth; i++)
|
||||||
|
{
|
||||||
|
var angle = toothCenterAngle + i * toothAngle;
|
||||||
|
var p = CSG.Vector2D.fromAngle(angle).times(rootRadius);
|
||||||
|
points.push(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the polygon and extrude into 3D:
|
||||||
|
var rootcircle = new CSG.Polygon2D(points).extrude({offset: [0, 0, thickness]});
|
||||||
|
|
||||||
|
var result = rootcircle.union(allteeth);
|
||||||
|
|
||||||
|
// center at origin:
|
||||||
|
result = result.translate([0, 0, -thickness/2]);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
</textarea><br>
|
||||||
|
<input type="submit" value="Update" onclick="updateSolid(); return false;">
|
||||||
|
<br><br>
|
||||||
|
</body>
|
||||||
|
</html>
|
117
index.html
117
index.html
|
@ -35,6 +35,8 @@ textarea:focus {
|
||||||
canvas { cursor: move; }
|
canvas { cursor: move; }
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
<link rel="stylesheet" href="openjscad.css" type="text/css">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
var gProcessor=null;
|
var gProcessor=null;
|
||||||
|
@ -67,7 +69,7 @@ and use the <a href="processfile.html"><b>OpenJsCad parser</b></a>.
|
||||||
<textarea id="code">
|
<textarea id="code">
|
||||||
function main()
|
function main()
|
||||||
{
|
{
|
||||||
var resolution = 16; // increase to get smoother corners (will get slow!)
|
var resolution = 24; // increase to get smoother corners (will get slow!)
|
||||||
|
|
||||||
var cube1 = CSG.roundedCube({center: [0,0,0], radius: [10,10,10], roundradius: 2, resolution: resolution});
|
var cube1 = CSG.roundedCube({center: [0,0,0], radius: [10,10,10], roundradius: 2, resolution: resolution});
|
||||||
var sphere1 = CSG.sphere({center: [5, 5, 5], radius: 10, resolution: resolution });
|
var sphere1 = CSG.sphere({center: [5, 5, 5], radius: 10, resolution: resolution });
|
||||||
|
@ -83,7 +85,7 @@ function main()
|
||||||
</textarea><br>
|
</textarea><br>
|
||||||
<input type="submit" value="Update" onclick="updateSolid(); return false;">
|
<input type="submit" value="Update" onclick="updateSolid(); return false;">
|
||||||
<br>
|
<br>
|
||||||
<h1>About</h1>
|
<h2>About</h2>
|
||||||
This is intended to become a Javascript based alternative to <a href="http://www.openscad.org/">OpenSCAD</a>,
|
This is intended to become a Javascript based alternative to <a href="http://www.openscad.org/">OpenSCAD</a>,
|
||||||
for 3D solid modeling.
|
for 3D solid modeling.
|
||||||
CSG model is contructed using Javascript. For example:<br>
|
CSG model is contructed using Javascript. For example:<br>
|
||||||
|
@ -97,6 +99,23 @@ The code should always contain a main() function, returning a CSG solid.
|
||||||
To build your own models, create a .jscad file with your javascript code and parse the file using the
|
To build your own models, create a .jscad file with your javascript code and parse the file using the
|
||||||
<a href="processfile.html">OpenJsCad parser</a>. When finished, click on Generate STL and save the result
|
<a href="processfile.html">OpenJsCad parser</a>. When finished, click on Generate STL and save the result
|
||||||
in an .stl file, ready to be printed on your 3d printer.
|
in an .stl file, ready to be printed on your 3d printer.
|
||||||
|
<h2>Why use OpenJsCad?</h2>
|
||||||
|
Some of the benefits:
|
||||||
|
<ul>
|
||||||
|
<li>Runs in your browser, no need to install any software.</li>
|
||||||
|
<li>You can create parametric models with user editable parameters: parameters can be changed in the browser window,
|
||||||
|
without the need to edit the source script. See the <a href="gearsdemo.html">Gears demo</a> for example!</li>
|
||||||
|
<li>JavaScript is an extremely flexible language, supporting dynamic arrays and object oriented programming.</li>
|
||||||
|
<li>Solids are stored in variables. This allows for example conditional cloning of objects, something which is nearly impossible in OpenSCAD.</li>
|
||||||
|
<li>Properties and Connectors (see below) make it very easy to attach objects to each other at predetermined
|
||||||
|
points, even if you don't know the actual orientation or size.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Viewer navigation</h2>
|
||||||
|
Click and drag to rotate the model around the origin.<br>
|
||||||
|
Shift+Drag moves the model around.<br>
|
||||||
|
Alt+drag zooms (by changing the distance between camera and model).
|
||||||
|
<h2>Demos</h2>
|
||||||
|
Try the <a href="gearsdemo.html">Gears demo</a>!
|
||||||
<h2>License</h2>
|
<h2>License</h2>
|
||||||
Copyright (c) 2012 Joost Nieuwenhuijse.
|
Copyright (c) 2012 Joost Nieuwenhuijse.
|
||||||
Uses CSG.js, <a href="https://github.com/evanw/csg.js">original</a> copyright (c) 2011 Evan Wallace,
|
Uses CSG.js, <a href="https://github.com/evanw/csg.js">original</a> copyright (c) 2011 Evan Wallace,
|
||||||
|
@ -109,11 +128,6 @@ know how to modify it as well.<br><br>
|
||||||
To contribute go to <a href="https://github.com/joostn/csg.js">CSG.js at GitHub</a>,
|
To contribute go to <a href="https://github.com/joostn/csg.js">CSG.js at GitHub</a>,
|
||||||
<a href="http://help.github.com/fork-a-repo/">create your own fork</a> and
|
<a href="http://help.github.com/fork-a-repo/">create your own fork</a> and
|
||||||
<a href="http://help.github.com/send-pull-requests/">send me a pull request</a>.
|
<a href="http://help.github.com/send-pull-requests/">send me a pull request</a>.
|
||||||
<h2>Viewer navigation</h2>
|
|
||||||
Click and drag to rotate the model around the origin.<br>
|
|
||||||
Shift+Drag moves the model around.<br>
|
|
||||||
Alt+drag zooms (by changing the distance between camera and model).
|
|
||||||
|
|
||||||
<h2>Primitive solids</h2>
|
<h2>Primitive solids</h2>
|
||||||
Currently the following solids are supported. The parameters are passed in an object; most
|
Currently the following solids are supported. The parameters are passed in an object; most
|
||||||
parameters are optional. 3D vectors can be passed in an array. If a scalar is passed
|
parameters are optional. 3D vectors can be passed in an array. If a scalar is passed
|
||||||
|
@ -422,5 +436,94 @@ var extruded=shape2d.extrude({
|
||||||
});
|
});
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<h2>Interactive parametric models</h2>
|
||||||
|
It is possible to make certain parameters
|
||||||
|
editable in the browser. This allows users not familiar with JavaScript to create customized STL files.
|
||||||
|
<br><br>
|
||||||
|
To do so, add a function getParameterDefinitions() to your .jscad source. This function should return
|
||||||
|
an array with parameter definitions. Currently 4 parameters types are supported: float, int, text and choice.
|
||||||
|
The user edited values of the parameters will be supplied as an object parameter to the main() function of your .jscad file.
|
||||||
|
<br><br>
|
||||||
|
A float, int or text parameter is created by including the following object in the array returned by getParameterDefinitions():
|
||||||
|
<pre>{
|
||||||
|
name: 'width',
|
||||||
|
type: 'float', // or 'text' or 'int'
|
||||||
|
default: 1.23, // optional, sets the initial value
|
||||||
|
caption: 'Width of the thingy:', // optional, displayed left of the input field
|
||||||
|
// if omitted, the 'name' is displayed (i.e. 'width')
|
||||||
|
}</pre>
|
||||||
|
A 'choice' parameter is created using the following object:
|
||||||
|
<pre>{
|
||||||
|
name: 'shape',
|
||||||
|
type: 'choice',
|
||||||
|
values: ["TRI", "SQU", "CIR"], // these are the values that will be supplied to your script
|
||||||
|
captions: ["Triangle", "Square", "Circle"], // optional, these values are shown in the listbox
|
||||||
|
// if omitted, the items in the 'values' array are used
|
||||||
|
caption: 'Shape:', // optional, displayed left of the input field
|
||||||
|
default: "SQU", // optional, default selected value
|
||||||
|
// if omitted, the first item is selected by default
|
||||||
|
}</pre>
|
||||||
|
To use the values add an argument to your main() function. This argument will be supplied an object
|
||||||
|
with the user edited parameter values:
|
||||||
|
<pre>
|
||||||
|
function main(params)
|
||||||
|
{
|
||||||
|
// custom error checking:
|
||||||
|
if(params.width <= 0) throw new Error("Width should be positive!");
|
||||||
|
|
||||||
|
if(params.shape == "TRI")
|
||||||
|
{
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
A complete example:
|
||||||
|
<pre>
|
||||||
|
function getParameterDefinitions() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'width',
|
||||||
|
type: 'float',
|
||||||
|
default: 10,
|
||||||
|
caption: "Width of the cube:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'height',
|
||||||
|
type: 'float',
|
||||||
|
default: 14,
|
||||||
|
caption: "Height of the cube:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'depth',
|
||||||
|
type: 'float',
|
||||||
|
default: 7,
|
||||||
|
caption: "Depth of the cube:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rounded',
|
||||||
|
type: 'choice',
|
||||||
|
caption: 'Round the corners?',
|
||||||
|
values: [0, 1],
|
||||||
|
captions: ["No thanks", "Yes please"],
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(params) {
|
||||||
|
var result;
|
||||||
|
if(params.rounded == 1)
|
||||||
|
{
|
||||||
|
result = CSG.roundedCube({radius: [params.width, params.height, params.depth], roundradius: 2, resolution: 32});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = CSG.cube({radius: [params.width, params.height, params.depth]});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
41
openjscad.css
Normal file
41
openjscad.css
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#filedropzone {
|
||||||
|
border: 2px dashed #bbb;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filedropzone_empty {
|
||||||
|
text-align: center;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filedropzone_filled {
|
||||||
|
color: black;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filebuttons {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas { cursor: move; }
|
||||||
|
|
||||||
|
.parametersdiv {
|
||||||
|
border: 1px solid rgb(200,200,200);
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parametersdiv .header {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusdiv {
|
||||||
|
width: 800px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
448
openjscad.js
448
openjscad.js
|
@ -197,16 +197,6 @@ OpenJsCad.Viewer.csgToMesh = function(csg) {
|
||||||
return mesh;
|
return mesh;
|
||||||
};
|
};
|
||||||
|
|
||||||
// parse javascript into solid:
|
|
||||||
OpenJsCad.javaScriptToSolid = function(script) {
|
|
||||||
var csg = new Function(script)();
|
|
||||||
if( (typeof(csg) != "object") || (!('polygons' in csg)))
|
|
||||||
{
|
|
||||||
throw new Error("Your javascript code should return a CSG object. Try for example: return CSG.cube();");
|
|
||||||
}
|
|
||||||
return csg;
|
|
||||||
};
|
|
||||||
|
|
||||||
// this is a bit of a hack; doesn't properly supports urls that start with '/'
|
// this is a bit of a hack; doesn't properly supports urls that start with '/'
|
||||||
// but does handle relative urls containing ../
|
// but does handle relative urls containing ../
|
||||||
OpenJsCad.makeAbsoluteUrl = function(url, baseurl) {
|
OpenJsCad.makeAbsoluteUrl = function(url, baseurl) {
|
||||||
|
@ -246,10 +236,48 @@ OpenJsCad.makeAbsoluteUrl = function(url, baseurl) {
|
||||||
OpenJsCad.isChrome = function()
|
OpenJsCad.isChrome = function()
|
||||||
{
|
{
|
||||||
return (navigator.userAgent.search("Chrome") >= 0);
|
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)
|
// callback: should be function(error, csg)
|
||||||
OpenJsCad.javaScriptToSolidASync = function(script, callback) {
|
OpenJsCad.javaScriptToSolidASync = function(script, mainParameters, callback) {
|
||||||
var baselibraries = [
|
var baselibraries = [
|
||||||
"csg.js",
|
"csg.js",
|
||||||
"openjscad.js"
|
"openjscad.js"
|
||||||
|
@ -257,28 +285,37 @@ OpenJsCad.javaScriptToSolidASync = function(script, callback) {
|
||||||
var baseurl = document.location + "";
|
var baseurl = document.location + "";
|
||||||
var workerscript = "";
|
var workerscript = "";
|
||||||
workerscript += script;
|
workerscript += script;
|
||||||
workerscript += "\n//// END OF USER SUPPLIED SCRIPT\n";
|
workerscript += "\n\n\n\n//// The following code is added by OpenJsCad:\n";
|
||||||
workerscript += "var _csg_libraries=" + JSON.stringify(baselibraries)+";\n";
|
workerscript += "var _csg_libraries=" + JSON.stringify(baselibraries)+";\n";
|
||||||
workerscript += "var _csg_baseurl=" + JSON.stringify(baseurl)+";\n";
|
workerscript += "var _csg_baseurl=" + JSON.stringify(baseurl)+";\n";
|
||||||
workerscript += "var _csg_makeAbsoluteURL=" + OpenJsCad.makeAbsoluteUrl.toString()+";\n";
|
workerscript += "var _csg_makeAbsoluteURL=" + OpenJsCad.makeAbsoluteUrl.toString()+";\n";
|
||||||
workerscript += "if(typeof(libs) == 'function') _csg_libraries = _csg_libraries.concat(libs());\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 = _csg_libraries.map(function(l){return _csg_makeAbsoluteURL(l,_csg_baseurl);});\n";
|
||||||
//workerscript += "importScripts.call(null, _csg_libraries);\n";
|
|
||||||
workerscript += "_csg_libraries.map(function(l){importScripts(l)});\n";
|
workerscript += "_csg_libraries.map(function(l){importScripts(l)});\n";
|
||||||
workerscript += "self.addEventListener('message', function(e) {if(e.data && e.data.cmd == 'render'){";
|
workerscript += "self.addEventListener('message', function(e) {if(e.data && e.data.cmd == 'render'){";
|
||||||
workerscript += " if(typeof(main) != 'function') throw new Error('Your jscad file should contain a function main() which returns a CSG solid.');\n";
|
workerscript += " OpenJsCad.runMainInWorker("+JSON.stringify(mainParameters)+");";
|
||||||
workerscript += " var csg = main(); self.postMessage({cmd: 'rendered', csg: csg});";
|
// 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";
|
workerscript += "}},false);\n";
|
||||||
|
|
||||||
var blobURL = OpenJsCad.textToBlobUrl(workerscript);
|
var blobURL = OpenJsCad.textToBlobUrl(workerscript);
|
||||||
|
|
||||||
if(!window.Worker) throw new Error("Your browser doesn't support Web Workers");
|
if(!window.Worker) throw new Error("Your browser doesn't support Web Workers. Please try the Chrome browser instead.");
|
||||||
var worker = new Worker(blobURL);
|
var worker = new Worker(blobURL);
|
||||||
worker.onmessage = function(e) {
|
worker.onmessage = function(e) {
|
||||||
if(e.data && e.data.cmd == 'rendered')
|
if(e.data)
|
||||||
{
|
{
|
||||||
var csg = CSG.fromObject(e.data.csg);
|
if(e.data.cmd == 'rendered')
|
||||||
callback(null, csg);
|
{
|
||||||
|
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) {
|
worker.onerror = function(e) {
|
||||||
|
@ -313,36 +350,20 @@ OpenJsCad.revokeBlobUrl = function(url) {
|
||||||
else throw new Error("Your browser doesn't support window.URL");
|
else throw new Error("Your browser doesn't support window.URL");
|
||||||
};
|
};
|
||||||
|
|
||||||
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.createElements();
|
|
||||||
};
|
|
||||||
|
|
||||||
OpenJsCad.FileSystemApiErrorHandler = function(fileError, operation) {
|
OpenJsCad.FileSystemApiErrorHandler = function(fileError, operation) {
|
||||||
var errormap = {
|
var errormap = {
|
||||||
1: NOT_FOUND_ERR,
|
1: 'NOT_FOUND_ERR',
|
||||||
2: SECURITY_ERR,
|
2: 'SECURITY_ERR',
|
||||||
3: ABORT_ERR,
|
3: 'ABORT_ERR',
|
||||||
4: NOT_READABLE_ERR,
|
4: 'NOT_READABLE_ERR',
|
||||||
5: ENCODING_ERR,
|
5: 'ENCODING_ERR',
|
||||||
6: NO_MODIFICATION_ALLOWED_ERR,
|
6: 'NO_MODIFICATION_ALLOWED_ERR',
|
||||||
7: INVALID_STATE_ERR,
|
7: 'INVALID_STATE_ERR',
|
||||||
8: SYNTAX_ERR,
|
8: 'SYNTAX_ERR',
|
||||||
9: INVALID_MODIFICATION_ERR,
|
9: 'INVALID_MODIFICATION_ERR',
|
||||||
10: QUOTA_EXCEEDED_ERR,
|
10: 'QUOTA_EXCEEDED_ERR',
|
||||||
11: TYPE_MISMATCH_ERR,
|
11: 'TYPE_MISMATCH_ERR',
|
||||||
12: PATH_EXISTS_ERR,
|
12: 'PATH_EXISTS_ERR',
|
||||||
};
|
};
|
||||||
var errname;
|
var errname;
|
||||||
if(fileError.code in errormap)
|
if(fileError.code in errormap)
|
||||||
|
@ -364,6 +385,55 @@ OpenJsCad.AlertUserOfUncaughtExceptions = function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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 = {
|
OpenJsCad.Processor.prototype = {
|
||||||
createElements: function() {
|
createElements: function() {
|
||||||
while(this.containerdiv.children.length > 0)
|
while(this.containerdiv.children.length > 0)
|
||||||
|
@ -390,9 +460,11 @@ OpenJsCad.Processor.prototype = {
|
||||||
this.viewerdiv.innerHTML = e.toString();
|
this.viewerdiv.innerHTML = e.toString();
|
||||||
}
|
}
|
||||||
this.errordiv = document.createElement("div");
|
this.errordiv = document.createElement("div");
|
||||||
this.errordiv.style.display = "none";
|
this.errorpre = document.createElement("pre");
|
||||||
|
this.errordiv.appendChild(this.errorpre);
|
||||||
this.statusdiv = document.createElement("div");
|
this.statusdiv = document.createElement("div");
|
||||||
this.statusdiv.style.width = this.viewerwidth + "px";
|
this.statusdiv.className = "statusdiv";
|
||||||
|
//this.statusdiv.style.width = this.viewerwidth + "px";
|
||||||
this.statusspan = document.createElement("span");
|
this.statusspan = document.createElement("span");
|
||||||
this.statusbuttons = document.createElement("div");
|
this.statusbuttons = document.createElement("div");
|
||||||
this.statusbuttons.style.float = "right";
|
this.statusbuttons.style.float = "right";
|
||||||
|
@ -403,20 +475,36 @@ OpenJsCad.Processor.prototype = {
|
||||||
var that = this;
|
var that = this;
|
||||||
this.abortbutton.onclick = function(e) {
|
this.abortbutton.onclick = function(e) {
|
||||||
that.abort();
|
that.abort();
|
||||||
}
|
};
|
||||||
this.statusbuttons.appendChild(this.abortbutton);
|
this.statusbuttons.appendChild(this.abortbutton);
|
||||||
this.generateStlButton = document.createElement("button");
|
this.generateStlButton = document.createElement("button");
|
||||||
this.generateStlButton.innerHTML = "Generate STL";
|
this.generateStlButton.innerHTML = "Generate STL";
|
||||||
this.generateStlButton.onclick = function(e) {
|
this.generateStlButton.onclick = function(e) {
|
||||||
that.generateStl();
|
that.generateStl();
|
||||||
}
|
};
|
||||||
this.statusbuttons.appendChild(this.generateStlButton);
|
this.statusbuttons.appendChild(this.generateStlButton);
|
||||||
this.downloadStlLink = document.createElement("a");
|
this.downloadStlLink = document.createElement("a");
|
||||||
this.downloadStlLink.innerHTML = "Download STL";
|
this.downloadStlLink.innerHTML = "Download STL";
|
||||||
this.statusbuttons.appendChild(this.downloadStlLink);
|
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.enableItems();
|
||||||
this.containerdiv.appendChild(this.statusdiv);
|
this.containerdiv.appendChild(this.statusdiv);
|
||||||
this.containerdiv.appendChild(this.errordiv);
|
this.containerdiv.appendChild(this.errordiv);
|
||||||
|
this.containerdiv.appendChild(this.parametersdiv);
|
||||||
this.clearViewer();
|
this.clearViewer();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -447,11 +535,19 @@ OpenJsCad.Processor.prototype = {
|
||||||
this.abortbutton.style.display = this.processing? "inline":"none";
|
this.abortbutton.style.display = this.processing? "inline":"none";
|
||||||
this.generateStlButton.style.display = ((!this.hasstl)&&(this.validcsg))? "inline":"none";
|
this.generateStlButton.style.display = ((!this.hasstl)&&(this.validcsg))? "inline":"none";
|
||||||
this.downloadStlLink.style.display = this.hasstl? "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) {
|
setError: function(txt) {
|
||||||
this.errordiv.innerHTML = txt;
|
this.hasError = (txt != "");
|
||||||
this.errordiv.style.display = (txt == "")? "none":"block";
|
this.errorpre.innerText = txt;
|
||||||
|
this.enableItems();
|
||||||
|
},
|
||||||
|
|
||||||
|
setDebugging: function(debugging) {
|
||||||
|
this.debugging = debugging;
|
||||||
},
|
},
|
||||||
|
|
||||||
// script: javascript code
|
// script: javascript code
|
||||||
|
@ -461,31 +557,128 @@ OpenJsCad.Processor.prototype = {
|
||||||
filename = filename.replace(/\.jscad$/i, "");
|
filename = filename.replace(/\.jscad$/i, "");
|
||||||
this.abort();
|
this.abort();
|
||||||
this.clearViewer();
|
this.clearViewer();
|
||||||
|
this.paramDefinitions = [];
|
||||||
|
this.paramControls = [];
|
||||||
|
this.script = null;
|
||||||
this.setError("");
|
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.processing = true;
|
||||||
this.statusspan.innerHTML = "Processing, please wait...";
|
this.statusspan.innerHTML = "Processing, please wait...";
|
||||||
this.filename = filename;
|
this.enableItems();
|
||||||
var that = this;
|
var that = this;
|
||||||
this.worker = OpenJsCad.javaScriptToSolidASync(script, function(err, csg) {
|
var paramValues = this.getParamValues();
|
||||||
that.processing = false;
|
if(this.debugging)
|
||||||
that.worker = null;
|
{
|
||||||
if(err)
|
try
|
||||||
{
|
|
||||||
that.setError(err);
|
|
||||||
that.statusspan.innerHTML = "Error.";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
var csg = OpenJsCad.javaScriptToSolidSync(this.script, paramValues);
|
||||||
|
that.processing = false;
|
||||||
that.solid = csg;
|
that.solid = csg;
|
||||||
if(that.viewer) that.viewer.setCsg(csg);
|
if(that.viewer) that.viewer.setCsg(csg);
|
||||||
that.validcsg = true;
|
that.validcsg = true;
|
||||||
that.statusspan.innerHTML = "Ready.";
|
that.statusspan.innerHTML = "Ready.";
|
||||||
}
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
that.processing = false;
|
||||||
|
that.setError(e.toString());
|
||||||
|
that.statusspan.innerHTML = "Error.";
|
||||||
|
}
|
||||||
that.enableItems();
|
that.enableItems();
|
||||||
if(that.onchange) that.onchange();
|
if(that.onchange) that.onchange();
|
||||||
});
|
}
|
||||||
this.enableItems();
|
else
|
||||||
if(this.onchange) this.onchange();
|
{
|
||||||
|
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() {
|
hasSolid: function() {
|
||||||
|
@ -496,6 +689,7 @@ OpenJsCad.Processor.prototype = {
|
||||||
return this.processing;
|
return this.processing;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
clearStl1: function() {
|
clearStl1: function() {
|
||||||
if(this.hasstl)
|
if(this.hasstl)
|
||||||
{
|
{
|
||||||
|
@ -519,6 +713,7 @@ OpenJsCad.Processor.prototype = {
|
||||||
if(this.onchange) this.onchange();
|
if(this.onchange) this.onchange();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
clearStl: function() {
|
clearStl: function() {
|
||||||
if(this.hasstl)
|
if(this.hasstl)
|
||||||
|
@ -543,14 +738,14 @@ OpenJsCad.Processor.prototype = {
|
||||||
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
|
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
|
||||||
if(!window.requestFileSystem)
|
if(!window.requestFileSystem)
|
||||||
{
|
{
|
||||||
throw new Error("Your browser does not support the HTML5 FileSystem API");
|
throw new Error("Your browser does not support the HTML5 FileSystem API. Please try the Chrome browser instead.");
|
||||||
}
|
}
|
||||||
if(!window.BlobBuilder)
|
if(!window.BlobBuilder)
|
||||||
{
|
{
|
||||||
throw new Error("Your browser does not support the HTML5 BlobBuilder API");
|
throw new Error("Your browser does not support the HTML5 BlobBuilder API. Please try the Chrome browser instead.");
|
||||||
}
|
}
|
||||||
// create a random directory name:
|
// create a random directory name:
|
||||||
var dirname = "OpenJsCadStlOutput_"+parseInt(Math.random()*1000000000, 10)+".stl";
|
var dirname = "OpenJsCadStlOutput1_"+parseInt(Math.random()*1000000000, 10)+".stl";
|
||||||
var filename = this.filename+".stl";
|
var filename = this.filename+".stl";
|
||||||
var that = this;
|
var that = this;
|
||||||
window.requestFileSystem(TEMPORARY, 20*1024*1024, function(fs){
|
window.requestFileSystem(TEMPORARY, 20*1024*1024, function(fs){
|
||||||
|
@ -586,5 +781,112 @@ OpenJsCad.Processor.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
|
@ -4,7 +4,7 @@
|
||||||
<script src="lightgl.js"></script>
|
<script src="lightgl.js"></script>
|
||||||
<script src="csg.js"></script>
|
<script src="csg.js"></script>
|
||||||
<script src="openjscad.js"></script>
|
<script src="openjscad.js"></script>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font: 14px/20px 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
font: 14px/20px 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
@ -26,34 +26,12 @@ pre, textarea {
|
||||||
textarea:focus {
|
textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
#filedropzone {
|
|
||||||
border: 2px dashed #bbb;
|
|
||||||
-moz-border-radius: 5px;
|
|
||||||
-webkit-border-radius: 5px;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 15px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filedropzone_empty {
|
|
||||||
text-align: center;
|
|
||||||
color: #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filedropzone_filled {
|
|
||||||
color: black;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filebuttons {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
a { color: inherit; }
|
a { color: inherit; }
|
||||||
|
</style>
|
||||||
|
|
||||||
canvas { cursor: move; }
|
<link rel="stylesheet" href="openjscad.css" type="text/css">
|
||||||
|
|
||||||
</style>
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,10 +104,10 @@ function fileChanged()
|
||||||
document.getElementById("filedropzone_filled").style.display = "none";
|
document.getElementById("filedropzone_filled").style.display = "none";
|
||||||
document.getElementById("filedropzone_empty").style.display = "block";
|
document.getElementById("filedropzone_empty").style.display = "block";
|
||||||
}
|
}
|
||||||
parseFile();
|
parseFile(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFile()
|
function parseFile(debugging)
|
||||||
{
|
{
|
||||||
if(gCurrentFile)
|
if(gCurrentFile)
|
||||||
{
|
{
|
||||||
|
@ -158,6 +136,7 @@ function parseFile()
|
||||||
{
|
{
|
||||||
var filename = gCurrentFile.name;
|
var filename = gCurrentFile.name;
|
||||||
filename = filename.replace(/^.*\/([^\/]*)$/, "$1");
|
filename = filename.replace(/^.*\/([^\/]*)$/, "$1");
|
||||||
|
gProcessor.setDebugging(debugging);
|
||||||
gProcessor.setJsCad(jscadscript, filename);
|
gProcessor.setJsCad(jscadscript, filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +163,8 @@ function parseFile()
|
||||||
<span id="currentfile">dfghdfgh</span>
|
<span id="currentfile">dfghdfgh</span>
|
||||||
<div id="filebuttons">
|
<div id="filebuttons">
|
||||||
<button id="getstlbutton" style="display:none" onclick="getStl();">Get STL</button>
|
<button id="getstlbutton" style="display:none" onclick="getStl();">Get STL</button>
|
||||||
<button onclick="parseFile();">Reload</button>
|
<button onclick="parseFile(false);">Reload</button>
|
||||||
|
<button onclick="parseFile(true);">Debug (see below)</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -207,5 +187,20 @@ When finished press Generate STL to generate the STL file. Then click Save STL a
|
||||||
a file with .stl extension.
|
a file with .stl extension.
|
||||||
<br><br>
|
<br><br>
|
||||||
For more information about OpenJsCad see the <a href="index.html">introduction</a>.
|
For more information about OpenJsCad see the <a href="index.html">introduction</a>.
|
||||||
|
|
||||||
|
<h2>Debugging</h2>
|
||||||
|
By default your .jscad file is parsed in a separate thread (in a Web Worker). This allows long running
|
||||||
|
scripts to be executed while the web browser stays responsive. The web browser's debugger does not
|
||||||
|
have access to scripts running in web workers however. To allow debugging you can use the Debug button above to execute
|
||||||
|
your jscad code in the main thread instead of in a web worker.
|
||||||
|
<br><br>
|
||||||
|
To debug your code in Google Chrome: open the Developer Tools by typing Ctrl+Shift+I (or Command+Option+I on mac).
|
||||||
|
Then press the Debug button above. The debugger will stop just before executing your main() function.
|
||||||
|
<br><br>
|
||||||
|
For more information about debugging in Chrome see
|
||||||
|
<a href="http://code.google.com/chrome/devtools/docs/overview.html" target="_blank">Chrome Developer Tools: Overview</a>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in a new issue