I recently wanted to make a She Loves Me, She Loves Me Not Game using JavaScript and SVG. My requirements were: 1) Support a random number of petals. 2) The petals should be clickable. Turns out these two requirements make for some fun JavaScript and Geometry.
Here is an image of what I was going for.
The first thing I tackled was the basic infrastructure of the flower. The following Flower function handles generating a random number of petals and the state of the game messages.
// by Jason Rowe - jasonrowe.com @jsonrow function Flower(thee, numberOfPetals) { var petals = numberOfPetals; if(!petals && petals !== 0){ petals = Math.floor(Math.random()* 5) + 5; } var originalTotalNumberOfPetals = petals; this.totalPetals = function () { return originalTotalNumberOfPetals; } this.removePetal = function(){ if(petals > 0){ petals = petals - 1; } return petals; }; this.petals = function(){ return petals; }; var love = false; this.toggleLove = function(){ love = !love; return love; }; this.isItLove = function(){ return love; }; this.result = function(){ var message; if(this.toggleLove()){ message = thee + " loves me"; } else{ message = thee + " loves me not"; } return message; }; }
The next thing was to get the actual drawing of the flower. The first challenge I ran into was creating a random number of petals equally distributed around the center of the flower. To do this I used the polar coordinate system.
var totalPetals = flowerData.petals(); for(var i = 0; i < totalPetals; i++){ log("creating petal"); // Creates Circle Polar Coordinate System var radius = 83; var centerX = 170; var centerY = 160; // get x, y position of the petals var xPosition = radius * Math.cos(2 * Math.PI * i / totalPetals) + centerX; var yPosition = radius * Math.sin(2 * Math.PI * i / totalPetals) + centerY; ....
It quickly became apparent that I needed more Geometry to point the petals in the right direction.
So to help find the correct rotation, I created a TrianglePointsToDegrees function which would solve the angles I needed.
//by Jason Rowe - jasonrowe.com @jsonrow 2013 //Given a triangle with points (x1, y1, x2, y2, x3, y3), // calculates angles A,B,C and edges a,b,c // // // lower case a,b,c are the edges // upper case A,B,C are angles // (x1, y1) // /\ // b / C\ a // / \ // / \ // (x2,y2)/A______B\(x3, y3) // c function TrianglePointsToDegrees(x1, y1, x2, y2, x3, y3){ //distance formula to find lengths between points function lineDistance( point1, point2 ){ var xs = 0; var ys = 0; xs = point2.x - point1.x; xs = xs * xs; ys = point2.y - point1.y; ys = ys * ys; return Math.sqrt( xs + ys ); }; log("create points"); var point1 = {"x": x1, "y": y1}; var point2 = {"x": x2, "y": y2}; var point3 = {"x": x3, "y": y3}; log("create triangle edges"); var edgeA = {}; edgeA.points = [point3, point1]; edgeA.distance = lineDistance(point3, point1); this.edgeA = function(){ return edgeA; }; var edgeB = {}; edgeB.points = [point1, point2]; edgeB.distance = lineDistance(point1, point2); this.edgeB = function(){ return edgeB; }; var edgeC = {}; edgeC.points = [point2, point3]; edgeC.distance = lineDistance(point2, point3); this.edgeC = function(){ return edgeC; }; var sidea = edgeA.distance; var sideb = edgeB.distance; var sidec = edgeC.distance; log("calc angles"); var anga = Math.acos((-sidea*sidea+sideb*sideb+sidec*sidec)/(2*sideb*sidec)); var angb = Math.acos((-sideb*sideb+sidea*sidea+sidec*sidec)/(2*sidea*sidec)); var angc = Math.acos((-sidec*sidec+sidea*sidea+sideb*sideb)/(2*sidea*sideb)); this.aDegree = function(){ return anga*180/Math.PI; }; this.bDegree = function(){ return angb*180/Math.PI; }; this.cDegree = function(){ return angc*180/Math.PI; }; }
Then after creating my x, y coordinates, I found the angle I needed, and completed the ellipse rotation
var totalPetals = flowerData.petals(); for(var i = 0; i < totalPetals; i++){ log("creating petal"); // Creates Circle Polar Coordinate System var radius = 83; var centerX = 170; var centerY = 160; // get x, y position of the petals var xPosition = radius * Math.cos(2 * Math.PI * i / totalPetals) + centerX; var yPosition = radius * Math.sin(2 * Math.PI * i / totalPetals) + centerY; var ellipseHeight = 70; var petalEllipse = this.paper.ellipse(xPosition, yPosition, 30, ellipseHeight); var angle = null; if(xPosition !== centerX){ var triangle = new TrianglePointsToDegrees(xPosition, yPosition, centerX, centerY, xPosition, yPosition + ellipseHeight) if(xPosition > centerX){ angle = triangle.cDegree(); } else{ angle = -triangle.cDegree(); } } //rotate ellipse around center if(angle){ petalEllipse.transform("r" + angle); }
The final fun Geometry was the stem which is a quadratic Bézier curve. I mostly just used trial and error to get the intermediate points Q “M 150 150, Q 210 227 150 300 Q 70 400 150 450″. The other points are just the start, middle, and end of the line.
var stem = this.paper.path("M 150 150, Q 210 227 150 300 Q 70 400 150 450") stem.attr({stroke:'#47D147',"stroke-width":5}); stem.attr("width", 5);
Leave a Reply