Browse Source

initial state, going to make AwesomeAttractor more OO

master
Joshua 15 years ago
commit
8c57b315af
  1. 345
      Attractor.cpp
  2. 61
      Attractor.hpp
  3. 33
      AttractorKernel.hpp
  4. 64
      AwesomeAttractor.cbp
  5. 52
      AwesomeAttractor.layout
  6. 40
      AwesomeAttractor.layout.save
  7. 238
      Canvas.cpp
  8. 50
      Canvas.hpp
  9. 110
      Parameters.cpp
  10. 34
      Parameters.hpp
  11. 111
      Projector.cpp
  12. 45
      Projector.hpp
  13. 126
      Vector.cpp
  14. 31
      Vector.hpp
  15. 14
      batchexport.sh
  16. 17
      batchrender.sh
  17. 73
      kernels/Lorenz.cpp
  18. 48
      kernels/Lorenz.hpp
  19. 71
      kernels/Unravel.cpp
  20. 49
      kernels/Unravel.hpp
  21. 133
      main.cpp
  22. 12
      myMath.hpp
  23. BIN
      pngwriter/.DS_Store
  24. BIN
      pngwriter/._.DS_Store
  25. BIN
      pngwriter/._Makefile
  26. BIN
      pngwriter/._pngwriter.cc
  27. BIN
      pngwriter/._pngwriter.h
  28. 32
      pngwriter/Makefile
  29. 4721
      pngwriter/pngwriter.cc
  30. 747
      pngwriter/pngwriter.h
  31. 2
      quick.sh
  32. 11
      stfu/ExampleProgramOutput.stf
  33. 28
      stfu/LICENSE.txt
  34. 45
      stfu/STFU.cbp
  35. 21
      stfu/STFU.depend
  36. 26
      stfu/STF_Format_Example.stf
  37. 6
      stfu/arrays.stf
  38. 282
      stfu/main.cpp
  39. 4
      stfu/screenoptions.stf
  40. 107
      stfu/simple documentation.html
  41. 9
      stfu/soundoptions.stf
  42. 276
      stfu/stf.cpp
  43. 358
      stfu/stf.hpp
  44. 4
      stfu/test.stf

345
Attractor.cpp

@ -0,0 +1,345 @@
#include <cmath>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
#include <cassert>
#include <string>
using namespace std;
#include "Attractor.hpp"
/*
Constructors & initialisers
*/
Attractor::Attractor():
dim(3), par(4), formula(LORENZ), param(NULL), point(NULL), new_point(NULL) {
// Default attractor: 3D Lorenz attrractor
init_vector();
init_param();
param[0] = 0.001; //dt
param[1] = 10; //sigma
param[2] = 28; //rho
param[3] = 8.0/3.0; //beta
}
Attractor::Attractor(unsigned int dimensions, FormulaChoice formula, unsigned int orde):
dim(dimensions), formula(formula), orde(orde), param(NULL), point(NULL), new_point(NULL) {
init(dimensions, formula, orde);
}
Attractor::Attractor(const char* const filename) {
ifstream file(filename);
cout << "Reading file " << filename << "..." << endl;
if ( !file ) {
cout << " Error reading file '" << filename << "' dying now..." << endl;
exit(2);
}
/*
file.attr:
lorenz
3
0
3.24454
1.25
....
*/
string fileFormula;
file >> fileFormula;
for ( unsigned int i = 0; fileFormula[i] != '\0'; i++ ) {
fileFormula[i] = tolower(fileFormula[i]);
}
unsigned int fileDim;
file >> fileDim;
unsigned int fileOrde;
file >> fileOrde;
cout << " Formula: " << fileFormula << endl;
cout << " Dimensions: " << fileDim << endl;
cout << " Orde: " << fileOrde << endl;
if ( fileFormula == "lorenz" )
init(fileDim, LORENZ, fileOrde);
else if ( fileFormula == "poly_n" )
init(fileDim, POLY_N, fileOrde);
else if ( fileFormula == "poly_a" )
init(fileDim, POLY_A, fileOrde);
else if ( fileFormula == "logistic" )
init(fileDim, LOGISTIC, fileOrde);
else if ( fileFormula == "unravel" )
init(fileDim, UNRAVEL, fileOrde);
else {
cout << " Formula not (yet) supported" << endl;
exit(3);
}
for ( unsigned int i = 0; i < par; i++ ) {
file >> param[i];
cout << " Parameter " << i << " set to " << param[i] << ", ";
}
cout << endl << " Reading file complete" << endl;
}
void Attractor::init(unsigned int dim_in, FormulaChoice formula_in, unsigned int orde_in) {
dim = dim_in;
formula = formula_in;
orde = orde_in;
switch (formula) {
case POLY_N: {
double n_coef = orde + 1;
for (unsigned int i = 2; i <= dim; i++) {
n_coef = n_coef*(orde + i)/(i - 1);
}
par = (unsigned int) n_coef;
break;
}
case LORENZ: {
assert(dim == 3 || dim == 4);
par = dim + 1;
break;
}
case POLY_A: {
par = dim;
break;
}
case LOGISTIC: {
par = dim;
break;
}
case UNRAVEL: {
assert(dim == 3);
par = 7;
break;
}
default: {
cout << "Formula not recognized" << endl;
exit(1);
break;
}
}
init_vector();
init_param();
}
void Attractor::init_vector() {
point = new double[dim];
new_point = new double[dim];
assert(point != NULL);
assert(new_point != NULL);
for ( unsigned int i = 0; i < dim; i++ ) {
point[i] = new_point[i] = 0.9;
}
}
void Attractor::init_param() {
param = new double[par];
assert(param != NULL);
for ( unsigned int i = 0; i < dim; i++ ) {
param[i] = 0.0;
}
}
void Attractor::init_range() {
// stabilize attractor
for ( unsigned int i = 0; i < 100000; i++ ) {
iterate();
if ( !is_chaos() ) {
cout << "Attractor died after " << i << " iterations" << endl;
exit(0);
}
}
// initialize ranges
for ( vector<Projector*>::iterator it = projectors.begin(); it != projectors.end(); it++ ) {
(*it)->extern_dim = dim;
(*it)->intern_dim = 2;
(*it)->init(point);
}
// update ranges
for ( unsigned int i = 0; i < 100000; i++ ) {
iterate();
if ( !is_chaos() ) {
cout << "Attractor died after " << i << " iterations" << endl;
exit(0);
}
for ( vector<Projector*>::iterator it = projectors.begin(); it != projectors.end(); it++ ) {
(*it)->update_range(point);
}
}
for ( vector<Projector*>::iterator it = projectors.begin(); it != projectors.end(); it++ ) {
(*it)->finish_range();
}
}
bool Attractor::is_chaos() {
/*
check existence of attractor:
Escaping
Single point attractor
Lyapunov exponent
*/
double sum = 0;
for ( unsigned int i = 0; i < dim; i++ ) {
const double dist = new_point[i] - point[i];
sum += dist*dist;
}
if ( sum >= 1.0e7 ) {
// big change => Escaping
return false;
}
if ( sum <= 1.0e-7 ) {
// small change => singularity
return false;
}
return true;
}
/*
Iteration & Math
*/
void Attractor::iterate() {
// pointer swap
double * temp = point;
point = new_point;
new_point = temp;
// calculations
switch (formula) {
case POLY_N:
polynome();
break;
case POLY_A:
poly_A();
break;
case POLY_2:
poly_2();
break;
case LOGISTIC:
logistic();
break;
default:
cout << "Formula not recognized" << endl;
exit(1);
break;
}
#ifdef HARDDEBUG
cout << "Formula " << formula << " is done" << endl;
cout << "Old Vector: ";
for ( unsigned int i = 0; i < dim; i++ ) {
cout << point[i] << " ";
}
cout << endl << "Fresh Vector: ";
for ( unsigned int i = 0; i < dim; i++ ) {
cout << new_point[i] << " ";
}
cout << endl;
#endif
}
void Attractor::polynome() {
unsigned int m = 0;
for ( unsigned int i = 0; i < dim; i++ ) {
#ifdef HARDDEBUG
cout << "Entering new dimension: " << i << " With m = " << m << endl;
#endif
new_point[i] = param[m];
m++;
recur(i, 0, 1, m);
}
}
void Attractor::recur(unsigned int curr_dimension, unsigned int prev_i, unsigned int n, unsigned int& m, double prev_product) {
double product;
for (unsigned int i = prev_i; i < dim; i++) {
#ifdef HARDDEBUG
for ( unsigned int j = 0; j < n; j++ )
cout << " ";
cout << "Calculation in dimension: " << i << " With m = " << m << " And depth = " << n << endl;
#endif
product = prev_product * point[i];
new_point[curr_dimension] += param[m] * product;
m++;
if (n < orde) {
recur(curr_dimension, i, n+1, m, product);
}
}
}
void Attractor::poly_A() {
switch (dim) {
case 3:
new_point[0] = param[0] + point[1] - point[1]*point[2];
new_point[1] = param[1] + point[2] - point[2]*point[0];
new_point[2] = param[2] + point[0] - point[0]*point[1];
break;
default:
for ( unsigned int i = 0; i < dim; i++ ) {
new_point[i] = param[i] + point[(i+1) % dim] - point[(i+1) % dim]*point[(i+2) % dim];
}
break;
}
}
void Attractor::poly_2() {
exit(2);
}
void Attractor::logistic() {
for ( unsigned int i = 0; i < dim; i++ ) {
new_point[i] = param[i]*point[i]*(1.0 - point[i]);
}
}
void Attractor::plot() {
for ( vector<Projector *>::iterator it = projectors.begin(); it != projectors.end(); it++ ) {
(*it)->plot(point);
}
}
/*
IO & control
*/
void Attractor::output() {
for ( unsigned int i = 0; i < dim; i++ ) {
cout << point[i] << " ";
}
cout << endl;
}

61
Attractor.hpp

@ -0,0 +1,61 @@
#ifndef ATTRACTOR_HPP
#define ATTRACTOR_HPP
#include <vector>
//#include "Vector.hpp"
//#include "Parameters.hpp"
#include "Projector.hpp"
#include "AttractorKernel.hpp"
enum FormulaChoice {
POLY_N, LORENZ, POLY_A, POLY_2, LOGISTIC, UNRAVEL
};
class Projector;
// TODO : Verschillende classas AttractorSystem en Attractor maken?
class Attractor {
public:
unsigned int dim;
unsigned int par;
unsigned int orde;
FormulaChoice formula;
double * param;
double * point;
double * new_point;
vector<Projector *> projectors;
public:
Attractor();
Attractor(unsigned int dimensions, FormulaChoice formula, unsigned int orde = 3);
Attractor(const char* const filename);
void init(unsigned int dimensions, FormulaChoice formula, unsigned int orde);
void init_vector();
void init_param();
void init_range();
// TODO : lyapunov exponent uit rekenen
bool is_chaos();
// TODO : optimaliseren voor lage graads veeltermen
void iterate();
void polynome();
void recur(unsigned int curr_dimension, unsigned int prev_i, unsigned int n, unsigned int& m, double prev_product = 1.0);
void poly_A();
void poly_2();
void logistic();
void plot();
void output();
};
#endif // ATTRACTOR_HPP

33
AttractorKernel.hpp

@ -0,0 +1,33 @@
#ifndef ATTRACTORKERNEL_HPP
#define ATTRACTORKERNEL_HPP
#include <string>
using namespace std;
class AttractorKernel {
public:
// AttractorKernel protocol
// parameters are stored in a array of doubles
// if you want to use other types, use the properties
virtual double& parameter(const unsigned int index) = 0;
virtual double*& parameters() = 0;
// get properties of the attractor
// such as the dimension
// you should delete the void pointer if you used it
virtual void * getProperty(const string identifier) = 0;
virtual void setProperty(const string identifier, const void * value) = 0;
// iterate his formula
// vector pointers will be swapped! so new remains new and old remains old
virtual void iterate() = 0;
// getter functions for teh resulta
virtual double * & vector() = 0;
virtual double * & previousVector() = 0;
};
#endif // ATTRACTORKERNEL_HPP

64
AwesomeAttractor.cbp

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="AwesomeAttractor" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin/Debug/AwesomeAttractor" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Debug/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
<Option output="bin/Release/AwesomeAttractor" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Release/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-Wall" />
</Compiler>
<Linker>
<Add library="libpng" />
</Linker>
<Unit filename="Attractor.cpp" />
<Unit filename="Attractor.hpp" />
<Unit filename="AttractorKernel.hpp" />
<Unit filename="Canvas.cpp" />
<Unit filename="Canvas.hpp" />
<Unit filename="Projector.cpp" />
<Unit filename="Projector.hpp">
<Option compilerVar="CC" />
</Unit>
<Unit filename="attr/test.attr" />
<Unit filename="attr/test2.attr" />
<Unit filename="attr/waardes.txt" />
<Unit filename="kernels/Lorenz.cpp" />
<Unit filename="kernels/Lorenz.hpp" />
<Unit filename="kernels/Unravel.cpp" />
<Unit filename="kernels/Unravel.hpp" />
<Unit filename="main.cpp" />
<Unit filename="myMath.hpp" />
<Unit filename="pngwriter/pngwriter.cc" />
<Unit filename="pngwriter/pngwriter.h" />
<Extensions>
<code_completion />
<envvars />
<debugger />
<lib_finder disable_auto="1" />
</Extensions>
</Project>
</CodeBlocks_project_file>

