Speed optimizations, added setColor() method, added math documentation

This commit is contained in:
Joost Nieuwenhuijse 2012-02-16 13:22:56 +01:00
parent 858a40bb41
commit 5f147dee59
3 changed files with 231 additions and 46 deletions

46
csg.js
View file

@ -105,7 +105,9 @@ CSG.fromObject = function(obj) {
var polygons = obj.polygons.map( function(p) { var polygons = obj.polygons.map( function(p) {
return CSG.Polygon.fromObject(p); return CSG.Polygon.fromObject(p);
}); });
return CSG.fromPolygons(polygons); var csg = CSG.fromPolygons(polygons);
csg = csg.canonicalized();
return csg;
}; };
CSG.prototype = { CSG.prototype = {
@ -234,6 +236,7 @@ CSG.prototype = {
var newpolygons = this.polygons.map(function(p) { return p.transform(matrix4x4); } ); var newpolygons = this.polygons.map(function(p) { return p.transform(matrix4x4); } );
var result=CSG.fromPolygons(newpolygons); var result=CSG.fromPolygons(newpolygons);
result.properties = this.properties._transform(matrix4x4); result.properties = this.properties._transform(matrix4x4);
result.isRetesselated = this.isRetesselated;
return result; return result;
}, },
@ -324,6 +327,7 @@ CSG.prototype = {
var factory = new CSG.fuzzyCSGFactory(); var factory = new CSG.fuzzyCSGFactory();
var result = factory.getCSG(this); var result = factory.getCSG(this);
result.isCanonicalized = true; result.isCanonicalized = true;
result.isRetesselated = this.isRetesselated;
result.properties = this.properties; // keep original properties result.properties = this.properties; // keep original properties
return result; return result;
} }
@ -336,10 +340,13 @@ CSG.prototype = {
} }
else else
{ {
var csg=this; //.canonicalized(); var csg=this.canonicalized();
var polygonsPerPlane = {}; var polygonsPerPlane = {};
csg.polygons.map(function(polygon) { csg.polygons.map(function(polygon) {
var planetag = polygon.plane.getTag(); var planetag = polygon.plane.getTag();
var sharedtag = null;
if(polygon.shared !== undefined) sharedtag = polygon.shared;
planetag += "/"+JSON.stringify(sharedtag);
if(! (planetag in polygonsPerPlane) ) if(! (planetag in polygonsPerPlane) )
{ {
polygonsPerPlane[planetag] = []; polygonsPerPlane[planetag] = [];
@ -363,6 +370,7 @@ CSG.prototype = {
} }
var result = CSG.fromPolygons(destpolygons); var result = CSG.fromPolygons(destpolygons);
result.isRetesselated = true; result.isRetesselated = true;
result.isCanonicalized = true;
result.properties = this.properties; // keep original properties result.properties = this.properties; // keep original properties
return result; return result;
} }
@ -447,7 +455,27 @@ CSG.prototype = {
connectTo: function(myConnector, otherConnector, mirror, normalrotation) { connectTo: function(myConnector, otherConnector, mirror, normalrotation) {
var matrix = myConnector.getTransformationTo(otherConnector, mirror, normalrotation); var matrix = myConnector.getTransformationTo(otherConnector, mirror, normalrotation);
return this.transform(matrix); return this.transform(matrix);
},
// set the .shared property of all polygons
// Returns a new CSG solid, the original is unmodified!
setShared: function(shared) {
var polygons = this.polygons.map( function(p) {
return new CSG.Polygon(p.vertices, shared, p.plane);
});
var result = CSG.fromPolygons(polygons);
result.properties = this.properties; // keep original properties
result.isRetesselated = this.isRetesselated;
result.isCanonicalized = this.isCanonicalized;
return result;
}, },
setColor: function(red,green,blue) {
var newshared = {
color: [red, green, blue],
};
return this.setShared(newshared);
},
}; };
@ -1337,7 +1365,7 @@ CSG.Plane.prototype = {
return result; return result;
}, },
// returns CSG.Point3D // returns CSG.Vector3D
intersectWithLine: function(line3d) { intersectWithLine: function(line3d) {
return line3d.intersectWithPlane(this); return line3d.intersectWithPlane(this);
}, },
@ -1403,7 +1431,7 @@ CSG.Polygon.fromObject = function(obj) {
var vertices = obj.vertices.map(function(v) { var vertices = obj.vertices.map(function(v) {
return CSG.Vertex.fromObject(v); return CSG.Vertex.fromObject(v);
}); });
var shared = null; var shared = obj.shared;
var plane = CSG.Plane.fromObject(obj.plane); var plane = CSG.Plane.fromObject(obj.plane);
return new CSG.Polygon(vertices, shared, plane); return new CSG.Polygon(vertices, shared, plane);
}; };
@ -1439,7 +1467,7 @@ CSG.Polygon.prototype = {
sidefacepoints.push(polygon2.vertices[i].pos); sidefacepoints.push(polygon2.vertices[i].pos);
sidefacepoints.push(polygon2.vertices[nexti].pos); sidefacepoints.push(polygon2.vertices[nexti].pos);
sidefacepoints.push(polygon1.vertices[nexti].pos); sidefacepoints.push(polygon1.vertices[nexti].pos);
var sidefacepolygon=CSG.Polygon.createFromPoints(sidefacepoints); var sidefacepolygon=CSG.Polygon.createFromPoints(sidefacepoints, this.shared);
newpolygons.push(sidefacepolygon); newpolygons.push(sidefacepolygon);
} }
polygon2 = polygon2.flipped(); polygon2 = polygon2.flipped();
@ -2435,6 +2463,9 @@ CSG.Polygon2D.prototype = {
var offsetvector = CSG.parseOptionAs3DVector(options, "offset", [0,0,1]); var offsetvector = CSG.parseOptionAs3DVector(options, "offset", [0,0,1]);
var twistangle = CSG.parseOptionAsFloat(options, "twistangle", 0); var twistangle = CSG.parseOptionAsFloat(options, "twistangle", 0);
var twiststeps = CSG.parseOptionAsInt(options, "twiststeps", 10); var twiststeps = CSG.parseOptionAsInt(options, "twiststeps", 10);
if(twistangle == 0) twiststeps = 1;
if(twiststeps < 1) twiststeps = 1;
// create the polygons: // create the polygons:
var newpolygons = []; var newpolygons = [];
@ -2784,6 +2815,7 @@ CSG.reTesselateCoplanarPolygons = function(sourcepolygons, destpolygons)
if(numpolygons > 0) if(numpolygons > 0)
{ {
var plane = sourcepolygons[0].plane; var plane = sourcepolygons[0].plane;
var shared = sourcepolygons[0].shared;
var orthobasis = new CSG.OrthoNormalBasis(plane); var orthobasis = new CSG.OrthoNormalBasis(plane);
var polygonvertices2d = []; // array of array of CSG.Vector2D var polygonvertices2d = []; // array of array of CSG.Vector2D
var polygontopvertexindexes = []; // array of indexes of topmost vertex per polygon var polygontopvertexindexes = []; // array of indexes of topmost vertex per polygon
@ -3108,7 +3140,6 @@ CSG.reTesselateCoplanarPolygons = function(sourcepolygons, destpolygons)
var vertex3d = new CSG.Vertex(point3d); var vertex3d = new CSG.Vertex(point3d);
vertices3d.push(vertex3d); vertices3d.push(vertex3d);
}); });
var shared = null;
var polygon = new CSG.Polygon(vertices3d, shared, plane); var polygon = new CSG.Polygon(vertices3d, shared, plane);
destpolygons.push(polygon); destpolygons.push(polygon);
} }
@ -3661,8 +3692,9 @@ CSG.Path2D.prototype = {
var offsetvector = [0, 0, height]; var offsetvector = [0, 0, height];
polygon2ds.map(function(polygon) { polygon2ds.map(function(polygon) {
var csg = polygon.extrude({offset: offsetvector}); var csg = polygon.extrude({offset: offsetvector});
result = result.union(csg); result = result.unionSub(csg, false, false);
}); });
result = result.reTesselated().canonicalized();
return result; return result;
}, },

