I have made a program that calculates a ballistic curve according to Runge Kutta 4.
It prints the results in a .csv file to open it in excel afterwards.
The exception is thrown at the point, when i close the file at the end of the method.
It works if i have a small step size, and only write less than 500 entry lines...
I even changed it, so it writes at the end of the calculation.
If I wait between every 100 entrys for a second, it sometimes works.
Btw. I’m very sure it’s not a problem of my calculation, because it works with h=0.1 und duration 3.
void BallisticCalculator::printGraph(float v0, float d, float m, float scopeOffset, float alpha, float h, float duration) {
// constants
static const float Roh = 1.2; // kg/m^3
static const float Cw = 0.3; //a typical Bullet
static const float PI = 3.1415926535;
Vector2D* g = new Vector2D(0, -9.81);
// open file
std::remove("RungeKutta.csv");
std::ofstream myfile;
myfile.open("RungeKutta.csv");
//myfile.open("X:\Math\RungeKutta.csv");
int w = 0; //writing acsess
// converting to SI
float A = (d / 2000) * (d / 2000) * PI; // mm to m^2
m = m / 1000; //gramm to kg
scopeOffset = scopeOffset / 100; //cm to meters
alpha = alpha * PI / (60 * 180); // MOA to degree to radians
// Luftwiderstands Beschleunigung (Luftwiderstandskraft durch masse der Kugel)
double k = Roh * Cw * A / 2 / m;
// data
Bullet* graph = new Bullet[duration / h];
for (double t = 0; t < duration; t += h) {
w++;
int i = (int)(t / h);
if (i == 0) {
graph[i].pos = Vector2D(0, -scopeOffset);
graph[i].v = Vector2D(std::cos(alpha) * v0, std::sin(alpha) * v0);
graph[i].a = acc(graph[i].v, *g, k);
}
else {
Vector2D v1, v2, v3, v4 = Vector2D(0, 0);
v1 = acc(graph[i - 1].v, *g, k);
v2 = acc(graph[i - 1].v + (v1 * (h / 2)), *g, k);
v3 = acc(graph[i - 1].v + (v2 * (h / 2)), *g, k);
v4 = acc(graph[i - 1].v + (v3 * h), *g, k);
graph[i].v = graph[i - 1].v + ((v1 + v2 + v2 + v3 + v3 + v4) * (h / 6));
graph[i].pos = graph[i - 1].pos + (graph[i].v * h);
graph[i].a = acc(graph[i].v, *g, k);
}
Iid like to do it this way:
myfile << t << ";" << graph[i].pos.x << ";" << graph[i].pos.y << ";" << norm(graph[i].v) << ";" << "\n";
// time distance drop speed
//waiting here makes it sometimes possible to work....
if (w > 100) {
myfile.close();
std::cout << "\n" << "Eingabetaste drücken, diese Pause dient zur Entlastung des Schreibsystems...";
std::cin.ignore();
myfile.open("RungeKutta.csv", std::ios::app);
w = 0;
}
}
// for testing purpuses
//for (int i = 0; i < (duration / h); i++) {
// myfile << graph[i].pos.x << ";" << graph[i].pos.y << ";" << norm(graph[i].v) << ";" << "\n";
//}
myfile.close();
//return state;
std::cout << "finished" <<"\n" << "\n";
};
BallisticCalculator::Vector2D BallisticCalculator::acc(Vector2D v, Vector2D g, float k) //v{x,y}, gravity, k = Roh * Cw * A / 2 / m
{
return (v * -k * norm(v))+ g;
}
the Exeption was:
Exception thrown at 0x7A5EB2E7 (ucrtbased.dll) in ConsoleApplicationRungeKutta.exe: 0xC0000005: Access violation writing location 0xC311F2DD.
thrown at the next line after myfile.close();
After discussing this with my co-worker, we found a solution. Although we couldn’t say what’s wrong with it, I’m probably accessing memory I shouldn’t.
Defining the Array size of the graph with a rounded Integer didn’t help. And I had to make the Arrays size dynamic.
It works with a vector, so the problem isn’t the fstream …
How to fix it:
//Bullet* graph = new Bullet[duration / h];
std::vector<Bullet> graph; // new
and:
for (double t = 0; t < duration; t += h) {
graph.push_back(Bullet()); //new
It would be intressting, why my array was not working..
Related
i am trying to implement a molecular dynamics simulation with the Lennard Jones potential.
I have the time evolution of the positions and velocities of the particles in multiple config files (n = 0,...,99) in steps of dt, such that t=n dt. So the actual simulation part is taken care of in that sense, for now i only have to calculate the potential energy and the force on each particle.
I already implemented a function to read in the config.dat files and put them in vectors, that part works as far as i know without an error. Then i wrote functions that calculate the force and the potential energy with a given distance r_ij between two particles (also used Newtown's third law so that i don't have to calculate the forces multiple times for the same interaction).
I also (hopefully correctly) implemented the periodic boundary conditions so that the particles can interact with their own images in the image boxes.
To test if my code works, i wanted to plot the total potential energy for all t=n dt.
However that does not work as intended because for some reason the potential energy that is written into the output files is always zero (the function for the potential energy returns zero if r_ij > r_cut, r_cut is where the potential is set to zero).
#include <iostream>
#include <math.h>
#include <fstream>
#include <stdlib.h>
#include <vector>
#include <string>
#include <utility>
#include <stdexcept>
#include <sstream>
using namespace std;
// Python >> C/C++
// Reads the initial states from the files
// The whole simulation in Python would have been as long as the function in c++ that just reads in the input
void read_input(int num_file, vector<double>& n, vector<double>& x, vector<double>& y, vector<double>& vx, vector<double>& vy, double& lx, double& ly) {
// Variable file name + opening it
ifstream file("configurations/config_" + to_string(num_file) + ".dat");
// Temp variables for reading in the file
double num, temp_x, temp_y, temp_vx, temp_vy;
// Looping over it
if (file.is_open()) {
string line;
while (getline(file, line)) {
// For the header; The header contains only the dimensions, e.g, 14 14, there are only 5 characters
if (line.length() == 5) {
stringstream dim_str(line);
dim_str >> lx >> ly;
continue;
}
// For the rest files
stringstream temp_str(line);
temp_str >> num >> temp_x >> temp_y >> temp_vx >> temp_vy;
n.push_back(num);
x.push_back(temp_x);
y.push_back(temp_y);
vx.push_back(temp_vx);
vy.push_back(temp_vy);
}
file.close();
}
}
// Calculates the potential for rij
double calc_pot(double r) {
double sigma = 1.0;
double epsilon = 1.0;
if (r <= pow(2, (1.0 / 6)) * sigma) {
double res = 4.0 * epsilon * (pow(sigma / r, 12) - pow(sigma / r, 6)) + epsilon;
return res;
}
else {
return 0;
}
}
// Calculates the force for rij
double calc_force(double r) {
double sigma = 1.0;
// Replaced the sigma^n with 1 bcs sigma = 1
double epsilon = 1.0;
if (r <= pow(2, (1.0 / 6)) * sigma) {
double res = (48.0 * epsilon / pow(r, 13)) - (24 * epsilon / pow(r, 7));
return res;
}
else {
return 0;
}
}
// Calculates the distance of the
double dist(double rx, double ry) {
return sqrt(rx * rx + ry * ry);
}
int main() {
// Misc. parameters
int N = 144;
double mass = 1.0;
double sigma = 1.0;
double epsilon = 1.0;
// Tau = sqrt(mass*sigma^2/epsilon) = (here) 1
double tau = 1.0;
double dt = 0.02;
// Vectors for the read in values for i and i+1
vector<double> n, x, y, vx, vy;
double lx, ly;
// Vector for the potential and two for the force, x and y
vector<double> epot(N), f_x(N), f_y(N);
for (int i = 0; i < N; i++) {
epot[i], f_x[i], f_y[i] = 0;
}
// Outerloop for time steps (in this case the files n = {0,..,99})
for (int k = 0; k <= 99; k++) {
string fname = "output/epot_" + to_string(k) + ".txt";
ofstream output(fname);
read_input(k, n, x, y, vx, vy, lx, ly);
// Inner two loops to accses every possible interaction without doing them twice
for (int i = 0; i < N - 1; i++) {
// Vecctor for particle i
double rix = x[i];
double riy = y[i];
for (int j = i + 1; j < N; j++) {
// Vector for particle i+1
double rjx = x[j];
double rjy = y[j];
// Periodic boundary cond.
if (rix > lx) {
rix -= lx;
}
if (riy > ly) {
riy -= ly;
}
if (rjx > lx) {
rjx -= lx;
}
if (rjy > ly) {
rjy -= ly;
}
if (rix < 0) {
rix += lx;
}
if (riy < 0) {
riy += ly;
}
if (rjx < 0) {
rjx += lx;
}
if (rjy < 0) {
rjy += ly;
}
// Component wise distance for the force
double dist_x = rix - rjx;
double dist_y = riy - rjy;
// Minimum image convention
if (abs(dist_x) > lx / 2) {
dist_x = (lx - abs(dist_x)) * (-dist_x) / abs(dist_x);
}
if (abs(dist_y) > ly / 2) {
dist_y = (ly - abs(dist_y)) * (-dist_y) / abs(dist_y);
}
// Normalized Force/R
f_x[i] += calc_force(dist_x) * (1 / dist(dist_x, dist_y));
f_y[i] += calc_force(dist_y) * (1 / dist(dist_x, dist_y));
f_y[j] += -calc_force(dist_x) * (1 / dist(dist_x, dist_y));
f_y[j] += -calc_force(dist_y) * (1 / dist(dist_x, dist_y));
// Potential energy
epot[i] += calc_pot(dist(dist_x, dist_y));
}
// Potential energy per particle
output << fixed << std::setprecision(4) << epot[i] / (N) << endl;
}
}
}
A config file looks something like this
14 14
0 0 0 1.0292605474705 0.394157727758591
1 0 1.16666666666667 1.05721528014223 1.9850461002085
2 0 2.33333333333333 1.18385526103892 0.143930912297367
3 0 3.5 -0.938850340823852 1.71993225409788
4 0 4.66666666666667 1.99468650405917 0.952210892864475
5 0 5.83333333333333 -0.985361963654284 3.05201529674118
6 0 7 2.84071317501321 0.0689241023507716
7 0 8.16666666666667 3.56152464385237 2.88858201933488
8 0 9.33333333333333 0.147896423269195 1.40592679110988
The header contains the dimensions of the simulation box, here (14,14).
Then all the lines have the corresponding values of {#Particle, x, y, velocity x, Velocity y).
The file above shows this for the first 9 particles.
I am relatively new to c/c++ so have mercy with me 😄.
Also i am aware that the code has still potential to be optimised but i will deal with that when i can calculate the force on each particle correctly.
Edit:
Here is the formula for the potential energy:
The force can be calculated via F= -d/dr U(r).
I have to apply the 4th order Runge Kutta method (RK) to the coupled equation in handbook of marinecraft hydrodynamics and motion control, page 154, equation 7.33, to determine v and r for every iteration. the RK method works when I use the functions separately, i.e., when I use the functions in separate programs. I have converted the equations from matrix form to linear equations. q1, q2, q3 and q4 are coefficients of v and r
The RK method is given as a function and is called twice to find v and r values. I can obtain the correct range of values of r as when rud increases, the values of v and r increase as well. when rud = rudmax, v and r should remain constant. however, v keeps increasing and doesn't stop. both v and r should be constant when rud = rudmax.
#include <iostream>
#include <fstream>
#include <cmath>
#include <iomanip>
using namespace std;
double dvdt(double r, double v);
double drdt(double v, double r);
double RKv1(double to, double vo, double t, double h);
double RKr1(double to, double ro, double t, double h);
double xo = 0; //initial X position
double yo = 0; //initial Y position
double x1; //final X position
double y11; //final Y position
double ti = 0; //initial time = 0
double tf; //time entered
double dt = 0.01; //time step (iteration time)
double rud = 0; //rudder angle
double m = 3.27385 * pow(10, 11); //mass of submarine (Kg)
double Iz = 2.31448 * pow(10, 12); //moment of inertia of submarine (kg.m^2)
double xg = 10.3; //x position of COG
double yg = 0; // position of COG
double u = 10; // surge velocity
double v, vo = 0; //sway velocity
double r, ro = 0; // yaw rate
double psio = 0; //initial ship heading
double psi1; //final ship heading
double rudmax; // max rudder angle
double au; //surge acceleration
double av; // sway acceleration
double ar; //angular acceleration
double X; //surge force
double Y; //sway force
double N; //turning moment
double h = 1;
//double q1, q2, q3, q4;
//matrices
//maneuvering and hydrodynamic coefficents
double Xu1 = -0.010087715;//-1.0467 * pow(10,-3);
double Yv1 = -0.290828106;//-23.889 * pow(10, -3);
double Yr1 = -0.008070039;//-1.0510 * pow(10, -3);
double Nv1 = -0.002928156;// 1.1531 * pow(10, -3);
double Nr1 = -0.056559574; //-0.50717 * pow(10, -3);
double Xu = -0.007115651;// -2.8763 * pow(10, -3);
double Yv = -0.35098699; //-38.948 * pow(10, -3);
double Yr = 0.091748747;// 2.0031 * pow(10, -3);
double Nv = -0.123433077;//-14.287 * pow(10, -3);
double Nr = -0.056559574;//-4.2267 * pow(10, -3);
double Yrud = -0.587323034;//1.4308 * pow(10, -3);
double Nrud = 0.991135206;// -0.71540 * pow(10, -3);
double a1 = m - Yv1;
double b1 = m * xg - Yr1;
double a2 = m * xg - Yr1;
double b2 = Iz - Nr1;
double det = a1 * b2 - a2 * b1;
double q1 = (-Yv * (Iz - Nr1) / det);
double q2 = ((m - Xu1) * u - Yr) * (-m * xg + Yr1) / det;
double q3 = ((Xu1 - Yv1) * u - Nv) * (-m * xg + Yr1) / det;
double q4 = ((m * xg - Yr1) * u - Nr) * (m - Yr1) / det;
int main()
{
std::ofstream fout; //for .CSV file
fout.open("results2.csv", ios::out | ios::app); //for .CSV file
//cout << det<<"\n";
/*cout << "surge velocity = ";
cin >> */ u = 10;
/*cout << "rudmax = ";
cin >> */rudmax = 30;
/*cout << "final time = ";
cin >>*/ tf = 100;
fout << "rud" << ", " << "ti" << ", " << "r" << ", " << "v" << "\n"; //for .CSV file
while (ti <= tf)
{
v = RKv1(ti, vo, ti + 0.025, h);
r = RKr1(ti, ro, ti + 0.025, h);
fout << rud << ", " << ti << ", " << r << ", " << v << "\n"; //for .CSV file
vo = v;
ro = r;
ti = ti + dt;
if (rudmax > 0)
{
if (rud < rudmax)
{
rud = rud + 0.005;
}
}
else if (rudmax < 0)
{
if (rud > rudmax)
{
rud = rud - 0.005;
}
}
}
}
/*---------------------------------------------------FUNCTIONS---------------------------------------------------*/
//functions for velocity and distance
double dvdt(double r, double v)
{
//return(-Yrud * rud - ((-Yv * (Iz - Nr1) / det) * v + ((m - Xu1) * u - Yr) * ((-m * xg - Yr1) / det )* r));
return(-Yrud * rud - q1 * v + q2 * r);
}
double drdt(double v, double r)
{
//return(-Nrud * rud - (((Xu1 - Yv1) * u - Nv) * ((-m * xg + Yr) / det) * v + ((m * xg - Yr1) * u - Nr) * ((m - Yr1) / det) * r));
return(-Nrud * rud - q3 * v + q4 * r);
}
//RK methods
//correction of rk method
double RKv1(double to, double vo, double t, double h)
{
int n = /*(int)((t - to) / h)*/ 1;
double k1, k2, k3, k4;
double v = vo;
for (int i = 1; i <= n; i++)
{
k1 = h * dvdt(ro, v);
k2 = h * dvdt(ro, v + 0.5 * k1);
k3 = h * dvdt(ro, v + 0.5 * k2);
k4 = h * dvdt(ro, v + k3);
v = v + (1.0 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4);
to = to + h;
}
return v;
}
double RKr1(double to, double ro, double t, double h)
{
int n = /*(int)((t - to) / h)*/ 1;
double k1, k2, k3, k4;
double r = ro;
for (int i = 1; i <= n; i++)
{
k1 = h * drdt(vo, r);
k2 = h * drdt(vo, r + 0.5 * k1);
k3 = h * drdt(vo, r + 0.5 * k2);
k4 = h * drdt(vo, r + k3);
r = r + (1.0 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4);
to = to + h;
}
return r;
}
the results are stored in the results2.csv file.
snippet of the result. r (-28.59) remains constant whereas v (-84000) keep increasing
Any help/suggestions are appreciated. thanks!
I have a XML file with info about a bus stops (e.g. route number, coordinates (lat, lon) of bus stop, etc.).
I have a task: Print the longest route using coordinates.
In general I understand the algorithm:
Do the matches between route number and vector of its coordinates.
Get length of each route (it's a broken line, let's say it's straight in order to make code simpler, it doesn't matter), where the vertex is a bus stop with its coordinates.
Find the max and print it.
The problem is: the data is unsorted, so if I try to get distance between two points (i and i + 1 for instance) the result will be wrong because geographically between these 2 points there're other points (there're somewhere further in the array) and they will be ignored -> length size will be wrong too.
Is there any ways to fix it?
Here is what I managed to do:
double getDistance(double lat1, double long1, double lat2, double long2) {
double la1 = lat1 * M_PI / 180.0;
double la2 = lat2 * M_PI / 180.0;
// Haversine
double dlat = (lat2 - lat1) * M_PI / 180.0;
double dlong = (long2 - long1) * M_PI / 180.0;
double ans = (sin(dlat / 2) * sin(dlat / 2)) +
cos(la1) * cos(la2) * sin(dlong / 2) * sin(dlong / 2);
double c = 2 * atan2(sqrt(ans), sqrt(1 - ans));
double R = 6371;
double d = R * c; // in metres
return d;
}
void getLongestRoute(vector<elementXML> transport, string transportName) {
// key - routeNumber, value - vector of coordinates
map<string, vector<pair<double, double>>> routesData;
// getting each route
for (int i = 0; i < transport.size(); i++) {
for (int j = 0; j < transport.at(i).getRoutes().size(); j++) {
routesData[transport.at(i).getRoutes().at(j)].push_back(transport.at(i).getCoordinates());
}
}
double maxLength = 0.0;
string max = "";
double length = 0.0;
for (const auto &routes : routesData) {
for (int i = 0; i < routes.second.size() - 1; i++) {
length += getDistance(routes.second.at(i).first, routes.second.at(i).second,
routes.second.at(i + 1).first, routes.second.at(i + 1).second);
}
if (length > maxLength) {
maxLength = length;
max = routes.first;
}
length = 0;
}
cout << "Longest route for " << transportName << " - " << "#" << max << ", length - "
<< round(maxLength) << " km" << endl;
}
Example of what I get from console:
Longest route for BUS - #487, length - 1050 km // ~90 km expected
It will be amazing if I get any help. Thanks.
I have an error message C++: Variable 'DiscreteGBM::S0' is uninitialized. Always initialize a member variable (type.6). This message is repeated a few times with names of different member variables.
Can someone kind teach me how to initialize please? The header file cannot be changed. Only allowed changing of cpp file. However for cpp file, main function cannot be changed.
This first code is for header file. This is not allowed to be changed
class DiscreteGBM
{
protected:
double r; // risk free interest rate
double S0; // stock price at time 0
double sigma; // volatility
double delta; // length of time steps
int numberSteps; // total number of time steps;
// start time is 0, end time is numberSteps*delta
public:
DiscreteGBM() {}
void SetRiskFreeRate(double r1); // set r to r1
void SetInitialStockPrice(double s); // set S0 to s
void SetVolatility(double s); // set sigma to s
void SetTimeStepSize(double d); // set delta to d
void SetNumberSteps(int n); // set numberSteps to n
vector<double> PricePath() const; // compute price path S_0,...,S_T (where T=delta*n)
// according to formula (1) in the instructions,
// and return the vector (S_0,...,S_T)
};
// class for simulation of European call and put options
class EuropeanOption : public DiscreteGBM
{
protected:
bool callPut; // callPut =0 if object is call option, callPut=1 if it's a put option
double strike; // strike price of option
public:
EuropeanOption() {}
void SetCallPut(bool c); // set callPut to c
void SetStrike(double s); // set strike to s
double Payoff(double S_T) const; // return payoff of option according to
// formulas (2) and (3) of the instructions;
// S_T is the stock price at maturity
double BlackScholesPrice() const; // return Black Scholes price of the option
// according to formulas (4) and (6) of the instructions
double MonteCarloPrice(int numberScenarios=10000) const;
// execute algorithm Monte Carlo pricing given
// in instructions
};
CPP file below
#include "Functions.h"
#include "PricingSimulation.h"
void DiscreteGBM::SetRiskFreeRate(double r1) {
r = r1;
}
void DiscreteGBM::SetInitialStockPrice(double s) {
S0 = s;
}
void DiscreteGBM::SetVolatility(double s) {
sigma = s;
}
void DiscreteGBM::SetTimeStepSize(double d) {
delta = d;
}
void DiscreteGBM::SetNumberSteps(int n) {
numberSteps = n;
}
vector<double> DiscreteGBM::PricePath() const {
vector<double> stock;
vector<double> rnorm;
rnorm = randn(numberSteps);
stock[0] = S0;
for (int i = 1; i <= numberSteps; i++) {
stock[i] = stock[i - 1] * exp((r - 0.5 * sigma * sigma) * delta + sigma * sqrt(delta) * rnorm[i]);
}
return stock;
}
void EuropeanOption::SetCallPut(bool c) {
callPut = c;
}
void EuropeanOption::SetStrike(double s) {
strike = s;
}
double EuropeanOption::Payoff(double S_T) const {
double value;
if (callPut == 0)
if ((S_T - strike) > 0)
value = S_T - strike;
else
value = 0;
else
if ((strike - S_T) > 0)
value = strike - S_T;
else
value = 0;
return value;
}
double EuropeanOption::BlackScholesPrice() const {
double value2, d1, d2;
d1 = (1 / (sigma * sqrt(numberSteps * delta)) * (log(S0 / strike) + (r + sigma * sigma / 2) * numberSteps * delta));
d2 = d1 - sigma * sqrt(numberSteps * delta);
if (callPut == 0)
value2 = normcdf(d1) * S0 - normcdf(d2) * strike * exp(-r * numberSteps * delta);
else
value2 = -normcdf(-d1) * S0 + normcdf(-d2) * strike * exp(-r * numberSteps * delta);
return value2;
}
double EuropeanOption::MonteCarloPrice(int numberScenarios = 10000) const {
int N = numberScenarios;
double sum;
double T = numberSteps * delta;
vector <double> A;
double O;
for (int i = 1; i <= N; i++) {
A = PricePath();
O = A[numberSteps];
sum += O;
}
double average = sum / N;
return exp(-r * T) * average;
}
int main()
{
SetSeed();
EuropeanOption Test;
for (double r = -0.1; r <= 0.1; r += 0.05)
for (double sigma = 0.1; sigma <= 1; sigma += 0.3)
for (int c = 0; c <= 1; c++)
{
Test.SetRiskFreeRate(r);
Test.SetInitialStockPrice(100);
Test.SetVolatility(sigma);
Test.SetNumberSteps(100);
Test.SetTimeStepSize(0.1);
Test.SetStrike(130);
Test.SetCallPut(c);
cout << "r = " << r << ", sigma = " << sigma;
cout << ", c = " << c << ": ";
cout << Test.BlackScholesPrice() << " , ";
cout << Test.MonteCarloPrice() << endl;
}
system("pause");
}
I tried a quick and dirty translation of the code here.
However, my version outputs noise comparable to grey t-shirt material, or heather if it please you:
#include <fstream>
#include "perlin.h"
double Perlin::cos_Interp(double a, double b, double x)
{
ft = x * 3.1415927;
f = (1 - cos(ft)) * .5;
return a * (1 - f) + b * f;
}
double Perlin::noise_2D(double x, double y)
{
/*
int n = (int)x + (int)y * 57;
n = (n << 13) ^ n;
int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return 1.0 - ((double)nn / 1073741824.0);
*/
int n = (int)x + (int)y * 57;
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
double Perlin::smooth_2D(double x, double y)
{
corners = ( noise_2D(x - 1, y - 1) + noise_2D(x + 1, y - 1) + noise_2D(x - 1, y + 1) + noise_2D(x + 1, y + 1) ) / 16;
sides = ( noise_2D(x - 1, y) + noise_2D(x + 1, y) + noise_2D(x, y - 1) + noise_2D(x, y + 1) ) / 8;
center = noise_2D(x, y) / 4;
return corners + sides + center;
}
double Perlin::interp(double x, double y)
{
int x_i = int(x);
double x_left = x - x_i;
int y_i = int(y);
double y_left = y - y_i;
double v1 = smooth_2D(x_i, y_i);
double v2 = smooth_2D(x_i + 1, y_i);
double v3 = smooth_2D(x_i, y_i + 1);
double v4 = smooth_2D(x_i + 1, y_i + 1);
double i1 = cos_Interp(v1, v2, x_left);
double i2 = cos_Interp(v3, v4, x_left);
return cos_Interp(i1, i2, y_left);
}
double Perlin::perlin_2D(double x, double y)
{
double total = 0;
double p = .25;
int n = 1;
for(int i = 0; i < n; ++i)
{
double freq = pow(2, i);
double amp = pow(p, i);
total = total + interp(x * freq, y * freq) * amp;
}
return total;
}
int main()
{
Perlin perl;
ofstream ofs("./noise2D.ppm", ios_base::binary);
ofs << "P6\n" << 512 << " " << 512 << "\n255\n";
for(int i = 0; i < 512; ++i)
{
for(int j = 0; j < 512; ++j)
{
double n = perl.perlin_2D(i, j);
n = floor((n + 1.0) / 2.0 * 255);
unsigned char c = n;
ofs << c << c << c;
}
}
ofs.close();
return 0;
}
I don't believe that I strayed too far from the aforementioned site's directions aside from adding in the ppm image generation code, but then again I'll admit to not fully grasping what is going on in the code.
As you'll see by the commented section, I tried two (similar) ways of generating pseudorandom numbers for noise. I also tried different ways of scaling the numbers returned by perlin_2D to RGB color values. These two ways of editing the code have just yielded different looking t-shirt material. So, I'm forced to believe that there's something bigger going on that I am unable to recognize.
Also, I'm compiling with g++ and the c++11 standard.
EDIT: Here's an example: http://imgur.com/Sh17QjK
To convert a double in the range of [-1.0, 1.0] to an integer in range [0, 255]:
n = floor((n + 1.0) / 2.0 * 255.99);
To write it as a binary value to the PPM file:
ofstream ofs("./noise2D.ppm", ios_base::binary);
...
unsigned char c = n;
ofs << c << c << c;
Is this a direct copy of your code? You assigned an integer to what should be the Y fractional value - it's a typo and it will throw the entire noise algorithm off if you don't fix:
double Perlin::interp(double x, double y)
{
int x_i = int(x);
double x_left = x - x_i;
int y_i = int(y);
double y_left = y = y_i; //This Should have a minus, not an "=" like the line above
.....
}
My guess is if you're successfully generating the bitmap with the proper color computation, you're getting vertical bars or something along those lines?
You also need to remember that the Perlin generator usually spits out numbers in the range of -1 to 1 and you need to multiply the resultant value as such:
value * 127 + 128 = {R, G, B}
to get a good grayscale image.