ofxBox2d.cpp problem


#1

So I’m using example-Simple within the ofxBox2d add-on now I am trying to delete the circles/boxes that leave the screen so that the program doesn’t slow down after you spawn a bunch of them. I went into the ofxBox2d.cpp file to see if there was something that deleted the objects that left the screen already and turns out there is code that does just that in the update(). It was initially commented out so I uncommented it but now it is giving me syntax errors. I have tried and looked up many different things but none have worked so I am now back to the original ofxBox2d.cpp file except I still left the destroy code in the update() uncommented (I’m mentioning this so you know exactly what my code is without me having to post it, since the ofxBox2d.cpp file can be found online). Any suggestions?


#2

Maybe this will help.

#include "ofApp.h"
ofxBox2d box2d;
vector<shared_ptr<ofxBox2dCircle>> circles;

static bool shouldRemoveShape(const shared_ptr<ofxBox2dBaseShape> shape) {
	return shape.get() && shape.get()->alive == false;
}

//--------------------------------------------------------------
void ofApp::setup(){
	
	ofSetVerticalSync(true);
	box2d.init();
	box2d.createBounds();

}

//--------------------------------------------------------------
void ofApp::update(){
	
	box2d.update();
	
	if (ofGetMousePressed()) {
		
		// create a shared ptr circle
		shared_ptr<ofxBox2dCircle> circle = shared_ptr<ofxBox2dCircle>(new ofxBox2dCircle);
		
		// set physical properties
		circle.get()->setPhysics(1, 0.7, 0.1);
		
		// setup the circle with the box2d world
		circle.get()->setup(box2d.getWorld(), ofGetMouseX(), ofGetMouseY(), ofRandom(5, 20));
		
		// add the circle to the vector
		circles.push_back(circle);
		
	}
	
	
	// remove if we have more than 50 shapes
	if (circles.size() > 50) {
		circles.front().get()->alive = false;
	}
	
	// remove the shape if we mark as not alive.
	// see static bool above...
	ofRemove(circles, shouldRemoveShape);
	
}

//--------------------------------------------------------------
void ofApp::draw(){
	for(auto &circle : circles) {
		circle.get()->draw();
	}
	
	ofDrawBitmapStringHighlight("Total Shapes "+ofToString(circles.size()), 20, 20);
	ofDrawBitmapStringHighlight(ofToString(ofGetFrameRate(), 1) + " FPS", 20, 45);
}

#3

So I believe this is removing the very first circle that was created on the screen as soon as you create the 51th circle, but is there a way I could directly delete the circle that is currently exiting the screen?


#4

Just check to see if the shape is inside the viewport, if not then mark and remove.

// remove the circle if the circle is no longer in the viewport
	for(auto &circle : circles) {
		ofPoint pos = circle.get()->getPosition();
		float radius = circle.get()->getRadius();
		
		if (pos.x > ofGetWidth() + radius) {
			circle.get()->alive = false;
		}
		else if (pos.x < -radius) {
			circle.get()->alive = false;
		}
		else if(pos.y > ofGetHeight() + radius) {
			circle.get()->alive = false;
		}
		else if(pos.y < -radius) {
			circle.get()->alive = false;
		}
		
		
	}

#5

ofRemove(circles, shouldRemoveShape) is giving me an error. It says “‘shouldRemoveShape’: undeclared identifier”


#6
static bool shouldRemoveShape(const shared_ptr<ofxBox2dBaseShape> shape) {
	return shape.get() && shape.get()->alive == false;
}

#7

I already added that function


#8

where did you add it?


#9

in my ofApp.cpp file not inside any other function


#10

can you send me a git gist of your .cpp


#11
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup() {

	ofSetFrameRate(60); // set frame rate
	ofBackground(0, 0, 0); // set background color

	//old OF default is 96 - but this results in fonts looking larger than in other programs.
	ofTrueTypeFont::setGlobalDpi(72);

	// load font
	verdana14.load("verdana.ttf", 14, true, true);
	verdana14.setLineHeight(18.0f);
	verdana14.setLetterSpacing(1.037);

	// enable the Windows Touch Hook
	ofxWinTouchHook::EnableTouch();

	// add touch listeners
	ofAddListener(ofxWinTouchHook::touchDown, this, &ofApp::touchDown);
	ofAddListener(ofxWinTouchHook::touchMoved, this, &ofApp::touchMove);
	ofAddListener(ofxWinTouchHook::touchUp, this, &ofApp::touchUp);

	// initialize variables
	x = 0;
	y = 0;
	touchCount = 0;

	touchMap.clear();
	copyTouchMap.clear();
	isShowTouchPoints = false;
	isDrawLines = false;
	isColorIn = false;
	redness = ofRandom(0, 255);
	greenness = ofRandom(0, 255);
	blueness = ofRandom(0, 255);

	ofSetVerticalSync(true);

	box2d.init();
	box2d.setGravity(0, 10);
	//box2d.createBounds();
	box2d.setFPS(60.0);
	box2d.registerGrabbing();
	isDrawCircles = false;
	isDrawBoxes = false;
	numCircles = 0;
	numBoxes = 0;
}