52
AwesomeAttractor.layout

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_layout_file>
<ActiveTarget name="Release" />
<File name="Attractor.cpp" open="1" top="1" tabpos="3">
<Cursor position="7114" topLine="283" />
</File>
<File name="Attractor.hpp" open="1" top="0" tabpos="4">
<Cursor position="1165" topLine="0" />
</File>
<File name="AttractorKernel.hpp" open="0" top="0" tabpos="1">
<Cursor position="165" topLine="0" />
</File>
<File name="Canvas.cpp" open="0" top="0" tabpos="3">
<Cursor position="4379" topLine="138" />
</File>
<File name="Canvas.hpp" open="0" top="0" tabpos="4">
<Cursor position="463" topLine="12" />
</File>
<File name="Projector.cpp" open="0" top="0" tabpos="7">
<Cursor position="2483" topLine="0" />
</File>
<File name="Projector.hpp" open="0" top="0" tabpos="6">
<Cursor position="876" topLine="3" />
</File>
<File name="attr/test2.attr" open="0" top="0" tabpos="9">
<Cursor position="43" topLine="0" />
</File>
<File name="kernels/Lorenz.cpp" open="1" top="0" tabpos="2">
<Cursor position="541" topLine="4" />
</File>
<File name="kernels/Lorenz.hpp" open="1" top="0" tabpos="1">
<Cursor position="264" topLine="0" />
</File>
<File name="kernels/Unravel.cpp" open="1" top="0" tabpos="6">
<Cursor position="921" topLine="9" />
</File>
<File name="kernels/Unravel.hpp" open="1" top="0" tabpos="5">
<Cursor position="133" topLine="0" />
</File>
<File name="main.cpp" open="0" top="0" tabpos="5">
<Cursor position="350" topLine="3" />
</File>
<File name="myMath.hpp" open="0" top="0" tabpos="8">
<Cursor position="138" topLine="0" />
</File>
<File name="pngwriter/pngwriter.cc" open="0" top="0" tabpos="1">
<Cursor position="21977" topLine="608" />
</File>
<File name="pngwriter/pngwriter.h" open="0" top="0" tabpos="2">
<Cursor position="19671" topLine="696" />
</File>
</CodeBlocks_layout_file>

40
AwesomeAttractor.layout.save

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_layout_file>
<ActiveTarget name="Release" />
<File name="Attractor.cpp" open="1" top="0" tabpos="2">
<Cursor position="1475" topLine="51" />
</File>
<File name="Attractor.hpp" open="1" top="0" tabpos="3">
<Cursor position="65" topLine="14" />
</File>
<File name="Canvas.cpp" open="1" top="0" tabpos="5">
<Cursor position="0" topLine="0" />
</File>
<File name="Canvas.hpp" open="1" top="0" tabpos="4">
<Cursor position="826" topLine="11" />
</File>
<File name="Parameters.cpp" open="0" top="0" tabpos="3">
<Cursor position="2126" topLine="49" />
</File>
<File name="Parameters.hpp" open="0" top="0" tabpos="4">
<Cursor position="464" topLine="0" />
</File>
<File name="Projector.cpp" open="1" top="1" tabpos="9">
<Cursor position="551" topLine="13" />
</File>
<File name="Projector.hpp" open="1" top="0" tabpos="8">
<Cursor position="383" topLine="1" />
</File>
<File name="Vector.cpp" open="1" top="0" tabpos="6">
<Cursor position="1835" topLine="69" />
</File>
<File name="Vector.hpp" open="1" top="0" tabpos="7">
<Cursor position="0" topLine="0" />
</File>
<File name="main.cpp" open="1" top="0" tabpos="1">
<Cursor position="690" topLine="8" />
</File>
<File name="waardes.txt" open="0" top="0" tabpos="8">
<Cursor position="0" topLine="0" />
</File>
</CodeBlocks_layout_file>

238
Canvas.cpp

@ -0,0 +1,238 @@
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdlib>
#include <cassert>
#include <cstdint>
using namespace std;
#include "pngwriter/pngwriter.h"
#include "Canvas.hpp"
Canvas::Canvas(unsigned int width, unsigned int height, unsigned int num_colors):
dim(2), width(width), height(height), num_colors(num_colors), v(0) {
int_array = new unsigned int[width*height*num_colors];
size = new unsigned int[2];
size[0] = width;
size[1] = height;
assert(int_array != NULL);
clear();
#ifdef HARDDEBUG
cout << "New canvas" << endl;
#endif
}
void Canvas::clear() {
for ( unsigned int i = 0; i < width*height*num_colors; i++ ) {
int_array[i] = 0;
}
}
//void Canvas::update_viewwindow() {
//
// //width and height of attractor
// const double dx = xmax - xmin;
// const double dy = ymax - ymin;
//
// //fix aspect ratio
// if ( dx > dy * ((float)width / height) ) {
// const double height2 = dx * ((float)height / width);
// const double middle = 0.5 * (ymax + ymin);
// ymax = middle + 0.5 * height2;
// ymin = middle - 0.5 * height2;
//
// } else {
// const double width2 = dy * ((float)width / height);
// const double middle = 0.5 * (xmax + xmin);
// xmax = middle + 0.5 * width2;
// xmin = middle - 0.5 * width2;
// }
//
// //add a 4% marge
// xmin -= 0.02 * dx;
// xmax += 0.02 * dx;
// ymin -= 0.02 * dy;
// ymax += 0.02 * dy;
//
// //constants for speed
// constant1 = width / (xmax - xmin);
// constant2 = height / (ymax - ymin);
//}
void Canvas::plot(double x, double y) {
// gets x and y coordinate
// ranges [-1, 1] and [-1, 1]
// so how to do the aspect shiz, i don't know
const unsigned int x_int = x*width + width*.5;
const unsigned int y_int = y*width + height*.5;
const unsigned int index = x_int + width * y_int;
if(x_int < width && y_int < height) {
int_array[index]++;
}
}
void Canvas::plot(double x, double y, unsigned int c) {
// same as plot(double x, double y)
// now with color control
const unsigned int x_int = x*width + width*.5;
const unsigned int y_int = y*width + height*.5;
const unsigned int index = x_int + width * y_int + width*height*c;
if(x_int < width && y_int < height) {
int_array[index]++;
}
}
void Canvas::plot(double x, double y, unsigned int c, double intensity) {
// same as plot(double x, double y, unsigned int c)
// but now uses the float array (not yet implemented
}
/*
I/O functions
*/
void Canvas::output() {
cout << "Canvas: " << endl;
cout << "Dimensions: " << width << " x " << height << " x " << num_colors << endl;
}
void Canvas::output_file(const char * filename){
unsigned int * max_int = new unsigned int[num_colors];
double * power = new double[num_colors];
for ( unsigned int i = 0; i < num_colors; i++ ) {
max_int[i] = 0;
double cumulative = 0;
unsigned int n = 0;
for ( unsigned int j = 0; j < width*height; j++) {
if ( max_int[i] < int_array[j+i*width*height] ) {
max_int[i] = int_array[j+i*width*height];
}
if ( int_array[j+i*width*height] ) {
cumulative += int_array[j+i*width*height];
n++;
}
}
if ( n > 100 ) {
const double average = cumulative / (double)n;
power[i] = -2.5/log(average/(double)max_int[i]);
if ( power[i] < 0 )
power[i] = 1;
} else {
power[i] = 1;
}
if ( n <= 10 ) {
cout << "not enough data" << endl;
}
}
const double vibrancy = v;
double averagePower = 0;
for ( unsigned int i = 0; i < num_colors; i++ ) {
averagePower += power[i];
}
averagePower /= num_colors;
for ( unsigned int i = 0; i < num_colors; i++ ) {
power[i] = vibrancy*power[i] + (1.0 - vibrancy*averagePower);
}
pngwriter * pngFile = new pngwriter(width, height, 0.0, filename);
pngFile->setcompressionlevel(9);
pngFile->settext("Attractor", "Joshua Moerman", "A awesome attractor", "AwesomeAttractor");
for ( unsigned int x = 0; x < width; x++ ) {
for ( unsigned int y = 0; y < height; y++ ) {
double r = 0.0;
double g = 0.0;
double b = 0.0;
for ( unsigned int c = 0; c < num_colors; c++ ) {
const double norm_value = (double)int_array[x + y*width + c*width*height]/max_int[c];
switch(c){
case 0: {
r = (pow(norm_value, power[c]))*3.0;
break;
}
case 1: {
g = (pow(norm_value, power[c]))*3.0;
break;
}
case 2: {
b = (pow(norm_value, power[c]))*3.0;
break;
}
default:
break;
}
}
//pngwriter clips values for me
pngFile->plot(x, y, r, g, b);
}
}
delete max_int;
delete power;
pngFile->close();
}
void Canvas::output_file(){
char filename[50];
time_t t = time(0);
struct tm* lt = localtime(&t);
int r = rand() % 10;
sprintf(filename, "render/attractor_%04d-%02d-%02d_%02d-%02d-%02d-%01d.png", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, r);
output_file(filename);
}
void Canvas::output_raw(const char * filename){
ofstream outfile (filename, ofstream::binary);
outfile.write(reinterpret_cast<char*>(int_array), sizeof(unsigned int)*width*height*num_colors);
}
void Canvas::output_raw(){
char filename[52];
time_t t = time(0);
struct tm* lt = localtime(&t);
int r = rand() % 10;
sprintf(filename, "render/canv%dx%d_%04d-%02d-%02d_%02d-%02d-%02d-%01d.canv", width, height, lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, r);
output_raw(filename);
}
void Canvas::input_raw(const char * filename){
ifstream infile(filename, ifstream::binary);
if ( ! infile ) {
cout << "poep" << endl;
return;
}
infile.seekg (0, ios::end);
int length = infile.tellg();
infile.seekg (0, ios::beg);
cout << "length: " << length << " =? " << static_cast<int>(width*height*num_colors*sizeof(unsigned int)) << endl;
infile.read (reinterpret_cast<char*>(int_array), sizeof (unsigned int)*width*height*num_colors);
}

50
Canvas.hpp

@ -0,0 +1,50 @@
#ifndef CANVAS_HPP
#define CANVAS_HPP
//#include "Vector.hpp"
#include "Attractor.hpp"
#include "Projector.hpp"
// TODO : Canvas class abstraheren (zodat er makkelijk verschillende soorten canvae gemaakt kunnen worden)
class Canvas{
friend class Projector;
unsigned int dim;
unsigned int width;
unsigned int height;
unsigned int num_colors;
unsigned int * size;
unsigned int * int_array;
public:
double v;
Canvas(unsigned int width, unsigned int height, unsigned int num_colors = 1);
void clear();
void plot(double x, double y);
void plot(double x, double y, unsigned int c);
// TODO : make double array in canvas (ander soort canvas)
// TODO : subpixel sampling (anders soort canvas)
void plot(double x, double y, unsigned int c, double intensity);
void output();
//void output(Vector& point);
void output_file(const char * filename);
void output_file();
void output_raw(const char * filename);
void output_raw();
void input_raw(const char * filename);
};
#endif // CANVAS_HPP

110
Parameters.cpp

@ -0,0 +1,110 @@
#include <iostream>
using namespace std;
#include "Parameters.hpp"
Parameters::Parameters(unsigned int num_parameters, float default_val):
num_parameters(num_parameters) {
begin = new (nothrow) Vector(num_parameters, default_val);
eind = new (nothrow) Vector(num_parameters, default_val);
interpolated = new (nothrow) Vector(num_parameters, default_val);
// *interpolated = *begin
check_pointers();
#ifdef HARDDEBUG
cout << "New Parameters with one default val:" << endl << *this << endl;
#endif
}
/*Parameters::Parameters(unsigned int num_parameters, float default_val1, float default_val2):
num_parameters(num_parameters) {
begin = new (nothrow) Vector(num_parameters, default_val1);
eind = new (nothrow) Vector(num_parameters, default_val2);
interpolated = new (nothrow) Vector(num_parameters, default_val1);
// *interpolated = *begin
check_pointers();
#ifdef HARDDEBUG
cout << "New Parameters with two default vals:" << endl << *this << endl;
#endif
}*/
Parameters::~Parameters() {
delete begin;
delete eind;
delete interpolated;
#ifdef HARDDEBUG
cout << "Parameters deleted" << endl;
#endif
}
void Parameters::check_pointers() {
assert(begin != NULL);
assert(eind != NULL);
assert(interpolated != NULL);
}
void Parameters::set(unsigned int parameter, float val1, float val2) {
assert(parameter < num_parameters);
begin->coordinates[parameter] = val1;
eind->coordinates[parameter] = val2;
interpolated->coordinates[parameter] = val1;
#ifdef HARDDEBUG
cout << "Parameter " << parameter << " set to: " << val1 << " - " << val2 << endl;
#endif
}
void Parameters::set(unsigned int parameter, float val) {
assert(parameter < num_parameters);
begin->coordinates[parameter] = val;
eind->coordinates[parameter] = val;
interpolated->coordinates[parameter] = val;
#ifdef HARDDEBUG
cout << "Parameter " << parameter << " set to: " << val << endl;
#endif
}
float Parameters::get(unsigned int parameter) {
assert(parameter < num_parameters);
#ifdef HARDDEBUG
cout << "Asked for parameter " << parameter << " with value:" << interpolated->coordinates[parameter] << endl;
#endif
return interpolated->coordinates[parameter];
}
void Parameters::interpolate(float time) {
/*
Dit is mogelijk met vector rekenen:
(*interpolated) = (*begin) * ( 1.0 - time ) + (*eind) * time;
Maar we doen het per element, zodat we simpelere code hebben,
geen vectoren hoeven te returnen en makkelijker kunnen optimaliseren
*/
const float invtime = 1.0 - time;
for ( unsigned int i = 0; i < num_parameters; i++ ) {
interpolated->coordinates[i] = invtime * begin->coordinates[i] + time * eind->coordinates[i];
}
#ifdef HARDDEBUG
cout << "interpolate() result" << endl << *interpolated << endl;
#endif
}
ostream& operator<<(ostream& os, const Parameters& param) {
os << param.num_parameters << endl;
os << "Begin:" << endl << *param.begin << endl;
os << "Eind:" << endl << *param.eind << endl;
os << "Interpolated:" << endl << *param.interpolated << endl;
os <<endl;
return os;
}

