Note: this code is not yet optimized - by storing some values instead of calculating them multiple times and by algebraically simplifying some operations I anticipate this code running far more quicklky and effeciently than it does.
//fractalCanvasEngine provides tools to generate and manipulate fractals function fractalCanvasEngine() { //transpose is a function to scale and move the points in a fractal so that the first point is on (0,0) and the last point is on (1, 0) this.transpose = function(sketchPoints){ var transposedSketchPoints = [], angle, length, pointAngle, pointLength, i; //calculates the length of the line connecting the first and last points length = Math.sqrt(Math.pow(sketchPoints[0][1] - sketchPoints[sketchPoints.length - 1][1], 2) + Math.pow(sketchPoints[0][2] - sketchPoints[sketchPoints.length - 1][2], 2)); //calculates the angle of the line connecting the first and last points angle = Math.asin((sketchPoints[0][2] - sketchPoints[sketchPoints.length - 1][2]) / length); //for each point in the fractal apply the rotation and scaling for(i = 0; i < sketchPoints.length; i++){ pointLength = Math.sqrt(Math.pow(sketchPoints[0][1] - sketchPoints[i][1], 2) + Math.pow(sketchPoints[0][2] - sketchPoints[i][2], 2)); pointAngle = Math.asin((sketchPoints[0][2] - sketchPoints[i][2]) / pointLength) - angle; //Add the newly transformed point to the output array transposedSketchPoints.push([ sketchPoints[i][0], Math.cos(pointAngle) * pointLength / length || 0, Math.sin(pointAngle) * pointLength * -1 / length || 0 ]); } return transposedSketchPoints; } //scale takes a fractal and rescales it to fit in a 1 by 1 window this.scale = function(points){ var minX = points[0][1] || 0, maxX = points[0][1] || 0, minY = points[0][2] || 0, maxY = points[0][2] || 0, scaleFactor, point; //find the size of the fractal (height and width) for(point in points){ if(points[point][1] < minX) minX = points[point][1]; if(points[point][1] > maxX) maxX = points[point][1]; if(points[point][2] < minY) minY = points[point][2]; if(points[point][2] > maxY) maxY = points[point][2]; } //calculate how much the fractal needs to be scaled scaleFactor = Math.min(1 / (maxX - minX), 1 / (maxY - minY)); //apply the scaling to each of the points for(point in points){ points[point][1] -= minX; points[point][1] *= scaleFactor; points[point][2] -= minY; points[point][2] *= scaleFactor; } return points; } //the heart of the fractal engine - a recursive function that generates the fractal this.getLevel = function(remainingLevels, points) { //continue recursive operation, there are more levels to calculate if(remainingLevels > 0){ var subLevel = this.getLevel(remainingLevels - 1, points), prevPoint, curPoint, rotFactor, scaleFactor, returnLevel = new Array(), subRot, subScale, i, inverse; for(i = 1; i < points.length; i++){ prevPoint = points[i-1]; curPoint = points[i]; if(curPoint[0] === 1 || curPoint[0] === 2){ //this is where the actual fractal is generated... its fairly complex. In short, it converts the fractal to polar, applies the transformations (including flipping it if necessary) and then converts it back to rectangular. I am not going to try to explain where/how all of that happens. scaleFactor = Math.sqrt( Math.pow((curPoint[2]-prevPoint[2]), 2) + Math.pow((curPoint[1]-prevPoint[1]), 2) ); rotFactor = Math.asin((curPoint[2]-prevPoint[2])/scaleFactor); inverse = (curPoint[0] === 1 ? 1 : -1); for(var j = 0; j < subLevel.length; j++){ subScale = Math.sqrt(Math.pow(subLevel[j][2], 2) + Math.pow(subLevel[j][1], 2)); subRot = Math.asin((subLevel[j][2]/subScale) || 0); if(subLevel[j][1] < 0) subRot += (Math.PI/2 - subRot)*2; returnLevel.push(Array(subLevel[j][0], ((subScale)*scaleFactor)*Math.cos(rotFactor+inverse*subRot)+prevPoint[1], ((subScale)*scaleFactor)*Math.sin(rotFactor+inverse*subRot)+prevPoint[2])); } } else returnLevel.push(Array(curPoint[0], curPoint[1], curPoint[2])); //the type of this segment is not one that requires it to be replaced by a fractal } return returnLevel; } else return [[1, 0, 0], [1, 1, 0]]; //level 0 is just a line } } var fractalCanvasEngine = new fractalCanvasEngine();
No comments:
Post a Comment