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) {
return CSG.Polygon.fromObject(p);
});
return CSG.fromPolygons(polygons);
var csg = CSG.fromPolygons(polygons);
csg = csg.canonicalized();
return csg;
};
CSG.prototype = {
@ -234,6 +236,7 @@ CSG.prototype = {
var newpolygons = this.polygons.map(function(p) { return p.transform(matrix4x4); } );
var result=CSG.fromPolygons(newpolygons);
result.properties = this.properties._transform(matrix4x4);
result.isRetesselated = this.isRetesselated;
return result;
},
@ -324,6 +327,7 @@ CSG.prototype = {
var factory = new CSG.fuzzyCSGFactory();
var result = factory.getCSG(this);
result.isCanonicalized = true;
result.isRetesselated = this.isRetesselated;
result.properties = this.properties; // keep original properties
return result;
}
@ -336,10 +340,13 @@ CSG.prototype = {
}
else
{
var csg=this; //.canonicalized();
var csg=this.canonicalized();
var polygonsPerPlane = {};
csg.polygons.map(function(polygon) {
var planetag = polygon.plane.getTag();
var sharedtag = null;
if(polygon.shared !== undefined) sharedtag = polygon.shared;
planetag += "/"+JSON.stringify(sharedtag);
if(! (planetag in polygonsPerPlane) )
{
polygonsPerPlane[planetag] = [];
@ -363,6 +370,7 @@ CSG.prototype = {
}
var result = CSG.fromPolygons(destpolygons);
result.isRetesselated = true;
result.isCanonicalized = true;
result.properties = this.properties; // keep original properties
return result;
}
@ -449,6 +457,26 @@ CSG.prototype = {
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);
},
};
// Parse an option from the options object
@ -1337,7 +1365,7 @@ CSG.Plane.prototype = {
return result;
},
// returns CSG.Point3D
// returns CSG.Vector3D
intersectWithLine: function(line3d) {
return line3d.intersectWithPlane(this);
},
@ -1403,7 +1431,7 @@ CSG.Polygon.fromObject = function(obj) {
var vertices = obj.vertices.map(function(v) {
return CSG.Vertex.fromObject(v);
});
var shared = null;
var shared = obj.shared;
var plane = CSG.Plane.fromObject(obj.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[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);
}
polygon2 = polygon2.flipped();
@ -2436,6 +2464,9 @@ CSG.Polygon2D.prototype = {
var twistangle = CSG.parseOptionAsFloat(options, "twistangle", 0);
var twiststeps = CSG.parseOptionAsInt(options, "twiststeps", 10);
if(twistangle == 0) twiststeps = 1;
if(twiststeps < 1) twiststeps = 1;
// create the polygons:
var newpolygons = [];
@ -2784,6 +2815,7 @@ CSG.reTesselateCoplanarPolygons = function(sourcepolygons, destpolygons)
if(numpolygons > 0)
{
var plane = sourcepolygons[0].plane;
var shared = sourcepolygons[0].shared;
var orthobasis = new CSG.OrthoNormalBasis(plane);
var polygonvertices2d = []; // array of array of CSG.Vector2D
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);
vertices3d.push(vertex3d);
});
var shared = null;
var polygon = new CSG.Polygon(vertices3d, shared, plane);
destpolygons.push(polygon);
}
@ -3661,8 +3692,9 @@ CSG.Path2D.prototype = {
var offsetvector = [0, 0, height];
polygon2ds.map(function(polygon) {
var csg = polygon.extrude({offset: offsetvector});
result = result.union(csg);
result = result.unionSub(csg, false, false);
});
result = result.reTesselated().canonicalized();
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>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>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>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>
<li>Extensive built in <a href="#math">support for 2D and 3D math</a> (classes for Vector2D, Vector3D, Plane, Line3D, Line2D)</li>
</ul>
<h2>Viewer navigation</h2>
Click and drag to rotate the model around the origin.<br>
@ -191,7 +192,8 @@ 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:
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>
var cube = CSG.cube();
@ -219,7 +221,7 @@ var cube3 = cube.transform(m);
</pre>
<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>
var cube = CSG.cube().translate([1,0,0]);
@ -559,5 +561,129 @@ function main(params) {
</pre>
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>
</html>

View file

@ -169,6 +169,11 @@ OpenJsCad.Viewer.csgToMesh = function(csg) {
for(var polygonindex = 0; polygonindex < numpolygons; 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 vertextag = vertex.getTag();
var vertexindex;
@ -181,7 +186,7 @@ OpenJsCad.Viewer.csgToMesh = function(csg) {
vertexindex = vertices.length;
vertexTag2Index[vertextag] = vertexindex;
vertices.push([vertex.pos.x, vertex.pos.y, vertex.pos.z]);
colors.push([0,0,1]);
colors.push(color);
}
return vertexindex;
});
@ -255,13 +260,19 @@ OpenJsCad.runMainInWorker = function(mainParameters)
catch(e)
{
var errtxt = e.stack;
if(!errtxt)
{
errtxt = e.toString();
}
self.postMessage({cmd: 'error', err: errtxt});
}
};
OpenJsCad.javaScriptToSolidSync = function(script, mainParameters, callback) {
OpenJsCad.javaScriptToSolidSync = function(script, mainParameters, debugging) {
var workerscript = "";
workerscript += script;
if(debugging)
{
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";
@ -270,6 +281,7 @@ OpenJsCad.javaScriptToSolidSync = function(script, mainParameters, callback) {
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();
@ -341,6 +353,7 @@ OpenJsCad.textToBlobUrl = function(txt) {
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");
if(!blobURL) throw new Error("createObjectURL() failed");
return blobURL;
};
@ -638,27 +651,10 @@ OpenJsCad.Processor.prototype = {
this.enableItems();
var that = this;
var paramValues = this.getParamValues();
if(this.debugging)
var useSync = this.debugging;
if(!useSync)
{
try
{
var csg = OpenJsCad.javaScriptToSolidSync(this.script, paramValues);
that.processing = false;
that.solid = csg;
if(that.viewer) that.viewer.setCsg(csg);
that.validcsg = true;
that.statusspan.innerHTML = "Ready.";
}
catch(e)
{
that.processing = false;
that.setError(e.toString());
that.statusspan.innerHTML = "Error.";
}
that.enableItems();
if(that.onchange) that.onchange();
}
else
{
this.worker = OpenJsCad.javaScriptToSolidASync(this.script, paramValues, function(err, csg) {
that.processing = false;
@ -679,6 +675,37 @@ OpenJsCad.Processor.prototype = {
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.solid = csg;
if(that.viewer) that.viewer.setCsg(csg);
that.validcsg = true;
that.statusspan.innerHTML = "Ready.";
}
catch(e)
{
that.processing = false;
var errtxt = e.stack;
if(!errtxt)
{
errtxt = e.toString();
}
that.setError(errtxt);
that.statusspan.innerHTML = "Error.";
}
that.enableItems();
if(that.onchange) that.onchange();
}
},
hasSolid: function() {