ofVec2f instead of ofVec3f Question

Is it completely okay to pass ofVec2f instead of ofVec3f as a function argument if I want to use zero as a third (z) value? Or would there be any penalty in performance?

for example, is there any difference in run time between
ofDrawTriangle(ofVec2f(0,0), ofVec2f(100,0), ofVec2f(100,100)) and
ofDrawTriangle(ofVec3f(0,0,0), ofVec3f(100,0,0), ofVec3f(100,100,0))?

As you can see in the code below, there are four different calls for drawing a triangle:

  1. ofDrawTriangle( 3 x ofVec3f)
  2. ofDrawTriangle( 3 x ofVec2f)
  3. ofDrawTriangle( 6 x float)
  4. ofDrawTriangle( 9 x float)

When you call the one with 3 ofVec3f arguments, it will take their x, y and z values and use those to call the function again but with 9 floats. This tells to renderer to actually draw the triangle (ofGetCurrentRenderer()->drawTriangle ...)

When you call it using 3 ofVec2f arguments, it will take their and y values and use those to call the function with 6 float arguments. Then that function will take the x and y arguments and add 0.0f as the z value to call the function with 9 float arguments, which will draw the triangle. This means there is one extra function call when you use ofVec2f. In theory this might make it slower, but I think the difference will be extremely small and you won’t notice it in practice.

Note: the code below doesn’t actually use ofVec2f or ofVec3f but the new glm::vec2 and glm::vec3, but the idea is still the same.

//----------------------------------------------------------
void ofDrawTriangle(const glm::vec3 & p1, const glm::vec3 & p2, const glm::vec3 & p3){
    ofDrawTriangle(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z);
}

//----------------------------------------------------------
void ofDrawTriangle(const glm::vec2 & p1, const glm::vec2 & p2, const glm::vec2 & p3){
	ofDrawTriangle(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
}

//----------------------------------------------------------
void ofDrawTriangle(float x1,float y1,float x2,float y2,float x3, float y3){
	ofDrawTriangle(x1, y1, 0.0f, x2, y2, 0.0f, x3, y3, 0.0f);
}

//----------------------------------------------------------
void ofDrawTriangle(float x1,float y1,float z1,float x2,float y2,float z2,float x3, float y3,float z3){
	ofGetCurrentRenderer()->drawTriangle(x1,y1,z1,x2,y2,z2,x3,y3,z3);
}

I actually wonder why the function call with the ofVec2f arguments doesn’t directly add the 0.0f’s for the z-values and call the one with 9 floats directly, instead of going through the extra step.

Update:
Here is a simple test script. Turns out ofVec3f is indeed slightly faster, but not a whole lot.
Update 2:
I changed it a bit so only the actual drawing time is measured, not the generation of the random numbers.

void ofApp::draw(){

	ofVec2f point1_2f;
	ofVec2f point2_2f;
	ofVec2f point3_2f;

	ofVec3f point1_3f;
	ofVec3f point2_3f;
	ofVec3f point3_3f;
	float startTime;
	float totalTime = 0;
	for(int i=0;i<100000;i++){
		point1_2f.set(ofRandom(0, 500), ofRandom(0, 500));
		point2_2f.set(ofRandom(0, 500), ofRandom(0, 500));
		point3_2f.set(ofRandom(0, 500), ofRandom(0, 500));
		startTime = ofGetElapsedTimeMicros();
		ofDrawTriangle(point1_2f, point2_2f, point3_2f);
		totalTime += ofGetElapsedTimeMicros() - startTime;
	}
	ofLog() << "drawing with ofVec2f took an average of " << (totalTime/100000) << " microseconds";

	totalTime = 0;
	for(int i=0;i<100000;i++){
		point1_3f.set(ofRandom(0, 500), ofRandom(0, 500), ofRandom(0, 500));
		point2_3f.set(ofRandom(0, 500), ofRandom(0, 500), ofRandom(0, 500));
		point3_3f.set(ofRandom(0, 500), ofRandom(0, 500), ofRandom(0, 500));
		startTime = ofGetElapsedTimeMicros();
		ofDrawTriangle(point1_3f, point2_3f, point3_3f);
		totalTime += ofGetElapsedTimeMicros() - startTime;
	}
	ofLog() << "drawing with ofVec3f took an average of " << (totalTime/100000) << " microseconds";

	ofExit();
}

Thank you for your answer :slight_smile:

I don’t think ofDrawTriangle has an internal 3 x ofVec2f call.

I can only see ofPoint x 3 and float x 6 and float x 9 calls.
So I think ofVec2f arguments calls ofPoint x 3 function directly just like using ofVec3f.
And I think they can be type casted internally just like you can do with bool <-> int, const char* <-> string…

For example, you can write

ofVec2f vec2f(1,1);
ofVec3f vec3f = vec2f;

This case vec3f will be 1, 1, 0
I think that is the same as writing

ofVec2f vec2f(1,1);
ofVec3f vec3f = static_cast<ofVec3f>(vec2f);

And since static type casting works during the compile time, I don’t think there would be any difference in runtime performance.

I tried your test code but I found out if I switch the measurement order between ofVec2f and ofVec3f, the ofVec2f was faster. and sometimes slower.

But this is just my opinion and I’m not 100% sure if I’m right.
@arturo : Could you clarify this for me please? Thank you in advance :slight_smile:

You’re running an older version of OF, here is the commit where the change was made from using ofPoint to glm::vec3 (the glm::vec2 option was added later).

The current code can be found in the github repo:

Also, you can still use the above example code to test the time difference youself (updated it slightly to only count the time drawing, not the random number generation).

1 Like

You are right.

I’ve been using of v0.9.8_osx so I downloaded the nightly build and it seems that ofVec2f and ofVec3f are now totally separated so no longer replaceable from one to another. I got many errors where I used ofVec2f instead of ofVec3f.
I don’t know why they changed it to this way. But I guess I have to change everything so I can follow with the future update.

I think you should only use a vec2 when you’re dealing with 2d positions. When working with 3d, use vec3: even if the z value is going to be zero it is still a xyz coordinate so it makes semantic sense so treat it like that.

the performance differences for things like this are completely negligible. in general don’t worry about performance or optimizations, take care that your code is easy to understand. Later if the application is somehow slow you can look for places where it might be problematic or use a profiler to see which functions are taking most resources. optimizing a couple of functions that use most of the time is usually enough to optimize the whole program and most of the time is not even necessary.