How do I batch-simplify (one after the other) paths of a collection of (very large) SVG files, preferably with a command-line tool on UNIX? - macos

I have perhaps two thousand SVG files, obtained as a result of scanning a number of brochures to greyscale JPG, then batch-binarising them and saving to monochrome TIFF using ScanTailor, then batch-vectorising the TIFFs (using the command-line utility ImageMagick and a quick FOR loop in Fish), and finally resizing/editing them by hand in Inkscape. You can see my workflow heavily favours command-line UNIX utilities; I'll use a graphical tool if necessary, but not for repetitive tasks. If it makes any difference at all, my preferred UNIX distribution is MacOS.
What is left, at the end of this process, is a ~750KB file containing essentially a complete mathematical description of the page. Every printed letter or stroke of the pen has its own path, and (because I didn't use any sort of despeckling algorithm) so does every meaningless artifact (although I made sure to clean up the edges of every page, for workflow reasons).
Most of the scans, though, were imperfect (300 ppi when 600 or 900 ppi would have been better); the binarisation algorithm (the Otsu method) wasn't perfect either, etc. All of the imperfections added up, so in most cases the paths are rather noisy. The path representing the printed capital letter H, for example, needs only eight nodes (corners), or sixteen if it has rounded terminals (ends). I'm willing to accept more than that, because after all the system isn't perfect, but when I see thirty nodes on the H, and it has scalloped sides under magnification, my eyes start to bleed.
I know this isn't anything to worry about when the pages (rendered as PNG) reach the print shop, because the Mark 1 Eyeball will smooth everything out, but I'm too much of a perfectionist to leave it like that.
To solve the problem, I tried selecting all paths in Inkscape with Cmd/A, then using the "simplify path" command by typing Cmd/L. What I expected was that Inkscape would smooth all the paths individually; what resulted was Inkscape smoothing everything collectively into one blurry mess.
I get the result I want if I select path number one and type Cmd/L, then path number two and again Cmd/L, but a representative page has over FOUR HUNDRED paths and this kind of workflow is essentially impracticable.
I know Inkscape has a (very badly documented) command-line mode, and there might perhaps be a script available to do what needs doing, but if it exists somewhere I can't find it. An ideal solution would be to do what I described above, but programmatically (shell script?), then a FOR loop to do it on every file in the directory.

The basic algorithm for path simplification is not that complicated, as long as you do not need to handle curves. So one avenue could be to write a script yourself. The following is an excerpt from a node.js script I have used to simplify polygons/polylines in maps. For geojson files in the size range of up to several MB it would run in 0.1-0.2 seconds on my (old) computer.
The idea is to take the first and last points of a polyline and to measure how far the second point is removed from a line connecting the two. If it is less than a threshold (the smallest deviation from a straight line you will be able to spot), it can be safely removed, and the next middle point can be examined. If not, the point is preserved and further examinations measure the distance from a line from that point to the last.
const sqEpsilon = ... // square (!) of the minimum distance to preserve
// takes a list points in the form [[x,y],...]
function simplifyDP (points) {
const len = points.length;
const markers = new Uint8Array(len);
markers[0] = markers[len - 1] = 1;
simplifyDPStep(points, markers, 0, len - 1);
return markers.reduce((pts, m, i) => {
if (m) pts.push(points[i]);
return pts;
}, []);
}
function simplifyDPStep (points, markers, first, last) {
let maxSqDist = 0, idx;
for (let i = first + 1; i <= last - 1; i++) {
const sqDist = sqDistance(points[i], points[first], points[last]);
if (sqDist > maxSqDist) {
idx = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqEpsilon) {
markers[idx] = 1;
simplifyDPStep(points, markers, first, idx);
simplifyDPStep(points, markers, idx, last);
}
}
function sqDistance(p, p1, p2) {
let x = p1[0],
y = p1[1];
const dx = p2[0] - x,
dy = p2[1] - y;
const dot = dx * dx + dy * dy;
if (dot > 0) {
const t = ((p[0] - x) * dx + (p[1] - y) * dy) / dot;
if (t > 1) {
x = p2[0];
y = p2[1];
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
const cdx = p[0] - x;
const cdy = p[1] - y;
return cdx * cdx + cdy * cdy;
}

Related

How can I get my GA to converge?

I'm trying to write a GA to solve the following puzzle...
A binary encoding is (I think) very efficient. Each piece can be:
the original way up or flipped - 1 bit
rotated by 0 (ie none), 90, 180 or 270 degs - 2 bits
at position (x, y), where x and y go from 0 to 7 - 3 bits for each co-ordinate
This means that each piece's orientation and position can be encoded in 9 bits, making a total of 117 bits for the whole puzzle.
The fitness is calculated by placing each piece in the frame, ignoring any parts that lie out of the frame, and then adding up the number of empty squares. When that hits zero, we have a solution.
I have some standard GA methods I've used in other code (which I'll paste in below), but I can't seem to get it to converge. The fitness drops to about 11 (give or take), but never seems to go any lower. I've tried fiddling with the parameters, but can't get it any better.
At the risk of posting too much code, I'll show what I've got (where it seems relevant). If anyone can give me some idea how I could improve, it would be great. This is all in C#, but it should be clear enough to people who use other languages.
After generating an initial population of 1000 chromosomes (code not shown as it's just generating random binary strings of length 117), I enter the main loop, where on each generation, I call the Breed method, passing in the current population and some parameters...
private static List<Chromosome> Breed(List<Chromosome> population, int crossoverGene,
double mutationProbability, double mutationRate) {
List<Chromosome> nextGeneration = new List<Chromosome>();
// Cross breed half of the population number
for (int nChromosome = 0; nChromosome < population.Count / 2; nChromosome++) {
Chromosome daddy = Roulette(population);
Chromosome mummy = Roulette(population);
string babyGenes = daddy.Genes.Substring(0, crossoverGene)
+ mummy.Genes.Substring(crossoverGene);
Chromosome baby = new Chromosome(babyGenes);
baby.Fitness = Fitness(baby);
nextGeneration.Add(baby);
}
// Mutate some chromosomes
int numberToMutate = (int)(P() * 100 * mutationProbability);
List<Chromosome> mutatedChromosomes = new List<Chromosome>();
for (int i = 0; i < numberToMutate; i++) {
Chromosome c = Roulette(population);
string mutatedGenes = MutateGenes(c.Genes, mutationRate);
Chromosome mutatedChromosome = new Chromosome(mutatedGenes);
mutatedChromosome.Fitness = Fitness(mutatedChromosome);
mutatedChromosomes.Add(mutatedChromosome);
}
// Get the next generation from the fittest chromosomes
nextGeneration = nextGeneration
.Union(population)
.Union(mutatedChromosomes)
.OrderBy(p => p.Fitness)
.Take(population.Count)
.ToList();
return nextGeneration;
}
MutateGenes just flips bits at random, based on the mutation rate passed in. The main loop continues until we either hit the maximum number of generations, or the fitness drops to zero. I'm currently running for 1000 generations.
Here's the roulette method...
private static Chromosome Roulette(List<Chromosome> population) {
double totalFitness = population.Sum(c => 1 / c.Fitness);
double targetProbability = totalFitness * P();
double cumProbability = 0.0;
List<Chromosome> orderedPopulation = population.OrderBy(c => c.Fitness).ToList();
for (int i = 0; i < orderedPopulation.Count; i++) {
Chromosome c = orderedPopulation[i];
cumProbability += 1 / c.Fitness;
if (cumProbability > targetProbability) {
return c;
}
}
return orderedPopulation.Last();
}
Don't know if you need to see any of the other code. I was a bit wary about posting too much in case it put people off!
Anyone able to make any suggestions as to how I can get this to improve?
Todor Balabanov's answer is very interesting. Probably using relative coordinates and a proper packing function is the keystone.
Anyway I'd like to expand upon your idea as much as possible. A full discussion is probably too long for Stackoverflow...
TL;DR
Binary encoding does not give you any advantage.
The chosen alphabet isn't the smallest that permits a natural expression of the problem.
Considering the full range of coordinates ([0;7] x [0;7]) for every piece is excessive (and somewhat misleading for fitness evaluation).
Points (2) and (3) allow to reduce the search space from 2^117 to 2^95 elements.
A more informative fitness function is a great help.
You can use a multi-value fitness score, penalizing configurations that present holes.
Squares covered by overlapping pieces shouldn't be counted: an illegal configuration cannot have a fitness greater than a legal one.
ALPS can reduce the problem of premature convergence (reference implementation here).
I've have elaborated on these points in a GitHub wiki (it's a work in progress).
If you use genetic algorithms framework like Apache GA Framework you can implement chromosomes as list of shapes and you can use permutation crossover and mutation.
You will have blank spaces, which you will try to minimize (reduce them to 0). It is not a problem that you will have blanks, just count them and include them as penalty component in the fitness function.
Generally GAs are not so strong in combinatorial problems. I did many experiments like solving Rubik’s Cube with GA or solving Puzzle 15 with GA. Another experiment was 2D Optimal Cutting Problem with GA. If you are interested I can provide you the research papers and source code (GitHub). GAs are good giving you sub-optimal solution, but they are not good in giving you the optimal solution, which is even harder when it is a combinatorial problem.
The size of the population is an open question. You should do convergence investigation with different populations. Bigger population does not mean better and faster solution. Even 100 is too much for most of the problems solved with GA.
If you use absolute coordinates you will need to handle x and y, which is too complicated. Imagine that you support list of shapes. Packing procedure can get shape by shape and place each shape as close as possible to already handled shapes. It will speed-up your convergence.
/**
* Pack function which uses bounding rectangle of the polygons in the sheet
* with specified dimensions.
*
* #param width
* Sheet width.
* #param height
* Sheet height.
*/
public void pack1(int width, int height) {
int level[] = new int[width];
for (int i = 0; i < level.length; i++) {
level[i] = 0;
}
/*
* Insure pieces width according sheet width.
*/
for (Piece piece: population.get(worstIndex)) {
if (piece.getWidth() > width) {
piece.flip();
}
}
/*
* Pack pieces.
*/
int x = 0;
int y = 0;
for (Piece piece: population.get(worstIndex)) {
if (x + (int) piece.getWidth() >= width) {
x = 0;
}
/*
* Find y offset for current piece.
*/
y = 0;
for (int dx = x; dx < (x + piece.getWidth()); dx++) {
if (dx < width && y < level[dx]) {
y = level[dx];
}
}
// TODO Check the delta after subtraction.
/*
* Set current piece coordinates.
*/
piece.moveX(x - piece.getMinX());
piece.moveY(y - piece.getMinY());
/*
* Move lines for next placement.
*/
for (int dx = x; dx < (x + piece.getWidth()); dx++) {
if (dx < width) {
level[dx] = (int)(y + piece.getHeight());
}
}
// TODO Some strange behavior with the rotation.
x += (int) piece.getWidth() + 1;
}
}
/**
* Pack function which uses exact boundaries of the polygons in the sheet
* with specified dimensions.
*
* #param width
* Sheet width.
* #param height
* Sheet height.
*/
public void pack2(int width, int height) {
/*
* Pieces already placed on the sheet.
*/
List < Piece > front = new ArrayList < Piece > ();
/*
* Virtual Y boundary.
*/
double level = 0;
/*
* Place all pieces on the sheet
*/
for (Piece current: population.get(worstIndex)) {
double bestLeft = 0;
double bestTop = level;
current.moveX(-current.getMinX());
current.moveY(-current.getMinY() + level);
/*
* Move across sheet width.
*/
while (current.getMaxX() < width) {
/*
* Touch sheet bounds of touch other piece.
*/
while (current.getMinY() > 0 && Util.overlap(current, front) == false) {
current.moveY(-1);
}
// TODO Plus one may be is wrong if the piece should be part of
// the area.
current.moveY(+2);
/*
* Keep the best found position.
*/
if (current.getMinY() < bestTop) {
bestTop = current.getMinY();
bestLeft = current.getMinX();
}
/*
* Try next position on right.
*/
current.moveX(+1);
}
/*
* Put the piece in the best available coordinates.
*/
current.moveX(-current.getMinX() + bestLeft);
current.moveY(-current.getMinY() + bestTop);
/*
* Shift sheet level if the current piece is out of previous bounds.
*/
if (current.getMaxY() > level) {
level = current.getMaxY() + 1;
}
/*
* Add current piece in the ordered set and the front set.
*/
front.add(current);
}
}
/**
* Pack function which uses exact boundaries of the polygons in the sheet
* with specified dimensions.
*
* #param width
* Sheet width.
* #param height
* Sheet height.
*/
public void pack3(int width, int height) {
Polygon stack = new Polygon(
GEOMETRY_FACTORY
.createLinearRing(new Coordinate[] {
new Coordinate(0, -2, 0), new Coordinate(width - 1, -2, 0),
new Coordinate(width - 1, 0, 0), new Coordinate(0, 0, 0), new Coordinate(0, -2, 0)
}),
null, GEOMETRY_FACTORY);
/*
* Virtual Y boundary.
*/
double level = stack.getEnvelopeInternal().getMaxX();
/*
* Place all pieces on the sheet
*/
for (Piece current: population.get(worstIndex)) {
double bestLeft = 0;
double bestTop = level;
current.moveX(-current.getMinX());
current.moveY(-current.getMinY() + level);
/*
* Move across sheet width.
*/
while (current.getMaxX() < width) {
/*
* Touch sheet bounds of touch other piece.
*/
while (current.getMinY() > 0 && Util.overlap(current, stack) == false) {
current.moveY(-1);
}
// TODO Plus one may be is wrong if the piece should be part of
// the area.
current.moveY(+2);
/*
* Keep the best found position.
*/
if (current.getMinY() < bestTop) {
bestTop = current.getMinY();
bestLeft = current.getMinX();
}
/*
* Try next position on right.
*/
current.moveX(+1);
}
/*
* Put the piece in the best available coordinates.
*/
current.moveX(-current.getMinX() + bestLeft);
current.moveY(-current.getMinY() + bestTop);
/*
* Shift sheet level if the current piece is out of previous bounds.
*/
if (current.getMaxY() > level) {
level = current.getMaxY() + 1;
}
/*
* Add current piece in the ordered set and the front set.
*/
stack = (Polygon) SnapOverlayOp.union(stack, current.getPolygon()).getBoundary().convexHull();
stack.normalize();
}
}
You have a very interesting problem to solve. I like it very much. First of all it is a combinatorial problem, which can be very hard to solve with the classical genetic algorithms. I have some comments, but they are my subjective opinion: 1) Binary encoding does not give you any advantage (only overhead for encoding and decoding), you can use C# objects; 2) It is not smart to ignore pieces outside of the frame; 3) You will be trapped in local optimum all the time, this is the nature of genetic algorithms; 4) Population size of 1K is too much, use something smaller; 5) Do not use absolute x-y coordinates, use relative coordinates and proper packing function.

Storing motion vectors from calculated optical flow in a practical way which enables reconstruction of subsequent frames from initial keyframes

I am trying to store the motion detected from optical flow for frames in a video sequence and then use these stored motion vectors in order to predict the already known frames using just the first frame as a reference. I am currently using two processing sketches - the first sketch draws a motion vector for every pixel grid (each of width and height 10 pixels). This is done for every frame in the video sequence. The vector is only drawn in a grid if there is sufficient motion detected. The second sketch aims to reconstruct the video frames crudely from just the initial frame of the video sequence combined with information about the motion vectors got from the first sketch.
My approach so far is as follows: I am able to determine the size, position and direction of each motion vector drawn in the first sketch from four variables. By creating four arrays (two for the motion vector's x and y coordinate and another two for its length in the x and y direction), every time a motion vector is drawn I can append each of the four variables to the arrays mentioned above. This is done for each pixel grid throughout an entire frame where the vector is drawn and for each frame in the sequence - via for loops. Once the arrays are full, I can then save them to a text file as a list of strings. I then load these strings from the text file into the second sketch, along with the first frame of the video sequence. I load the strings into variables within a while loop in the draw function and convert them back into floats. I increment a variable by one each time the draw function is called - this moves on to the next frame (I used a specific number as a separator in my text-files which appears at the end of every frame - the loop searches for this number and then increments the variable by one, thus breaking the while loop and the draw function is called again for the subsequent frame). For each frame, I can draw 10 by 10 pixel boxes and move then by the parameters got from the text files in the first sketch. My problem is simply this: How do I draw the motion of a particular frame without letting what I've have blitted to the screen in the previous frame affect what will be drawn for the next frame. My only way of getting my 10 by 10 pixel box is by using the get() function which gets pixels that are already drawn to the screen.
Apologies for the length and complexity of my question. Any tips would be very much appreciated! I will add the code for the second sketch. I can also add the first sketch if required, but it's rather long and a lot of it is not my own. Here is the second sketch:
import processing.video.*;
Movie video;
PImage [] naturalMovie = new PImage [0];
String xlengths [];
String ylengths [];
String xpositions [];
String ypositions [];
int a = 0;
int c = 0;
int d = 0;
int p;
int gs = 10;
void setup(){
size(640, 480, JAVA2D);
xlengths = loadStrings("xlengths.txt");
ylengths = loadStrings("ylengths.txt");
xpositions = loadStrings("xpositions.txt");
ypositions = loadStrings("ypositions.txt");
video = new Movie(this, "sample1.mov");
video.play();
rectMode(CENTER);
}
void movieEvent(Movie m) {
m.read();
PImage f = createImage(m.width, m.height, ARGB);
f.set(0, 0, m);
f.resize(width, height);
naturalMovie = (PImage []) append(naturalMovie, f);
println("naturalMovie length: " + naturalMovie.length);
p = naturalMovie.length - 1;
}
void draw() {
if(naturalMovie.length >= p && p > 0){
if (c == 0){
image(naturalMovie[0], 0, 0);
}
d = c;
while (c == d && c < xlengths.length){
float u, v, x0, y0;
u = float(xlengths[a]);
v = float(ylengths[a]);
x0 = float(xpositions[a]);
y0 = float(ypositions[a]);
if (u != 1.0E-19){
//stroke(255,255,255);
//line(x0,y0,x0+u,y0+v);
PImage box;
box = get(int(x0-gs/2), int(y0 - gs/2), gs, gs);
image(box, x0-gs/2 +u, y0 - gs/2 +v, gs, gs);
if (a < xlengths.length - 1){
a += 1;
}
}
else if (u == 1.0E-19){
if (a < xlengths.length - 1){
c += 1;
a += 1;
}
}
}
}
}
Word to the wise: most people aren't going to read that wall of text. Try to "dumb down" your posts so they get to the details right away, without any extra information. You'll also be better off if you post an MCVE instead of only giving us half your code. Note that this does not mean posting your entire project. Instead, start over with a blank sketch and only create the most basic code required to show the problem. Don't include any of your movie logic, and hardcode as much as possible. We should be able to copy and paste your code onto our own machines to run it and see the problem.
All of that being said, I think I understand what you're asking.
How do I draw the motion of a particular frame without letting what I've have blitted to the screen in the previous frame affect what will be drawn for the next frame. My only way of getting my 10 by 10 pixel box is by using the get() function which gets pixels that are already drawn to the screen.
Separate your program into a view and a model. Right now you're using the screen (the view) to store all of your information, which is going to cause you headaches. Instead, store the state of your program into a set of variables (the model). For you, this might just be a bunch of PVector instances.
Let's say I have an ArrayList<PVector> that holds the current position of all of my vectors:
ArrayList<PVector> currentPositions = new ArrayList<PVector>();
void setup() {
size(500, 500);
for (int i = 0; i < 100; i++) {
currentPositions.add(new PVector(random(width), random(height)));
}
}
void draw(){
background(0);
for(PVector vector : currentPositions){
ellipse(vector.x, vector.y, 10, 10);
}
}
Notice that I'm just hardcoding their positions to be random. This is what your MCVE should do as well. And then in the draw() function, I'm simply drawing each vector. This is like drawing a single frame for you.
Now that we have that, we can create a nextFrame() function that moves the vectors based on the ArrayList (our model) and not what's drawn on the screen!
void nextFrame(){
for(PVector vector : currentPositions){
vector.x += random(-2, 2);
vector.y += random(-2, 2);
}
}
Again, I'm just hardcoding a random movement, but you would be reading these from your file. Then we just call the nextFrame() function as the last line in the draw() function:
If you're still having trouble, I highly recommend posting an MCVE similar to mine and posting a new question. Good luck.

Position of object with n known points and distances

I'm doing some work with triangulating the position of a receiver based on its distance to known points in space. I basically get a set of distances D[N] from the nearby broadcast points, and have a lookup table to give me the X, Y, and Z values for each. I need to find the X, Y, and Z points of my receiver.
I've seen that you can use trilateration to solve this problem in 2D cases, but it doesn't work for 3D. I would like to be able to use N points in order to improve accuracy, but I suppose I could also use the closest 4 points.
My issue is I have no idea how to programmatically solve the system of equations algebraically so it can be done in my program. I've seen a few solutions using things like Matlab, but I don't have the same tools.
This seems to solve the problem, if someone knows how to translate Matlab into a C language (I've never used Matlab): Determine the position of a point in 3D space given the distance to N points with known coordinates
Here is my solution in C++(should be easy to convert to plain C). It does not use any advanced algebra so it does not require any non-standard libraries. It works good when the number of points is rather big(the error gets smaller when the number of points grows), so it is better to pass all the points that you have to the solve method. It minimizes the sum of squared differences using sub-gradient descent.
const int ITER = 2000;
const double ALPHA = 2.0;
const double RATIO = 0.99;
double sqr(double a) {
return a * a;
}
struct Point {
double x;
double y;
double z;
Point(double _x=0.0, double _y=0.0, double _z=0.0): x(_x), y(_y), z(_z) {}
double dist(const Point &other) const {
return sqrt(sqr(x - other.x) + sqr(y - other.y) + sqr(z - other.z));
}
Point operator + (const Point& other) const {
return Point(x + other.x, y + other.y, z + other.z);
}
Point operator - (const Point& other) const {
return Point(x - other.x, y - other.y, z - other.z);
}
Point operator * (const double mul) const {
return Point(x * mul, y * mul, z * mul);
}
};
Point solve(const vector<Point>& given, const vector<double>& dist) {
Point res;
double alpha = ALPHA;
for (int iter = 0; iter < ITER; iter++) {
Point delta;
for (int i = 0; i < given.size(); i++) {
double d = res.dist(given[i]);
Point diff = (given[i] - res) * (alpha * (d - dist[i]) / max(dist[i], d));
delta = delta + diff;
}
delta = delta * (1.0 / given.size());
alpha *= RATIO;
res = res + delta;
}
return res;
}
Here is an answer for using Matlab (which is not what you are asking for), which uses the Nelder-Mead simplex optimization algorithm. Because you do not have access to Matlab, you could use R (freely available). The code above is easily translatable to R and in R you can use the Nelder-Mead algorithm (to replace 'fminsearch' using the neldermead package. For differences between R and Matlab (and Octave), see: http://cran.r-project.org/doc/contrib/R-and-octave.txt
function Ate=GetCoordinate(Atr,Dte)
% Atr = coordinates for known points
% Dte = distances for receiver to each row in Atr
% At5e = coordinate for receiver
[~,ii]=sort(Dte,'ascend');
Ate=mean(Atr(ii(1:4),:)); % (reasonable) start point
[Ate,~]=fminsearch(#(Ate) ED(Ate,Atr,Dte),Ate); % Uses Nelder-Mead simplex algorithm to find optimum
end
function d=ED(Ate,Atr,Dte) % calculates the sum of the squared difference between the measured distances and distances based on coordinate Ate (for receiver)
for k=1:size(Dte,1)
d(k,1)=sqrt((Atr(k,:)-Ate)*(Atr(k,:)-Ate)'); % Euclidean distance
end
d=sqrt(sum((Dte-d).^2));
end

What are some algorithms that will allow me to simulate planetary physics?

I'm interested in doing a "Solar System" simulator that will allow me to simulate the rotational and gravitational forces of planets and stars.
I'd like to be able to say, simulate our solar system, and simulate it across varying speeds (ie, watch Earth and other planets rotate around the sun across days, years, etc). I'd like to be able to add planets and change planets mass, etc, to see how it would effect the system.
Does anyone have any resources that would point me in the right direction for writing this sort of simulator?
Are there any existing physics engines which are designed for this purpose?
It's everything here and in general, everything that Jean Meeus has written.
You need to know and understand Newton's Law of Universal Gravitation and Kepler's Laws of Planetary Motion. These two are simple and I'm sure you've heard about them, if not studied them in high school. Finally, if you want your simulator to be as accurate as possible, you should familiarize yourself with the n-Body problem.
You should start out simple. Try making a Sun object and an Earth object that revolves around it. That should give you a very solid start and it's fairly easy to expand from there. A planet object would look something like:
Class Planet {
float x;
float y;
float z; // If you want to work in 3D
double velocity;
int mass;
}
Just remember that F = MA and the rest just just boring math :P
This is a great tutorial on N-body problems in general.
http://www.artcompsci.org/#msa
It's written using Ruby but pretty easy to map into other languages etc. It covers some of the common integration approaches; Forward-Euler, Leapfrog and Hermite.
You might want to take a look at Celestia, a free space simulator. I believe that you can use it to create fictitious solar systems and it is open source.
All you need to implement is proper differential equation (Keplers law) and using Runge-Kutta. (at lest this worked for me, but there are probably better methods)
There are loads of such simulators online.
Here is one simple one implemented in 500lines of c code. (montion algorhitm is much less)
http://astro.berkeley.edu/~dperley/programs/ssms.html.
Also check this:
http://en.wikipedia.org/wiki/Kepler_problem
http://en.wikipedia.org/wiki/Two-body_problem
http://en.wikipedia.org/wiki/N-body_problem
In physics this is known as the N-Body Problem. It is famous because you can not solve this by hand for a system with more than three planets. Luckily, you can get approximate solutions with a computer very easily.
A nice paper on writing this code from the ground up can be found here.
However, I feel a word of warning is important here. You may not get the results you expect. If you want to see how:
the mass of a planet affects its orbital speed around the Sun, cool. You will see that.
the different planets interact with each other, you will be bummed.
The problem is this.
Yeah, modern astronomers are concerned with how Saturn's mass changes the Earth's orbit around the Sun. But this is a VERY minor effect. If you are going to plot the path of a planet around the Sun, it will hardly matter that there are other planets in the Solar System. The Sun is so big it will drown out all other gravity. The only exceptions to this are:
If your planets have very elliptical orbits. This will cause the planets to potentially get closer together, so they interact more.
If your planets are almost the exact same distance from the Sun. They will interact more.
If you make your planets so comically large they compete with the Sun for gravity in the outer Solar System.
To be clear, yes, you will be able to calculate some interactions between planets. But no, these interactions will not be significant to the naked eye if you create a realistic Solar System.
Try it though, and find out!
Check out nMod, a n-body modeling toolkit written in C++ and using OpenGL. It has a pretty well populated solar system model that comes with it and it should be easy to modify. Also, he has a pretty good wiki about n-body simulation in general. The same guy who created this is also making a new program called Moody, but it doesn't appear to be as far along.
In addition, if you are going to do n-body simulations with more than just a few objects, you should really look at the fast multipole method (also called the fast multipole algorithm). It can the reduce number of computations from O(N^2) to O(N) to really speed up your simulation. It is also one of the top ten most successful algorithms of the 20th century, according to the author of this article.
Algorithms to simulate planetary physics.
Here is an implementation of the Keppler parts, in my Android app. The main parts are on my web site for you can download the whole source: http://www.barrythomas.co.uk/keppler.html
This is my method for drawing the planet at the 'next' position in the orbit. Think of the steps like stepping round a circle, one degree at a time, on a circle which has the same period as the planet you are trying to track. Outside of this method I use a global double as the step counter - called dTime, which contains a number of degrees of rotation.
The key parameters passed to the method are, dEccentricty, dScalar (a scaling factor so the orbit all fits on the display), dYear (the duration of the orbit in Earth years) and to orient the orbit so that perihelion is at the right place on the dial, so to speak, dLongPeri - the Longitude of Perihelion.
drawPlanet:
public void drawPlanet (double dEccentricity, double dScalar, double dYear, Canvas canvas, Paint paint,
String sName, Bitmap bmp, double dLongPeri)
{
double dE, dr, dv, dSatX, dSatY, dSatXCorrected, dSatYCorrected;
float fX, fY;
int iSunXOffset = getWidth() / 2;
int iSunYOffset = getHeight() / 2;
// get the value of E from the angle travelled in this 'tick'
dE = getE (dTime * (1 / dYear), dEccentricity);
// get r: the length of 'radius' vector
dr = getRfromE (dE, dEccentricity, dScalar);
// calculate v - the true anomaly
dv = 2 * Math.atan (
Math.sqrt((1 + dEccentricity) / (1 - dEccentricity))
*
Math.tan(dE / 2)
);
// get X and Y coords based on the origin
dSatX = dr / Math.sin(Math.PI / 2) * Math.sin(dv);
dSatY = Math.sin((Math.PI / 2) - dv) * (dSatX / Math.sin(dv));
// now correct for Longitude of Perihelion for this planet
dSatXCorrected = dSatX * (float)Math.cos (Math.toRadians(dLongPeri)) -
dSatY * (float)Math.sin(Math.toRadians(dLongPeri));
dSatYCorrected = dSatX * (float)Math.sin (Math.toRadians(dLongPeri)) +
dSatY * (float)Math.cos(Math.toRadians(dLongPeri));
// offset the origin to nearer the centre of the display
fX = (float)dSatXCorrected + (float)iSunXOffset;
fY = (float)dSatYCorrected + (float)iSunYOffset;
if (bDrawOrbits)
{
// draw the path of the orbit travelled
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
// get the size of the rect which encloses the elliptical orbit
dE = getE (0.0, dEccentricity);
dr = getRfromE (dE, dEccentricity, dScalar);
rectOval.bottom = (float)dr;
dE = getE (180.0, dEccentricity);
dr = getRfromE (dE, dEccentricity, dScalar);
rectOval.top = (float)(0 - dr);
// calculate minor axis from major axis and eccentricity
// http://www.1728.org/ellipse.htm
double dMajor = rectOval.bottom - rectOval.top;
double dMinor = Math.sqrt(1 - (dEccentricity * dEccentricity)) * dMajor;
rectOval.left = 0 - (float)(dMinor / 2);
rectOval.right = (float)(dMinor / 2);
rectOval.left += (float)iSunXOffset;
rectOval.right += (float)iSunXOffset;
rectOval.top += (float)iSunYOffset;
rectOval.bottom += (float)iSunYOffset;
// now correct for Longitude of Perihelion for this orbit's path
canvas.save();
canvas.rotate((float)dLongPeri, (float)iSunXOffset, (float)iSunYOffset);
canvas.drawOval(rectOval, paint);
canvas.restore();
}
int iBitmapHeight = bmp.getHeight();
canvas.drawBitmap(bmp, fX - (iBitmapHeight / 2), fY - (iBitmapHeight / 2), null);
// draw planet label
myPaint.setColor(Color.WHITE);
paint.setTextSize(30);
canvas.drawText(sName, fX+20, fY-20, paint);
}
The method above calls two further methods which provide values of E (the mean anomaly) and r, the length of the vector at the end of which the planet is found.
getE:
public double getE (double dTime, double dEccentricity)
{
// we are passed the degree count in degrees (duh)
// and the eccentricity value
// the method returns E
double dM1, dD, dE0, dE = 0; // return value E = the mean anomaly
double dM; // local value of M in radians
dM = Math.toRadians (dTime);
int iSign = 1;
if (dM > 0) iSign = 1; else iSign = -1;
dM = Math.abs(dM) / (2 * Math.PI); // Meeus, p 206, line 110
dM = (dM - (long)dM) * (2 * Math.PI) * iSign; // line 120
if (dM < 0)
dM = dM + (2 * Math.PI); // line 130
iSign = 1;
if (dM > Math.PI) iSign = -1; // line 150
if (dM > Math.PI) dM = 2 * Math.PI - dM; // line 160
dE0 = Math.PI / 2; // line 170
dD = Math.PI / 4; // line 170
for (int i = 0; i < 33; i++) // line 180
{
dM1 = dE0 - dEccentricity * Math.sin(dE0); // line 190
dE0 = dE0 + dD * Math.signum((float)(dM - dM1));
dD = dD / 2;
}
dE = dE0 * iSign;
return dE;
}
getRfromE:
public double getRfromE (double dE, double dEccentricty, double dScalar)
{
return Math.min(getWidth(), getHeight()) / 2 * dScalar * (1 - (dEccentricty * Math.cos(dE)));
}
It looks like it is very hard and requires strong knowledge of physics but in fact it is very easy, you need to know only 2 formulas and basic understanding of vectors:
Attractional force (or gravitational force) between planet1 and planet2 with mass m1 and m2 and distance between them d: Fg = G*m1*m2/d^2; Fg = m*a. G is a constant, find it by substituting random values so that acceleration "a" will not be too small and not too big approximately "0.01" or "0.1".
If you have total vector force which is acting on a current planet at that instant of time, you can find instant acceleration a=(total Force)/(mass of current planet). And if you have current acceleration and current velocity and current position, you can find new velocity and new position
If you want to look it real you can use following supereasy algorythm (pseudocode):
int n; // # of planets
Vector2D planetPosition[n];
Vector2D planetVelocity[n]; // initially set by (0, 0)
double planetMass[n];
while (true){
for (int i = 0; i < n; i++){
Vector2D totalForce = (0, 0); // acting on planet i
for (int j = 0; j < n; j++){
if (j == i)
continue; // force between some planet and itself is 0
Fg = G * planetMass[i] * planetMass[j] / distance(i, j) ^ 2;
// Fg is a scalar value representing magnitude of force acting
// between planet[i] and planet[j]
// vectorFg is a vector form of force Fg
// (planetPosition[j] - planetPosition[i]) is a vector value
// (planetPosition[j]-planetPosition[i])/(planetPosition[j]-plantetPosition[i]).magnitude() is a
// unit vector with direction from planet[i] to planet[j]
vectorFg = Fg * (planetPosition[j] - planetPosition[i]) /
(planetPosition[j] - planetPosition[i]).magnitude();
totalForce += vectorFg;
}
Vector2D acceleration = totalForce / planetMass[i];
planetVelocity[i] += acceleration;
}
// it is important to separate two for's, if you want to know why ask in the comments
for (int i = 0; i < n; i++)
planetPosition[i] += planetVelocity[i];
sleep 17 ms;
draw planets;
}
If you're simulating physics, I highly recommend Box2D.
It's a great physics simulator, and will really cut down the amount of boiler plate you'll need, with physics simulating.
Fundamentals of Astrodynamics by Bate, Muller, and White is still required reading at my alma mater for undergrad Aerospace engineers. This tends to cover the orbital mechanics of bodies in Earth orbit...but that is likely the level of physics and math you will need to start your understanding.
+1 for #Stefano Borini's suggestion for "everything that Jean Meeus has written."
Dear Friend here is the graphics code that simulate solar system
Kindly refer through it
/*Arpana*/
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
#include<math.h>
#include<dos.h>
void main()
{
int i=0,j=260,k=30,l=150,m=90;
int n=230,o=10,p=280,q=220;
float pi=3.1424,a,b,c,d,e,f,g,h,z;
int gd=DETECT,gm;
initgraph(&gd,&gm,"c:\tc\bgi");
outtextxy(0,10,"SOLAR SYSTEM-Appu");
outtextxy(500,10,"press any key...");
circle(320,240,20); /* sun */
setfillstyle(1,4);
floodfill(320,240,15);
outtextxy(310,237,"sun");
circle(260,240,8);
setfillstyle(1,2);
floodfill(258,240,15);
floodfill(262,240,15);
outtextxy(240,220,"mercury");
circle(320,300,12);
setfillstyle(1,1);
floodfill(320,298,15);
floodfill(320,302,15);
outtextxy(335,300,"venus");
circle(320,160,10);
setfillstyle(1,5);
floodfill(320,161,15);
floodfill(320,159,15);
outtextxy(332,150, "earth");
circle(453,300,11);
setfillstyle(1,6);
floodfill(445,300,15);
floodfill(448,309,15);
outtextxy(458,280,"mars");
circle(520,240,14);
setfillstyle(1,7);
floodfill(519,240,15);
floodfill(521,240,15);
outtextxy(500,257,"jupiter");
circle(169,122,12);
setfillstyle(1,12);
floodfill(159,125,15);
floodfill(175,125,15);
outtextxy(130,137,"saturn");
circle(320,420,9);
setfillstyle(1,13);
floodfill(320,417,15);
floodfill(320,423,15);
outtextxy(310,400,"urenus");
circle(40,240,9);
setfillstyle(1,10);
floodfill(38,240,15);
floodfill(42,240,15);
outtextxy(25,220,"neptune");
circle(150,420,7);
setfillstyle(1,14);
floodfill(150,419,15);
floodfill(149,422,15);
outtextxy(120,430,"pluto");
getch();
while(!kbhit()) /*animation*/
{
a=(pi/180)*i;
b=(pi/180)*j;
c=(pi/180)*k;
d=(pi/180)*l;
e=(pi/180)*m;
f=(pi/180)*n;
g=(pi/180)*o;
h=(pi/180)*p;
z=(pi/180)*q;
cleardevice();
circle(320,240,20);
setfillstyle(1,4);
floodfill(320,240,15);
outtextxy(310,237,"sun");
circle(320+60*sin(a),240-35*cos(a),8);
setfillstyle(1,2);
pieslice(320+60*sin(a),240-35*cos(a),0,360,8);
circle(320+100*sin(b),240-60*cos(b),12);
setfillstyle(1,1);
pieslice(320+100*sin(b),240-60*cos(b),0,360,12);
circle(320+130*sin(c),240-80*cos(c),10);
setfillstyle(1,5);
pieslice(320+130*sin(c),240-80*cos(c),0,360,10);
circle(320+170*sin(d),240-100*cos(d),11);
setfillstyle(1,6);
pieslice(320+170*sin(d),240-100*cos(d),0,360,11);
circle(320+200*sin(e),240-130*cos(e),14);
setfillstyle(1,7);
pieslice(320+200*sin(e),240-130*cos(e),0,360,14);
circle(320+230*sin(f),240-155*cos(f),12);
setfillstyle(1,12);
pieslice(320+230*sin(f),240-155*cos(f),0,360,12);
circle(320+260*sin(g),240-180*cos(g),9);
setfillstyle(1,13);
pieslice(320+260*sin(g),240-180*cos(g),0,360,9);
circle(320+280*sin(h),240-200*cos(h),9);
setfillstyle(1,10);
pieslice(320+280*sin(h),240-200*cos(h),0,360,9);
circle(320+300*sin(z),240-220*cos(z),7);
setfillstyle(1,14);
pieslice(320+300*sin(z),240-220*cos(z),0,360,7);
delay(20);
i++;
j++;
k++;
l++;
m++;
n++;
o++;
p++;
q+=2;
}
getch();
}

How do I visualize audio data?

I would like to have something that looks something like this. Two different colors are not nessesary.
(source: sourceforge.net)
I already have the audio data (one sample/millisecond) from a stereo wav in two int arrays, one each for left and right channel. I have made a few attempts but they don't look anywhere near as clear as this, my attempts get to spikey or a compact lump.
Any good suggestions? I'm working in c# but psuedocode is ok.
Assume we have
a function DrawLine(color, x1, y1, x2, y2)
two int arrays with data right[] and left[] of lenght L
data values between 32767 and -32768
If you make any other assumptions just write them down in your answer.
for(i = 0; i < L - 1; i++) {
// What magic goes here?
}
This is how it turned out when I applied the solution Han provided. (only one channel)
alt text http://www.imagechicken.com/uploads/1245877759099921200.jpg
You'll likely have more than 1 sample for each pixel. For each group of samples mapped to a single pixel, you could draw a (vertical) line segment from the minimum value in the sample group to the maximum value. If you zoom in to 1 sample per pixel or less, this doesn't work anymore, and the 'nice' solution would be to display the sinc interpolated values.
Because DrawLine cannot paint a single pixel, there is a small problem when the minimum and maximum are the same. In that case you could copy a single pixel image in the desired position, as in the code below:
double samplesPerPixel = (double)L / _width;
double firstSample = 0;
int endSample = firstSample + L - 1;
for (short pixel = 0; pixel < _width; pixel++)
{
int lastSample = __min(endSample, (int)(firstSample + samplesPerPixel));
double Y = _data[channel][(int)firstSample];
double minY = Y;
double maxY = Y;
for (int sample = (int)firstSample + 1; sample <= lastSample; sample++)
{
Y = _data[channel][sample];
minY = __min(Y, minY);
maxY = __max(Y, maxY);
}
x = pixel + _offsetx;
y1 = Value2Pixel(minY);
y2 = Value2Pixel(maxY);
if (y1 == y2)
{
g->DrawImageUnscaled(bm, x, y1);
}
else
{
g->DrawLine(pen, x, y1, x, y2);
}
firstSample += samplesPerPixel;
}
Note that Value2Pixel scales a sample value to a pixel value (in the y-direction).
You might want to look into the R language for this. I don't have very much experience with it, but it's used largely in statistical analysis/visualization scenarios. I would be surprised if they didn't have some smoothing function to get rid of the extremes like you mentioned.
And you should have no trouble importing your data into it. Not only can you read flat text files, but it's also designed to be easily extensible with C, so there is probably some kind of C# interface as well.

Resources