==The Crux==
How do I elegantly define five equal-area clickable regions in terms of (x,y) coordinates for a pentagon?
==The Context==
I have a clickable sprite for a javascript game using Phaser. The sprite is shaped like a pentagon and each of the vertices defines a territory that is one-fifth of the pentagon's area. When the user clicks within the territory, the event handler calls the appropriate function for that territory and returns the (x,y) position clicked within the territory.
I am a novice javascript programmer who would like to learn how (and why) to write elegant code, if possible. I can do a little math. Normally, clickable regions are defined as rectangles, but this is a special case. The five-fold symmetry of the graphic is integral to the theme of the game (Five Elements Gong Fu).
What is a good way to define these clickable regions in terms of the coordinates? I feel as if there is some computer-math-formula-wizardry ( that could be thought of that would rely on the symmetry to somehow define the coordinates in an elegant way, but I haven't been able to determine one.
Additionally, the graphic is centered in a png file that is rectangular. I need to somehow translate the coordinates of the pentagon's neighborhoods to the coordinates of the canvas so that the user can feel they are clicking the appropriate region.
edit
==Crappy Graphic==
Ideally, the pentagon would be drawn symmetrically, but I just roughed this up. Like this:
How do I define the (x,y) coordinates of neighborhood A so that the user can click region A and it calls Function_for_region_A(handler)?
I do not know anything about phaser, so here is only javascrip. You could rotate the whole canvas and reput it to it's normal angle when done. So when rotated, the regions become squares and it makes the code and programming simpler. I think you must rotate the canvas 60° to get squares, but I am not sure. I'm not sure if this is correct, so plz do not jugde me or take away points from me xD, THIS IS REALLY REALLY COMPLICATED FOR ME.Here is the code :`
var ctx = document.getElementById("canvasId").getContext("2d");
//code to draw the the pentagon here
var regionA = { //Because the canvas rotates, the x and y points change, so it must have other destination points.
x : pointToTheLeftInRegionA.x,
y : pointToTheLeftInRegionA.y - pointAtTopInRegionA.y / 2
}
function canvasWhenClicked(event) { //Function called when : "<canvas onclick="canvasWhenClicked(event)" ...></canvas>"
var X = event.clientX; //Finds x coordinates of where you clicked
var Y = event.clientY; //Finds x coordinates of where you clicked
ctx.rotate(60 * Math.PI / 180);
if (X > regionA.x && X < regionA.width + regioA.x) { //Sees if the X where clicked is bigger than regionA's X and smaller than regionA X + width
if (Y > regionA.y && Y < regionA.height + regionA.y) { //Sees if the Y where clicked is bigger than regionA's Y and smaller than regionA X + height
Function_for_region_A(handler); //If clicked in box, call function
}
}
ctx.rotate((360 - 60) * Math.PI / 180); //rotate the canvas back
}
Related
I created a small game using JavaScript canvas. It's a 2D shooting game where the controller is the mouse and enemies spawn randomly within the canvas.
I am trying to get the JavaScript to detect if my cursor is overlaying my "enemy" component
For example. The logic statement is
onclick,
If aimcursor is overlapping enemy
enemy spawns at random location.
My only problem is how to check if my aimcursor is overlapping enemy
The If statement I'm using right now is what I have learnt from a website but it doesn't seem to be working.
document.addEventListener("mousedown", function(){
var gunshot = new Audio('gunshot.mp3');
gunshot.play();
if(aimcursor.x >= enemy.x && enemy.y >= aimcursor.y&&
enemy.y <= aimcursor.y && enemy.y <= aimcursor.y){}
Keep a separate gamestate object which contains x/y positions of all objects. Each time you draw on the canvas you use these coordinates. You might already be doing this. So lets say enemy.x and enemy.y are currently available within scope.
The next thing to do is getting the cursors's x/y position. You can get this by checking the MouseEvent instance provided by the mousedown callback.
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
The MouseEvent instance contains the clientX and clientY properties. These will reflect the coordinates of te cursor based on the client area, not the canvas, so we need to convert this first. Please see this answer:
How do I get the coordinates of a mouse click on a canvas element?
Once you actually got the coordinates. You need to check whether the mouse x/y coordinate is within a certain boundary around the enemy coordinate. This is because modern screens have high resolutions, and most likely no person is precise enough to click that correct 1 pixel area on the screen.
The boundary which is acceptable as valid input is called a "bounding box" and is typically a square or a rectangle the size of the image used to represent the enemy. If the enemy is a 50x50 px image placed on the center of it's x/y coordinate. the bounding box will start at x - 25px, y - 25px and will stretch to x + 25px, y + 25px.
You need to take the bounding box into account and validate whether the user clicked within that region. This will probably look somewhat like.
if(mouse.x >= enemy.x - 25 && mouse.x <= enemy.x + 25 && mouse.y >= enemy.y - 25 && mouse.y <= enemy.y + 25)`
(from the top of my head).
If the calculated mouse position on click matches that condition can consider that a hit.
I'm implementing some basic annotation draw features, such as arrows. Now I'm a little bit stuck with ellipse.
The methods to draw an ellipse usually address using it's two diameters and eventually a rotation:
However I want to display the ellipse between the point user clicked and the one he's hovering, therefore I need a function that calculates diameters and rotation based on two points:
How would I do that? Can it be achieved with sufficient performance (as it renders during mouse-hovering)?
the steps you shoul follow:
get the angle of the line (from this post: get angle of a line from horizon)
rotate the canvas or at least the part you currently drawing (live demo here: http://www.html5canvastutorials.com/advanced/html5-canvas-transform-rotate-tutorial)
draw an ellipse in canvas (http://www.scienceprimer.com/draw-oval-html5-canvas)
the resulted ellipse will be transformed as described
It can be done in the same way that it is normally done, just using different math to calculate the shape. Without writing the entire code for you, you can start by having an event trigger when the user clicks the mouse button down. The function will copy the users x and y position based on the screen. Then there is a second function which will handle mouse movement. This function will keep track of the x and y coords of the mouse while it is in motion. The final function will be a mouse up event, when a user lifts their finger from the mouse button (assuming this is when the event should be finished). Using the initial and final position of the x and y coordinates, you can calculate the length of the line the user created. That line is the long diameter of the ellipse. Half this number for the large radius. Then use whatever ratio you are using to calculate the smaller radius from the larger one. Then create an ellipse based on these numbers.
For the math: Suppose your first point is x1,y1 and the end point is x2,y2
I'm also assuming that we have a line going from bottom-left to top-right
Distance between two points = sqrt((x2-x1)^2 + (y2-y1)^2) ---> (we will call this d1)
half of this is the length of the large radius ---> (we will call this r1)
Midpoint formula = ((x1+x2)/2 , (y1+y2)/2) ---> axis of rotation (we will call it (m1, m2))
distance from midpoint to end is just the radius
radius is now the hypotenuse of constructed plane, y2-m2 is height of right triangle.
Find the angles between midpoint and one end of larger radius - sin((y2-m2)/r1).
Angle of smaller radius is this angle + pi/4 radians.
calculate length of smaller radius based on ratio.
I am trying to build a small app where my users can straighten up a tilted face with just 2 clicks
I ask my users to click on the middle of the nose and the middle of the eyebrows of the face within the image.
From there I get 2 points eyebrowMiddle(x1,y1) and noseMiddle (x2,y2).
Is it possible via these 2 points to calculate how much Canvas
rotation I need to have to rotate the image and make the face straight
in relation to the canvas rectangle?
Also, how can I detect and adjust accordingly if the image is tilted
to the left or right?
Here is a more descriptive image to show you what I mean now.
PS:
x1,y1 and x2,y2 are in relation to the canvas perimeter of
course, not the browser window or anything else.
We have tried the line equation such as m = (x2-x1) / (y2-y1) but the
result is always near 1 so I don't think we are following the right
course at the moment.
We don't care if the image looks wrong in the canvas as long as the
face features are parallel in relation to the bottom of the canvas
(they should be looking straight).
To perform such a rotation, you need to decide of the pivot point. Here i choose the eyebrow.
Then you have to choose a point in the target canvas where this pivot point will be hooked. I decided to choose the point at middle x coordinates, and at fourth of the screen in y.
To compute the rotation angle, you have to use atan2, which will nicely give you the angle for a given deltaY / deltaX in between two points ( angle = Math.atan2 ( delta y , delta x ) ) .
Then to draw :
- Translate to the target point.
- rotate by right angle.
- draw the image centering on its pivot.
ET VOILA, it works :-)
function rotate() {
ctx.save();
// go to default center position
ctx.translate(eyeBrowTargetPosition.x, eyeBrowTargetPosition.y);
// compute angle
var yDelta = noseMiddle.y - eyebrowMiddle.y;
var xDelta = noseMiddle.x - eyebrowMiddle.x ;
var angle = Math.atan2 (yDelta ,xDelta);
// compensate for angle
ctx.rotate(angle);
//draw image centering input on eyebrow
ctx.drawImage(face, -eyebrowMiddle.x, -eyebrowMiddle.y);
ctx.restore();
};
jsbin is here :
http://jsbin.com/wavokaku/2/edit?js,output
result with an approximation of the existing green dots :
I have a question with regards to the pivot property of a DisplayObject. In particular, i want to rotate a DisplayObjectContainer around its center; so i set the pivot to its center point. However, i've noticed that this affects the position of the element.
For example, if i set the position to 0,0 (which is the default one) pixijs will try to position the object according to its center point and not the top left corner. So the children of the the DisplayObjectContainer (which in my case is an instance of the Graphics class) run out of the main viewport.
Is there any way to set the rotation point but still position the object in respect to its top left corner.
You need to draw the graphics container at the point at which you wish your object to rotate around and then draw the object so that its center is the graphic's x/y position. So, to draw a rectangle that is drawn at the precise coordinates you wish while pivoting around its center, you would use this function.
self.createRect = function (x1, y1, x2, y2, color) {
var graphics = new PIXI.Graphics();
graphics.beginFill(color || 0x000000);
//This is the point around which the object will rotate.
graphics.position.x = x1 + (x2/2);
graphics.position.y = y1 + (y2/2);
// draw a rectangle at -width/2 and -height/2. The rectangle's top-left corner will
// be at position x1/y1
graphics.drawRect(-(x2/2), -(y2/2), x2, y2);
self.stage.addChild(graphics);
return graphics;
};
Since it's a square or rectangle, we can calculate where its center should be on the screen by using x1 + (width/2) and y1 + (height/2) then we offset the rect to the left and to the top by half of its width and half of its height using drawRect(-(width/2), -(height/2), x2, y2);
The pivot property is a bit confusing. Imagine the following example:
Your pivot is a screw located somewhere on the object (it can be of course also located somewhere outside, but just for better understanding imagine that your object is a plank of wood with a screw sticking out). Your position (of the graphics/container) is a screw. The object is always rotating around the position, but you can change the pivot (the position of the screw on your plank of wood) on the object, so it will be the new point of the rotation for the object. Finally you can try to screw your plank of wood to the screw.
Basically, the default values of the position and pivot is 0. So if you have your object drawn for example here:
test.drawRoundedRect(100, 100, 200, 200,12);
you can now try to rotate it and you'll see, that it is rotating around the point (0,0).
The graphic is always rotating around the position, you can try to locate it somewhere else:
test.position.x = 200;
test.position.y = 200;
The object is now rotating around the point (200,200). But that's just a shift.
We can now try to change the pivot point (which is the screw) to any different position. So, on your plank of wood you just place the screw on (50,50), then (100,100), etc and you'll see that it affects your object position.
Now, for our example, we can set the pivot point to (200,200) to the same coordinates as the position of the object.
test.pivot.x = 200;
test.pivot.y = 200;
And finally it is rotating around the center point of the drawn object.
The solution provided by #Spencer is an alternative of the pivot property.
I've searched for the answer to this and have tried many proposed solutions, but none seem to work. I've been struggling with this forever so any insight is greatly appreciated.
I have 3 shapes (vectors I suppose) on a JS canvas, each with an orientation represented as degrees off of 0 and a width. I need to drag one of these shapes "straight out" from its orientation. This is difficult to explain in words so please view the graphic I created:
The middle (diagonal) shape is at 45 degrees. It's origin is the red dot, (x1,y1). The user drags the shape and their mouse lies at the green dot, (x2,y2). Since the shape's origin is in the lower left, I need to position the shape at the position of the lighter blue shape as if the user has dragged straight outward from the shape's origin.
I don't think it matters, but the library I'm using to do this is KineticJS. Here's the code and some information I have available which may help solve the problem. This code positions the shape on top of the mouse, which isn't what I want:
var rotationDeg = this.model.get("DisplayOri"), // rotation in degrees
rotationRadians = rotationDeg * Math.PI / 180, // rotation in rads
unchanged = this.content.getAbsolutePosition(), // {x,y} of the shape before any dragging
dragBoundFunc = function (changed) {
// called on a mouseMove event, so changed is always different and is the x,y of mouse on stage
var delta = {
x: changed.x - unchanged.x,
y: changed.y - unchanged.y
};
return changed; // go to the mouse position
};
[edit] I should mention that the obvious of "return delta" doesn't work.
It sounds like you want to constrain the movement of the object.
Determine the vector representing the constraint axis : that is, we only want motion to occur along this line. It appears from your drawing that this is in the direction of the short line from the red dot out to the left. That vector has a direction of -1/m where m is the slope of the line we are moving.
Constrain the movement. The movement is represented by the mouse move delta - but we only want the portion of that movement in the direction of the constraint axis. This is done with a dot product of the two vectors.
So in pseudo code
m = (line.y2 - line.y1)/(line.x2 - line.x1)
constraintSlope = -1/m
contraintVector = {1, constraintSlope} //unit vector in that direction
userMove = {x2-x1, y2-y1} //vector of mouse move direction
projection = userMove.x * constraintVector.x + userMove.y * constraintVector.y
translation = projection * constraintVector //scaled vector