2012-01-20 15:52:48 +01:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
|
|
<html><head>
|
|
|
|
<script src="lightgl.js"></script>
|
|
|
|
<script src="csg.js"></script>
|
2012-01-21 15:18:57 +01:00
|
|
|
<script src="openjscad.js"></script>
|
2012-01-20 15:52:48 +01:00
|
|
|
<style>
|
|
|
|
|
|
|
|
body {
|
|
|
|
font: 14px/20px 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
|
|
max-width: 800px;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:28:19 +01:00
|
|
|
//h1, h2 { font: bold 50px/50px 'Helvetica Neue', Helvetica, Arial; }
|
|
|
|
//h2 { font-size: 30px; margin: 10px 0 0 0; }
|
2012-01-20 15:52:48 +01:00
|
|
|
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; }
|
|
|
|
|
2012-01-21 17:28:19 +01:00
|
|
|
#needchrome {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
2012-01-20 15:52:48 +01:00
|
|
|
</style>
|
|
|
|
<script>
|
|
|
|
|
|
|
|
var gViewer=null;
|
|
|
|
var gSolid=new CSG();
|
|
|
|
var gSolidSource="";
|
|
|
|
|
2012-01-20 16:09:04 +01:00
|
|
|
function isChrome()
|
|
|
|
{
|
|
|
|
return (navigator.userAgent.search("Chrome") >= 0);
|
|
|
|
}
|
|
|
|
|
2012-01-20 15:52:48 +01:00
|
|
|
function onload()
|
|
|
|
{
|
2012-01-21 17:28:19 +01:00
|
|
|
var needchromediv = document.getElementById("needchrome");
|
|
|
|
if(needchromediv)
|
|
|
|
{
|
|
|
|
if(!isChrome())
|
|
|
|
{
|
|
|
|
needchromediv.style.display="block";
|
|
|
|
}
|
|
|
|
}
|
2012-01-21 15:18:57 +01:00
|
|
|
var containerelement = document.getElementById("viewer");
|
|
|
|
gViewer = new OpenJsCad.Viewer(containerelement, 600, 600, 50);
|
|
|
|
updateSolid();
|
2012-01-20 15:52:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function updateSolid()
|
|
|
|
{
|
2012-01-21 15:18:57 +01:00
|
|
|
if(gViewer.supported())
|
2012-01-20 15:52:48 +01:00
|
|
|
{
|
2012-01-21 15:18:57 +01:00
|
|
|
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>';
|
2012-01-20 15:52:48 +01:00
|
|
|
}
|
2012-01-21 15:18:57 +01:00
|
|
|
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";
|
2012-01-20 15:52:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getStl()
|
|
|
|
{
|
|
|
|
updateSolid();
|
|
|
|
var stl=gSolid.toStlString();
|
2012-01-21 15:18:57 +01:00
|
|
|
var stlarea = document.getElementById('stloutput');
|
|
|
|
stlarea.value=stl;
|
|
|
|
stlarea.style.display = "inline";
|
2012-01-20 15:52:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
<title>OpenJsCad</title>
|
|
|
|
</head>
|
|
|
|
<body onload="onload()">
|
|
|
|
<h1>OpenJsCad</h1>
|
2012-01-21 17:28:19 +01:00
|
|
|
<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.
|
2012-01-20 15:52:48 +01:00
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<td><div id="viewer" class="viewer" style="background-image:none;width:600px;height:600px;"></div></td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
2012-01-21 17:28:19 +01:00
|
|
|
<h2>Playground</h2>
|
|
|
|
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
|
|
|
|
and use the <a href="processfile.html"><b>OpenJsCad parser</b></a>.
|
|
|
|
<br><br>
|
|
|
|
<textarea id="code">var resolution = 16; // increase to get smoother corners (will get slow!)
|
2012-01-20 15:52:48 +01:00
|
|
|
|
2012-01-20 16:36:55 +01:00
|
|
|
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 sphere2 = sphere1.translate([12, 5, 0]);
|
|
|
|
var sphere3 = CSG.sphere({center: [20, 0, 0], radius: 30, resolution: resolution });
|
2012-01-20 15:52:48 +01:00
|
|
|
|
|
|
|
var result = cube1;
|
|
|
|
result = result.union(sphere1);
|
|
|
|
result = result.subtract(sphere2);
|
|
|
|
result = result.intersect(sphere3);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
</textarea>
|
|
|
|
|
|
|
|
<p id="error"></p>
|
|
|
|
<br>
|
2012-01-21 15:18:57 +01:00
|
|
|
<input type="submit" value="Update Preview" onclick="updateSolid(); return false;">
|
2012-01-20 15:52:48 +01:00
|
|
|
<input type="submit" value="Get STL" onclick="getStl(); return false;"><br>
|
2012-01-21 15:18:57 +01:00
|
|
|
<textarea id="stloutput" readonly style="width: 80%; height: 100px; display: none;"></textarea>
|
2012-01-20 15:52:48 +01:00
|
|
|
<br>
|
|
|
|
<h1>About</h1>
|
|
|
|
This is intended to become a Javascript based alternative to <a href="http://www.openscad.org/">OpenSCAD</a>,
|
|
|
|
for 3D solid modeling.
|
|
|
|
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.
|
2012-01-21 17:28:19 +01:00
|
|
|
The code should always end in a return statement, returning a CSG solid.
|
2012-01-20 15:52:48 +01:00
|
|
|
<br><br>
|
2012-01-21 17:28:19 +01:00
|
|
|
To build your own modes, 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,
|
|
|
|
ready to be printed on your 3d printer.
|
2012-01-20 15:52:48 +01:00
|
|
|
<h2>License</h2>
|
|
|
|
Copyright (c) 2012 Joost Nieuwenhuijse.
|
|
|
|
Uses CSG.js, <a href="https://github.com/evanw/csg.js">original</a> copyright (c) 2011 Evan Wallace,
|
|
|
|
<a href="https://github.com/joostn/csg.js">extensively modified</a>, copyright (c) 2012 Joost Nieuwenhuijse.
|
|
|
|
Uses <a href="https://github.com/evanw/lightgl.js">lightgl.js</a> by Evan Wallace for WebGL rendering.
|
|
|
|
All code released under MIT license.
|
|
|
|
<br><br>
|
2012-01-21 17:28:19 +01:00
|
|
|
Contributions are welcome! It's all written in Javascript, so if you know how to use it you
|
|
|
|
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>,
|
2012-01-20 15:52:48 +01:00
|
|
|
<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>.
|
|
|
|
<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>
|
|
|
|
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
|
|
|
|
for a parameter which expects a 3D vector, it is used for the x, y and z value.
|
|
|
|
In other words: <code>radius: 1</code> will give <code>radius: [1,1,1]</code>.
|
|
|
|
<br><br>
|
|
|
|
All rounded solids have a 'resolution' parameter which controls tesselation. If resolution
|
|
|
|
is set to 8, then 8 polygons per 360 degree of revolution are used. Beware that rendering
|
|
|
|
time will increase dramatically when increasing the resolution. For a sphere the number of polygons
|
|
|
|
increases quadratically with the resolution used.
|
|
|
|
<br><br>
|
|
|
|
<pre>
|
|
|
|
// a cube:
|
|
|
|
var cube = CSG.cube({
|
|
|
|
center: [0, 0, 0],
|
|
|
|
radius: [1, 1, 1]
|
|
|
|
});
|
|
|
|
|
|
|
|
// a sphere:
|
|
|
|
var sphere = CSG.sphere({
|
|
|
|
center: [0, 0, 0],
|
|
|
|
radius: 2, // must be scalar
|
|
|
|
resolution: 32
|
|
|
|
});
|
|
|
|
|
|
|
|
// a cylinder:
|
|
|
|
var cylinder = CSG.cylinder({
|
|
|
|
start: [0, -1, 0],
|
|
|
|
end: [0, 1, 0],
|
|
|
|
radius: 1,
|
|
|
|
resolution: 16
|
|
|
|
});
|
|
|
|
|
|
|
|
// like a cylinder, but with spherical endpoints:
|
|
|
|
var roundedCylinder = CSG.roundedCylinder({
|
|
|
|
start: [0, -1, 0],
|
|
|
|
end: [0, 1, 0],
|
|
|
|
radius: 1,
|
|
|
|
resolution: 16
|
|
|
|
});
|
|
|
|
|
|
|
|
// a rounded cube:
|
|
|
|
var cube = CSG.roundedCube({
|
|
|
|
center: [0, 0, 0],
|
|
|
|
radius: 1,
|
|
|
|
roundradius: 0.2,
|
|
|
|
resolution: 8,
|
|
|
|
});
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
<h2>CSG operations</h2>
|
|
|
|
The 3 standard CSG operations are supported. All CSG operations return a new solid; the source solids
|
|
|
|
are not modified:
|
|
|
|
<pre>
|
|
|
|
var csg1 = cube.union(sphere);
|
|
|
|
var csg2 = cube.intersect(sphere);
|
|
|
|
var csg3 = cube.subtract(sphere);
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
<h2>Transformations</h2>
|
|
|
|
Solids can be translated, scaled and rotated. Multiple transforms can be combined into a single matrix transform:
|
|
|
|
<pre>
|
|
|
|
var cube = CSG.cube();
|
|
|
|
|
|
|
|
// translation:
|
|
|
|
var cube2 = cube.translate([1, 2, 3]);
|
|
|
|
|
|
|
|
// scaling:
|
|
|
|
var largecube = cube.scale(2.0);
|
|
|
|
var stretchedcube = cube.scale([1.5, 1, 0.5]);
|
|
|
|
|
|
|
|
// rotation:
|
|
|
|
var rotated1 = cube.rotateX(-45); // rotate around the X axis
|
|
|
|
var rotated2 = cube.rotateY(90); // rotate around the Y axis
|
|
|
|
var rotated3 = cube.rotateZ(20); // rotate around the Z axis
|
|
|
|
|
|
|
|
// combine multiple transforms into a single matrix transform:
|
|
|
|
var m = new CSG.Matrix4x4();
|
|
|
|
m = m.multiply(CSG.Matrix4x4.rotationX(40));
|
|
|
|
m = m.multiply(CSG.Matrix4x4.rotationZ(40));
|
|
|
|
m = m.multiply(CSG.Matrix4x4.translation([-.5, 0, 0]));
|
|
|
|
m = m.multiply(CSG.Matrix4x4.scaling([1.1, 1.2, 1.3]));
|
|
|
|
|
|
|
|
// and apply the transform:
|
|
|
|
var cube3 = cube.transform(m);
|
|
|
|
</pre>
|
|
|
|
|
2012-01-21 18:56:46 +01:00
|
|
|
<h2>Mirroring</h2>
|
|
|
|
Solids can be mirrored in any plane in 3D space:
|
|
|
|
<pre>
|
|
|
|
var cube = CSG.cube().translate([1,0,0]);
|
|
|
|
|
|
|
|
var cube2 = cube.mirroredX(); // mirrored in the x=0 plane
|
|
|
|
var cube3 = cube.mirroredY(); // mirrored in the y=0 plane
|
|
|
|
var cube4 = cube.mirroredZ(); // mirrored in the z=0 plane
|
|
|
|
|
|
|
|
// create a plane by specifying 3 points:
|
|
|
|
var plane = CSG.Plane.fromPoints([5,0,0], [5, 1, 0], [3, 1, 7]);
|
|
|
|
|
|
|
|
// and mirror in that plane:
|
|
|
|
var cube5 = cube.mirrored(plane);
|
|
|
|
</pre>
|
|
|
|
|
2012-01-21 21:13:54 +01:00
|
|
|
<h2>Cutting by a plane</h2>
|
|
|
|
A solid can be cut by a plane; only the part on the back side is kept:
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
var cube = CSG.cube({radius: 10});
|
|
|
|
|
|
|
|
// create a plane by specifying 3 points:
|
|
|
|
var plane1 = CSG.Plane.fromPoints([5,0,0], [7, 1, 0], [3, 1, 7]);
|
|
|
|
|
|
|
|
// or by specifying a normal and a point on the plane:
|
|
|
|
var plane2 = CSG.Plane.fromNormalAndPoint([3, 1, 2], [5, 0, 0]);
|
|
|
|
|
|
|
|
// and cut by the plane:
|
|
|
|
var part1 = cube.cutByPlane(plane2);
|
|
|
|
|
|
|
|
// or if we need the other half of the cube:
|
|
|
|
var part2 = cube.cutByPlane(plane2.flipped());
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
2012-01-20 15:52:48 +01:00
|
|
|
<h2>Expansion and contraction</h2>
|
|
|
|
Expansion can be seen
|
|
|
|
as the 3D convolution of an object with a sphere. Contraction is the reverse: the area outside the solid
|
|
|
|
is expanded, and this is then subtracted from the solid.
|
|
|
|
<br><br>
|
|
|
|
Expansion and contraction are very powerful ways to get an object with nice smooth corners. For example
|
|
|
|
a rounded cube can be created by expanding a normal cube.
|
|
|
|
<br><br>
|
|
|
|
Note that these are expensive operations: spheroids are created around every corner and edge in the original
|
|
|
|
object, so the number of polygons quickly increases. Expansion and contraction therefore are only practical for simple
|
|
|
|
non-curved objects.
|
|
|
|
<br><br>
|
|
|
|
expand() and contract() take two parameters: the first is the radius of expansion or contraction; the second
|
|
|
|
parameter is optional and specififies the resolution (number of polygons on spherical surfaces, per 360 degree revolution).
|
|
|
|
<pre>
|
|
|
|
var cube1 = CSG.cube({radius: 1.0});
|
|
|
|
var cube2 = CSG.cube({radius: 1.0}).translate([-0.3, -0.3, -0.3]);
|
|
|
|
var csg = cube1.subtract(cube2);
|
|
|
|
var rounded = csg.expand(0.2, 8);
|
|
|
|
</pre>
|
|
|
|
|
2012-01-21 18:03:27 +01:00
|
|
|
<h2>Determining the bounds of an object</h2>
|
|
|
|
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
|
|
|
|
an array with two elements specifying the minimum x,y,z coordinate and the maximum x,y,z coordinate:
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
var cube1 = CSG.cube({radius: 10});
|
|
|
|
var cube2 = CSG.cube({radius: 5});
|
|
|
|
|
|
|
|
// get the right bound of cube1 and the left bound of cube2:
|
|
|
|
var deltax = cube1.getBounds()[1].x - cube2.getBounds()[0].x;
|
|
|
|
|
|
|
|
// align cube2 so it touches cube1:
|
|
|
|
cube2 = cube2.translate([deltax, 0, 0]);
|
|
|
|
|
|
|
|
return cube1.union(cube2);
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
<h2>2D shapes</h2>
|
2012-01-20 15:52:48 +01:00
|
|
|
Two dimensional shapes can be defined through the Polygon2D class. Currently this requires the polygon to be convex
|
|
|
|
(i.e. all corners less than 180 degrees). Shapes can be transformed (rotation, translation, scaling).
|
|
|
|
To actually use the shape it needs to be extruded into a 3D CSG object through the extrude() function. extrude()
|
|
|
|
places the 2D solid onto the z=0 plane, and extrudes in the specified direction. Extrusion can be done with an optional
|
|
|
|
twist. This rotates the solid around the z axis (and not necessariy around the extrusion axis) during extrusion.
|
|
|
|
The total degrees of rotation is specified in the twistangle parameter, and twiststeps determine the number of steps
|
|
|
|
between the bottom and top surface.
|
|
|
|
<pre>
|
|
|
|
// Create a shape; argument is an array of 2D coordinates
|
|
|
|
// The shape must be convex, can be specified in clockwise or in counterclockwise direction
|
|
|
|
var shape2d=new CSG.Polygon2D([[0,0], [5,0], [3,5], [0,5]]);
|
|
|
|
|
|
|
|
// Do some transformations:
|
|
|
|
shape2d=shape2d.translate([-2, -2]);
|
|
|
|
shape2d=shape2d.rotate(20);
|
|
|
|
shape2d=shape2d.scale([0.2, 0.2]);
|
|
|
|
|
|
|
|
// And extrude. This creates a CSG solid:
|
|
|
|
var extruded=shape2d.extrude({
|
|
|
|
offset: [0.5, 0, 2], // direction for extrusion
|
|
|
|
twistangle: 180, // top surface is rotated 180 degrees
|
|
|
|
twiststeps: 100 // create 100 slices
|
|
|
|
});
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|