I have a screen where I would like to calculate the next x and y based on angle.
The first footstep is this example starts at Step 1.
How can I calculate the next footstep where I want to increase going forward by 120 and the side step needs to weave in and out of about 60.
Please bear in mind that the starting point could be x = 100, y = 100 with the angle being 180 so the footsteps would have to then be going up the y axis.
I've attempted the following Javascript but the footsteps seem to get confused:
this.startingFootPrintX = Math.floor(Math.random() * 1000) + 20; //Random number between 20 and 1000
this.startingFootPrintY = Math.floor(Math.random() * 560) + 20; //Random number between 20 and 560
this.startingAngle = Math.floor(Math.random() * 340) + 20; //Random number between 20 and 340
startFootSteps();
startFootSteps(){
console.log(this.startingFootPrintX);
console.log(this.startingFootPrintY);
this.startingFootPrintX = Math.round(Math.cos(this.startingAngle * Math.PI / 180) * 120 + this.startingFootPrintX);
this.startingFootPrintY = Math.round(Math.sin(this.startingAngle * Math.PI / 180) * 60 + this.startingFootPrintY);
setInterval(function () {
startFootSteps();
}, 3000);
}
Diagram:
The step direction (black line) is given by (cos θ, sin θ). The step offset direction (small blue lines) is given by (sin θ, -cos θ)
The position recurrence is given by:
s determines which side of the black line the next footprint is on, i.e. -1 for the left foot and +1 for the right.
If you know the starting position c0 and the starting foot s0, a closed-form solution is given by:
This alternates between both feet for every step.
In your diagram example the parameters are w = 60, d = 120, θ = 40°, c0 = (96, 438), s0 = -1 (starting with left foot).
UPDATE: JavaScript code snippet
this.startingPosX = Math.floor(Math.random() * 1000) + 20;
this.startingPosY = Math.floor(Math.random() * 560) + 20;
this.startingAngle = Math.floor(Math.random() * 340) + 20;
this.startingFoot = 1 - 2 * Math.round(Math.random()); // select which foot to start with
this.stepSize = 120;
this.stepWidth = 60;
footsteps(0);
footsteps(n) {
// should actually pre-calculate these outside but oh well
let a = this.startingAngle * Math.PI / 180;
let c = Math.cos(a), s = Math.sin(a);
// current foot
let d = this.startingFoot * (1 - 2 * (n % 2));
// apply equations
this.footprintX = Math.round(
this.startingPosX + // initial
this.stepSize * n * c + // step
this.stepWidth * 0.5 * d * s // foot offset
);
this.footprintY = Math.round(
this.startingPosY + // initial
this.stepSize * n * s - // step
this.stepWidth * 0.5 * d * c // foot offset
);
// draw the foot here
console.log(this.footprintX);
console.log(this.footprintY);
// increment the step counter for the next call
setInterval(function() { footsteps(n+1); }, 3000);
}
Related
My Rocket is hitting this Inertia object, as defined in handleCollision. I'm passing in a rocket which has a .r value for its theta and .power for its magnitude.
I'm wanting to update my .rotation & .magnitude according to an inelastic collision as defined by Wikipedia
When colliding from the left, my Inertia moves to the right.
But when colliding from the right it errors and moves exactly 180 degrees off. So if the rocket is up and right at a 45 degree angle from the inertia object, the object will move up and right at a 45 degree angle.
What am I missing here? I thought it might be an issue with the atan function so I converted by the y component & x component of the vector to radians first, same issue.
handleCollision(rocket) {
var angle = rocket.r * Math.PI / 180.0;
var rr = this.rotation * Math.PI / 180;
var rocketVector = {'x' : r.power * Math.cos(angle), 'y' : r.power * Math.sin(angle)};
var inertiaVector = {'x' : this.magnitude * Math.cos(rr), 'y' : this.magnitude * Math.sin(rr)};
var rMass = 10;
var shipMass = 10;
var x = (rMass * rocketVector.x) + (shipMass * inertiaVector.x);
var y = (rMass * rocketVector.y) + (shipMass * inertiaVector.y);
var xDividedByMass = x / (rMass + shipMass);
var yDividedByMass = y / (rMass + shipMass);
var yRadians = (yDividedByMass * Math.PI / 180);
var xRadians = (xDividedByMass * Math.PI / 180);
var theta = Math.atan( yRadians / xRadians);
theta = theta * 180 / Math.PI;
console.log(theta);
var hypotenuse = Math.sqrt((xDividedByMass * xDividedByMass) + (yDividedByMass * yDividedByMass));
this.magnitude = hypotenuse;
this.rotation = theta;
if (this.rotation < 0) {
this.rotation += 360;
} else if (this.rotation > 360) {
this.rotation -= 360;
}
}
If xDividedbyMass>0, you are great because you are quadrant I or IV where arctangent kicks out its values. If you do not like the negative angle, okay add 360 like you did.
But if x<0 and y>0, you will get a negative angle and want to add 180 to get to Q II (tangent has a period of 180). And if x<0, and y<0, you are in QIII and again arctan gives you something in Q1 to which you must add 180.
The logic will look something like this.
if ((x > 0) && (y<0)) {
this.rotation += 360;
} else if (x<0) {
this.rotation += 180;
}
So I have an object rotating around an origin point. Once I rotate and then change the origin point. My object seems to jump positions. After the jump it rotates fine... Need help finding the pattern/why it's jumping and what I need to do to stop it.
Here's the rotation code:
adjustMapTransform = function (_x, _y) {
var x = _x + (map.width/2);
var y = _y + (map.height/2);
//apply scale here
var originPoint = {
x:originXInt,
y:originYInt
};
var mapOrigin = {
x:map.x + (map.width/2),
y:map.y + (map.height/2)
};
//at scale 1
var difference = {
x:mapOrigin.x - originPoint.x,
y:mapOrigin.y - originPoint.y
};
x += (difference.x * scale) - difference.x;
y += (difference.y * scale) - difference.y;
var viewportMapCentre = {
x: originXInt,
y: originYInt
}
var rotatedPoint = {};
var angle = (rotation) * Math.PI / 180.0;
var s = Math.sin(angle);
var c = Math.cos(angle);
// translate point back to origin:
x -= viewportMapCentre.x;
y -= viewportMapCentre.y;
// rotate point
var xnew = x * c - y * s;
var ynew = x * s + y * c;
// translate point back:
x = xnew + viewportMapCentre.x - (map.width/2);
y = ynew + viewportMapCentre.y - (map.height/2);
var coords = {
x:x,
y:y
};
return coords;
}
Also here is a JS Fiddle project that you can play around in to give you a better idea of what's happening.
EDITED LINK - Got rid of the originY bug and scaling bug
https://jsfiddle.net/fionoble/6k8sfkdL/13/
Thanks!
The direction of rotation is a consequence of the sign you pick for the elements in your rotation matrix. [This is Rodrigues formula for rotation in two dimensions]. So to rotate in the opposite direction simply subtract your y cosine term rather than your y sine term.
Also you might try looking at different potential representations of your data.
If you use the symmetric representation of the line between your points you can avoid shifting and instead simply transform your coordinates.
Take your origin [with respect to your rotation], c_0, to be the constant offset in the symmetric form.
You have for a point p relative to c_0:
var A = (p.x - c_0.x);
var B = (p.y - c_0.y);
//This is the symmetric form.
(p.x - c_0.x)/A = (p.y - c_0.y)/B
which will be true under a change of coordinates and for any point on the line (which also takes care of scaling/dilation).
Then after the change of coordinates for rotation you have [noting that this rotation has the opposite sense, not the same as yours].
//This is the symmetric form of the line incident on your rotated point
//and on the center of its rotation
((p.x - c_0.x) * c + (p.y - c_0.y) * s)/A = ((p.x - c_0.x) * s - (p.y - c_0.y) * c)/B
so, multiplying out we get
(pn.x - c_0.x) * B * c + (pn.y - c_0.y) * B * s = (pn.x - c_0.x) * A * s - (pn.y - c_0.y) * A * c
rearrangement gives
(pn.x - c_0.x) * (B * c - A * s) = - (pn.y - c_0.y) * (B * s + A * c)
pn.y = -(pn.x - c_0.x) * (B * c - A * s) / (B * s + A * c) + c_0.y;
for any scaling.
I want to plot a range of points on the lower left section (6 to 9 o'clock) of the perimeter of a circle. However, the starting point of rendering X,Y coordinates always begins at 3 o'clock.
!https://dl.dropboxusercontent.com/u/55849501/plotting-xy.png
Here is the rendering portion of my code:
var items = 5;
for(var i = 0; i < items; i++) {
var x = 96 + 100 * Math.cos(0.665 * Math.PI * i / items);
var y = 96 + 100 * Math.sin(0.665 * Math.PI * i / items);
$("#center").append("<div class='point' style='left:"+ x +"px;top:"+ y +"px'></div>");
}
And here is a jsfiddle of the code in action: http://jsfiddle.net/jE26S/198/
In summary:
I want the points to render starting at the 6 o'clock position instead of the 3 o'clock position.
What you are really doing here is interpolating between two values of theta. In your case, you want to start at Pi/2 and end at Pi. I took the liberty of re-writing your snippet using this interpolation paradigm. Also, you can adjust how far you want the dots/items away from the circle using outerCircleRadius.
var items = 5;
var startTheta = .5 * Math.PI;
var endTheta = 1 * Math.PI;
var outerCircleRadius = 112;
var cx = 90;
var cy = 90;
for(var i = 0; i < items; i++) {
var theta = startTheta + (endTheta - startTheta) * i / (items - 1)
var x = cx + outerCircleRadius * Math.cos(theta);
var y = cy + outerCircleRadius * Math.sin(theta);
$("#center").append("<div class='point' style='left:"+ x +"px;top:"+ y +"px'></div>");
}
Something like this?
var x = 86 + 100 * Math.cos(0.665 * Math.PI * (items-1+i-0.5) / items);
var y = 96 + 100 * Math.sin(0.665 * Math.PI * (items-1+i-0.5) / items);
Everything in Richard Shurtz's answer, except for the "items - 1" in the first line in the loop.
This worked for any number of items:
var items = 5;
var startTheta = .5 * Math.PI;
var endTheta = 1 * Math.PI;
var outerCircleRadius = 112;
var cx = 90;
var cy = 90;
for(var i = 0; i < items; i++) {
var theta = startTheta + (endTheta - startTheta) * i / items
var x = cx + outerCircleRadius * Math.cos(theta);
var y = cy + outerCircleRadius * Math.sin(theta);
$("#center").append("<div class='point' style='left:"+ x +"px;top:"+ y +"px'></div>");
}
I need som help on moving the elements in the array I created call the "Predator" array. Now I have all the images appearing on the SVG box as well as stored in the array.
I have difficulties calling them in a "for" loop and to move them randomly. Further that, I would want to animate the images more closely to real life. Example pls take a look on this website.
<script>
var startPredator = 20; //number of fish to start with
var Predator = []; //array of fish
var predatorW = 307; //fish width
var predatorH = 313; //fish height
var velocity = 100; //base velocity
var imageStrip; //predator image strip
//var backgroundImage; //background image
//var backgroundImageW = 981; //background image width
//var backgroundImageH = 767; //background image height
//var WIDTH = document.body.offsetWidth;
//var HEIGHT = document.body.offsetHeight;
function animate(){
document.animation.src = arraylist[imgNum].src
imgNum++
for (var i=arraylist[].length; i--;){
arraylist[i].move();
}
setInterval(function () { draw(); }, 16.7);
}
function start() {
for (var i = 0; i < arraylist.length; i++) {
var image = document.createElement("img");
image.alt = images[i][0];
image.width = "150";
image.height = "150";
image.src = images[i][1];
var j = i;
if (j > 2) j = i - 3;
document.getElementsByTagName("div")[j].appendChild(image);
}
}
function Predator() {
var angle = Math.PI * 2 * Math.random(); //set the x,y direction this predator swims
var xAngle = Math.cos(angle); //set the x value of the angle
var yAngle = Math.sin(angle); //set the y value of the angle
var zAngle = 1+-2*Math.round(Math.random()); //set if the predator is swimming toward us or away. 1 = toward us; -1 = away from us
var x = Math.floor(Math.random() * (WIDTH - predatorW) + predatorW / 2); //set the starting x location
var y = Math.floor(Math.random() * (HEIGHT - predatorH) + predatorH / 2); //set the starting y location
var zFar = 100; //set how far away can a predator go
var zFarFactor = 1; //set the max size the predator can be. 1=100%
var zClose = 0; //set how near a predator can come
var z = Math.floor(Math.random() * ((zFar - zClose))); //set the starting z location
var scale = .1; //set the rate of scaling each frame
var flip = 1; //set the direction of the fish. 1=right; -1=left
var cellCount = 16; //set the number of cells (columns) in the image strip animation
var cell = Math.floor(Math.random() * (cellCount-1)); //set the first cell (columns) of the image strip animation
var cellReverse = -1; //set which direction we go through the image strip
var species = Math.floor(Math.random() * 3); //set which species of predator this predator is. each species is a row in the image strip
// stop predator from swimming straight up or down
if (angle > Math.PI * 4 / 3 && angle < Math.PI * 5 / 3 || angle > Math.PI * 1 / 3 && angle < Math.PI * 2 / 3) {
angle = Math.PI * 1 / 3 * Math.random();
xAngle = Math.cos(angle);
yAngle = Math.sin(angle);
}
// face the predator the right way if angle is between 6 o'clock and 12 o'clock
if (angle > Math.PI / 2 && angle < Math.PI / 2 * 3) {
flip = -1;
}
function swim() {
// Calculate next position of species
var nextX = x + xAngle * velocity * fpsMeter.timeDeltaS;
var nextY = y + yAngle * velocity * fpsMeter.timeDeltaS;
var nextZ = z + zAngle * .1 * velocity * fpsMeter.timeDeltaS;
var nextScale = Math.abs(nextZ) / (zFar - zClose);
// If species is going to move off right side of screen
if (nextX + fishW / 2 * scale > WIDTH) {
// If angle is between 3 o'clock and 6 o'clock
if ((angle >= 0 && angle < Math.PI / 2)) {
angle = Math.PI - angle;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
// If angle is between 12 o'clock and 3 o'clock
else if (angle > Math.PI / 2 * 3) {
angle = angle - (angle - Math.PI / 2 * 3) * 2
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
}
// If fish is going to move off left side of screen
if (nextX - fishW / 2 * scale < 0) {
// If angle is between 6 o'clock and 9 o'clock
if ((angle > Math.PI / 2 && angle < Math.PI)) {
angle = Math.PI - angle;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
// If angle is between 9 o'clock and 12 o'clock
else if (angle > Math.PI && angle < Math.PI / 2 * 3) {
angle = angle + (Math.PI / 2 * 3 - angle) * 2
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
}
// If fish is going to move off bottom side of screen
if (nextY + fishH / 2 * scale > HEIGHT) {
// If angle is between 3 o'clock and 9 o'clock
if ((angle > 0 && angle < Math.PI)) {
angle = Math.PI * 2 - angle;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
}
}
// If fish is going to move off top side of screen
if (nextY - fishH / 2 * scale < 0) {
// If angle is between 9 o'clock and 3 o'clock
if ((angle > Math.PI && angle < Math.PI * 2)) {
angle = angle - (angle - Math.PI) * 2;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle);
}
}
// If fish is going too far (getting too small)
if (nextZ <= zClose && zAngle < 0) {
zAngle = -zAngle;
}
// If fish is getting to close (getting too large)
if (((WIDTH / fishW) * 10) < ((fishW * fish.length) / WIDTH)) {
zFarFactor = .3
}
else if (((WIDTH / fishW) * 2) < ((fishW * fish.length) / WIDTH)) {
zFarFactor = .5
}
else { zFarFactor = 1 }
if (nextZ >= zFar * zFarFactor && zAngle > 0) {
zAngle = -zAngle;
}
if (scale < .1) { scale = .1 }; //don't let fish get too tiny
//draw the fish
//locate the fish
ctx.save();
ctx.translate(x, y);
ctx.scale(scale, scale); // make the fish bigger or smaller depending on how far away it is.
ctx.transform(flip, 0, 0, 1, 0, 0); //make the fish face the way he's swimming.
ctx.drawImage(imageStrip, fishW * cell, fishH * species, fishW, fishH, -fishW / 2, -fishH / 2, fishW, fishH); //draw the fish
ctx.save();
scale = nextScale // increment scale for next time
ctx.restore();
ctx.restore();
//increment to next state
x = nextX;
y = nextY;
z = nextZ;
if (cell >= cellCount-1 || cell <= 0) { cellReverse = cellReverse * -1; } //go through each cell in the animation
cell = cell + 1 * cellReverse; //go back down once we hit the end of the animation
}
return {
swim: swim
}
}
</script>
My concern is more on the animate and start function. Am I calling the images from the array to move?
Please offer a helping hand. Thanks
I have been using this this oval function (that I found at Wikipedia http://en.wikipedia.org/wiki/Ellipse) for plotting points in a layout. I have been laying things out with two to five points plotted around an oval without any problem. The function has a parameter called 'steps'; the 'steps' parameter sets the quantity of points to plot around the oval.
Here's the main problem: If 'steps' (the number of points to plot) is equal to the numbers 7, 11, 13 or 14, it breaks. It's been a few years since I took trigonometry, so basically I am stuck.
Second minor issue: I had the code printing out all points, but when I copied/pasted and removed extraneous code to post here, it only prints out the last plotting point (not sure why).
<html>
<head>
<script type="text/javascript">
var elipticalLayout=new Array();
for (i=0; i <36; i++){
elipticalLayout[i]=new Array(2);
}
/*
* This functions returns an array containing the specified
* number of 'steps' (points) to draw an ellipse.
*
* #param x {double} X coordinate
* #param y {double} Y coordinate
* #param a {double} Semimajor axis
* #param b {double} Semiminor axis
* #param angle {double} Angle of the ellipse
*
* Attribution: This function is from http://en.wikipedia.org/wiki/Ellipse
*/
function calculateEllipticalLayout(x, y, a, b, angle, steps) {
var points = [];
// Angle is given by Degree Value
var beta = -angle * (Math.PI / 180); //(Math.PI/180) converts Degree Value into Radians
var sinbeta = Math.sin(beta);
var cosbeta = Math.cos(beta);
for (var i = 0; i < 360; i += 360 / steps) //{
var alpha = i * (Math.PI / 180) ;
var sinalpha = Math.sin(alpha);
var cosalpha = Math.cos(alpha);
var X = x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta);
var Y = y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta);
elipticalLayout[i/(360/steps)][0]=X;
elipticalLayout[i/(360/steps)][1]=Y;
}
</script>
</head>
<body>
<script type="text/javascript">
calculateEllipticalLayout(300, 300, 245, 125, 15, 15);
for (i=0; i<elipticalLayout.length; i++){
document.write(i + ", " + elipticalLayout[i][0] + ", " + elipticalLayout[i][1] + "<br>");
}
</script>
</body>
</html>
I would declare elipticalLayout within the function and return it. Below is how I'd write the function.
Note that there is no such thing as a "double" or even integer in javascript, a variable's type is defined by its value. Numbers are just numbers, they can be primitives or Number objects (almost never required).
If you are using the returned values to plot positions in pixels, you likely want to round them to integers first. I've used a simple truncation method to convert them to integers.
var elipticalLayout = [];
/*
* This functions returns an array containing the specified
* number of 'steps' (points) to draw an ellipse.
*
* #param x - X coordinate
* #param y - Y coordinate
* #param a - Semimajor axis
* #param b - Semiminor axis
* #param angle - Angle of the ellipse
*
* Attribution: This function is from http://en.wikipedia.org/wiki/Ellipse
*/
function calculateEllipticalLayout(x, y, a, b, angle, steps) {
var points = [];
var step = 360 / steps;
var k = Math.PI/180; // Convert deg to rad
var cos = Math.cos;
var sin = Math.sin;
var i = 0;
// Convert angle to radians
var beta = -angle * k;
var sinbeta = sin(beta);
var cosbeta = cos(beta);
while (i < 360) {
var alpha = i * k;
var sinalpha = sin(alpha);
var cosalpha = cos(alpha);
var X = x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta);
var Y = y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta);
// Truncate numbers to integer and put into array
elipticalLayout.push([X|0, Y|0]);
// Keep X,Y as floats
// elipticalLayout.push([X, Y]);
i += step;
}
}
You fill only steps number of elements in elipticalLayout.
But trying to output 36 of them.
This elipticalLayout[i/(360/steps)][0]=X; is wrong as will lead to "holes" in the sequence due to float to int rounding.
You should have something like this:
var n = 0;
for(...)
elipticalLayout[n][0]=X;
elipticalLayout[n][1]=Y;
++n;