diff --git a/csg.js b/csg.js index 24c5790..6fd47ec 100644 --- a/csg.js +++ b/csg.js @@ -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; } @@ -447,7 +455,27 @@ CSG.prototype = { connectTo: function(myConnector, otherConnector, mirror, normalrotation) { var matrix = myConnector.getTransformationTo(otherConnector, mirror, normalrotation); 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; }, - // 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(); @@ -2435,6 +2463,9 @@ CSG.Polygon2D.prototype = { var offsetvector = CSG.parseOptionAs3DVector(options, "offset", [0,0,1]); 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; }, diff --git a/index.html b/index.html index 63d0055..7c0a019 100644 --- a/index.html +++ b/index.html @@ -105,10 +105,11 @@ Some of the benefits:
var cube = CSG.cube(); @@ -219,7 +221,7 @@ var cube3 = cube.transform(m);
var cube = CSG.cube().translate([1,0,0]); @@ -559,5 +561,129 @@ function main(params) {Or see the Gears demo for another example of interactive parameters. +
+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 ++ +
+// --------- 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); ++ +