View file

@ -105,10 +105,11 @@ Some of the benefits:
<li>Runs in your browser, no need to install any software.</li> <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, <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> 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>JavaScript is an extremely flexible language, supporting dynamic arrays, object oriented programming, closures, anonymous functions and more</li>
<li>Solids are stored in variables. This allows for example conditional cloning of objects, something which is nearly impossible in OpenSCAD.</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 <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> points, even if you don't know the actual orientation or size.</li>
<li>Extensive built in <a href="#math">support for 2D and 3D math</a> (classes for Vector2D, Vector3D, Plane, Line3D, Line2D)</li>
</ul> </ul>
<h2>Viewer navigation</h2> <h2>Viewer navigation</h2>
Click and drag to rotate the model around the origin.<br> Click and drag to rotate the model around the origin.<br>
@ -191,7 +192,8 @@ var csg3 = cube.subtract(sphere);
</pre> </pre>
<h2>Transformations</h2> <h2>Transformations</h2>
Solids can be translated, scaled and rotated. Multiple transforms can be combined into a single matrix transform: Solids can be translated, scaled and rotated. Multiple transforms can be combined into a single matrix transform.
For <a href="#math">matrix and vector math see below</a>.
<pre> <pre>
var cube = CSG.cube(); var cube = CSG.cube();
@ -219,7 +221,7 @@ var cube3 = cube.transform(m);
</pre> </pre>
<h2>Mirroring</h2> <h2>Mirroring</h2>
Solids can be mirrored in any plane in 3D space: Solids can be mirrored in any plane in 3D space. For <a href="#math">plane math see below</a>.
<pre> <pre>
var cube = CSG.cube().translate([1,0,0]); var cube = CSG.cube().translate([1,0,0]);
@ -559,5 +561,129 @@ function main(params) {
</pre> </pre>
Or see the <a href="gearsdemo.html">Gears demo</a> for another example of interactive parameters. Or see the <a href="gearsdemo.html">Gears demo</a> for another example of interactive parameters.
<h2>Miscellaneous</h2>
Solids can be given a color using the setColor(r, g, b) function. Beware: this returns a new solid,
the original solid is not modified! Faces of the solid will keep their original color when doing
CSG operations (union() etc). Colors values range between 0.0 and 1.0 (not 255).
<pre>
var cube1 = CSG.cube({radius: 10});
cube1 = cube1.setColor(0.5, 0, 0);
var cube2 = CSG.cube({radius: 10});
cube2 = cube2.setColor(0, 0.5, 0);
cube2 = cube2.translate([5,1,4]);
var result = cube1.subtract(cube2);
// the resulting solid will have faces with 2 different colors
</pre>
<h2><a name="math"></a>2D and 3D Math</h2>
There are utility classes for many 2D and 3D operations. Below is a quick summary, for details
view the source of csg.js:
<pre>
// --------- Vector3D ---------------------
var vec1 = new CSG.Vector3D(1,2,3); // 3 arguments
var vec2 = new CSG.Vector3D( [1,2,3] ); // 1 array argument
var vec3 = new CSG.Vector3D(vec2); // cloning a vector
// vector math. All operations return a new vector, the original is unmodified!
vec.negated()
vec.abs()
vec.plus(othervector)
vec.minus(othervector)
vec.times(3.0)
vec.dividedBy(-5)
vec.dot(othervector)
vec.lerp(othervector, t) // linear interpolation (0 <= t <= 1)
vec.length()
vec.lengthSquared() // == vec.length()^2
vec.unit()
vec.cross(othervector) // cross product: returns a vector perpendicular to both
vec.distanceTo(othervector)
vec.distanceToSquared(othervector) // == vec.distanceTo(othervector)^2
vec.equals(othervector)
vec.multiply4x4(matrix4x4) // right multiply by a 4x4 matrix
// --------- Vector2D ---------------------
var vec1 = new CSG.Vector2D(1,2); // 2 arguments
var vec2 = new CSG.Vector2D( [1,2] ); // 1 array argument
var vec3 = new CSG.Vector2D(vec2); // cloning a vector
// vector math. All operations return a new vector, the original is unmodified!
vec.negated()
vec.abs()
vec.plus(othervector)
vec.minus(othervector)
vec.times(3.0)
vec.dividedBy(-5)
vec.dot(othervector)
vec.lerp(othervector, t) // linear interpolation (0 <= t <= 1)
vec.length()
vec.unit()
vec.normal() // returns a 90 degree clockwise rotated vector
vec.distanceTo(othervector)
vec.equals(othervector)
vec.multiply4x4(matrix4x4) // right multiply by a 4x4 matrix
vec.toVector3D(z) // convert to a vector3D by adding a z coordinate
vec.angleDegrees() // returns the angle of the vector: [1,0] = 0 degrees, [0, 1] = 90 degrees, etc
vec.angleRadians() // ditto in radians
var vec = CSG.Vector2D.fromAngleDegrees(degrees); // returns a vector at the specified angle
var vec = CSG.Vector2D.fromAngleRadians(radians); // returns a vector at the specified angle
// --------- Matrix4x4 ---------------------
var m1 = new CSG.Matrix4x4(); // unity matrix
var m2 = new CSG.Matrix4x4( [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] );
// elements are passed in row order
var result = m1.plus(m2);
var result = m1.minus(m2);
var result = m1.multiply(m2);
// matrix vector multiplication (vectors are padded with zeroes to get a 4x1 vector):
var vec3d = m1.rightMultiply1x3Vector(vec3d); // matrix * vector
var vec3d = m1.leftMultiply1x3Vector(vec3d); // vector * matrix
var vec2d = m1.rightMultiply1x2Vector(vec2d); // matrix * vector
var vec2d = m1.leftMultiply1x2Vector(vec2d); // vector * matrix
// common transformation matrices:
var m = CSG.Matrix4x4.rotationX(degrees); // matrix for rotation about X axis
var m = CSG.Matrix4x4.rotationY(degrees); // matrix for rotation about Y axis
var m = CSG.Matrix4x4.rotationZ(degrees); // matrix for rotation about Z axis
var m = CSG.Matrix4x4.translation(vec3d); // translation
var m = CSG.Matrix4x4.scaling(vec3d); // scale
var m = CSG.Matrix4x4.mirroring(plane); // mirroring in a plane; the argument must be a CSG.Plane
// matrix transformations can be concatenated:
var transform = CSG.Matrix4x4.rotationX(20).multiply(CSG.Matrix4x4.rotationY(30));
// Use a CSG solid's transform() method to apply the transformation to a CSG solid
// ------------ Plane --------------------------
// a 3D plane is represented by a normal vector (should have unit length) and a distance from the origin w
// the plane passes through normal.times(w)
var plane1 = new CSG.Plane(normal, w);
// Or we can construct a plane from 3 points:
var plane2 = CSG.Plane.fromPoints(p1, p2, p3);
// Or from a normal vector and 1 point:
var plane3 = CSG.Plane.fromNormalAndPoint(normal, point);
// Flip a plane (front side becomes back side):
var plane4 = plane3.flipped();
// Apply transformations (rotation, scaling, translation):
var transformed = plane3.transformed(matrix4x4); // argument is a CSG.Matrix4x4
// Intersection of plane and 3d line:
var point = plane3.intersectWithLine(line); // argument is CSG.Line3D, returns a CSG.Vector3D
// Intersection of 2 planes:
var line = plane3.intersectWithPlane(plane); // argument is another CSG.Plane, returns a CSG.Line3D
// Distance to point:
var w = signedDistanceToPoint(point); // argument is CSG.Vector3D, returns a float (positive
// if in front of plane, negative if in back)
// ------------ Line3D --------------------------
// A line in 3d space is represented by a point and a direction vector.
// Direction should be a unit vector. Point can be any point on the line:
var line = new CSG.Line3D(point, direction); // argumenst are CSG.Vector3D
// or by giving two points:
var line = CSG.Line3D.fromPoints(p1, p2); // argumenst are CSG.Vector3D
var point = intersectWithPlane(plane); // == plane.intersectWithLine(this)
var line2 = line.reverse(); // same line but reverse direction
var line2 = line.transform(matrix4x4); // for rotation, scaling, etc
var p = line.closestPointOnLine(point); // project point onto the line
var d = line.distanceToPoint(point);
</pre>
</body> </body>
</html> </html>

View file

@ -169,6 +169,11 @@ OpenJsCad.Viewer.csgToMesh = function(csg) {
for(var polygonindex = 0; polygonindex < numpolygons; polygonindex++) for(var polygonindex = 0; polygonindex < numpolygons; polygonindex++)
{ {
var polygon = polygons[polygonindex]; var polygon = polygons[polygonindex];
var color = [0,0,1];
if(polygon.shared && polygon.shared.color)
{
color = polygon.shared.color;
}
var indices = polygon.vertices.map(function(vertex) { var indices = polygon.vertices.map(function(vertex) {
var vertextag = vertex.getTag(); var vertextag = vertex.getTag();
var vertexindex; var vertexindex;
@ -181,7 +186,7 @@ OpenJsCad.Viewer.csgToMesh = function(csg) {
vertexindex = vertices.length; vertexindex = vertices.length;
vertexTag2Index[vertextag] = vertexindex; vertexTag2Index[vertextag] = vertexindex;
vertices.push([vertex.pos.x, vertex.pos.y, vertex.pos.z]); vertices.push([vertex.pos.x, vertex.pos.y, vertex.pos.z]);
colors.push([0,0,1]); colors.push(color);
} }
return vertexindex; return vertexindex;
}); });
@ -254,22 +259,29 @@ OpenJsCad.runMainInWorker = function(mainParameters)
} }
catch(e) catch(e)
{ {
var errtxt = e.stack; var errtxt = e.stack;
if(!errtxt)
{
errtxt = e.toString();
}
self.postMessage({cmd: 'error', err: errtxt}); self.postMessage({cmd: 'error', err: errtxt});
} }
}; };
OpenJsCad.javaScriptToSolidSync = function(script, mainParameters, callback) { OpenJsCad.javaScriptToSolidSync = function(script, mainParameters, debugging) {
var workerscript = ""; var workerscript = "";
workerscript += script; workerscript += script;
workerscript += "\n\n\n\n\n\n\n/* -------------------------------------------------------------------------\n"; if(debugging)
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 += "\n\n\n\n\n\n\n/* -------------------------------------------------------------------------\n";
workerscript += "Click on a line number to set or clear a breakpoint\n"; workerscript += "OpenJsCad debugging\n\nAssuming you are running Chrome:\nF10 steps over an instruction\nF11 steps into an instruction\n";
workerscript += "For more information see: http://code.google.com/chrome/devtools/docs/overview.html\n\n"; workerscript += "F8 continues running\nPress the (||) button at the bottom to enable pausing whenever an error occurs\n";
workerscript += "------------------------------------------------------------------------- */\n"; workerscript += "Click on a line number to set or clear a breakpoint\n";
workerscript += "\n\n// Now press F11 twice to enter your main() function:\n\n"; workerscript += "For more information see: http://code.google.com/chrome/devtools/docs/overview.html\n\n";
workerscript += "debugger;\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)+");"; workerscript += "return main("+JSON.stringify(mainParameters)+");";
var f = new Function(workerscript); var f = new Function(workerscript);
var csg = f(); var csg = f();
@ -341,6 +353,7 @@ OpenJsCad.textToBlobUrl = function(txt) {
if(window.URL) blobURL = window.URL.createObjectURL(blob) if(window.URL) blobURL = window.URL.createObjectURL(blob)
else if(window.webkitURL) blobURL = window.webkitURL.createObjectURL(blob) else if(window.webkitURL) blobURL = window.webkitURL.createObjectURL(blob)
else throw new Error("Your browser doesn't support window.URL"); else throw new Error("Your browser doesn't support window.URL");
if(!blobURL) throw new Error("createObjectURL() failed");
return blobURL; return blobURL;
}; };
@ -638,11 +651,41 @@ OpenJsCad.Processor.prototype = {
this.enableItems(); this.enableItems();
var that = this; var that = this;
var paramValues = this.getParamValues(); var paramValues = this.getParamValues();
if(this.debugging) var useSync = this.debugging;
if(!useSync)
{ {
try try
{ {
var csg = OpenJsCad.javaScriptToSolidSync(this.script, paramValues); 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();
});
}
catch(e)
{
useSync = true;
}
}
if(useSync)
{
try
{
var csg = OpenJsCad.javaScriptToSolidSync(this.script, paramValues, this.debugging);
that.processing = false; that.processing = false;
that.solid = csg; that.solid = csg;
if(that.viewer) that.viewer.setCsg(csg); if(that.viewer) that.viewer.setCsg(csg);
@ -652,33 +695,17 @@ OpenJsCad.Processor.prototype = {
catch(e) catch(e)
{ {
that.processing = false; that.processing = false;
that.setError(e.toString()); var errtxt = e.stack;
if(!errtxt)
{
errtxt = e.toString();
}
that.setError(errtxt);
that.statusspan.innerHTML = "Error."; that.statusspan.innerHTML = "Error.";
} }
that.enableItems(); that.enableItems();
if(that.onchange) that.onchange(); if(that.onchange) that.onchange();
} }
else
{
this.worker = OpenJsCad.javaScriptToSolidASync(this.script, paramValues, function(err, csg) {
that.processing = false;
that.worker = null;
if(err)
{
that.setError(err);
that.statusspan.innerHTML = "Error.";
}
else
{
that.solid = csg;
if(that.viewer) that.viewer.setCsg(csg);
that.validcsg = true;
that.statusspan.innerHTML = "Ready.";
}
that.enableItems();
if(that.onchange) that.onchange();
});
}
}, },
hasSolid: function() { hasSolid: function() {