//--------------------------------------------------------------
void ofApp::update() {
	ofSetColor(255, 255, 255); // set color of objects

	string1 = "Touch points: " + ofToString(touchCount); // count text for number of fingers touching screen

	string2 = "Number of circles: " + ofToString(numCircles); // text for number of circles
	
	string3 = "Number of boxes: " + ofToString(numBoxes); // text for number of boxes

	box2d.update();

	deleteCircles(); // delete a circle once one goes off screen

	deleteBoxes(); // delete a box once one goes off screen
}

//--------------------------------------------------------------
void ofApp::draw() {
	// display count text for number of fingers touching screen
	verdana14.drawString(string1, 30, 35);

	verdana14.drawString(string2, 30, 60); // display circle counter text

	verdana14.drawString(string3, 30, 85); // display box counter text
	
	// do commands if corresponding key is pressed
	if (isShowTouchPoints)
		drawTouchPoints();
	if (isDrawLines)
		drawLines();
	if (isColorIn)
		colorIn();

	/*for (int i = 0; i < circles.size(); i++) {
		ofFill();
		ofSetHexColor(0xf6c738);
		circles[i].get()->draw();
	}*/

	for (auto &circle : circles) {
		circle.get()->draw();
		ofPoint pos = circle.get()->getPosition();
		float radius = circle.get()->getRadius();

		if (pos.x > ofGetWidth() + radius) {
			circle.get()->alive = false;
		}
		else if (pos.x < -radius) {
			circle.get()->alive = false;
		}
		else if (pos.y > ofGetHeight() + radius) {
			circle.get()->alive = false;
		}
		else if (pos.y < -radius) {
			circle.get()->alive = false;
		}

		ofRemove(circles, shouldRemoveShape); // remove the shape if we mark as not alive

		numCircles--;
	}

	for (int i = 0; i < boxes.size(); i++) {
		ofFill();
		ofSetHexColor(0xBF2545);
		boxes[i].get()->draw();
	}

	// draw the ground
	//box2d.drawGround();

	if (isDrawCircles)
		drawCircles();
	if (isDrawBoxes)
		drawBoxes();
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key) {
	switch (key) {
		// toggle visual display of touch points crosshair and coordinates
		case 't': case 'T':
			isShowTouchPoints = !isShowTouchPoints;
			break;

		// toggle drawing of straight lines among all points
		case 'd': case 'D':
			isDrawLines = !isDrawLines;
			break;

		// toggle coloring of gaps in between lines with different colors
		case 'c': case 'C':
			isColorIn = !isColorIn;
			break;

		case 'o': case 'O':
			isDrawCircles = !isDrawCircles;
			break;

		case 'b': case 'B':
			isDrawBoxes = !isDrawBoxes;
			break;
	}
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key) {

}

//--------------------------------------------------------------
void ofApp::touchDown(ofTouchEventArgs & touch) {
	//ofLog() << "touch down: " << touch.x << ", " << touch.y << ", id: " << touch.id;

	// store x and y coordinates of initial touch
	x = touch.x;
	y = touch.y;

	// register the touch into map
	touchMap[touch.id] = touch;
	copyTouchMap[touch.id] = touch;

	touchCount++; // one more finger is touching screen
}

void ofApp::touchMove(ofTouchEventArgs & touch) {
	//ofLog() << "touch move: " << touch.x << ", " << touch.y << ", id: " << touch.id;

	// update x and y coordinates of touch
	x = touch.x;
	y = touch.y;

	// need this to update x & y of touch point
	touchMap[touch.id] = touch;
	copyTouchMap[touch.id] = touch;
}

void ofApp::touchUp(ofTouchEventArgs & touch) {
	//ofLog() << "touch up: " << touch.x << ", " << touch.y << ", id: " << touch.id;
	
	// if-else statement used to fix bug where touchCount would go to -1
	if (touchCount <= 0)
		touchCount = 0;
	else
		touchCount--; // one less finger is touching screen

	// force clearing of maps, in case touch releases miss some erases for some strange reasons (do not know why it is happening)
	touchMap.clear();
	copyTouchMap.clear();
}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h) {

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg) {

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo) {

}