34
Parameters.hpp

@ -0,0 +1,34 @@
#ifndef PARAMETER_HPP
#define PARAMETER_HPP
#include "Vector.hpp"
class Parameters {
Vector * begin;
Vector * eind;
Vector * interpolated;
void check_pointers();
public:
// for checks and assertions
unsigned int num_parameters;
Parameters(unsigned int num_parameters, float default_val = 0.0);
//Parameters(unsigned int num_parameters, float default_val1, float default_val2);
~Parameters();
void set(unsigned int parameter, float val1, float val2);
void set(unsigned int parameter, float val);
float get(unsigned int parameter);
void interpolate(float time);
// output operator
friend ostream& operator<<(ostream& os, const Parameters& param);
};
#endif // PARAMETER_HPP

111
Projector.cpp

@ -0,0 +1,111 @@
#include <iostream>
#include <cmath>
#include <vector>
#include <cassert>
using namespace std;
#include "Projector.hpp"
#include "Canvas.hpp"
#include "myMath.hpp"
void Projector::init(double * point) {
init_vector();
project(point);
init_range();
}
void Projector::init_vector() {
project_point = new double[intern_dim];
offset = new double[intern_dim];
assert(project_point != NULL);
assert(offset != NULL);
}
void Projector::init_range() {
range_min = new double[intern_dim];
range_max = new double[intern_dim];
assert(range_min != NULL);
assert(range_max != NULL);
for ( unsigned int i = 0; i < intern_dim; i++ ) {
range_min[i] = range_max[i] = project_point[i];
}
}
void Projector::update_range(double * point) {
project(point);
for ( unsigned int i = 0; i < intern_dim; i++ ) {
if ( project_point[i] < range_min[i] ) {
range_min[i] = project_point[i];
} else if ( project_point[i] > range_max[i] ) {
range_max[i] = project_point[i];
}
}
}
void Projector::finish_range() {
// double max_dist = 0.0;
// for ( unsigned int i = 0; i < intern_dim; i++ ) {
// const double dist = range_max[i] - range_min[i];
// if ( dist > max_dist )
// max_dist = dist;
// }
//
// factor = 0.9/max_dist;
// for ( unsigned int i = 0; i < intern_dim; i++ ) {
// offset[i] = -0.5*factor*(range_min[i] + range_max[i]);
// }
factor = canvas->size[0] / (range_max[0] - range_min[0]);
unsigned int teh_size = canvas->size[0];
for ( unsigned int i = 1; i < intern_dim; i++ ) {
double dist = range_max[i] - range_min[i];
if ( factor * dist > (double)canvas->size[i] ) {
factor = (double)canvas->size[i] / dist;
//teh_size = canvas->size[i];
cout << "crap for dim" << i << endl;
}
}
factor /= (double)teh_size;
for ( unsigned int i = 0; i < intern_dim; i++ ) {
offset[i] = -0.5*factor*(range_min[i] + range_max[i]);
}
}
void Projector::project(double * point) {
assert(extern_dim >= 2);
project_point[0] = point[0];
project_point[1] = point[1];
}
void Projector::plot(double * point) {
project(point);
const double x = project_point[0]*factor + offset[0];
const double y = project_point[1]*factor + offset[1];
//cout << x << ", " << y << endl;
canvas->plot(x, y);
if ( even(point[2]*4.0) )
canvas->plot(x, y, 1);
else
canvas->plot(x, y, 2);
}
void Projector::output(){
cout << "Projector properties: " << endl;
cout << " factor: " << factor << endl;
for ( unsigned int i = 0; i < intern_dim; i++ ) {
cout << " dimension " << i << ": offset: " << offset[i] << ", range: [" << range_min[i] << ", " << range_max[i] << "]" << endl;
}
}

45
Projector.hpp

@ -0,0 +1,45 @@
#ifndef PROJECTOR_HPP
#define PROJECTOR_HPP
#include <vector>
#include "Canvas.hpp"
class Canvas;
class Projector{
public:
unsigned int extern_dim;
unsigned int intern_dim;
Canvas * canvas;
double * project_point;
double * range_min;
double * range_max;
double factor;
double * offset;
void init(double * point);
void init_vector();
void init_range();
void update_range(double * point);
void finish_range();
// TODO : Matrix gebruiken voor lineaire afbeelding
// TODO : Over kleuren nadenken
/*
Kleurmodi:
-genormalizeerde coordinaten als kleurintensiteit (gebruikt fp canvas)
-kleurbanden, dus met een periodieke functie (gebruikt int canvas)
*/
void project(double * point);
void plot(double * point);
void output();
};
#endif // PROJECTOR_HPP

126
Vector.cpp

@ -0,0 +1,126 @@
#include <iostream>
using namespace std;
#include "Vector.hpp"
Vector::Vector():
dimension(0) {
coordinates = new (nothrow) float[0];
assert(coordinates != NULL);
#ifdef HARDDEBUG
cout << "New vector (without elements)" << endl;
#endif
}
Vector::Vector(unsigned int d):
dimension(d) {
coordinates = new (nothrow) float[dimension];
assert(coordinates != NULL);
#ifdef HARDDEBUG
cout << "New vector:" << endl << *this << endl;
#endif
}
Vector::Vector(unsigned int d, float default_val):
dimension(d) {
coordinates = new (nothrow) float[dimension];
assert(coordinates != NULL);
for (unsigned int i = 0; i < dimension; i++) {
coordinates[i] = default_val;
}
#ifdef HARDDEBUG
cout << "New vector with default values:" << endl << *this << endl;
#endif
}
Vector::~Vector() {
delete[] coordinates;
#ifdef HARDDEBUG
cout << "coordinates deleted" << endl;
#endif
}
Vector& Vector::operator=(const Vector& a) {
if ( dimension != a.dimension ) {
dimension = a.dimension;
delete[] coordinates;
coordinates = new float[dimension];
#ifdef HARDDEBUG
cout << "Dimensions were not equal, made new vector" << endl;
#endif
}
for ( unsigned int i = 0; i < dimension; i++ ) {
coordinates[i] = a.coordinates[i];
}
#ifdef HARDDEBUG
cout << "operator= result" << endl << *this << endl;
#endif
return *this;
}
ostream& operator<<(ostream& os, const Vector& a) {
os << a.dimension << endl;
for ( unsigned int i = 0; i < a.dimension; i++ ) {
os << a.coordinates[i] << " ";
}
os << endl;
return os;
}
float& Vector::operator[](const unsigned int index) {
assert(index < dimension);
return coordinates[index];
}
// matig werkende optelling en scalaire vermenigvuldiging van vectoren
/*
Vector Vector::operator+(const Vector a) const {
if ( dimension != a.dimension ) {
cout << "WARNING: dimensions not equal in vector addition" << endl;
exit(1);
} else {
static Vector ret(dimension);
for ( unsigned int i = 0; i < dimension; i++ ) {
ret.coordinates[i] = coordinates[i] + a.coordinates[i];
}
#ifdef HARDDEBUG
cout << "operator+ result" << endl << ret << endl;
#endif
return ret;
}
}
Vector Vector::operator*(const float a) const {
static Vector ret(dimension);
for ( unsigned int i = 0; i < dimension; i++ ) {
ret.coordinates[i] = coordinates[i] * a;
}
#ifdef HARDDEBUG
cout << "operator* result" << endl << ret << endl;
#endif
return ret;
}
*/

31
Vector.hpp

@ -0,0 +1,31 @@
#ifndef VECTOR_HPP
#define VECTOR_HPP
class Vector {
public:
unsigned int dimension;
float * coordinates;
// const, dest
Vector();
Vector(unsigned int d);
Vector(unsigned int d, float default_val);
~Vector();
// output operator
friend ostream& operator<<(ostream& os, const Vector& a);
// easy access
float& Vector::operator[](const unsigned int index);
// vector rekenen
Vector& operator=(const Vector& a);
// faaloperatoren
// Vector operator+(const Vector a) const;
// Vector operator*(const float a) const;
};
#endif // VECTOR_HPP

14
batchexport.sh

@ -0,0 +1,14 @@
CANVEXT=".canv"
PNGEXT=".png"
CANVPATH="canvas/"
PNGPATH="render/"
cd $CANVPATH
for FILE in `ls *$CANVEXT | sed -e 's/'$CANVEXT'$//'`
do
../bin/Debug/AwesomeAttractor -c ../$CANVPATH$FILE$CANVEXT 3200 3200 3 ../$PNGPATH$FILE$PNGEXT
#Do other stuff with file
done

17
batchrender.sh

@ -0,0 +1,17 @@
ATTREXT=".attr"
CANVEXT=".canv"
ATTRPATH="attr/"
CANVPATH="canvas/"
cd $ATTRPATH
for FILE in `ls *$ATTREXT | sed -e 's/'$ATTREXT'$//'`
do
../bin/Debug/AwesomeAttractor -a ../$ATTRPATH$FILE$ATTREXT 500 ../$CANVPATH$FILE$CANVEXT 800 600 3
#Do other stuff with file
done
cd ..
zip -r canvae.zip $CANVPATH*$CANVEXT

73
kernels/Lorenz.cpp

@ -0,0 +1,73 @@
#include "Lorenz.hpp"
Lorenz::Lorenz() {
init();
}
void Lorenz::init() {
// allocation
myParameters = new double[4];
vectorNew = new double[3];
vectorOld = new double[3];
// initialisation
assert(myParameters != NULL);
assert(vectorNew != NULL);
assert(vectorOld != NULL);
for ( unsigned int i = 0; i < 4; i++ ) {
myParameters[i] = 0.0;
}
for ( unsigned int i = 0; i < 3; i++ ) {
vectorNew[i] = vectorOld[i] = 1.0;
}
}
// the main function
void Lorenz::iterate() {
swap(vectorNew, vectorOld);
vectorNew[0] = vectorOld[0] + myParameters[0] * myParameters[1] * (vectorOld[1] - vectorOld[0]);
vectorNew[1] = vectorOld[1] + myParameters[0] * (vectorOld[0] * (myParameters[2] - vectorOld[2]) - vectorOld[1]);
vectorNew[2] = vectorOld[2] + myParameters[0] * (vectorOld[0] * vectorOld[1] - myParameters[3] * vectorOld[2]);
}
/*
4D:
new_point[0] = point[0] + param[0] * param[1] * (point[1] - point[0]);
new_point[1] = point[1] + param[0] * (point[0] * (param[2] - point[2]) - point[1] + point[3]);
new_point[2] = point[2] + param[0] * (point[0] * point[1] - param[3] * point[2]);
new_point[3] = point[3] - param[0] * param[4] * point[0];
break;
*/
// setters, getters, all i/o to other classes/objects
void * Lorenz::getProperty(const string identifier) {
if ( identifier == "dimension" ) {
unsigned int * _return = new unsigned int;
*_return = 3;
return _return;
}
return NULL;
}
void Lorenz::setProperty(const string identifier, const void * value) {
return;
}
double * & Lorenz::parameters() {
return myParameters;
}
double & Lorenz::parameter(const unsigned int index) {
return myParameters[index];
}
double * & Lorenz::vector() {
return vectorNew;
}
double * & Lorenz::previousVector() {
return vectorOld;
}

48
kernels/Lorenz.hpp

@ -0,0 +1,48 @@
#ifndef LORENZ_HPP
#define LORENZ_HPP
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <string>
using namespace std;
#include "../AttractorKernel.hpp"
class Lorenz : public AttractorKernel {
double * myParameters;
double * vectorNew;
double * vectorOld;
void init();
public:
Lorenz();
Lorenz(const unsigned int dimensions);
// parameters are stored in a array of doubles
// if you want to use other types, use the properties
virtual double& parameter(const unsigned int index);
virtual double*& parameters();
// get properties of the attractor
// such as the dimension
// you should delete the void pointer if you used it
virtual void * getProperty(const string identifier);
virtual void setProperty(const string identifier, const void * value);
// iterate his formula
// vector pointers will be swapped! so new remains new and old remains old
virtual void iterate();
// getter functions for teh resulta
virtual double * & vector();
virtual double * & previousVector();
};
#endif // LORENZ_HPP

