CSG construction must now be done in a main() function instead of in global scope; Added CSG properties and connectors; file processing is now done in the background (Web Workers); STL file is now downloaded instead of copied from a text area
This commit is contained in:
parent
fbafcf6864
commit
3b4e1fd1a6
109
csg.js
109
csg.js
|
@ -100,6 +100,14 @@ CSG.fromPolygons = function(polygons) {
|
||||||
return csg;
|
return csg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// create from an untyped object with identical property names:
|
||||||
|
CSG.fromObject = function(obj) {
|
||||||
|
var polygons = obj.polygons.map( function(p) {
|
||||||
|
return CSG.Polygon.fromObject(p);
|
||||||
|
});
|
||||||
|
return CSG.fromPolygons(polygons);
|
||||||
|
};
|
||||||
|
|
||||||
CSG.prototype = {
|
CSG.prototype = {
|
||||||
toPolygons: function() {
|
toPolygons: function() {
|
||||||
return this.polygons;
|
return this.polygons;
|
||||||
|
@ -221,9 +229,7 @@ CSG.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
mirrored: function(plane) {
|
mirrored: function(plane) {
|
||||||
var newpolygons = this.polygons.map(function(p) { return p.mirrored(plane); } );
|
return this.transform(CSG.Matrix4x4.mirroring(plane));
|
||||||
return CSG.fromPolygons(newpolygons);
|
|
||||||
// TODO: also mirror properties
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mirroredX: function() {
|
mirroredX: function() {
|
||||||
|
@ -269,7 +275,7 @@ CSG.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
toString: function() {
|
toString: function() {
|
||||||
var result = "";
|
var result = "CSG solid:\n";
|
||||||
this.polygons.map(function(p){ result += p.toString(); });
|
this.polygons.map(function(p){ result += p.toString(); });
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
@ -422,8 +428,15 @@ CSG.prototype = {
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
connectTo: function(myConnector, otherConnector, mirror, axisrotation) {
|
// Connect a solid to another solid, such that two CSG.Connectors become connected
|
||||||
var matrix = myConnector.getTransformationTo(otherConnector, mirror, axisrotation);
|
// myConnector: a CSG.Connector of this solid
|
||||||
|
// otherConnector: a CSG.Connector to which myConnector should be connected
|
||||||
|
// mirror: false: the 'axis' vectors of the connectors should point in the same direction
|
||||||
|
// true: the 'axis' vectors of the connectors should point in opposite direction
|
||||||
|
// normalrotation: degrees of rotation between the 'normal' vectors of the two
|
||||||
|
// connectors
|
||||||
|
connectTo: function(myConnector, otherConnector, mirror, normalrotation) {
|
||||||
|
var matrix = myConnector.getTransformationTo(otherConnector, mirror, normalrotation);
|
||||||
return this.transform(matrix);
|
return this.transform(matrix);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -587,8 +600,8 @@ CSG.sphere = function(options) {
|
||||||
}
|
}
|
||||||
var result = CSG.fromPolygons(polygons);
|
var result = CSG.fromPolygons(polygons);
|
||||||
result.properties.sphere = new CSG.Properties();
|
result.properties.sphere = new CSG.Properties();
|
||||||
result.properties.sphere.center = new CSG.Vector3D(c);
|
result.properties.sphere.center = new CSG.Vector3D(center);
|
||||||
result.properties.sphere.facepoint = c.plus(xvector);
|
result.properties.sphere.facepoint = center.plus(xvector);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -737,9 +750,9 @@ CSG.roundedCylinder = function(options) {
|
||||||
var ray = zvector.unit();
|
var ray = zvector.unit();
|
||||||
var axisX = xvector.unit();
|
var axisX = xvector.unit();
|
||||||
result.properties.roundedCylinder = new CSG.Properties();
|
result.properties.roundedCylinder = new CSG.Properties();
|
||||||
result.properties.cylinder.start = new CSG.Connector(p1, ray.negated(), axisX);
|
result.properties.roundedCylinder.start = new CSG.Connector(p1, ray.negated(), axisX);
|
||||||
result.properties.cylinder.end = new CSG.Connector(p2, ray, axisX);
|
result.properties.roundedCylinder.end = new CSG.Connector(p2, ray, axisX);
|
||||||
result.properties.cylinder.facepoint = p1.plus(xvector);
|
result.properties.roundedCylinder.facepoint = p1.plus(xvector);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -991,6 +1004,12 @@ CSG.Vertex = function(pos) {
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// create from an untyped object with identical property names:
|
||||||
|
CSG.Vertex.fromObject = function(obj) {
|
||||||
|
var pos = new CSG.Vector3D(obj.pos);
|
||||||
|
return new CSG.Vertex(pos);
|
||||||
|
};
|
||||||
|
|
||||||
CSG.Vertex.prototype = {
|
CSG.Vertex.prototype = {
|
||||||
// Return a vertex with all orientation-specific data (e.g. vertex normal) flipped. Called when the
|
// Return a vertex with all orientation-specific data (e.g. vertex normal) flipped. Called when the
|
||||||
// orientation of a polygon is flipped.
|
// orientation of a polygon is flipped.
|
||||||
|
@ -1040,6 +1059,13 @@ CSG.Plane = function(normal, w) {
|
||||||
this.w = w;
|
this.w = w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// create from an untyped object with identical property names:
|
||||||
|
CSG.Plane.fromObject = function(obj) {
|
||||||
|
var normal = new CSG.Vector3D(obj.normal);
|
||||||
|
var w = parseFloat(obj.w);
|
||||||
|
return new CSG.Plane(normal, w);
|
||||||
|
};
|
||||||
|
|
||||||
// `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
|
// `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
|
||||||
// point is on the plane.
|
// point is on the plane.
|
||||||
CSG.Plane.EPSILON = 1e-5;
|
CSG.Plane.EPSILON = 1e-5;
|
||||||
|
@ -1343,6 +1369,16 @@ CSG.Polygon = function(vertices, shared, plane) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// create from an untyped object with identical property names:
|
||||||
|
CSG.Polygon.fromObject = function(obj) {
|
||||||
|
var vertices = obj.vertices.map(function(v) {
|
||||||
|
return CSG.Vertex.fromObject(v);
|
||||||
|
});
|
||||||
|
var shared = null;
|
||||||
|
var plane = CSG.Plane.fromObject(obj.plane);
|
||||||
|
return new CSG.Polygon(vertices, shared, plane);
|
||||||
|
};
|
||||||
|
|
||||||
CSG.Polygon.prototype = {
|
CSG.Polygon.prototype = {
|
||||||
// check whether the polygon is convex (it should be, otherwise we will get unexpected results)
|
// check whether the polygon is convex (it should be, otherwise we will get unexpected results)
|
||||||
checkIfConvex: function() {
|
checkIfConvex: function() {
|
||||||
|
@ -1466,19 +1502,17 @@ CSG.Polygon.prototype = {
|
||||||
return new CSG.Polygon(newvertices, this.shared, newplane);
|
return new CSG.Polygon(newvertices, this.shared, newplane);
|
||||||
},
|
},
|
||||||
|
|
||||||
mirrored: function(plane) {
|
|
||||||
var newvertices = this.vertices.map(function(v) {
|
|
||||||
var newpos = plane.mirrorPoint(v.pos);
|
|
||||||
return new CSG.Vertex(newpos);
|
|
||||||
});
|
|
||||||
newvertices.reverse();
|
|
||||||
return new CSG.Polygon(newvertices, this.shared);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Affine transformation of polygon. Returns a new CSG.Polygon
|
// Affine transformation of polygon. Returns a new CSG.Polygon
|
||||||
transform: function(matrix4x4) {
|
transform: function(matrix4x4) {
|
||||||
var newvertices = this.vertices.map(function(v) { return v.transform(matrix4x4); } );
|
var newvertices = this.vertices.map(function(v) { return v.transform(matrix4x4); } );
|
||||||
var newplane = this.plane.transform(matrix4x4);
|
var newplane = this.plane.transform(matrix4x4);
|
||||||
|
var scalefactor = matrix4x4.elements[0] * matrix4x4.elements[5] * matrix4x4.elements[10];
|
||||||
|
if(scalefactor < 0)
|
||||||
|
{
|
||||||
|
// the transformation includes mirroring. We need to reverse the vertex order
|
||||||
|
// in order to preserve the inside/outside orientation:
|
||||||
|
newvertices.reverse();
|
||||||
|
}
|
||||||
return new CSG.Polygon(newvertices, this.shared, newplane);
|
return new CSG.Polygon(newvertices, this.shared, newplane);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2155,6 +2189,21 @@ CSG.Matrix4x4.translation = function(v) {
|
||||||
return new CSG.Matrix4x4(els);
|
return new CSG.Matrix4x4(els);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create an affine matrix for mirroring into an arbitrary plane:
|
||||||
|
CSG.Matrix4x4.mirroring = function(plane) {
|
||||||
|
var nx = plane.normal.x;
|
||||||
|
var ny = plane.normal.y;
|
||||||
|
var nz = plane.normal.z;
|
||||||
|
var w = plane.w;
|
||||||
|
var els = [
|
||||||
|
(1.0-2.0*nx*nx), (-2.0*ny*nx), (-2.0*nz*nx), 0,
|
||||||
|
(-2.0*nx*ny), (1.0-2.0*ny*ny), (-2.0*nz*ny), 0,
|
||||||
|
(-2.0*nx*nz), (-2.0*ny*nz), (1.0-2.0*nz*nz), 0,
|
||||||
|
(-2.0*nx*w), (-2.0*ny*w), (-2.0*nz*w), 1
|
||||||
|
];
|
||||||
|
return new CSG.Matrix4x4(els);
|
||||||
|
};
|
||||||
|
|
||||||
// Create an affine matrix for scaling:
|
// Create an affine matrix for scaling:
|
||||||
CSG.Matrix4x4.scaling = function(v) {
|
CSG.Matrix4x4.scaling = function(v) {
|
||||||
// parse as CSG.Vector3D, so we can pass an array or a CSG.Vector3D
|
// parse as CSG.Vector3D, so we can pass an array or a CSG.Vector3D
|
||||||
|
@ -3372,19 +3421,11 @@ CSG.Connector = function(point, axisvector, normalvector) {
|
||||||
this.normalvector = new CSG.Vector3D(normalvector);
|
this.normalvector = new CSG.Vector3D(normalvector);
|
||||||
};
|
};
|
||||||
|
|
||||||
CSG.Connector.test = function() {
|
|
||||||
var con1 = new CSG.Connector([1,2,5], [1,1,0], [1, -0.8, 0]);
|
|
||||||
var con2 = new CSG.Connector([-5,1,-1], [2,3,5], [0,0,1]);
|
|
||||||
var transform = con1.getTransformationTo(con2, false, 0);
|
|
||||||
var check = con1.transform(transform);
|
|
||||||
};
|
|
||||||
|
|
||||||
CSG.Connector.prototype = {
|
CSG.Connector.prototype = {
|
||||||
normalized: function() {
|
normalized: function() {
|
||||||
var axisvector = this.axisvector.unit();
|
var axisvector = this.axisvector.unit();
|
||||||
// make the normal vector truly normal:
|
// make the normal vector truly normal:
|
||||||
var n = this.normalvector.cross(axisvector).unit();
|
var n = this.normalvector.cross(axisvector).unit();
|
||||||
// var normalvector = n.cross(axisvector);
|
|
||||||
var normalvector = axisvector.cross(n);
|
var normalvector = axisvector.cross(n);
|
||||||
return new CSG.Connector(this.point, axisvector, normalvector);
|
return new CSG.Connector(this.point, axisvector, normalvector);
|
||||||
},
|
},
|
||||||
|
@ -3396,9 +3437,15 @@ CSG.Connector.prototype = {
|
||||||
return new CSG.Connector(point, axisvector, normalvector);
|
return new CSG.Connector(point, axisvector, normalvector);
|
||||||
},
|
},
|
||||||
|
|
||||||
getTransformationTo: function(other, mirror, axisrotation) {
|
// Get the transformation matrix to connect this Connector to another connector
|
||||||
|
// other: a CSG.Connector to which this connector should be connected
|
||||||
|
// mirror: false: the 'axis' vectors of the connectors should point in the same direction
|
||||||
|
// true: the 'axis' vectors of the connectors should point in opposite direction
|
||||||
|
// normalrotation: degrees of rotation between the 'normal' vectors of the two
|
||||||
|
// connectors
|
||||||
|
getTransformationTo: function(other, mirror, normalrotation) {
|
||||||
mirror = mirror? true:false;
|
mirror = mirror? true:false;
|
||||||
axisrotation = axisrotation? Number(axisrotation):0;
|
normalrotation = normalrotation? Number(normalrotation):0;
|
||||||
var us = this.normalized();
|
var us = this.normalized();
|
||||||
other = other.normalized();
|
other = other.normalized();
|
||||||
// shift to the origin:
|
// shift to the origin:
|
||||||
|
@ -3425,7 +3472,7 @@ CSG.Connector.prototype = {
|
||||||
angle1 = normalsbasis.to2D(usAxesAligned.normalvector).angle();
|
angle1 = normalsbasis.to2D(usAxesAligned.normalvector).angle();
|
||||||
angle2 = normalsbasis.to2D(other.normalvector).angle();
|
angle2 = normalsbasis.to2D(other.normalvector).angle();
|
||||||
rotation = 180.0 * (angle2 - angle1) / Math.PI;
|
rotation = 180.0 * (angle2 - angle1) / Math.PI;
|
||||||
rotation += axisrotation;
|
rotation += normalrotation;
|
||||||
transformation = transformation.multiply(normalsbasis.getProjectionMatrix());
|
transformation = transformation.multiply(normalsbasis.getProjectionMatrix());
|
||||||
transformation = transformation.multiply(CSG.Matrix4x4.rotationZ(rotation));
|
transformation = transformation.multiply(CSG.Matrix4x4.rotationZ(rotation));
|
||||||
transformation = transformation.multiply(normalsbasis.getInverseProjectionMatrix());
|
transformation = transformation.multiply(normalsbasis.getInverseProjectionMatrix());
|
||||||
|
|
225
index.html
225
index.html
|
@ -32,100 +32,38 @@ textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
//h1, h2 { font: bold 50px/50px 'Helvetica Neue', Helvetica, Arial; }
|
|
||||||
//h2 { font-size: 30px; margin: 10px 0 0 0; }
|
|
||||||
a { color: inherit; }
|
|
||||||
.viewer { width: 200px; height: 200px; background: #EEE url(images.png); }
|
|
||||||
#combined .viewer { width: 150px; height: 150px; }
|
|
||||||
table { border-collapse: collapse; margin: 0 auto; }
|
|
||||||
td { padding: 5px; text-align: center; }
|
|
||||||
td code { background: none; border: none; color: inherit; }
|
|
||||||
canvas { cursor: move; }
|
canvas { cursor: move; }
|
||||||
|
|
||||||
#needchrome {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
var gProcessor=null;
|
||||||
var gViewer=null;
|
|
||||||
var gSolid=new CSG();
|
|
||||||
var gSolidSource="";
|
|
||||||
|
|
||||||
function isChrome()
|
|
||||||
{
|
|
||||||
return (navigator.userAgent.search("Chrome") >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onload()
|
function onload()
|
||||||
{
|
{
|
||||||
var needchromediv = document.getElementById("needchrome");
|
gProcessor = new OpenJsCad.Processor(document.getElementById("viewer"));
|
||||||
if(needchromediv)
|
|
||||||
{
|
|
||||||
if(!isChrome())
|
|
||||||
{
|
|
||||||
needchromediv.style.display="block";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var containerelement = document.getElementById("viewer");
|
|
||||||
gViewer = new OpenJsCad.Viewer(containerelement, 600, 600, 50);
|
|
||||||
updateSolid();
|
updateSolid();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSolid()
|
function updateSolid()
|
||||||
{
|
{
|
||||||
if(gViewer.supported())
|
gProcessor.setJsCad(document.getElementById('code').value);
|
||||||
{
|
|
||||||
var code=document.getElementById('code').value;
|
|
||||||
if(code != gSolidSource)
|
|
||||||
{
|
|
||||||
gSolidSource=code;
|
|
||||||
var errtxt = "";
|
|
||||||
var csg = new CSG();
|
|
||||||
try {
|
|
||||||
csg = OpenJsCad.javaScriptToSolid(code);
|
|
||||||
} catch (e) {
|
|
||||||
errtxt = 'Error: <code>' + e.toString().replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') + '</code>';
|
|
||||||
}
|
}
|
||||||
gSolid = csg;
|
|
||||||
var errdiv=document.getElementById('error');
|
|
||||||
errdiv.innerHTML = errtxt;
|
|
||||||
errdiv.style.display = (errtxt == "")? "none":"block";
|
|
||||||
gViewer.setCsg(gSolid);
|
|
||||||
var stlarea = document.getElementById('stloutput');
|
|
||||||
stlarea.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStl()
|
|
||||||
{
|
|
||||||
updateSolid();
|
|
||||||
var stl=gSolid.toStlString();
|
|
||||||
var stlarea = document.getElementById('stloutput');
|
|
||||||
stlarea.value=stl;
|
|
||||||
stlarea.style.display = "inline";
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<title>OpenJsCad</title>
|
<title>OpenJsCad</title>
|
||||||
</head>
|
</head>
|
||||||
<body onload="onload()">
|
<body onload="onload()">
|
||||||
<h1>OpenJsCad</h1>
|
<h1>OpenJsCad</h1>
|
||||||
<div id="needchrome">Please note: OpenJsCad currently only runs reliably on Google Chrome!</div>
|
|
||||||
Create an STL file for 3D printing using constructive solid modeling in Javascript.
|
Create an STL file for 3D printing using constructive solid modeling in Javascript.
|
||||||
<table>
|
<div id="viewer"></div>
|
||||||
<tr>
|
|
||||||
<td><div id="viewer" class="viewer" style="background-image:none;width:600px;height:600px;"></div></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<h2>Playground</h2>
|
<h2>Playground</h2>
|
||||||
Try it by entering some code below. Anything you enter will be lost as soon as this page is reloaded;
|
Try it by entering some code below. Anything you enter will be lost as soon as this page is reloaded;
|
||||||
to build your own models you should instead store them in a .jscad file on your computer
|
to build your own models you should instead store them in a .jscad file on your computer
|
||||||
and use the <a href="processfile.html"><b>OpenJsCad parser</b></a>.
|
and use the <a href="processfile.html"><b>OpenJsCad parser</b></a>.
|
||||||
<br><br>
|
<br><br>
|
||||||
<textarea id="code">var resolution = 16; // increase to get smoother corners (will get slow!)
|
<textarea id="code">
|
||||||
|
function main()
|
||||||
|
{
|
||||||
|
var resolution = 16; // 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 });
|
||||||
|
@ -137,25 +75,24 @@ result = result.union(sphere1);
|
||||||
result = result.subtract(sphere2);
|
result = result.subtract(sphere2);
|
||||||
result = result.intersect(sphere3);
|
result = result.intersect(sphere3);
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
</textarea>
|
</textarea><br>
|
||||||
|
<input type="submit" value="Update" onclick="updateSolid(); return false;">
|
||||||
<p id="error"></p>
|
|
||||||
<br>
|
|
||||||
<input type="submit" value="Update Preview" onclick="updateSolid(); return false;">
|
|
||||||
<input type="submit" value="Get STL" onclick="getStl(); return false;"><br>
|
|
||||||
<textarea id="stloutput" readonly style="width: 80%; height: 100px; display: none;"></textarea>
|
|
||||||
<br>
|
<br>
|
||||||
<h1>About</h1>
|
<h1>About</h1>
|
||||||
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>
|
||||||
<code>var cube = CSG.cube(); return cube;</code> creates a cube with a radius of 1 and centered at the origin.
|
<pre>function main() {
|
||||||
The code should always end in a return statement, returning a CSG solid.
|
var cube = CSG.cube();
|
||||||
|
return cube;
|
||||||
|
}</pre>
|
||||||
|
creates a cube with a radius of 1 and centered at the origin.
|
||||||
|
The code should always contain a main() function, returning a CSG solid.
|
||||||
<br><br>
|
<br><br>
|
||||||
To build your own modes, 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 you can generate an .stl file,
|
<a href="processfile.html">OpenJsCad parser</a>. When finished, click on Generate STL and save the result
|
||||||
ready to be printed on your 3d printer.
|
in an .stl file, ready to be printed on your 3d printer.
|
||||||
<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,
|
||||||
|
@ -317,9 +254,129 @@ var csg = cube1.subtract(cube2);
|
||||||
var rounded = csg.expand(0.2, 8);
|
var rounded = csg.expand(0.2, 8);
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<h2>Using Properties</h2>
|
||||||
|
The 'property' property of a solid can be used to store metdata for the object,
|
||||||
|
for example the coordinate of a specific point of interest of the solid. Whenever
|
||||||
|
the object is transformed (i.e. rotated, scaled or translated), the properties
|
||||||
|
are transformed with it. So the property will keep pointing to the same point
|
||||||
|
of interest even after several transformations have been applied to the solid.
|
||||||
|
<br><br>
|
||||||
|
Properties can have any type, but only the properties of classes supporting
|
||||||
|
a 'transform' method will actually be transformed. This includes CSG.Vector3D,
|
||||||
|
CSG.Plane and CSG.Connector. In particular CSG.Connector properties (see below)
|
||||||
|
can be very useful: these can
|
||||||
|
be used to attach a solid to another solid at a predetermined location regardless of the
|
||||||
|
current orientation.
|
||||||
|
<br><br>
|
||||||
|
It's even possible to include a CSG solid as a property of another solid. This could
|
||||||
|
be used for example
|
||||||
|
to define the cutout cylinders to create matching screw holes for an object. Those 'solid properties'
|
||||||
|
get the same transformations as the owning solid but they will not be visible in the result
|
||||||
|
of CSG operations such as union().
|
||||||
|
<br><br>
|
||||||
|
Other kind of properties (for
|
||||||
|
example, strings) will still be included in the properties of the transformed
|
||||||
|
solid, but the properties will not get any transformation when the owning solid is transformed. <br><br>
|
||||||
|
All primitive solids have some predefined properties, such as the center point
|
||||||
|
of a sphere (TODO: document).
|
||||||
|
<br><br>
|
||||||
|
The solid resulting from CSG operations (union(), subtract(), intersect()) will get
|
||||||
|
the merged properties of both source solids. If identically named properties exist, only
|
||||||
|
one of them will be kept.
|
||||||
|
<pre>
|
||||||
|
var cube = CSG.cube({radius: 1.0});
|
||||||
|
cube.properties.aCorner = new CSG.Vector3D([1, 1, 1]);
|
||||||
|
cube = cube.translate([5, 0, 0]);
|
||||||
|
cube = cube.scale(2);
|
||||||
|
// cube.properties.aCorner will now point to [12, 2, 2],
|
||||||
|
// which is still the same corner point
|
||||||
|
|
||||||
|
// Properties can be stored in arrays; all properties in the array
|
||||||
|
// will be transformed if the solid is transformed:
|
||||||
|
cube.properties.otherCorners = [
|
||||||
|
new CSG.Vector3D([-1, 1, 1]),
|
||||||
|
new CSG.Vector3D([-1, -1, 1])
|
||||||
|
];
|
||||||
|
|
||||||
|
// and we can create sub-property objects; these must be of the
|
||||||
|
// CSG.Properties class. All sub properties will be transformed with
|
||||||
|
// the solid:
|
||||||
|
cube.properties.myProperties = new CSG.Properties();
|
||||||
|
cube.properties.myProperties.someProperty = new CSG.Vector3D([-1, -1, -1]);
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h2>Connectors</h2>
|
||||||
|
The CSG.Connector class is intended to facilitate
|
||||||
|
attaching two solids to each other at a predetermined
|
||||||
|
location and orientation.
|
||||||
|
For example suppose we have a CSG solid depicting a servo motor
|
||||||
|
and a solid of a servo arm: by defining a Connector property for each of them, we
|
||||||
|
can easily attach the servo arm to the servo motor at the correct position
|
||||||
|
(i.e. the motor shaft) and orientation (i.e. arm perpendicular to the shaft)
|
||||||
|
even if we don't know their current position and orientation
|
||||||
|
in 3D space.<br><br>
|
||||||
|
In other words Connector give us the freedom to rotate and translate objects at will without the need
|
||||||
|
to keep track of their positions and boundaries. And if a third party library exposes connectors for
|
||||||
|
its solids, the user of the library does not have to know the actual dimensions or
|
||||||
|
shapes, only the names of the connector properties.
|
||||||
|
<br><br>
|
||||||
|
A CSG.Connector consist of 3 properties:<br>
|
||||||
|
<b>point</b>: a CSG.Vector3D defining the connection point in 3D space<br>
|
||||||
|
<b>axis</b>: a CSG.Vector3D defining the direction vector of the connection
|
||||||
|
(in the case of the servo motor example it would point in the direction of the shaft)<br>
|
||||||
|
<b>normal</b>: a CSG.Vector3D direction vector somewhat perpendicular to axis; this
|
||||||
|
defines the "12 o'clock" orientation of the connection.
|
||||||
|
<br><br>
|
||||||
|
When connecting two connectors, the solid is transformed such that the <b>point</b>
|
||||||
|
properties will be identical, the <b>axis</b> properties will have the same direction
|
||||||
|
(or opposite direction if mirror == true), and the <b>normal</b>s match as much as possible.
|
||||||
|
<br><br>
|
||||||
|
Connectors can be connected by means of two methods:<br>
|
||||||
|
A CSG solid's <b>connectTo()</b> function transforms a solid such that two connectors
|
||||||
|
become connected.<br>
|
||||||
|
Alternatively we can use a connector's <b>getTransformationTo()</b> method to obtain
|
||||||
|
a transformation matrix which would connect the connectors. This can be used if we
|
||||||
|
need to apply the same transform to multiple solids.
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
var cube1 = CSG.cube({radius: 10});
|
||||||
|
var cube2 = CSG.cube({radius: 4});
|
||||||
|
|
||||||
|
// define a connector on the center of one face of cube1
|
||||||
|
// The connector's axis points outwards and its normal points
|
||||||
|
// towards the positive z axis:
|
||||||
|
cube1.properties.myConnector = new CSG.Connector([10, 0, 0], [1, 0, 0], [0, 0, 1]);
|
||||||
|
|
||||||
|
// define a similar connector for cube 2:
|
||||||
|
cube2.properties.myConnector = new CSG.Connector([0, -4, 0], [0, -1, 0], [0, 0, 1]);
|
||||||
|
|
||||||
|
// do some random transformations on cube 1:
|
||||||
|
cube1 = cube1.rotateX(30);
|
||||||
|
cube1 = cube1.translate([3.1, 2, 0]);
|
||||||
|
|
||||||
|
// Now attach cube2 to cube 1:
|
||||||
|
cube2 = cube2.connectTo(
|
||||||
|
cube2.properties.myConnector,
|
||||||
|
cube1.properties.myConnector,
|
||||||
|
true, // mirror
|
||||||
|
0 // normalrotation
|
||||||
|
);
|
||||||
|
|
||||||
|
// Or alternatively:
|
||||||
|
var matrix = cube2.properties.myConnector.getTransformationTo(
|
||||||
|
cube1.properties.myConnector,
|
||||||
|
true, // mirror
|
||||||
|
0 // normalrotation
|
||||||
|
);
|
||||||
|
cube2 = cube2.transform(matrix);
|
||||||
|
|
||||||
|
var result = cube2.union(cube1);
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
<h2>Determining the bounds of an object</h2>
|
<h2>Determining the bounds of an object</h2>
|
||||||
The getBounds() function can be used to retrieve the bounding box of an object.
|
The getBounds() function can be used to retrieve the bounding box of an object.
|
||||||
This can be useful if you want to align two objects to each other. getBounds() returns
|
getBounds() returns
|
||||||
an array with two elements specifying the minimum x,y,z coordinate and the maximum x,y,z coordinate:
|
an array with two elements specifying the minimum x,y,z coordinate and the maximum x,y,z coordinate:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
|
|
274
openjscad.js
274
openjscad.js
|
@ -206,3 +206,277 @@ OpenJsCad.javaScriptToSolid = function(script) {
|
||||||
}
|
}
|
||||||
return csg;
|
return csg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback: should be function(error, csg)
|
||||||
|
OpenJsCad.javaScriptToSolidASync = function(script, callback) {
|
||||||
|
var baselibraries = [
|
||||||
|
"csg.js",
|
||||||
|
"openjscad.js"
|
||||||
|
];
|
||||||
|
var baseurl = document.location + "";
|
||||||
|
var workerscript = "";
|
||||||
|
workerscript += script;
|
||||||
|
workerscript += "\n//// END OF USER SUPPLIED SCRIPT\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 += "importScripts.call(null, _csg_libraries);\n";
|
||||||
|
workerscript += "_csg_libraries.map(function(l){importScripts(l)});\n";
|
||||||
|
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 += " var csg = main(); self.postMessage({cmd: 'rendered', csg: csg});";
|
||||||
|
workerscript += "}},false);\n";
|
||||||
|
|
||||||
|
var blobURL = OpenJsCad.textToBlobUrl(workerscript);
|
||||||
|
|
||||||
|
if(!window.Worker) throw new Error("Your browser doesn't support Web Workers");
|
||||||
|
var worker = new Worker(blobURL);
|
||||||
|
worker.onmessage = function(e) {
|
||||||
|
if(e.data && e.data.cmd == 'rendered')
|
||||||
|
{
|
||||||
|
var csg = CSG.fromObject(e.data.csg);
|
||||||
|
callback(null, csg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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.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.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.errordiv.style.display = "none";
|
||||||
|
this.statusdiv = document.createElement("div");
|
||||||
|
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.enableItems();
|
||||||
|
this.containerdiv.appendChild(this.statusdiv);
|
||||||
|
this.containerdiv.appendChild(this.errordiv);
|
||||||
|
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";
|
||||||
|
},
|
||||||
|
|
||||||
|
setError: function(txt) {
|
||||||
|
this.errordiv.innerHTML = txt;
|
||||||
|
this.errordiv.style.display = (txt == "")? "none":"block";
|
||||||
|
},
|
||||||
|
|
||||||
|
setJsCad: function(script) {
|
||||||
|
this.abort();
|
||||||
|
this.clearViewer();
|
||||||
|
this.setError("");
|
||||||
|
this.processing = true;
|
||||||
|
this.statusspan.innerHTML = "Processing, please wait...";
|
||||||
|
var that = this;
|
||||||
|
this.worker = OpenJsCad.javaScriptToSolidASync(script, 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();
|
||||||
|
});
|
||||||
|
this.enableItems();
|
||||||
|
if(this.onchange) this.onchange();
|
||||||
|
},
|
||||||
|
|
||||||
|
hasSolid: function() {
|
||||||
|
return this.validcsg;
|
||||||
|
},
|
||||||
|
|
||||||
|
isProcessing: function() {
|
||||||
|
return this.processing;
|
||||||
|
},
|
||||||
|
|
||||||
|
clearStl: function() {
|
||||||
|
if(this.hasstl)
|
||||||
|
{
|
||||||
|
this.hasstl = false;
|
||||||
|
OpenJsCad.revokeBlobUrl(this.stlBlobUrl);
|
||||||
|
this.stlBlobUrl = null;
|
||||||
|
this.enableItems();
|
||||||
|
if(this.onchange) this.onchange();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
generateStl: 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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
150
processfile.html
150
processfile.html
|
@ -8,9 +8,6 @@
|
||||||
|
|
||||||
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;
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre, code, textarea {
|
pre, code, textarea {
|
||||||
|
@ -25,9 +22,6 @@ pre, textarea {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
textarea {
|
|
||||||
height: 200px;
|
|
||||||
}
|
|
||||||
textarea:focus {
|
textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
@ -55,90 +49,27 @@ textarea:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
a { color: inherit; }
|
a { color: inherit; }
|
||||||
.viewer { width: 200px; height: 200px; background: #EEE url(images.png); }
|
|
||||||
#combined .viewer { width: 150px; height: 150px; }
|
|
||||||
table { border-collapse: collapse; margin: 0 auto; }
|
|
||||||
td { padding: 5px; text-align: center; }
|
|
||||||
td code { background: none; border: none; color: inherit; }
|
|
||||||
canvas { cursor: move; }
|
|
||||||
#errdiv {
|
|
||||||
border: 2px solid red;
|
|
||||||
display: none;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#needchrome {
|
canvas { cursor: move; }
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
var gViewer=null;
|
|
||||||
var gSolid=new CSG();
|
|
||||||
|
|
||||||
var gCurrentFile = null;
|
var gCurrentFile = null;
|
||||||
|
|
||||||
function isChrome()
|
var gProcessor=null;
|
||||||
{
|
|
||||||
return (navigator.userAgent.search("Chrome") >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onload()
|
function onload()
|
||||||
{
|
{
|
||||||
var needchromediv = document.getElementById("needchrome");
|
|
||||||
if(needchromediv)
|
|
||||||
{
|
|
||||||
if(!isChrome())
|
|
||||||
{
|
|
||||||
needchromediv.style.display="block";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var containerelement = document.getElementById("viewer");
|
gProcessor = new OpenJsCad.Processor(document.getElementById("viewer"));
|
||||||
gViewer = new OpenJsCad.Viewer(containerelement, 600, 600, 50);
|
|
||||||
setupDragDrop();
|
setupDragDrop();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e.toString());
|
alert(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSolid()
|
|
||||||
{
|
|
||||||
if(gViewer.supported())
|
|
||||||
{
|
|
||||||
var code=document.getElementById('code').value;
|
|
||||||
if(code != gSolidSource)
|
|
||||||
{
|
|
||||||
gSolidSource=code;
|
|
||||||
var errtxt = "";
|
|
||||||
var csg = new CSG();
|
|
||||||
try {
|
|
||||||
csg = OpenJsCad.javaScriptToSolid(code);
|
|
||||||
} catch (e) {
|
|
||||||
errtxt = 'Error: <code>' + e.toString().replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') + '</code>';
|
|
||||||
}
|
|
||||||
gSolid = csg;
|
|
||||||
var errdiv=document.getElementById('error');
|
|
||||||
errdiv.innerHTML = errtxt;
|
|
||||||
errdiv.style.display = (errtxt == "")? "none":"block";
|
|
||||||
gViewer.setCsg(gSolid);
|
|
||||||
var stlarea = document.getElementById('stloutput');
|
|
||||||
stlarea.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStl()
|
|
||||||
{
|
|
||||||
updateSolid();
|
|
||||||
var stl=gSolid.toStlString();
|
|
||||||
var stlarea = document.getElementById('stloutput');
|
|
||||||
stlarea.value=stl;
|
|
||||||
stlarea.style.display = "inline";
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupDragDrop()
|
function setupDragDrop()
|
||||||
{
|
{
|
||||||
// Check for the various File API support.
|
// Check for the various File API support.
|
||||||
|
@ -206,8 +137,6 @@ function fileChanged()
|
||||||
|
|
||||||
function parseFile()
|
function parseFile()
|
||||||
{
|
{
|
||||||
setErrorText("");
|
|
||||||
gSolid=new CSG();
|
|
||||||
if(gCurrentFile)
|
if(gCurrentFile)
|
||||||
{
|
{
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
@ -215,78 +144,45 @@ function parseFile()
|
||||||
var txt = evt.target.result;
|
var txt = evt.target.result;
|
||||||
};
|
};
|
||||||
reader.onloadend = function(evt) {
|
reader.onloadend = function(evt) {
|
||||||
|
try
|
||||||
|
{
|
||||||
if (evt.target.readyState == FileReader.DONE)
|
if (evt.target.readyState == FileReader.DONE)
|
||||||
{
|
{
|
||||||
var jscadscript = evt.target.result;
|
var jscadscript = evt.target.result;
|
||||||
if(jscadscript == "")
|
if(jscadscript == "")
|
||||||
{
|
{
|
||||||
alert("Could not read file. This may be due to security restrictions: in Google Chrome the File API only works if this html page is on a web server. Accessing this page from your hard drive does not work.");
|
throw new Error("Could not read file. This may be due to security restrictions: in Google Chrome the File API only works if this html page is on a web server. Accessing this page from your hard drive does not work.");
|
||||||
gViewer.setCsg(gSolid);
|
if(gProcessor) gProcessor.clearViewer();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parseJscad(jscadscript);
|
if(gProcessor)
|
||||||
|
{
|
||||||
|
gProcessor.setJsCad(jscadscript);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
alert("Failed to read file");
|
throw new Error("Failed to read file");
|
||||||
gViewer.setCsg(gSolid);
|
if(gProcessor) gProcessor.clearViewer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
alert(e.toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(gCurrentFile, "UTF-8");
|
reader.readAsText(gCurrentFile, "UTF-8");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseJscad(script)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var csg = OpenJsCad.javaScriptToSolid(script);
|
|
||||||
gSolid = csg;
|
|
||||||
gViewer.setCsg(gSolid);
|
|
||||||
setErrorText("");
|
|
||||||
}
|
|
||||||
catch(e)
|
|
||||||
{
|
|
||||||
gSolid = new CSG();
|
|
||||||
gViewer.setCsg(gSolid);
|
|
||||||
setErrorText(e.toString());
|
|
||||||
}
|
|
||||||
enableItems();
|
|
||||||
var stlarea = document.getElementById('stloutput');
|
|
||||||
stlarea.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableItems()
|
|
||||||
{
|
|
||||||
var hassolid = (gSolid.polygons.length > 0);
|
|
||||||
document.getElementById('getstlbutton').style.display = hassolid? "inline":"none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function setErrorText(errtxt)
|
|
||||||
{
|
|
||||||
var errdiv = document.getElementById("errdiv");
|
|
||||||
errdiv.style.display = (errtxt == "")? "none":"block";
|
|
||||||
errdiv.innerHTML = errtxt;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStl()
|
|
||||||
{
|
|
||||||
var stl=gSolid.toStlString();
|
|
||||||
var stlarea = document.getElementById('stloutput');
|
|
||||||
stlarea.value=stl;
|
|
||||||
stlarea.style.display = "inline";
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<title>OpenJsCad parser</title>
|
<title>OpenJsCad parser</title>
|
||||||
<body onload="onload()">
|
<body onload="onload()">
|
||||||
<h1>OpenJsCad parser</h1>
|
<h1>OpenJsCad parser</h1>
|
||||||
<div id="needchrome">Please note: OpenJsCad currently only runs reliably on Google Chrome!</div>
|
<div id="viewer"></div>
|
||||||
<div id="viewer" class="viewer" style="background-image:none;width:600px;height:600px;"></div>
|
|
||||||
<br>
|
<br>
|
||||||
<div id="errdiv"></div>
|
|
||||||
<div id="filedropzone">
|
<div id="filedropzone">
|
||||||
<div id="filedropzone_empty">Drop your .jscad file here</div>
|
<div id="filedropzone_empty">Drop your .jscad file here</div>
|
||||||
<div id="filedropzone_filled">
|
<div id="filedropzone_filled">
|
||||||
|
@ -297,23 +193,23 @@ function getStl()
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="stloutput" readonly style="width: 80%; height: 100px; display: none;"></textarea><br>
|
|
||||||
<br>
|
<br>
|
||||||
<h2>Instructions:</h2>
|
<h2>Instructions:</h2>
|
||||||
Create a new file in your favorite text editor. To get started enter the following text:
|
Create a new file in your favorite text editor. To get started enter the following text:
|
||||||
<br>
|
<br>
|
||||||
<pre>
|
<pre>function main() {
|
||||||
var cube = CSG.roundedCube({radius: 10, roundradius: 2, resolution: 16});
|
var cube = CSG.roundedCube({radius: 10, roundradius: 2, resolution: 16});
|
||||||
var sphere = CSG.sphere({radius: 10, resolution: 16}).translate([5, 5, 5]);
|
var sphere = CSG.sphere({radius: 10, resolution: 16}).translate([5, 5, 5]);
|
||||||
return cube.union(sphere);
|
return cube.union(sphere);
|
||||||
|
}
|
||||||
</pre>
|
</pre>
|
||||||
Save this to a file on your desktop with .jscad extension. Then drag & drop the file from your desktop
|
Save this to a file on your desktop with .jscad extension. Then drag & drop the file from your desktop
|
||||||
to the box above (marked with 'drop your .jscad file here).<br><br>
|
to the box above (marked with 'drop your .jscad file here).<br><br>
|
||||||
The 3d model should now appear. You can continue to make changes to your .jscad file. Just press Reload
|
The 3d model should now appear. You can continue to make changes to your .jscad file. Just press Reload
|
||||||
to parse the changes, you do not need to drag & drop the file again.
|
to parse the changes, you do not need to drag & drop the file again.
|
||||||
<br><br>
|
<br><br>
|
||||||
When finished press Get STL to generate the STL file. Copy & paste the STL text to a file with
|
When finished press Generate STL to generate the STL file. Then click Save STL and save it to
|
||||||
.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>.
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in a new issue