// draws to screen all current touch points
void ofApp::drawTouchPoints(void) {
	// loop through map
	for (auto &i : touchMap) {
		auto &touch = i.second; // store value inside each element of map

		// store x and y coordinates of each touch
		x = (int)touch.x;
		y = (int)touch.y;

		// draw crosses at location where finger is touching screen
		ofDrawLine(x - 50, y, x + 50, y);
		ofDrawLine(x, y - 50, x, y + 50);

		touchCoord = "x = " + ofToString(x) + "\ny = " + ofToString(y); // store touch coordinates for each touch

		verdana14.drawString(touchCoord, x + 50, y - 50); // display touch coordinates
	}
}

// draw striaght lines from each touch point to every other touch point
void ofApp::drawLines(void) {
	// loop through map
	for (auto &i : touchMap) {
		auto &oneCoord = i.second; // store individual value of each element inside touchMap so it can be the source of connecting points
		ofNoFill();
		ofBeginShape();
		// loop through copy map to connect lines to other points in same main loop
		for (auto &j : copyTouchMap) {
			auto &otherCoords = j.second; // store all values of copyMap so we know all points we need to connect source point to

			// store x and y coordinates of source point
			x = (int)oneCoord.x;
			y = (int)oneCoord.y;

			// store x and y coordinates of points source must connect to
			otherX = (int)otherCoords.x;
			otherY = (int)otherCoords.y;

			// draw lines
			//ofDrawLine(x, y, otherX, otherY);
			ofVertex(x, y);
			ofVertex(otherX, otherY);
			
		}
		ofEndShape();
	}
}

// color-in gaps in between lines with different colors
void ofApp::colorIn(void) {
	ofFill(); // enable color filling
	ofSetColor(redness, greenness, blueness); // set color
	ofBeginShape(); // begin establishing vertices of desired shape to draw
	// loop through map
	for (auto &i : touchMap) {
		auto &oneCoord = i.second; // store individual value of each element inside touchMap so it can be the source of connecting points

			// store x and y coordinates of source point
			x = (int)oneCoord.x;
			y = (int)oneCoord.y;

			ofVertex(x, y); // draw lines
	}
	ofEndShape(); // end establishment of vertices for desired shape to draw
}

void ofApp::drawCircles(void) {
	float r = ofRandom(4, 20);

	// loop through map
	for (auto &i : touchMap) {
		auto &touch = i.second; // store value inside each element of map

		// store x and y coordinates of each touch
		x = (int)touch.x;
		y = (int)touch.y;

		circles.push_back(shared_ptr<ofxBox2dCircle>(new ofxBox2dCircle));
		circles.back().get()->setPhysics(3.0, 0.53, 0.1);
		circles.back().get()->setup(box2d.getWorld(), x, y, r);

		numCircles++;
	}
}

void ofApp::drawBoxes(void) {
	float w = ofRandom(4, 20);
	float h = ofRandom(4, 20);

	// loop through map
	for (auto &i : touchMap) {
		auto &touch = i.second; // store value inside each element of map

		// store x and y coordinates of each touch
		x = (int)touch.x;
		y = (int)touch.y;

		boxes.push_back(shared_ptr<ofxBox2dRect>(new ofxBox2dRect));
		boxes.back().get()->setPhysics(3.0, 0.53, 0.1);
		boxes.back().get()->setup(box2d.getWorld(), x, y, w, h);

		numBoxes++;
	}
}

void ofApp::deleteCircles(void) {
	//float y = 0;
	//for (int i = 0; i < circles.size(); i++) {
	//	y = circles[i].get()->getPosition().y;
	//	if (y > ofGetWindowHeight()) {
	//		//circles.erase(circles.begin());
	//		circles.front().get()->alive = false;
	//		ofRemove(circles, shouldRemoveShape);
	//		numCircles--;
	//	}
	//}
}

void ofApp::deleteBoxes(void) {
	//float y = 0;
	//int counter = 0;
	//for (int i = 0; i < boxes.size(); i++) {
	//	y = boxes[i].get()->getPosition().y;
	//	if (y > ofGetWindowHeight()) {
	//		//for (int j = 0; j < i; j++)
	//		for (auto& it : boxes)
	//			boxes.erase(boxes.begin());
	//		numBoxes--;
	//	}
	//}
}

static bool shouldRemoveShape(const shared_ptr<ofxBox2dBaseShape> shape) {
	return shape.get() && shape.get()->alive == false;
}Preformatted text

#12

You need to put that function at the top of the page.


#13

My program crashes as soon as the first circle leaves the screen. The shouldRemoveShape function is causing my program to crash, I just verified this with a few print statements


#14

call the ofRemove function after the loop, you can not change the size of the vector inside the loop.

void ofApp::deleteCircles(void) {
	float y = 0;
	for (int i = 0; i < circles.size(); i++) {
		y = circles[i].get()->getPosition().y;
		if (y > ofGetWindowHeight()) {
			circles.front().get()->alive = false;
			numCircles--;
		}
	}

    ofRemove(circles, shouldRemoveShape);
}

#15

Brilliant. Thank you so much!