71
kernels/Unravel.cpp

@ -0,0 +1,71 @@
#include "Unravel.hpp"
Unravel::Unravel(){
init();
}
void Unravel::init() {
// allocation
myParameters = new double[4];
vectorNew = new double[3];
vectorOld = new double[3];
// initialisation
assert(myParameters != NULL);
assert(vectorNew != NULL);
assert(vectorOld != NULL);
for ( unsigned int i = 0; i < 7; i++ ) {
myParameters[i] = 0.0;
}
for ( unsigned int i = 0; i < 3; i++ ) {
vectorNew[i] = vectorOld[i] = 0.0;
}
}
void Unravel::iterate() {
swap(vectorNew, vectorOld);
vectorNew[0] = myParameters[0]*(vectorOld[2] + myParameters[1]);
vectorNew[1] = myParameters[2]*(vectorOld[0] + myParameters[3]);
vectorNew[2] = myParameters[4]*(vectorOld[1] + myParameters[5]);
const double dist = vectorNew[0]*vectorNew[0] + vectorNew[1]*vectorNew[1] + vectorNew[2]*vectorNew[2];
if ( dist > myParameters[6]*myParameters[6] ) {
const double sqrtDist = sqrt(dist);
const double p = 1.0 - myParameters[6] * ( static_cast<int> ( sqrtDist / myParameters[6] ) + 1.0 ) / sqrtDist;
vectorNew[0] *= p;
vectorNew[1] *= p;
vectorNew[2] *= p;
}
}
// setters, getters, all i/o to other classes/objects
void * Unravel::getProperty(const string identifier) {
if ( identifier == "dimension" ) {
unsigned int * _return = new unsigned int;
*_return = 3;
return _return;
}
return NULL;
}
void Unravel::setProperty(const string identifier, const void * value) {
return;
}
double * & Unravel::parameters() {
return myParameters;
}
double & Unravel::parameter(const unsigned int index) {
return myParameters[index];
}
double * & Unravel::vector() {
return vectorNew;
}
double * & Unravel::previousVector() {
return vectorOld;
}

49
kernels/Unravel.hpp

@ -0,0 +1,49 @@
#ifndef UNRAVEL_HPP
#define UNRAVEL_HPP
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <string>
#include <cmath>
using namespace std;
#include "../AttractorKernel.hpp"
class Unravel : public AttractorKernel {
double * myParameters;
double * vectorNew;
double * vectorOld;
void init();
public:
Unravel();
Unravel(const unsigned int dimensions);
// parameters are stored in a array of doubles
// if you want to use other types, use the properties
virtual double& parameter(const unsigned int index);
virtual double*& parameters();
// get properties of the attractor
// such as the dimension
// you should delete the void pointer if you used it
virtual void * getProperty(const string identifier);
virtual void setProperty(const string identifier, const void * value);
// iterate his formula
// vector pointers will be swapped! so new remains new and old remains old
virtual void iterate();
// getter functions for teh resulta
virtual double * & vector();
virtual double * & previousVector();
};
#endif // UNRAVEL_HPP

133
main.cpp

@ -0,0 +1,133 @@
#include <iostream>
using namespace std;
#include "Attractor.hpp"
#include "Canvas.hpp"
#include "Projector.hpp"
#include "AttractorKernel.hpp"
#include "kernels/Lorenz.hpp"
// TODO : Allemaal files inlezen, voor makkelijker gebruik
int main(int argc, char *argv[]) {
AttractorKernel * mijnAttractortje;
mijnAttractortje = new Lorenz();
AttractorKernel &attractor = *mijnAttractortje;
attractor.parameter(0) = 1.0;
attractor.parameter(2) = 2.0;
double * & vector = attractor.vector();
unsigned int * _dimension = (unsigned int*)mijnAttractortje->getProperty("dimension");
unsigned int dimension = *_dimension;
delete _dimension;
cout << "Dimension = " << dimension << endl;
for ( unsigned int i = 0; i < 20; i++ ) {
mijnAttractortje->iterate();
cout << "vector = ";
for ( unsigned int i = 0; i < dimension; i++ ) {
cout << " " << vector[i];
}
cout << endl;
}
/*if ( argc <= 2 ) {
cout << endl << "nothing to do..." << endl;
cout << "usage:" << endl;
cout << " rendering to canvas: -a my_attractor.attr 500000000 my_attractor.canv 800 600 3" << endl;
cout << " canvas to png: -c my_attractor.canv 800 600 3 my_atttractor.png" << endl << endl;
exit(0);
}
int mode;
string argv1 = argv[1];
if ( argv1 == "-a" ) {
cout << "rendermode" << endl;
mode = 1;
} else if ( argv1 == "-c" ) {
cout << "canvasmode" << endl;
mode = 2;
} else {
cout << "i do.. i do... i do not understand... \"" << argv1 << "\""<< endl;
exit(0);
}
switch ( mode ) {
case 1: {
if ( argc != 8 ) {
cout << "all parameters must be set..." << endl;
exit(0);
}
string attractorFile = argv[2];
unsigned int iterations = atoi(argv[3]);
string canvasFile = argv[4];
unsigned int width = atoi(argv[5]);
unsigned int height = atoi(argv[6]);
unsigned int numColors = atoi(argv[7]);
Attractor attract(attractorFile.c_str());
cout << attractorFile << " is read" << endl;
Projector projection;
Canvas canvas(width, height, numColors);
projection.canvas = &canvas;
attract.projectors.push_back(&projection);
attract.init_range();
projection.output();
for ( unsigned int j = 1; j <= 100; j++ ) {
for ( unsigned int i = 0; 100*i <= iterations; i++ ) {
attract.iterate();
attract.plot();
}
cout << j << "% done" << endl;
}
canvas.output_raw(canvasFile.c_str());
cout << canvasFile << " is outputted" << endl;
break;
}
case 2: {
if ( argc != 7 ) {
cout << "all parameters must be set..." << endl;
exit(0);
}
string canvasFile = argv[2];
unsigned int width = atoi(argv[3]);
unsigned int height = atoi(argv[4]);
unsigned int numColors = atoi(argv[5]);
string pngFile = argv[6];
Canvas canvas(width, height, numColors);
canvas.input_raw(canvasFile.c_str());
cout << canvasFile << " is read" << endl;
for ( double v = -1.5; v < 1.6; v += 1.5 ) {
canvas.v = v;
canvas.output_file();
}
cout << pngFile << " was exported" << endl;
break;
}
default: {
cout << "WTF" << endl;
break;
}
}*/
return 0;
}

12
myMath.hpp

@ -0,0 +1,12 @@
#ifndef MYMATH_HPP
#define MYMATH_HPP
#include <cmath>
using namespace std;
bool even(double x) {
return (((int)floor(x)) % 2 == 0);
}
#endif // MYMATH_HPP

BIN
pngwriter/.DS_Store

Binary file not shown.

BIN
pngwriter/._.DS_Store

Binary file not shown.

BIN
pngwriter/._Makefile

Binary file not shown.

BIN
pngwriter/._pngwriter.cc

Binary file not shown.

BIN
pngwriter/._pngwriter.h

Binary file not shown.

32
pngwriter/Makefile

@ -0,0 +1,32 @@
############ PARTIAL MAKEFILE FOR PNGWRITER ######################################
#
# Website: Main: http://pngwriter.sourceforge.net/
# Author: Paul Blackburn
# Email: individual61@users.sourceforge.net
# Version: 0.5.4 (19 / II / 2009)
# License: GNU General Public License
# Copyright 2002, 2003, 2004, 2005, 2006, 2007,
# 2008, 2009 Paul Blackburn
#
##################################################################################
include ../make.include
OBJECTS=pngwriter.o
all: libpngwriter.a
libpngwriter.a: $(OBJECTS)
ar rv $@ $^
ranlib $@
pngwriter.o: pngwriter.cc pngwriter.h
$(CXX) $(CXXFLAGS) $(INC) -g -c -o pngwriter.o pngwriter.cc
clean :
rm -f $(OBJECTS) libpngwriter.a pngtest.cc~ pngwriter.cc~
rm -f pngwriter.h~ Makefile~
rm -f .DS_Store

4721
pngwriter/pngwriter.cc

File diff suppressed because it is too large

747
pngwriter/pngwriter.h

