I have written myself a formula to rotate the points of regular polygons (and maybe even irregular) around an orientation point. The formula seems to work on discretely coded rectangles:
tempShape.points = [[0,0],[wid,0],[wid,hig],[0,hig]];
But not on polygons generated by formula:
tempShape.points = [[len,0]];
for(var i=1; i < poin; i++){
if(gen){
tempShape.points[i] = [0,0];
tempShape.points[i][0] = len*Math.cos(angle*i);
tempShape.points[i][1] = len*Math.sin(angle*i);
}
}
This is my rotation formula:
for(var i2 = 0; i2 < displayList[i].points.length; i2++){
displayList[i].points[i2][0] = (displayList[i].pointsRot[i2][0] + displayList[i].oPoint[0]) * Math.cos(displayList[i].rotation) - (displayList[i].pointsRot[i2][1] + displayList[i].oPoint[1]) * Math.sin(displayList[i].rotation);
displayList[i].points[i2][1] = (displayList[i].pointsRot[i2][1] + displayList[i].oPoint[1]) * Math.cos(displayList[i].rotation) + (displayList[i].pointsRot[i2][0] + displayList[i].oPoint[0]) * Math.sin(displayList[i].rotation);
}
Where displayList[i] is the shape, .points is the array containing all the points, .rotation is the rotation of the shape, .oPoint is the point of orientation.
I hope you can help, thanks in advance.
Kyle.
Edit:
Just tried rather than incrementing the rotation I set a static rotation of 0.01, but this makes it act like it would normally if incremented the rotation by 0.01, so somewhere (I don't know where) it must be like incrementing my rotation by the set rotation, I just need to figure it out. Could I give my whole code for someone to look through to help me?
You are using in the second formula value displayList[i].pointsRot[i2][0] which has been already changed in the first formula. Just remember original value in temporary variable and use it.
Edit: It seems I was entangled in your longlonglong names.
Сheck that your formulas match to:
NewX = CenterX + (OldX-CenterX)*Cos(Fi) - (OldY-CenterY)*Sin(Fi)
NewY = CenterY + (OldX-CenterX)*Sin(Fi) + (OldY-CenterY)*Cos(Fi)
where (CenterX, CenterY) is point of rotation
Related
I am working on a "rally" game where a car is drawing on hills made of cosine curves. I know the current xspeed of the car (without hills) but the problem is that I need to know the xspeed of the car on the hills to be able to draw the wheels on right places and keep the speed steady.
At the moment my solution looks like this.
function drawWheelOnBasicHill(hillStart, xLocWheel, wheelNro) {
var cw = 400 //the width of the hill
t_max = 2*Math.PI;
var scale = 80, step = cw, inc = t_max/step;
var t1 = (xLocWheel-hillStart)*inc
var y1 = -scale*0.5 * Math.cos(t1);
if(wheelNro == 1 ){ //backwheel
drawRotatedImage(wheel, car.wheel1x, car.wheel1y-y1-45,sx);
//drawing the wheel on canvas
} else { //frontwheel
drawRotatedImage(wheel, car.wheel2x, car.wheel2y-y1-45,sx);
}
for(var i=1; i<=car.speed; i++){ //finding the next xlocation of the wheel with the
//same distance (on the curve) to the previous location as the speed of the car(=the
//distance to the new point on the flat ground)
var t2 = (xLocWheel + i -hillStart)*inc
var y2 = -scale*0.5 * Math.cos(t2);
if(Math.round(Math.sqrt(i^2+(y2-y1)^2))==car.speed){
sx = sx+i; //the new xcoordinate break;
}
}
}
The for loop is the problem. It might bee too slow (animation with fps 24). I cant understand why the if statement isnt working at the moment. It works sometimes but most of the times the value of the condition newer reaches the actual xspeed.
Are there some more efficient and easier ways to do this? Or does this code contain some errors? I really appreciate your efforts to solve this! Ive been looking at this piece of code the whole day..
So i is the variable and
x2=x1+i
t2=t1+i*inc
y1=-scale*0.5 * Math.cos(t1)
y2=-scale*0.5 * Math.cos(t2)
which somehow is strange. The landscape should be time independent, that is, y should be a function of x only. The time step is external, determined by the speed of the animation loop. So a more logical model would have dx as variable and
dt = t2-t1
x2 = x1 + dx
y1 = f(x1) = -0.5*scale*cos(x1)
y2 = f(x2) = -0.5*scale*cos(x2)
and you would be looking for the intersection of
(x2-x1)^2+(y2-y1)^2 = (speed*dt)^2
which simplifies to
(speed*dt)^2=dx^2+0.25*scale^2*(cos(x1+dx)-cos(x1))^2
For small values of dx, which would be the case if dt or speed*dt is small,
cos(x1+dx)-cos(x1) is approx. -sin(x1)*dx
leading to
dx = (speed*dt) / sqrt( 1+0.25*scale^2*sin(x1)^2 )
To get closer to the intersection of curve and circle, you can then iterate the fixed point equation
dydx = 0.5*scale*(cos(x1+dx)-cos(x1))/dx
dx = (speed*dt) / ( 1+dydx^2 )
a small number of times.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Did you ever played the "Tank wars" game?
I'm programming this game with JavaScript + Canvas (for a personal challenge), and what I need is an algorithm for generating that random green land every time I start the game, but I'm not too good at maths, so I can't do it myself.
I don't want someone to give me the code, I only want the idea for the algorithm.
Thanks!
(9 segments)
Fiddle demo
(7 segments)
The main generation function look like this:
var numOfSegments = 9; // split horizontal space
var segment = canvas.width / numOfSegments; // calc width of each segment
var points = [], calcedPoints;
var variations = 0.22; // adjust this: lower = less variations
var i;
//produce some random heights across the canvas
for(i=0; i < numOfSegments + 1; i++) {
points.push(segment * i);
points.push(canvas.height / 2.8 + canvas.height * variations * Math.random());
}
//render the landscape
ctx.beginPath();
ctx.moveTo(canvas.width, canvas.height);
ctx.lineTo(0, canvas.height);
calcedPoints = ctx.curve(points); // see below
ctx.closePath();
ctx.fillStyle = 'green';
ctx.fill();
The curve() function is a separate function which generate a cardinal spline. In here you can modify it to also store tension values to make more spikes. You can also used the generated points as a basis for where and at what angle the tanks will move at.
The function for cardinal spline:
CanvasRenderingContext2D.prototype.curve = function(pts, tension, numOfSegments) {
tension = (tension != 'undefined') ? tension : 0.5;
numOfSegments = numOfSegments ? numOfSegments : 16;
var _pts = [], res = [], t, i, l, r = 0,
x, y, t1x, t2x, t1y, t2y,
c1, c2, c3, c4, st, st2, st3, st23, st32;
_pts = pts.concat();
_pts.unshift(pts[1]);
_pts.unshift(pts[0]);
_pts.push(pts[pts.length - 2]);
_pts.push(pts[pts.length - 1]);
l = (_pts.length - 4);
for (i = 2; i < l; i+=2) {
//overrides and modifies tension for each segment.
tension = 1 * Math.random() - 0.3;
for (t = 0; t <= numOfSegments; t++) {
t1x = (_pts[i+2] - _pts[i-2]) * tension;
t2x = (_pts[i+4] - _pts[i]) * tension;
t1y = (_pts[i+3] - _pts[i-1]) * tension;
t2y = (_pts[i+5] - _pts[i+1]) * tension;
st = t / numOfSegments;
st2 = st * st;
st3 = st2 * st;
st23 = st3 * 2;
st32 = st2 * 3;
c1 = st23 - st32 + 1;
c2 = -(st23) + st32;
c3 = st3 - 2 * st2 + st;
c4 = st3 - st2;
x = c1 * _pts[i] + c2 * _pts[i+2] + c3 * t1x + c4 * t2x;
y = c1 * _pts[i+1] + c2 * _pts[i+3] + c3 * t1y + c4 * t2y;
res[r++] = x;
res[r++] = y;
} //for t
} //for i
l = res.length;
for(i=0;i<l;i+=2) this.lineTo(res[i], res[i+1]);
return res; //return calculated points
}
Look into perlin noise generation, this in combination with a good smoothing algorithm can produce some pretty good terrain, and is fairly quick. There is a reference version of the code kicking around the net somewhere, which should provide you with a fairly hefty headstart
First you need a point that is random y (between 55,65); got x=0
So this is the origin point for the green, lets keep it as x1,y1 (x1 always 0).
Then you need a random integer between 30 to 40. This is x2. And a random y which is in the range y1 + 8 to y1 + 20.
Then x3 and y3 on same principle (lets call it formula type 1)
Now you need to first get a random either -1 or 1, this will be directions of y4. So y4 can go higher than y3 or lower ... this will be formula type 2.
You need to keep a max and min y for a new y, if it crosses that then go the other way -> this will be a correction type formula 3.
Xn keeps increasing till its >= width of board.
Join the lines in a eclipses ... and looks like web searches is the way to go !
I am sure there are a lot of coded libraries that you could use to make this easy. But if you are trying to code this by yourself, here is my idea.
You need to define terrain from everything else. So every part of your environment is a cluster for example. You need to define how are separated these clusters, by nodes(points) for example.
You can create a polygon from a sequence of points, and this polygon can become whatever you want, in this case terrain.
See that on the image you passed, there are peaks, those are the nodes (points). Remember to define also nodes on the borders of your environment.
There are surely a novel, written algorithms, either fractal as #DesertIvy pointed out or others, maybe there are libraries as well, but if you want toi generate what is in the image, it can be pretty straightforward, since it is just (slightly curved) lines between points. If you do it in phases, not trying to be correct at once, it is easy:
Split x region of your game screen into sections (with some minimal and maximal width) using random (you may be slightly off in last section, but it does not matter as much, I think). Remember the x-es where sections meet (including the ones at game screen border)
Prepare some data structure to include y-s as well, on previously remembered x-s. Start with leftmost.y = 0, slope = Math.random()-0.5;.
Generate each next undefined y beginning with 1: right.y = left.y + slope * (right.x-left.x); as well as update slope after each y: slope += Math.random()-0.5;. Do not bother, for the moment, if it all fits into game screen.
If you want arcs, you can generate "curviness" parameter for each section randomly which represent how much the middle of the line is bumped compared to straight lines.
Fit the ys into the game screen: first find maximal and minimal generated y (mingeny, maxgeny) (you can track this while generating in point 4). Choose where the max and min y in game screen (minscry, maxscry) (say at the top fourth and at the bottom fourth). Then transform generated ys so that it spans between minscry and maxscry: for every point, do apoint.y = minscry + (maxscry-minscry)/(maxgeny-mingeny)*(apoint.y-mingeny).
Now use lines between [x,y] points as a terrain, if you want to use "curviness", than add curvemodifier to y for any particular x in a section between leftx and rightx. The arc need not to be a circle: I would suggest a parabola or cosine which are easy to produce: var middle = (left.x+right.x)/2; var excess = (x-left)/(middle-left); and then either var curvemodifier = curviness * (1-excess*excess); or var curvemodifier = curviness * Math.cos(Math.PI/2*excess).
Wow...At one point I was totally addicted to tank wars.
Since you are on a learning adventure...
You might also learn about the context.globalCompositeOperation.
This canvas operation will let you grab an image of actual grass and composite it into your game.
You can randomize the grass appearance by changing the x/y of your drawImage();
Yes, the actual grass would probably be too distracting to include in your finished game, but learning about compositing would be valuable knowledge to have.
...and +1 for the question: Good for you in challenging yourself !
How can I place rectangles with variable width and height, randomly in a stage but away from a circle in the center which has radius of x
Thanks in advance
EDIT
check my code so far
http://jsfiddle.net/chchrist/cAShH/1/
The three potential options I would follow are:
Generate random coordinates in [400,400] and then check that the distance from [200,200] is less than 50. If it is, fine; if not, start again.
Generate random polar coordinates (i.e., angle and distance), where the distance is greater than 50. Then convert these to Cartesian, centred around [200,200] and bounded to your area... The problem with this approach is that it would introduce bias at the extremities of your rectangular area.
Ignore the circle and bound it by a square, then use the first approach but with simplified logic.
One approach might be to think about how to map uniform random numbers into legal positions.
For example (simplifying slightly), if you had a 200 x 200 square, and you wanted to avoid any points in a 100x100 square in the middle, you could do the following for each coordinate. Generate a random number between 0 and 100. If it's less than 50, use it directly; otherwise add 100 to it (to put it in the 150-200 range)
Conceptually this stretches the range around the "hole" in the middle, while still leaving the resulting points uniformly distributed.
It'll be trickier with your circle, as the axes are not independent, but a variation on this method could be worth considering. (Especially if you only have "soft" requirements for randomness and so can relax the constraints on the distribution somewhat).
I would start with a coordinate system centered at 0,0 and after you have generated valid coordinates map them onto your square/rectangle.
Here's a simple example:
function getValidCoordinates() {
var x, y, isValid = false;
while (!isValid) {
x = Math.random() * 400 - 200;
y = Math.random() * 400 - 200;
if (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) > 50)
isValid = true;
//else alert('too close ' + x + ',' + y);
}
return {x: x + 200, y: y + 200};
}
for (var i=0; i < 10; i++) {
var co = getValidCoordinates();
alert('x=' + co.x + ', y=' + co.y);
}
I'm sorry to say that Math really isn't my strong suit. Normally I can get by, but this has got me totally stumped.
I'm trying to code up a quiz results screen in HTML/CSS/Javascript.
On my interface, I have a semicircle (the right hemisphere of a target).
I have a range of 'scores' (integers out of 100 - so 50, 80, 90 etc.).
I need to plot these points on the semicircle to be n% away from the centre, where n is the value of each score - the higher the score, the closer to the centre of the target the point will appear.
I know how wide my semicircle is, and have already handled the conversion of the % values so that the higher ones appear closer to the centre while the lower ones appear further out.
What I can't wrap my head around is plotting these points on a line that travels out from the centre point (x = 0, y = target height/2) of the target at a random angle (so the points don't overlap).
Any suggestions are gratefully received!
Do you have an example of what you want this to look like? It sounds like you want to divide up the circle into N slices where N is the number of points you need to display, then plot the points along each of those radii. So you might have something like:
Edit: code was rotating about the origin, not the circle specified
var scores = [];
//...
//assume scores is an array of distances from the center of the circle
var points = [];
var interval = 2 * Math.PI / N;
var angle;
for (var i = 0; i < N; i++) {
angle = interval * i;
//assume (cx, cy) are the coordinates of the center of your circle
points.push({
x: scores[i] * Math.cos(angle) + cx,
y: scores[i] * Math.sin(angle) + cy
});
}
Then you can plot points however you see fit.
After much headscratching, I managed to arrive at this solution (with the help of a colleague who's much, much better at this kind of thing than me):
(arr_result is an array containing IDs and scores - scores are percentages of 100)
for (var i = 0; i < arr_result.length; i++){
var angle = angleArray[i]; // this is an array of angles (randomised) - points around the edge of the semicircle
var radius = 150; // width of the semicircle
var deadZone = 25 // to make matters complicated, the circle has a 'dead zone' in the centre which we want to discount
var maxScore = 100
var score = parseInt(arr_result[i]['score'], 10)
var alpha = angle * Math.PI
var distance = (maxScore-score)/maxScore*(radius-deadZone) + deadZone
var x = distance * Math.sin(alpha)
var y = radius + distance * Math.cos(alpha)
$('#marker_' + arr_result[i]['id'], templateCode).css({ // target a specific marker and move it using jQuery
'left' : pointX,
'top': pointY
});
}
I've omitted the code for generating the array of angles and randomising that array - that's only needed for presentational purposes so the markers don't overlap.
I also do some weird things with the co-ordinates before I move the markers (again, this has been omitted) as I want the point to be at the bottom-centre of the marker rather than the top-left.
Over the last two days I've effectively figured out how NOT to rotate Raphael Elements.
Basically I am trying to implement a multiple pivot points on element to rotate it by mouse.
When a user enters rotation mode 5 pivots are created. One for each corner of the bounding box and one in the center of the box.
When the mouse is down and moving it is simple enough to rotate around the pivot using Raphael elements.rotate(degrees, x, y) and calculating the degrees based on the mouse positions and atan2 to the pivot point.
The problem arises after I've rotated the element, bbox, and the other pivots. There x,y position in the same only there viewport is different.
In an SVG enabled browser I can create new pivot points based on matrixTransformation and getCTM. However after creating the first set of new pivots, every rotation after the pivots get further away from the transformed bbox due to rounding errors.
The above is not even an option in IE since in is VML based and cannot account for transformation.
Is the only effective way to implement
element rotation is by using rotate
absolute or rotating around the center
of the bounding box?
Is it possible at all the create multi
pivot points for an object and update
them after mouseup to remain in the
corners and center of the transformed
bbox?
UPDATE:
I've attempted to use jQuery offset to find the pivot after it's been rotated, and to use that offset location as the pivot point.
Demo site ...
http://weather.speedfetishperformance.com/dev/raphael/rotation.html
The best cross-browser way I can think of to do what you want is to implement the rotation yourself rather than let SVG do it. Rotating x,y coordinates is fairly simple and I've been using this (tcl) code whenever I need to do 2D rotation: Canvas Rotation.
The upside to this is you have maximum control of the rotation since you're doing it manually. This solves the problems you're having trying to guess the final coordinates after rotation. Also, this should be cross browser compatible.
The downside is you have to use paths. So no rects (though it should be easy to convert them to paths) or ellipses (a little bit harder to convert to path but doable). Also, since you're doing it manually, it should be slower than letting SVG do it for you.
Here's a partial implementation of that Tcl code in javascript:
first we need a regexp to tokenize SVG paths:
var svg_path_regexp = (function(){
var number = '-?[0-9.]+';
var comma = '\s*[, \t]\s*';
var space = '\s+';
var xy = number + comma + number;
var standard_paths = '[mlcsqt]';
var horiz_vert = '[hv]\s*' + number;
var arc = 'a\s*' + xy + space + number + space + xy + space + xy;
var OR = '\s*|';
return new RegExp(
standard_paths +OR+
xy +OR+
horiz_vert +OR+
arc,
'ig'
);
})();
now we can implement the rotate function:
function rotate_SVG_path (path, Ox, Oy, angle) {
angle = angle * Math.atan(1) * 4 / 180.0; // degrees to radians
var tokens = path.match(svg_path_regexp);
for (var i=0; i<tokens.length; i++) {
var token = tokens[i].replace(/^\s+|\s+$/g,''); // trim string
if (token.match(/\d/)) { // assume it's a coordinate
var xy = token.split(/[, \t]+/);
var x = parseFloat(xy[0]);
var y = parseFloat(xy[1]);
x = x - Ox; // Shift to origin
y = y - Oy;
var xx = x * Math.cos(angle) - y * Math.sin(angle); // Rotate
var yy = x * Math.sin(angle) + y * Math.cos(angle);
x = xx + Ox; // Shift back
y = yy + Oy;
token = x + ',' + y;
}
else if (token.match(/^[hv]/)) {
// handle horizontal/vertical line here
}
else if (token.match(/^a/)) {
// handle arcs here
}
tokens[i] = token;
}
return tokens.join('');
}
The above rotate function implements everything except horizontal/vertical lines (you need to keep track of previous xy value) and arcs. Neither should be too hard to implement.