No errors, but translated code from Processing doesn't run as it should!

Hi everybody,

I’m attempting to port Daniel Shiffman’s Collatz conjecture script from Processing to OF.
When build my translation, the OF app crashes instantly, but my IDE doesn’t show any compiler errors.

ofApp.h

#pragma once

#include "ofMain.h"
#include <iostream>
#include <vector>
#include <algorithm>

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);
    
        int collatz(int n);
    
    int count;
    float len;
    float angle;
    
    std::vector<int> sequence;
    
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    
    ofBackground(0);
    
    count = 0;
    len = ofGetHeight() / 100.0;
    angle = 0.15;
    
}

//--------------------------------------------------------------
void ofApp::update(){
    
    sequence.clear();
    
    int n = count;
    
    do {
        sequence.push_back(n);
        n = collatz(n);
    }
    while (n != 1);
    sequence.push_back(1);
    std::reverse(sequence.begin(), sequence.end());
    
    count++;

}

//--------------------------------------------------------------
void ofApp::draw(){
    
    ofLoadIdentityMatrix();
    ofTranslate(ofGetWidth() / 2, ofGetHeight());
    
    for (int i = 0; i < sequence.size(); i++) {
        
        if (sequence[i] % 2 == 0) {
            ofRotateRad(angle);
        } else {
            ofRotateRad(-angle);
        }
        
        ofSetLineWidth(2);
        ofSetColor(255, 50);
        ofDrawLine(0, 0, 0, -len);
        ofTranslate(0, -len);
        
    }
    
    std::cout << "finished" << std::endl;
    
}

//--------------------------------------------------------------
int ofApp::collatz(int n){
    if (n % 2 == 0) {
        return n / 2; // even
    } else {
        return (n * 3 + 1) / 2; // odd
    }
}

I didn’t exactly know how to translate resetMatrix() from Processing to OF, but the Processing documentation states that this replaces the current matrix with the identity matrix, so I substituted it with ofLoadIdentityMatrix() (ofApp.cpp: line 36).
The OF documentation is quite obscure about what this does.

I don’t know if this causes the program to crash or if I’m overlooking another mistake I made?

I appreciate any help! :slight_smile:

can you post the original procesing code please?
I think that it is because the first time the code runs into the update function it never exits the while, because if n = 0 then colatz function returns 0 which is different to 1 allowing to have the while loop running for ever.

There is no need to call

ofLoadIdentityMatrix();

That happens for each draw cycle.

1 Like

I’ve linked to the original code above. By clicking on “Collatz conjecture” you should be directed to Daniel’s GitHub.

Yes, you’re absolutely right! Starting the conjecture with 0 provokes an endless loop!
I’ve fixed that by setting count in ofApp::setup to 1, instead of 0, and now the OF app window finally appears, but nothing is drawn? Only the black background can be seen. :confused:

Noted!

Thanks for helping me again, Roy. Much appreciated! :slight_smile:

Hi, so the problem is that Shifman’s code all the drawing is generated at once and not over the drawing loop. On each new frame the transformations are reset so the code does not work as it relies on accumulating transformations.
The good part, is that you can adapt this quite easily.

ofApp.cpp


//--------------------------------------------------------------
int collatz(int n){
    if (n % 2 == 0) {
        return n / 2; // even
    } else {
        return (n * 3 + 1) / 2; // odd
    }
}

void ofApp::setup(){
	
	cout << "ofGetWidth(): " << ofGetWidth() << "  ofGetHeight() " << ofGetHeight() <<endl;
	
	fbo.allocate(ofGetWidth(), ofGetHeight());
	fbo.begin();
	ofClear(0, 0, 0, 255);
	float len = ofGetHeight()/100.0;
	float angle = 0.15;
	for (int i = 1; i < 100000; i++) {
		vector<int> sequence;
		int n = i;
		do {
			sequence.push_back(n);
			n = collatz(n);
		} while (n != 1);
		sequence.push_back(1);
		std::reverse(sequence.begin(), sequence.end());

		ofPushMatrix();
		ofTranslate(ofGetWidth()/2, ofGetHeight());
		for (int j = 0; j < sequence.size(); j++) {
			int & value = sequence[j];
			if (value % 2 == 0) {
				ofRotateRad(angle);
			} else {
				ofRotateRad(-angle);
			}
			ofSetLineWidth(2);
			ofSetColor(255, 2);
			ofDrawLine(0, 0, 0, -len);
			ofTranslate(0, -len);
		}
		ofPopMatrix();
		// Visualize the list
	}
	
	
	fbo.end();
	
}

//--------------------------------------------------------------
void ofApp::draw(){
	ofSetColor(255);
	fbo.draw(0,0);
}

ofApp.h

#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void draw();

	ofFbo fbo;
	
};
1 Like

Good morning,

Yes, I changed it, since it only seems to be possible to draw within the ofApp::draw() function in OF, whereas in Processing, Shiffman draws in setup() .
ofFbo sure is a great workaround though. Thanks!

OK, this must have escaped me.

Haha, thanks!! You’re a wizard! :wink:
Works like a charm!

I have a couple of questions about your code.

  1. Why do you clear the color at the beginning of ofApp::setup()?

  2. Is there a benefit to defining functions inside ofApp.cpp, not being part of the ofApp class (i.e. int collatz(int n)), other than not having to declare them in ofApp.h?
    Also do these functions need be written on top of the file, or is their placement irrelevant?

  3. I’ve also noticed that you use cout, endl, and vector without the preceding std::, but std::reverse, why is that?

  4. If I want to animate this, how can I keep track of the transformations? Can I simply save them to a class variable, and start from this saved transformation at each iteration?

Thanks again and have a nice weekend!

Hi, glad to help

Because the fbo contains “garbage” in its memory. If you dont clear you’ll see what was stored before in the memory location that got assigned to the FBO. It is like a rule of thumb to clear the fbo, immediately after you allocate it, unless you want to have that garbage there (which some times is quite interesting :smiley: )

That’s just laziness. You can do this because the function does not need access to any of ofApp’s members. The placement is relevant. It has to be before where you use it. Be aware that in larger projects you might run into trouble if you have 2 of this kind of functions with the same name, even on when on different files.

Simply because I copied it from your code and did not delete the std::. Inside ofApp you are able to not write the std::, but it only works in ofApp.

you can simply store the matrix and then reload it on the next frame.
in the ofApp.h file declare the matrix glm::mat4 matrix; as a class variable.
then you can load it with ofLoadMatrix (matrix) and retrieve it with matrix = ofGetCurrentViewMatrix();

best

1 Like

Hi,

Noted! I like the way you’re thinking here. :wink:

OK, is that because function overloading would come into effect?

OK, I get it.

Nice, I’m going try to this! Thank you!!

Regards.

not really. It creates a problem with the linker (the post compiler process that links all the compiled pieces), as the linker sees two functions named the same and does not know which one it should address. Often it will throw a lot of errors, even when you might only need to modify a single function’s name. Probably a lot will say it is a bad idea to use functions like this, but it will become noticeable if you are coding a larger project with several files, or an addon or library which is intended to be shared, in which you can not take for granted that that function’s name will not collide with something that the other person using your code has written.

1 Like

I see. Thanks for explaining that!

1 Like

Hi.

My teacher & studio boss did a-collatz in 2002. Besides Collatz conjecture, you also get Terras, a very interesting variation. AChaosCollatz.. We also use additional internal variations, for pattern synthesis. Don’t use it for audio, it breaks record players out of its reach into a perpetual very interesting song.

Hth.

&c

1 Like

Thanks for sharing, @coding!