@ -0,0 +1,747 @@
//********** pngwriter.h **********************************************
// Author: Paul Blackburn
//
// Email: individual61@users.sourceforge.net
//
// Version: 0.5.4 (19 / II / 2009)
//
// Description: Library that allows plotting a 48 bit
// PNG image pixel by pixel, which can
// then be opened with a graphics program.
//
// License: GNU General Public License
// Copyright 2002, 2003, 2004, 2005, 2006, 2007,
// 2008, 2009 Paul Blackburn
//
// Website: Main: http://pngwriter.sourceforge.net/
// Sourceforge.net: http://sourceforge.net/projects/pngwriter/
// Freshmeat.net: http://freshmeat.net/projects/pngwriter/
//
// Documentation: This header file is commented, but for a
// quick reference document, and support,
// take a look at the website.
//
//*************************************************************************
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* */
#ifndef PNGWRITER_H
#define PNGWRITER_H 1
#define PNGWRITER_VERSION 0.54
#define NO_FREETYPE
#include <png.h>
// REMEMBER TO ADD -DNO_FREETYPE TO YOUR COMPILATION FLAGS IF PNGwriter WAS
// COMPILED WITHOUT FREETYPE SUPPORT!!!
//
// RECUERDA AGREGAR -DNO_FREETYPE A TUS OPCIONES DE COMPILACION SI PNGwriter
// FUE COMPILADO SIN SOPORTE PARA FREETYPE!!!
//
#ifndef NO_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
#endif
#ifdef OLD_CPP // For compatibility with older compilers.
#include <iostream.h>
#include <math.h>
#include <wchar.h>
#include <string.h>
using namespace std;
#endif // from ifdef OLD_CPP
#ifndef OLD_CPP // Default situation.
#include <iostream>
#include <cmath>
#include <cwchar>
#include <string>
#endif // from ifndef OLD_CPP
//png.h must be included before FreeType headers.
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#define PNG_BYTES_TO_CHECK (4)
#define PNGWRITER_DEFAULT_COMPRESSION (6)
class pngwriter
{
private:
char * filename_;
char * textauthor_;
char * textdescription_;
char * texttitle_;
char * textsoftware_;
int height_;
int width_;
int backgroundcolour_;
int bit_depth_;
int rowbytes_;
int colortype_;
int compressionlevel_;
bool transformation_; // Required by Mikkel's patch
unsigned char * * graph_;
double filegamma_;
double screengamma_;
void circle_aux(int xcentre, int ycentre, int x, int y, int red, int green, int blue);
void circle_aux_blend(int xcentre, int ycentre, int x, int y, double opacity, int red, int green, int blue);
int check_if_png(char *file_name, FILE **fp);
int read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr);
int read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr,
png_bytepp *image, png_uint_32 *width, png_uint_32 *height);
void flood_fill_internal( int xstart, int ystart, double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue);
void flood_fill_internal_blend( int xstart, int ystart, double opacity, double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue);
#ifndef NO_FREETYPE
void my_draw_bitmap( FT_Bitmap * bitmap, int x, int y, double red, double green, double blue);
void my_draw_bitmap_blend( FT_Bitmap * bitmap, int x, int y,double opacity, double red, double green, double blue);
#endif
/* The algorithms HSVtoRGB and RGBtoHSV were found at http://www.cs.rit.edu/~ncs/
* which is a page that belongs to Nan C. Schaller, though
* these algorithms appear to be the work of Eugene Vishnevsky.
* */
void HSVtoRGB( double *r, double *g, double *b, double h, double s, double v );
void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v );
/* drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
* ( <gurkan@linuks.mine.nu>, http://www.linuks.mine.nu/ )
* */
void drawtop(long x1,long y1,long x2,long y2,long x3, int red, int green, int blue);
void drawbottom(long x1,long y1,long x2,long x3,long y3, int red, int green, int blue);
void drawbottom_blend(long x1,long y1,long x2,long x3,long y3, double opacity, int red, int green, int blue);
void drawtop_blend(long x1,long y1,long x2,long y2,long x3, double opacity, int red, int green, int blue);
public:
/* General Notes
* It is important to remember that all functions that accept an argument of type "const char *" will also
* accept "char *", this is done so you can have a changing filename (to make many PNG images in series
* with a different name, for example), and to allow you to use string type objects which can be easily
* turned into const char * (if theString is an object of type string, then it can be used as a const char *
* by saying theString.c_str()).
* It is also important to remember that whenever a function has a colour coeffiecient as its argument,
* that argument can be either an int from 0 to 65535 or a double from 0.0 to 1.0.
* It is important to make sure that you are calling the function with the type that you want.
* Remember that 1 is an int, while 1.0 is a double, and will thus determine what version of the function
* will be used. Similarly, do not make the mistake of calling for example plot(x, y, 0.0, 0.0, 65535),
* because
* there is no plot(int, int, double, double, int).
* Also, please note that plot() and read() (and the functions that use them internally)
* are protected against entering, for example, a colour coefficient that is over 65535
* or over 1.0. Similarly, they are protected against negative coefficients. read() will return 0
* when called outside the image range. This is actually useful as zero-padding should you need it.
* */
/* Compilation
* A typical compilation would look like this:
*
* g++ my_program.cc -o my_program freetype-config --cflags \
* -I/usr/local/include -L/usr/local/lib -lpng -lpngwriter -lz -lfreetype
*
* If you did not compile PNGwriter with FreeType support, then remove the
* FreeType-related flags and add -DNO_FREETYPE above.
* */
/* Constructor
* The constructor requires the width and the height of the image, the background colour for the
* image and the filename of the file (a pointer or simple "myfile.png"). The background colour
* can only be initialized to a shade of grey (once the object has been created you can do whatever
* you want, though), because generally one wants either a white (65535 or 1.0) or a black (0 or 0.0)
* background to start with.
* The default constructor creates a PNGwriter instance that is 250x250, white background,
* and filename "out.png".
* Tip: The filename can be given as easily as:
* pngwriter mypng(300, 300, 0.0, "myfile.png");
* Tip: If you are going to create a PNGwriter instance for reading in a file that already exists,
* then width and height can be 1 pixel, and the size will be automatically adjusted once you use
* readfromfile().
* */
pngwriter();
pngwriter(const pngwriter &rhs);
pngwriter(int width, int height, int backgroundcolour, char * filename);
pngwriter(int width, int height, double backgroundcolour, char * filename);
pngwriter(int width, int height, int backgroundcolour, const char * filename);
pngwriter(int width, int height, double backgroundcolour, const char * filename);
/* Destructor
* */
~pngwriter();
/* Assignment Operator
* */
pngwriter & operator = (const pngwriter & rhs);
/* Plot
* With this function a pixel at coordinates (x, y) can be set to the desired colour.
* The pixels are numbered starting from (1, 1) and go to (width, height).
* As with most functions in PNGwriter, it has been overloaded to accept either int arguments
* for the colour coefficients, or those of type double. If they are of type int,
* they go from 0 to 65535. If they are of type double, they go from 0.0 to 1.0.
* Tip: To plot using red, then specify plot(x, y, 1.0, 0.0, 0.0). To make pink,
* just add a constant value to all three coefficients, like this:
* plot(x, y, 1.0, 0.4, 0.4).
* Tip: If nothing is being plotted to your PNG file, make sure that you remember
* to close() the instance before your program is finished, and that the x and y position
* is actually within the bounds of your image. If either is not, then PNGwriter will
* not complain-- it is up to you to check for this!
* Tip: If you try to plot with a colour coefficient out of range, a maximum or minimum
* coefficient will be assumed, according to the given coefficient. For example, attempting
* to plot plot(x, y, 1.0,-0.2,3.7) will set the green coefficient to 0 and the red coefficient
* to 1.0.
* */
void plot(int x, int y, int red, int green, int blue);
void plot(int x, int y, double red, double green, double blue);
/* Plot HSV
* With this function a pixel at coordinates (x, y) can be set to the desired colour,
* but with the colour coefficients given in the Hue, Saturation, Value colourspace.
* This has the advantage that one can determine the colour that will be plotted with
* only one parameter, the Hue. The colour coefficients must go from 0 to 65535 and
* be of type int, or be of type double and go from 0.0 to 1.0.
* */
void plotHSV(int x, int y, double hue, double saturation, double value);
void plotHSV(int x, int y, int hue, int saturation, int value);
/* Read
* With this function we find out what colour the pixel (x, y) is. If "colour" is 1,
* it will return the red coefficient, if it is set to 2, the green one, and if
* it set to 3, the blue colour coefficient will be returned,
* and this returned value will be of type int and be between 0 and 65535.
* Note that if you call read() on a pixel outside the image range, the value returned
* will be 0.
* */
int read(int x, int y, int colour);
/* Read, Average
* Same as the above, only that the average of the three colour coefficients is returned.
*/
int read(int x, int y);
/* dRead
* With this function we find out what colour the pixel (x, y) is. If "colour" is 1,
* it will return the red coefficient, if it is set to 2, the green one, and if
* it set to 3, the blue colour coefficient will be returned,
* and this returned value will be of type double and be between 0.0 and 1.0.
* Note that if you call dread() outside the image range, the value returned will be 0.0
* */
double dread(int x, int y, int colour);
/* dRead, Average
* Same as the above, only that the average of the three colour coefficients is returned.
*/
double dread(int x, int y);
/* Read HSV
* With this function we find out what colour the pixel (x, y) is, but in the Hue,
* Saturation, Value colourspace. If "colour" is 1,
* it will return the Hue coefficient, if it is set to 2, the Saturation one, and if
* it set to 3, the Value colour coefficient will be returned, and this returned
* value will be of type int and be between 0 and 65535. Important: If you attempt
* to read the Hue of a pixel that is a shade of grey, the value returned will be
* nonsensical or even NaN. This is just the way the RGB -> HSV algorithm works:
* the Hue of grey is not defined. You might want to check whether the pixel
* you are reading is grey before attempting a readHSV().
* Tip: This is especially useful for categorizing sections of the image according
* to their colour.
* */
int readHSV(int x, int y, int colour);
/* dRead HSV
* With this function we find out what colour the pixel (x, y) is, but in the Hue,
* Saturation, Value colourspace. If "colour" is 1,
* it will return the Hue coefficient, if it is set to 2, the Saturation one, and if
* it set to 3, the Value colour coefficient will be returned,
* and this returned value will be of type double and be between 0.0 and 1.0.
* */
double dreadHSV(int x, int y, int colour);
/* Clear
* The whole image is set to black.
* */
void clear(void);
/* Close
* Close the instance of the class, and write the image to disk.
* Tip: If you do not call this function before your program ends, no image
* will be written to disk.
* */
void close(void);
/* Rename
* To rename the file once an instance of pngwriter has been created.
* Useful for assigning names to files based upon their content.
* Tip: This is as easy as calling pngwriter_rename("newname.png")
* If the argument is a long unsigned int, for example 77, the filename will be changed to
* 0000000077.png
* Tip: Use this to create sequences of images for movie generation.
* */
void pngwriter_rename(char * newname);
void pngwriter_rename(const char * newname);
void pngwriter_rename(long unsigned int index);
/* Figures
* These functions draw basic shapes. Available in both int and double versions.
* The line functions use the fast Bresenham algorithm. Despite the name,
* the square functions draw rectangles. The circle functions use a fast
* integer math algorithm. The filled circle functions make use of sqrt().
* */
void line(int xfrom, int yfrom, int xto, int yto, int red, int green,int blue);
void line(int xfrom, int yfrom, int xto, int yto, double red, double green,double blue);
void triangle(int x1, int y1, int x2, int y2, int x3, int y3, int red, int green, int blue);
void triangle(int x1, int y1, int x2, int y2, int x3, int y3, double red, double green, double blue);
void square(int xfrom, int yfrom, int xto, int yto, int red, int green,int blue);
void square(int xfrom, int yfrom, int xto, int yto, double red, double green,double blue);
void filledsquare(int xfrom, int yfrom, int xto, int yto, int red, int green,int blue);
void filledsquare(int xfrom, int yfrom, int xto, int yto, double red, double green,double blue);
void circle(int xcentre, int ycentre, int radius, int red, int green, int blue);
void circle(int xcentre, int ycentre, int radius, double red, double green, double blue);
void filledcircle(int xcentre, int ycentre, int radius, int red, int green, int blue);
void filledcircle(int xcentre, int ycentre, int radius, double red, double green, double blue);
/* Read From File
* Open the existing PNG image, and copy it into this instance of the class. It is important to mention
* that PNG variants are supported. Very generally speaking, most PNG files can now be read (as of version 0.5.4),
* but if they have an alpha channel it will be completely stripped. If the PNG file uses GIF-style transparency
* (where one colour is chosen to be transparent), PNGwriter will not read the image properly, but will not
* complain. Also, if any ancillary chunks are included in the PNG file (chroma, filter, etc.), it will render
* with a slightly different tonality. For the vast majority of PNGs, this should not be an issue. Note:
* If you read an 8-bit PNG, the internal representation of that instance of PNGwriter will be 8-bit (PNG
* files of less than 8 bits will be upscaled to 8 bits). To convert it to 16-bit, just loop over all pixels,
* reading them into a new instance of PNGwriter. New instances of PNGwriter are 16-bit by default.
* */
void readfromfile(char * name);
void readfromfile(const char * name);
/* Get Height
* When you open a PNG with readfromfile() you can find out its height with this function.
* */
int getheight(void);
/* Get Width
* When you open a PNG with readfromfile() you can find out its width with this function.
* */
int getwidth(void);
/* Set Compression Level
* Set the compression level that will be used for the image. -1 is to use the default,
* 0 is none, 9 is best compression.
* Remember that this will affect how long it will take to close() the image. A value of 2 or 3
* is good enough for regular use, but for storage or transmission you might want to take the time
* to set it at 9.
* */
void setcompressionlevel(int level);
/* Get Bit Depth
* When you open a PNG with readfromfile() you can find out its bit depth with this function.
* Mostly for troubleshooting uses.
* */
int getbitdepth(void);
/* Get Colour Type
* When you open a PNG with readfromfile() you can find out its colour type (libpng categorizes
* different styles of image data with this number).
* Mostly for troubleshooting uses.
* */
int getcolortype(void);
/* Set Gamma Coeff
* Set the image's gamma (file gamma) coefficient. This is experimental, but use it if your image's colours seem too bright
* or too dark. The default value of 0.5 should be fine. The standard disclaimer about Mac and PC gamma
* settings applies.
* */
void setgamma(double gamma);
/* Get Gamma Coeff
* Get the image's gamma coefficient. This is experimental.
* */
double getgamma(void);
/* Bezier Curve
* (After Frenchman Pierre BŽzier from Regie Renault)
* A collection of formulae for describing curved lines
* and surfaces, first used in 1972 to model automobile surfaces.
* (from the The Free On-line Dictionary of Computing)
* See http://www.moshplant.com/direct-or/bezier/ for one of many
* available descriptions of bezier curves.
* There are four points used to define the curve: the two endpoints
* of the curve are called the anchor points, while the other points,
* which define the actual curvature, are called handles or control points.
* Moving the handles lets you modify the shape of the curve.
* */
void bezier( int startPtX, int startPtY,
int startControlX, int startControlY,
int endPtX, int endPtY,
int endControlX, int endControlY,
double red, double green, double blue);
void bezier( int startPtX, int startPtY,
int startControlX, int startControlY,
int endPtX, int endPtY,
int endControlX, int endControlY,
int red, int green, int blue);
/* Set Text
* Sets the text information in the PNG header. If it is not called, the default is used.
*/
void settext(char * title, char * author, char * description, char * software);
void settext(const char * title, const char * author, const char * description, const char * software);
/* Version Number
* Returns the PNGwriter version number.
*/
static double version(void);
/* Write PNG
* Writes the PNG image to disk. You can still change the PNGwriter instance after this.
* Tip: This is exactly the same as close(), but easier to remember.
* Tip: To make a sequence of images using only one instance of PNGwriter, alter the image, change its name,
* write_png(), then alter the image, change its name, write_png(), etc.
*/
void write_png(void);
/* Plot Text
* Uses the Freetype2 library to set text in the image. face_path is the file path to a
* TrueType font file (.ttf) (FreeType2 can also handle other types). fontsize specifices the approximate
* height of the rendered font in pixels. x_start and y_start specify the placement of the
* lower, left corner of the text string. angle is the text angle in radians. text is the text to be rendered.
* The colour coordinates can be doubles from 0.0 to 1.0 or ints from 0 to 65535.
* Tip: PNGwriter installs a few fonts in /usr/local/share/pngwriter/fonts to get you started.
* Tip: Remember to add -DNO_FREETYPE to your compilation flags if PNGwriter was compiled without FreeType support.
* */
void plot_text(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue);
void plot_text(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue);
/* Plot UTF-8 Text
* Same as the above, but the text to be plotted is encoded in UTF-8. Why would you want this? To be able to plot
* all characters available in a large TrueType font, for example: for rendering Japenese, Chinese and other
* languages not restricted to the standard 128 character ASCII space.
* Tip: The quickest way to get a string into UTF-8 is to write it in an adequate text editor, and save it as a file
* in UTF-8 encoding, which can then be read in in binary mode.
* */
void plot_text_utf8(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue);
void plot_text_utf8(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue);
/* Bilinear Interpolation of Image
* Given a floating point coordinate (x from 0.0 to width, y from 0.0 to height),
* this function will return the interpolated colour intensity specified by
* colour (where red = 1, green = 2, blue = 3).
* bilinear_interpolate_read() returns an int from 0 to 65535, and
* bilinear_interpolate_dread() returns a double from 0.0 to 1.0.
* Tip: Especially useful for enlarging an image.
* */
int bilinear_interpolation_read(double x, double y, int colour);
double bilinear_interpolation_dread(double x, double y, int colour);
/* Plot Blend
* Plots the colour given by red, green blue, but blended with the existing pixel
* value at that position. opacity is a double that goes from 0.0 to 1.0.
* 0.0 will not change the pixel at all, and 1.0 will plot the given colour.
* Anything in between will be a blend of both pixel levels. Please note: This is neither
* alpha channel nor PNG transparency chunk support. This merely blends the plotted pixels.
* */
void plot_blend(int x, int y, double opacity, int red, int green, int blue);
void plot_blend(int x, int y, double opacity, double red, double green, double blue);
/* Invert
* Inverts the image in RGB colourspace.
* */
void invert(void);
/* Resize Image
* Resizes the PNGwriter instance. Note: All image data is set to black (this is
* a resizing, not a scaling, of the image).
* */
void resize(int width, int height);
/* Boundary Fill
* All pixels adjacent to the start pixel will be filled with the fill colour, until the boundary colour is encountered.
* For example, calling boundary_fill() with the boundary colour set to red, on a pixel somewhere inside a red circle,
* will fill the entire circle with the desired fill colour. If, on the other hand, the circle is not the boundary colour,
* the rest of the image will be filled.
* The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
* */
void boundary_fill(int xstart, int ystart, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue) ;
void boundary_fill(int xstart, int ystart, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) ;
/* Flood Fill
* All pixels adjacent to the start pixel will be filled with the fill colour, if they are the same colour as the
* start pixel. For example, calling flood_fill() somewhere in the interior of a solid blue rectangle will colour
* the entire rectangle the fill colour. The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
* */
void flood_fill(int xstart, int ystart, double fill_red, double fill_green, double fill_blue) ;
void flood_fill(int xstart, int ystart, int fill_red, int fill_green, int fill_blue) ;
/* Polygon
* This function takes an array of integer values containing the coordinates of the vertexes of a polygon.
* Note that if you want a closed polygon, you must repeat the first point's coordinates for the last point.
* It also requires the number of points contained in the array. For example, if you wish to plot a triangle,
* the array will contain 6 elements, and the number of points is 3. Be very careful about this; if you specify the wrong number
* of points, your program will either segfault or produce points at nonsensical coordinates.
* The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
* */
void polygon(int * points, int number_of_points, double red, double green, double blue);
void polygon(int * points, int number_of_points, int red, int green, int blue);
/* Plot CMYK
* Plot a point in the Cyan, Magenta, Yellow, Black colourspace. Please note that this colourspace is
* lossy, i.e. it cannot reproduce all colours on screen that RGB can. The difference, however, is
* barely noticeable. The algorithm used is a standard one. The colour components are either
* doubles from 0.0 to 1.0 or ints from 0 to 65535.
* */
void plotCMYK(int x, int y, double cyan, double magenta, double yellow, double black);
void plotCMYK(int x, int y, int cyan, int magenta, int yellow, int black);
/* Read CMYK, Double version
* Get a pixel in the Cyan, Magenta, Yellow, Black colourspace. if 'colour' is 1, the Cyan component will be returned
* as a double from 0.0 to 1.0. If 'colour is 2, the Magenta colour component will be returned, and so on, up to 4.
* */
double dreadCMYK(int x, int y, int colour);
/* Read CMYK
* Same as the above, but the colour components returned are an int from 0 to 65535.
* */
int readCMYK(int x, int y, int colour);
/* Scale Proportional
* Scale the image using bilinear interpolation. If k is greater than 1.0, the image will be enlarged.
* If k is less than 1.0, the image will be shrunk. Negative or null values of k are not allowed.
* The image will be resized and the previous content will be replaced by the scaled image.
* Tip: use getheight() and getwidth() to find out the new width and height of the scaled image.
* Note: After scaling, all images will have a bit depth of 16, even if the original image had
* a bit depth of 8.
* */
void scale_k(double k);
/* Scale Non-Proportional
* Scale the image using bilinear interpolation, with different horizontal and vertical scale factors.
* */
void scale_kxky(double kx, double ky);
/* Scale To Target Width and Height
* Scale the image in such a way as to meet the target width and height.
* Tip: if you want to keep the image proportional, scale_k() might be more appropriate.
* */
void scale_wh(int finalwidth, int finalheight);
/* Blended Functions
* All these functions are identical to their non-blended types. They take an extra argument, opacity, which is
* a double from 0.0 to 1.0 and represents how much of the original pixel value is retained when plotting the
* new pixel. In other words, if opacity is 0.7, then after plotting, the new pixel will be 30% of the
* original colour the pixel was, and 70% of the new colour, whatever that may be. As usual, each function
* is available in int or double versions. Please note: This is neither alpha channel nor PNG transparency chunk support. This merely blends the plotted pixels.
* */
// Start Blended Functions
void plotHSV_blend(int x, int y, double opacity, double hue, double saturation, double value);
void plotHSV_blend(int x, int y, double opacity, int hue, int saturation, int value);
void line_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue);
void line_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue);
void square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue);
void square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue);
void filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue);
void filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue);
void circle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue);
void circle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue);
void filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue);
void filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue);
void bezier_blend( int startPtX, int startPtY,
int startControlX, int startControlY,
int endPtX, int endPtY,
int endControlX, int endControlY,
double opacity,
double red, double green, double blue);
void bezier_blend( int startPtX, int startPtY,
int startControlX, int startControlY,
int endPtX, int endPtY,
int endControlX, int endControlY,
double opacity,
int red, int green, int blue);
void plot_text_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue);
void plot_text_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue);
void plot_text_utf8_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue);
void plot_text_utf8_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue);
void boundary_fill_blend(int xstart, int ystart, double opacity, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue) ;
void boundary_fill_blend(int xstart, int ystart, double opacity, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) ;
void flood_fill_blend(int xstart, int ystart, double opacity, double fill_red, double fill_green, double fill_blue) ;
void flood_fill_blend(int xstart, int ystart, double opacity, int fill_red, int fill_green, int fill_blue) ;
void polygon_blend(int * points, int number_of_points, double opacity, double red, double green, double blue);
void polygon_blend(int * points, int number_of_points, double opacity, int red, int green, int blue);
void plotCMYK_blend(int x, int y, double opacity, double cyan, double magenta, double yellow, double black);
void plotCMYK_blend(int x, int y, double opacity, int cyan, int magenta, int yellow, int black);
// End of Blended Functions
/* Laplacian
* This function applies a discrete laplacian to the image, multiplied by a constant factor.
* The kernel used in this case is:
* 1.0 1.0 1.0
* 1.0 -8.0 1.0
* 1.0 1.0 1.0
* Basically, this works as an edge detector. The current pixel is assigned the sum of all neighbouring
* pixels, multiplied by the corresponding kernel element. For example, imagine a pixel and its 8 neighbours:
* 1.0 1.0 0.0 0.0
* 1.0 ->1.0<- 0.0 0.0
* 1.0 1.0 0.0 0.0
* This represents a border between white and black, black is on the right. Applying the laplacian to
* the pixel specified above pixel gives:
* 1.0*1.0 + 1.0*1.0 + 0.0*1.0 +
* 1.0*1.0 + 1.0*-8.0 + 0.0*1.0 +
* 1.0*1.0 + 1.0*1.0 + 0.0*1.0 = -3.0
* Applying this to the pixel to the right of the pixel considered previously, we get a sum of 3.0.
* That is, after passing over an edge, we get a high value for the pixel adjacent to the edge. Since
* PNGwriter limits the colour components if they are off-scale, and the result of the laplacian
* may be negative, a scale factor and an offset value are included. This might be useful for
* keeping things within range or for bringing out more detail in the edge detection. The
* final pixel value will be given by:
* final value = laplacian(original pixel)*k + offset
* Tip: Try a value of 1.0 for k to start with, and then experiment with other values.
* */
void laplacian(double k, double offset);
/* Filled Triangle
* Draws the triangle specified by the three pairs of points in the colour specified
* by the colour coefficients. The colour components are either doubles from 0.0 to
* 1.0 or ints from 0 to 65535.
* */
void filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, int red, int green, int blue);
void filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, double red, double green, double blue);
/* Filled Triangle, Blended
* Draws the triangle specified by the three pairs of points in the colour specified
* by the colour coefficients, and blended with the background. See the description for Blended Functions.
* The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
* */
void filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, int red, int green, int blue);
void filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, double red, double green, double blue);
/* Arrow, Filled Arrow
* Plots an arrow from (x1, y1) to (x2, y2) with the arrowhead at the second point, given the size in pixels
* and the angle in radians of the arrowhead. The plotted arrow consists of one main line, and two smaller
* lines originating from the second point. Filled Arrow plots the same, but the arrowhead is a solid triangle.
* Tip: An angle of 10 to 30 degrees looks OK.
* */
void arrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue);
void arrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue);
void filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue);
void filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue);
/* Cross, Maltese Cross
* Plots a simple cross at x, y, with the specified height and width, and in the specified colour.
* Maltese cross plots a cross, as before, but adds bars at the end of each arm of the cross.
* The size of these bars is specified with x_bar_height and y_bar_width.
* The cross will look something like this:
*
* ----- <-- ( y_bar_width)
* |
* |
* |-------| <-- ( x_bar_height )
* |
* |
* -----
* */
void cross( int x, int y, int xwidth, int yheight, double red, double green, double blue);
void cross( int x, int y, int xwidth, int yheight, int red, int green, int blue);
void maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, double red, double green, double blue);
void maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, int red, int green, int blue);
/* Diamond and filled diamond
* Plots a diamond shape, given the x, y position, the width and height, and the colour.
* Filled diamond plots a filled diamond.
* */
void filleddiamond( int x, int y, int width, int height, int red, int green, int blue);
void diamond(int x, int y, int width, int height, int red, int green, int blue);
void filleddiamond( int x, int y, int width, int height, double red, double green, double blue);
void diamond(int x, int y, int width, int height, double red, double green, double blue);
/* Get Text Width, Get Text Width UTF8
* Returns the approximate width, in pixels, of the specified *unrotated* text. It is calculated by adding
* each letter's width and kerning value (as specified in the TTF file). Note that this will not
* give the position of the farthest pixel, but it will give a pretty good idea of what area the
* text will occupy. Tip: The text, when plotted unrotated, will fit approximately in a box with its lower left corner at
* (x_start, y_start) and upper right at (x_start + width, y_start + size), where width is given by get_text_width()
* and size is the specified size of the text to be plotted. Tip: Text plotted at position
* (x_start, y_start), rotated with a given 'angle', and of a given 'size'
* whose width is 'width', will fit approximately inside a rectangle whose corners are at
* 1 (x_start, y_start)
* 2 (x_start + width*cos(angle), y_start + width*sin(angle))
* 3 (x_start + width*cos(angle) - size*sin(angle), y_start + width*sin(angle) + size*cos(angle))
* 4 (x_start - size*sin(angle), y_start + size*cos(angle))
* */
int get_text_width(char * face_path, int fontsize, char * text);
int get_text_width_utf8(char * face_path, int fontsize, char * text);
};
#endif

