Related
There is a idle tone of a car. I want to make accelerating and deccelerating that sound by changing ffts.
How can achieve this. I only know C and little bit C++.
start with your idle sound array of raw audio [array1] ( this is the payload of a WAV file in PCM format of the car idling ) which will be in the time domain
feed this array1 into a FFT call which will return a new array [array2] which will be the frequency domain representation of the same underlying data as array1 ... where in this new array array2, element zero represents zero Hertz and the freq increment (incr_freq), separating each element, is defined by source sound array1 parms as per
incr_freq := sample_rate / number_of_samples_in_array1
... value of each element of array2 will be a complex number from which you can calc the magnitude and phase of the given freq ... to be clear frequency with regard to array2 is derived based on element position starting with element zero which is the DC bias and can be ignored ... knowing the frequency increment above (incr_freq) lets show the first few elements of array2
complex_number_0 := array2[0] // element 0 DC bias ignore this element
// its frequency_0 = 0
complex_number_1 := array2[1] // element 1
// its at frequency1 = frequency_0 + incr_freq
complex_number_2 := array2[2] // element 2
// its at frequency2 = frequency_1 + incr_freq
now identify the top X magnitudes in array2 (nugget1) ... these are the dominant frequencies which are most responsible to capture the essence of the car sound ... we save for later the element value of array2 for the X elements ... we calc magnitude using below which is inside loop across all elements of array2
for index_fft, curr_complex := range complex_fft {
curr_real = real(curr_complex) // pluck out real portion of imaginary number
curr_imag = imag(curr_complex) // ditto for imaginary part of complex number
curr_mag = 2.0 * math.Sqrt(curr_real*curr_real+curr_imag*curr_imag) / number_of_samples_array2
// ... more goodness here
}
now feed the array2 FFT array into an inverse FFT call which will return an array [array3] once again in the time domain ( raw audio )
if you do not alter the data of array2 your array3 will be the same (to a first approximation) as array1 ... now jack up your array2 to impart the acc or dec before sending it into that inverse FFT call
the secret sauce of how to alter array2 is left as an exercise ... my guess is put into a loop your synthesis of array3 from array2 which gets immediately rendered to your speakers inside this loop (loop_secret_sauce) ... where you increment (acc) or decrement(dec) the top X frequencies as identified above as nugget1 ... meaning as a whole shift the entire set of freq of all of the top X frequencies as defined by their magnitude ... give a non linear aspect to this shift of the set of X frequencies ... possibly not only increment or dec the freq of this set but also muck about with their relative magnitudes as well as introduce a wider swatch of frequencies in this loop
to give yourself traction in making this secret sauce use as array1 several different recordings of the car when its at idle or acc or dec and compare its array2 and use the diff between idle, acc, dec inside this sauce loop
here we drill down on mechanics ... when source audio is idle we iterate across its array2 and identify the top X elements of array2 with the greatest magnitude ... these X elements of array2 get saved into array top_mag_idle ... do same for source audio of acc and save in array top_mag_acc ... critical step ... examine difference between the elements stored in top_mag_idle versus top_mag_acc ... this transition between elements of top_mag_idle into top_mag_acc is your secret sauce which you will put into loop_secret_sauce ... to get concrete here when you loop across loop_secret_sauce and update array2 elements to reflect top_mag_idle the audio will sound idle over time when you continue looping across array2 to synthesize array3 and transition to updating array2 elements to reflect top_mag_acc the sound will be of an accelerating car
perhaps to gain intuition on the secret sauce consider this ... imagine listening to a car on idle ... as with any complex system which generates audio it will have a set of dominant frequencies meaning there are a set of say 5 different frequencies with the greatest magnitude ( loudest freqs ) ... similar to a pianist playing a cord on a piano where the shape of her hand and fingers remain static yet she is repeatedly tapping the keyboard ... now the car starts to accelerate ... the analogy here is she continues to repeatedly tap the keyboard with that same static hand and finger layout yet now she slides her hand up to the right along the keyboard as she continues to tap the keyboard ... in your code inside loop_secret_sauce the original set of freqs (top_mag_idle) will generate the idle car sound when you synthesize array3 from array2 ... then to implement acc you increment in unison all freqs in top_mag_idle and repeat synthesis of array3 from array2 this will give you the acc sound
until you get this working I would only use mono ( one channel not stereo )
sounds like an interesting project ... have fun !!!
I want to plot the frequency spectrum of a music file (like they do for example in Audacity). Hence I want the frequency in Hertz on the x-axis and the amplitude (or desibel) on the y-axis.
I devide the song (about 20 million samples) into blocks of 4096 samples at a time. These blocks will result in 2049 (N/2 + 1) complex numbers (sine and cosine -> real and imaginary part). So now I have these thousands of individual 2049-arrays, how do I combine them?
Lets say I do the FFT 5000 times resulting in 5000 2049-arrays of complex numbers. Do I plus all the values of the 5000 arrays and then take the magnitude of the combined 2049-array? Do I then sacle the x-axis with the songs sample rate / 2 (eg: 22050 for a 44100hz file)?
Any information will be appriciated
What application are you using for this? I assume you are not doing this by hand, so here is a Matlab example:
>> fbins = fs/N * (0:(N/2 - 1)); % Where N is the number of fft samples
now you can perform
>> plot(fbins, abs(fftOfSignal(1:N/2)))
Stolen
edit: check this out http://www.codeproject.com/Articles/9388/How-to-implement-the-FFT-algorithm
Wow I've written a load about this just recently.
I even turned it into a blog post available here.
My explanation is leaning towards spectrograms but its just as easy to render a chart like you describe!
I might not be correct on this one, but as far as I'm aware, you have 2 ways to get the spectrum of the whole song.
1) Do a single FFT on the whole song, which will give you an extremely good frequency resolution, but is in practice not efficient, and you don't need this kind of resolution anyway.
2) Divide it into small chunks (like 4096 samples blocks, as you said), get the FFT for each of those and average the spectra. You will compromise on the frequency resolution, but make the calculation more manageable (and also decrease the variance of the spectrum). Wilhelmsen link's describes how to compute an FFT in C++, and I think some library already exists to do that, like FFTW (but I never managed to compile it, to be fair =) ).
To obtain the magnitude spectrum, average the energy (square of the magnitude) accross all you chunks for every single bins. To get the result in dB, just 10 * log10 the results. That is of course assuming that you are not interested in the phase spectrum. I think this is known as the Barlett's method.
I would do something like this:
// At this point you have the FFT chunks
float sum[N/2+1];
// For each bin
for (int binIndex = 0; binIndex < N/2 + 1; binIndex++)
{
for (int chunkIndex = 0; chunkIndex < chunkNb; chunkIndex++)
{
// Get the magnitude of the complex number
float magnitude = FFTChunk[chunkIndex].bins[binIndex].real * FFTChunk[chunkIndex].bins[binIndex].real
+ FFTChunk[chunkIndex].bins[binIndex].im * FFTChunk[chunkIndex].bins[binIndex].im;
magnitude = sqrt(magnitude);
// Add the energy
sum[binIndex] += magnitude * magnitude;
}
// Average the energy;
sum[binIndex] /= chunkNb;
}
// Then get the values in decibel
for (int binIndex = 0; binIndex < N/2 + 1; binIndex++)
{
sum[binIndex] = 10 * log10f(sum[binIndex]);
}
Hope this answers your question.
Edit: Goz's post will give you plenty of information on the matter =)
Commonly, you would take just one of the arrays, corresponding to the point in time of the music in which you are interested. The you would calculate the log of the magnitude of each complex array element. Plot the N/2 results as Y values, and scale the X axis from 0 to Fs/2 (where Fs is the sampling rate).
I have created a simple Chebyshev low pass filter based on coefficients generated by this site: http://www-users.cs.york.ac.uk/~fisher/mkfilter/, which I am using to filter out frequencies above 4kHz in an 16kHz sample rate audio signal before downsampling to 8kHz. Here's my code (which is C#, but this question is not C# specific, feel free to use other languages in different languages).
/// <summary>
/// Chebyshev, lowpass, -0.5dB ripple, order 4, 16kHz sample rte, 4kHz cutoff
/// </summary>
class ChebyshevLpf4Pole
{
const int NZEROS = 4;
const int NPOLES = 4;
const float GAIN = 1.403178626e+01f;
private float[] xv = new float[NZEROS+1];
private float[] yv = new float[NPOLES + 1];
public float Filter(float inValue)
{
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4];
xv[4] = inValue / GAIN;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4];
yv[4] = (xv[0] + xv[4]) + 4 * (xv[1] + xv[3]) + 6 * xv[2]
+ (-0.1641503452f * yv[0]) + (0.4023376691f * yv[1])
+ (-0.9100943707f * yv[2]) + (0.5316388226f * yv[3]);
return yv[4];
}
}
To test it I created a sine wave "chirp" from 20Hz to 8kHz using Audacity. The test signal looks like this:
After filtering it I get:
The waveform shows that the filter is indeed reducing the amplitude of frequencies above 4kHz, but I have a load of noise added to my signal. This seems to be the case whichever of the filter types I try to implement (e.g. Butterworth, Raised Cosine etc).
Am I doing something wrong, or do these filters simply introduce artefacts at other frequencies? If I downsample using the naive approach of averaging every pair of samples, I don't get this noise at all (but obviously the aliasing is much worse).
OK, it was me being really stupid. The creation of my LPF was happening inside a processing loop instead of outside, meaning that every 512 samples I was creating a new one meaning I was losing the saved state. With just one instance of my filter running over the whole file, the noise goes away, and as expected I get aliased frequencies as the filter cannot completely remove everything above the cutoff.
I checked your filter-code in Mathematica and it works fine here without introducing noise, so probably the noise comes from some other part of your code.
It's possible that you have numerical stability problems, particularly if any of the poles are close to the unit circle. Try making all your intermediate terms double precision and then cast back to single precision at the end. I'm not too familiar with C# but in C this would be:
yv[4] = (float)(((double)xv[0] + (double)xv[4]) + 4.0 * ((double)xv[1] + (double)xv[3]) + 6.0 * xv[2]
+ (-0.1641503452 * (double)yv[0]) + (0.4023376691 * (double)yv[1])
+ (-0.9100943707 * (double)yv[2]) + (0.5316388226 * (double)yv[3]));
You haven't properly initialized your xv and yv arrays before using them for the first time. In most languages this means their values are undefined which may lead to unexpected results like yours. Initializing them to a proper value (like 0) may solve your issue.
I would like to do an algebraic curve fit of 2D data points, but for various reasons - it isn't really possible to have much of the sample data in memory at once, and iterating through all of it is an expensive process.
(The reason for this is that actually I need to fit thousands of curves simultaneously based on gigabytes of data which I'm reading off disk, and which is therefore sloooooow).
Note that the number of polynomial coefficients will be limited (perhaps 5-10), so an exact fit will be extremely unlikely, but this is ok as I'm trying to find an underlying pattern in data with a lot of random noise.
I understand how one can use a genetic algorithm to fit a curve to a dataset, but this requires many passes through the sample data, and thus isn't practical for my application.
Is there a way to fit a curve with a single pass of the data, where the state that must be maintained from sample to sample is minimal?
I should add that the nature of the data is that the points may lie anywhere on the X axis between 0.0 and 1.0, but the Y values will always be either 1.0 or 0.0.
So, in Java, I'm looking for a class with the following interface:
public interface CurveFit {
public void addData(double x, double y);
public List<Double> getBestFit(); // Returns the polynomial coefficients
}
The class that implements this must not need to keep much data in its instance fields, no more than a kilobyte even for millions of data points. This means that you can't just store the data as you get it to do multiple passes through it later.
edit: Some have suggested that finding an optimal curve in a single pass may be impossible, however an optimal fit is not required, just as close as we can get it in a single pass.
The bare bones of an approach might be if we have a way to start with a curve, and then a way to modify it to get it slightly closer to new data points as they come in - effectively a form of gradient descent. It is hoped that with sufficient data (and the data will be plentiful), we get a pretty good curve. Perhaps this inspires someone to a solution.
Yes, it is a projection. For
y = X beta + error
where lowercased terms are vectors, and X is a matrix, you have the solution vector
\hat{beta} = inverse(X'X) X' y
as per the OLS page. You almost never want to compute this directly but rather use LR, QR or SVD decompositions. References are plentiful in the statistics literature.
If your problem has only one parameter (and x is hence a vector as well) then this reduces to just summation of cross-products between y and x.
If you don't mind that you'll get a straight line "curve", then you only need six variables for any amount of data. Here's the source code that's going into my upcoming book; I'm sure that you can figure out how the DataPoint class works:
Interpolation.h:
#ifndef __INTERPOLATION_H
#define __INTERPOLATION_H
#include "DataPoint.h"
class Interpolation
{
private:
int m_count;
double m_sumX;
double m_sumXX; /* sum of X*X */
double m_sumXY; /* sum of X*Y */
double m_sumY;
double m_sumYY; /* sum of Y*Y */
public:
Interpolation();
void addData(const DataPoint& dp);
double slope() const;
double intercept() const;
double interpolate(double x) const;
double correlate() const;
};
#endif // __INTERPOLATION_H
Interpolation.cpp:
#include <cmath>
#include "Interpolation.h"
Interpolation::Interpolation()
{
m_count = 0;
m_sumX = 0.0;
m_sumXX = 0.0;
m_sumXY = 0.0;
m_sumY = 0.0;
m_sumYY = 0.0;
}
void Interpolation::addData(const DataPoint& dp)
{
m_count++;
m_sumX += dp.getX();
m_sumXX += dp.getX() * dp.getX();
m_sumXY += dp.getX() * dp.getY();
m_sumY += dp.getY();
m_sumYY += dp.getY() * dp.getY();
}
double Interpolation::slope() const
{
return (m_sumXY - (m_sumX * m_sumY / m_count)) /
(m_sumXX - (m_sumX * m_sumX / m_count));
}
double Interpolation::intercept() const
{
return (m_sumY / m_count) - slope() * (m_sumX / m_count);
}
double Interpolation::interpolate(double X) const
{
return intercept() + slope() * X;
}
double Interpolation::correlate() const
{
return m_sumXY / sqrt(m_sumXX * m_sumYY);
}
Why not use a ring buffer of some fixed size (say, the last 1000 points) and do a standard QR decomposition-based least squares fit to the buffered data? Once the buffer fills, each time you get a new point you replace the oldest and re-fit. That way you have a bounded working set that still has some data locality, without all the challenges of live stream (memoryless) processing.
Are you limiting the number of polynomial coefficients (i.e. fitting to a max power of x in your polynomial)?
If not, then you don't need a "best fit" algorithm - you can always fit N data points EXACTLY to a polynomial of N coefficients.
Just use matrices to solve N simultaneous equations for N unknowns (the N coefficients of the polynomial).
If you are limiting to a max number of coefficients, what is your max?
Following your comments and edit:
What you want is a low-pass filter to filter out noise, not fit a polynomial to the noise.
Given the nature of your data:
the points may lie anywhere on the X axis between 0.0 and 1.0, but the Y values will always be either 1.0 or 0.0.
Then you don't need even a single pass, as these two lines will pass exactly through every point:
X = [0.0 ... 1.0], Y = 0.0
X = [0.0 ... 1.0], Y = 1.0
Two short line segments, unit length, and every point falls on one line or the other.
Admittedly, an algorithm to find a good curve fit for arbitrary points in a single pass is interesting, but (based on your question), that's not what you need.
Assuming that you don't know which point should belong to which curve, something like a Hough Transform might provide what you need.
The Hough Transform is a technique that allows you to identify structure within a data set. One use is for computer vision, where it allows easy identification of lines and borders within the field of sight.
Advantages for this situation:
Each point need be considered only once
You don't need to keep a data structure for each candidate line, just one (complex, multi-dimensional) structure
Processing of each line is simple
You can stop at any point and output a set of good matches
You never discard any data, so it's not reliant on any accidental locality of references
You can trade off between accuracy and memory requirements
Isn't limited to exact matches, but will highlight partial matches too.
An approach
To find cubic fits, you'd construct a 4-dimensional Hough space, into which you'd project each of your data-points. Hotspots within Hough space would give you the parameters for the cubic through those points.
You need the solution to an overdetermined linear system. The popular methods are Normal Equations (not usually recommended), QR factorization, and singular value decomposition (SVD). Wikipedia has decent explanations, Trefethen and Bau is very good. Your options:
Out-of-core implementation via the normal equations. This requires the product A'A where A has many more rows than columns (so the result is very small). The matrix A is completely defined by the sample locations so you don't have to store it, thus computing A'A is reasonably cheap (very cheap if you don't need to hit memory for the node locations). Once A'A is computed, you get the solution in one pass through your input data, but the method can be unstable.
Implement an out-of-core QR factorization. Classical Gram-Schmidt will be fastest, but you have to be careful about stability.
Do it in-core with distributed memory (if you have the hardware available). Libraries like PLAPACK and SCALAPACK can do this, the performance should be much better than 1. The parallel scalability is not fantastic, but will be fine if it's a problem size that you would even think about doing in serial.
Use iterative methods to compute an SVD. Depending on the spectral properties of your system (maybe after preconditioning) this could converge very fast and does not require storage for the matrix (which in your case has 5-10 columns each of which are the size of your input data. A good library for this is SLEPc, you only have to find a the product of the Vandermonde matrix with a vector (so you only need to store the sample locations). This is very scalable in parallel.
I believe I found the answer to my own question based on a modified version of this code. For those interested, my Java code is here.
With the help of the Stack Overflow community I've written a pretty basic-but fun physics simulator.
You click and drag the mouse to launch a ball. It will bounce around and eventually stop on the "floor".
My next big feature I want to add in is ball to ball collision. The ball's movement is broken up into a x and y speed vector. I have gravity (small reduction of the y vector each step), I have friction (small reduction of both vectors each collision with a wall). The balls honestly move around in a surprisingly realistic way.
I guess my question has two parts:
What is the best method to detect ball to ball collision?
Do I just have an O(n^2) loop that iterates over each ball and checks every other ball to see if it's radius overlaps?
What equations do I use to handle the ball to ball collisions? Physics 101
How does it effect the two balls speed x/y vectors? What is the resulting direction the two balls head off in? How do I apply this to each ball?
Handling the collision detection of the "walls" and the resulting vector changes were easy but I see more complications with ball-ball collisions. With walls I simply had to take the negative of the appropriate x or y vector and off it would go in the correct direction. With balls I don't think it is that way.
Some quick clarifications: for simplicity I'm ok with a perfectly elastic collision for now, also all my balls have the same mass right now, but I might change that in the future.
Edit: Resources I have found useful
2d Ball physics with vectors: 2-Dimensional Collisions Without Trigonometry.pdf
2d Ball collision detection example: Adding Collision Detection
Success!
I have the ball collision detection and response working great!
Relevant code:
Collision Detection:
for (int i = 0; i < ballCount; i++)
{
for (int j = i + 1; j < ballCount; j++)
{
if (balls[i].colliding(balls[j]))
{
balls[i].resolveCollision(balls[j]);
}
}
}
This will check for collisions between every ball but skip redundant checks (if you have to check if ball 1 collides with ball 2 then you don't need to check if ball 2 collides with ball 1. Also, it skips checking for collisions with itself).
Then, in my ball class I have my colliding() and resolveCollision() methods:
public boolean colliding(Ball ball)
{
float xd = position.getX() - ball.position.getX();
float yd = position.getY() - ball.position.getY();
float sumRadius = getRadius() + ball.getRadius();
float sqrRadius = sumRadius * sumRadius;
float distSqr = (xd * xd) + (yd * yd);
if (distSqr <= sqrRadius)
{
return true;
}
return false;
}
public void resolveCollision(Ball ball)
{
// get the mtd
Vector2d delta = (position.subtract(ball.position));
float d = delta.getLength();
// minimum translation distance to push balls apart after intersecting
Vector2d mtd = delta.multiply(((getRadius() + ball.getRadius())-d)/d);
// resolve intersection --
// inverse mass quantities
float im1 = 1 / getMass();
float im2 = 1 / ball.getMass();
// push-pull them apart based off their mass
position = position.add(mtd.multiply(im1 / (im1 + im2)));
ball.position = ball.position.subtract(mtd.multiply(im2 / (im1 + im2)));
// impact speed
Vector2d v = (this.velocity.subtract(ball.velocity));
float vn = v.dot(mtd.normalize());
// sphere intersecting but moving away from each other already
if (vn > 0.0f) return;
// collision impulse
float i = (-(1.0f + Constants.restitution) * vn) / (im1 + im2);
Vector2d impulse = mtd.normalize().multiply(i);
// change in momentum
this.velocity = this.velocity.add(impulse.multiply(im1));
ball.velocity = ball.velocity.subtract(impulse.multiply(im2));
}
Source Code: Complete source for ball to ball collider.
If anyone has some suggestions for how to improve this basic physics simulator let me know! One thing I have yet to add is angular momentum so the balls will roll more realistically. Any other suggestions? Leave a comment!
To detect whether two balls collide, just check whether the distance between their centers is less than two times the radius. To do a perfectly elastic collision between the balls, you only need to worry about the component of the velocity that is in the direction of the collision. The other component (tangent to the collision) will stay the same for both balls. You can get the collision components by creating a unit vector pointing in the direction from one ball to the other, then taking the dot product with the velocity vectors of the balls. You can then plug these components into a 1D perfectly elastic collision equation.
Wikipedia has a pretty good summary of the whole process. For balls of any mass, the new velocities can be calculated using the equations (where v1 and v2 are the velocities after the collision, and u1, u2 are from before):
If the balls have the same mass then the velocities are simply switched. Here's some code I wrote which does something similar:
void Simulation::collide(Storage::Iterator a, Storage::Iterator b)
{
// Check whether there actually was a collision
if (a == b)
return;
Vector collision = a.position() - b.position();
double distance = collision.length();
if (distance == 0.0) { // hack to avoid div by zero
collision = Vector(1.0, 0.0);
distance = 1.0;
}
if (distance > 1.0)
return;
// Get the components of the velocity vectors which are parallel to the collision.
// The perpendicular component remains the same for both fish
collision = collision / distance;
double aci = a.velocity().dot(collision);
double bci = b.velocity().dot(collision);
// Solve for the new velocities using the 1-dimensional elastic collision equations.
// Turns out it's really simple when the masses are the same.
double acf = bci;
double bcf = aci;
// Replace the collision velocity components with the new ones
a.velocity() += (acf - aci) * collision;
b.velocity() += (bcf - bci) * collision;
}
As for efficiency, Ryan Fox is right, you should consider dividing up the region into sections, then doing collision detection within each section. Keep in mind that balls can collide with other balls on the boundaries of a section, so this may make your code much more complicated. Efficiency probably won't matter until you have several hundred balls though. For bonus points, you can run each section on a different core, or split up the processing of collisions within each section.
Well, years ago I made the program like you presented here.
There is one hidden problem (or many, depends on point of view):
If the speed of the ball is too
high, you can miss the collision.
And also, almost in 100% cases your new speeds will be wrong. Well, not speeds, but positions. You have to calculate new speeds precisely in the correct place. Otherwise you just shift balls on some small "error" amount, which is available from the previous discrete step.
The solution is obvious: you have to split the timestep so, that first you shift to correct place, then collide, then shift for the rest of the time you have.
You should use space partitioning to solve this problem.
Read up on
Binary Space Partitioning
and
Quadtrees
As a clarification to the suggestion by Ryan Fox to split the screen into regions, and only checking for collisions within regions...
e.g. split the play area up into a grid of squares (which will will arbitrarily say are of 1 unit length per side), and check for collisions within each grid square.
That's absolutely the correct solution. The only problem with it (as another poster pointed out) is that collisions across boundaries are a problem.
The solution to this is to overlay a second grid at a 0.5 unit vertical and horizontal offset to the first one.
Then, any collisions that would be across boundaries in the first grid (and hence not detected) will be within grid squares in the second grid. As long as you keep track of the collisions you've already handled (as there is likely to be some overlap) you don't have to worry about handling edge cases. All collisions will be within a grid square on one of the grids.
A good way of reducing the number of collision checks is to split the screen into different sections. You then only compare each ball to the balls in the same section.
One thing I see here to optimize.
While I do agree that the balls hit when the distance is the sum of their radii one should never actually calculate this distance! Rather, calculate it's square and work with it that way. There's no reason for that expensive square root operation.
Also, once you have found a collision you have to continue to evaluate collisions until no more remain. The problem is that the first one might cause others that have to be resolved before you get an accurate picture. Consider what happens if the ball hits a ball at the edge? The second ball hits the edge and immediately rebounds into the first ball. If you bang into a pile of balls in the corner you could have quite a few collisions that have to be resolved before you can iterate the next cycle.
As for the O(n^2), all you can do is minimize the cost of rejecting ones that miss:
1) A ball that is not moving can't hit anything. If there are a reasonable number of balls lying around on the floor this could save a lot of tests. (Note that you must still check if something hit the stationary ball.)
2) Something that might be worth doing: Divide the screen into a number of zones but the lines should be fuzzy--balls at the edge of a zone are listed as being in all the relevant (could be 4) zones. I would use a 4x4 grid, store the zones as bits. If an AND of the zones of two balls zones returns zero, end of test.
3) As I mentioned, don't do the square root.
I found an excellent page with information on collision detection and response in 2D.
http://www.metanetsoftware.com/technique.html (web.archive.org)
They try to explain how it's done from an academic point of view. They start with the simple object-to-object collision detection, and move on to collision response and how to scale it up.
Edit: Updated link
You have two easy ways to do this. Jay has covered the accurate way of checking from the center of the ball.
The easier way is to use a rectangle bounding box, set the size of your box to be 80% the size of the ball, and you'll simulate collision pretty well.
Add a method to your ball class:
public Rectangle getBoundingRect()
{
int ballHeight = (int)Ball.Height * 0.80f;
int ballWidth = (int)Ball.Width * 0.80f;
int x = Ball.X - ballWidth / 2;
int y = Ball.Y - ballHeight / 2;
return new Rectangle(x,y,ballHeight,ballWidth);
}
Then, in your loop:
// Checks every ball against every other ball.
// For best results, split it into quadrants like Ryan suggested.
// I didn't do that for simplicity here.
for (int i = 0; i < balls.count; i++)
{
Rectangle r1 = balls[i].getBoundingRect();
for (int k = 0; k < balls.count; k++)
{
if (balls[i] != balls[k])
{
Rectangle r2 = balls[k].getBoundingRect();
if (r1.Intersects(r2))
{
// balls[i] collided with balls[k]
}
}
}
}
I see it hinted here and there, but you could also do a faster calculation first, like, compare the bounding boxes for overlap, and THEN do a radius-based overlap if that first test passes.
The addition/difference math is much faster for a bounding box than all the trig for the radius, and most times, the bounding box test will dismiss the possibility of a collision. But if you then re-test with trig, you're getting the accurate results that you're seeking.
Yes, it's two tests, but it will be faster overall.
This KineticModel is an implementation of the cited approach in Java.
I implemented this code in JavaScript using the HTML Canvas element, and it produced wonderful simulations at 60 frames per second. I started the simulation off with a collection of a dozen balls at random positions and velocities. I found that at higher velocities, a glancing collision between a small ball and a much larger one caused the small ball to appear to STICK to the edge of the larger ball, and moved up to around 90 degrees around the larger ball before separating. (I wonder if anyone else observed this behavior.)
Some logging of the calculations showed that the Minimum Translation Distance in these cases was not large enough to prevent the same balls from colliding in the very next time step. I did some experimenting and found that I could solve this problem by scaling up the MTD based on the relative velocities:
dot_velocity = ball_1.velocity.dot(ball_2.velocity);
mtd_factor = 1. + 0.5 * Math.abs(dot_velocity * Math.sin(collision_angle));
mtd.multplyScalar(mtd_factor);
I verified that before and after this fix, the total kinetic energy was conserved for every collision. The 0.5 value in the mtd_factor was the approximately the minumum value found to always cause the balls to separate after a collision.
Although this fix introduces a small amount of error in the exact physics of the system, the tradeoff is that now very fast balls can be simulated in a browser without decreasing the time step size.
Improving the solution to detect circle with circle collision detection given within the question:
float dx = circle1.x - circle2.x,
dy = circle1.y - circle2.y,
r = circle1.r + circle2.r;
return (dx * dx + dy * dy <= r * r);
It avoids the unnecessary "if with two returns" and the use of more variables than necessary.
After some trial and error, I used this document's method for 2D collisions : https://www.vobarian.com/collisions/2dcollisions2.pdf
(that OP linked to)
I applied this within a JavaScript program using p5js, and it works perfectly. I had previously attempted to use trigonometrical equations and while they do work for specific collisions, I could not find one that worked for every collision no matter the angle at the which it happened.
The method explained in this document uses no trigonometrical functions whatsoever, it's just plain vector operations, I recommend this to anyone trying to implement ball to ball collision, trigonometrical functions in my experience are hard to generalize. I asked a Physicist at my university to show me how to do it and he told me not to bother with trigonometrical functions and showed me a method that is analogous to the one linked in the document.
NB : My masses are all equal, but this can be generalised to different masses using the equations presented in the document.
Here's my code for calculating the resulting speed vectors after collision :
//you just need a ball object with a speed and position vector.
class TBall {
constructor(x, y, vx, vy) {
this.r = [x, y];
this.v = [0, 0];
}
}
//throw two balls into this function and it'll update their speed vectors
//if they collide, you need to call this in your main loop for every pair of
//balls.
function collision(ball1, ball2) {
n = [ (ball1.r)[0] - (ball2.r)[0], (ball1.r)[1] - (ball2.r)[1] ];
un = [n[0] / vecNorm(n), n[1] / vecNorm(n) ] ;
ut = [ -un[1], un[0] ];
v1n = dotProd(un, (ball1.v));
v1t = dotProd(ut, (ball1.v) );
v2n = dotProd(un, (ball2.v) );
v2t = dotProd(ut, (ball2.v) );
v1t_p = v1t; v2t_p = v2t;
v1n_p = v2n; v2n_p = v1n;
v1n_pvec = [v1n_p * un[0], v1n_p * un[1] ];
v1t_pvec = [v1t_p * ut[0], v1t_p * ut[1] ];
v2n_pvec = [v2n_p * un[0], v2n_p * un[1] ];
v2t_pvec = [v2t_p * ut[0], v2t_p * ut[1] ];
ball1.v = vecSum(v1n_pvec, v1t_pvec); ball2.v = vecSum(v2n_pvec, v2t_pvec);
}
I would consider using a quadtree if you have a large number of balls. For deciding the direction of bounce, just use simple conservation of energy formulas based on the collision normal. Elasticity, weight, and velocity would make it a bit more realistic.
Here is a simple example that supports mass.
private void CollideBalls(Transform ball1, Transform ball2, ref Vector3 vel1, ref Vector3 vel2, float radius1, float radius2)
{
var vec = ball1.position - ball2.position;
float dis = vec.magnitude;
if (dis < radius1 + radius2)
{
var n = vec.normalized;
ReflectVelocity(ref vel1, ref vel2, ballMass1, ballMass2, n);
var c = Vector3.Lerp(ball1.position, ball2.position, radius1 / (radius1 + radius2));
ball1.position = c + (n * radius1);
ball2.position = c - (n * radius2);
}
}
public static void ReflectVelocity(ref Vector3 vel1, ref Vector3 vel2, float mass1, float mass2, Vector3 intersectionNormal)
{
float velImpact1 = Vector3.Dot(vel1, intersectionNormal);
float velImpact2 = Vector3.Dot(vel2, intersectionNormal);
float totalMass = mass1 + mass2;
float massTransfure1 = mass1 / totalMass;
float massTransfure2 = mass2 / totalMass;
vel1 += ((velImpact2 * massTransfure2) - (velImpact1 * massTransfure2)) * intersectionNormal;
vel2 += ((velImpact1 * massTransfure1) - (velImpact2 * massTransfure1)) * intersectionNormal;
}