Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- function main() {
- var startTime = (new Date()).getTime();
- var points = 30;
- var elites = 10;
- /* Since we force the population count to be a triangular number, those selected for reproduction will always
- produce the same size population after being crossed over with every other selected thing with no repeats
- */
- var polygonCount = elites * (elites + 1) / 2;
- var candidateList = new Array();
- var generations = 200;
- var mutationMultiplier = 50;
- /* Initialize our canvas as global variables */
- canvas = document.getElementById("drawing");
- context = canvas.getContext("2d");
- /* generate a bunch of polygons */
- for (var i = 0; i < polygonCount; i++) {
- var pointSet = new Array();
- for (var j = 0; j < points; j++) {
- var a = Math.ceil(Math.random() * canvas.width);
- var b = Math.ceil(Math.random() * canvas.height);
- pointSet.push(new Point(a, b));
- }
- candidateList.push(new CandidatePolygon(pointSet));
- }
- /* Sort by fitness */
- candidateList.sort(function(a, b){return b.fitness - a.fitness;});
- /* Make a table of all the candidates */
- function printATableInTheRankingArea(sortedPolygonList){
- var dataTableElement = document.getElementById("rankingEntry");
- var rowEntry = "";
- for (var i = 0; (i < sortedPolygonList.length) && (i < 100); i++) {
- rowEntry += "<tr id=\"datarow_" + i + "\">" +
- "<td>" + i + "</td>" +
- "<td>" + sortedPolygonList[i].area + "px<sup>2</sup></td>" +
- "<td>" + sortedPolygonList[i].perimeter + "px</td>" +
- "<td>" + sortedPolygonList[i].sigma + "</td>" +
- "<td>" + sortedPolygonList[i].mean + "</td>" +
- "<td>" + sortedPolygonList[i].isoperimetric_ratio + "</td>" +
- "<td>" + sortedPolygonList[i].fitness + "</td></tr>";
- }
- dataTableElement.innerHTML = rowEntry;
- }
- /* Run through the generations */
- for(var n = 0; n < generations; n++){
- var newGeneration = new Array();
- var selectFew = new Array();
- for(var i = 0; i < elites; i++) selectFew.push(candidateList[i]);
- for(var j = 0; j < selectFew.length; j++){
- for(var k = 0; k < j; k++){
- var newPoly = crossover(selectFew[j], selectFew[k], mutationMultiplier);
- newGeneration.push(newPoly);
- }
- }
- candidateList = newGeneration;
- candidateList.sort(function(a, b){return b.fitness - a.fitness;});
- }
- drawPoints(candidateList[0].points);
- printATableInTheRankingArea(candidateList);
- var runtime = (new Date()).getTime() - startTime;
- document.getElementById("stats").innerHTML =
- "Took " + runtime + "ms.<br>Count per generation: " + candidateList.length
- + "<br>Height: " + canvas.height + "<br>Width: " + canvas.width;
- }
- /* constructor for an ordered pair */
- function Point(x, y) {
- /* Rectangular form in y-inverted coordinates, with the top left as the origin (disgusting) */
- this.x = x;
- this.y = y;
- /* Rectangular form in _correct_ coordinates, with the center as the origin */
- this.correctedx = x - (canvas.width / 2);
- this.correctedy = ((canvas.height / 2) - y); // reflect over the Y axis
- /* Polar coordinates, with angle in degrees to avoid having to work with floating point precisions of pi and all that shit */
- this.radius = Math.sqrt(this.correctedx * this.correctedx + this.correctedy * this.correctedy);
- this.theta = Math.ceil(Math.atan2(this.correctedy, this.correctedx) * (180 / Math.PI));
- }
- /* Draw a collection of points onto the canvas */
- function drawPoints(points) {
- var size = points.length;
- for (var i = 0; i < size; i++) {
- var a = points[i];
- var b = (i == size - 1)? points[0] : points[i + 1]; // Last coordinate needs to be the first point
- // draw a line
- context.moveTo(a.x, a.y);
- context.lineTo(b.x, b.y);
- context.stroke();
- }
- }
- /* constructor for a polygon */
- function CandidatePolygon(scatteredPoints){
- /* gets the area of a polygon from a list of points
- this uses the formula for a set of points defining a convex polygon with determinates
- https://en.wikipedia.org/wiki/Shoelace_formula
- */
- this.determineArea = function() {
- var size = this.cardinality;
- var area = 0;
- for (var i = 0; i < size; i++) {
- var a = this.points[i];
- var b = (i == size - 1)? this.points[0] : this.points[i + 1]; // the last point connects to the first
- area = area + ((a.x * b.y) - (b.x * a.y));
- }
- return Math.abs(area * 0.5);
- }
- /* Finds the center of the points by using the... center of mass formula */
- this.getStandardDeviationofLengths = function(){
- var size = this.cardinality;
- var secondMoment = 0;
- for (var i = 0; i < size; i++) {
- var a = this.points[i];
- var b = (i == size - 1)? this.points[0] : this.points[i + 1]; // Last coordinate needs to be the first point
- var distance = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y);
- secondMoment += distance;
- }
- return ~(~(Math.sqrt((secondMoment - this.mean * this.mean) / size) * 100))/ 100; // NOT converts to integer
- }
- /* Calculate the perimeter of the polygon */
- this.calculatePerimeter = function() {
- var size = this.cardinality;
- var perim = 0;
- for (var i = 0; i < size; i++) {
- var a = this.points[i];
- var b = (i == size - 1)? this.points[0] : this.points[i + 1]; // Last coordinate needs to be the first point
- var distance = Math.sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
- perim += distance;
- }
- return Math.ceil(perim);
- }
- /* Calculate the perimeter of the polygon */
- this.getFitness = function() {
- var fit = 1/(1 - this.isoperimetric_ratio);
- /* punish some things that aren't good */
- if(this.area > (canvas.width * canvas.height * 0.75)) fit = fit / 10; // really punish
- if(this.area < (canvas.width * canvas.height * 0.10)) fit = fit / 10; // really punish
- if(this.sigma > 30) fit = fit / 5; // punish
- /* Also, there is a 5% punishment for every point outside the outside the boundry */
- for(var i = 0; i < this.cardinality; i++){
- var x = this.points[i].x;
- var y = this.points[i].y;
- if( (x < 0) || (x > canvas.width) || (y < 0) || (y > canvas.height) ) fit = fit * 0.95;
- }
- return fit;
- }
- /* Sort them greatest to least, by their corrected polar angle
- This has the effect of making it a (kinda) nice convex polygon
- */
- this.points = scatteredPoints.sort(function(a, b) {return b.theta - a.theta;});
- this.cardinality = this.points.length;
- this.area = this.determineArea();
- this.perimeter = this.calculatePerimeter();
- this.mean = this.perimeter / this.cardinality;
- this.sigma = this.getStandardDeviationofLengths();
- this.isoperimetric_ratio = (4 * 3.1415 * this.area) / (this.perimeter * this.perimeter);
- this.fitness = this.getFitness();
- }
- function crossover(a, b, mutation){
- mutation = (mutation == null)? 100 : mutation;
- var pointSet = new Array();
- /* Mutations mut=[-mutation,mutation] */
- var x_mutation = Math.round((Math.random() * 2 - 1) * mutation);
- var y_mutation = Math.round((Math.random() * 2 - 1) * mutation);
- for (var i = 0; (i < a.cardinality) && (i < b.cardinality); i++) {
- var cx = ((a.points[i].correctedx + b.points[i].correctedx) / 2) + x_mutation;
- var cy = ((a.points[i].correctedy + b.points[i].correctedy) / 2) + y_mutation;
- if((a.area + b.area) < (canvas.width * canvas.height * 0.25)) {
- cx = cx * 2; cy = cy * 2;
- }
- if((a.area + b.area) > (canvas.width * canvas.height)) {
- cx = cx / 2; cy = cy / 2;
- }
- var x = cx + (canvas.width / 2);
- var y = (canvas.height / 2) - cy;
- pointSet.push(new Point(x, y));
- }
- return new CandidatePolygon(pointSet);
- }
Advertisement
Add Comment
Please, Sign In to add comment