2
quick.sh

@ -0,0 +1,2 @@
./bin/Debug/AwesomeAttractor -a attr/quick.attr 15000000 canvas/quick.canv 600 600 3
./bin/Debug/AwesomeAttractor -c canvas/quick.canv 600 600 3 quick.png

11
stfu/ExampleProgramOutput.stf

@ -0,0 +1,11 @@
Screen options: {
Brightness: "1.5"
Contrast: "70.1"
heigth: "1200"
rendermode: "OpenGL"
width: "1920"
}
Sound options: {
max soundfx: "128"
outputmode: "5.1"
}

28
stfu/LICENSE.txt

@ -0,0 +1,28 @@
Copyright (c) 2009 Maurice Bos and Nick Overdijk
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The names of the authors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- Maurice Bos (maurice@bosbyte.nl)
- Nick Overdijk (nick@dotsimplicity.net)

45
stfu/STFU.cbp

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="STFU" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin\Debug\STFU" prefix_auto="1" extension_auto="1" />
<Option object_output="obj\Debug\" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-Wall" />
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
<Option output="bin\Release\SSSParser" prefix_auto="1" extension_auto="1" />
<Option object_output="obj\Release\" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-Wall" />
</Compiler>
<Unit filename="main.cpp" />
<Unit filename="stf.cpp" />
<Unit filename="stf.hpp" />
<Extensions>
<code_completion />
<envvars />
<debugger />
<lib_finder disable_auto="1" />
</Extensions>
</Project>
</CodeBlocks_project_file>

21
stfu/STFU.depend

@ -0,0 +1,21 @@
# depslib dependency file v1.0
1241021944 source:/media/Data/Code/C/AwesomeAttractor/stfu/main.cpp
<iostream>
<fstream>
"stf.hpp"
1241022258 /media/Data/Code/C/AwesomeAttractor/stfu/stf.hpp
<fstream>
<string>
<sstream>
<map>
<stdexcept>
1241019586 source:/media/Data/Code/C/AwesomeAttractor/stfu/stf.cpp
<iostream>
<fstream>
<map>
<string>
<typeinfo>
"stf.hpp"

26
stfu/STF_Format_Example.stf

@ -0,0 +1,26 @@
imthefirst: "The first value!"
name with spaces: "Names can contain spaces"
name
with
newlines: "And even newlines!"
test: {
blah: "The value of [root].test.blah"
newlines: "newlines and all their whitespace
get stored"
comment: This comment value contains some information about how to comment in SSS files.
"You can place comment before the quotes, the parser will ignore it."
output this: "This particular value is retrieved in the code."
output child:
Same goes for children. The parser ignores anything until a curly bracket is found. {
info: "Awwwwwwright! Tree output oh yeah."
coolness: "+20"
}
Array: { :"This" :"is" :"like" :"an" :"array" }
2D array:
{
: { :"First" :"Row" }
: { :"Second" :"Row" :"!" }
}
}

6
stfu/arrays.stf

@ -0,0 +1,6 @@
array: { :"Some" :"Unnamed" :"Values" }
2D array:
{
: { :"First" :"Row" }
: { :"Second" :"Row" :"!" }
}

282
stfu/main.cpp

@ -0,0 +1,282 @@
#include <iostream>
#include <fstream>
#include "stf.hpp"
using namespace std;
using stfu::node;
/*! \author Nick Overdijk
* \version 1.2
* \date 2009-04-27
*
* STFU Example App
*/
void prettyprint(const string &title) {
cout
<< "\t\t-----" << title << "-----\t\t\n"
<< endl;
}
void read_examples(node &n) {
prettyprint("Reading Examples");
cout
<< "Reading a STF file in a node is trivial with the stream insertion operator, it returns whether it had success: true/false\n"
<< endl;
if ("test.stf" >> n) {
prettyprint("Outputting a node/tree");
cout << n;
cout
<< "\n\nnode::getChild() returns a node&, so we can easily output nested nodes:\n"
<< endl;
cout << n.getChild("child");
cout
<< "\n\nnode::getValue() returns a string&, so we can easily output values too:\n"
<< endl;
cout << n.getValue("imthefirst");
cout
<< "\n\nThe constness of getChild and getValue depend on the object."
<< endl;
} else {
cerr << "Couldn't read file!" << endl;
}
}
void change_examples(node &n) {
cout
<< "\t\t-----Change examples------\t\t\n"
<< endl;
if ("test.stf" >> n) {
prettyprint("Edit a value");
cout << "Before:" << endl;
cout << n;
n.value("imthefirst") = "O YEAH!";
cout << "After: " << endl;
cout << n;
prettyprint("Edit a node");
cout << "Before:" << endl;
cout << n;
n.child("child") = n;
cout << "After: " << endl;
cout << n;
prettyprint("Edit the name of a value");
cout << "Before:" << endl;
cout << n;
n.renameValue("imthefirst", "roflolmao");
cout << "After: " << endl;
cout << n;
prettyprint("Edit the name of a child");
cout << "Before:" << endl;
cout << n;
n.renameChild("child", "NEW NAMEZ");
cout << "After: " << endl;
cout << n;
} else {
cerr << "Couldn't read file!" << endl;
}
}
void create_examples(node &n) {
prettyprint("Creation examples");
node screenoptions;
node soundoptions;
if (!("screenoptions.stf" >> screenoptions && "soundoptions.stf" >> soundoptions)) {
cerr << "Couldn't read files!" << endl;
return;
}
cout << "You can merge these two tree quite easily into one tree (aka \"Adding children\"):" << endl;
cout << "Before (screenoptions):" << endl;
cout << screenoptions << endl;
cout << "Before (soundoptions):" << endl;
cout << soundoptions << endl;
//child() creates the child when it doesn't exist
n.child("Screen options") = screenoptions;
//but you can do this too
n.addChild("Sound options", soundoptions);
cout << "After (both are now children of options):" << endl;
cout << n;
prettyprint("Adding a value");
cout << "Before:" << endl;
cout << n;
//this way, NOT sure child() won't create a child (if it doesn't exist, it'll create it)
//also not sure value() won't alter a value (if the value[index] exists, it'll change that)
n.child("Screen options").value("Contrast") = "70.1";
//this way, 100% sure that getChild() won't create a child, and addValue() will always add a value
//get{Child|Value} throw an out_of_range when they can't find the {Child|Value}.
n.getChild("Screen options").addValue("Brightness") = "1.5";
cout << "After:" << endl;
cout << n;
prettyprint("Writing to a file (aka \"Saving to a file\")");
const char *filename = "ExampleProgramOutput.stf";
if (!(filename << n)) {
cerr << "Couldn't write to ";
} else {
cout << "Node succesfully saved to ";
}
cout << filename << endl << endl;
}
void remove_examples(node &n) {
prettyprint("Removal examples");
if (!("test.stf" >> n)) {
cerr << "Couldn't open file!" << endl;
}
prettyprint("Deleting a value");
cout << "Before:" << endl;
cout << n;
//remove{Child|Value} throws an out_of_range when the child/value isn't found
n.removeValue("imthefirst");
cout << "After:" << endl;
cout << n;
prettyprint("Deleting a child/node");
cout << "Before:" << endl;
cout << n;
n.removeChild("child");
cout << "After:" << endl;
cout << n;
}
void array_examples(node &n) {
prettyprint("Array Examples");
if (!("arrays.stf" >> n)) {
cerr << "Couldn't open arrays.stf!" << endl;
return;
}
prettyprint("Iterating over an array: try/catch method");
node &array = n.getChild("array");
try {
size_t i = 0;
while (true) {
//use getValue(), it throws an out_of_range when there are no more elements
cout << array.getValue(i) << endl;
i++;
}
} catch (out_of_range &e) {/*we're done! ;-)*/}
prettyprint("Iterating over an array: size() method");
size_t max = array.values.size();
for(size_t i = 0; i < max; i++){
cout << array.getValue(i) << endl;
}
prettyprint("Iterating over a 2D-array: size() method");
node &multiarray = n.getChild("2D array");
//rows are children
size_t rows = multiarray.children.size();
//each row can have a different amount of cols
size_t cols[rows];
for(size_t i = 0; i < rows; i++){
//same as with size() method above
cols[i] = multiarray.getChild(i).values.size();
//output to see it for yourself
cout << i << ',' << cols[i] << endl;
}
//so, we're done. Code could've been written in 100 ways, but this does it step by step to be clear.
for(size_t row = 0; row < rows; row++){
for(size_t col = 0; col < cols[row]; col++){
cout << multiarray.getChild(row).getValue(col) << ", ";
}
cout << "\b\b " << endl;
}
/*Arrays are simply unnamed nodes/values. Nothing more, nothing less.
You can change them like nodes/values, add them the same way, etc.
Most of the functions are overloaded so that you don't have to specify a name
These functions call their overloaded brethren with name = "" */
prettyprint("Creating an array");
cout << "Before:\n\n";
cout << n << endl;
node &newarray = n.addChild("my new array");
stringstream buf;
for(size_t i = 0; i < 10; i++){
buf << i;
newarray.addValue() = buf.str();
}
cout << "After:\n\n";
cout << n << endl;
}
int main() {
prettyprint("Welcome to the stf example app");
ifstream license("LICENSE.txt");
if (license.good()) {
string line;
while (getline(license, line)) {
cout << line << endl;
}
license.close();
} else {
cerr << "Couldn't open/read LICENSE.txt" << endl;
exit(-1);
}
cout << endl;
cout
<< "This application shows how easy it is to use STFU (Simple Tree File Utility).\n"
<< "STF is a \"new\" way to store leveled data, just as XML can do, for example.\n"
<< "The format looks like a C(++) struct, which we think is easier to read than XML.\n"
<< "Each {...} is a node, a node has other nodes and values. The file itself is the\n"
<< "(unnamed) root node.\n"
<< endl;
//create an empty node
node n;
read_examples(n);
n.clear();
change_examples(n);
n.clear();
create_examples(n);
n.clear();
remove_examples(n);
n.clear();
array_examples(n);
n.clear();
return 0;
}

4
stfu/screenoptions.stf

@ -0,0 +1,4 @@
width: "1920"
heigth: "1200"
rendermode: (OpenGL or DirectX) "OpenGL"

107
stfu/simple documentation.html

@ -0,0 +1,107 @@
<h1>STF - Simple Tree Format</h1>
<p>
STF is a new file format to store a tree of values, just like XML and JSON can do. It has support for only 2 types: values and nodes. A node can have values and other nodes, allowing the creation of a tree format. A STF file itself is a node (the virtual root node). Nodes are a collection of other values and (child)nodes.
</p>
<h2>Usage</h2>
<p>
Nodes and values are named, the smallest name being "". We refer to the nodes with "" as name as unnamed nodes/values, but the same rules apply for the syntax; examples will follow, don't worry. Each name is followed by a ':' which signifies the end of the name (the name-terminator). The ':' is followed by either a '{' or a '"', which signifies the type of the name (the type-specifier): '{' are lists, '"' are values. The parser will extract and ignore anything in between the name-terminator and the type-specifier.
</p>
<h2>Examples</h2>
<h3>Starting easy</h3>
<p>
Here's an example of a very small STF file. It shows values and nodes and their syntax, without fancy stuff.
<pre>
name: "First value in the file"
childnode: {
value: "This is another value, inside a child node with the name "childnode"
}
</pre>
</p>
<h3>Comments</h3>
<p>
Don't want to have a very long descriptive name, but do want a bit of extra info? Comments (or anything really) can be place in between the name-terminator and the type-specifier.
<pre>
name: This text will be ignore by the parser "First value in the file."
childnode: This text will too, be ignored {
value: You probably already guessed it
But this text will to be ignored. Anything here is allowed, except for a type-specifier of course! ;-)
"This is another value, inside a child node with the name ""childnode""."
}
<h3>Getting more interesting</h3>
<p>
This example shows what characters are allowed inside names and values.
<pre>
firstnode: {
subnode: {
blah: "xyz"
blah: "names of values/childs don't have to be unique!"
}
}
secondnode: {
name with space: "names can contain spaces"
name
with
newlines: "names can contain newlines"
blah: "values can contain
newlines too!"
}
</pre>
</p>
<p>
NOTE: Take a look at the other .stf files included with this project for more examples.
</p>
<h1>STFU - Simple Tree Format Utility</h1>
<p>
STFU is the first C++ implementation for STF.
</p>
<p>
This example will show you some of the basic features of STFU:
<pre>
#include <iostream>
#include "stf.hpp"
using namespace std;
using stfu::node;
int main(){
node n,f;
if (n.read("blah.stf")){ //could also do: 'if ("blah.stf" >> n){'
cout << n.value("test"); // Read a value
cout << n.child("subnode").value("hi",0); //Read the first value
cout << n.child("subnode").value("hi",1); //Read the second value
n.child("subnode").value("test") = "blah"; //Add a value
if (f.read("another_file.stf")){
n.child("secondnode") = f; //add a child
}
n.write("blah.stf"); //Write changes back to the file
// "blah.stf" << n; would also be valid.
cout << n; //output the file to the console instead of a file.
} else {
cout << "ERROR!" << endl;
}
}
//NOTE: For more detailed examples, please take a look at the 'main.cpp' file of this project.
</pre>
</p>
<p>
As you see, the .child() and .value() functions can be used to read, create and change values/childs. More advanced functions are available, 'main.cpp' contains examples to explain all of them.
</p>

9
stfu/soundoptions.stf

@ -0,0 +1,9 @@
max soundfx:
A value between 0 and 255
"128"
outputmode:
Options are: 2.0, 4.0, 4.1, 5.1, 7.1 and 412.28
"5.1"

276
stfu/stf.cpp

@ -0,0 +1,276 @@
/*
Copyright (c) 2009 Maurice Bos and Nick Overdijk
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The names of the authors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- Maurice Bos (maurice@bosbyte.nl)
- Nick Overdijk (nick@dotsimplicity.net)
*/
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <typeinfo>
#include "stf.hpp"
using namespace std;
using stfu::node;
using stfu::child;
using stfu::value;
ostream &stfu::operator<< (ostream &out, const node &root) {
root.write(out);
return out;
}
bool stfu:: operator<< (const char *filename, const node &root){
return root.write(filename);
}
bool stfu::operator<< (ofstream &out, const node &root) {
return root.write(out);
}
bool stfu::operator>> (istream &in, node &root) {
return root.read(in);
}
bool stfu::operator>> (const char *filename, node &root) {
return root.read(filename);
}
const string &node::getValue(const string &name, size_t index) const throw (out_of_range) {
return get_indexed<string, string>(values, name, index);
}
/*Function is const, but shouldn't be called on const objects since it returns a nonconst-reference to a member*/
string &node::getValue(const string &name, size_t index) throw (out_of_range) {
return get_indexed<string, string>(values, name, index);
}
string &node::addValue(const string &name) {
return values.insert(pair<string, string>(name, string()))->second;
}
string &node::value(const string &name, size_t index) {
try {
return getValue(name, index);
} catch (out_of_range &e) {
//it doesn't exist: create it
return addValue(name);
}
}
void node::removeValue(const string &name, size_t index) throw (out_of_range) {
values.erase(get_indexed_it(values, name, index));
return;
}
void node::renameValue(const string &oldName, const string &newName, size_t index) {
addValue(newName) = value(oldName, index);
removeValue(oldName, index);
}
const node &node::getChild(const string &name, size_t index) const throw (out_of_range) {
return get_indexed<string, node>(children, name, index);
}
node &node::getChild(const string &name, size_t index) throw (out_of_range) {
return get_indexed<string, node>(children, name, index);
}
node &node::addChild(const string &name) {
return children.insert(pair<string, node>(name, node()))->second;
}
node &node::addChild(const string &name, node &newNode) {
return children.insert(pair<string, node>(name, newNode))->second;
}
node &node::child(const string &name, size_t index) {
//if there's no such child, add one
try {
return getChild(name, index);
} catch (out_of_range &e) {
//it doesn't exist: create it
return addChild(name);
}
}
void node::renameChild(const string &oldName, const string &newName, size_t index) {
node copy = child(oldName, index);
removeChild(oldName, index);
addChild(newName) = copy;
}
void node::removeChild(const string &name, size_t index) throw (out_of_range) {
children.erase(get_indexed_it(children, name, index));
return;
}
bool node::read(const char *filename) {
ifstream f(filename);
if (!f.good()) return false;
bool success = read(f);
f.close();
return success;
}
bool node::read(istream &in) {
while (1) {
in >> ws; //Skip whitespace
//if end of node is reached, return
if (in.peek() == '}') {
in.ignore();
return true;
}
if (in.eof()) {
return true; //End of the file is reached
}
string name;
char type; // '{' or '"'
streamRead(in, name, ':'); //Read name (all chars before ':')
type = streamSkip(in,"\"{"); //Skip everything until '{' or '"'
switch (type) {
//in case of value
case '"': {
string value;
while (1) {
if (streamRead(in, value, '"') == 0) { //Read to the closing-"
return false;
}
if (in.peek() == '"') {
in.ignore();
value += '"';
continue;
} else {
break;
}
}
this->values.insert(pair<string,string>(name, value));
break;
}
//in case of child
case '{': {
node sub;
if (!sub.read(in)) { //Recursively read the subnode
return false;
}
this->children.insert(pair<string,node>(name,sub));
break;
}
default:
return false;
}
}
}
/*Writes to a file using it's overloaded self*/
bool node::write(const char *filename) const {
ofstream f(filename);
if (!f.good()) {
return false;
}
bool success = write(f);
f.close();
return success;
}
// TODO (Nick#1#): Make write() not put unnamed values on a new line, children are okay.
bool node::write(ostream &out, size_t depth, string indent) const {
string indentation;
for (size_t i = 0; i < depth; i++) {
indentation += indent;
}
for (multimap<string, string>::const_iterator value_it = values.begin(); value_it != values.end(); value_it++) {
//Escape all the '"' by adding a second '"'
string value(value_it->second);
size_t found = value.find('"');
//while there are more ", insert second "s
while (found != value.npos) {
value.insert(found, 1, '"');
found = value.find('"', found+2);
}
out << indentation << value_it->first << ": \"" << value << '"' << endl;
}
for (multimap<string, node>::const_iterator child_it = children.begin(); child_it != children.end(); child_it++) {
out << indentation << child_it->first << ": {" << endl;
child_it->second.write(out, depth+1);
out << indentation << '}' << endl;
}
return true;
}
char node::streamSkip(istream &in, const string &delimiters) {
char cur;
//Return if the current char is part of delimiters[]
while (in >> noskipws >> cur) {
if (delimiters.find_first_of(cur) != delimiters.npos) return cur;
}
return 0;
}
char node::streamRead(istream &in, string &out, const string &delimiters) {
char cur;
//Return if the current char is part of delimiters[]
while (in >> noskipws >> cur) {
if (delimiters.find(cur) != delimiters.npos) return cur;
out += cur;
}
return 0;
}
char node::streamRead(istream &in, string &out, const char delimiter) {
char cur;
//Return if the current char is delimiter
while (in >> noskipws >> cur) {
if (delimiter == cur) return cur;
out += cur;
}
return 0;
}

358
stfu/stf.hpp

@ -0,0 +1,358 @@
/*
Copyright (c) 2009 Maurice Bos and Nick Overdijk
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The names of the authors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- Maurice Bos (maurice@bosbyte.nl)
- Nick Overdijk (nick@dotsimplicity.net)
*/
#include <fstream>
#include <string>
#include <sstream>
#include <map>
#include <stdexcept>
#ifndef STFU_HPP
#define STFU_HPP
namespace stfu {
using namespace std;
const static string not_found("search->No such child/value");
/*! \class node
* \brief A node in an STF tree.
* \author Maurice Bos
* \author Nick Overdijk
* \version 1.0
* \date 2009-04-25
*
* When you read an STF file through a node (with read() for example), this node will be the root node, without a name. See the examples for more information.
*/
class node {
/** Overloaded ostream's operator<< */
friend ostream &operator<< (ostream &out, const node &root);
/** Returns whether it was succesful or not*/
friend bool operator<< (ofstream &out, const node &root);
/** Acts like write(), but looks like this: "filename" << node */
friend bool operator<< (const char *filename, const node &root);
/** Overloaded istream's operator>> */
friend bool operator>> (istream &in, node &root);
/** Acts like read(), but looks like this: "filename" >> node */
friend bool operator>> (const char *filename, node &root);
public:
//@{
/** The values and children belonging to this node. To add, remove, do whatever: you CAN use these variables directly. To use indexing, use the member functions below*/
multimap<string, string> values;
multimap<string, node> children;
//@}
/**
Clears the whole node recursively.
*/
void clear() {
values.clear();
children.clear();
}
/**
Gets the string value from a variable
\param name The name of the value you wish to retrieve
\param index If there are more values with the same name, they are indexed. Here you can supply its indexnumber.
\return The retrieved value
*/
string &value(const string &name, size_t index = 0);
/**
Same as value(), but for unnamed values
\note same as value("", index)
\param index If there are more unnamed values, they are indexed. Here you can supply its indexnumber. \return Same as value
*/
string &value(size_t index){
return value("", index);
}
/**
Creates and adds a value.
\param name The name of the value to be created
\return A reference to the value of the created value.
*/
string &addValue(const string &name = "");
/**
Const function for const objects to get a value. It's NOT possible to call value() on constant objects for value() isn't const.
\param name Name of the value to be retrieved
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
\return Returns a const string& to the value of value with the name and index specified
*/
const string &getValue(const string &name, size_t index) const throw (out_of_range);
/**
Same as getValue() const, but for unnamed values
\note same as getValue("", index) const
\param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber.
\return Returns a const string& to the value of value with the name and index specified
*/
const string &getValue(size_t index) const throw (out_of_range){
return getValue("", index);
}
/**
Same as getValue() const, but for non-const objects. The returned string& can safely be modified.
\param name Name of the value to be retrieved
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
\return Returns a string& to the value of value with the name and index specified
*/
string &getValue(const string &name, size_t index = 0) throw (out_of_range);
/**
Same as getValue(), but for unnamed values
\note same as getValue("", index)
\param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber.
\return Returns a string& to the value of value with the name and index specified
*/
string &getValue(size_t index) throw (out_of_range){
return getValue("", index);
}
/**
Removes a value. Surprise huh?
\param name Name of the value to be removed
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
*/
void removeValue(const string &name, size_t index = 0) throw (out_of_range);
/**
Removes an unnamed value.
\note same as removeValue("", index);
\param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber.
*/
void removeValue(size_t index) throw (out_of_range){
removeValue("", index);
}
/**
Renames a value.
\param oldName Name of the value to be renamed
\param newName The name that the oldName-value should have
\param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber.
*/
void renameValue(const string &oldName, const string &newName, size_t index = 0);
/**
Changes, adds or retrieves node
\param name The name of the child you wish to retrieve, change or add.
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
\return The retrieved node
If this index number is > the number of variables with that name, a new variable with that name is created with index = current number of same name variables + 1.
So say you have 4 variables named "tree", you call child("tree", 10), another tree gets made with index 5, NOT 10.
*/
node &child(const string &name, size_t index = 0);
/**
Same as child(), but for unnamed children.
\note same as child("", index)
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
\return The retrieved node
*/
node &child(size_t index = 0){
return child("", index);
}
/**
Guarranteed to add a child
\param name Name for the child
\return A reference to the created node
*/
node &addChild(const string &name = "");
/**
As addChild(name), but copies data from newChild as well.
\param name Name for the child
\param newChild Data to copy from the child.
\return A reference to the created node
*/
node &addChild(const string &name, node &newChild);
/**
As addChild(name, newChild), but without name, for unnamed children
\note same as addChild("", newChild);
\param newChild Data to copy from the child.
\return A reference to the created node
*/
node &addChild(node &newChild){
return addChild("", newChild);
}
/**
Const function for const objects to get a node. It's NOT possible to call child() for this, for child() isn't const.
\param name Name of the child to be retrieved
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
\return Returns a const node& to the node with the name and index specified
*/
const node &getChild(const string &name, size_t index = 0) const throw (out_of_range);
/**
Const function for const objects to get an unnamed const node&.
\note same as getChild("", index) const;
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
\return Returns a const node& to the node with the name and index specified
*/
const node &getChild(size_t index = 0) const throw (out_of_range){
return getChild("", index);
}
/**
Same as getChild() const, but for non-const objects. The returned child & can be modified
\param name Name of the child to be retrieved
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
\return Returns a node& to the node with the name and index specified
*/
node &getChild(const string &name, size_t index = 0) throw (out_of_range);
/**
Same as getChild() const, but for non-const objects.
\note same as getChild("", index);
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
\return Returns a node& to the node with the name and index specified
*/
node &getChild(size_t index = 0) throw (out_of_range){
return getChild("", index);
}
/**
Removes a child. Surprise huh?
\param name Name of the child to be removed
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
*/
void removeChild(const string &name, size_t index = 0) throw (out_of_range);
/**
As removeChild() for unnamed children.
\param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber.
*/
void removeChild(size_t index = 0) throw (out_of_range){
removeChild("", index);
}
/**
Renames a child.
\param oldName Name of the child to be renamed
\param newName The name that the oldName-child should have
\param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber.
*/
void renameChild(const string &oldName, const string &newName, size_t index = 0);
/**
Reads the STF from an istream
\return Returns whether it was succesful
*/
bool read(istream &in);
/**
Reads the STF from a file
\return Returns whether it was succesful
*/
bool read(const char *filename);
/**
Writes the STF to an ostream with optional indentation
\param out ostream to write to
\param depth how much indentation to start with
\param indent What string to use as indenation
\return Returns whether it was succesful
*/
bool write(ostream &out, size_t depth = 0, string indent = "\t") const;
/**
Writes to a file. Simply first opens a file and passes that ostream to itself.
\param filename File to write to
\return Returns whether it was succesful
*/
bool write(const char *filename) const;
private:
char streamRead(istream &in,string &out, const string &delim);
char streamRead(istream &in, string &out, const char delim);
char streamSkip(istream &in, const string &delim);
/*
returns a T2&, not a const T2&. The functions getValue/getChild make sure this will be done correctly for const objects
this function can NOT use get_indexed_it, because that function can't be const:
const_iterators can not be transformed into iterators, and the iterator is needed for erase(), which wants an iterator.
*/
template <typename T1, typename T2>
T2& get_indexed(const multimap<T1, T2> &container, const T1 &key, size_t index = 0) const throw (out_of_range) {
size_t count = container.count(key);
if (count != 0 && count-1 >= index) {
typename multimap<T1, T2>::const_iterator it = container.find(key);
while (index--) it++;
return const_cast<T2&>(it->second);
} else {
throw out_of_range((string)"get_indexed->"+"Element " + key + "doesn't exist!");
}
}
// template <typename T1, typename T2>
// typename multimap<T1, T2>::iterator get_indexed_it(multimap<T1, T2> &container, const T1 &key, size_t index = 0) throw (out_of_range) {
// size_t count = container.count(key);
//
// if (count != 0 && count-1 >= index) {
// typename multimap<T1, T2>::iterator it = container.find(key);
// while (index--) it++;
// return it;
// } else {
// throw out_of_range((string)"get_indexed_it->"+"Element " + key + "doesn't exist!");
// }
// }
template <typename Container, typename T>
typename Container::iterator get_indexed_it(Container& container, const T& key, size_t index = 0)
throw (out_of_range) {
typename Container::iterator it = container.find(key);
while (index-- && it != container.end() && it->first==key) it++;
if (it == container.end()) throw out_of_range("get_indexed_it(Container&, const T&, size_t)");
return it;
}
};
typedef pair<string, string> value;
typedef pair<string, node> child;
typedef multimap<string, string>::iterator valueiterator;
typedef multimap<string, node>::iterator childiterator;
}
#endif // STFU_HPP

4
stfu/test.stf

@ -0,0 +1,4 @@
imthefirst: "The first value inside root!"
child: {
blah: "The value of [root].child.blah"
}