Sub pixel antialiasing canvas pixel shift algorithms in javascript - javascript

I'm trying to work out a way of doing pseudo 3d, distorting textures with the javascript canvas.
The best method for my needs so far has been to use displacement maps which I'm largely following from this tutorial and source code example.
The basic principle is to use the channel level (RGBA) from a selected pixel in the displacement map then applying a pixel shifting algorithm... all good so far.
The problem is that this method of shifting the texture image's pixels is very binary and renders a slightly 'jagged' edge due to the fact that - it's simply shifting 'full' pixels.
When compared to PhotoShop or some of the ImageMagick examples here the javascript method looks much less realistic. This is due to PS & IMs sub-pixel processing abilities whereby medians can be derived for inter-pixel data.
Question: Can anyone suggest a step which can be integrated into my algorithm to produce a gaussian/aliased smoothness to the output?
Perhaps I can simply run the imagedata through an FFT and back again? are there any examples of this in action?
I'm a little stumped and would very much appreciate some pointers.

1) You are mentionning two very differents algorithms : Displacement mapping is a 3D technique, so it involves 'Z', and projection, and the other one is a 2D pixel shifting algorithm way way simpler.
(The soundstep link provided uses the word 'displacement mapping', yet it is a pixel shifting
technique.)
2) Whatever the size of your MVC project, the algorithm should be isolated, and have a signature like :
var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset)
and either return a new canvas OR change sourceCanvas in place.
If there's no such function, please do not talk about MVC, unless the M stands for 'Mess'. ;-) -
3) The algorithm is quite simple in fact, you must iterate through the destination pixel and look the color of the pixel they should come from (and not the other way around) :
var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset) {
var shiftXY = { xS:0, yS:0 };
var shiftCanvasWidth = shiftCanvas.width ;
var shiftCanvasHeight = shiftCanvas.height;
for ( var x=0 ; x < shiftCanvasWidth ; x ++) {
for ( var y = 0 ; y < shiftCanvasHeight ; y++ ) {
readShift ( shiftCanvas, x, y, shiftXY );
var sourceColor = readPixelColor ( sourceCanvas,
xOffset + shiftXY.xS,
yOffset + shiftXY.yS) ;
writePixel(sourceCanvas, xOffset + x , yOffset + y, sourceColor );
}
}
};
// sourceColor represents the color within a 32 bits integer (r,g,b,a * 8 bits).
it would be too long to write everything here but :
-- within pixelShift loop you should not deal with the source canvas, but a with 32 bits performance array.
-- the shift canvas should be converted ONCE into a Int8Array array, and stored as this.
this array size is shiftWidth * shiftHeight
odd index contains x shift, even contains y shift.
the array is pre-processed, and contains the shift value - 128.
for this shiftArray :
shiftX (x,y) = shiftArray [ 2 * (x + y * shiftWidth) ] ;
shiftY (x,y) = shiftArray [ 2 * (x + y * shiftWidth) + 1 ] ;
-- So pixelShift should look like :
var pixelShift = function (sourceCanvas,
shiftArray, shiftWidth, shiftHeight, xOffset, yOffset) {
[ Get a 32 bit performance array out of the canvas's target area ]
[ process this array using the shiftArray ]
[ write back the processed array onto the canvas's target area ]
}
-- And the core loop can be processed in a linear fashion :
var areaSize = shiftWidth * shiftHeight ;
for ( pixelIndex=0 ; pixelIndex < areaSize ; pixelIndex++ ) {
var linearShift = shiftArray [ 2*pixelIndex ]
+ shiftWidth * shiftArray [ 2*pixelIndex + 1 ] ;
targetAreaArray [ pixelIndex ] = targetAreaArray [ pixelIndex + linearShift ] ;
}
-- Rq : you might want to perform a boundary check on (pixelIndex + linearShift) within [0, areaSize[.
-- I think now you cannot get any faster.
The performance bottleneck will be the getImageData and putImageData you need to get/put the target area, but as far as i know, there's no other way to get a binary view on a Canvas than those two slooooow functions.

Related

3D point rotation algorithm

I'm currently working on a JavaScript project which involves 3D point rotation. Using simple trigonometry, I have sketched my own 3D point rotation algorithm, but I have to deal with a huge amount of data (+300 000 points) and my function slows down the runtime substantially (the FPS rate drops from 60 to 12).
I'm looking for another 3D point rotation ALGORITHM which...
rotates points around origin by X, Y and Z axes' angles (PITCH, YAW and ROLL)
has a quite good efficiency (don't worry about this too much, it will always be faster than mine)
is written in JavaScript, C-like code or pseudo-code
Any help will be greatly appreciated :)
Context: 3D point cloud renderer (I want every point to be rotated)
A rotated vector can be described as a product of a rotation matrix with that vector. The German Wikipedia page on pitch, roll and yaw describes the rotation matrix for given Euler rotation angles.
With that information, the rotation of all points with the same angles can be written as JavaScript function, where the points array is global:
function rotate(pitch, roll, yaw) {
var cosa = Math.cos(yaw);
var sina = Math.sin(yaw);
var cosb = Math.cos(pitch);
var sinb = Math.sin(pitch);
var cosc = Math.cos(roll);
var sinc = Math.sin(roll);
var Axx = cosa*cosb;
var Axy = cosa*sinb*sinc - sina*cosc;
var Axz = cosa*sinb*cosc + sina*sinc;
var Ayx = sina*cosb;
var Ayy = sina*sinb*sinc + cosa*cosc;
var Ayz = sina*sinb*cosc - cosa*sinc;
var Azx = -sinb;
var Azy = cosb*sinc;
var Azz = cosb*cosc;
for (var i = 0; i < points.length; i++) {
var px = points[i].x;
var py = points[i].y;
var pz = points[i].z;
points[i].x = Axx*px + Axy*py + Axz*pz;
points[i].y = Ayx*px + Ayy*py + Ayz*pz;
points[i].z = Azx*px + Azy*py + Azz*pz;
}
}
Most of that is setting up the rotation matrix as described in the article. The last three lines inside the loop are the matrix multiplication. You have made a point of not wanting to get into matrices, but that's hardly intimidating, is it? Sooner or later you will encounter more matrices and you should be prepared to deal with them. The stuff you need – multiplication, mainly – is simple. The more complicated stuff like inverting matrices is not needed for your requirements.
Anyway, that performs reasonably fast for 300,000 points. I was able to rotate a point cloud of that size and render it on a 1000px &times 1000px canvas in about 10ms.
From wikipedia:
If you multiply your points by each of these matrices they will be rotated by the amount you want.
For example, if I want to rotate point [1, 0, 0] by 90° around the z axis (in the xy plane), sin(90) = 1 and cos(90) = 0 so you get this:
| 0 -1 0 | |1| |0|
| 1 0 0 | * |0| = |1|
| 0 0 1 | |0| |0|

Negative coordinates in a grid based game

I have written a small 2D game in javascript that uses a grid where the player starts at position [0,0] and can move an almost infinite distance in either direction.
Now I want to implement A* pathfinding, but I'm having some problems finding the best way to store the world with all it's different obstacles, enemies and terrain. This is what I have tried or thought about so far.
Array of arrays
Here I store the world in an array of arrays [x][y].
var world = [[]];
world[312][11] = 0;
world[312][12] = 0;
world[312][13] = 1;
world[312][14] = 1;
...
This works great with A* pathfinding! It's easy and very fast to access a specific coordinate and populate the world. In the example above I just store passable (0) or impassable (1) terrain, but I can store pretty much whatever I want there. However, this doesn't work very well with negative coordinates like if my players is at [-12][-230]. Negative keys in a javascript array isn't actually part of the array, they won't be included in world.length or world[3].length and from what I understand, it's overall bad practice and might have some impact on the performance as well. I read somewhere that if you are using negative keys in your array, you are doing it wrong.
I would still not pass the entire world into the A* function for obvious reasons. Just a small part close to my player, but the coordinates would correspond to the positions in the array which is easy to work with.
A separate array of arrays just for A* pathfinding
This is where I'm at right now. I have a separate 50x50 grid called pathMap = [[]], that is only used for pathfinding.
var pathMap = [[]];
pathMap[0][0] = 0;
pathMap[0][1] = 0;
pathMap[0][2] = 1;
pathMap[0][3] = 1;
...
It starts at pathMap[0][0] and goes to pathMap[50][50] and is working as an overlay on my current position where I (as the player) will always be in the center position. My real coordinates may be something like [-5195,323], but it translates to pathMap[25][25] and everything close to me is put on the pathMap in relation to my position.
Now this works, but it's a huge mess. All the translations from one coordinate to another back and forth makes my brain hurt. Also, when I get the path back from A*, I have to translate each step of it back to the actual position my element should move to in the real world. I also have to populate the same object into 2 different grids every update which hurts performance a bit as well.
Array of objects
I think this is where I want to be, but I have some issues with this as well.
var world = [];
world[0] = { x: -10, y: 3, impassable: 0 };
world[1] = { x: -10, y: 4, impassable: 0 };
world[2] = { x: -10, y: 5, impassable: 1 };
world[3] = { x: -10, y: 6, impassable: 1 };
...
Works great with negative x or y values! However, it's not as easy to find for instance [10,3] in this array. I have to loop through the entire array to look for an object where x == 10 and y == 3 instead of the very easy and fast approach world[10][3] in the first example. Also, I can't really rely on the coordinates being in the right order using this version, sorting becomes harder, as does other things that was a lot easier with the array of arrays.
Rebuild the game to always be on the positive side
I would prefer not to do this, but I have considered placing the players starting position at something like [1000000,1000000] instead, and making negative coordinates off limits. It seems like a failure if I have to remove the vision I have of endlessness just to make the pathfinding work with less code. I know there will always be some upper or lower limits anyways, but I just want to start at [0,0] and not some arbitrary coordinate for array related reasons.
Other?
In javascript, is there another option that works better and is not described above? I'm very open to suggestions!
Is there a best practice for similar cases?
You have three coordinates system you must distinguish :
the world coordinates.
the world model / path-finding (array) coordinates.
the screen coordinates.
The screen coordinates system depends upon :
the viewport = the canvas. (width, height in pixels).
a camera = (x,y) center in world coordinates + a viewWidth (in world coordinates).
To avoid headaches, build a small abstraction layer that will do the math for you.
You might want to use Object.defineProperty to define properties, that will provide a fluent interface.
var canvas = ... ;
var canvasWidth = canvas.width;
var canvasHeigth = canvas.heigth;
var world = {
width : 1000, // meters
height : 1000, // meters
tileSize : 0.5, // height and width of a tile, in meter
model : null, // 2D array sized ( width/tileSize, XtileSize )
};
// possibles world coordinates range from -width/2 to width/2 ; - height/2 height/2.
var camera = {
x : -1,
y : -1,
viewWidth : 10, // we see 10 meters wide scene
viewHeight : -1 // height is deduced from canvas aspect ratio
};
camera.viewHeight = camera.viewWidth * canvasWidth / canvasHeight ;
Then your character looks like :
// (x,y) is the center of the character in centered world coordinates
// here (0,0) means (500,500) world coords
// (1000,1000) array coords
// (320, 240) screen coords in 640X480
function /*Class*/ Character(x, y) {
var _x=x;
var _y=y;
var _col=0;
var _row=0;
var _sx=0.0;
var _sy=0.0;
var dirty = true;
Object.defineProperty(this,'x',
{ get : function() {return _x; }
set : function(v) { _x=v;
dirty=true; } });
Object.defineProperty(this,'x',
{ get : function() {return _y; }
set : function(v) { _y=v;
dirty=true; } });
Object.defineProperty(this,'col',
{ get : function() {
if (dirty) updateCoords();
return _col; } });
Object.defineProperty(this,'row',
{ get : function() {
if (dirty) updateCoords();
return _row; } });
Object.defineProperty(this,'sx',
{ get : function() {
if (dirty) updateCoords();
return _sx; } });
Object.defineProperty(this,'sy',
{ get : function() {
if (dirty) updateCoords();
return _sy; } });
function updateCoords() {
_row = ( ( _x + 0.5 * world.width )/ world.tileSize ) | 0 ;
_col = ( ( _x + 0.5 * world.height )/ world.tileSize ) | 0 ;
_sx = canvasWidth * ( 0.5 + ( _x - camera.x ) / camera.viewWidth ) ;
_sy = canvasHeight * ( 0.5 + ( _y - camera.y ) / camera.viewHeight ) ;
dirty = false;
}
}

KinectJS: Algorithm required to determine new X,Y coords after image resize

BACKGROUND:
The app allows users to upload a photo of themselves and then place a pair of glasses over their face to see what it looks like. For the most part, it is working fine. After the user selects the location of the 2 pupils, I auto zoom the image based on the ratio between the distance of the pupils and then already known distance between the center points of the glasses. All is working fine there, but now I need to automatically place the glasses image over the eyes.
I am using KinectJS, but the problem is not with regards to that library or javascript.. it is more of an algorithm requirement
WHAT I HAVE TO WORK WITH:
Distance between pupils (eyes)
Distance between pupils (glasses)
Glasses width
Glasses height
Zoom ratio
SOME CODE:
//.. code before here just zooms the image, etc..
//problem is here (this is wrong, but I need to know what is the right way to calculate this)
var newLeftEyeX = self.leftEyePosition.x * ratio;
var newLeftEyeY = self.leftEyePosition.y * ratio;
//create a blue dot for testing (remove later)
var newEyePosition = new Kinetic.Circle({
radius: 3,
fill: "blue",
stroke: "blue",
strokeWidth: 0,
x: newLeftEyeX,
y: newLeftEyeY
});
self.pointsLayer.add(newEyePosition);
var glassesWidth = glassesImage.getWidth();
var glassesHeight = glassesImage.getHeight();
// this code below works perfect, as I can see the glasses center over the blue dot created above
newGlassesPosition.x = newLeftEyeX - (glassesWidth / 4);
newGlassesPosition.y = newLeftEyeY - (glassesHeight / 2);
NEEDED
A math genius to give me the algorithm to determine where the new left eye position should be AFTER the image has been resized
UPDATE
After researching this for the past 6 hours or so, I think I need to do some sort of "translate transform", but the examples I see only allow setting this by x and y amounts.. whereas I will only know the scale of the underlying image. Here's the example I found (which cannot help me):
http://tutorials.jenkov.com/html5-canvas/transformation.html
and here is something which looks interesting, but it is for Silverlight:
Get element position after transform
Is there perhaps some way to do the same in Html5 and/or KinectJS? Or perhaps I am going down the wrong road here... any ideas people?
UPDATE 2
I tried this:
// if zoomFactor > 1, then picture got bigger, so...
if (zoomFactor > 1) {
// if x = 10 (for example) and if zoomFactor = 2, that means new x should be 5
// current x / zoomFactor => 10 / 2 = 5
newLeftEyeX = self.leftEyePosition.x / zoomFactor;
// same for y
newLeftEyeY = self.leftEyePosition.y / zoomFactor;
}
else {
// else picture got smaller, so...
// if x = 10 (for example) and if zoomFactor = 0.5, that means new x should be 20
// current x * (1 / zoomFactor) => 10 * (1 / 0.5) = 10 * 2 = 20
newLeftEyeX = self.leftEyePosition.x * (1 / zoomFactor);
// same for y
newLeftEyeY = self.leftEyePosition.y * (1 / zoomFactor);
}
that didn't work, so then I tried an implementation of Rody Oldenhuis' suggestion (thanks Rody):
var xFromCenter = self.leftEyePosition.x - self.xCenter;
var yFromCenter = self.leftEyePosition.y - self.yCenter;
var angle = Math.atan2(yFromCenter, xFromCenter);
var length = Math.hypotenuse(xFromCenter, yFromCenter);
var xNew = zoomFactor * length * Math.cos(angle);
var yNew = zoomFactor * length * Math.sin(angle);
newLeftEyeX = xNew + self.xCenter;
newLeftEyeY = yNew + self.yCenter;
However, that is still not working as expected. So, I am not sure what the issue is currently. If anyone has worked with KinectJS before and has an idea of what the issue may be, please let me know.
UPDATE 3
I checked Rody's calculations on paper and they seem fine, so there is obviously something else here messing things up.. I got the coordinates of the left pupil at zoom factors 1 and 2. With those coordinates, maybe someone can figure out what the issue is:
Zoom Factor 1: x = 239, y = 209
Zoom Factor 2: x = 201, y = 133
OK, since it's an algorithmic question, I'm going to keep this generic and only write pseudo code.
I f I understand you correctly, What you want is the following:
Transform all coordinates such that the origin of your coordinate system is at the zoom center (usually, central pixel)
Compute the angle a line drawn from this new origin to a point of interest makes with the positive x-axis. Compute also the length of this line.
The new x and y coordinates after zooming are defined by elongating this line, such that the new line is the zoom factor times the length of the original line.
Transform the newly found x and y coordinates back to a coordinate system that makes sense to the computer (e.g., top left pixel = 0,0)
Repeat for all points of interest.
In pseudo-code (with formulas):
x_center = image_width/2
y_center = image_height/2
x_from_zoom_center = x_from_topleft - x_center
y_from_zoom_center = y_from_topleft - y_center
angle = atan2(y_from_zoom_center, x_from_zoom_center)
length = hypot(x_from_zoom_center, y_from_zoom_center)
x_new = zoom_factor * length * cos(angle)
y_new = zoom_factor * length * sin(angle)
x_new_topleft = x_new + x_center
y_new_topleft = y_new + y_center
Note that this assumes the number of pixels used for length and width stays the same after zooming. Note also that some rounding should take place (keep everything double precision, and only round to int after all calculations)
In the code above, atan2 is the four-quadrant arctangent, available in most programming languages, and hypot is simply sqrt(x*x + y*y), but then computed more carefully (e.g., to avoid overflow etc.), also available in most programing languages.
Is this indeed what you were after?

Space filling with circles of unequal size

Here is my problem:
I have a bunch of circles that I need to display inside a canvas.
There are an arbitrary number of circles, each with a predefined radius.
The summed area of circles is always smaller than the area of the canvas.
I want to position the circles so that they take up the maximal space available inside the canvas, without touching each other. My goal is to achieve a visually pleasing effect where the circles appear well distributed inside the canvas. I don't know if this is really "space filling", as my goal is not to minimize the distance between elements, but rather to maximize it.
Here is an example of what I am trying to achieve:
My first "brute force" idea was the following:
For each circle: calculate the shortest distance between its border and each other circle's border; sum all of these distances, call that X.
Calculate the sum of all X's.
Randomly change the distances between the circles.
Redo 1-3 for a preset number of iterations and take the maximal value obtained at step (2).
However, this does not seem elegant; I'm sure there is a better way to do it. Is there any existing algorithm to achieve such a layout? Is there any existing library that I could use (JavaScript or Ruby) to achieve this?
Edit
Here is a Javascript version of the accepted answer, which uses Raphael to draw the circles.
I would try to insert sphere after sphere (largest first). Each one is added in the largest available space, with some random jitter.
One relatively easy way to find (more or less) the largest available space, is to imagine a grid of points on your view and store for each grid point (in a 2D array) the closest distance to any item: edge or sphere, whichever is closest. This array is updated as each new sphere is added.
To add a new sphere, just take the grid point with highest distance and apply some random jitter (you actually know how much you can jitter, because you know the distance to the closest item). (I would randomize not more than (d-r)/2 where d is the distance in the array and r is the radius of the sphere to add.
Updating this array after adding another circle is no rocket science: you calculate for each grid point the distance to newly added sphere and replace the stored value if that was larger.
It is possible that your grid is too coarse, and you can't add any more circle (when the 2D array contains no distances larger than the radius of the circle to add). Then you have to increase (e.g. double) the grid resolution before continuing.
Here are some result of this implementation (it took me about 100 lines of code)
100 Circles of varying size
500 Circles of varying size
100 Circles of same size
And here is some rough C++ code (just the algorithm, don't expect this to compile)
// INITIALIZATION
// Dimension of canvas
float width = 768;
float height = 1004;
// The algorithm creates a grid on the canvas
float gridSize=10;
int gridColumns, gridRows;
float *dist;
void initDistances()
{
// Determine grid dimensions and allocate array
gridColumns = width/gridSize;
gridRows = height/gridSize;
// We store a 2D array as a 1D array:
dist = new float[ gridColumns * gridRows ];
// Init dist array with shortest distances to the edges
float y = gridSize/2.0;
for (int row=0; row<gridRows; row++)
{
float distanceFromTop = y;
float distanceFromBottom = height-y;
for (int col=0; col<gridColumns; col++)
{
int i = row*gridColumns+col;
dist[i]=(distanceFromTop<distanceFromBottom?distanceFromTop:distanceFromBottom);
}
y+=gridSize;
}
float x = gridSize/2.0;
for (int col=0; col<gridColumns; col++)
{
float distanceFromLeft = x;
float distanceFromRight = width-x;
for (int row=0; row<gridRows; row++)
{
int i = row*gridColumns+col;
if (dist[i]>distanceFromLeft) dist[i] = distanceFromLeft;
if (dist[i]>distanceFromRight) dist[i] = distanceFromRight;
}
x+=gridSize;
}
}
void drawCircles()
{
for (int circle = 0; circle<getNrOfCircles(); circle++)
{
// We assume circles are sorted large to small!
float radius = getRadiusOfCircle( circle );
// Find gridpoint with largest distance from anything
int i=0;
int maxR = 0;
int maxC = 0;
float maxDist = dist[0];
for (int r=0; r<gridRows; r++)
for (int c=0; c<gridColumns; c++)
{
if (maxDist<dist[i]) {
maxR= r; maxC= c; maxDist = dist[i];
}
i++;
}
// Calculate position of grid point
float x = gridSize/2.0 + maxC*gridSize;
float y = gridSize/2.0 + maxR*gridSize;
// Apply some random Jitter
float offset = (maxDist-radius)/2.0;
x += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
y += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
drawCircle(x,y,radius);
// Update Distance array with new circle;
i=0;
float yy = gridSize/2.0;
for (int r=0; r<gridRows; r++)
{
float xx = gridSize/2.0;
for (int c=0; c<gridColumns; c++)
{
float d2 = (xx-x)*(xx-x)+(yy-y)*(yy-y);
// Naive implementation
// float d = sqrt(d2) - radius;
// if (dist[i]>d) dist[i] = d;
// Optimized implementation (no unnecessary sqrt)
float prev2 = dist[i]+radius;
prev2 *= prev2;
if (prev2 > d2)
{
float d = sqrt(d2) - radius;
if (dist[i]>d) dist[i] = d;
}
xx += gridSize;
i++;
}
yy += gridSize;
}
}
}
Perhaps some application of force-directed layout would be useful.
Since your goal is just to "achieve a pleasing effect", not solve a math problem, you should try the simplest algorithm which could work first and see if it looks good. There should be no need to use very complex math.
I understand that you want the spheres to "fill" the available space, not leaving large empty areas while other areas are crowded. You also want the layout to appear random -- not lined up on a grid or anything like that.
The obvious, dead-simple way to achieve that is simply to place the spheres one by one, in random locations. If one lands on top of an already-placed sphere, generate another random location until you find one where it fits.
It appears there are about 40 spheres in the image shown. The chances of 40 spheres all landing in the same area of the image, leaving the rest of the image empty, is very, very small. As the number of spheres increases, the chances of getting a very unbalanced layout will become close to zero.
Try that first and see if it meets your needs. If it is not "even" enough, you should be able to use some very simple math to bias the randomly chosen locations in favor of choosing empty areas. There should be no need to use complex algorithms.

2D game algorithm to calculate a bullet's needed speed to hit target?

I have a rather simple bird's-view 2D game where tower sprites defend against incoming moving sprites by shooting a bullet at them. My question: How do I calculate the needed bullet speed for the bullet to reach its moving target, provided that the bullet will always have the same defined speed?
I'm using JavaScript and have these sprite variables (among others):
sprite.x, sprite.y, sprite.width, sprite.height, sprite.speedX (i.e. velocity), sprite.speedY... so I have the objects originSprite, targetSprite and bulletSprite, all with these type of values, and I need to set the right bulletSprite speed values.
Probably for it to look good, the bullet would start at the outside of the originSprite (or some defined radius, though I guess starting from the originSprite center would also work), but its bullet center would try hit into the center of the targetSprite or so. Note there's no gravity or anything in this world. (Perhaps I should have my sprites variables using angle and velocity but right now I'm using speedX and speedY...)
Thanks so much!
Treat the targets sprite as a straight line in a 2 dimensional room where:
A(time) = (sprite.positionX + sprite.speedX * time, sprite.positionX + sprite.speedX * time)
As your bullet have constant speed you also know:
bullet.speedX^2 + bullet.speedY^2 = bullet.definedSpeed^2
Then you can also calculate a straight line for the bullet:
B(time) = (bullet.positionX + bullet.speedX * time, bullet.positionX + bullet.speedX * time)
And you know that both lines interset somewhere:
A(time) = B(time)
Then it's up to you to solve those equations with your given values and seek a minimum for time.
Some physical insight
1 ) For the target being a "Point Object"
So you have to solve the VECTOR equation
Positionbullet [ time=t1 > t0 ] == Positiontarget [ time=t1 > t0 ] -- (Eq 1)
Where the positions are given by the motion (also VECTOR) equations
Positionobject [ t ] = Positionobject [ t0 ] + Speedobject * ( t - t0 )
Now, the condition for the bullet to be able to reach the target is that the Eq 1 has solutions for x and y. Let's write down the equation for x:
Xbullet [ t0 ] + SpeedXbullet * ( t - t0 ) = Xtarget [ t0 ] + SpeedXtarget * ( t - t0 )
So for the collision time we have
( tCollision - t0 ) = (xtarget [ t 0 ] - xbullet [ t0 ] ) / (SpeedXbullet - SpeedXtarget) -- (Eq 2)
As we need solutions with t > t0, that means that for having an intercept is enough that>
Sign ( xtarget[ t0 ] - xbullet[ t0 ] ) = Sign ( SpeedXbullet - SpeedXtarget ) -- (Eq 3)
Which tells us the evident fact that if an object is moving faster than the other, and in the same direction, they will eventually collide.
From Eq 2, you can see that for a given SpeedXtarget there exist infinite solutions (as already pointed out in other answers) for t and SpeedXbullet, so I think your specifications are not complete.
I guess (as stated in a commentary I made in another answer) thinking in a "tower defense" kind of game, that your bullets have a limited range.
So you need also another constraint:
Distance [ Positiontarget [ tCollision - t0 ] - Positionbullet [ t0 ] ] < BulletRange -- (Eq 4)
Which still permits infinite solutions, but bounded by an upper value for the Collision time, given by the fact that the target may abandon the range.
Further, the distance is given by
Distance[v,u]= +Sqrt[ (Vx-Ux)^2 + (Vx-Vy)^2 ]
So, Eq 4 becomes,
(Xtarget[tCollision - t0] - Xbullet[t0])2 + (Ytarget[tCollision - t0] - Ybullet[t0])2 < BulletRange2 -- (Eq 5)
Note that { Xbullet[t0] , Ybullet[t0} is the tower position.
Now, replacing in Eq 5 the values for the target position:
(Xtarget[t0] + SpeedXtarget * (t-t0) - Xbullet[t0])2 + (Ytarget[t0] + SpeedYtarget * (t-t0) - Ybullet[t0])2 < BulletRange2 -- (Eq 6)
Calling the initial distances:
Dxt0 = Xtarget[t0] - Xbullet[t0]
and
Dyt0 = Ytarget[t0] - Ybullet[t0]
Equation 6 becomes
(Dtx0 + SpeedXtarget * (t-t0) )2 + (Dty0 + SpeedYtarget * (t-t0))2 < BulletRange2 -- (Eq 7)
Which is a quadratic equation to be solved in t-t0. The positive solution will give us the largest time allowed for the collision. Afterwards the target will be out of range.
Now calling
Speedtarget 2 = SpeedXtarget 2 + SpeedYtarget 2
and
H = Dtx0 * SpeedXtarget + Dty0 * SpeedYtarget
TCollision Max = t0 - ( H
+/- Sqrt ( BulletRange2 * Speedtarget 2 - H2 ) ) / Speedtarget 2
So you need to produce the collision BEFORE this time. The sign of the
square root should be taken such as the time is greater than t0
After you select an appropriate flying time for your bullet from the visual
effects point of view, you can calculate the SpeedX and SpeedY for the bullet
from
SpeedXbullet = ( Xtarget [ t0 ] - Xbullet [ t0 ] ) / ( tCollision - t0 ) + SpeedXtarget
and
SpeedYbullet = ( Ytarget [ t0 ] - Ybullet [ t0 ] ) / ( tCollision - t0 ) + SpeedYtarget
2 ) For the target and tower being "Extensive Objects"
Now, it is trivial to generalize for the case of the target being a circle of radius R. What you get, is the equivalent of an "extended range" for the bullets. That extension is just R.
So, replacing BulletRange by (BulletRange + R) you get the new equations for the maximum allowed collision time.
If you also want to consider a radius for the cannons, the same considerations apply, giving a "double extended range
NewBulletRange = BulletRange + RTarget + RTower
Unlimited Range Bullets
In the case that you decide that some special bullets should not have range (and detection) limitations, there is still the screen border constraint. But it is a little more difficult to tackle. Should you need this kind of projectile, leave a comment and I'll try to do some math.
Using vectors can make the math around this seem a little simpler. Sylvester seems to be a promising implementation of vectors in JavaScript, but for the purpose of my example, I'll write my own vector functions. I'm also going to assume .x / .y are measured top/left corner.
// this is a "constant" - representing 10px motion per "time unit"
var bulletSpeed = 10;
// calculate the vector from our center to their center
var enemyVec = vec_sub(targetSprite.getCenter(), originSprite.getCenter());
// measure the "distance" the bullet will travel
var dist = vec_mag(enemyVec);
// adjust for target position based on the amount of "time units" to travel "dist"
// and the targets speed vector
enemyVec = vec_add(enemyVec, vec_mul(targetSprite.getSpeed(), dist/bulletSpeed));
// calculate trajectory of bullet
var bulletTrajectory = vec_mul(vec_normal(enemyVec), bulletSpeed);
// assign values
bulletSprite.speedX = bulletTrajectory.x;
bulletSprite.speedY = bulletTrajectory.y;
// functions used in the above example:
// getCenter and getSpeed return "vectors"
sprite.prototype.getCenter = function() {
return {
x: this.x+(this.width/2),
y: this.y+(this.height/2)
};
};
sprite.prototype.getSpeed = function() {
return {
x: this.speedX,
y: this.speedY
};
};
function vec_mag(vec) { // get the magnitude of the vector
return Math.sqrt( vec.x * vec.x + vec.y * vec.y);
}
function vec_sub(a,b) { // subtract two vectors
return { x: a.x-b.x, y: a.y-b.y };
}
function vec_add(a,b) { // add two vectors
return { x: a.x + b.x, y: a.y + b.y };
}
function vec_mul(a,c) { // multiply a vector by a scalar
return { x: a.x * c, y: a.y * c };
}
function vec_div(a,c) { // divide == multiply by 1/c
return vec_mul(a, 1.0/c);
}
function vec_normal(a) { // normalize vector
return vec_div(a, vec_mag(a));
}
Compute the distance between shooter and target: dist = sqrt((xt - xs)^2 + (yt - ys)^2)
Divide the x and y distances by the above one: nx = (xt - xs)/dist; ny = (yt - ys)/dist; (normalization of the vector)
Multiply the results by a factor to get n pixels per time unit, ie. a speed in each direction. It should give a constant speed in the wanted direction.
I assume that the target will move on a straight line with constant velocity.
If both the direction and the speed of the bullet are variable (i.e. you try to calculation speedX and speedY for the bullet), there are infinitely many solutions.
If you set a fixed direction, you simply intersect the two lines of the bullet and the target. From the distance between the current point of the target and the intersection point (and the target's speed) you can calculate the time the target will take to reach this intersection point.
From the distance between the origin of the bullet and the intersection point (and the previously calculated time) you can calculate the speed of the bullet.

Categories

Resources