Speed optimizations, added OpenJsCad.log() function
This commit is contained in:
parent
fcefd5a257
commit
54787e2ed6
2 changed files with 461 additions and 167 deletions
590
csg.js
590
csg.js
|
@ -91,12 +91,16 @@ for solid CAD anyway.
|
||||||
CSG = function() {
|
CSG = function() {
|
||||||
this.polygons = [];
|
this.polygons = [];
|
||||||
this.properties = new CSG.Properties();
|
this.properties = new CSG.Properties();
|
||||||
|
this.isCanonicalized = true;
|
||||||
|
this.isRetesselated = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Construct a CSG solid from a list of `CSG.Polygon` instances.
|
// Construct a CSG solid from a list of `CSG.Polygon` instances.
|
||||||
CSG.fromPolygons = function(polygons) {
|
CSG.fromPolygons = function(polygons) {
|
||||||
var csg = new CSG();
|
var csg = new CSG();
|
||||||
csg.polygons = polygons;
|
csg.polygons = polygons;
|
||||||
|
csg.isCanonicalized = false;
|
||||||
|
csg.isRetesselated = false;
|
||||||
return csg;
|
return csg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,6 +114,67 @@ CSG.fromObject = function(obj) {
|
||||||
return csg;
|
return csg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reconstruct a CSG from the output of toCompactBinary()
|
||||||
|
CSG.fromCompactBinary = function(bin) {
|
||||||
|
var planes = [];
|
||||||
|
var planeData = bin.planeData;
|
||||||
|
var numplanes = planeData.length / 4;
|
||||||
|
var arrayindex = 0;
|
||||||
|
for(var planeindex = 0; planeindex < numplanes; planeindex++)
|
||||||
|
{
|
||||||
|
var x = planeData[arrayindex++];
|
||||||
|
var y = planeData[arrayindex++];
|
||||||
|
var z = planeData[arrayindex++];
|
||||||
|
var w = planeData[arrayindex++];
|
||||||
|
var normal = new CSG.Vector3D(x,y,z);
|
||||||
|
var plane = new CSG.Plane(normal, w);
|
||||||
|
planes.push(plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
var vertices = [];
|
||||||
|
var vertexData = bin.vertexData;
|
||||||
|
var numvertices = vertexData.length / 3;
|
||||||
|
arrayindex = 0;
|
||||||
|
for(var vertexindex = 0; vertexindex < numvertices; vertexindex++)
|
||||||
|
{
|
||||||
|
var x = vertexData[arrayindex++];
|
||||||
|
var y = vertexData[arrayindex++];
|
||||||
|
var z = vertexData[arrayindex++];
|
||||||
|
var pos = new CSG.Vector3D(x,y,z);
|
||||||
|
var vertex = new CSG.Vertex(pos);
|
||||||
|
vertices.push(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var shareds = bin.shared.map(function(shared){
|
||||||
|
return CSG.Polygon.Shared.fromObject(shared);
|
||||||
|
});
|
||||||
|
|
||||||
|
var polygons = [];
|
||||||
|
var numpolygons = bin.numPolygons;
|
||||||
|
var numVerticesPerPolygon = bin.numVerticesPerPolygon;
|
||||||
|
var polygonVertices = bin.polygonVertices;
|
||||||
|
var polygonPlaneIndexes = bin.polygonPlaneIndexes;
|
||||||
|
var polygonSharedIndexes = bin.polygonSharedIndexes;
|
||||||
|
arrayindex = 0;
|
||||||
|
for(var polygonindex = 0; polygonindex < numpolygons; polygonindex++)
|
||||||
|
{
|
||||||
|
var numpolygonvertices = numVerticesPerPolygon[polygonindex];
|
||||||
|
var polygonvertices = [];
|
||||||
|
for(var i = 0; i < numpolygonvertices; i++)
|
||||||
|
{
|
||||||
|
polygonvertices.push(vertices[polygonVertices[arrayindex++]]);
|
||||||
|
}
|
||||||
|
var plane = planes[polygonPlaneIndexes[polygonindex]];
|
||||||
|
var shared = shareds[polygonSharedIndexes[polygonindex]];
|
||||||
|
var polygon = new CSG.Polygon(polygonvertices, shared, plane);
|
||||||
|
polygons.push(polygon);
|
||||||
|
}
|
||||||
|
var csg = CSG.fromPolygons(polygons);
|
||||||
|
csg.isCanonicalized = true;
|
||||||
|
csg.isRetesselated = true;
|
||||||
|
return csg;
|
||||||
|
};
|
||||||
|
|
||||||
CSG.prototype = {
|
CSG.prototype = {
|
||||||
toPolygons: function() {
|
toPolygons: function() {
|
||||||
return this.polygons;
|
return this.polygons;
|
||||||
|
@ -134,16 +199,23 @@ CSG.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
unionSub: function(csg, retesselate, canonicalize) {
|
unionSub: function(csg, retesselate, canonicalize) {
|
||||||
var a = new CSG.Tree(this.polygons);
|
if(! this.mayOverlap(csg))
|
||||||
var b = new CSG.Tree(csg.polygons);
|
{
|
||||||
a.clipTo(b, false);
|
return this.unionForNonIntersecting(csg);
|
||||||
b.clipTo(a, true);
|
}
|
||||||
var newpolygons = a.allPolygons().concat(b.allPolygons());
|
else
|
||||||
var result = CSG.fromPolygons(newpolygons);
|
{
|
||||||
result.properties = this.properties._merge(csg.properties);
|
var a = new CSG.Tree(this.polygons);
|
||||||
if(canonicalize) result = result.canonicalized();
|
var b = new CSG.Tree(csg.polygons);
|
||||||
if(retesselate) result = result.reTesselated();
|
a.clipTo(b, false);
|
||||||
return result;
|
b.clipTo(a, true);
|
||||||
|
var newpolygons = a.allPolygons().concat(b.allPolygons());
|
||||||
|
var result = CSG.fromPolygons(newpolygons);
|
||||||
|
result.properties = this.properties._merge(csg.properties);
|
||||||
|
if(canonicalize) result = result.canonicalized();
|
||||||
|
if(retesselate) result = result.reTesselated();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Like union, but when we know that the two solids are not intersecting
|
// Like union, but when we know that the two solids are not intersecting
|
||||||
|
@ -152,6 +224,8 @@ CSG.prototype = {
|
||||||
var newpolygons = this.polygons.concat(csg.polygons);
|
var newpolygons = this.polygons.concat(csg.polygons);
|
||||||
var result = CSG.fromPolygons(newpolygons);
|
var result = CSG.fromPolygons(newpolygons);
|
||||||
result.properties = this.properties._merge(csg.properties);
|
result.properties = this.properties._merge(csg.properties);
|
||||||
|
result.isCanonicalized = this.isCanonicalized && csg.isCanonicalized;
|
||||||
|
result.isRetesselated = this.isRetesselated && csg.isRetesselated;
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -232,7 +306,7 @@ CSG.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Affine transformation of CSG object. Returns a new CSG object
|
// Affine transformation of CSG object. Returns a new CSG object
|
||||||
transform: function(matrix4x4) {
|
transform1: function(matrix4x4) {
|
||||||
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);
|
||||||
|
@ -240,6 +314,48 @@ CSG.prototype = {
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
transform: function(matrix4x4) {
|
||||||
|
var scalefactor = matrix4x4.elements[0] * matrix4x4.elements[5] * matrix4x4.elements[10];
|
||||||
|
var ismirror = (scalefactor < 0);
|
||||||
|
var transformedvertices = {};
|
||||||
|
var transformedplanes = {};
|
||||||
|
var newpolygons = this.polygons.map(function(p) {
|
||||||
|
var newplane;
|
||||||
|
var plane = p.plane;
|
||||||
|
var planetag = plane.getTag();
|
||||||
|
if(planetag in transformedplanes)
|
||||||
|
{
|
||||||
|
newplane = transformedplanes[planetag];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newplane = plane.transform(matrix4x4);
|
||||||
|
transformedplanes[planetag] = newplane;
|
||||||
|
}
|
||||||
|
var newvertices = p.vertices.map(function(v) {
|
||||||
|
var newvertex;
|
||||||
|
var vertextag = v.getTag();
|
||||||
|
if(vertextag in transformedvertices)
|
||||||
|
{
|
||||||
|
newvertex = transformedvertices[vertextag];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newvertex = v.transform(matrix4x4);
|
||||||
|
transformedvertices[vertextag] = newvertex;
|
||||||
|
}
|
||||||
|
return newvertex;
|
||||||
|
});
|
||||||
|
if(ismirror) newvertices.reverse();
|
||||||
|
return new CSG.Polygon(newvertices, p.shared, newplane);
|
||||||
|
});
|
||||||
|
var result=CSG.fromPolygons(newpolygons);
|
||||||
|
result.properties = this.properties._transform(matrix4x4);
|
||||||
|
result.isRetesselated = this.isRetesselated;
|
||||||
|
result.isCanonicalized = this.isCanonicalized;
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
mirrored: function(plane) {
|
mirrored: function(plane) {
|
||||||
return this.transform(CSG.Matrix4x4.mirroring(plane));
|
return this.transform(CSG.Matrix4x4.mirroring(plane));
|
||||||
},
|
},
|
||||||
|
@ -344,9 +460,8 @@ CSG.prototype = {
|
||||||
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;
|
var sharedtag = polygon.shared.getTag();
|
||||||
if(polygon.shared !== undefined) sharedtag = polygon.shared;
|
planetag += "/"+sharedtag;
|
||||||
planetag += "/"+JSON.stringify(sharedtag);
|
|
||||||
if(! (planetag in polygonsPerPlane) )
|
if(! (planetag in polygonsPerPlane) )
|
||||||
{
|
{
|
||||||
polygonsPerPlane[planetag] = [];
|
polygonsPerPlane[planetag] = [];
|
||||||
|
@ -408,6 +523,27 @@ CSG.prototype = {
|
||||||
return this.cachedBoundingBox;
|
return this.cachedBoundingBox;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// returns true if there is a possibility that the two solids overlap
|
||||||
|
// returns false if we can be sure that they do not overlap
|
||||||
|
mayOverlap: function(csg) {
|
||||||
|
if( (this.polygons.length == 0) || (csg.polygons.length == 0) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var mybounds = this.getBounds();
|
||||||
|
var otherbounds = csg.getBounds();
|
||||||
|
if(mybounds[1].x < otherbounds[0].x) return false;
|
||||||
|
if(mybounds[0].x > otherbounds[1].x) return false;
|
||||||
|
if(mybounds[1].y < otherbounds[0].y) return false;
|
||||||
|
if(mybounds[0].y > otherbounds[1].y) return false;
|
||||||
|
if(mybounds[1].z < otherbounds[0].z) return false;
|
||||||
|
if(mybounds[0].z > otherbounds[1].z) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Cut the solid by a plane. Returns the solid on the back side of the plane
|
// Cut the solid by a plane. Returns the solid on the back side of the plane
|
||||||
cutByPlane: function(plane) {
|
cutByPlane: function(plane) {
|
||||||
// Ideally we would like to do an intersection with a polygon of inifinite size
|
// Ideally we would like to do an intersection with a polygon of inifinite size
|
||||||
|
@ -471,12 +607,99 @@ CSG.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
setColor: function(red,green,blue) {
|
setColor: function(red,green,blue) {
|
||||||
var newshared = {
|
var newshared = new CSG.Polygon.Shared([red, green, blue]);
|
||||||
color: [red, green, blue],
|
|
||||||
};
|
|
||||||
return this.setShared(newshared);
|
return this.setShared(newshared);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toCompactBinary: function() {
|
||||||
|
var csg = this.canonicalized();
|
||||||
|
var numpolygons = csg.polygons.length;
|
||||||
|
var numpolygonvertices = 0;
|
||||||
|
var numvertices = 0;
|
||||||
|
var vertexmap = {};
|
||||||
|
var vertices = [];
|
||||||
|
var numplanes = 0;
|
||||||
|
var planemap = {};
|
||||||
|
var polygonindex = 0;
|
||||||
|
var planes = [];
|
||||||
|
var shareds = [];
|
||||||
|
var sharedmap = {};
|
||||||
|
var numshared = 0;
|
||||||
|
csg.polygons.map(function(p){
|
||||||
|
p.vertices.map(function(v){
|
||||||
|
++numpolygonvertices;
|
||||||
|
var vertextag = v.getTag();
|
||||||
|
if(! (vertextag in vertexmap))
|
||||||
|
{
|
||||||
|
vertexmap[vertextag] = numvertices++;
|
||||||
|
vertices.push(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var planetag = p.plane.getTag();
|
||||||
|
if(! (planetag in planemap))
|
||||||
|
{
|
||||||
|
planemap[planetag] = numplanes++;
|
||||||
|
planes.push(p.plane);
|
||||||
|
}
|
||||||
|
var sharedtag = p.shared.getTag();
|
||||||
|
if(! (sharedtag in sharedmap))
|
||||||
|
{
|
||||||
|
sharedmap[sharedtag] = numshared++;
|
||||||
|
shareds.push(p.shared);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var numVerticesPerPolygon = new Uint32Array(numpolygons);
|
||||||
|
var polygonSharedIndexes = new Uint32Array(numpolygons);
|
||||||
|
var polygonVertices = new Uint32Array(numpolygonvertices);
|
||||||
|
var polygonPlaneIndexes = new Uint32Array(numpolygons);
|
||||||
|
var vertexData = new Float64Array(numvertices * 3);
|
||||||
|
var planeData = new Float64Array(numplanes * 4);
|
||||||
|
var polygonVerticesIndex = 0;
|
||||||
|
for(var polygonindex = 0; polygonindex < numpolygons; ++polygonindex)
|
||||||
|
{
|
||||||
|
var p = csg.polygons[polygonindex];
|
||||||
|
numVerticesPerPolygon[polygonindex] = p.vertices.length;
|
||||||
|
p.vertices.map(function(v){
|
||||||
|
var vertextag = v.getTag();
|
||||||
|
var vertexindex = vertexmap[vertextag];
|
||||||
|
polygonVertices[polygonVerticesIndex++] = vertexindex;
|
||||||
|
});
|
||||||
|
var planetag = p.plane.getTag();
|
||||||
|
var planeindex = planemap[planetag];
|
||||||
|
polygonPlaneIndexes[polygonindex] = planeindex;
|
||||||
|
var sharedtag = p.shared.getTag();
|
||||||
|
var sharedindex = sharedmap[sharedtag];
|
||||||
|
polygonSharedIndexes[polygonindex] = sharedindex;
|
||||||
|
}
|
||||||
|
var verticesArrayIndex = 0;
|
||||||
|
vertices.map(function(v){
|
||||||
|
var pos = v.pos;
|
||||||
|
vertexData[verticesArrayIndex++] = pos.x;
|
||||||
|
vertexData[verticesArrayIndex++] = pos.y;
|
||||||
|
vertexData[verticesArrayIndex++] = pos.z;
|
||||||
|
});
|
||||||
|
var planesArrayIndex = 0;
|
||||||
|
planes.map(function(p){
|
||||||
|
var normal = p.normal;
|
||||||
|
planeData[planesArrayIndex++] = normal.x;
|
||||||
|
planeData[planesArrayIndex++] = normal.y;
|
||||||
|
planeData[planesArrayIndex++] = normal.z;
|
||||||
|
planeData[planesArrayIndex++] = p.w;
|
||||||
|
});
|
||||||
|
var result = {
|
||||||
|
numPolygons: numpolygons,
|
||||||
|
numVerticesPerPolygon: numVerticesPerPolygon,
|
||||||
|
polygonPlaneIndexes: polygonPlaneIndexes,
|
||||||
|
polygonSharedIndexes: polygonSharedIndexes,
|
||||||
|
polygonVertices: polygonVertices,
|
||||||
|
vertexData: vertexData,
|
||||||
|
planeData: planeData,
|
||||||
|
shared: shareds,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse an option from the options object
|
// Parse an option from the options object
|
||||||
|
@ -1230,136 +1453,116 @@ CSG.Plane.prototype = {
|
||||||
{
|
{
|
||||||
var EPS = CSG.Plane.EPSILON;
|
var EPS = CSG.Plane.EPSILON;
|
||||||
var thisw = this.w;
|
var thisw = this.w;
|
||||||
// first check if the polygon's bounding sphere is completely in front or in back:
|
var hasfront = false;
|
||||||
var bound = polygon.boundingSphere();
|
var hasback = false;
|
||||||
var spherecenter = bound[0];
|
var vertexIsBack = [];
|
||||||
var sphereradius = bound[1];
|
var MINEPS = -EPS;
|
||||||
sphereradius += EPS;
|
for (var i = 0; i < numvertices; i++) {
|
||||||
//var d = this.signedDistanceToPoint(spherecenter);
|
var t = planenormal.dot(vertices[i].pos) - thisw;
|
||||||
var d = planenormal.dot(spherecenter) - thisw;
|
var isback = (t < 0);
|
||||||
|
vertexIsBack.push(isback);
|
||||||
if(d > sphereradius)
|
if(t > EPS) hasfront = true;
|
||||||
|
if(t < MINEPS) hasback = true;
|
||||||
|
}
|
||||||
|
if( (!hasfront) && (!hasback) )
|
||||||
|
{
|
||||||
|
// all points coplanar
|
||||||
|
var t = planenormal.dot(polygon.plane.normal);
|
||||||
|
result.type = (t >= 0)? 0:1;
|
||||||
|
}
|
||||||
|
else if(!hasback)
|
||||||
{
|
{
|
||||||
result.type = 2;
|
result.type = 2;
|
||||||
}
|
}
|
||||||
else if(d < -sphereradius)
|
else if(!hasfront)
|
||||||
{
|
{
|
||||||
result.type = 3;
|
result.type = 3;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// no, we really have to check each vertex separately:
|
// spanning
|
||||||
var hasfront = false;
|
result.type = 4;
|
||||||
var hasback = false;
|
var frontvertices = [], backvertices = [];
|
||||||
var vertexIsBack = [];
|
var isback = vertexIsBack[0];
|
||||||
var MINEPS = -EPS;
|
for(var vertexindex = 0; vertexindex < numvertices; vertexindex++)
|
||||||
for (var i = 0; i < numvertices; i++) {
|
|
||||||
var t = planenormal.dot(vertices[i].pos) - thisw;
|
|
||||||
var isback = (t < 0);
|
|
||||||
vertexIsBack.push(isback);
|
|
||||||
if(t > EPS) hasfront = true;
|
|
||||||
if(t < MINEPS) hasback = true;
|
|
||||||
}
|
|
||||||
if( (!hasfront) && (!hasback) )
|
|
||||||
{
|
{
|
||||||
// all points coplanar
|
var vertex = vertices[vertexindex];
|
||||||
var t = planenormal.dot(polygon.plane.normal);
|
var nextvertexindex = vertexindex + 1;
|
||||||
result.type = (t >= 0)? 0:1;
|
if(nextvertexindex >= numvertices) nextvertexindex = 0;
|
||||||
}
|
var nextisback = vertexIsBack[nextvertexindex];
|
||||||
else if(!hasback)
|
if(isback == nextisback)
|
||||||
{
|
|
||||||
result.type = 2;
|
|
||||||
}
|
|
||||||
else if(!hasfront)
|
|
||||||
{
|
|
||||||
result.type = 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// spanning
|
|
||||||
result.type = 4;
|
|
||||||
var frontvertices = [], backvertices = [];
|
|
||||||
var isback = vertexIsBack[0];
|
|
||||||
for(var vertexindex = 0; vertexindex < numvertices; vertexindex++)
|
|
||||||
{
|
{
|
||||||
var vertex = vertices[vertexindex];
|
// line segment is on one side of the plane:
|
||||||
var nextvertexindex = vertexindex + 1;
|
if(isback)
|
||||||
if(nextvertexindex >= numvertices) nextvertexindex = 0;
|
|
||||||
var nextisback = vertexIsBack[nextvertexindex];
|
|
||||||
if(isback == nextisback)
|
|
||||||
{
|
{
|
||||||
// line segment is on one side of the plane:
|
backvertices.push(vertex);
|
||||||
if(isback)
|
|
||||||
{
|
|
||||||
backvertices.push(vertex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
frontvertices.push(vertex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// line segment intersects plane:
|
frontvertices.push(vertex);
|
||||||
var point = vertex.pos;
|
}
|
||||||
var nextpoint = vertices[nextvertexindex].pos;
|
}
|
||||||
var line = CSG.Line3D.fromPoints(point, nextpoint);
|
else
|
||||||
var intersectionpoint = this.intersectWithLine(line);
|
{
|
||||||
var intersectionvertex = new CSG.Vertex(intersectionpoint);
|
// line segment intersects plane:
|
||||||
if(isback)
|
var point = vertex.pos;
|
||||||
{
|
var nextpoint = vertices[nextvertexindex].pos;
|
||||||
backvertices.push(vertex);
|
var line = CSG.Line3D.fromPoints(point, nextpoint);
|
||||||
backvertices.push(intersectionvertex);
|
var intersectionpoint = this.intersectWithLine(line);
|
||||||
frontvertices.push(intersectionvertex);
|
var intersectionvertex = new CSG.Vertex(intersectionpoint);
|
||||||
}
|
if(isback)
|
||||||
else
|
{
|
||||||
{
|
backvertices.push(vertex);
|
||||||
frontvertices.push(vertex);
|
backvertices.push(intersectionvertex);
|
||||||
frontvertices.push(intersectionvertex);
|
frontvertices.push(intersectionvertex);
|
||||||
backvertices.push(intersectionvertex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
isback = nextisback;
|
else
|
||||||
} // for vertexindex
|
|
||||||
|
|
||||||
// remove duplicate vertices:
|
|
||||||
var EPS_SQUARED = CSG.Plane.EPSILON * CSG.Plane.EPSILON;
|
|
||||||
if(backvertices.length >= 3)
|
|
||||||
{
|
|
||||||
var prevvertex = backvertices[backvertices.length - 1];
|
|
||||||
for(var vertexindex = 0; vertexindex < backvertices.length; vertexindex++)
|
|
||||||
{
|
{
|
||||||
var vertex = backvertices[vertexindex];
|
frontvertices.push(vertex);
|
||||||
if(vertex.pos.distanceToSquared(prevvertex.pos) < EPS_SQUARED)
|
frontvertices.push(intersectionvertex);
|
||||||
{
|
backvertices.push(intersectionvertex);
|
||||||
backvertices.splice(vertexindex,1);
|
}
|
||||||
vertexindex--;
|
|
||||||
}
|
|
||||||
prevvertex = vertex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(frontvertices.length >= 3)
|
isback = nextisback;
|
||||||
|
} // for vertexindex
|
||||||
|
|
||||||
|
// remove duplicate vertices:
|
||||||
|
var EPS_SQUARED = CSG.Plane.EPSILON * CSG.Plane.EPSILON;
|
||||||
|
if(backvertices.length >= 3)
|
||||||
|
{
|
||||||
|
var prevvertex = backvertices[backvertices.length - 1];
|
||||||
|
for(var vertexindex = 0; vertexindex < backvertices.length; vertexindex++)
|
||||||
{
|
{
|
||||||
var prevvertex = frontvertices[frontvertices.length - 1];
|
var vertex = backvertices[vertexindex];
|
||||||
for(var vertexindex = 0; vertexindex < frontvertices.length; vertexindex++)
|
if(vertex.pos.distanceToSquared(prevvertex.pos) < EPS_SQUARED)
|
||||||
{
|
{
|
||||||
var vertex = frontvertices[vertexindex];
|
backvertices.splice(vertexindex,1);
|
||||||
if(vertex.pos.distanceToSquared(prevvertex.pos) < EPS_SQUARED)
|
vertexindex--;
|
||||||
{
|
}
|
||||||
frontvertices.splice(vertexindex,1);
|
prevvertex = vertex;
|
||||||
vertexindex--;
|
}
|
||||||
}
|
}
|
||||||
prevvertex = vertex;
|
if(frontvertices.length >= 3)
|
||||||
}
|
{
|
||||||
}
|
var prevvertex = frontvertices[frontvertices.length - 1];
|
||||||
if (frontvertices.length >= 3)
|
for(var vertexindex = 0; vertexindex < frontvertices.length; vertexindex++)
|
||||||
{
|
{
|
||||||
result.front = new CSG.Polygon(frontvertices, polygon.shared, polygon.plane);
|
var vertex = frontvertices[vertexindex];
|
||||||
}
|
if(vertex.pos.distanceToSquared(prevvertex.pos) < EPS_SQUARED)
|
||||||
if (backvertices.length >= 3)
|
{
|
||||||
{
|
frontvertices.splice(vertexindex,1);
|
||||||
result.back = new CSG.Polygon(backvertices, polygon.shared, polygon.plane);
|
vertexindex--;
|
||||||
}
|
}
|
||||||
|
prevvertex = vertex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (frontvertices.length >= 3)
|
||||||
|
{
|
||||||
|
result.front = new CSG.Polygon(frontvertices, polygon.shared, polygon.plane);
|
||||||
|
}
|
||||||
|
if (backvertices.length >= 3)
|
||||||
|
{
|
||||||
|
result.back = new CSG.Polygon(backvertices, polygon.shared, polygon.plane);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1409,6 +1612,7 @@ CSG.Plane.prototype = {
|
||||||
// passed as the third argument
|
// passed as the third argument
|
||||||
CSG.Polygon = function(vertices, shared, plane) {
|
CSG.Polygon = function(vertices, shared, plane) {
|
||||||
this.vertices = vertices;
|
this.vertices = vertices;
|
||||||
|
if(!shared) shared = CSG.Polygon.defaultShared;
|
||||||
this.shared = shared;
|
this.shared = shared;
|
||||||
var numvertices = vertices.length;
|
var numvertices = vertices.length;
|
||||||
|
|
||||||
|
@ -1432,7 +1636,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 = obj.shared;
|
var shared = CSG.Polygon.Shared.fromObject(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);
|
||||||
};
|
};
|
||||||
|
@ -1667,6 +1871,37 @@ CSG.Polygon.isStrictlyConvexPoint = function(prevpoint, point, nextpoint, normal
|
||||||
return (crossdotnormal >= 1e-5);
|
return (crossdotnormal >= 1e-5);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// # class CSG.Polygon.Shared
|
||||||
|
|
||||||
|
// Holds the shared properties for each polygon (currently only color)
|
||||||
|
|
||||||
|
CSG.Polygon.Shared = function(color) {
|
||||||
|
this.color = color;
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Polygon.Shared.fromObject = function(obj) {
|
||||||
|
return new CSG.Polygon.Shared(obj.color);
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Polygon.Shared.prototype = {
|
||||||
|
getTag: function() {
|
||||||
|
var result = this.tag;
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
result = CSG.getTag();
|
||||||
|
this.tag = result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
// get a string uniquely identifying this object
|
||||||
|
getHash: function() {
|
||||||
|
if(!this.color) return "null";
|
||||||
|
return ""+this.color[0]+"/"+this.color[1]+"/"+this.color[2];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Polygon.defaultShared = new CSG.Polygon.Shared(null);
|
||||||
|
|
||||||
// # class PolygonTreeNode
|
// # class PolygonTreeNode
|
||||||
|
|
||||||
// This class manages hierarchical splits of polygons
|
// This class manages hierarchical splits of polygons
|
||||||
|
@ -1781,39 +2016,57 @@ CSG.PolygonTreeNode.prototype = {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// no children. Split the polygon:
|
// no children. Split the polygon:
|
||||||
if(this.polygon)
|
var polygon = this.polygon;
|
||||||
|
if(polygon)
|
||||||
{
|
{
|
||||||
var splitresult = plane.splitPolygon(this.polygon);
|
var bound = polygon.boundingSphere();
|
||||||
switch(splitresult.type)
|
var sphereradius = bound[1] + 1e-4;
|
||||||
|
// var d = plane.normal.dot(bound[0]) - plane.w;
|
||||||
|
var planenormal = plane.normal;
|
||||||
|
var spherecenter = bound[0];
|
||||||
|
var d = planenormal.x*spherecenter.x + planenormal.y*spherecenter.y + planenormal.z*spherecenter.z - plane.w;
|
||||||
|
if(d > sphereradius)
|
||||||
{
|
{
|
||||||
case 0: // coplanar front:
|
frontnodes.push(this);
|
||||||
coplanarfrontnodes.push(this);
|
}
|
||||||
break;
|
else if(d < -sphereradius)
|
||||||
|
{
|
||||||
case 1: // coplanar back:
|
backnodes.push(this);
|
||||||
coplanarbacknodes.push(this);
|
}
|
||||||
break;
|
else
|
||||||
|
{
|
||||||
case 2: // front:
|
var splitresult = plane.splitPolygon(polygon);
|
||||||
frontnodes.push(this);
|
switch(splitresult.type)
|
||||||
break;
|
{
|
||||||
|
case 0: // coplanar front:
|
||||||
case 3: // back:
|
coplanarfrontnodes.push(this);
|
||||||
backnodes.push(this);
|
break;
|
||||||
break;
|
|
||||||
|
case 1: // coplanar back:
|
||||||
case 4: // spanning:
|
coplanarbacknodes.push(this);
|
||||||
if(splitresult.front)
|
break;
|
||||||
{
|
|
||||||
var frontnode = this.addChild(splitresult.front);
|
case 2: // front:
|
||||||
frontnodes.push(frontnode);
|
frontnodes.push(this);
|
||||||
}
|
break;
|
||||||
if(splitresult.back)
|
|
||||||
{
|
case 3: // back:
|
||||||
var backnode = this.addChild(splitresult.back);
|
backnodes.push(this);
|
||||||
backnodes.push(backnode);
|
break;
|
||||||
}
|
|
||||||
break;
|
case 4: // spanning:
|
||||||
|
if(splitresult.front)
|
||||||
|
{
|
||||||
|
var frontnode = this.addChild(splitresult.front);
|
||||||
|
frontnodes.push(frontnode);
|
||||||
|
}
|
||||||
|
if(splitresult.back)
|
||||||
|
{
|
||||||
|
var backnode = this.addChild(splitresult.back);
|
||||||
|
backnodes.push(backnode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3338,9 +3591,23 @@ CSG.fuzzyFactory.joinSets = function(set1, set2) {
|
||||||
CSG.fuzzyCSGFactory = function() {
|
CSG.fuzzyCSGFactory = function() {
|
||||||
this.vertexfactory = new CSG.fuzzyFactory(3, 1e-5);
|
this.vertexfactory = new CSG.fuzzyFactory(3, 1e-5);
|
||||||
this.planefactory = new CSG.fuzzyFactory(4, 1e-5);
|
this.planefactory = new CSG.fuzzyFactory(4, 1e-5);
|
||||||
|
this.polygonsharedfactory = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
CSG.fuzzyCSGFactory.prototype = {
|
CSG.fuzzyCSGFactory.prototype = {
|
||||||
|
getPolygonShared: function(sourceshared) {
|
||||||
|
var hash = sourceshared.getHash();
|
||||||
|
if(hash in this.polygonsharedfactory)
|
||||||
|
{
|
||||||
|
return this.polygonsharedfactory[hash];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.polygonsharedfactory[hash] = sourceshared;
|
||||||
|
return sourceshared;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getVertex: function(sourcevertex) {
|
getVertex: function(sourcevertex) {
|
||||||
var elements = [sourcevertex.pos.x, sourcevertex.pos.y, sourcevertex.pos.z];
|
var elements = [sourcevertex.pos.x, sourcevertex.pos.y, sourcevertex.pos.z];
|
||||||
var result = this.vertexfactory.lookupOrCreate(elements, function(els) {
|
var result = this.vertexfactory.lookupOrCreate(elements, function(els) {
|
||||||
|
@ -3359,11 +3626,12 @@ CSG.fuzzyCSGFactory.prototype = {
|
||||||
|
|
||||||
getPolygon: function(sourcepolygon) {
|
getPolygon: function(sourcepolygon) {
|
||||||
var newplane = this.getPlane(sourcepolygon.plane);
|
var newplane = this.getPlane(sourcepolygon.plane);
|
||||||
|
var newshared = this.getPolygonShared(sourcepolygon.shared);
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var newvertices = sourcepolygon.vertices.map(function(vertex) {
|
var newvertices = sourcepolygon.vertices.map(function(vertex) {
|
||||||
return _this.getVertex(vertex);
|
return _this.getVertex(vertex);
|
||||||
});
|
});
|
||||||
return new CSG.Polygon(newvertices, sourcepolygon.shared, newplane);
|
return new CSG.Polygon(newvertices, newshared, newplane);
|
||||||
},
|
},
|
||||||
|
|
||||||
getCSG: function(sourcecsg) {
|
getCSG: function(sourcecsg) {
|
||||||
|
|
38
openjscad.js
38
openjscad.js
|
@ -1,6 +1,25 @@
|
||||||
OpenJsCad = function() {
|
OpenJsCad = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
OpenJsCad.log = function(txt) {
|
||||||
|
var timeInMs = Date.now();
|
||||||
|
var prevtime = OpenJsCad.log.prevLogTime;
|
||||||
|
if(!prevtime) prevtime = timeInMs;
|
||||||
|
var deltatime = timeInMs - prevtime;
|
||||||
|
OpenJsCad.log.prevLogTime = timeInMs;
|
||||||
|
var timefmt = (deltatime*0.001).toFixed(3);
|
||||||
|
txt = "["+timefmt+"] "+txt;
|
||||||
|
if( (typeof(console) == "object") && (typeof(console.log) == "function") )
|
||||||
|
{
|
||||||
|
console.log(txt);
|
||||||
|
}
|
||||||
|
else if( (typeof(self) == "object") && (typeof(self.postMessage) == "function") )
|
||||||
|
{
|
||||||
|
self.postMessage({cmd: 'log', txt: txt});
|
||||||
|
}
|
||||||
|
else throw new Error("Cannot log");
|
||||||
|
};
|
||||||
|
|
||||||
// A viewer is a WebGL canvas that lets the user view a mesh. The user can
|
// A viewer is a WebGL canvas that lets the user view a mesh. The user can
|
||||||
// tumble it around by dragging the mouse.
|
// tumble it around by dragging the mouse.
|
||||||
OpenJsCad.Viewer = function(containerelement, width, height, initialdepth) {
|
OpenJsCad.Viewer = function(containerelement, width, height, initialdepth) {
|
||||||
|
@ -243,13 +262,16 @@ OpenJsCad.runMainInWorker = function(mainParameters)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(typeof(main) != 'function') throw new Error('Your jscad file should contain a function main() which returns a CSG solid.');
|
if(typeof(main) != 'function') throw new Error('Your jscad file should contain a function main() which returns a CSG solid.');
|
||||||
|
OpenJsCad.log.prevLogTime = Date.now();
|
||||||
var csg = main(mainParameters);
|
var csg = main(mainParameters);
|
||||||
if( (typeof(csg) != "object") || (!(csg instanceof CSG)) )
|
if( (typeof(csg) != "object") || (!(csg instanceof CSG)) )
|
||||||
{
|
{
|
||||||
throw new Error("Your main() function should return a CSG solid.");
|
throw new Error("Your main() function should return a CSG solid.");
|
||||||
}
|
}
|
||||||
self.postMessage({cmd: 'rendered', csg: csg});
|
var csg_bin = csg.toCompactBinary();
|
||||||
|
csg = null; // not needed anymore
|
||||||
|
self.postMessage({cmd: 'rendered', csg: csg_bin});
|
||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
|
@ -278,6 +300,7 @@ OpenJsCad.javaScriptToSolidSync = function(script, mainParameters, debugging) {
|
||||||
}
|
}
|
||||||
workerscript += "return main("+JSON.stringify(mainParameters)+");";
|
workerscript += "return main("+JSON.stringify(mainParameters)+");";
|
||||||
var f = new Function(workerscript);
|
var f = new Function(workerscript);
|
||||||
|
OpenJsCad.log.prevLogTime = Date.now();
|
||||||
var csg = f();
|
var csg = f();
|
||||||
return csg;
|
return csg;
|
||||||
};
|
};
|
||||||
|
@ -314,7 +337,8 @@ OpenJsCad.javaScriptToSolidASync = function(script, mainParameters, callback) {
|
||||||
{
|
{
|
||||||
if(e.data.cmd == 'rendered')
|
if(e.data.cmd == 'rendered')
|
||||||
{
|
{
|
||||||
var csg = CSG.fromObject(e.data.csg);
|
//var csg = CSG.fromObject(e.data.csg);
|
||||||
|
var csg = CSG.fromCompactBinary(e.data.csg);
|
||||||
callback(null, csg);
|
callback(null, csg);
|
||||||
}
|
}
|
||||||
else if(e.data.cmd == "error")
|
else if(e.data.cmd == "error")
|
||||||
|
@ -322,6 +346,10 @@ OpenJsCad.javaScriptToSolidASync = function(script, mainParameters, callback) {
|
||||||
// var errtxt = "Error in line "+e.data.err.lineno+": "+e.data.err.message;
|
// var errtxt = "Error in line "+e.data.err.lineno+": "+e.data.err.message;
|
||||||
callback(e.data.err, null);
|
callback(e.data.err, null);
|
||||||
}
|
}
|
||||||
|
else if(e.data.cmd == "log")
|
||||||
|
{
|
||||||
|
console.log(e.data.txt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
worker.onerror = function(e) {
|
worker.onerror = function(e) {
|
||||||
|
@ -923,6 +951,4 @@ OpenJsCad.Processor.prototype = {
|
||||||
});
|
});
|
||||||
this.paramControls = paramControls;
|
this.paramControls = paramControls;
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue