Use different fonts

If I want to use different fonts throughout my program, do I have to use the load font function every time? Right now this is what I am doing in my draw() and it works, but I’ve heard it is better practice to put the load font functions in the setup(), so is there a better way to go about this?

1 Like

Something like this?

// .h
ofTrueTypeFont font1;
ofTrueTypeFont font2;

// .cpp
void ofApp::setup() {
    // load fonts once
    font1.load("font1.ttf", 19);
    font2.load("font2.ttf", 84);
}
void ofApp::draw() {
    // reuse loaded fonts
    font1.drawString("small", 20, 20);
    font2.drawString("big", 50, 20);
}

what if i want to use the same font but use a different size where the size is a variable that changes throughout my code, so in that case would I have to use another font.load function with the size parameter being my new size and therefore this new font.load function call would not go in setup, or is there a better/cleaner way to go about this?

1 Like

There’s two ways to draw fonts: as bitmaps (faster) and as vectors (resizable).

void ofApp::setup() {
    ofSetBackgroundColor(55);
    ofSetBackgroundAuto(false);
    bool makeContours {true};
    float simplifyFont {0.0f};
    font1.load("NovaMono.ttf", 64, true, true, makeContours, simplifyFont);
}
void ofApp::draw() {
    ofTranslate(ofRandomWidth(), ofRandomHeight());
    ofScale(ofRandom(0.1f, 10.0f));
    ofSetColor(static_cast<int>(ofRandom(255)));
    font1.drawStringAsShapes("$", 0, 0);
}

Loading fonts (or loading anything) is slow, so normally it’s not a good idea to do inside update() or draw().

So I could just use the ofScale function right before I use the draw string/shape function and that would work?

Calling ofScale works, but depending on what technique you are using (vector vs bitmap) I guess it will look good or pixelated. Note how I used makeContours, simplifyFont and drawStringAsShapes to get the vector version.

OK. So from my understanding ofTranslate determines the x and y coordinates of the upper left hand corner of the text, and that replaces the two zero parameters inside the drawStringAsShapes, is that correct?

I tried using makeContours, simplifyFont and drawStringAsShapes but my program will not display anything when I run it. I think this may be because I am trying to use different fonts throughout my program while also modifying sizes.

Here is my code. You only need to pay attention to everywhere where I am using ofTranslate, ofScale, ofSetColor, drawStringAsShapes.

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
	ofSetFrameRate(60); // set frame rate
	ofBackground(54, 54, 54, 255); // set background color

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

	buttonSound.loadSound("Button.wav");     // load the sound file

	verdana30.load("verdana.ttf", mainTextSize, true, true, makeContours, simplifyFont); // load main text font
	
	verdana14.load("verdana.ttf", butTextSize, true, true, makeContours, simplifyFont); // load buttton text font

	bFirst  = true; // not sure this is for, but needed as parameter for load font function
	typeStr = "We agree to disagree"; // main string

	n = 15; // duration of disagree button movement, the higher the less, the lower the more
}

//--------------------------------------------------------------
void ofApp::update(){
	ofSoundUpdate();    // not sure if this is needed, but seen this in oF example
	if (easeCounter >= 0) { // only go in if mouse is in the vicinity of disagree button
		easing.setup(EASE_FRAMES, n, easeCounter); // initialize frames and speed of easing function for disagree button

		// update currX and currY disagree button positions as they slowly approach the newX x-coordinates and the newY y-coordinates (random location)
		disagreeButCurrX = disagreeButNewX - (easing.easing((float)easeCounter) * deltaXBut);
		disagreeButCurrY = disagreeButNewY - (easing.easing((float)easeCounter) * deltaYBut);
		disagreeTextCurrX = disagreeTextNewX - (easing.easing((float)easeCounter) * deltaXText);
		disagreeTextCurrY = disagreeTextNewY - (easing.easing((float)easeCounter) * deltaYText);
		
		/* DEBUG
		ofLog(OF_LOG_NOTICE, "easeCounter: %d", easeCounter);
		ofLog(OF_LOG_NOTICE, "outRatio: %f", easing.easing((float)easeCounter));
		ofLog(OF_LOG_NOTICE, "disagreeButCurrX: %f", disagreeButCurrY);
		ofLog(OF_LOG_NOTICE, "disagreeButCurrY: %f", disagreeButCurrY);
		ofLog(OF_LOG_NOTICE, "disagreeButNewX: %f", disagreeButNewX);
		ofLog(OF_LOG_NOTICE, "disagreeButNewY: %f", disagreeButNewY);
		ofLog(OF_LOG_NOTICE, "deltaX: %f", deltaX);
		ofLog(OF_LOG_NOTICE, "deltaY: %f\n", deltaY);
		*/

		easeCounter--; // move to next frame
	}
	// make sure button area is always where the button is
	disagreeButSpaceX1 = disagreeButCurrX;
	disagreeButSpaceX2 = disagreeButCurrX + butSizeX;
	disagreeButSpaceY1 = disagreeButCurrY;
	disagreeButSpaceY2 = disagreeButCurrY + butSizeY;
}

//--------------------------------------------------------------
void ofApp::draw(){
	// set circles initial position to middle of screen/window
	if (once) { // due to oF bug, setup that differs between OF_WINDOW and OF_FULLSCREEN mode must be put in in draw() instead of setup()
		// things will change depending on whether window mode is OF_WINDOW or OF_FULLSCREEN
		switch (ofGetWindowMode()) {

			// if window mode is OF_WINDOW
			case 0:
				// get width and height of window
				width = ofGetWindowWidth();
				height = ofGetWindowHeight();

				// determine size of main text
				mainTextSize = ofGetWindowWidth() / 15;
				butTextSize = ofGetWindowWidth() / 25;

				// determine size of button text
				butSizeX = ofGetWindowWidth() / 4;
				butSizeY = ofGetWindowHeight() / 10;

				// determine new size of agree button when clicked
				agreeButNewSizeX = butSizeX - (butSizeX / 4);
				agreeButNewSizeY = butSizeY - (butSizeY / 4);

				agreeTextNewSize = butTextSize - (butTextSize / 4); // determine new size of agree text when clicked

				// determine coordinates of main text
				mainTextX = ofGetWindowWidth() / 8;
				mainTextY = ofGetWindowHeight() / 2;

				// determine coordinates of agree button
				agreeButCurrX = ofGetWindowWidth() / 6;
				agreeButCurrY = ofGetWindowHeight() * 2 / 3;

				// determine coordinates of agree text
				agreeTextCurrX = agreeButCurrX + (agreeButCurrX / 3.5);
				agreeTextCurrY = agreeButCurrY + (agreeButCurrY / 9);

				// determine coordinates of agree button after being clicked
				agreeButNewX = agreeButCurrX + (agreeButCurrX / 5);
				agreeButNewY = agreeButCurrY + (agreeButCurrY / 50);

				// determine coordinates of agree text after being clicked
				agreeTextNewX = agreeTextCurrX + (agreeTextCurrX / 10);
				agreeTextNewY = agreeTextCurrY - (agreeTextCurrX / 100);

				// determine initial coordinates of disagree button
				disagreeButCurrX = ofGetWindowWidth() / 1.75;
				disagreeButCurrY = ofGetWindowHeight() * 2 / 3;

				// determine initial coordinates of disagree text
				disagreeTextCurrX = disagreeButCurrX + (disagreeButCurrX / 22.5);
				disagreeTextCurrY = disagreeButCurrY + (disagreeButCurrY / 9);

				// get x and y offset between top left corner of disagree button and top left corner of disagree text
				disagreeTextButOffsetX = abs(disagreeButCurrX - disagreeTextCurrX);
				disagreeTextButOffsetY = abs(disagreeButCurrY - disagreeTextCurrY);

				// get agree button occupying area
				agreeButSpaceX1 = agreeButCurrX;
				agreeButSpaceX2 = agreeButCurrX + butSizeX;
				agreeButSpaceY1 = agreeButCurrY;
				agreeButSpaceY2 = agreeButCurrY + butSizeY;

				// get disagree button occupying area
				disagreeButSpaceX1 = disagreeButCurrX;
				disagreeButSpaceX2 = disagreeButCurrX + butSizeX;
				disagreeButSpaceY1 = disagreeButCurrY;
				disagreeButSpaceY2 = disagreeButCurrY + butSizeY;

				// load main text font
				//verdana30.load("verdana.ttf", mainTextSize); // modify font size here
				ofScale(mainTextSize); // modify font size here
				break;

			// if window mode is OF_FULLSCREEN
			case 1:
				// get width and height of screen
				width = ofGetScreenWidth();
				height = ofGetScreenHeight();

				// determine size of main text
				mainTextSize = ofGetScreenWidth() / 15;
				butTextSize = ofGetScreenWidth() / 25;

				// determine size of button text
				butSizeX = ofGetScreenWidth() / 4;
				butSizeY = ofGetScreenHeight() / 10;

				// determine new size of agree button when clicked
				agreeButNewSizeX = butSizeX - (butSizeX / 4);
				agreeButNewSizeY = butSizeY - (butSizeY / 4);

				agreeTextNewSize = butTextSize - (butTextSize / 4); // determine new size of agree text when clicked

				// determine coordinates of main text
				mainTextX = ofGetScreenWidth() / 8;
				mainTextY = ofGetScreenHeight() / 2;

				// determine coordinates of agree button
				agreeButCurrX = ofGetScreenWidth() / 6;
				agreeButCurrY = ofGetScreenHeight() * 2 / 3;

				// determine coordinates of agree text
				agreeTextCurrX = agreeButCurrX + (agreeButCurrX / 3.5);
				agreeTextCurrY = agreeButCurrY + (agreeButCurrY / 9);

				// determine coordinates of agree button after being clicked
				agreeButNewX = agreeButCurrX + (agreeButCurrX / 5);
				agreeButNewY = agreeButCurrY + (agreeButCurrY / 50);

				// determine coordinates of agree text after being clicked
				agreeTextNewX = agreeTextCurrX + (agreeTextCurrX / 10);
				agreeTextNewY = agreeTextCurrY - (agreeTextCurrX / 100);

				// determine initial coordinates of disagree button
				disagreeButCurrX = ofGetScreenWidth() / 1.75;
				disagreeButCurrY = ofGetScreenHeight() * 2 / 3;

				// determine initial coordinates of disagree text
				disagreeTextCurrX = disagreeButCurrX + (disagreeButCurrX / 22.5);
				disagreeTextCurrY = disagreeButCurrY + (disagreeButCurrY / 9);

				// get x and y offset between top left corner of disagree button and top left corner of disagree text
				disagreeTextButOffsetX = abs(disagreeButCurrX - disagreeTextCurrX);
				disagreeTextButOffsetY = abs(disagreeButCurrY - disagreeTextCurrY);

				// get agree button occupying area
				agreeButSpaceX1 = agreeButCurrX;
				agreeButSpaceX2 = agreeButCurrX + butSizeX;
				agreeButSpaceY1 = agreeButCurrY;
				agreeButSpaceY2 = agreeButCurrY + butSizeY;

				// get disagree button occupying area
				disagreeButSpaceX1 = disagreeButCurrX;
				disagreeButSpaceX2 = disagreeButCurrX + butSizeX;
				disagreeButSpaceY1 = disagreeButCurrY;
				disagreeButSpaceY2 = disagreeButCurrY + butSizeY;

				// load main text font
				//verdana30.load("verdana.ttf", mainTextSize); // modify font size here
				ofScale(mainTextSize); // modify font size here
				break;
		}
		once = false; // make sure program does this setup only once
	}

	// load buttton text font
	//verdana14.load("verdana.ttf", butTextSize); // modify font size here
	ofScale(butTextSize); // modify font size here

	//ofSetColor(225); // set color of main text
	ofSetColor(static_cast<int>(225)); // set color of main text

	//verdana30.drawString(typeStr, mainTextX, mainTextY); // modify main text font position here
	ofTranslate(mainTextX, mainTextY); // modify main text font position here
	verdana30.drawStringAsShapes(typeStr, 0, 0);

	// Draw the "I agree" button.
	if (mouseHoveringAgreeBut() && mouseClicked) { // agree button is being clicked
		ofDrawRectangle(agreeButNewX, agreeButNewY, agreeButNewSizeX, agreeButNewSizeY); // draw the clicked agree button

		// load agree text font
		//verdana14.load("verdana.ttf", agreeTextNewSize); // modify clicked agree text font size here
		ofScale(agreeTextNewSize); // modify clicked agree text font size here
		
		//ofSetColor(0, 0, 0); // set color of clicked agree text
		ofSetColor(static_cast<int>(0)); // set color of clicked agree text

		//verdana14.drawString("I agree", agreeTextNewX, agreeTextNewY); // draw clicked agree text
		ofTranslate(agreeTextNewX, agreeTextNewY); // draw clicked agree text
		verdana14.drawStringAsShapes("I agree", 0, 0);
	}
	else {
		ofDrawRectangle(agreeButCurrX, agreeButCurrY, butSizeX, butSizeY); // draw agree button

		//ofSetColor(0, 0, 0); // set color of agree button
		ofSetColor(static_cast<int>(0)); // set color of agree button

		//verdana14.drawString("I agree", agreeTextCurrX, agreeTextCurrY); // draw agree text
		ofTranslate(agreeTextCurrX, agreeTextCurrY); // draw agree text
		verdana14.drawStringAsShapes("I agree", 0, 0);
	}

	// load button text font
	//verdana14.load("verdana.ttf", butTextSize); // modify button text font size here
	ofScale(butTextSize); // modify button text font size here

	// Draw the "I disagree" button.
	if (mouseHoveringDisagreeBut() && !mouseHovered) { // mouse is hovering/approaching disagree button and was not on it before
		// set newX and newY of disagree button to new random coordinates
		disagreeButNewX = ofRandom(abs(width - butSizeX));
		disagreeButNewY = ofRandom(abs(height - butSizeY));
		disagreeTextNewX = disagreeButNewX + disagreeTextButOffsetX;
		disagreeTextNewY = disagreeButNewY + disagreeTextButOffsetY;

		easeCounter = EASE_FRAMES - 1; // makes sure frame is updated exactly as many times as EASE_FRAMES
		easing.setup(EASE_FRAMES, n, easeCounter); // initialize frames and speed of easing function for disagree button

		// variables for disagree button coordinate deltas
		deltaXBut = disagreeButNewX - disagreeButCurrX;
		deltaYBut = disagreeButNewY - disagreeButCurrY;
		deltaXText = disagreeTextNewX - disagreeTextCurrX;
		deltaYText = disagreeTextNewY - disagreeTextCurrY;
		
		mouseHovered = true; // makes sure easing happens as soon as disagree button is reached/approached
	}
	//ofSetColor(225); // set color of disagree button
	ofSetColor(static_cast<int>(225)); // set color of disagree button
	ofDrawRectangle(disagreeButCurrX, disagreeButCurrY, butSizeX, butSizeY); // draw disagree button

	//ofSetColor(0, 0, 0); // set color of disagree text
	ofSetColor(static_cast<int>(0)); // set color of disagree text
	//verdana14.drawString("I disagree", disagreeTextCurrX, disagreeTextCurrY); // draw disagree text
	ofTranslate(disagreeTextCurrX, disagreeTextCurrY); // draw disagree text
	verdana14.drawStringAsShapes("I disagree", 0, 0);

	// once disagree button has reached new coordinates, it is ready to move again if approached
	if (disagreeButCurrX == disagreeButNewX && disagreeButCurrY == disagreeButNewY && disagreeTextCurrX == disagreeTextNewX && disagreeTextCurrY == disagreeTextNewY)
		mouseHovered = false;
}```

I don’t have time to test your code today, but one important thing to notice: ofTranslate, ofScale and ofRotate accumulate. The all transform the coordinate system. So if you translate one time, and then again, the origin is moved twice. Same with scale.

The solution is to wrap them inside ofPushMatrix() and ofPopMatrix() like this:

ofPushMatrix();
ofTranslate(...);
ofScale(...);
ofRotate(...);
drawYourThingHere();
ofPopMatrix();

What ofPushMatrix() does is to save the current coordinate system. Then you alter that coordinate system, draw, and finally with ofPopMatrix() you restore the saved coordinate system, to leave it as nothing happened (except that you drew something on the screen).

ps. you don’t need the static_cast when using an int. I used it above because ofRandom() returns a float and I don’t like seeing warnings in my IDE about types being converted automatically (even if it works without the cast).

update:

ofTranslate determines the x and y coordinates of the upper left hand corner of the text, and that replaces the two zero parameters inside the drawStringAsShapes, is that correct?

If I remember right it sets the bottom left coordinates of the text, but you should test that.

By looking at your code, I suspect the ofScale() is setting a large number, like 100. That means “draw the text 100 times larger”. The value is not the pixel height of the text, but a multiplying factor. 1 means use the original size you defined with the font, 2 means twice as large.

So now I’m starting to think that I don’t actually have to use ofTranslate because I can just use the variables that have the coordinates that I want and directly put those into the drawStringAsShapes function, right? Also, does this mean that i should be using ofPushMatrix right before I use ofScale and ofPopMatrix right after I use ofScale (since you mentioned that “ofTranslate, ofScale and ofRotate accumulate”)?

Hi. If you use ofScale() you are scaling the coordinate system. Say you do ofScale(2.0f); and your screen is 500 x 500 pixels in size. By drawing now something at 250, 250 it would be placed at the bottom right pixel of the screen, when you wanted it twice as big but centered.

That’s why when using translations and rotations we often draw at position 0, 0 and use ofTranslate. Otherwise it’s too complicated to calculate where to place things, taking into account the scaling and rotation. With the previous example, you first translate to 250, 250 (so the origin is now at the center of the screen), then scale at 2x, and finally draw your shape, which should be twice the requested size.

If you’re not very familiar with how it works, maybe this chapter helps: https://openframeworks.cc/ofBook/chapters/intro_to_graphics.html#movingtheworld (or videos 28, 29 and 30 at funprogramming.org)

When using rotation and scaling, the typical order looks like this:

ofPushMatrix();
ofTranslate(posx, posy); // translate should happen first
ofScale(scaleFactor); // then either scale or rotate. It does not matter which one goes first.
ofRotateDeg(45);
ofDrawRectangle(0, 0, 200, 50); // then draw some things before you reset the previous transformations
ofPopMatrix(); // reset the  transformations so the origin is at the top left, x points right, scale 1:1.

If you would draw your shape after ofPopMatrix() the transformations would achieve nothing. It would be like temporarily moving the paper under your hand, but then putting it back where it was before you start drawing.