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);